Friday 25 May 2018

Droid read & write file

In all but the most trivial applications you're going to have to read and write data to some sort of persistent storage. In this post we are going to write and write data to a file in our apps sandbox. to get started let's build our UI

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:a="http://schemas.android.com/apk/res/android"
              a:layout_width="match_parent"
              a:layout_height="match_parent"
              a:orientation="vertical">
  <EditText a:id="@+id/Input_TextBox" a:hint="input value"
            a:layout_width="fill_parent"
            a:layout_height="wrap_content"/>
  <Button a:id="@+id/Save_Button" a:text="Save"
          a:layout_width="fill_parent"
          a:layout_height="wrap_content"/>
  <Button a:id="@+id/Load_Button" a:text="Load"
          a:layout_width="fill_parent" a:layout_height="wrap_content"/>
  <ListView a:id="@+id/Inputs_ListView"
            a:layout_width ="match_parent" a:layout_height="fill_parent" />

</LinearLayout>

here we have a EditText which is just a TextBox, with two buttons, one to save data and another to load it and finally a ListView to display all of our data elements. also notice that I shortened the android namespace just to an a, this is a personal preference.

before we look at our activity code we have to make sure that we grant read and write permission to our application in the manifest.

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

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


if we where to scroll down further we'd also find the entry for "WRITE_EXTERNAL_STORAGE"

let's take a look at our activity code.

using Android.App;
using Android.OS;
using Android.Support.V7.App;
using Android.Widget;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;

namespace pav.FileAccess
{
    [Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)]
    public class MainActivity : AppCompatActivity
    {
        //refrences to view controls
        EditText InputTextBox;
        Button SaveButton;
        Button LoadButton;
        ListView InputsListView;

        //Datasource for our listview
        ArrayAdapter<string> data;

        //Andriod path for sandbox for file IO
        static readonly string PATH = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);

        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            // Set our view from the "main" layout resource
            SetContentView(Resource.Layout.activity_main);

            //reference view contorls
            InputTextBox = base.FindViewById<EditText>(Resource.Id.Input_TextBox);
            SaveButton = base.FindViewById<Button>(Resource.Id.Save_Button);
            LoadButton = base.FindViewById<Button>(Resource.Id.Load_Button);
            InputsListView = base.FindViewById<ListView>(Resource.Id.Inputs_ListView);

            //register event recievers for button clicks
            SaveButton.Click += SaveButton_Click;
            LoadButton.Click += LoadButton_Click;

            //set data for listview
            data = new ArrayAdapter<string>(this, Android.Resource.Layout.SimpleListItem1, new List<string>());

            //set listview data
            InputsListView.Adapter = data;
        }
       
        private async void LoadButton_Click(object sender, System.EventArgs e)
        {
            //concat file name with file io path
            var filePath = System.IO.Path.Combine(path, "data.txt");
            string fileData = null;

            //read file content
            using (var streamReader = new StreamReader(filePath))
                fileData = await streamReader.ReadToEndAsync();

            //set data for listview
            data = new ArrayAdapter<string>(this, Android.Resource.Layout.SimpleListItem1, new List<string>(fileData.Split(';')));

            //set listview data
            InputsListView.Adapter = data;
        }

        private async void SaveButton_Click(object sender, System.EventArgs e)
        {
            //get data input from textbox
            var input = InputTextBox.Text;
           
            if (!String.IsNullOrEmpty(input))
            {
                //add to listview
                data.Add(input);
              
                //show toast notification to user
                Toast.MakeText(this, $"added: {input}", ToastLength.Short).Show();

                //save all list data to file
                await WriteToFileAsync();

                //clear textbox
                InputTextBox.Text = string.Empty;
            }
        }

        private async Task WriteToFileAsync()
        {
            // get filepath for io
            var filePath = System.IO.Path.Combine(path, "data.txt");
           
            //use string builder for string to save
            var sb = new StringBuilder();
        
            for (int i = 0; i < data.Count; i++)
                if (i == data.Count - 1)
                    sb.Append(data.GetItem(i));
                else
                    sb.AppendFormat("{0};", data.GetItem(i));
           
            //write content of string builder to file
            using (var streamWriter = new StreamWriter(filePath, false))
               await streamWriter.WriteAsync(sb.ToString());
        }
    }
}


Notice that we create a static get property for our file io path
static readonly string PATH = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
this will be the folder where we will read and write all of our data.

also notice that on our button clicks we leveraged the async/await key words, this kept us from locking up our UI while reading and writing to file.