Showing posts with label Android. Show all posts
Showing posts with label Android. Show all posts

Saturday, February 16, 2013

Subclassing Spinner and ArrayAdapter to change drop-down appearance

As we know, applying different layout can change text color and size, background color and so on. There is no need to do any Java code to achieve that, we just use existing Android classes. Now if we want to change drop-down item and include there more controls than just TextView and one drawable or maybe to apply custom resizing, than we have to dive into ArrayAdapter source and write custom adapter. I will not bother you with image and two TextViews, there is plenty of tutorials on Web about that, instead I will just try to resize drop-down to keep it simple. So here is complete code for minimalistic custom adapter:

public class CustomAdapter extends ArrayAdapter implements SpinnerAdapter{
    private LayoutInflater mInflater;
    private int mFieldId = 0;
    private int mResource;
    public CustomAdapter(Context context, int textViewResourceId,
            String[] objects) {
        super(context, textViewResourceId, objects);
        mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }
    @Override
    public View getDropDownView(int position, View convertView, ViewGroup parent) {
        return createViewFromResource(position, convertView, parent, mResource);
    }
    @Override
    public void setDropDownViewResource(int resource) {
        this.mResource = resource;
    }
    private View createViewFromResource(int position, View convertView, ViewGroup parent,
            int resource) {
        View view;
        TextView text;
        if (convertView == null) {
            view = mInflater.inflate(resource, parent, false);
        } else {
            view = convertView;
        }
        try {
            if (mFieldId  == 0) {
                //  If no custom field is assigned, assume the whole resource is a TextView
                text = (TextView) view;
            } else {
                //  Otherwise, find the TextView field within the layout
                text = (TextView) view.findViewById(mFieldId);
            }
        } catch (ClassCastException e) {
            Log.e("ArrayAdapter", "You must supply a resource ID for a TextView");
            throw new IllegalStateException(
                    "ArrayAdapter requires the resource ID to be a TextView", e);
        }
        String item = getItem(position);
        if (item instanceof CharSequence) {
            text.setText((CharSequence)item);
        } else {
            text.setText(item.toString());
        }
        view.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
        return view;
    }
}


Most of createViewFromResource is Android code from ArrayAdapter. My modest contribution is only red line. If I specify there some values for width, instead of LayoutParams.FILL_PARENT then I will achieve this:


Well, items are narrower but container is still the same, so subclassing ArrayAdapter doesn’t really help. What needs to be resized and repositioned is AlertDialog which is holding those rows. We can find that out when we open Spinner source. Now I will again do minimalistic subclassing of Spinner.

public class CustomSpinner extends Spinner {
    private AlertDialog mPopup;
    public CustomSpinner(Context context) {
        super(context);
    }
    public CustomSpinner(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mPopup != null && mPopup.isShowing()) {
            mPopup.dismiss();
            mPopup = null;
        }
    }
    @Override
    public boolean performClick() {
        Context context = getContext();

        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        CharSequence prompt = getPrompt();
        if (prompt != null) {
            builder.setTitle(prompt);
        }
        mPopup = builder.setSingleChoiceItems(
                new DropDownAdapter(getAdapter()), getSelectedItemPosition(),
                this).show();
        WindowManager.LayoutParams layout = mPopup.getWindow().getAttributes();
        layout.x = -128;
        layout.y = -110;
        layout.height = 320;
        layout.width = 240;
        mPopup.getWindow().setAttributes(layout);
        return true;
    }
    @Override
    public void onClick(DialogInterface dialog, int which) {
        setSelection(which);
        dialog.dismiss();
        mPopup = null;
    }
    /*
     * here you copy and paste code for DropDownAdapter from Spinner
     */
}


Hardcoded values are good enough for illustration of subclassing. Now since we are using custom control we need to change tag in layout:


Now if we build and run application in emulator we will get this:



Not very complicated.

Thursday, February 14, 2013

Customizing Spinner appearance

If you want to change appearance of Button or TextView, it is very easy. You open layout subfolder in res folder, then you open activity_main.xml and add the following lines:

android:gravity="center"
android:background="#000000"
android:textColor="#ff0000"
android:textSize="32sp"


Something like that, Button text is centered anyway and TextView background is see through by default, so not all of them may be required. But if you want to change appearance of Spinner things are becoming complicated. Naturally first solution is do Google search and see how others are doing it. That gets you to Stack Overflow and there is plenty of solutions which are based on enthusiasm of contributor to say something. That eloquent guessing, without any understanding what is actually going on, goes that far that they are claiming that you must subclass ArrayAdapter to change text color. To be honest Android documentation is not very helpful here and according to modern scientific approach people go here agile, they do trial and error in plain English. If you do not have knowledge demonstrate effort. Naturally there is much better way. Android is open source project and getting Java code for it is not difficult, in Android SDK Manager one just needs to select Sources for Android SDK and download it. Now if we take a look at typical Spinner usage we see something like this:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Spinner spinner=(Spinner)findViewById(R.id.spinner1);
    spinner.setOnItemSelectedListener(this);
    adapter=new ArrayAdapter(this, android.R.layout.simple_spinner_item, items);
    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    spinner.setAdapter(adapter);
}


