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.

Thursday, 24 May 2018

Droid key-value storage

In all but the most trivial applications we need to store data between application launches the simplest form of this is to save our data as key value pares. In andriod this can be accomplished using Shared preferences.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:a="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    a:layout_width="match_parent"
    a:orientation="vertical"
    a:layout_height="match_parent">
    <EditText
        a:layout_width="match_parent"
        a:layout_height="wrap_content"
        a:id="@+id/Input_EditText" />
    <Button
        a:text="Save"
        a:layout_width="match_parent"
        a:layout_height="wrap_content"
        a:id="@+id/Save_Button" />
    <Button
        a:text="Load"
        a:layout_width="match_parent"
        a:layout_height="wrap_content"
        a:id="@+id/Load_Button" />

</LinearLayout>

The above code will simply create a simple view that will let us save and load a text value to persisted storage.

using Android.App;
using Android.Content;
using Android.OS;
using Android.Support.V7.App;
using Android.Widget;

namespace pav.DataStore.Activities
{
    [Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)]
    public class MainActivity : AppCompatActivity
    {
        EditText Input_EditText;
        ISharedPreferences Data;

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

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

            base.FindViewById<Button>(Resource.Id.Save_Button).Click += SaveButton_Click;
            base.FindViewById<Button>(Resource.Id.Load_Button).Click += LoadButton_Click;
            this.Input_EditText = base.FindViewById<EditText>(Resource.Id.Input_EditText);

            //Create a private data file to store our value key pairs in
            Data = Application.Context.GetSharedPreferences("data", FileCreationMode.Private);
        }

        private void LoadButton_Click(object sender, System.EventArgs e)
            =>Input_EditText.Text = Data.GetString("Input", null);
       
        private void SaveButton_Click(object sender, System.EventArgs e)
        {
            //transactional
            var data = Data.Edit();
            data.PutString("Input", Input_EditText.Text);
            data.Commit();
        }
    }
}


in the above we wire up the logic to save a value key pair with the key "Input" to a private file on our device

  • private: File creation mode: the default mode, where the created file can only be accessed by the calling application (or all applications sharing the same user ID).
  • WorldReadable: any package can read file
  • WorldWriteable: any package can write to it
  • EnableWriteAheadLogging: Database open flag: when set, the database is opened with write-ahead logging enabled by default.
  • Append:File creation mode: for use with Android.Content.Context.OpenFileOutput(System.String, Android.Content.FileCreationMode), if the file already exists then write data to the end of the existing file instead of erasing it.

Droid implicit activities

Explicit activities are intents that are within your package (.apk) and specifically identified, whereas implicit Activities do not specify the activity, but instead specify the operation you want completed and let's ART decide which activity to start; thing like

  • Dial Action: dial a number
  • Airplane mode action
  • View action: launch browser, show location, play music
  • Send to action: send email, text message,
  • Pick action: image, contact
  • etc

Typically implicit activities are used to launch activities outside of your package, but they can also launch activities inside your package. Implicit intents require several things to accomplish their task:

  • Action: the thing you want done
  • Data: the data you want passed to the activity
  • MIME Type: they type of information ART is going to have to operate on
  • Categories: Style of activity you want
  • Extras: more data to operate on

Not every field above is required, often times action and data are all you need.

Some actions are obvious as to which activity a user needs, for example the DialAction or ActionImageCapture, however others like the ViewAction are not and need a piece of data to help art route them appropriately.

Taking a photo

private void PhotoButton_Click(object sender, EventArgs e)
{
    var intent = new Intent();
    intent.SetAction(MediaStore.ActionImageCapture);

    if (intent.ResolveActivity(PackageManager) != null)
        base.StartActivity(intent);

}

Notice that since taking a photo is a trivial Action and all it needs to have is its action set and the activity will be correctly routed.

Showing a location is a dad more complicated.

private void GeoButton_Click(object sender, EventArgs e)
{
    var intent = new Intent();
    intent.SetAction(Intent.ActionView);
    intent.SetData(Android.Net.Uri.Parse("geo:46.5215962,6.6380333?z=17"));

    if (intent.ResolveActivity(PackageManager) != null)
        base.StartActivity(intent);

}

since the "View" action is more ambiguous it needs to be coupled with data that helps ART route your implicit activity correctly to the map activity.


Calling  a phone

private void PhoneButton_Click(object sender, EventArgs e)
{
    var intent = new Intent();
    intent.SetData(Android.Net.Uri.Parse("tel:(855) 926-2746"));

    if (intent.ResolveActivity(PackageManager) != null)
        base.StartActivity(intent);
}


again a simple action that all it needs is the data, the tel protocol lets ART know you want a dialer.

Sending an email

private void EmailButton_Click(object sender, EventArgs e)
{
    var intent = new Intent();
    intent.SetAction(Intent.ActionSendto);
    if (intent.ResolveActivity(PackageManager) != null)
    {
        intent.SetData(Android.Net.Uri.Parse("mailto:"));
        intent.PutExtra(Intent.ExtraEmail, new[] { "pawel.ciucias@fake.com" });
        intent.PutExtra(Intent.ExtraSubject, "Heyoh!!");

        base.StartActivity(intent);
    }

}

So to wrap up implicit intents set the the intent then call the start activity without specifying the activity and they allow ART to route them to the appropriate activity.

Wednesday, 23 May 2018

Droid explicit activities

You can think of activities as Views, they are more or less equivalent to Xaml pages with code-behinds; not as elegantly separated into partial classes, but in essence the same principle. Internal activities that is activities within the same package (.apk file) are made up of a axml file in the Resources->layout folder that represent the xml markup as well as activity c# classes.


Now I move my c# {name}Activity.cs files which are in essence our code behinds into their own folder with their own namespace, by default they're are located in the root of the project which doesn't sit well with me.

