Android offline dictionary tutorial

1. Creating the UI

UI image

Add the material dependency in build.gradle

implementation 'com.google.android.material:material:1.1.0'

1.1 MainActivity.xml

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:padding="20dp"
    android:orientation="vertical"
    android:background="#ffffff">


    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:fontFamily="@font/ubuntu_mono"
            android:text="dictionary"
            android:textSize="30sp"/>

        <com.google.android.material.tabs.TabLayout
            android:id="@+id/tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:tabIndicatorColor="@color/colorPrimaryDark"
            app:tabIndicatorFullWidth="false"/>

    </com.google.android.material.appbar.AppBarLayout>

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/vapger"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

MainActivity.java

package demo.simpledictionary;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;
import demo.simpledictionary.frags.FavFrag;
import demo.simpledictionary.frags.HomeFrag;

import android.os.Bundle;
import android.widget.Switch;

import com.google.android.material.tabs.TabLayout;

public class MainActivity extends AppCompatActivity {

    ViewPager viewPager;
    TabLayout tabLayout;

    // make dbhelper static here
    public static DbHelper dbHelper;

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

        viewPager = findViewById(R.id.vapger);
        tabLayout = findViewById(R.id.tabs);

        viewPager.setAdapter(new SectionPagerAdapter(getSupportFragmentManager()));
        tabLayout.setupWithViewPager(viewPager);

        // now we will import the external database
        dbHelper = new DbHelper(this,"sample.db",1);
        try{
            dbHelper.CheckDb();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    class SectionPagerAdapter extends FragmentPagerAdapter{

        String[] titlePage = {"Home","Fav"};

        public SectionPagerAdapter(@NonNull FragmentManager fm) {
            super(fm);
        }

        @Nullable
        @Override
        public CharSequence getPageTitle(int position) {
            return titlePage[position];
        }

        @NonNull
        @Override
        public Fragment getItem(int position) {
            switch (position){
                case 0:
                    return new HomeFrag();
                case 1:
                    return new FavFrag();
                default:
                    return new HomeFrag();
            }

        }

        @Override
        public int getCount() {
            return 2;
        }
    }

}

1.2. Home fragment.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".frags.HomeFrag">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="10dp">

        <AutoCompleteTextView
            android:id="@+id/srchbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/transparent"
            android:fontFamily="@font/ubuntu_mono"
            android:hint="search here"
            android:textSize="25sp"/>

        <ImageView
            android:id="@+id/btnmic"
            android:layout_width="20dp"
            android:layout_height="20dp"
            android:src="@drawable/microphone"
            android:layout_alignParentRight="true"
            android:layout_centerInParent="true"/>
    </RelativeLayout>

</FrameLayout>

HomeFrag.java

import android.content.Context;
import android.content.Intent;
import android.media.Image;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.ImageView;
import android.widget.Toast;

import java.util.ArrayList;

import demo.simpledictionary.AnswerAct;
import demo.simpledictionary.MainActivity;
import demo.simpledictionary.R;

public class HomeFrag extends Fragment {

    Context mcontext;
    AutoCompleteTextView searchBar;
    ImageView voicebtn;

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        this.mcontext = context;

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_home, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        searchBar = view.findViewById(R.id.srchbar);
        voicebtn = view.findViewById(R.id.btnmic);

        searchBar.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

                if(s.length() == 1){
                    searchBar.setAdapter(new ArrayAdapter<String>(
                            mcontext,android.R.layout.simple_list_item_1,
                            MainActivity.dbHelper.getEngWord(s.toString())));
                }
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });
        // now we will setup on itemclick listener

        searchBar.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                String word = (String)parent.getItemAtPosition(position);
                String getAns = MainActivity.dbHelper.GetHindWord(word);
                Intent i = new Intent(mcontext, AnswerAct.class);
                i.putExtra("engword",word);
                i.putExtra("hinword",getAns);
                startActivity(i);
            }
        });
    }
}

1.3 Favfrag.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".frags.FavFrag">


    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
        android:id="@+id/swiperef"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <ListView
            android:id="@+id/lstv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>


</LinearLayout>

FavFrag.java

import android.content.Context;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;

import java.util.ArrayList;

import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import demo.simpledictionary.FavModel;
import demo.simpledictionary.MainActivity;
import demo.simpledictionary.R;


public class FavFrag extends Fragment {

    ListView listView;

    Context mcontext;
    SwipeRefreshLayout swipeRefreshLayout;
    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        this.mcontext = context;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_fav, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        listView = view.findViewById(R.id.lstv);
        listView.setAdapter(new ListAdapter(mcontext, MainActivity.dbHelper.GetFavData()));