Appearance of Spinner is defined by android.R.layout.simple_spinner_item in collapsed state and android.R.layout.simple_spinner_dropdown_item in expanded state. If we want Spinner to look differently, we need to change layout. Unfortunately android.R.layout.simple_spinner_item is not part of Sources for Android SDK but android.widget.ArrayAdapter is available. We open it and see relevant methods:

public View getView(int position, View convertView, ViewGroup parent) {
    return createViewFromResource(position, convertView, parent, mResource);
}
public View getDropDownView(int position, View convertView, ViewGroup parent) {
    return createViewFromResource(position, convertView, parent, mDropDownResource);
}


Then we look for createViewFromResource, where real work is done:

private View createViewFromResource(int position, View convertView, ViewGroup parent,
        int resource) {
    View view;
    TextView text;

    if (convertView == null) {
        view = mInflater.inflate(resource, parent, false);
    } else {
        view = convertView;
    }
    try {
        if (mFieldId == 0) {
            //  If no custom field is assigned, assume the whole resource is a TextView
            text = (TextView) view;
        } else {
            //  Otherwise, find the TextView field within the layout
            text = (TextView) view.findViewById(mFieldId);
        }
    } catch (ClassCastException e) {
        Log.e("ArrayAdapter", "You must supply a resource ID for a TextView");
        throw new IllegalStateException(
                "ArrayAdapter requires the resource ID to be a TextView", e);
    }
    T item = getItem(position);
    if (item instanceof CharSequence) {
        text.setText((CharSequence)item);
    } else {
        text.setText(item.toString());
    }
    return view;
}

And there we find required information, that printed with red font. We need to supply layout which is TextView or layout containing TextView and to supply ID of TextView fot the second case. Now when we got idea what we are doing changing appearance is trivial.
Collapsed appearance layout:


Expanded appearance layout:


We save those two as custom Spinner layouts. We know that ID can be omitted and we happily omit it, less typing. We use our custom layouts like this:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Spinner spinner=(Spinner)findViewById(R.id.spinner1);
    spinner.setOnItemSelectedListener(this);
    adapter=new ArrayAdapter
(this, R.layout.col, items);
    adapter.setDropDownViewResource(R.layout.exp);
    spinner.setAdapter(adapter);
}


Obviously I saved them as col.xml and exp.xml.

Wednesday, February 13, 2013

Talking Exposure Timer

Is simple talking count-down timer for Android. As title says purpose of it is to help taking pictures where long exposure is required and one can’t look at watch, for example in astrophotography. That is also main reason why user interface is black and red. So, according to my intentions primary users should be people who are doing astrophotography and manual tracking or barn door tracking.
User interface is simple and we got frequency of announcements and duration spinners at the top, start and cancel buttons in the middle and info text at the bottom. If text to speech engine fails to initialize, start button is disabled and appropriate info is displayed. If text to speech engine initializes it will use default language for phone. Cancel is not immediate but is applied on first delayed post, during next announcement of remaining time.
To use it select how frequently you want announcements and how long exposure should be, after delay of 10 seconds, common for DSLR cameras in remote shutter BULB mode, it starts talking and counting down. While count down lasts, application obtains wake lock, screen will go dim but will not go to “sleep”. It is compiled for and tested on Android 2.2 device LG-P500.
Signed APK is available for download from here https://docs.google.com/file/d/0B0cIChfVrJ7WbkhISE5ZdGdSdmM/edit?usp=sharing, download, copy to SD card and open in file manager to install.
Eclipse project is here https://docs.google.com/file/d/0B0cIChfVrJ7WaS1oRl9lTUJUNmc/edit?usp=sharing, download, import in Eclipse and build.


Tuesday, February 12, 2013

Spinner and ArrayAdapter

This one is simplest possible tutorial, but I need Spinner to allow user to set time interval and frequency for countdown timer. So lets explain it, maybe somebody doesn’t know how to use it.
Spinner is control which corresponds to drop-down, like HTML select or Java/Swing JComboBox. When user selects it, it expands, pops-up list view where selection could be made. After selection is made list view disappears and selected item’s value is on collapsed control. Data binding and selection events are handled by ArrayAdapter and AdapterView. In Android SDK is example how to load ArrayAdapter from resource and I will load it from array of strings. Here is complete code:

public class MainActivity extends Activity implements AdapterView.OnItemSelectedListener {
    private static final String[] items={"5", "10", "15", "20", "30"};
    private ArrayAdapter adapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Spinner spinner=(Spinner)findViewById(R.id.spinner1);
        spinner.setOnItemSelectedListener(this);
        adapter=new ArrayAdapter(this, android.R.layout.simple_spinner_item, items);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spinner.setAdapter(adapter);
        spinner.setSelection(0);
    }
    @Override
    public void onItemSelected(AdapterView parent, View view, int position, long id) {
        Toast.makeText(this, adapter.getItem(position) + " is selected", Toast.LENGTH_LONG).show();
    }
    @Override
    public void onNothingSelected(AdapterView parent) {
        Toast.makeText(this, "Nothing selected", Toast.LENGTH_SHORT).show();
    }
}


