6 Hacker News Reader

Leerdoelen

Na het maken van deze app wordt van je verwacht dat je:

  • gegevens uit een JSONArray kunt halen met een for-lus
  • weet hoe je de methode publishProgress in de doInBackground methode van een AsyncTask klasse gebruikt en hoe je de gegevens in de UI thread gebruikt met de methode onProgressUpdate
  • vanuit je app een browser of de telefoon app kunt openen met een uri

Opdracht

Bekijk onderstaand filmpje en maak de app.

Gebruikte methodes/klassen

Onze reader haalt de artikelen van hacker news op in twee stappen. Eerst wordt een JSONArray met de id's van de artikelen opgehaald. Per id wordt vervolgens het artikel opgevraagd. Aangezien het om zo'n 500 artikelen gaat is dit een vrij tijdrovende aangelegenheid.

JSONArray

De JSONArray met id's die Hacker News levert is een array met enkelvoudige waarden. De waarden bevinden zich tussen rechte haken en worden met komma's van elkaar gescheiden:

[ 9129911, 9129199, 9127761, 9128141, 9128264, 9127792, 9129248, 9127092, 9128367, ..., 9038733 ]

Nadat je de JSONArray hebt gemaakt met de binnengehaalde string kun je de waardes één voor één opvragen met een for-lus:

JSONArray jsonArray = new JSONArray(jsonArrayString);
for (int i = 0; i < jsonArray.length() ; i++) {
    String id = jsonArray.getString(i);
}

Meestal bestaat een JSONArray uit een reeks JSONObjecten. In dat geval heeft de array de volgende structuur:

[
     {
        "type": "home",
        "number": "212 555-1234"
     },
     {
        "type": "fax",
        "number": "646 555-4567"
     }
]

Ook hier kun je met een for-lus doorheen loopen om de JSONObjecten op te halen:

JSONArray jsonArray = new JSONArray(jsonArrayString);
for (int i = 0; i < jsonArray.length() ; i++) {
    JSONObject jsonObject = jsonArray.getJSONObject(i);
}

publishProgress

Het binnenhalen van alle 500 artikelen is een langdurige operatie. Om er voor te zorgen dat de gebruiker ziet dat het proces gaande is wordt de voortgang van de achtergrond thread op de UI vertoond. Hiervoor gebruik je de methode publishProgress in de doInBackground methode. In de definitie van de klasse moet je aangeven wat voor type gegevens er worden doorgegeven:

class DownloadTaak extends AsyncTask<Void, String, Void> {
    @Override
    protected Void doInBackground(Void... params) {
        //TODO haal JSONArray met id's artikelen

        //maak een voortgangs teller
        int teller = 0;

        //publiceer voortgang
        for (int i = 0; i < jsonArray.length(); i++) {
            //TODO haal artikel mbv z'n id
            teller++;
            publishProgress("Downloaded: " + teller + " articles");
        }
    }
}

onProgressUpdate

De string die met de methode publishProgress wordt doorgegeven aan de UI thread moet worden opgevangen met de methode onProgressUpdate van de AsyncTask klasse. De doorgegeven string zit in een array met in dit geval één waarde. De string wordt op een TextView vertoond.

@Override
protected void onProgressUpdate(String... values) {
    String update = values[0];
    voortgangTextView.setText(update);
}

Een web browser openen

Tot nu toe hebben we intents gebruikt om van de ene naar de andere activity te gaan binnen de app. Dit soort intents worden explicit genoemd omdat precies wordt aangegeven welke activity wordt gestart. Een intent kan echter ook worden gebruikt om een andere app te starten, zoals de standaard browser of de telefoon app. Dit soort intents worden implicit genoemd omdat we niet weten welke app wordt gestart. Android kijkt zelf op basis van de Uri die wordt meegegeven welke app(s) geschikt is/zijn en laat de gebruiker een keuze kan maken als er meerdere mogelijkheden zijn. In het volgende codefragment wordt nadat de gebruiker op een list item heeft geklikt de url van een webpagina uit een ArrayList gehaald en gebruikt om de standaard web browser te starten.

listView = (ListView) findViewById(R.id.listView);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {

    @Override
    public void onItemClick(AdapterView parent, View view, int position, long id) {

        // haal de url uit de lijst
        String urlString = urlLijst.get(position);

        // parse hem naar een Uri
        Uri uri = Uri.parse(urlString);
        Intent browserIntent = new Intent(Intent.ACTION_VIEW, uri));
        startActivity(browserIntent);
    }
});

De telefoon app starten

Onderstaande code start de telefoon app met een gegeven telefoonnummer. De gebruiker moet in het eerste voorbeeld nog op de bel knop drukken om de actie te bevestigen. In het tweede voorbeeld wordt er meteen gebeld.

String telefoonNummer = "tel:020-1234567";
Uri telUri = Uri.parse(telefoonNummer);

// open telefoon app met gegeven nummer
Intent telefoonIntent = new Intent(Intent.ACTION_DIAL, telUri);

// gaat meteen bellen (vereist permissie)
Intent telefoonIntent = new Intent(Intent.ACTION_CALL, telUri);
startActivity(telefoonIntent);

Voor ACTION_CALL is toestemming vereist in het AndroidManifest:

<uses-permission android:name="android.permission.CALL_PHONE" />