        swipeRefreshLayout = view.findViewById(R.id.swiperef);
        swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                // refresh the list when new word is added or deleted
                listView.setAdapter(new ListAdapter(mcontext, MainActivity.dbHelper.GetFavData()));
                swipeRefreshLayout.setRefreshing(false);
            }
        });

    }

    class ListAdapter extends BaseAdapter{
        Context mcontext;
        ArrayList<FavModel> flist;
        LayoutInflater inflater;
        public ListAdapter(Context cont, ArrayList<FavModel> list) {
            this.mcontext = cont;
            this.flist = list;
            inflater = LayoutInflater.from(mcontext);
        }

        @Override
        public int getCount() {
            return flist.size();
        }

        @Override
        public Object getItem(int position) {
            return position;
        }

        @Override
        public long getItemId(int position) {
            return 0;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View view = inflater.inflate(R.layout.fav_list_layout,null,false);

            TextView txt1 = view.findViewById(R.id.txteng);
            TextView txt2 = view.findViewById(R.id.txthin);

            txt2.setText(flist.get(position).getMean());
            txt1.setText(flist.get(position).getWord());
            return view;
        }
    }
}

FavModel.java. Class

public class FavModel {
    String word;
    String mean;

    public FavModel(String word, String mean) {
        this.word = word;
        this.mean = mean;
    }

    public String getWord() {
        return word;
    }

    public void setWord(String word) {
        this.word = word;
    }

    public String getMean() {
        return mean;
    }

    public void setMean(String mean) {
        this.mean = mean;
    }
}

fav_list_layout.xml layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="3dp">

    <TextView
        android:id="@+id/txteng"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:textSize="20sp"/>

    <TextView
        android:id="@+id/txthin"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:textSize="20sp"/>

</LinearLayout>

2. DbHelper.java

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;

import androidx.annotation.Nullable;

public class DbHelper extends SQLiteOpenHelper {

    Context mcontext;
    String dbname, dbpath;

    public DbHelper(@Nullable Context context, @Nullable String name, int version) {
        super(context, name, null, version);
        this.mcontext = context;
        this.dbname = name;
        dbpath = "/data/data/" + "demo.simpledictionary" + "/databases/";

    }

    @Override
    public void onCreate(SQLiteDatabase db) {

    }

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

    }

    public void CheckDb(){
        SQLiteDatabase checkDb = null;

        try{
            checkDb = SQLiteDatabase.openDatabase(dbpath + dbname,null,0);
        }catch (Exception e){
            e.printStackTrace();
        }
        if(checkDb != null){
            // database already exists
            Log.d("qwerty","database already exists");
            Log.d("qwerty","Opening database");
            OpenDatabase();
        }else{
            // copy database from assets folder to app storage
            Log.d("qwerty","Copying Database");

            CopyDatabse();
        }
    }

    public void CopyDatabse(){
        this.getReadableDatabase();

        try{
            InputStream ios = mcontext.getAssets().open("sample.db"); // pass database file name here
            OutputStream os = new FileOutputStream(dbpath + dbname);

            byte[] buffer = new byte[1024];
            int len;
            while((len = ios.read(buffer)) > 0){
                os.write(buffer,0,len);
            }
            os.close();
            os.flush();
            ios.close();
        }catch (Exception e){
            e.printStackTrace();
        }
        Log.d("qwerty","database copied");
        // you can check the database from device file explorer
        // go to data > data > package name > databases
        // databse created successfully

    }

    public void OpenDatabase(){
        SQLiteDatabase sqLiteDatabase = SQLiteDatabase.openDatabase(dbpath + dbname,null,0);
    }
    // now we will setup search bar

    public ArrayList<String> getEngWord(String query){
        ArrayList<String> arr = new ArrayList<>();
        SQLiteDatabase sqLiteDatabase = this.getReadableDatabase();
        String columnName = "_from";
        try{
            Cursor cursor = sqLiteDatabase.query(
                    "primary_word",
                    new String[]{columnName},
                    columnName + " LIKE ?",
                    new String[]{query + "%"},
                    null,null,columnName
            );

            int index = cursor.getColumnIndex(columnName);

            while(cursor.moveToNext()){
                arr.add(cursor.getString(index));
            }
            sqLiteDatabase.close();
            cursor.close();
        }catch (Exception e){
            e.printStackTrace();
        }
        return arr;
    }

    public String GetHindWord(String engQuery){
        SQLiteDatabase sqd = this.getReadableDatabase();
        Cursor cursor = sqd.rawQuery("SELECT * FROM primary_word WHERE _from = '"+engQuery+"'",null);

        String ans = "";
        while (cursor.moveToNext()){
            ans = cursor.getString(cursor.getColumnIndex("_to"));
        }
        return ans;
    }

    // to get list of words saved in fav table
    public ArrayList<String> getFavList(){
        SQLiteDatabase sqd = this.getReadableDatabase();
        ArrayList<String> favList = new ArrayList<>();
        Cursor cursor = sqd.rawQuery("SELECT * FROM fav",null);

        while (cursor.moveToNext()){
            favList.add(cursor.getString(cursor.getColumnIndex("word")));
        }
        return favList;
    }

    public ArrayList<FavModel> GetFavData(){
        SQLiteDatabase sqd = this.getReadableDatabase();
        String word, mean;
        ArrayList<FavModel> list = new ArrayList<>();

        Cursor cursor = sqd.rawQuery("SELECT * FROM fav",null);
        while (cursor.moveToNext()){
            word = cursor.getString(cursor.getColumnIndex("word"));
            mean = cursor.getString(cursor.getColumnIndex("mean"));

            list.add(new FavModel(word,mean));
        }
        sqd.close();
        cursor.close();
        return list;
    }
    // to save word in fav table
    public boolean SaveFavWord(String engword,String hinword){
        SQLiteDatabase sqd = this.getWritableDatabase();
        ContentValues cv = new ContentValues();
        cv.put("word",engword);
        cv.put("mean",hinword);

        // word && mean is column name in fav table

        long res = sqd.insert("fav",null,cv);
        if(res == -1) return false;

        sqd.close();
        return true;
    }
    public  void deleteFav(String word){
        SQLiteDatabase sqd = this.getWritableDatabase();

        sqd.execSQL("DELETE FROM fav WHERE word = '"+word+"'");
    }
}