Add to default imports AdapterView, ArrayAdapter, Spinner and Toast. In layout, activity_main.xml I placed one Spinner and accepted default name for it. AdapterView.OnItemSelectedListener is used to implement listeners inside activity and again do it differently from SDK example. Difference between android.R.layout.simple_spinner_dropdown_item and android.R.layout.simple_spinner_item is that first one will give you nicer user interface with radio button. To ArrayAdapter constructor we must pass array of strings, array of integers won’t work. Before we show UI we set selected item to position 0. Selecting different items will demonstrate how listener works.

Sunday, February 10, 2013

Timer, not it is Handler.postDelayed

In previous post http://grumpyoldprogrammer.blogspot.com/2013/02/text-to-speech.html I managed to init TTS. Now with talkative Android I can go and implement countdown timer. Whole story with SoundPool and TTS was about I need tool to count for me exposure while I am doing manual tracking. Only that I won’t use Timer or CountDownTimer but ordinary Handler. I leave whole code from TTS how it is and change button onClick listener and add couple of variables. Those are new class variables:

private int counter = 60;
private Handler timer;
boolean timerRunning = false;
private Runnable timerTask = new Runnable() {
    public void run() {
        myTTS.speak(Integer.toString(counter), TextToSpeech.QUEUE_FLUSH, null);
        if (counter > 1) {
            counter-=5;
            timer.postDelayed(timerTask, 5000);
        } else {
            counter = 60;
            timerRunning = false;
        }
    }
};


For more elaborate application I would make counter and delay adjustable but now I like it simple. Don’t forget to create instance of Handler. Now new onClick method:

public void onClick(View v) {
    if (!timerRunning) {
        timerRunning = true;
        timer.post(timerTask);
    }
}


If we do not have active cycle, onClick will start new one. Runnable.run will ask TTS to read counter and decrease it for 5 what matches the second parameter in postDelayed and finally reposts self again using Handler. When counter is 0 we reset it back to 60 and clear flag so that onClick listener can be used again. On emulator execution of 60 seconds countdown takes about 60.3 seconds, so it is not very precise but also not unusable.

Saturday, February 9, 2013

Text-To-Speech

This one is also known as TTS and it is present on Android platform since version 1.6. Usage is uncomplicated but initialization is complicated. Whole initialization issue is about can we have desired Language, is it supported on particular phone. To request language we need to implement OnInitListener interface and that one is defined in android.speech.tts.TextToSpeech.OnInitListener, android.speech.tts.TextToSpeech we need to use TTS and we will import both of them. Before setting any language we want to check what is available and that goes in form question and answer. Inside onCreate we will use Intent:

Intent checkTTSIntent = new Intent();
checkTTSIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
startActivityForResult(checkTTSIntent, MY_DATA_CHECK_CODE);


Naturally to hear reply we will implement onActivityResult:

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == MY_DATA_CHECK_CODE) {
        if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) {
            myTTS = new TextToSpeech(this, this);
        } else {
            Intent installTTSIntent = new Intent();
            installTTSIntent.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
            startActivity(installTTSIntent);
        }
    }
}


Where myTTS is class level variable. If we have TTS data we create TTS instance, otherwise we start installation process which takes user online. Previous two steps are Intent story which is simplest form of IPC on Android platform, everybody should know that. Since we passed as parameter to TTS constructor this as OnInitListener, we should now implement that listener and set language.

public void onInit(int status) {
    if (status == TextToSpeech.SUCCESS) {
        if(myTTS.isLanguageAvailable(Locale.ITALY)==TextToSpeech.LANG_COUNTRY_AVAILABLE)
            myTTS.setLanguage(Locale.ITALY);
        else
            myTTS.setLanguage(Locale.UK);
    } else {
        Toast.makeText(this, "TTS failed!", Toast.LENGTH_LONG);
    }
}


If init was success we check can we have Italian, if Italian is not available we settle for proper English. Now that initialization is done, we can pass strings containing desired speech to TTS and listen:

String story1 = "are you done yet";
String story2 = "when it will be ready";
myTTS.speak(story1, TextToSpeech.QUEUE_FLUSH, null);
myTTS.speak(story2, TextToSpeech.QUEUE_ADD, null);


Obviously we are mimicking project manager. If Italian succeeded, we should use “stai ancora finito” and “quando sarà pronto”, according to Google Translate, but that is not important right now.

Wednesday, February 6, 2013

Simple SoundPool example

