A sample application using internal storage

As a suitable example generally helps the understanding of a concept, let's create a quick bare bones file-updater application that utilizes internal storage. The operation of the file updater is simple. It collects text data from the user via an input field and updates a file stored in internal storage. A user can then check the text existing in that file from a view within the application. Simple, right? It's supposed to be! Create a new Android project and name it whatever you want. Just ensure that whatever name you give it reflects the purpose of the application. Once the project is created, create two new packages named base and main, within the src project package.

Add a BaseView interface to the base package, containing the following code:

package com.example.storageexamples.base

interface BaseView {

fun bindViews()

fun setupInstances()
}

You will already be familiar with view interface definitions if you read the previous chapter. If you haven't, this would be a good time to do so. In the main package, create a MainView extending the BaseView as follows:

package com.example.storageexamples.main

import com.example.storageexamples.base.BaseView

interface MainView : BaseView {

fun navigateToHome()
fun navigateToContent()
}

The file-updater application is going to have two distinct views in the form of fragments that can be shown to a user. The first view is the home view from which a user can update the content of the file, and the second a content view from which the user can read the content of the updated file.

Now, create a new empty activity within main. Name this activity MainActivity. Ensure that you make MainActivity the launcher activity. Once MainActivity is created, ensure that it extends the MainView before proceeding. Open activity_main.xml and edit it to contain the following content:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout

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="com.example.storageexamples.main.MainActivity">

<LinearLayout
android:id="@+id/ll_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"/>
</android.support.constraint.ConstraintLayout>

We added a LinearLayout view group to the root view of the layout file. This layout is going to serve as the container for the home and content fragment of the application. We now need layouts for the home and content fragments. The following is the home fragment layout (fragment_home.xml):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:paddingTop="@dimen/padding_default"
android:paddingBottom="@dimen/padding_default"
android:paddingStart="@dimen/padding_default"
android:paddingEnd="@dimen/padding_default"
android:gravity="center_horizontal"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/header_title"
android:textSize="45sp"
android:textStyle="bold"/>
<EditText
android:id="@+id/et_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_top_large"
android:hint="@string/hint_enter_text"/>
<Button
android:id="@+id/btn_submit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_default"
android:text="@string/submit"/>
<Button
android:id="@+id/btn_view_file"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/view_file"
android:background="@android:color/transparent"/>
</LinearLayout>

Before you open the design window to view how the layout translates we need to add some value resources. Your project's strings.xml file should look similar to the following (with the exception of the app_name string resource):

<resources>
<string name="app_name">Storage Examples</string>
<string name="hint_enter_text">Enter text here…</string>
<string name="submit">Update file</string>
<string name="view_file">View file</string>
<string name="header_title">FILE UPDATER</string>
</resources>

In addition, your project should have a dimens.xml file containing the following code:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="padding_default">16dp</dimen>
<dimen name="margin_default">16dp</dimen>
<dimen name="margin_top_large">64dp</dimen>
</resources>

Once you are done adding the preceding resources, you can view the layout design window of fragment_home.xml:

Now for the content fragment layout. Add a fragment_content.xml layout file to the layout resource directory containing the following code:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:padding="@dimen/padding_default"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textStyle="bold"
android:layout_marginTop="@dimen/margin_default"/>
</LinearLayout>

The layout contains a single TextView that will render the text existing in the internal storage file to the user of the application. You can check the layout design window if you want, but there will not be much to see.

We have reached the point where we need to create appropriate fragment classes to render the fragment layouts we just created. Add the HomeFragment class to MainActivity, which is as follows:

class HomeFragment : Fragment(), BaseView, View.OnClickListener {

private lateinit var layout: LinearLayout
private lateinit var tvHeader: TextView
private lateinit var etInput: EditText
private lateinit var btnSubmit: Button
private lateinit var btnViewFile: Button

private var outputStream: FileOutputStream? = null


override fun onCreateView(inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?): View {

// inflate the fragment_home.xml layout
layout = inflater.inflate(R.layout.fragment_home,
container, false) as LinearLayout
setupInstances()
bindViews()

return layout
}

override fun bindViews() {
tvHeader = layout.findViewById(R.id.tv_header)
etInput = layout.findViewById(R.id.et_input)
btnSubmit = layout.findViewById(R.id.btn_submit)
btnViewFile = layout.findViewById(R.id.btn_view_file)

btnSubmit.setOnClickListener(this)
btnViewFile.setOnClickListener(this)
}

The following method is for the instantiation of instance properties:

