Survival Reader – RSS App for Android

Survival Reader was designed to help survivalists and emergency preparedness enthusiasts have easy access to several popular preparedness blogs. It pulls in the blog sites’ RSS feeds and displays the articles in the app for quick browsing and reading. Links to the full articles are also provided. The app also provides a listing of food and emergency gear organized into categories. The food and gear are linked to Thrive Life products. Thrive Life has a large variety of high quality freeze-dried and dehydrated food. Various can sizes and package sizes are available.

Here’s how the app looks:

Main Menu

Article List

Emergency Gear

Emergency Food

Blogs

Article View


The app is available on Google Play right now for free and on Amazon’s Android App Marketplace as soon as it’s approved.
Advertisements

Android App: CougarBoard RSS

CougarBoard RSS is wrapper Android app for Cougar Board’s RSS feeds. It wasn’t easy to find a library that would reliably convert the RSS XML to Java objects. I ended up doing my own XML parsing using JDom. All Android web requests need to be done asynchronously, so some extra threading was needed. I used some webviews to show forum threads and news articles inside the app and used Androids Html.fromHtml() utility for displaying html in TextViews and having links functional. I learned about Intent filters for capturing link requests within the app and handling them. Finally, I used a ListView adapter to display each item in the RSS feeds. A ListView in Android has huge performance gains for large lists because it only renders items that are visible.

Here’s the async task for retrieving the RSS xml:

import android.os.AsyncTask;
import android.util.Log;
import org.jdom2.Document;
import org.jdom2.input.SAXBuilder;

public abstract class RequestTask extends AsyncTask {

    public Exception exception;
    protected abstract void onPostExecute(Document result);

    public Document doInBackground(String... urls) {
        try {
            SAXBuilder builder = new SAXBuilder();
            URL url = new URL(urls[0]);
            Document document = (Document) builder.build(url);
            // Try again
            if(document == null) {
                document = (Document) builder.build(url);
                if(document == null) {
                    document = (Document) builder.build(url);
                }
            }
            return document;
            //return IOUtil.getUrl(urls[0]);
        } catch (Exception e) {
            Log.e("cb", "exception", e);
            this.exception = e;
            return null;
        }
    }
}

Here’s the code to run the async task:

        new RequestTask() {
            @Override
            protected void onPostExecute(Document result) {
                loadFeed(result);
            }
        }.execute(url);

Here’s how to parse an RSS feed using JDom:

                SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z");
                Element rootNode = document.getRootElement();
                Element channel = rootNode.getChild("channel");
                List items = channel.getChildren("item");

                List messages = new ArrayList();

                for(Element item : items) {
                    FeedMessage message = new FeedMessage();
                    message.setTitle(item.getChildText("title").trim());
                    message.setAuthor(item.getChildText("author").trim());
                    message.setGuid(item.getChildText("guid").trim());
                    message.setLink(item.getChildText("link").trim());
                    message.setDescription(item.getChildText("description").trim());
                    message.setPubDate(item.getChildText("pubDate").trim()); // Sat, 31 Aug 2013 15:00:07 -0600
                    try {
                        Date dte = format.parse(message.getPubDate());
                        message.setDate(dte);
                    } catch(Exception e) {Log.e("cb", "exception", e); }
                    messages.add(message);
                }

Here’s part of the adapter for the ListView. The NewsEntryCellView is an extended class of a LinearLayout which can be loaded from a layout xml or created programatically:

public class FeedListAdapter extends BaseAdapter {
    private List messages;
    private Activity context;

    public FeedListAdapter(Activity context, List messages) {
        this.context = context;
        this.messages = messages;
    }

    public int getCount() {
        return messages.size();
    }

    public FeedMessage getItem(int index) {
        return messages.get(index);
    }

    public long getItemId(int index) {
        return index;
    }

    public View getView(int index, View cellRenderer, ViewGroup viewGroup) {
        NewsEntryCellView newsEntryCellView = (NewsEntryCellView) cellRenderer;
        if (cellRenderer == null) {
            newsEntryCellView = new NewsEntryCellView();
        }
        newsEntryCellView.display(index);
        return newsEntryCellView;
    }
}

Future versions will record the RSS feeds into a sqlite database and keep track of what has been viewed. Another desire is to setup a server that can pull the RSS and push notifications to devices using Google’s Cloud Messaging system and Amazon’s Device Messaging API.

Here’s how version 1 of the app looks.

MainMenuMessageBoardNewsThreadsNewsThreadsWebView

This was a fun project that I hope to work on more and also use it to make an app for UteFans.net.

The app is available on Google Play right now for $1 and on Amazon’s Android App Marketplace as soon as it’s approved.

Android App – Number Trivia

I was trying to come up with another app idea so I was browsing through mashape.com and found numbersapi.com. The creators allow it to be used by anyone. As always, with any app idea, I searched Google Play for an existing version and I did not find one. I decided to make a trivia app using the numbers api. I decided to increase my knowledge on how to use gradients instead of images on buttons.

Here’s how you define a gradient style in a design xml file. Then you just use it as a the button’s style:

<shape xmlns:android=”http://schemas.android.com/apk/res/android&#8221; android:padding=”16dp” android:shape=”rectangle” >
<corners android:radius=”6dp” />
<gradient android:angle=”270″ android:endColor=”@color/lightblue” android:startColor=”@color/darkblue” />
<stroke android:width=”2px” android:color=”@color/lightblue” />
</shape>

This gave the buttons a nice gradient, border, and rounded corners. It only took me about 10 hours to put the entire app together and that includes lots of interruptions throughout. I also included Amazon apps so I could submit the app towards earning a Kindle Fire HD for helping them beta test their new mobile ad API. Here’s the utility class I used to help insert the ads and rotate them at a set interval.


import android.app.Activity;
import android.util.*;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import com.amazon.device.ads.*;
import java.util.*;

public class AdUtil implements AdListener {

    public static final String APP_KEY = "app_key_goes_here";
    public static final boolean AD_TESTING = false;

    public static void loadAd(Activity activity, LinearLayout adContainer, int width, DisplayMetrics displayMetrics) {

        try {

            AdRegistration.enableLogging(false);
            AdRegistration.enableTesting(AD_TESTING);

            final AdLayout adView;

            // If you use height=wrap_content then you don't need this stuff.  I realized this afterwards.
            if(width >= 1024) {
                adView = new AdLayout(activity, AdSize.SIZE_1024x50);
            } else if(width >= 728) {
                ViewGroup.LayoutParams params = adContainer.getLayoutParams();
                params.height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 90, displayMetrics);
                adView = new AdLayout(activity, AdSize.SIZE_728x90);
            } else if(width >= 600) {
                ViewGroup.LayoutParams params = adContainer.getLayoutParams();
                params.height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 90, displayMetrics);
                adView = new AdLayout(activity, AdSize.SIZE_600x90);
            } else if(width >= 320) {
                adView = new AdLayout(activity, AdSize.SIZE_320x50);
            } else {
                adView = new AdLayout(activity, AdSize.SIZE_300x50);
            }

            LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
            adContainer.addView(adView, lp);

            adView.setListener(new AdUtil());

            AdRegistration.setAppKey(APP_KEY);
            AdTargetingOptions adOptions = new AdTargetingOptions();
            adView.loadAd(adOptions);

            Timer timer = new Timer();
            class UpdateAdTask extends TimerTask {
                public void run() {
                    try {
                        AdTargetingOptions adOptions = new AdTargetingOptions();
                        adView.loadAd(adOptions);
                    } catch(Exception ex) {
                        Log.e("trivia", "exception", ex);
                    }
                }
            }

            TimerTask updateAd = new UpdateAdTask();
            timer.scheduleAtFixedRate(updateAd, 0, 35 * 1000);
        } catch (Exception e) {
            Log.e("trivia", "exception", e);
        }
    }
}

With this app, I also learned that you can not make network requests on the display thread. Android forces you to create an alternative request and asynchronously make requests. This is better anyway. The API is really fast so I didn’t put any loaders gifs in.

Here’s what it looks like:

Capture1 Capture3 Capture2

The app is available here:
Google Play
Amazon Marketplace

Android App – Star Wars Sounds

I thought it would be fun to have a Star Wars soundboard on Android. I searched for existing ones on Google Play and found a few, but they were low quality in UI design and sound quality. StarWars.com has a flash soundboard with hundreds of high quality sound clips from the movies. I used these sounds for my app.

Playing sounds in Android:
At first I tried to use the SoundPool, but this preloads the sounds into memory and I had 350 sounds so the app would crash and also take forever to load. The better option was Android’s Media Player that plays the sounds on demand. I placed the mp3 files in the raw folder of my app and accessed them. Here’s some code snippets.

private void playSound(int resId) {
        try {
            if(mp == null) {
                mp = new MediaPlayer();
            }
            AssetFileDescriptor afd = this.getResources().openRawResourceFd(resId);
            mp.reset();
            mp.setVolume(1.0f, 1.0f);
            mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
            afd.close();
            mp.prepare();
            mp.start();
        } catch (Exception e) {
            Log.e("sw", "exception", e);
        }
    }

A friend noticed that his phone wouldn’t let his volume buttons control media player sounds unless there was a sound currently playing. To solve that I created a blank mp3 and had it looping the entire time during the app so volume control was easy to manage on devices. I loop this sound using setLooping(true);

    private void playBlank() {
        try {
            AssetFileDescriptor afd = this.getResources().openRawResourceFd(R.raw.blank);
            blankMp.reset();
            blankMp.setVolume(1.0f, 1.0f);
            blankMp.setLooping(true);
            blankMp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
            afd.close();
            blankMp.prepare();
            blankMp.start();
        } catch(Exception ex) {
            Log.e("sw", "exception", ex);
        }
    }

Here’s the app:
Star Wars Sounds App

This app is available here:
Google Play
Amazon Marketplace

Android App – Memorize Scriptures