I needed it for timer which plays beep on every 15 to 30 seconds. Since I am using remote shutter release and doing manual tracking, astrophotography, I can’t check how long exposure is until now and go back to tracking if it is not long enough.
SoundPool is intended to load audio from resources in APK, and MediaPlayer service immediately decodes audio to raw 16 bit PCM which stays loaded in memory. Multiple audio streams can be played simultaneously, they can’t be started simultaneously but they can be overlapping. Anyway, take a look at documentation http://developer.android.com/reference/android/media/SoundPool.html
Doing research on the Web I concluded that Ogg Vorbis is preferred file format to use for audio files and that they should not be longer than few seconds. To convert audio files to Ogg Vorbis I am using Audacity http://audacity.sourceforge.net/ works on all major operating systems.
So here is the code for very simple player:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    player = new SoundPool(2, AudioManager.STREAM_MUSIC, 100);
    soundList = new SparseIntArray();
    soundList.put(1, player.load(this, R.raw.biska, 1));
    Button button1 = (Button)findViewById(R.id.button1);
    button1.setOnClickListener(button1OnClickListener);
}


We have SoundPool for max two streams, type is music stream and quality 100%. Next we associate known key with result of loading audio from resource.
To play it we want to set volume so it matches current media volume on target phone:

Button.OnClickListener button1OnClickListener
= new Button.OnClickListener(){
    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        AudioManager audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
        float curVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
        float maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
        float volume = curVolume/maxVolume;
        player.play(soundList.get(1), volume, volume, 1, 0, 1f);
    }
};


Proper cleanup would consist from calling release() method of instance of SoundPool and setting reference to it to null.
Here is the link to complete example https://docs.google.com/file/d/0B0cIChfVrJ7WRW10SWFmd2ZZUzg/edit?usp=sharing included Ogg Vorbis is about 5 seconds long. If you click twice play button you can “enjoy” simultaneous play of two streams.

Monday, December 31, 2012

Download YouTube video and convert it for Android

For downloading videos from YouTube I am using YouTubeDownloader2 and you can find it here http://sourceforge.net/projects/ytd2/. Why I am using that and not using Firefox add-ons? YouTubeDownloader2 is standalone program written in Java which is not storing my browsing habits in some remote DB. How do I know that? That is OSS, downloaded code, checked what it does, compiled it. If you don’t do Java, trust others who do ;-)
Once video is on file system we can encode it. It should be easy on Linux unless you are have common repo FFmpeg which is compiled without support for H.264, mp3 and few others codecs. If you are experienced Linux user you will visit FFmpeg wiki where is detailed instruction https://ffmpeg.org/trac/ffmpeg/wiki/UbuntuCompilationGuide how to configure and build FFmpeg. I am on Mint so that is valid set of instructions for me, people using other distro will look for their set of instructions. If you are not experienced user maybe mencoder which comes by default with everything enabled is better option. So, install libavcodec-extra-53 or maybe 54 and mencoder. We can ask mencoder what is supported if in terminal we execute:

~ $ mencoder -ovc help
MEncoder svn r34540 (Ubuntu), built with gcc-4.6 (C) 2000-2012 MPlayer Team

Available codecs:
copy     - frame copy, without re-encoding. Doesn't work with filters.
frameno  - special audio-only file for 3-pass encoding, see DOCS.
raw      - uncompressed video. Use fourcc option to set format explicitly.
nuv      - nuppel video
lavc     - libavcodec codecs - best quality!
xvid     - XviD encoding
x264     - H.264 encoding


and it produces video codecs list. For audio, query looks like this:

~ $ mencoder -oac help
MEncoder svn r34540 (Ubuntu), built with gcc-4.6 (C) 2000-2012 MPlayer Team

Available codecs:
   copy     - frame copy, without re-encoding (useful for AC3)
   pcm      - uncompressed PCM audio
   mp3lame  - cbr/abr/vbr MP3 using libmp3lame
   lavc     - FFmpeg audio encoder (MP2, AC3, ...)
   faac     - FAAC AAC audio encoder


There is small problem with mencoder, there is no WinFF for it and one have to do quite a bit of reading to assemble proper set of parameters. To save you from learning, here is what I do:

~ $ mencoder -of lavf -lavfopts format=mp4 -oac lavc -ovc lavc -lavcopts vbitrate=480:aglobal=1:vglobal=1:acodec=libfaac:abitrate=64:vcodec=mpeg4:keyint=25 -ofps 15 -af lavcresample=44100 -vf harddup,scale=480:320 -mc 0 "/home/yourlogin/in.flv" -o "/home/yourlogin/out.mp4"

I want H.263 video and AAC audio in MP4 container. Video should have bitrate up to 480kb/s and must be resized to 480x320 pixels, also frame rate is 15 frames per second.  For audio requested was resampling at 44.1 KHz and bit rate up to 64kb/s. It is not requirement that input video must be FLV, it should work for any container which you can find at YouTube. If your screen is different from 480x320, you may want to resize video differently. For example for 320x240 screen, vbitrate=256 and scale=320:240 should be good choice.
Not so nice as WinFF but not very complicated either.

Thursday, December 6, 2012

RESTful web service for picture upload Android client

