Saturday 7 January 2017

Xamarin Android - Display all installed apps in ListView

Hi all, few months ago I started working on Xamarin Android apps.

It takes a little time because we need to code in C# instead of java, there are few syntactical changes when compared to Native android development. Eg : Few Methods are converted to attributes and handling click events using delegates and stuff like that.

The first thing I did was to develop a sample project to read all the packages installed and display on  a custom ListView.

This way we learn :

1. Creating and accessing the views in Xamarin.
2. Creating a custom List Adapter for our Activity.
3. Writing the Activity Class and registering events.
4. Displaying a simple Toast Message.

However creating the Android Project, Deploying the apk on device is all as same as Native Android and IDE gives similar options which feels familiar. I use the Visual Studio for developing this app.

Lets Start :

Create an Activity Class MainActivty.cs

The class has following methods :

1. getInstalledApplicationsList - Reads and adds the package name to an ArrayList.

2. importInstalledAppsData - Read app name and app icon from individual packages.

3. successCallBack - Populate the Listview with the retrieved list and register On item click of the list to display the package name as a Toast message.

 using System;
using System.Collections;
using System.Collections.Generic;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Graphics.Drawables;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using PackageName.Src;

namespace PackageName
{
    [Activity(Label = "PackageName", MainLauncher = true, Icon = "@drawable/icon")]
    public class MainActivity : Activity
    {
        int count = 1;
        List<AppDao> appList = new List<AppDao>();
        ArrayList packageName = new ArrayList();
        private ListView lvApps;
        private PackageAdapter packageAdapter;
        private Context mContext;
        private ProgressDialog _progressDialog;

        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);
            mContext = this;
            // Set our view from the "main" layout resource
            SetContentView(Resource.Layout.Main);
            lvApps = FindViewById<ListView>(Resource.Id.lv_apps);

            _progressDialog = new ProgressDialog(this);
            _progressDialog.SetMessage("Fetching apps list");
            _progressDialog.SetTitle("Please wait...");
            _progressDialog.SetCancelable(false);
            _progressDialog.Show();
            new GetAppsTask(this).Execute();

        }

        public IList<ApplicationInfo> getInstalledApplicationsList(PackageManager pm)
        {
            IList<ApplicationInfo> packages = pm.GetInstalledApplications(PackageInfoFlags.MetaData);

            foreach (var value in packages)
            {
                packageName.Add(value.PackageName);
            }

            return packages;
        }

        public void importInstalledAppsData()
        {

            PackageManager pkgManager = this.ApplicationContext.PackageManager;
            IList<ApplicationInfo> packages = getInstalledApplicationsList(pkgManager);
            int count = 0;
            foreach (ApplicationInfo packageInfo in packages)
            {
                AppDao appDao = new AppDao();
                appDao.AppName = (String)pkgManager.GetApplicationLabel(packageInfo);
                appDao.AppIcon = (Drawable) pkgManager.GetApplicationIcon(packageInfo);

                appList.Add(appDao);
                count++;
            }
        }

        public void successCallBack()
        {
            packageAdapter = new PackageAdapter(this, appList);
            lvApps.Adapter = packageAdapter;

            lvApps.ItemClick += delegate(object sender, AdapterView.ItemClickEventArgs e)
            {
                Toast.MakeText(this, packageName[e.Position].ToString(), ToastLength.Short).Show();
            };
            _progressDialog.Hide();
        }

    }
}

Create an AsyncTask to Fetch all package names

The Task is called from the main activity and once completes the successCallBack of MainActivity is called

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;

namespace PackageName.Src
{
    class GetAppsTask : AsyncTask
    {
        private MainActivity _context;

        public GetAppsTask(MainActivity context)
        {
            _context = context;
        }

        protected override void OnPreExecute()
        {
            base.OnPreExecute();
            
        }

        protected override Java.Lang.Object DoInBackground(params Java.Lang.Object[] @params)
        {
            _context.importInstalledAppsData();
            return true;
        }

        protected override void OnPostExecute(Java.Lang.Object result)
        {
            base.OnPostExecute(result);
            _context.successCallBack();
            
        }
    }
}

Create an Model class

The model has two attributes app icon and app name.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.Graphics.Drawables;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;

namespace PackageName
{
    class AppDao
    {
        public string AppName { get; set; }
        public Drawable AppIcon { get; set; }
    }
}

Create an Custom List Adapter class

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Object = Java.Lang.Object;

namespace PackageName
{
    class PackageAdapter : BaseAdapter
    {

        List<AppDao> _appsList = new List<AppDao>();
        Activity _activity;

        public PackageAdapter(Activity activity, List<AppDao> appsList)
        {
            this._activity = activity;
            foreach (var value in appsList)
            {
                _appsList.Add(value);
            }
            

        }

        public override Object GetItem(int position)
        {
            return (Object) _appsList[position].ToString();
        }

        public override long GetItemId(int position)
        {
            return position;
        }

        public override View GetView(int position, View convertView, ViewGroup parent)
        {
            var view = convertView ?? _activity.LayoutInflater.Inflate(
            Resource.Layout.List_row, parent, false);
            var appName = view.FindViewById<TextView>(Resource.Id.tv_appName);
            var appImage = view.FindViewById<ImageView>(Resource.Id.iv_app);
            appName.Text = _appsList[position].AppName;
            appImage.SetImageDrawable(_appsList[position].AppIcon);

                
            return view;
        }

        public override int Count
        {
            get { return _appsList.Count; }
        }
    }
}

Create Layout files

The main layout and the row to be inflated in  listview.

Main.axml

<?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:layout_height="match_parent">
    <ListView
        android:id="@+id/lv_apps"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

List_row.axml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="#FF89C9E1"
    android:padding="8dp">
    <ImageView
        android:id="@+id/iv_app"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:padding="5dp"
        android:src="@drawable/icon"
        android:layout_alignParentLeft="true" />
    <TextView
        android:id="@+id/tv_appName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_toRightOf="@+id/iv_app"
        android:gravity="left|center"
        android:textSize="14dip"
        android:textColor="#000000"
        android:text="App Name" />
</RelativeLayout>