3. AnswerActivity

3.1. activity_answer.xml;

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".AnswerAct"
    android:padding="10dp"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp">

        <ImageView
            android:layout_width="25dp"
            android:layout_height="25dp"
            android:src="@drawable/backarrow"
            android:layout_centerInParent="true"
            android:layout_alignParentLeft="true"
            android:tint="@color/colorPrimaryDark"
            android:onClick="btnback"
            android:background="@drawable/ripple"
            />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="search"
            android:textSize="25sp"/>
    </RelativeLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_marginTop="20dp">

        <TextView
            android:id="@+id/txtengword"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:fontFamily="@font/ubuntu"
            android:text="engwords"
            android:textSize="30sp" />

        <ImageView
            android:id="@+id/btnspeak"
            android:layout_width="25sp"
            android:layout_height="25sp"
            android:src="@drawable/speaker"
            android:layout_marginRight="15dp"
            android:tint="#424242"
            android:layout_gravity="center"
            android:background="@drawable/ripple"/>

        <ImageView
            android:id="@+id/btnsave"
            android:layout_width="25sp"
            android:layout_height="25sp"
            android:layout_marginRight="10dp"
            android:src="@drawable/bookmark"
            android:layout_gravity="center"
            android:background="@drawable/ripple"/>
    </LinearLayout>


    <TextView
        android:id="@+id/txthinword"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:fontFamily="@font/ubuntu"
        android:text="hinword"
        android:textSize="30sp" />

</LinearLayout>

3.2 AnswerAct.java

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;

public class AnswerAct extends AppCompatActivity {

    TextView engtxt, anstxt;
    ImageView savebtn, speakbtn;

    String engWord, hinWord;

    boolean wordSaved;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_answer);

        engtxt = findViewById(R.id.txtengword);
        anstxt = findViewById(R.id.txthinword);

        savebtn = findViewById(R.id.btnsave);
        speakbtn = findViewById(R.id.btnspeak);

        engWord = getIntent().getStringExtra("engword");
        hinWord = getIntent().getStringExtra("hinword");

        savebtn.setOnClickListener(SaveWords);
        checkFavWord();
        setData();
    }

    public void btnback(View view){
        finish();
    }
    public void setData(){
        if(!hinWord.isEmpty()){
            engtxt.setText(engWord);
            anstxt.setText(hinWord);
        }
    }

    private View.OnClickListener SaveWords = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // to save save word in db || delete word from databse if already saved
            // calling SaveFavword methods of dnhelper class
            if(wordSaved){
                // delete word from table
                MainActivity.dbHelper.deleteFav(engWord);
                Toast.makeText(AnswerAct.this, "word deleted", Toast.LENGTH_SHORT).show();
                savebtn.setImageResource(R.drawable.bookmark);
                wordSaved = false;
            }else {
                boolean saved = MainActivity.dbHelper.SaveFavWord(engWord,hinWord);
                if(saved) {
                    Toast.makeText(AnswerAct.this, "Word saved", Toast.LENGTH_SHORT).show();

                    // to change the fav button img
                    savebtn.setImageResource(R.drawable.bksaved);
                    wordSaved = true;
                }
                else Toast.makeText(AnswerAct.this, "Word not saved", Toast.LENGTH_SHORT).show();
            }
            }

    };

    public boolean checkFavWord(){
        ArrayList<String> lst = MainActivity.dbHelper.getFavList();
        if(lst.contains(engWord)){
            // it mean word is already in favlist
            // change in bookmark btn
            savebtn.setImageResource(R.drawable.bksaved);
            return true;
        }else {
            savebtn.setImageResource(R.drawable.bookmark);
            return false;
        }
    }

}

1 thought on “Android offline dictionary tutorial”

  1. please can i get the database file you used to develop android offline dictionary app? because am not able to handle characters like hindi letters,in my db. perhaps i get hint from your database.

    or can you tell me how ,you inserted the hindi letters into your database?

    Reply

Leave a Comment