  override fun setupInstances() {   

Let's open a new FileOutputStream to a file named content_fileThis file is a private file stored in internal storage and as such is only accessible by your application:

    outputStream = activity?.openFileOutput("content_file",
Context.MODE_PRIVATE)
}

The following function is called to display an error to the user if an invalid input is given:

private fun showInputError() {
etInput.error = "File input cannot be empty."
etInput.requestFocus()
}

Let's write a string content with the use of file via a FileOutputStream

private fun writeFile(content: String) {
outputStream?.write(content.toByteArray())
outputStream?.close()
}

And the function below is called to clear the input in the input field:

private fun clearInput() {
etInput.setText("")
}

The following code snippet shows a success message to the user when invoked:

private fun showSaveSuccess() {
Toast.makeText(activity, "File updated successfully.",
Toast.LENGTH_LONG).show()
}

override fun onClick(view: View?) {
val id = view?.id

if (id == R.id.btn_submit) {
if (TextUtils.isEmpty(etInput.text)) {

It will display an error message if the user submits an empty value as file content input:

  showInputError()
} else {

Let's write content to the file, clear the input EditText and show a file update success message:

     writeFile(etInput.text.toString())
clearInput()
showSaveSuccess()
}
} else if (id == R.id.btn_view_file) {
// retrieve a reference to MainActivity
val mainActivity = activity as MainActivity

Let's navigate user to the content fragment and display home button on action bar to enable a user go back to the previous fragment:

      mainActivity.navigateToContent()
mainActivity.showHomeNavigation()
}
}
}

Read through the comments of HomeFragment to ensure you understand what’s going on. Now that we have added HomeFragment to MainActivity, we must also add a fragment to render the fragment_content.xml layout to a user. The following is the necessary ContentFragment class. Add this class to MainActivity:

class ContentFragment : Fragment(), BaseView {

private lateinit var layout: LinearLayout
private lateinit var tvContent: TextView

private lateinit var inputStream: FileInputStream

override fun onCreateView(inflater: LayoutInflater?,
container: ViewGroup?,
savedInstanceState: Bundle?): View {

layout = inflater?.inflate(R.layout.fragment_content,
container, false) as LinearLayout
setupInstances()
bindViews()

return layout
}

override fun onResume() {

Let's update content rendered in TextView upon resumption of fragment:

  updateContent()
super.onResume()
}

private fun updateContent() {
tvContent.text = readFile()
}

override fun bindViews() {
tvContent = layout.findViewById(R.id.tv_content)
}

override fun setupInstances() {
inputStream = activity.openFileInput("content_file")
}

The following code reads the content of file in internal storage and returns content as a string:

  private fun readFile(): String {
var c: Int
var content = ""

c = inputStream.read()

while (c != -1) {
content += Character.toString(c.toChar())
c = inputStream.read()
}

inputStream.close()

return content
}
}

The tvContent instance (the TextView displaying the file content to the user) is updated upon resumption of the ContentFragment activity. The content of the TextView is updated by setting the text of the TextView to the content read from the file with readFile(). The last thing that is necessary is the completion of MainActivity.

Your fully completed MainActivity class should look similar to the following:

package com.example.storageexamples.main

import android.aupport.v4.app.Fragment
import android.content.Context
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.*
import com.example.storageexamples.R
import com.example.storageexamples.base.BaseView
import java.io.FileInputStream
import java.io.FileOutputStream

class MainActivity : AppCompatActivity(), MainView {

private lateinit var llContainer: LinearLayout

Let's set up fragment instances:


private lateinit var homeFragment: HomeFragment
private lateinit var contentFragment: ContentFragment

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupInstances()
bindViews()
navigateToHome()
}

override fun bindViews() {
llContainer = findViewById(R.id.ll_container)
}

override fun setupInstances() {
homeFragment = HomeFragment()
contentFragment = ContentFragment()
}

private fun hideHomeNavigation() {
supportActionBar?.setDisplayHomeAsUpEnabled(false)
}

private fun showHomeNavigation() {
supportActionBar?.setDisplayHomeAsUpEnabled(true)
}

override fun navigateToHome() {
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.ll_container, homeFragment)
transaction.commit()

supportActionBar?.title = "Home"
}

override fun navigateToContent() {
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.ll_container, contentFragment)
transaction.commit()