In previous tutorial http://grumpyoldprogrammer.blogspot.com/2012/11/image-upload-via-restful-web-service.html I described how to create simple server application and deploy it on Tomcat. Now we are going to take a look at client application.
Simplest is to use Apache HttpComponents Client. It is possible to convert HttpMime jar for Android and import it as library. Since not all classes are required for picture upload we can download source and add the following files to project:

AbstractContentBody.java
ByteArrayBody.java
ContentBody.java
ContentDescriptor.java
FormBodyPart.java
Header.java
HttpMultipart.java
HttpMultipartMode.java
MIME.java
MinimalField.java
MultipartEntity.java
StringBody.java


Naturally whole project like this becomes Apache licensed. Some of those Java files are in org.apache.http.entity.mime.content package and others are in org.apache.http.entity.mime.
In this way we avoid compiling two files FileBody.java and InputStreamBody.java
Now will we go refactoring package names to fit them into project is of minor relevance, I usually do.
We create new Android project and we code onCreate like this:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    try {
        bm = BitmapFactory.decodeFile("/sdcard/DCIM/FinalM8.JPG");
        if(bm==null)
            throw new Exception("no picture!");
        new FetchItemsTask().execute();
    } catch (Exception e) {
        e.printStackTrace();
    }
}


Please note that image path is hardcoded, so change it accordingly. Bitmap bm is loaded, defined as field, and if it is different from null we create and execute AsyncTask to upload picture. To keep things simple as possible we will implement only doInBackground, like this:

protected Void doInBackground(Void... arg0) {
    // TODO Auto-generated method stub
    try {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        bm.compress(CompressFormat.JPEG, 50, bos);
        byte[] data = bos.toByteArray();
        HttpClient httpClient = new DefaultHttpClient();
        HttpPost postRequest =
            new HttpPost("http://localhost:8080/WebApplication3/xyz/generic/images");
        ByteArrayBody bab = new ByteArrayBody(data, "FinalM8.JPG");
        MultipartEntity reqEntity = new MultipartEntity(
                HttpMultipartMode.BROWSER_COMPATIBLE);
        reqEntity.addPart("image", bab);
        postRequest.setEntity(reqEntity);
        HttpResponse response = httpClient.execute(postRequest);
        System.out.println("Status is "+response.getStatusLine());
        BufferedReader reader = new BufferedReader(new InputStreamReader(
                response.getEntity().getContent(), "UTF-8"));
        String sResponse;
        StringBuilder s = new StringBuilder();
        while ((sResponse = reader.readLine()) != null) {
            s = s.append(sResponse);
        }
        System.out.println("Response: " + s);
    } catch (Exception e) {
        // handle exception here
        e.printStackTrace();
    }
    return null;
}


Image is compressed on 50%, loaded into byte array and upload is attempted. In order to run code just push onto emulator some image and change path.

Sunday, November 25, 2012

Final part of Android tutorial

Here we are going to take a look at ContentProvider, subclassing of SimpleCursorAdapter, ListView and assembling of all that into almost usable application.

ContentProvider


Most irritating concept of whole Android platform. That is some kind of grand unified interface to access all data publicly available on system. As we may expect from Internet search oriented company, there is Uri which starts with "content://" then we have “authority” and “base path”, like this:

private static final String AUTHORITY = "za.org.droidika.tutorial.SearchResultProvider";
private static final String TWEETS_BASE_PATH = "tweets";
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY
        + "/" + TWEETS_BASE_PATH);


That is not all, we also differentiate between operations on unit and bulk operations:

public static final int TWEETS = 100;
public static final int TWEET_ID = 110;
public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE
        + "vnd.org.droidika.tutorial/tweets";
public static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE
        + "vnd.org.droidika.tutorial/tweets";


