Philippe Khin
Simple Android application to read Lang8 webscraped posts.
5 December 2016
Just building a CRUD Android app
java
android
lang8

In the previous post, after managing to scrape all the posts from a specific user from Lang8, I've created a Python GUI desktop application to read them in a more convenient way.

So now, I'm gonna show you how to create the same application, but on Android. Start with the official tutorial for learning basics Android concept.

It's a small and simple application, but helps me a lot to improve my Japanese by daily reviewing these posts on my smartphone.

Here's how it looks like :

Pretty similar to the desktop version isn't it ?

Here's the code for the MainActivity.java :

package com.example.lang8yoo;

import android.app.Activity;
import android.content.Context;
import android.content.res.AssetManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.text.method.ScrollingMovementMethod;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Random;
import java.util.Scanner;


public class MainActivity extends Activity  {

  TextView currentPageDisplay;
  EditText pageSearchLine;
  Button pageSearchButton;

  TextView textView;

  Button previousButton;
  Button randomButton;
  Button nextButton;

  public int current_page_index;
  public int entries_total;
  public String lastReadEntryIndex_FILE = "lastReadEntryIndex.txt";
  public int last_read_entry_index;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);


        currentPageDisplay = (TextView) findViewById(R.id.currentPageDisplay);
        pageSearchLine     = (EditText) findViewById(R.id.pageSearchLine);
        pageSearchButton   = (Button) findViewById(R.id.pageSearchButton);

        textView           = (TextView) findViewById(R.id.textView);

        previousButton     = (Button) findViewById(R.id.previousButton);
        randomButton       = (Button) findViewById(R.id.randomButton);
        nextButton         = (Button) findViewById(R.id.nextButton);

        try {
            entries_total         = getAssets().list("yamasvEntries").length;
            BufferedReader br     = new BufferedReader(new
                                    InputStreamReader(openFileInput(lastReadEntryIndex_FILE)));
            last_read_entry_index = Integer.parseInt(br.readLine());
        br.close();
          updateScreenWith(last_read_entry_index);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void randomPageDisplay(View view) throws IOException {
      int random_page_index = new Random().nextInt(entries_total+1)+1;
      updateScreenWith(random_page_index);

    }


    public void nextPageDisplay(View view) throws IOException {
      int next_page_index = current_page_index < entries_total ? current_page_index + 1 : current_page_index;
      updateScreenWith(next_page_index);
    }


    public void previousPageDisplay(View view) throws IOException {
      int previous_page_index = current_page_index > 1 ? current_page_index - 1 : current_page_index;
      updateScreenWith(previous_page_index);
    }


    public void searchPage(View view) throws IOException {
      if (TextUtils.isDigitsOnly(pageSearchLine.getText())) {
        int page_to_search_index = Integer.parseInt(pageSearchLine.getText().toString());
        if (1 <= page_to_search_index && page_to_search_index <= entries_total){
          updateScreenWith(page_to_search_index);
        }
      }
    }


    public void updateScreenWith(int page_to_display_index) throws IOException {
      BufferedReader br = new BufferedReader(new InputStreamReader(
                            getAssets().open("yamasvEntries/" + page_to_display_index + ".txt")));
      String page_to_display_text = "";
      String entry_line;
      while ((entry_line = br.readLine()) != null ){
        page_to_display_text += entry_line + "\n";
      }
      br.close();

        textView.setTextSize(22);
        textView.setText(page_to_display_text);
        current_page_index = page_to_display_index;
        currentPageDisplay.setText(current_page_index+"/"+entries_total);

        // Android can't write the assets folder read-only files,
        // instead it create/write file on internal storage with openFileOutput
        FileOutputStream outputStream;
        try {
            outputStream = openFileOutput(lastReadEntryIndex_FILE, Context.MODE_PRIVATE);
            outputStream.write(Integer.toString(current_page_index).toString().getBytes());
            outputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

And here's the activity_main.xml layout main file :

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context="com.example.lang8yoo.MainActivity"
        android:descendantFocusability="beforeDescendants"
        android:focusableInTouchMode="true" >

        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"
            android:layout_alignParentBottom="true"
            android:weightSum="1">

            <LinearLayout
                android:orientation="horizontal"
                android:layout_width="match_parent"
                android:layout_height="44dp"
                android:layout_weight="0.02">
                <TextView
                    android:text="80/1520"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:id="@+id/currentPageDisplay"
                    android:layout_weight="1"
                    android:textSize="24sp" />
                <EditText
                    android:layout_width="70dp"
                    android:layout_height="wrap_content"
                    android:inputType="textPersonName"
                    android:ems="10"
                    android:id="@+id/pageSearchLine"
                    android:layout_weight="0.15" />
                <Button
                    android:text="Search"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:id="@+id/pageSearchButton"
                    android:textSize="14sp"
                    android:onClick="searchPage"/>
            </LinearLayout>

            <ScrollView
                android:layout_width="match_parent"
                android:layout_height="489dp">
                <TextView
                    android:text="Lang8 yoo"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:id="@+id/textView"
                    android:layout_weight="1.07" />
            </ScrollView>

            <LinearLayout
                android:orientation="horizontal"
                android:layout_width="match_parent"
                android:layout_height="67dp">
                <Button
                    android:text="Previous"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:id="@+id/previousButton"
                    android:layout_weight="1"
                    android:textSize="14sp"
                    android:onClick="previousPageDisplay"/>
                <Button
                    android:text="Random"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:id="@+id/randomButton"
                    android:layout_weight="1"
                    android:textSize="14sp"
                    android:onClick="randomPageDisplay"/>
                <Button
                    android:text="Next"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:id="@+id/nextButton"
                    android:layout_weight="1"
                    android:textSize="14sp"
                    android:onClick="nextPageDisplay"/>
            </LinearLayout>
        </LinearLayout>
    </RelativeLayout>

The code itself is pretty similar to the Python version one. So, I stored the 1520 scraped entries files in a folder. With Android, you have to store all your static files under a folder called assets that you have to create beforehand, which is located at /app/src/main/.

But to 'remember' the last entry number I've read, I have to store the entry index somewhere, in a file like in the desktop version, so that the next time I open the app, it's gonna read the entry index and display the corresponding entry.

But one thing to note: in Android, statics files stored in the assets folder can only be read and not writed ! So, it's not possible to write in a file in the assets folder since the APK file is not extensible in size or something like that, so you have to write in a file located on the internal storage. This is done in the last part of the code in the MainActivity.java.

Download the app

And if you are interested, here is the APK file for the simple Android application to read these entries.

All credit goes to yamasv, I post the app here with his approval :)

For the complete code, please check my GitHub repository.

© 2020, Philippe Khin