Posts in the “android” category

An Android ActionBar + ListFragment example

The source code below corresponds to an article I wrote titled, Android ActionBar example: How to create an options menu item.

package com.valleyprogramming.justbe;

import android.app.ListFragment;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.ListView;
import android.widget.TextView;
import com.valleyprogramming.justbe.QuotesDatabaseHelper.QuoteCursor;
import com.example.android.swipedismiss.SwipeDismissListViewTouchListener;

public class QuotesListFragment extends ListFragment {

    private static final String TAG = "VPQuotesListFragment";
    private QuoteCursor quoteCursor;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
        getActivity().setTitle(R.string.title_quotes_screen);

        // config the database and adapter
        quoteCursor = DatabaseManager.get(getActivity()).queryQuotes();

        // create an adapter to point at this cursor
        QuoteCursorAdapter adapter = new QuoteCursorAdapter(getActivity(), quoteCursor);
        setListAdapter(adapter);
    }

    @Override
    public void onDestroy() {
        quoteCursor.close();

        //TODO working on possible memory leaks
        quoteCursor = null;
        setListAdapter(null);
        
        super.onDestroy();
    }

    @Override
    public void onActivityCreated(Bundle bundle) {
        super.onActivityCreated(bundle);
        // get a handle to our list view to implement 'swipe to delete'
        ListView listView = getListView();
        // Create a ListView-specific touch listener. ListViews are given special treatment because
        // by default they handle touches for their list items... i.e. they're in charge of drawing
        // the pressed state (the list selector), handling list item clicks, etc.
        SwipeDismissListViewTouchListener touchListener =
            new SwipeDismissListViewTouchListener(
                listView,
                new SwipeDismissListViewTouchListener.DismissCallbacks() {
                    @Override
                    public boolean canDismiss(int position) {
                        return true;
                    }
                    @Override
                    public void onDismiss(ListView listView, int[] reverseSortedPositions) {
                        for (int position : reverseSortedPositions) {
                            String quoteToDelete = ((QuoteCursor)(getListAdapter()).getItem(position)).getQuote().getQuote();
                            DatabaseManager.get(getActivity()).deleteQuote(quoteToDelete);

                            // need the next two lines to update the list after the delete
                            refreshQuoteCursorDataSet();

                            handleCasesWhereThisQuoteWasTheCurrentlyDisplayedQuote(quoteToDelete);
                        }
                    }
                });
        listView.setOnTouchListener(touchListener);
        // setting this scroll listener is required to ensure that during ListView scrolling,
        // we don't look for swipes.
        listView.setOnScrollListener(touchListener.makeScrollListener());

    }
    
    private void handleCasesWhereThisQuoteWasTheCurrentlyDisplayedQuote(String quoteThatWasDeleted) {
        if (GlobalState.currentlyDisplayedQuote.equals(quoteThatWasDeleted)) {
            if (GlobalState.pinnedStateEnabled) {
                // the quote that was deleted was the current quote and it was pinned
                PreferencesUtils.handleUserUnpinsQuoteAction(getActivity().getBaseContext());
                GlobalState.currentlyDisplayedQuote = "";
            } else {
                // the quote that was deleted was the current quote but it was not pinned
                GlobalState.currentlyDisplayedQuote = "";
            }
        }
    }

    /**
     * When a list item is clicked, perform the 'Edit' action.
     */
    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        String quote = ((QuoteCursor)(getListAdapter()).getItem(position)).getQuote().getQuote();
        Log.d(TAG, quote + " was clicked");

        // i'm doing this in the long form just for my own knowledge
        Context context = getActivity();
        Class destination = EditQuoteActivity.class;
        Intent intent = new Intent(context, destination);
        intent.putExtra(EditQuoteFragment.KEY_QUOTE_EXTRA, quote);
        startActivityForResult(intent, 0);
    }

    /**
     * update the list of quotes when the user returns, presumably after
     * using the 'edit' screen. p. 197.
     * 
     * the book's code example actually uses onActivityResult instead of
     * onResume. i just tested it, and they both get called, so either one
     * will work.
     */
    @Override
    public void onResume() {
        super.onResume();
        refreshQuoteCursorDataSet();
    }

    /**
     * as the code shows, the cursor needs to be refreshed before
     * notifyDataSetChanged is called
     */
    private void refreshQuoteCursorDataSet() {
        quoteCursor.requery();
        ((QuoteCursorAdapter)getListAdapter()).notifyDataSetChanged();
    }

    /**
     * the menu layout has the 'add/new' menu item
     */
    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
        menuInflater.inflate(R.menu.menu_quotes_list, menu);
        super.onCreateOptionsMenu(menu, menuInflater);
    }

    /**
     * react to the user tapping/selecting an options menu item
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_item_new_quote:
                Intent addQuoteIntent = new Intent(getActivity(), NewQuoteActivity.class);
                startActivityForResult(addQuoteIntent, 0);
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    /**
     * The adapter class that will work with the ListView.
     */
    private static class QuoteCursorAdapter extends CursorAdapter {

        private QuoteCursor mRunCursor;

        public QuoteCursorAdapter(Context context, QuoteCursor cursor) {
            super(context, cursor, 0);
            mRunCursor = cursor;
        }

        @Override
        public View newView(Context context, Cursor cursor, ViewGroup parent) {
            LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            return inflater.inflate(R.layout.quote_item_view, parent, false);
        }

        @Override
        public void bindView(View view, Context context, Cursor cursor) {
            // get the quote for the current row
            Quote quote = mRunCursor.getQuote();

            // this works with my custom view (quote_item_view.xml)
            TextView textView = (TextView)view.findViewById(R.id.the_quote);
            textView.setText(quote.getQuote());

            // set the text size
            textView.setTextAppearance(context, R.style.listViewText);

            // confirmed: this works. can also specify alpha as '00-ff' before the rgb
            // component, but that won't do anything in this case
            textView.setBackgroundColor(Color.parseColor("#ffffff"));
        }

    }

}