Further in the source code (available from here https://github.com/FBulovic/grumpyoldprogrammer) we combine our identifiers and load into UriMatcher and we add DB mappings to HashMap. That, with help of SQLiteOpenHelper, is enough of configuration and we can finally implement CRUD methods. I will explain bulk insert and you can find out what is going on in others on you own.

public int bulkInsert(Uri uri, ContentValues[] values) {
    final SQLiteDatabase db = dbInst.getWritableDatabase();
    final int match = sURIMatcher.match(uri);
    switch(match){
    case TWEETS:
        int numInserted= 0;
        db.beginTransaction();
        try {
            SQLiteStatement insert =
                db.compileStatement("insert into " + DbHelper.TABLE_NAME
                        + "(" + DbHelper.USER + "," + DbHelper.DATE
                        + "," + DbHelper.TEXT + ")"
                        +" values " + "(?,?,?)");
            for (ContentValues value : values){
                insert.bindString(1, value.getAsString(DbHelper.USER));
                insert.bindString(2, value.getAsString(DbHelper.DATE));
                insert.bindString(3, value.getAsString(DbHelper.TEXT));
                insert.execute();
            }
            db.setTransactionSuccessful();
            numInserted = values.length;
        } finally {
            db.endTransaction();
        }
        getContext().getContentResolver().notifyChange(uri, null);
        return numInserted;
    default:
        throw new UnsupportedOperationException("unsupported uri: " + uri);
    }
}


We obtain writable instance of DB and we ask URIMatcher do we have right Uri, we do not want to attempt inserting wrong data or do bulk inserting of single row. Next we compile insert statement and open transaction. Inside for loop we assign parameters and execute inserts. If everything vent well we commit transaction and close it inside finally. At the end we ask ContentResolver to notify subscribers about new situation.
We do not pay any attention on resource management, we do not manage cursors, nothing. Suddenly SQLite is friendly and cooperative. That is probably main reason, beside ability to share data across application boundaries, why we should use providers. Naturally there is more, but it happens not in code but inside AndroidManifes.xml
Within application element we place this:

    android:name=".SearchResultProvider"
    android:authorities="za.org.droidika.tutorial.SearchResultProvider" />


Now ContentResolver knows how to find our provider.


Subclassing SimpleCursorAdapter



Since whole idea behind application was implementing something like autocompletion, we type and ListView content changes while we typing, we need custom data adapter. There is only one interesting method to implement:

public Cursor runQuery(CharSequence constraint) {
    String searchString = constraint.toString();
    if (searchString == null || searchString.length() == 0)
        c = contentResolver.query(SearchResultProvider.CONTENT_URI, null, null, null, null);
    else {
        c = contentResolver.query(SearchResultProvider.CONTENT_URI, null, DbHelper.TEXT + " like '%"
                + searchString + "%'", null, null);
    }
    if (c != null) {
        c.moveToFirst();
    }
    return c;
}


If we have search string we do like query and return cursor and if we do not have search string we return everything. Again we do not manage cursor, we just leave everything to Android and it behaves really friendly.

Assembling application


User interface is influenced with SearchableDictionary sample from Android SDK. We type in EditText on top of the screen our search string and data adapter and provider load result into ListView. In order to retrieve data we start “cron job” and it does Twitter search every three minutes and stores data into DB. MainActivity contains example how to create and use menu, how to check is network available and only nontrivial method there is this one:

private void buildList() {
    String[] columns = new String[] { DbHelper.DATE, DbHelper.TEXT,
            DbHelper.USER };
    int[] to = new int[] { R.id.textView2, R.id.textView4, R.id.textView6 };
    Cursor cursor = createCursor();
    final SearchableCursorAdapter dataAdapter = new SearchableCursorAdapter(this, R.layout.list_entry,
            cursor, columns, to);
    ListView listView = (ListView) findViewById(R.id.missingList);
    listView.setAdapter(dataAdapter);
    EditText textFilter = (EditText) findViewById(R.id.myFilter);
    textFilter.addTextChangedListener(new TextWatcher() {

        public void afterTextChanged(Editable s) {
        }

        public void beforeTextChanged(CharSequence s, int start, int count,
                int after) {
        }

        public void onTextChanged(CharSequence s, int start, int before,
                int count) {
            if (dataAdapter != null) {
                    dataAdapter.getFilter().filter(s.toString());
            }
        }
    });
    dataAdapter.setFilterQueryProvider(dataAdapter);
}


We do setup of ListView, create data adapter, assign data adapter and use TextWatcher to run queries against content provider. Not very complicated.
Again, visit repository https://github.com/FBulovic/grumpyoldprogrammer retrieve code and you have quite comprehensive example, written in such way that is easy to understand. If you are going to use it in production configure HttpClient properly, how is that done is described here http://grumpyoldprogrammer.blogspot.com/2012/10/is-it-safe.html

Saturday, October 27, 2012

Tutorial part 3

In this part we are going to take a look at AsyncTask which is light-weight framework intended to save programmers from concurrency traps and handling of Java threads.

AsyncTask

Most important thing is AsyncTask is intended to be used on UI thread, so do not try to use it elsewhere. Signature is android.os.AsyncTask and you can use them or ignore them and replace unused ones with Void. If you are planning to pass array of strings as parameters and not use progress or result than it becomes android.os.AsyncTask.
There are four commonly used and overridden methods:

protected void onPreExecute ()
protected abstract Result doInBackground (Params... params)
protected void onProgressUpdate (Progress... values)
protected void onPostExecute (Result result)


obviously only doInBacground is compulsory to override and that is only one which is not executing on UI thread. Typical implementation looks like this:

private class FetchItemsTask extends AsyncTask {
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        progressDialog.setMessage("Downloading. Please wait...");
        progressDialog.show();
    }
    @Override
    protected Void doInBackground(Void... params) {
        do some heavy I/O work here
        return null;
    }
    @Override
    protected void onPostExecute(Void result) {
        pass to UI result of work here
        progressDialog.dismiss();
    }
}