supportActionBar?.title = "File content"
}

override fun onOptionsItemSelected(item: MenuItem?): Boolean {
val id = item?.itemId

if (id == android.R.id.home) {
navigateToHome()
hideHomeNavigation()
}

return super.onOptionsItemSelected(item)
}

class HomeFragment : Fragment(), BaseView, View.OnClickListener {

private lateinit var layout: LinearLayout
private lateinit var tvHeader: TextView
private lateinit var etInput: EditText
private lateinit var btnSubmit: Button
private lateinit var btnViewFile: Button

private lateinit var outputStream: FileOutputStream


override fun onCreateView(inflater: LayoutInflater?,
container: ViewGroup?,
savedInstanceState: Bundle?): View {

Let's create the fragment_home.xml layout:

  layout = inflater?.inflate(R.layout.fragment_home,
container, false) as LinearLayout
setupInstances()
bindViews()

return layout
}

override fun bindViews() {
tvHeader = layout.findViewById(R.id.tv_header)
etInput = layout.findViewById(R.id.et_input)
btnSubmit = layout.findViewById(R.id.btn_submit)
btnViewFile = layout.findViewById(R.id.btn_view_file)

btnSubmit.setOnClickListener(this)
btnViewFile.setOnClickListener(this)
}

//Method for the instantiation of instance properties
override fun setupInstances() {

Let's open a new FileOutputStream to a file named content_fileThis file is a private file stored in internal storage and as such is only accessible by your application:


outputStream = activity.openFileOutput("content_file",
Context.MODE_PRIVATE)
}

//Called to display an error to the user if an invalid input is given

private fun showInputError() {
etInput.error = "File input cannot be empty."
etInput.requestFocus()
}

// Writes string content to a file via a [FileOutputStream]
private fun writeFile(content: String) {
outputStream.write(content.toByteArray())
}

//Called to clear the input in the input field

private fun clearInput() {
etInput.setText("")
}

//Shows a success message to the user when invoked.

private fun showSaveSuccess() {
Toast.makeText(activity, "File updated successfully.",
Toast.LENGTH_LONG).show()
}

override fun onClick(view: View?) {
val id = view?.id

if (id == R.id.btn_submit) {

The following code snippet displays an error message if the user submits an empty value as file content input:

  if (TextUtils.isEmpty(etInput.text)) {
showInputError()
} else {
//Write content to the file, clear the input
//EditText and show a file update success message

writeFile(etInput.text.toString())
clearInput()
showSaveSuccess()
}
} else if (id == R.id.btn_view_file) {
// retrieve a reference to MainActivity
val mainActivity = activity as MainActivity

We will navigate user to the content fragment and display home button on action bar to enable a user go back to the previous fragment:


mainActivity.navigateToContent()
mainActivity.showHomeNavigation()
}
}
}

class ContentFragment : Fragment(), BaseView {

private lateinit var layout: LinearLayout
private lateinit var tvContent: TextView

private lateinit var inputStream: FileInputStream

override fun onCreateView(inflater: LayoutInflater?,
container: ViewGroup?,
savedInstanceState: Bundle?): View {

layout = inflater?.inflate(R.layout.fragment_content,
container, false) as LinearLayout
setupInstances()
bindViews()

return layout
}

Let's update content rendered in TextView upon resumption of fragment:

override fun onResume() {
updateContent()
super.onResume()
}

private fun updateContent() {
tvContent.text = readFile()
}

override fun bindViews() {
tvContent = layout.findViewById(R.id.tv_content)
}

override fun setupInstances() {
inputStream = activity.openFileInput("content_file")
}

Now, we will read the content of file in internal storage and return content as a string:

    private fun readFile(): String {
var c: Int
var content = ""

c = inputStream.read()

while (c != -1) {
content += Character.toString(c.toChar())
c = inputStream.read()
}

inputStream.close()

return content
}
}
}

Now that we have completed MainActivity, the application is ready to be run. 

Build and run the project on a device of your choice. Upon launch of the application, the home fragment of MainActivity will be presented on the device's display. Type in any content of your choice within the EditText input:

After adding content to the EditText, click the UPDATE FILE button. The internal storage file will be updated with the content provided, and you will be notified upon completion of this update with a toast message. Having updated the file, click the VIEW FILE button:

The content fragment will be displayed with its TextView containing the updated file content. Though simple, the file-updater application we just created is a good example showing how Android applications work with internal storage.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.216.27.251