A co-worker carried around 3×5 cards in order to memorize scripture references. He asked how hard it would be to create an Android app to replace his 3×5 cards. I became interested and volunteered to create the app which lead to my first published Android app and also my Android app creation obsession. 🙂

First challenge, have the entire LDS standard works of scripture available in a sqlite database. Luckily, a fellow Mormon developer has made the scriptures available in several data formats including sqlite found here: http://scriptures.nephi.org/. Android is a lot happier if the id field of each sqlite table is ‘_id’ so I had to do some modifications to the tables.

I placed the pre-populated SQLite database in the assets folder of my app and copied over to the App’s data directory on Android if it was it’s first time running on a device. Here’s my helper class for doing all this:

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.*;
import android.util.Log;

import java.io.*;
import java.sql.SQLException;
import java.util.Locale;

class DatabaseHelper extends SQLiteOpenHelper {
    private static String DB_PATH = "/data/data/com.spriton.memorizescriptures2/databases/";
    public static final String DATABASE_NAME = "scriptures.sqlite";

    private static final int SCHEMA_VERSION = 1;

    public SQLiteDatabase db;
    private final Context myContext;

    public DatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, SCHEMA_VERSION);
        this.myContext = context;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }

    public void createDatabase() {
        if (!DbExists()) {
            this.getReadableDatabase();
            copyDataBase();
        }
    }

    private boolean DbExists() {
        SQLiteDatabase db = null;
        try {
            String databasePath = DB_PATH + DATABASE_NAME;
            db = SQLiteDatabase.openDatabase(databasePath, null,  SQLiteDatabase.OPEN_READWRITE);

            db.setLocale(Locale.getDefault());
            db.setLockingEnabled(true);
            db.setVersion(1);

            Cursor c = db.rawQuery("SELECT * FROM sqlite_master WHERE type='table'", null);
            if(c.getCount()  " + outFilePath);
        try {
            iStream = myContext.getAssets().open(DATABASE_NAME);
            oStream = new FileOutputStream(outFilePath);
            byte[] buffer = new byte[2048];
            int length;
            while ((length = iStream.read(buffer)) > 0) {
                oStream.write(buffer, 0, length);
            }
            oStream.flush();
            oStream.close();
            iStream.close();
        } catch (IOException e) {
            Log.e("scripture", "exception", e);
        }
    }

    public void openDatabase() throws SQLException {
        String path = DB_PATH + DATABASE_NAME;
        db = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READWRITE);
    }

    public SQLiteDatabase getDatabase() {
        return db;
    }

    @Override
    public synchronized void close() {
        if (db != null) {
            db.close();
        }
        super.close();
    }

Second challenge, find a simple and light ORM for Android and SQLite. This is where OrmLite came in handy. I’m used to Hibernate, so the object mappings and queries took some getting used to but they worked just fine.

Here’s an OrmLite query example:

     QueryBuilder query = OrmHelper.getHelper(this).getScriptureDao().queryBuilder();
     Where where = query.where();
     where.isNull("deleted");
     query.orderBy("created", true);
     List scriptures = query.query();

Third challenge, learning how to style Android. It took a lot of searching and trial and error to get the app to look the way I wanted. I was targeting Android API 10 (2.3) so I was not able to use the newer Holo themes that make things look sharp. I did find a Holo Theme Generator that gave me a good start once I got it in my project and got it to build properly.

The result:

Menu Review2 ViewAll2

NewScripture

This app is available on:
Google Play
Amazon Marketplace

Android Studio

Android Studio is only in beta, but it’s awesome! It is a huge improvement from the Eclipse development environment. It is my first experience with the IntelliJ IDE so I had to learn all new shortcuts. The wysiwyg works great and they system warns you of Android API compatibility issues in your code and layouts. The main problem I have ran into with the beta version is the new gradle build system. If you start with a fresh install of Android Studio, there probably isn’t any problems, but when upgrading to newer beta releases, they project builds change and that can cause problems with existing projects. Gradle also gives very cryptic error messages which can leave you scratching your head. Overall though, Android Studio has been a good experience and has motivated me in building 3 apps that have been published so far. I will post about each one and some code snippets from them.

AS

Native Mobile App Development using Appcelerator

There are a few native mobile app frameworks out there that will compile to both Android and iOS platforms.  I settled onto Appcelerator because it is free, it uses JavaScript, it provide a good IDE with build management, and it has great documentation.   It has been a learning curve for me because I’m not super familiar with mobile app development.   The other challenge was the advanced object oriented use of JavaScript.  Normally I just use simple JavaScript classes to manage the UI features of specific web pages.  Never before have I used JavaScript as the sole language to an application.

Quick Success:

Within a couple hours I was able to get the Appcelerator IDE setup, attach the Android API, and deploy a ‘Hello World’ application to a Kindle Fire.  I suggest using a native device to test applications since the Android emulator is uber slow.  The most recent release of Appcelerator also offers remote application debugging.

I’m in the process of using REST APIs provided by ShelfReliancePro to create a mobile app for that web application.  So far it has gone pretty well and was able to load data from the API into the Android App.