Usually it is declared as inner class inside Activity and we have instance of ProgressDialog accessible.
It is not full size threading framework, it should be used as suggested to do lengthy I/O operations. That lengthy is few seconds not few minutes. So good candidate for downloading image from Internet or pulling data from DB. Very good idea here is to go to your Android SDK Manager and to download source code and see how implementation of AsyncTask looks like. Equally good idea is to place logging at every method and log ID of current thread, so that we can be sure where is what executed.
If we take a look at my example in git://github.com/FBulovic/grumpyoldprogrammer.git we will see that my BroadcastReceiver contains  AsyncTask. Knowing that  AsyncTask can bi instantiated only from UI thread we can conclude that  BroadcastReceiver is also executed on UI thread. That was nice intro to CursorAdapter and ContentProvider where execution happens outside of UI thread but they need to communicate with UI thread and that may course some problems here and there. Especially you want to access SQLite DB directly without using ContentProvider.
Next part of this tutorial will follow, soon.

Thursday, October 25, 2012

Tutorial part 2

Since we have scheduler to retrieve data maybe we want to put data into database. On Android we have SQLite as part of environment so choice is easy.

Subclassing SQLiteOpenHelper

Things are straightforward here. There are two methods which must be implemented:

@Override
public void onCreate(SQLiteDatabase arg0) {
    arg0.execSQL(createSQL);
}
@Override
public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
    arg0.execSQL("DROP TABLE IF EXISTS "+TABLE_NAME);
    onCreate(arg0);
}


variable createSQL is String containing simple SQL statement to create table:

CREATE TABLE tweets (_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 
    user_name TEXT NOT NULL, date_name TEXT NOT NULL, 
    text_name TEXT NOT NULL)


Self-incrementing primary key _id is required by Android, to use it in data binding, must be named like this.
Another thing which is required is constructor:

public DbHelper(Context context) {
    super(context, DATABASE_NAME, null, 1);
}


Beside context we need to pass to super-class constructor DB name, optional CursorFactory and version.
Using helper looks like this:

DbHelper dbW = new DbHelper(this);
SQLiteDatabase db = dbW.getReadableDatabase();
Cursor cursor = db.query("tweets", new String[] { "_id", DbHelper.DATE,
        DbHelper.TEXT, DbHelper.USER }, null, null, null, null, null,
        null);
cursor.moveToFirst();
...
do something with cursor
...
cursor.close();


It is obviously called DbHelper, since query needs to be done readable database is enough. Query method looks odd and since we are selecting everything we just provide table name and collection of columns. Once when work with cursor is done we need to close it, SQLite is quite tough on resource managemet. Other parameters of query method are selection, order, limit, having, group by. If we want to specify selection it will be something like this:

cursor = db.query("route", new String[] { "_id",
        DbHelper.ADDRESS, DbHelper.PHONE,
        DbHelper.SURNAME }, DbHelper.STATUS + "="
        + Integer.toString(filterEquals) + " and "
        + DbHelper.ADDRESS + " like '%" + searchString + "%'",
        null, null, null, null, null);


So selection is “where”.
Instead of this pseudo OO approach raw SQL can be used:

String updateSQL = "UPDATE my_table SET complete = 4  WHERE status > 0;";
dbW.getWritableDatabase().execSQL(updateSQL);


It is certainly easier at the beginning to use normal SQL – less unknown stuff to fight with.
If we are take a look at code for this (and other) tutorial, we see that I never used directly SQL except inside provider. If you use it directly be prepared for really strict resource management and Android likes executing things on different threads what doesn't go nicely with SQLite which doesn't let you close cursors created on different tread from yours. Cure for this is to call startManagingCursor and leave to Android to close dangling cursor. Do not be discouraged with deprecation of startManagingCursor if you can't control execution it is much better than leaking resources. All that nightmare suddenly stops if you do not use SQLite DB directly but wrapped up in provider. Political approach, you are free to do what you want but we will make you use what we want you to use.
Code for this tutorial (and others) is here git://github.com/FBulovic/grumpyoldprogrammer.git
Next part of this tutorial will follow.

Tuesday, October 23, 2012

Android tutorial (multipart)

This tutorial will cover AsyncTask, SQLite, SQLiteOpenHelper, ContentProvider, AlarmManager, BroadcastReceiver, custom filterable CursorAdapter, ListView, TextWatcher, JSONArray and JSONObject.
We will actually use HttpClient but I was talking about it already. Goal of tutorial is to fetch periodically search results from Twitter, store them in DB and display them in list view. We will make those search results searchable.

Cron job Android style

Android is Linux but Cron daemon is missing, we must use  AlarmManager and  BroadcastReceiver. Simplest form of  BroadcastReceiver looks like this:

public class CronJobScheduler extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        System.out.println("inside onReceive");
    }
}


To use it from your Activity you will do something like this:

Context context = getBaseContext();
AlarmManager manager=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, CronJobScheduler.class);
PendingIntent pending = PendingIntent.getBroadcast(context, 0, intent, 0);
manager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 1000 * 20 , pending);


and at appropriate place you will also stop scheduled job, like this:

Context context = getBaseContext();
Intent intent = new Intent(context, CronJobScheduler.class);
PendingIntent pending = PendingIntent.getBroadcast(context, 0, intent, 0);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(pending);


But that is not all, in AndroidManifest.xml we must place this:



It must be within application element. If we build app and run it in emulator it will print every 20 seconds inside onReceive.
In order to do something useful (like download JSON, parse it and insert data in DB) we will use later this code:

PowerManager manager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock lock = manager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HOWZIT:)");
lock.acquire();
cr = context.getContentResolver();
new FetchItemsTask().execute();
lock.release();


In order to have this executed we will need to add some permissions to  AndroidManifest.xml, like this:



We are acquiring PowerManager.PARTIAL_WAKE_LOCK and that is CPU lock, we want CPU to be awake so that scheduled work could be done. As usual it is good idea to release CPU lock when work is done ;-)
If anybody needs code for this it is available here git://github.com/FBulovic/grumpyoldprogrammer.git there is few other things, also.
Next part of this tutorial will follow, soon.

Is it safe?

Looking at tech headlines I spotted this:

Over 1,000 Android Apps Contain SSL Flaws by Tom Brewster

If they are claiming existence of some virus or Trojan which works on Android, I would just ignore it as complete nonsense. Now after initial “this is FUD” reaction I proceeded reading and encountered the following claims:

More than 1,000 legitimate Android apps contain SSL (Secure Sockets Layer) weaknesses, leaving them vulnerable to Man-in-the-Middle (MITM) attacks, researchers have claimed.

Without naming names, the students found one “generic online banking app”, which was trusting all certificates, even the MITM proxy with a self-signed certificate set up by the researchers. It had between 100,000 and 500,000 users.


Not naming names is really nice touch, maybe more unsuspecting users can fall victim to MITM criminals.
Knowing that software companies lately relying on do idiotic test recruitment process, I started to change opinion from trolling-FUD to it is actually possible. In pursuit of margins, software companies are inclined to employ younger, inexperienced and cheaper candidates, but in the same time they will force them to work faster and be productive as more experienced programmers. At the end it hurts them (couldn't care less for them), final users and whole development platform.
Naturally FUD ingredient can't be completely omitted, is situation with security implementation on proprietary mobile operating systems better? We can mess-up SSL configuration on every kind of OS using the same HttpComponents.
So, problem is not in platform, it is difficult to imagine something safer than Linux (Android uses Linux kernel), but in incompetency of would be programmers and their would be project managers. All those C# .NET Windows developers suddenly are becoming experts for Linux and Android – crazy stuff.

Apache HttpComponents and  HttpClient

Android uses 4.x version of HttpClient and that is old and reputable library. Problem is that in pursuit of deadlines people jump on Google search and finish with some half-baked solution from “Stack Overflow”. “Stack Overflow” can not replace help files and it is not its purpose. Read the manual. If you are not sure what help file says, download source code, yes that is open source you can do it, see how they are using that class in examples and tests. That is faster than reading threads on different forums.
It is possible to configure HttpClient , something like this:

private static final DefaultHttpClient client;
private static HttpParams params = new BasicHttpParams();
static {

    HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
    HttpProtocolParams.setContentCharset(params, "utf-8");
    params.setBooleanParameter("http.protocol.expect-continue", false);
    SchemeRegistry registry = new SchemeRegistry();
    registry.register(new Scheme("http", PlainSocketFactory
            .getSocketFactory(), 80));
    final SSLSocketFactory sslSocketFactory = SSLSocketFactory
            .getSocketFactory();
    sslSocketFactory
            .setHostnameVerifier(SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
    registry.register(new Scheme("https", sslSocketFactory, 443));
    ClientConnectionManager manager = new ThreadSafeClientConnManager(
            params, registry);
    ConnManagerParams.setMaxTotalConnections(params, 20);
    ConnManagerParams.setMaxConnectionsPerRoute(params,
            new ConnPerRoute() {
                public int getMaxForRoute(HttpRoute httproute) {
                    return 10;
                }
            });
    client = new DefaultHttpClient(manager, params);
}


It doesn't have to be used in just give me defaults style. We need thread safe connection manager since we are going to use threads to check later is singleton thread safe.
If one does search on “How to ignore SSL certificate errors in Apache HttpClient 4.0”, there is nice example how not to configure HttpClient. May be used in early development stage, but releasing things like that is asking for lawsuit.
Once client is configured, one may want to provide it in organized manner to the rest of application, singleton looks like ideal storage for it.

Lazy Initialization Holder Class Idiom

This one is quite popular in Java world since appearance of book Java Concurrency in Practice by Goetz and others. It relies on Java Language Specification and JVM loading. Here is how it aproximately looks like:

public class SafeLazy {
    private static class SingletonHolder {
        private static final DefaultHttpClient client = new DefaultHttpClient();
    }

    public static DefaultHttpClient getInstance() {
        return SingletonHolder.client;
    }
}


Since outer class doesn't have static fields it will be loaded without initializing HttpClient and SingletonHolder. JVM will perform initialization of inner class only when it needs to be accessed. Naturally one may want to configure slightly that client, like in the previous code example.
Now you can avoid problems of type “if I try successive downloads it just hangs”, “I trust every certificate from everywhere” and so on.
If you need complete sample with test it is available here https://github.com/FBulovic/isitsafe.git you know how to use Git, don't you?