Activities in Android are not started directly, it's something the ART does for you. to switch to an activity you must:
  1. package your activity request inside of an intent
  2. Using the context you send the intent to the ART
  3. ART looks inside the intent and decides which intent to start for you
Intents are used to start activities and come in two flavors: implicit and explicit; explicit intents are used when you know the identity of the Activity to start.

To start an explicit activity

private void StatsBUTTON_Click(object sender, EventArgs e)
{
    var intent = new Intent(packageContext: this, type: typeof(StatsActivity));
    base.StartActivity(intent);
}

We define our intent by supplying it with our context and they type of the activity we want to start, keep in mind that activities inherit from context down in the class hierarchy which is why we can pass a reference to itself as the context.

once you navigate to you target activity, make sure is to set the content view inside your activity to the xml markup of your view that's that axml file

using Android.App;
using Android.OS;
using Android.Widget;

namespace pav.TicTacToe
{
    [Activity(Label = "StatsActivity")]
    public class StatsActivity : Activity
    {
        Button CancelBUTTON;
        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            // Set the view from the resource.layout folder
            SetContentView(Resource.Layout.activity_stats);

            CancelBUTTON = FindViewById<Button>(Resource.Id.CancelBUTTON);
            CancelBUTTON.Click +=(s,a)=> this.Finish();
        }
    }
}

notice that in my OnCreate(Bundle savedInstanceState) method after i call the base OnCreate method i set the content view to that of my axml file that contains the markup for my view. without this you'll just get a blank page.

now in many situations when navigating between activities we're going to want to pass arguments from one to the next and maybe even back, the arguments that are passed between activities must be serializable types .

one way to pass data between activities is to leverage the putExtra method inside the intent, this is the same as creating a bundle and adding value key pairs to it, but a little less verbose

void ItemClicked(object sender, EventArgs e)
{
    var intent = new Intent(this, typeof(DetailsActivity));
    intent.PutExtra("Id", 45);
    base.StartActivity(intent);
}

To retrieve values from our bundle we can extract it from our intent

protected override void OnCreate(Bundle bundle)
{
    int position = base.Intent.GetIntExtra(name:"id",defaultValue: -1);
}

This is a rather trivial pattern, a more interesting approach would be for one activity to navigate to another, then that second activity to process the request and return some sort of result code with some data.

For example let's say that we have a guess the number game, now in this game we define the number to guess on the source Activity(View), but to guess the number we go to a target Activity (View), then return our guess from the target back to the source to check if it was correct.

In our source activity we would override or just call the StartActivityForResult(intent, requestCode, bundle) method to launch our target activity. this is where we'd set the data to send via the intent or bundle parameters, and the request code that would then identify the target returning results.

then in our Target activity we would extract our extras like we normally would wire up event receivers, etc; basically go about our business as usual.

the difference in our target would be that before we call the base finish method we'd have to call the SetResult method, here we'd set our result code [Canceled, Ok, FirstUser].

finally back in our source we have to override the OnActivityResult(requestCode, result, intent) method , here we can correlate our request and response using the request code, then identify the result using the result enum and finally extract any data from our target using the extras on the intent.


Android vs Xamarin droid

Xamarin produces native cross-platform solutions written in c#, with "real" native User interfaces, it can result in anywhere between 30% to 75% cross platform code, depending on how UI vs Business logic (BL) heavy you're application is; whereas for an enterprise application that leverages Xamarin forms as well as a shared project or a Portable Class Library (PCL) you can potentially reach 95% cross-platform code.

But how does it work? Xamarin provides c# wrappers for the underlying java utility libraries that include things like data structures, Networking, and so on, there are also c# wrappers for Android specific api's, things like data storage, device features such as: gps, camera, phone and so on. Xamarin apps also have access to the Mono.net library that contains most of the .net types.


Since Xamarin leverages the Java SDK we need to have the JDK installed to compile Xamarin android applications as well as the Android SDK.

If we where developing an Android application using Eclipse, what we'd do is start with java code, we'd then use a java compiler to get java bytecode, then that javabytcode would be complied into Android Dalvik bytcode using the android dex compiler. The dex code is then packaged with the application resources such as images, sounds, etc into an application package or (.apk) file which is just really a zip, Once the package file is installed onto the Android device the Android runtime translates the dex file into native code; each of these native files then run in their own process with there their own copy of ART on the device.


Now in xamarin we follow a very similar workflow, where we write our c# code, then we compile it into IL, which sort of looks like a variant of c. That IL code is JIT complied on the device using the MonoRuntime, which is packaged into our application package (.apk) file along with any resources and .dex files.

our andriod application then executes both the Mono runtime that JIT compiles our IL code and the Android runtime that executes the native dex bytecode, Both the Mono runtime  and ART interact with the underlying device.




keep in mind that in our xamarin application we can include Java.jar files that don't have wrappers, there are two ways to accomplish this:
  • Java Native Interface (JNI): Use Reflection
  • Java Bindings Library: 
Neither technique is simple but either can be used.

Friday, 18 May 2018

Android SDK Manager

To update the android SDK and the Tool in visual studio you have to use the Android SDK Manger to access this manager go to tools->Android-> Android SDK Manger


once this is selected this will launch the android SDKs and Tools application, here you can install various platforms their code names,  version numbers, and most importantly their API levels.


now if we switch over to the tools tab we can see the various "tools" that we have install and that have pending updates.

This where we can see our sdk version, keep in mind that the sdk is backwords compatible and thus we should try and keep it up to date.

we can also leverage platform/version specific features by inspecting what platform and version our device is.

var version = Android.OS.Build.VERSION.SdkInt;

if (version >= Android.OS.BuildVersionCodes.Kitkat)
{
    // do things supported by kitkatversion
}