The reason I show the code here is because it shows how to enable the Android ActionBar, in this case from a Fragment, and more specifically a ListFragment.

A simple Android ListActivity example

Android ListActivity FAQ: How do I create a class that extends the Android ListActivity class?

The easiest way to show a ListActivity example is to share some source code with the non-essential code stripped out. To that end, here's an example ListActivity class:

When is the Android Fragment onCreateOptionsMenu method called?

Android FAQ: When is the Android Fragment onCreateOptionsMenu method called?

I was just working through a problem with an Android Menu and MenuItem, and added some debug code to the methods in my Android Fragment, and found that the onCreateOptionsMenu method is called after onStart. I didn’t put Log/debug code in every activity lifecycle method, but for the ones I did add logging code to, the specific order of the fragment method calls looked like this:

  1. onCreate
  2. onCreateView
  3. onActivityCreated
  4. onStart
  5. onCreateOptionsMenu

I was surprised, I thought onCreateOptionsMenu was called earlier — which led to my null pointer exceptions — but this is the way the calls worked with Android 7.x.

The Android method lifecycle image comes from this android.com page.

An example of Android StrictMode output (with improper database access)

I was just working with an example of how to use Android’s new Room Persistence Library, and the example I was working with ran some of its code on the main Android thread, also known as its “UI thread.” I knew this was bad, but I wanted to start with someone’s example, and then figure out a good way to get the Room method calls to run on a background thread, such as using an AsyncTask. (The Android docs don’t specify a “best practice” for this atm.)

One way to make sure you don’t accidentally run code like this on the main Android thread is to enable Android’s StrictMode. Per the StrictMode documentation, you can enable StrictMode in the onCreate method of an Activity like this:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // detects things you might be doing by accident and brings them to your attention so you can fix them
    // https://developer.android.com/reference/android/os/StrictMode.html
    StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
            .detectDiskReads()
            .detectDiskWrites()
            .detectNetwork()   // or .detectAll() for all detectable problems
            .penaltyLog()
            .build());
    StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
            .detectLeakedSqlLiteObjects()
            .detectLeakedClosableObjects()
            .penaltyLog()
            .penaltyDeath()
            .build());
...
...
...

After I enabled StrictMode in this example application and ran it again, I saw A LOT of logcat output that looks like this:

07-20 10:19:19.775 18278-18278/com.foo.roomdemo D/StrictMode: StrictMode policy violation; 
~duration=17 ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=65543 violation=2
      at android.os.StrictMode$AndroidBlockGuardPolicy.onReadFromDisk(StrictMode.java:1293)
      at android.database.sqlite.SQLiteConnection.applyBlockGuardPolicy(SQLiteConnection.java:1041)
      at android.database.sqlite.SQLiteConnection.executeForLong(SQLiteConnection.java:595)
      at android.database.sqlite.SQLiteConnection.setPageSize(SQLiteConnection.java:251)
      at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:213)
      at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:193)
      at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:463)
      at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:185)
      at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:177)
      at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:808)
      at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:793)
      at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:696)
      at android.app.ContextImpl.openOrCreateDatabase(ContextImpl.java:652)
      at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:289)
      at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:223)
      at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:163)
      at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:107)
      at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:83)
      at android.arch.persistence.room.RoomDatabase.query(RoomDatabase.java:166)
      at com.foo.persistence.UserDao_Impl.getAll(UserDao_Impl.java:84)
      at com.foo.persistence.MainActivity.lambda$-com_nagarro_persistence_MainActivity_lambda$1(MainActivity.java:46)
      at com.foo.persistence.-$Lambda$0.$m$0(Unknown Source)
      at com.foo.persistence.-$Lambda$0.onClick(Unknown Source)
      at android.view.View.performClick(View.java:5637)
      at android.view.View$PerformClick.run(View.java:22429)
      at android.os.Handler.handleCallback(Handler.java:751)
      at android.os.Handler.dispatchMessage(Handler.java:95)
      at android.os.Looper.loop(Looper.java:154)
      at android.app.ActivityThread.main(ActivityThread.java:6119)
      at java.lang.reflect.Method.invoke(Native Method)
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)

That’s what StrictMode does: it shows you when you have long-running tasks such as database, network, and file I/O methods running on the main thread, which again is a no-no.

In summary, if you wanted to see how to enable StrictMode, and what the StrictMode output looks like, I hope this is helpful.

Android: Generic methods to write/save Android preferences data

I was working with Android Preferences recently, and after writing a few “save my preference” methods, I decided to take a few minutes to organize my code and put all of those Preference methods in a PreferenceUtils class. I made the methods static, and following functional programming principles, I made their output depend only on their input, so they require you to pass in a Context reference, along with your preference key and value.

Source code for an Android ViewPager example (Activity and Fragment)

This is a little unusual for me, but I'm putting the following code here so I can remember how I implement a ViewPager in my current Android application. I want to put this code out here in its current state before it gets more complicated. In fact, I only wish I had copied it out here earlier, before I made it as complicated as it is.

The only things that are important to know are:

How to turn off the Android haptic feedback vibration setting

If you want to turn off the Android haptic feedback vibration setting on Android 7, I just found that you have to follow these steps:

Settings > Sound > Other sounds > Vibrate on tap

It feels odd that the setting is buried under the “Sound” setting, but there it is.

Instructions for filing an Android bug report

As a quick note (circa March 10, 2017 and Android 7), I filed an Android bug report recently, and when I did, one of the Android developers asked if I would file an Android bug report. To do so, they gave me these instructions:

“After reproducing the issue, navigate to developer settings, ensure ‘USB debugging’ is enabled, then enable ‘Bug report shortcut’. To take bug report, hold the power button and select the ‘Take bug report’ option. Note: Please upload the files to google drive and share the folder to android-bugreport@google.com, then share the link here.”

I thought that was interesting, I had never seen the “Bug report shortcut” approach before.

How to print an array in Android Log output (Logcat)

If you need to dump the contents of an array to the Android Log (Logcat) output, I can confirm that this approach works, at least with simple arrays of integers and strings that know how to print themselves:

Log.i("MyAndroidClass", Arrays.toString(arr));

If you’re trying to print more complicated arrays of custom objects you’ll probably need to implement good toString methods on those objects, and then this technique should work.

Next version of my Android football game

This image shows a very early (ugly) prototype of the next version of my Android football game. The orange boxes on the bottom-left let you choose the formation, then the “run,” “pass,” and “other” icons stand for tabs that let you choose different plays. I may put the formations inside a tab as well ... that would be more consistent, and it will be something I’ll need to do as the game grows.

A major new feature in the game is that you can can create your own custom teams, with each player on the team having a collection of ratings. For instance, a running back will have rating categories of a) running, b) blocking, and c) pass-catching abilities. Therefore, the running plays will let you choose to run left or right, and passing plays will let you choose the intended receiver.

I’ll write more as time goes on, but this ugly little prototype is one of the first steps in the redesign/upgrade.

MyShake: A free app that has the ability to recognize earthquake shaking

Per their website: “MyShake is a free app for Android phones that has the ability to recognize earthquake shaking using the sensors in every smartphone. The app runs ‘silently’ in the background on your phone using very little power – just like the step-tracking fitness apps. When the shaking fits the vibrational profile of an earthquake, the app sends the anonymous information to our central system that confirms the location and magnitude of the quake.”

“Our goal is to build a worldwide seismic network and use the data to reduce the effects of earthquakes on us as individuals, and our society as a whole. MyShake also provides users with information about recent earthquakes around the world and significant global historical earthquakes.”