gui: Convert UI to use Fragments

Change-Id: I7269604a1da72e6b22f4bbac31c1f5561660ccf5
Refs: #2646, #2667
diff --git a/app/build.gradle b/app/build.gradle
index 674060e..86267a6 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -3,7 +3,7 @@
 apply plugin: 'com.android.application'
 
 android {
-    compileSdkVersion 19
+    compileSdkVersion 21
     buildToolsVersion "21.1.2"
 
     defaultConfig {
@@ -130,7 +130,7 @@
 
 dependencies {
     compile fileTree(dir: 'libs', include: ['*.jar'])
-    compile 'com.android.support:appcompat-v7:20.0.0'
+    compile 'com.android.support:appcompat-v7:21.0.3'
     compile 'com.android.support:support-v4:21.0.3'
 
     compile 'net.named-data:jndn:0.4'
@@ -141,5 +141,4 @@
     compile 'com.google.protobuf:protobuf-java:2.6.1'
 
     compile 'joda-time:joda-time:2.7'
-    compile 'commons-lang:commons-lang:2.6'
 }
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e28c78b..8ce237c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -20,22 +20,6 @@
             </intent-filter>
         </activity>
 
-        <activity
-            android:name=".LogcatSettingsActivity"
-            android:label="@string/nfd_log_settings" />
-
-        <activity
-            android:name=".LogcatActivity"
-            android:label="@string/nfd_logger" />
-
-        <activity
-            android:name=".FaceListActivity"
-            android:label="Faces" />
-
-        <activity
-            android:name=".RouteListActivity"
-            android:label="Routes" />
-
         <service
             android:name=".service.NfdService"
             android:process="net.named_data.nfd.service.NfdService"
diff --git a/app/src/main/java/net/named_data/nfd/DrawerFragment.java b/app/src/main/java/net/named_data/nfd/DrawerFragment.java
new file mode 100644
index 0000000..8190941
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/DrawerFragment.java
@@ -0,0 +1,527 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015 Regents of the University of California
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ *
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.preference.PreferenceManager;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v4.widget.ViewDragHelper;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.ActionBarActivity;
+import android.support.v7.app.ActionBarDrawerToggle;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+/**
+ * DrawerFragment that provides navigation for MainActivity.
+ */
+public class DrawerFragment extends Fragment {
+
+  public static DrawerFragment
+  newInstance(ArrayList<DrawerFragment.DrawerItem> items) {
+    Bundle drawerParams = new Bundle();
+    drawerParams.putParcelableArrayList(DrawerFragment.BUNDLE_PARAMETERS, items);
+
+    DrawerFragment fragment = new DrawerFragment();
+    fragment.setArguments(drawerParams);
+    return fragment;
+  }
+
+  @Override
+  public void onAttach(Activity activity) {
+    super.onAttach(activity);
+    try {
+      // Register callback
+      m_callbacks = (DrawerCallbacks)activity;
+    } catch (ClassCastException e) {
+      throw new ClassCastException("Host activity must implement DrawerFragment.DrawerCallbacks.");
+    }
+  }
+
+  @Override
+  public void onDetach() {
+    super.onDetach();
+    m_callbacks = null;
+  }
+
+  @Override
+  public void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+
+    // Read in the flag indicating whether or not the user has demonstrated awareness of the
+    // drawer. See PREF_DRAWER_SHOWN_TO_USER_FOR_THE_FIRST_TIME for details.
+    SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
+    m_hasUserSeenDrawer = sp.getBoolean(PREF_DRAWER_SHOWN_TO_USER_FOR_THE_FIRST_TIME, false);
+
+    if (savedInstanceState != null) {
+      m_drawerSelectedPosition = savedInstanceState.getInt(DRAWER_SELECTED_POSITION_BUNDLE_KEY);
+      m_restoredFromSavedInstanceState = true;
+    }
+
+    m_drawerItems = getArguments().getParcelableArrayList(BUNDLE_PARAMETERS);
+  }
+
+  @Override
+  public View onCreateView(LayoutInflater inflater,
+                           ViewGroup container,
+                           Bundle savedInstanceState)
+  {
+    m_drawerListView = (ListView)inflater.inflate(
+      R.layout.activity_main_drawer_listview, container, false);
+
+    m_drawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+      @Override
+      public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+        // Update UI
+        updateSelection(position);
+      }
+    });
+
+    m_drawerListView.setAdapter(new DrawerListAdapter(getActionBar().getThemedContext(), m_drawerItems));
+    m_drawerListView.setItemChecked(m_drawerSelectedPosition, true);
+
+    return m_drawerListView;
+  }
+
+  @Override
+  public void onActivityCreated(@Nullable Bundle savedInstanceState) {
+    super.onActivityCreated(savedInstanceState);
+    // Fragment influences action bar
+    setHasOptionsMenu(true);
+
+    // Initialize and set up the navigation drawer UI
+    initializeDrawerFragment(getActivity().findViewById(R.id.navigation_drawer),
+                             (DrawerLayout)getActivity().findViewById(R.id.drawer_layout));
+
+    if (savedInstanceState == null) {
+      // when restoring (e.g., after rotation), rely on system to restore previous state of
+      // fragments
+      updateSelection(m_drawerSelectedPosition);
+    }
+  }
+
+  /**
+   * Initialize drawer fragment after being attached to the host activity.
+   *
+   * @param drawerFragmentViewContainer View container that holds the navigation drawer
+   * @param drawerLayout DrawerLayout of the drawer in the host Activity
+   */
+  private void initializeDrawerFragment(View drawerFragmentViewContainer,
+                                        DrawerLayout drawerLayout)
+  {
+    m_drawerFragmentViewContainer = drawerFragmentViewContainer;
+    m_drawerLayout = drawerLayout;
+
+    // Setup drawer and action bar
+    ActionBar actionBar = getActionBar();
+    actionBar.setDisplayHomeAsUpEnabled(true);
+    actionBar.setHomeButtonEnabled(true);
+
+    m_drawerToggle = new ActionBarDrawerToggle(
+        getActivity(),
+        m_drawerLayout,
+        R.string.accessibility_open_drawer,
+        R.string.accessibility_close_drawer)
+    {
+      @Override
+      public void onDrawerClosed(View drawerView) {
+        super.onDrawerClosed(drawerView);
+        if (!isAdded()) {
+          return;
+        }
+
+        // Allow update calls to onCreateOptionsMenu() and
+        // onPrepareOptionsMenu() to update Menu UI.
+        getActivity().supportInvalidateOptionsMenu();
+      }
+
+      @Override
+      public void onDrawerOpened(View drawerView) {
+        super.onDrawerOpened(drawerView);
+        if (!isAdded()) {
+          // Drawer has not been added; Nothing to de here
+          return;
+        }
+
+        // Flag that user has seen drawer for the first time
+        if (!m_hasUserSeenDrawer) {
+          m_hasUserSeenDrawer = true;
+          SharedPreferences sp = PreferenceManager
+              .getDefaultSharedPreferences(getActivity());
+
+          sp.edit()
+            .putBoolean(PREF_DRAWER_SHOWN_TO_USER_FOR_THE_FIRST_TIME, true)
+            .apply();
+        }
+
+        // Allow update calls to onCreateOptionsMenu() and
+        // onPrepareOptionsMenu() to update Menu UI
+        getActivity().supportInvalidateOptionsMenu();
+      }
+
+      @Override
+      public void onDrawerStateChanged(int newState) {
+        super.onDrawerStateChanged(newState);
+        switch (newState) {
+          case ViewDragHelper.STATE_DRAGGING:
+            // Fall through; Same condition.
+          case ViewDragHelper.STATE_SETTLING:
+            if (m_drawerCurrentState == DRAWER_CLOSED) {
+                m_shouldHideOptionsMenu = true;
+                m_drawerCurrentState = DRAWER_SLIDING;
+                getActivity().supportInvalidateOptionsMenu();
+            }
+            break;
+          case ViewDragHelper.STATE_IDLE:
+            if (m_drawerCurrentState == DRAWER_SLIDING) {
+              m_drawerCurrentState = DRAWER_OPENED;
+              m_shouldHideOptionsMenu = false;
+              getActivity().supportInvalidateOptionsMenu();
+            } else if (m_drawerCurrentState == DRAWER_OPENED) {
+              m_drawerCurrentState = DRAWER_CLOSED;
+            }
+            break;
+        }
+      }
+    };
+
+    // Open drawer for the first time
+    if (!m_hasUserSeenDrawer && !m_restoredFromSavedInstanceState) {
+      m_shouldHideOptionsMenu = true;
+      m_drawerCurrentState = DRAWER_OPENED;
+      m_drawerLayout.openDrawer(m_drawerFragmentViewContainer);
+    }
+
+    // Post to drawer's handler to update UI State
+    m_drawerLayout.post(new Runnable() {
+      @Override
+      public void run() {
+        m_drawerToggle.syncState();
+      }
+    });
+
+    m_drawerLayout.setDrawerListener(m_drawerToggle);
+  }
+
+  @Override
+  public void onSaveInstanceState(Bundle outState) {
+    super.onSaveInstanceState(outState);
+    outState.putInt(DRAWER_SELECTED_POSITION_BUNDLE_KEY, m_drawerSelectedPosition);
+  }
+
+  @Override
+  public void onConfigurationChanged(Configuration newConfig) {
+    super.onConfigurationChanged(newConfig);
+    // Forward the new configuration the drawer toggle component.
+    m_drawerToggle.onConfigurationChanged(newConfig);
+  }
+
+  @Override
+  public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+    super.onCreateOptionsMenu(menu, inflater);
+    // Update menu UI when the drawer is open. This gives the user a better
+    // contextual perception of the application.
+    if (m_drawerLayout != null && isDrawerOpen()) {
+      // Inflate drawer specific menu here (if any)
+      showGlobalContextActionBar();
+    }
+
+  }
+
+  @Override
+  public void onPrepareOptionsMenu(Menu menu) {
+    super.onPrepareOptionsMenu(menu);
+    // Remove option menu items when drawer is sliding out
+    if (m_shouldHideOptionsMenu) {
+      for (int i = 0; i < menu.size(); i++) {
+        menu.getItem(i).setVisible(false);
+      }
+    }
+
+  }
+
+  @Override
+  public boolean onOptionsItemSelected(MenuItem item) {
+    // Handle drawer selection events
+    if (m_drawerToggle.onOptionsItemSelected(item)) {
+      return true;
+    }
+
+    // Handle other menu items
+    switch (item.getItemId()) {
+    // Handle activity menu item here (if any)
+    default:
+      return super.onOptionsItemSelected(item);
+    }
+  }
+
+  /**
+   * Convenience method that updates the UI and callbacks the host activity.
+   *
+   * @param position Position of the selected item within the Drawer's ListView.
+   */
+  private void updateSelection(int position) {
+    // Update Position
+    m_drawerSelectedPosition = position;
+
+    // Update UI of selected position
+    if (m_drawerListView != null) {
+      m_drawerListView.setItemChecked(position, true);
+    }
+
+    // Close drawer
+    if (m_drawerLayout != null) {
+      m_drawerLayout.closeDrawer(m_drawerFragmentViewContainer);
+    }
+
+    // Invoke host activity callback
+    if (m_callbacks != null) {
+      DrawerItem item = m_drawerItems.get(position);
+      m_callbacks.onDrawerItemSelected(item.getItemCode(), item.getItemName());
+    }
+  }
+
+  /**
+   * Safe convenience method to determine if drawer is open.
+   *
+   * @return True if drawer is present and in an open state; false otherwise
+   */
+  public boolean isDrawerOpen() {
+    return m_drawerLayout != null && m_drawerLayout.isDrawerOpen(m_drawerFragmentViewContainer);
+  }
+
+  /**
+   * Convenience method to update host activity action bar so that the user is informed of
+   * the app's "current context" of the fragment.
+   */
+  private void showGlobalContextActionBar() {
+    ActionBar actionBar = getActionBar();
+    actionBar.setDisplayShowTitleEnabled(true);
+    actionBar.setTitle(R.string.app_name);
+  }
+
+  /**
+   * Convenience method to get host activity's ActionBar. This makes for easy updates
+   * in a single location when upgrading to use >= HONEYCOMB (API 11) ActionBar.
+   *
+   * @return Host activity's ActionBar.
+   */
+  private ActionBar getActionBar() {
+    return ((ActionBarActivity)getActivity()).getSupportActionBar();
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
+
+  /**
+   * DrawerItem represents a single selection option in the Drawer, complete
+   * with the ability to set a Drawable resource icon for display along
+   * with the drawer item name.
+   */
+  public static class DrawerItem implements Parcelable {
+    @Override
+    public int describeContents()
+    {
+      return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int i)
+    {
+      parcel.writeInt(m_itemNameId);
+      parcel.writeInt(m_iconResId);
+      parcel.writeInt(m_itemCode);
+    }
+
+    public static final Parcelable.Creator<DrawerItem> CREATOR = new Parcelable.Creator<DrawerItem>() {
+      public DrawerItem
+      createFromParcel(Parcel in) {
+        return new DrawerItem(in.readInt(), in.readInt(), in.readInt());
+      }
+
+      public DrawerItem[] newArray(int size) {
+        return new DrawerItem[size];
+      }
+    };
+
+    public
+    DrawerItem(int itemNameId, int resIconId, int itemCode) {
+      m_itemNameId = itemNameId;
+      m_iconResId = resIconId;
+      m_itemCode = itemCode;
+    }
+
+    public int
+    getItemName() {
+      return m_itemNameId;
+    }
+
+    public int
+    getIconResId() {
+      return m_iconResId;
+    }
+
+    public int
+    getItemCode() {
+      return m_itemCode;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    /** Drawer item name */
+    private final int m_itemNameId;
+
+    /** Resource ID of a drawable to be shown as the item's icon */
+    private final int m_iconResId;
+
+    /** Item code for feedback to the host activity's implemented callback. */
+    private final int m_itemCode;
+
+  }
+
+  /**
+   * Customized DrawerListAdapter to furnishes the Drawer with DrawerItem
+   * information.
+   */
+  private static class DrawerListAdapter extends ArrayAdapter<DrawerItem> {
+
+    public DrawerListAdapter(Context context, ArrayList<DrawerItem> drawerItems) {
+      super(context, 0, drawerItems);
+      m_layoutInflater =
+          (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+      m_resources = context.getResources();
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+      DrawerItemHolder holder;
+
+      if (convertView == null) {
+        holder = new DrawerItemHolder();
+
+        convertView = m_layoutInflater.inflate(R.layout.list_item_drawer_item, null);
+        convertView.setTag(holder);
+
+        holder.m_icon = (ImageView) convertView.findViewById(R.id.drawer_item_icon);
+        holder.m_text = (TextView) convertView.findViewById(R.id.drawer_item_text);
+      } else {
+        holder = (DrawerItemHolder)convertView.getTag();
+      }
+
+      // Update items in holder
+      DrawerItem item = getItem(position);
+      if (item.getIconResId() != 0) {
+        holder.m_icon.setImageDrawable(m_resources.getDrawable(item.getIconResId()));
+      }
+      holder.m_text.setText(item.getItemName());
+
+      return convertView;
+    }
+
+    private static class DrawerItemHolder {
+      private ImageView m_icon;
+      private TextView m_text;
+    }
+
+    /** Layout inflater for use */
+    private final LayoutInflater m_layoutInflater;
+
+    /** Reference to get context's resources */
+    private final Resources m_resources;
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
+
+  /** Callback that host activity must implement */
+  public static interface DrawerCallbacks {
+    /** Callback to host activity when a drawer item is selected */
+    void onDrawerItemSelected(int itemCode, int itemNameId);
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
+
+  /** SharedPreference: Display drawer when drawer loads for the very first time */
+  private static final String PREF_DRAWER_SHOWN_TO_USER_FOR_THE_FIRST_TIME
+      = "DRAWER_PRESENTED_TO_USER_ON_FIRST_LOAD";
+
+  /** Bundle key used to (re)store position of selected drawer item */
+  private static final String DRAWER_SELECTED_POSITION_BUNDLE_KEY
+      = "DRAWER_SELECTED_POSITION";
+
+  /** Bundle argument key for bundle parameters */
+  private static final String BUNDLE_PARAMETERS = "net.named_data.nfd.drawer_fragment_parameters";
+
+  /** Callback to parent activity */
+  private DrawerCallbacks m_callbacks;
+
+  /** DrawerToggle for interacting with drawer and action bar app icon */
+  private ActionBarDrawerToggle m_drawerToggle;
+
+  /** Reference to DrawerLayout fragment in host activity */
+  private DrawerLayout m_drawerLayout;
+
+  /** Reference to drawer's ListView */
+  private ListView m_drawerListView;
+
+  /** Drawer's fragment container in the host activity */
+  private View m_drawerFragmentViewContainer;
+
+  /** Current position of the Drawer's selection */
+  private int m_drawerSelectedPosition = 0;
+
+  /** Flag that denotes if the fragment is restored from an instance state */
+  private boolean m_restoredFromSavedInstanceState;
+
+  /** Flag that denotes if the user has seen the Drawer when the app loads for the first time */
+  private boolean m_hasUserSeenDrawer;
+
+  /** ArrayList of DrawerItems to be displayed in the Drawer */
+  private ArrayList<DrawerItem> m_drawerItems;
+
+  /** Flag that marks if drawer is sliding outwards and being displayed */
+  private boolean m_shouldHideOptionsMenu;
+
+  /** Keeping track of the drawer state */
+  private int m_drawerCurrentState = DRAWER_CLOSED;
+
+  /** Drawer State Values that are used by m_drawerCurrentState */
+  private static final int DRAWER_SLIDING = 1;
+  private static final int DRAWER_CLOSED  = 2;
+  private static final int DRAWER_OPENED  = 3;
+}
diff --git a/app/src/main/java/net/named_data/nfd/FaceCreateDialog.java b/app/src/main/java/net/named_data/nfd/FaceCreateDialog.java
deleted file mode 100644
index 0020f59..0000000
--- a/app/src/main/java/net/named_data/nfd/FaceCreateDialog.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2015 Regents of the University of California
- *
- * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
- * See AUTHORS.md for complete list of NFD Android authors and contributors.
- *
- * NFD Android is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NFD Android, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.named_data.nfd;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.WindowManager;
-import android.widget.EditText;
-
-import net.named_data.jndn_xx.util.FaceUri;
-import net.named_data.nfd.utils.Nfdc;
-import net.named_data.nfd.utils.NfdcAsyncTask;
-
-public class FaceCreateDialog extends DialogFragment
-{
-  @Override
-  public Dialog
-  onCreateDialog(Bundle savedInstanceState) {
-    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
-    LayoutInflater inflater = getActivity().getLayoutInflater();
-    builder
-      .setView(inflater.inflate(R.layout.create_face, null))
-      .setPositiveButton("Create face", new DialogInterface.OnClickListener() {
-          @Override
-          public void onClick(DialogInterface dialog, int id)
-          {
-            EditText uriBox = (EditText) getDialog().findViewById(R.id.faceUri);
-            final String uri = uriBox.getText().toString();
-            new NfdcAsyncTask(getActivity(),
-                              new NfdcAsyncTask.Task() {
-                                public String
-                                runTask() throws Exception
-                                {
-                                  try {
-                                    Nfdc nfdc = new Nfdc();
-                                    int faceId = nfdc.faceCreate(m_uri);
-                                    nfdc.shutdown();
-                                    return "OK. Face id: " + String.valueOf(faceId);
-                                  } catch (FaceUri.CanonizeError e) {
-                                    return "Error creating face (" + e.getMessage() + ")";
-                                  } catch (FaceUri.Error e) {
-                                    return "Error creating face (" + e.getMessage() + ")";
-                                  }
-                                }
-
-                                ///////////////////////////
-                                private String m_uri = uri;
-                              }).execute();
-          }
-        })
-      .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
-          public void onClick(DialogInterface dialog, int id)
-          {
-            FaceCreateDialog.this.getDialog().cancel();
-          }
-        })
-    ;
-
-    Dialog faceCreateDialog = builder.create();
-    faceCreateDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
-    return faceCreateDialog;
-  }
-}
diff --git a/app/src/main/java/net/named_data/nfd/FaceCreateDialogFragment.java b/app/src/main/java/net/named_data/nfd/FaceCreateDialogFragment.java
new file mode 100644
index 0000000..2013129
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/FaceCreateDialogFragment.java
@@ -0,0 +1,72 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015 Regents of the University of California
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ *
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.DialogFragment;
+import android.view.LayoutInflater;
+import android.view.WindowManager;
+import android.widget.EditText;
+
+public class FaceCreateDialogFragment extends DialogFragment {
+  public static interface OnFaceCreateRequested {
+    public void
+    createFace(String faceUri);
+  }
+
+  public static FaceCreateDialogFragment
+  newInstance() {
+    return new FaceCreateDialogFragment();
+  }
+
+  @NonNull @Override
+  public Dialog
+  onCreateDialog(@Nullable Bundle savedInstanceState) {
+    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+    LayoutInflater inflater = getActivity().getLayoutInflater();
+    builder
+      .setView(inflater.inflate(R.layout.create_face, null))
+      .setPositiveButton(R.string.face_add_dialog_create_face, new DialogInterface.OnClickListener() {
+          @Override
+          public void onClick(DialogInterface dialog, int id)
+          {
+            EditText uriBox = (EditText) getDialog().findViewById(R.id.faceUri);
+            String uri = uriBox.getText().toString();
+            ((OnFaceCreateRequested)getTargetFragment()).createFace(uri);
+          }
+        })
+      .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+          public void onClick(DialogInterface dialog, int id)
+          {
+            FaceCreateDialogFragment.this.getDialog().cancel();
+          }
+        })
+    ;
+
+    Dialog faceCreateDialog = builder.create();
+    faceCreateDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
+    return faceCreateDialog;
+  }
+}
diff --git a/app/src/main/java/net/named_data/nfd/FaceListActivity.java b/app/src/main/java/net/named_data/nfd/FaceListActivity.java
deleted file mode 100644
index 3ca5042..0000000
--- a/app/src/main/java/net/named_data/nfd/FaceListActivity.java
+++ /dev/null
@@ -1,390 +0,0 @@
-/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2015 Regents of the University of California
- *
- * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
- * See AUTHORS.md for complete list of NFD Android authors and contributors.
- *
- * NFD Android is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NFD Android, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.named_data.nfd;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.FragmentManager;
-import android.app.FragmentTransaction;
-import android.app.ListFragment;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.preference.PreferenceFragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.TextView;
-
-import com.intel.jndn.management.types.FacePersistency;
-import com.intel.jndn.management.types.FaceScope;
-import com.intel.jndn.management.types.FaceStatus;
-import com.intel.jndn.management.types.LinkType;
-
-import net.named_data.jndn.encoding.EncodingException;
-import net.named_data.jndn.util.Blob;
-import net.named_data.nfd.utils.NfdcAsyncTask;
-import net.named_data.nfd.utils.Nfdc;
-
-import org.joda.time.Period;
-import org.joda.time.format.PeriodFormat;
-import org.w3c.dom.Text;
-
-import java.nio.ByteBuffer;
-import java.util.LinkedList;
-import java.util.List;
-
-public class FaceListActivity extends Activity {
-  @Override
-  public void
-  onCreate(Bundle savedInstanceState)
-  {
-    super.onCreate(savedInstanceState);
-
-    FragmentManager fm = getFragmentManager();
-    FragmentTransaction ft = fm.beginTransaction();
-    if (fm.findFragmentById(android.R.id.content) == null) {
-      ft.add(android.R.id.content, m_faceListFragment);
-    } else {
-      ft.replace(android.R.id.content, m_faceListFragment);
-    }
-    ft.commit();
-  }
-
-  public static class FaceListFragment extends ListFragment
-    implements AdapterView.OnItemLongClickListener,
-               AdapterView.OnItemClickListener {
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState)
-    {
-      super.onActivityCreated(savedInstanceState);
-
-      setListAdapter(new FaceListAdapter(getActivity()));
-      getListView().setLongClickable(true);
-
-      getListView().setOnItemLongClickListener(this);
-      getListView().setOnItemClickListener(this);
-
-    }
-
-    @Override
-    public boolean
-    onItemLongClick(final AdapterView<?> parent, View view, final int position, long id)
-    {
-      final int faceId = (int)id;
-      if (faceId < 256) {
-        return false;
-      }
-
-      AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity());
-      alertDialogBuilder
-        .setMessage("Delete face " + String.valueOf(faceId) + "?")
-        .setCancelable(false)
-        .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
-          public void onClick(DialogInterface dialog, int id)
-          {
-            new NfdcAsyncTask(getActivity(),
-                              new NfdcAsyncTask.Task() {
-                                public String
-                                runTask() throws Exception
-                                {
-                                  Nfdc nfdc = new Nfdc();
-                                  nfdc.faceDestroy(faceId);
-                                  nfdc.shutdown();
-
-                                  getActivity().runOnUiThread(new Runnable() {
-                                    @Override
-                                    public void run()
-                                    {
-                                      ((FaceListAdapter) parent.getAdapter()).updateFaceList();
-                                    }
-                                  });
-                                  return null;
-                                }
-                              }).execute();
-          }
-        })
-        .setNegativeButton("No", new DialogInterface.OnClickListener() {
-          public void onClick(DialogInterface dialog, int id)
-          {
-            dialog.cancel();
-          }
-        });
-
-      AlertDialog alertDialog = alertDialogBuilder.create();
-      alertDialog.show();
-
-      return true;
-    }
-
-    @Override
-    public void onItemClick(AdapterView<?> parent, View view, int position, long id)
-    {
-      FaceStatus s = (FaceStatus)getListAdapter().getItem(position);
-      FaceStatusFragment fragment = new FaceStatusFragment();
-      Bundle bundle = new Bundle();
-      bundle.putByteArray("faceStatus", s.wireEncode().getImmutableArray());
-      fragment.setArguments(bundle);
-
-      getFragmentManager()
-        .beginTransaction()
-        .addToBackStack("FaceStatus")
-        .replace(android.R.id.content, fragment)
-        .commit();
-    }
-
-    private class FaceListAdapter extends BaseAdapter {
-      public FaceListAdapter(Context context)
-      {
-        this.m_inflater = LayoutInflater.from(context);
-        this.m_context = context;
-
-        updateFaceList();
-      }
-
-      public void
-      updateFaceList()
-      {
-        new NfdcAsyncTask(m_context,
-                          new NfdcAsyncTask.Task() {
-                            public String
-                            runTask() throws Exception
-                            {
-                              synchronized (m_facesLock) {
-                                Nfdc nfdc = new Nfdc();
-                                m_faces = nfdc.faceList();
-                                nfdc.shutdown();
-                              }
-
-                              getActivity().runOnUiThread(new Runnable() {
-                                @Override
-                                public void run()
-                                {
-                                  notifyDataSetChanged();
-                                }
-                              });
-                              return null;
-                            }
-                          }).execute();
-      }
-
-      @Override
-      public int
-      getCount()
-      {
-        synchronized (m_facesLock) {
-          if (m_faces == null)
-            return 0;
-          else
-            return m_faces.size();
-        }
-      }
-
-      @Override
-      public Object
-      getItem(int position)
-      {
-        synchronized (m_facesLock) {
-          assert m_faces != null && position < m_faces.size();
-          return m_faces.get(position);
-        }
-      }
-
-      @Override
-      public long
-      getItemId(int position)
-      {
-        synchronized (m_facesLock) {
-          assert m_faces != null && position < m_faces.size();
-          return m_faces.get(position).getFaceId();
-        }
-      }
-
-      @Override
-      public View
-      getView(int position, View convertView, ViewGroup parent)
-      {
-        if (convertView == null) {
-          convertView = this.m_inflater.inflate(android.R.layout.simple_list_item_2, parent, false);
-        }
-
-        FaceStatus s;
-        synchronized(m_facesLock) {
-          s = m_faces.get(position);
-        }
-
-        ((TextView)convertView.findViewById(android.R.id.text2)).setText(String.valueOf(s.getFaceId()));
-        ((TextView)convertView.findViewById(android.R.id.text1)).setText(s.getUri());
-
-        return convertView;
-      }
-
-      /////////////////////////////////////////////////////////////////////////
-      private LayoutInflater m_inflater;
-      private Context m_context;
-      private List<FaceStatus> m_faces;
-      private final Object m_facesLock = new Object();
-    }
-  }
-
-  ////////////////////////////////////////////////////////////////////////////////////////////////
-
-  public static class FaceStatusFragment extends ListFragment {
-
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState)
-    {
-      super.onActivityCreated(savedInstanceState);
-
-      FaceStatus s = null;
-      try {
-        s = new FaceStatus();
-        s.wireDecode(new Blob(getArguments().getByteArray("faceStatus")).buf());
-      }
-      catch (EncodingException e) {
-        assert false;
-      }
-
-      m_faceStatus.add(new Item("Face ID",          String.valueOf(s.getFaceId())));
-      m_faceStatus.add(new Item("Local FaceUri",    s.getLocalUri()));
-      m_faceStatus.add(new Item("Remote FaceUri",   s.getUri()));
-      m_faceStatus.add(new Item("Expires in",       s.getExpirationPeriod() < 0 ?
-        "never" :
-        PeriodFormat.getDefault().print(new Period(s.getExpirationPeriod()))));
-      m_faceStatus.add(new Item("Face scope",       getScope(s.getFaceScope())));
-      m_faceStatus.add(new Item("Face persistency", getPersistency(s.getFacePersistency())));
-      m_faceStatus.add(new Item("Link type",        getLinkType(s.getLinkType())));
-      m_faceStatus.add(new Item("In interests",     String.valueOf(s.getInInterests())));
-      m_faceStatus.add(new Item("In data",          String.valueOf(s.getInDatas())));
-      m_faceStatus.add(new Item("Out interests",    String.valueOf(s.getOutInterests())));
-      m_faceStatus.add(new Item("Out data",         String.valueOf(s.getOutDatas())));
-      m_faceStatus.add(new Item("In bytes",         String.valueOf(s.getInBytes())));
-      m_faceStatus.add(new Item("Out bytes",        String.valueOf(s.getOutBytes())));
-
-      setListAdapter(new FaceStatusAdapter(getActivity()));
-    }
-
-    private class FaceStatusAdapter extends BaseAdapter {
-      public FaceStatusAdapter(Context context)
-      {
-        this.m_inflater = LayoutInflater.from(context);
-      }
-
-      @Override
-      public int getCount()
-      {
-        return m_faceStatus.size();
-      }
-
-      @Override
-      public Object getItem(int position)
-      {
-        return m_faceStatus.get(position);
-      }
-
-      @Override
-      public long getItemId(int position)
-      {
-        return position;
-      }
-
-      @Override
-      public View getView(int position, View convertView, ViewGroup parent)
-      {
-        if (convertView == null) {
-          convertView = m_inflater.inflate(R.layout.face_status_item, parent, false);
-        }
-
-        Item i = (Item)getItem(position);
-        ((TextView)convertView.findViewById(R.id.title)).setText(i.getTitle());
-        ((TextView)convertView.findViewById(R.id.value)).setText(i.getValue());
-
-        return convertView;
-      }
-
-      /////////////////////////////////////////////////////////////////////////
-      private LayoutInflater m_inflater;
-    }
-
-    private static class Item {
-      public Item(String title, String value)
-      {
-        m_title = title;
-        m_value = value;
-      }
-
-      public String getValue()
-      {
-        return m_value;
-      }
-
-      public void setValue(String value)
-      {
-        m_value = value;
-      }
-
-      public String getTitle()
-      {
-        return m_title;
-      }
-
-      public void setTitle(String title)
-      {
-        m_title = title;
-      }
-
-      /////////////////////////////////////////////////////////////////////////
-      private String m_title;
-      private String m_value;
-    }
-    private List<Item> m_faceStatus = new LinkedList<Item>();
-  }
-
-  /////////////////////////////////////////////////////////////////////////////
-
-  private static String
-  getScope(FaceScope scope)
-  {
-    assert scope.getNumericValue() < s_scopes.length;
-    return s_scopes[scope.getNumericValue()];
-  }
-
-  private static String
-  getPersistency(FacePersistency persistency)
-  {
-    assert persistency.getNumericValue() < s_persistencies.length;
-    return s_persistencies[persistency.getNumericValue()];
-  }
-
-  private static String
-  getLinkType(LinkType linkType)
-  {
-    assert linkType.getNumericValue() < s_linkTypes.length;
-    return s_linkTypes[linkType.getNumericValue()];
-  }
-
-  private static final String[] s_scopes = {"Local", "Non-local"};
-  private static final String[] s_persistencies = {"Persistent", "On-demand", "Permanent"};
-  private static final String[] s_linkTypes = {"Point-to-point", "Multi-access"};
-
-  private FaceListFragment m_faceListFragment = new FaceListFragment();
-}
diff --git a/app/src/main/java/net/named_data/nfd/FaceListFragment.java b/app/src/main/java/net/named_data/nfd/FaceListFragment.java
new file mode 100644
index 0000000..168fe85
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/FaceListFragment.java
@@ -0,0 +1,528 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015 Regents of the University of California
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ *
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.v4.app.ListFragment;
+import android.util.Pair;
+import android.view.ActionMode;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.intel.jndn.management.types.FaceStatus;
+
+import net.named_data.jndn_xx.util.FaceUri;
+import net.named_data.nfd.utils.G;
+import net.named_data.nfd.utils.Nfdc;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class FaceListFragment extends ListFragment implements FaceCreateDialogFragment.OnFaceCreateRequested {
+
+  public static FaceListFragment
+  newInstance() {
+    return new FaceListFragment();
+  }
+
+  @Override
+  public void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+
+    setListAdapter(new FaceListAdapter(getActivity()));
+  }
+
+  @Override
+  public View onCreateView(final LayoutInflater inflater,
+                           ViewGroup container,
+                           Bundle savedInstanceState)
+  {
+    @SuppressLint("InflateParams")
+    View v = inflater.inflate(R.layout.fragment_face_list, null);
+
+    // Get info unavailable view
+    m_faceListInfoUnavailableView = v.findViewById(R.id.face_list_info_unavailable);
+
+    // Get progress bar spinner view
+    m_reloadingListProgressBar
+        = (ProgressBar)v.findViewById(R.id.face_list_reloading_list_progress_bar);
+
+    // Set refresh button click listener
+    Button refreshFaceListButton = (Button)v.findViewById(R.id.face_list_refresh_button);
+    refreshFaceListButton.setOnClickListener(new View.OnClickListener() {
+      @Override
+      public void onClick(View v) {
+        retrieveFaceList();
+      }
+    });
+
+    Button addFaceButton = (Button)v.findViewById(R.id.face_list_add_button);
+    addFaceButton.setOnClickListener(new View.OnClickListener() {
+      @Override
+      public void onClick(View view)
+      {
+        FaceCreateDialogFragment dialog = FaceCreateDialogFragment.newInstance();
+        dialog.setTargetFragment(FaceListFragment.this, 0);
+        dialog.show(getFragmentManager(), "FaceCreateFragment");
+      }
+    });
+
+    // Setup list view for deletion
+    ListView listView = (ListView)v.findViewById(android.R.id.list);
+    listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
+    listView.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
+      @Override
+      public void
+      onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
+        if (checked && id < 256) {
+          getListView().setItemChecked(position, false);
+        }
+      }
+
+      @Override
+      public boolean
+      onCreateActionMode(ActionMode mode, Menu menu) {
+        MenuInflater menuInflater = mode.getMenuInflater();
+        menuInflater.inflate(R.menu.menu_face_list_multiple_modal_menu, menu);
+        return true;
+      }
+
+      @Override
+      public boolean
+      onPrepareActionMode(ActionMode mode, Menu menu) {
+        return false;
+      }
+
+      @Override
+      public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+        switch (item.getItemId()) {
+          case R.id.menu_item_delete_face_item:
+            FaceListAdapter faceListAdapter = (FaceListAdapter)getListAdapter();
+            List<Integer> deleteFaceInfoList = new ArrayList<Integer>();
+
+            for (int i = faceListAdapter.getCount() - 1; i >= 0; i--) {
+              if (getListView().isItemChecked(i)) {
+                int faceId = faceListAdapter.getItem(i).getFaceId();
+                if (faceId < 256)
+                  continue;
+                deleteFaceInfoList.add(faceListAdapter.getItem(i).getFaceId());
+              }
+            }
+
+            // Delete selected faceIds
+            m_faceDestroyAsyncTask = new FaceDestroyAsyncTask();
+            m_faceDestroyAsyncTask.execute(deleteFaceInfoList);
+
+            mode.finish();
+            return true;
+          default:
+            return false;
+        }
+      }
+
+      @Override
+      public void onDestroyActionMode(ActionMode mode) {
+      }
+    });
+
+    return v;
+  }
+
+  @Override
+  public void onResume() {
+    super.onResume();
+    startFaceListRetrievalTask();
+  }
+
+  @Override
+  public void onPause() {
+    super.onPause();
+    stopFaceListRetrievalTask();
+
+    if (m_faceDestroyAsyncTask != null) {
+      m_faceDestroyAsyncTask.cancel(false);
+      m_faceDestroyAsyncTask = null;
+    }
+
+    if (m_faceCreateAsyncTask != null) {
+      m_faceCreateAsyncTask.cancel(false);
+      m_faceCreateAsyncTask = null;
+    }
+  }
+
+  @Override
+  public void onAttach(Activity activity) {
+    super.onAttach(activity);
+    try {
+      m_callbacks = (Callbacks)activity;
+    } catch (Exception e) {
+      G.Log("Hosting activity must implement this fragment's callbacks: " + e);
+    }
+  }
+
+  @Override
+  public void onDetach() {
+    super.onDetach();
+    m_callbacks = null;
+  }
+
+  @Override
+  public void
+  onListItemClick(ListView l, View v, int position, long id) {
+    if (m_callbacks != null) {
+      FaceStatus faceStatus = (FaceStatus)l.getAdapter().getItem(position);
+      m_callbacks.onFaceItemSelected(faceStatus);
+    }
+  }
+
+  @Override
+  public void
+  createFace(String faceUri)
+  {
+    m_faceCreateAsyncTask = new FaceCreateAsyncTask(faceUri);
+    m_faceCreateAsyncTask.execute();
+  }
+
+  /////////////////////////////////////////////////////////////////////////
+
+  /**
+   * Updates the underlying adapter with the given list of FaceStatus.
+   *
+   * Note: This method should only be called from the UI thread.
+   *
+   * @param list Update ListView with the given List&lt;FaceStatus&gt;
+   */
+  private void updateFaceList(List<FaceStatus> list) {
+    if (list == null) {
+      m_faceListInfoUnavailableView.setVisibility(View.VISIBLE);
+      return;
+    }
+
+    ((FaceListAdapter)getListAdapter()).updateList(list);
+  }
+
+  /**
+   * Convenience method that starts the AsyncTask that retrieves the
+   * list of available faces.
+   */
+  private void retrieveFaceList() {
+    // Update UI
+    m_faceListInfoUnavailableView.setVisibility(View.GONE);
+
+    // Stop if running; before starting the new Task
+    stopFaceListRetrievalTask();
+    startFaceListRetrievalTask();
+  }
+
+  /**
+   * Create a new AsyncTask for face list information retrieval.
+   */
+  private void startFaceListRetrievalTask() {
+    m_faceListAsyncTask = new FaceListAsyncTask();
+    m_faceListAsyncTask.execute();
+  }
+
+  /**
+   * Stops a previously started face retrieval AsyncTask.
+   */
+  private void stopFaceListRetrievalTask() {
+    if (m_faceListAsyncTask != null) {
+      m_faceListAsyncTask.cancel(false);
+      m_faceListAsyncTask = null;
+    }
+  }
+
+  /**
+   * Custom adapter for displaying face information in a ListView.
+   */
+  private static class FaceListAdapter extends BaseAdapter {
+    private FaceListAdapter(Context context) {
+      m_layoutInflater = LayoutInflater.from(context);
+    }
+
+    private void
+    updateList(List<FaceStatus> faces)
+    {
+      m_faces = faces;
+      notifyDataSetChanged();
+    }
+
+    @Override
+    public FaceStatus
+    getItem(int i)
+    {
+      assert m_faces != null;
+      return m_faces.get(i);
+    }
+
+    @Override
+    public long getItemId(int i)
+    {
+      assert m_faces != null;
+      return m_faces.get(i).getFaceId();
+    }
+
+    @Override
+    public int getCount()
+    {
+      return (m_faces != null) ? m_faces.size() : 0;
+    }
+
+    @SuppressLint("InflateParams")
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+      FaceInfoHolder holder;
+
+      if (convertView == null) {
+        holder = new FaceInfoHolder();
+
+        convertView = m_layoutInflater.inflate(R.layout.list_item_face_status_item, null);
+        convertView.setTag(holder);
+
+        holder.m_faceUri = (TextView)convertView.findViewById(R.id.list_item_face_uri);
+        holder.m_faceId = (TextView)convertView.findViewById(R.id.list_item_face_id);
+      } else {
+        holder = (FaceInfoHolder)convertView.getTag();
+      }
+
+      FaceStatus info = getItem(position);
+      holder.m_faceUri.setText(info.getUri());
+      holder.m_faceId.setText(String.valueOf(info.getFaceId()));
+
+      return convertView;
+    }
+
+    private static class FaceInfoHolder {
+      private TextView m_faceUri;
+      private TextView m_faceId;
+    }
+
+    private final LayoutInflater m_layoutInflater;
+    private List<FaceStatus> m_faces;
+  }
+
+  /**
+   * AsyncTask that gets the list of faces from the running NFD.
+   */
+  private class FaceListAsyncTask extends AsyncTask<Void, Void, Pair<List<FaceStatus>, Exception>> {
+    @Override
+    protected void
+    onPreExecute() {
+      // Display progress bar
+      m_reloadingListProgressBar.setVisibility(View.VISIBLE);
+    }
+
+    @Override
+    protected Pair<List<FaceStatus>, Exception>
+    doInBackground(Void... params) {
+      Exception returnException = null;
+      Nfdc nfdc = new Nfdc();
+      List<FaceStatus> faceStatusList = null;
+      try {
+        faceStatusList = nfdc.faceList();
+      } catch (Exception e) {
+        returnException = e;
+      }
+      nfdc.shutdown();
+      return new Pair<>(faceStatusList, returnException);
+    }
+
+    @Override
+    protected void
+    onCancelled() {
+      // Remove progress bar
+      m_reloadingListProgressBar.setVisibility(View.GONE);
+
+      // Nothing to do here; No change in UI.
+    }
+
+    @Override
+    protected void
+    onPostExecute(Pair<List<FaceStatus>, Exception> result) {
+      // Remove progress bar
+      m_reloadingListProgressBar.setVisibility(View.GONE);
+
+      if (result.second != null) {
+        Toast.makeText(getActivity(), "Error communicating with NFD (" + result.second.getMessage() + ")",
+                       Toast.LENGTH_LONG).show();
+      }
+
+      updateFaceList(result.first);
+    }
+  }
+
+  /**
+   * AsyncTask that destroys faces that are passed in as a list of FaceInfo.
+   */
+  private class FaceDestroyAsyncTask extends AsyncTask<List<Integer>, Void, Exception> {
+    @Override
+    protected void
+    onPreExecute() {
+      // Display progress bar
+      m_reloadingListProgressBar.setVisibility(View.VISIBLE);
+    }
+
+    @SafeVarargs
+    @Override
+    protected final Exception
+    doInBackground(List<Integer>... params) {
+      Exception retval = null;
+
+      Nfdc nfdc = new Nfdc();
+      try {
+        for (List<Integer> faces : params) {
+          for (int faceId : faces) {
+            nfdc.faceDestroy(faceId);
+          }
+        }
+      } catch (Exception e) {
+        retval = e;
+      }
+      nfdc.shutdown();
+
+      return retval;
+    }
+
+    @Override
+    protected void onCancelled() {
+      // Remove progress bar
+      m_reloadingListProgressBar.setVisibility(View.GONE);
+
+      // Nothing to do here; No change in UI.
+    }
+
+    @Override
+    protected void onPostExecute(Exception e) {
+      // Remove progress bar
+      m_reloadingListProgressBar.setVisibility(View.GONE);
+
+      if (e != null) {
+        Toast.makeText(getActivity(), "Error communicating with NFD (" + e.getMessage() + ")",
+                       Toast.LENGTH_LONG).show();
+      }
+      else {
+        // Reload face list
+        retrieveFaceList();
+      }
+    }
+  }
+
+  private class FaceCreateAsyncTask extends AsyncTask<Void, Void, String> {
+    public FaceCreateAsyncTask(String faceUri)
+    {
+      m_faceUri = faceUri;
+    }
+
+    @Override
+    protected String
+    doInBackground(Void... params)
+    {
+      try {
+        Nfdc nfdc = new Nfdc();
+        int faceId = nfdc.faceCreate(m_faceUri);
+        nfdc.shutdown();
+        return "OK. Face id: " + String.valueOf(faceId);
+      }
+      catch (FaceUri.CanonizeError e) {
+        return "Error creating face (" + e.getMessage() + ")";
+      }
+      catch (FaceUri.Error e) {
+        return "Error creating face (" + e.getMessage() + ")";
+      }
+      catch (Exception e) {
+        return "Error communicating with NFD (" + e.getMessage() + ")";
+      }
+    }
+
+    @Override
+    protected void onPreExecute()
+    {
+      // Display progress bar
+      m_reloadingListProgressBar.setVisibility(View.VISIBLE);
+    }
+
+    @Override
+    protected void onPostExecute(String status)
+    {
+      // Display progress bar
+      m_reloadingListProgressBar.setVisibility(View.VISIBLE);
+      Toast.makeText(getActivity(), status, Toast.LENGTH_LONG).show();
+
+      retrieveFaceList();
+    }
+
+    @Override
+    protected void onCancelled()
+    {
+      // Remove progress bar
+      m_reloadingListProgressBar.setVisibility(View.GONE);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    private String m_faceUri;
+  }
+  /////////////////////////////////////////////////////////////////////////
+
+  public interface Callbacks {
+    /**
+     * This method is called when a face is selected and more
+     * information about the face should be presented to the user.
+     *
+     * @param faceStatus FaceStatus instance with information about the face
+     */
+    public void onFaceItemSelected(FaceStatus faceStatus);
+  }
+
+  /////////////////////////////////////////////////////////////////////////
+
+  /** Reference to the most recent AsyncTask that was created for listing faces */
+  private FaceListAsyncTask m_faceListAsyncTask;
+
+  /** Callback handler of the hosting activity */
+  private Callbacks m_callbacks;
+
+  /** Reference to the most recent AsyncTask that was created for destroying faces */
+  private FaceDestroyAsyncTask m_faceDestroyAsyncTask;
+
+  /** Reference to the most recent AsyncTask that was created for creating a face */
+  private FaceCreateAsyncTask m_faceCreateAsyncTask;
+
+  /** Reference to the view to be displayed when no information is available */
+  private View m_faceListInfoUnavailableView;
+
+  /** Progress bar spinner to display to user when destroying faces */
+  private ProgressBar m_reloadingListProgressBar;
+}
diff --git a/app/src/main/java/net/named_data/nfd/FaceStatusFragment.java b/app/src/main/java/net/named_data/nfd/FaceStatusFragment.java
new file mode 100644
index 0000000..13aa367
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/FaceStatusFragment.java
@@ -0,0 +1,235 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015 Regents of the University of California
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ *
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.ListFragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+
+import com.intel.jndn.management.types.FacePersistency;
+import com.intel.jndn.management.types.FaceScope;
+import com.intel.jndn.management.types.FaceStatus;
+import com.intel.jndn.management.types.LinkType;
+
+import net.named_data.jndn.encoding.EncodingException;
+import net.named_data.jndn.util.Blob;
+import net.named_data.nfd.utils.G;
+
+import org.joda.time.Period;
+import org.joda.time.format.PeriodFormat;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class FaceStatusFragment extends ListFragment {
+
+  /**
+   * Create a new instance of {@link net.named_data.nfd.FaceStatusFragment} with
+   * the given detail face information as a byte array object.
+   * 
+   * Note:
+   * faceStatus.wireEncode().getImmutableArray()
+   * This byte array should be retrieved via a call to:
+   * {@link com.intel.jndn.management.types.FaceStatus#wireEncode()} for a
+   * {@link net.named_data.jndn.util.Blob} object. Subsequently, an
+   * immutable byte array can be retrieved via
+   * {@link net.named_data.jndn.util.Blob#getImmutableArray()}.
+   * 
+   * @param faceStatus FaceStatus instance with information about the face
+   * @return Fragment instance of {@link net.named_data.nfd.FaceStatusFragment}
+   * that is ready for use by the hosting activity
+   */
+  public static FaceStatusFragment
+  newInstance(FaceStatus faceStatus) {
+    Bundle args = new Bundle();
+    args.putByteArray(EXTRA_FACE_INFORMATION, faceStatus.wireEncode().getImmutableArray());
+
+    FaceStatusFragment fragment = new FaceStatusFragment();
+    fragment.setArguments(args);
+
+    return fragment;
+  }
+
+  @Override
+  public void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    m_listItems = new ArrayList<>();
+
+    Resources res = getResources();
+    m_scopes = res.getStringArray(R.array.face_scopes);
+    m_linkTypes = res.getStringArray(R.array.face_link_types);
+    m_persistencies = res.getStringArray(R.array.face_persistency);
+
+    // Get face status information
+    FaceStatus faceStatus = new FaceStatus();
+    try {
+      byte [] args = getArguments().getByteArray(EXTRA_FACE_INFORMATION);
+      faceStatus.wireDecode(new Blob(args).buf());
+    } catch (EncodingException e) {
+      G.Log("EXTRA_FACE_INFORMATION: EncodingException: " + e);
+    }
+
+    // Creating list of items to be displayed
+    m_listItems.add(new ListItem("Face ID",          String.valueOf(faceStatus.getFaceId())));
+    m_listItems.add(new ListItem("Local FaceUri",    faceStatus.getLocalUri()));
+    m_listItems.add(new ListItem("Remote FaceUri",   faceStatus.getUri()));
+    m_listItems.add(new ListItem("Expires in",       faceStatus.getExpirationPeriod() < 0 ?
+        getString(R.string.expire_never):
+        PeriodFormat.getDefault().print(new Period(faceStatus.getExpirationPeriod()))));
+    m_listItems.add(new ListItem("Face scope",       getScope(faceStatus.getFaceScope())));
+    m_listItems.add(new ListItem("Face persistency", getPersistency(faceStatus.getFacePersistency())));
+    m_listItems.add(new ListItem("Link type",        getLinkType(faceStatus.getLinkType())));
+    m_listItems.add(new ListItem("In interests",     String.valueOf(faceStatus.getInInterests())));
+    m_listItems.add(new ListItem("In data",          String.valueOf(faceStatus.getInDatas())));
+    m_listItems.add(new ListItem("Out interests",    String.valueOf(faceStatus.getOutInterests())));
+    m_listItems.add(new ListItem("Out data",         String.valueOf(faceStatus.getOutDatas())));
+    m_listItems.add(new ListItem("In bytes",         String.valueOf(faceStatus.getInBytes())));
+    m_listItems.add(new ListItem("Out bytes",        String.valueOf(faceStatus.getOutBytes())));
+
+    m_faceStatusAdapter = new FaceStatusAdapter(getActivity(), m_listItems);
+    setListAdapter(m_faceStatusAdapter);
+  }
+
+  @SuppressLint("InflateParams")
+  @Override
+  public View onCreateView(LayoutInflater inflater,
+                           @Nullable ViewGroup container,
+                           @Nullable Bundle savedInstanceState)
+  {
+    return inflater.inflate(R.layout.fragment_face_detail, null);
+  }
+
+  /////////////////////////////////////////////////////////////////////////
+
+  private String
+  getScope(FaceScope scope)
+  {
+    return m_scopes[scope.getNumericValue()];
+  }
+
+  private String
+  getPersistency(FacePersistency persistency)
+  {
+    return m_persistencies[persistency.getNumericValue()];
+  }
+
+  private String
+  getLinkType(LinkType linkType)
+  {
+    return m_linkTypes[linkType.getNumericValue()];
+  }
+
+  /**
+   * Generic list item model for use in
+   * {@link net.named_data.nfd.FaceStatusFragment.FaceStatusAdapter}.
+   */
+  private static class ListItem {
+    public ListItem(String title, String value) {
+      m_title = title;
+      m_value = value;
+    }
+
+    public String getValue() {
+      return m_value;
+    }
+
+    public void setValue(String value) {
+      m_value = value;
+    }
+
+    public String getTitle() {
+      return m_title;
+    }
+
+    public void setTitle(String title) {
+      m_title = title;
+    }
+
+    private String m_title;
+    private String m_value;
+  }
+
+  /**
+   * Custom ListView adapter that displays face status information.
+   */
+  private static class FaceStatusAdapter extends ArrayAdapter<ListItem> {
+    private FaceStatusAdapter(Context context, List<ListItem> objects) {
+      super(context, 0, objects);
+      m_layoutInflater = LayoutInflater.from(context);
+    }
+
+    @SuppressLint("InflateParams")
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+      ListItemHolder holder;
+      if (convertView == null) {
+        holder = new ListItemHolder();
+
+        convertView = m_layoutInflater.inflate(R.layout.list_item_face_generic_item, null);
+        convertView.setTag(holder);
+
+        holder.m_title = (TextView)convertView.findViewById(R.id.list_item_generic_title);
+        holder.m_value = (TextView)convertView.findViewById(R.id.list_item_generic_value);
+      } else {
+        holder = (ListItemHolder)convertView.getTag();
+      }
+
+      ListItem info = getItem(position);
+      holder.m_title.setText(info.getTitle());
+      holder.m_value.setText(info.getValue());
+
+      return convertView;
+    }
+
+    private static class ListItemHolder {
+      private TextView m_title;
+      private TextView m_value;
+    }
+
+    private final LayoutInflater m_layoutInflater;
+  }
+
+  /////////////////////////////////////////////////////////////////////////
+
+  /** Bundle argument key for face information byte array */
+  private static final String EXTRA_FACE_INFORMATION
+      = "net.named_data.nfd.face_detail_fragment_face_id";
+
+  /**
+   * List items to be displayed; Used when creating
+   * {@link net.named_data.nfd.FaceStatusFragment.FaceStatusAdapter}
+   */
+  private ArrayList<ListItem> m_listItems;
+
+  /** Reference to custom {@link net.named_data.nfd.FaceStatusFragment.FaceStatusAdapter} */
+  private FaceStatusAdapter m_faceStatusAdapter;
+
+  private String[] m_scopes;
+  private String[] m_persistencies;
+  private String[] m_linkTypes;
+}
diff --git a/app/src/main/java/net/named_data/nfd/LogcatActivity.java b/app/src/main/java/net/named_data/nfd/LogcatFragment.java
similarity index 66%
rename from app/src/main/java/net/named_data/nfd/LogcatActivity.java
rename to app/src/main/java/net/named_data/nfd/LogcatFragment.java
index de5b039..ed4b1e8 100644
--- a/app/src/main/java/net/named_data/nfd/LogcatActivity.java
+++ b/app/src/main/java/net/named_data/nfd/LogcatFragment.java
@@ -19,11 +19,14 @@
 
 package net.named_data.nfd;
 
-import android.content.Intent;
+import android.annotation.SuppressLint;
+import android.app.Activity;
 import android.os.Bundle;
-import android.support.v7.app.ActionBarActivity;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
 import android.view.LayoutInflater;
 import android.view.Menu;
+import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
@@ -32,7 +35,6 @@
 import android.widget.TextView;
 
 import net.named_data.nfd.utils.G;
-import net.named_data.nfd.utils.LogcatTags;
 
 import java.io.BufferedReader;
 import java.io.IOException;
@@ -40,55 +42,91 @@
 import java.util.ArrayList;
 
 /**
- * Display of NfdService's logcat output for easy debugging
+ * Logcat fragment that houses the output of LogCat in a ListView for viewing.
+ *
+ * The ListView is backed by a {@link net.named_data.nfd.LogcatFragment.LogListAdapter}
+ * that that limits the number of logs that are being displayed to the user. This
+ * prevents the ListView from growing indeterminately. This maximum number of
+ * log lines that is allowed to be displayed is set by
+ * {@link net.named_data.nfd.LogcatFragment#s_logMaxLines}.
+ *
  */
-public class LogcatActivity extends ActionBarActivity {
+public class LogcatFragment extends Fragment {
+
+  public static Fragment newInstance() {
+    return new LogcatFragment();
+  }
 
   @Override
-  public boolean onCreateOptionsMenu(Menu menu) {
-    getMenuInflater().inflate(R.menu.menu_log, menu);
-    return true;
+  public void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    setHasOptionsMenu(true);
+  }
+
+  @Override
+  public View onCreateView(LayoutInflater inflater,
+                           @Nullable ViewGroup container,
+                           @Nullable Bundle savedInstanceState) {
+    @SuppressLint("InflateParams")
+    View v = inflater.inflate(R.layout.fragment_logcat_output, null);
+
+    // Get UI Elements
+    m_logListView = (ListView) v.findViewById(R.id.log_output);
+    m_logListAdapter = new LogListAdapter(inflater, s_logMaxLines);
+    m_logListView.setAdapter(m_logListAdapter);
+
+    return v;
+  }
+
+  @Override
+  public void onResume() {
+    super.onResume();
+    startLogging();
+    G.Log("LogcatFragment(): onResume()");
+  }
+
+  @Override
+  public void onPause() {
+    super.onPause();
+    stopLogging();
+    G.Log("LogcatFragment(): onPause()");
+  }
+
+  @Override
+  public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+    super.onCreateOptionsMenu(menu, inflater);
+    inflater.inflate(R.menu.menu_log, menu);
   }
 
   @Override
   public boolean onOptionsItemSelected(MenuItem item) {
     switch (item.getItemId()) {
-    case R.id.action_log_settings:
-      startActivity(new Intent(this, LogcatSettingsActivity.class));
-      return true;
-    default:
-      return super.onOptionsItemSelected(item);
+      case R.id.action_log_settings:
+        m_callbacks.onDisplayLogcatSettings();
+        return true;
+      default:
+        return super.onOptionsItemSelected(item);
     }
   }
 
   @Override
-  protected void onCreate(Bundle savedInstanceState) {
-    super.onCreate(savedInstanceState);
-    setContentView(R.layout.activity_log);
-
-    // Get UI Elements
-    m_logListView = (ListView) findViewById(R.id.log_output);
-    m_logListAdapter = new LogListAdapter(getLayoutInflater(), s_logMaxLines);
-    m_logListView.setAdapter(m_logListAdapter);
+  public void onAttach(Activity activity) {
+    super.onAttach(activity);
+    try {
+      m_callbacks = (Callbacks) activity;
+    } catch (ClassCastException e) {
+      G.Log("Hosting activity must implement callback.");
+      throw e;
+    }
   }
 
   @Override
-  protected void onResume() {
-    super.onResume();
-    startLogging();
+  public void onDetach() {
+    super.onDetach();
+    m_callbacks = null;
   }
 
-  @Override
-  protected void onPause() {
-    super.onPause();
-    stopLogging();
-  }
-
-  @Override
-  protected void onDestroy() {
-    super.onDestroy();
-    G.Log("LogActivity::onDestroy()");
-  }
+  //////////////////////////////////////////////////////////////////////////////
 
   /**
    * Starts logging by spawning a new thread to capture logs.
@@ -97,7 +135,7 @@
     // Clear output, update UI and get tag arguments
     clearLogOutput();
     appendLogText(getString(R.string.loading_logger));
-    m_tagArguments = LogcatTags.getTags(this);
+    m_tagArguments = LogcatSettingsManager.get(getActivity()).getTags();
 
     new Thread(){
       @Override
@@ -149,19 +187,19 @@
        */
       // Build command for execution
       String cmd = String.format("%s -v time %s *:S",
-        "logcat",
-        m_tagArguments);
+          "logcat",
+          m_tagArguments);
 
       G.Log("LogCat Command: " + cmd);
 
       m_logProcess =  Runtime.getRuntime().exec(cmd);
       BufferedReader in = new BufferedReader(
-        new InputStreamReader(m_logProcess.getInputStream()));
+          new InputStreamReader(m_logProcess.getInputStream()));
 
       String line;
       while ((line = in.readLine()) != null) {
         final String message = line;
-        runOnUiThread(new Runnable() {
+        getActivity().runOnUiThread(new Runnable() {
           @Override
           public void run() {
             appendLogText(message);
@@ -171,13 +209,13 @@
 
       // Wait for process to join this thread
       m_logProcess.waitFor();
-    } catch (IOException e) {
-      G.Log("captureLog(): " + e);
-    } catch (InterruptedException e) {
+    } catch (IOException | InterruptedException e) {
       G.Log("captureLog(): " + e);
     }
   }
 
+  //////////////////////////////////////////////////////////////////////////////
+
   /**
    * Custom LogListAdapter to limit the number of log lines that
    * is being stored and displayed.
@@ -193,7 +231,7 @@
      *                 the ListView for this adapter.
      */
     public LogListAdapter(LayoutInflater layoutInflater, int maxLines) {
-      m_data = new ArrayList<String>();
+      m_data = new ArrayList<>();
       m_layoutInflater = layoutInflater;
       m_maxLines = maxLines;
     }
@@ -237,6 +275,7 @@
       return position;
     }
 
+    @SuppressLint("InflateParams")
     @Override
     public View getView(int position, View convertView, ViewGroup parent) {
       LogEntryViewHolder holder;
@@ -244,7 +283,7 @@
       if (convertView == null) {
         holder = new LogEntryViewHolder();
 
-        convertView = m_layoutInflater.inflate(R.layout.log_item, null);
+        convertView = m_layoutInflater.inflate(R.layout.list_item_log, null);
         convertView.setTag(holder);
 
         holder.logLineTextView = (TextView) convertView.findViewById(R.id.log_line);
@@ -257,13 +296,13 @@
     }
 
     /** Underlying message data store for log messages*/
-    private ArrayList<String> m_data;
+    private final ArrayList<String> m_data;
 
     /** Layout inflater for inflating views */
-    private LayoutInflater m_layoutInflater;
+    private final LayoutInflater m_layoutInflater;
 
     /** Maximum number of log lines to display */
-    private int m_maxLines;
+    private final int m_maxLines;
   }
 
   /**
@@ -273,7 +312,15 @@
     public TextView logLineTextView;
   }
 
-  /** Maximum number of log lines to be displayed */
+  //////////////////////////////////////////////////////////////////////////////
+
+  public interface Callbacks {
+    void onDisplayLogcatSettings();
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
+
+  /** Maximum number of log lines to be displayed by the backing adapter of the ListView */
   private static final int s_logMaxLines = 380;
 
   /** Process in which logcat is running in */
@@ -287,4 +334,7 @@
 
   /** Tag argument to logcat */
   private String m_tagArguments;
+
+  /** Callback reference to hosting activity */
+  private Callbacks m_callbacks;
 }
diff --git a/app/src/main/java/net/named_data/nfd/LogcatSettingItem.java b/app/src/main/java/net/named_data/nfd/LogcatSettingItem.java
new file mode 100644
index 0000000..d5a3fff
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/LogcatSettingItem.java
@@ -0,0 +1,61 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015 Regents of the University of California
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ *
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd;
+
+/**
+ * Logcat setting item that contains information about the tag name
+ * in the m_logTag field and log level in the m_logLevel field.
+ */
+public class LogcatSettingItem {
+
+  public LogcatSettingItem(String logTag, String logLevel) {
+    m_logTag = logTag;
+    m_logLevel = logLevel;
+  }
+
+  public String getLogTag() {
+    return m_logTag;
+  }
+
+  public void setLogTag(String logTag) {
+    m_logTag = logTag;
+  }
+
+  public String getLogLevel() {
+    return m_logLevel;
+  }
+
+  public void setLogLevel(String logLevel) {
+    m_logLevel = logLevel;
+  }
+
+  @Override
+  public String toString() {
+    return String.format("%s: %s", m_logTag, m_logLevel);
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
+
+  /** Log tag that logcat should monitor */
+  private String m_logTag;
+
+  /** Log level (aka priority level) that logcat should use for this log tag */
+  private String m_logLevel;
+}
diff --git a/app/src/main/java/net/named_data/nfd/LogcatSettingsActivity.java b/app/src/main/java/net/named_data/nfd/LogcatSettingsActivity.java
deleted file mode 100644
index b8b0f13..0000000
--- a/app/src/main/java/net/named_data/nfd/LogcatSettingsActivity.java
+++ /dev/null
@@ -1,250 +0,0 @@
-/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2015 Regents of the University of California
- *
- * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
- * See AUTHORS.md for complete list of NFD Android authors and contributors.
- *
- * NFD Android is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NFD Android, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.named_data.nfd;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.preference.ListPreference;
-import android.preference.Preference;
-import android.preference.PreferenceFragment;
-import android.preference.PreferenceGroup;
-import android.preference.PreferenceManager;
-import android.preference.PreferenceScreen;
-
-import net.named_data.nfd.utils.LogcatTags;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class LogcatSettingsActivity extends Activity {
-
-  @Override
-  protected void onCreate(Bundle savedInstanceState) {
-    super.onCreate(savedInstanceState);
-
-    getFragmentManager().
-        beginTransaction().
-        replace(android.R.id.content, new NfdLogSettingsFragment()).
-        commit();
-  }
-
-  public static class NfdLogSettingsFragment extends PreferenceFragment {
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-      super.onCreate(savedInstanceState);
-
-      // Load the preferences from an XML resource
-      addPreferencesFromResource(R.xml.pref_nfd_log);
-
-      PreferenceScreen preferenceScreen = getPreferenceScreen();
-
-      //// NOTE: This section of code demonstrates how a preference can
-      //// be added programmatically.
-      ////
-      // Creating and inserting a preference programmatically
-      // Preference customPreference = preferenceScreen.
-      //     findPreference(getString(R.string.pref_category_title_tags_key));
-      // if (customPreference instanceof PreferenceCategory) {
-      //   ListPreference listPreference = new ListPreference(getActivity());
-      //   listPreference.setKey("NFDTest_Key");
-      //   listPreference.setTitle("NFDTest");
-      //   listPreference.setDefaultValue("I");
-      //   listPreference.setNegativeButtonText(null);
-      //   listPreference.setDialogTitle("NFDTest");
-
-      //   String [] keys = getResources().getStringArray(R.array.pref_log_levels);
-      //   String [] values = getResources().getStringArray(R.array.pref_log_level_values);
-
-      //   listPreference.setEntries(keys);
-      //   listPreference.setEntryValues(values);
-
-      //   ((PreferenceCategory) customPreference).addPreference(listPreference);
-      // }
-
-      // Collect all Preference in the hierarchy
-      m_tagListPreferences = new ArrayList<ListPreference>();
-      extractPreferences(m_tagListPreferences,
-          (PreferenceGroup) preferenceScreen.
-              findPreference(getString(R.string.pref_category_title_tags_key)));
-
-      // Set all preference setting
-      m_resetPreference = preferenceScreen.
-          findPreference(getString(R.string.pref_tags_log_level_key));
-    }
-
-    @Override
-    public void onResume() {
-      super.onResume();
-      registerListeners();
-    }
-
-    @Override
-    public void onPause() {
-      super.onPause();
-      unregisterPreferenceListeners();
-      saveTagArguments();
-    }
-
-    /**
-     * Convenience method to register preference listeners.
-     */
-    private void registerListeners() {
-      for (Preference p : m_tagListPreferences) {
-        if (p instanceof ListPreference) {
-          registerPreferenceListener(p);
-        }
-      }
-
-      m_resetPreference.setOnPreferenceChangeListener(m_setAllPreferenceChangeListener);
-    }
-
-    /**
-     * Convenience method to unregister preference listeners.
-     */
-    private void unregisterPreferenceListeners() {
-      for (Preference p : m_tagListPreferences) {
-        if (p instanceof ListPreference) {
-          unregisterPreferenceListener(p);
-        }
-      }
-
-      m_resetPreference.setOnPreferenceChangeListener(null);
-    }
-
-    /**
-     * Register preference listener and fire an update.
-     *
-     * @param preference Preference to register listener.
-     */
-    private void registerPreferenceListener(Preference preference) {
-      // Attach listener
-      preference.setOnPreferenceChangeListener(m_tagPreferenceChangeListener);
-
-      // Trigger update
-      m_tagPreferenceChangeListener.onPreferenceChange(preference,
-          PreferenceManager.
-              getDefaultSharedPreferences(preference.getContext()).
-              getString(preference.getKey(), ""));
-    }
-
-    /**
-     * Unregister preference listener for the given preference.
-     *
-     * @param preference Preference to unregister listener.
-     */
-    private void unregisterPreferenceListener(Preference preference) {
-      // Remove listener
-      preference.setOnPreferenceChangeListener(null);
-    }
-
-    /**
-     * Convenience method that extracts all list preferences within a hierarchy
-     * recursively.
-     *
-     * @param list List to add preference to
-     * @param preferenceGroup Root preference group to begin search from
-     */
-    private void extractPreferences(List<ListPreference> list, PreferenceGroup preferenceGroup) {
-      for (int i = 0; i < preferenceGroup.getPreferenceCount(); i++) {
-        final Preference preference = preferenceGroup.getPreference(i);
-
-        if (preference instanceof ListPreference) {
-          list.add((ListPreference) preference);
-        } else if (preference instanceof PreferenceGroup) {
-          extractPreferences(list, (PreferenceGroup) preference);
-        }
-      }
-    }
-
-    /**
-     * Save tag arguments for quick retrieval.
-     */
-    private void saveTagArguments() {
-      LogcatTags.TagBuilder tagBuilder = LogcatTags.TagBuilder.getTagBuilder();
-
-      for (Preference p : m_tagListPreferences) {
-        if (p instanceof ListPreference) {
-          ListPreference listPreference = (ListPreference) p;
-          tagBuilder.addTag(listPreference.getTitle(), listPreference.getValue());
-        }
-      }
-
-      LogcatTags.saveTags(getActivity(), tagBuilder.generateTagString());
-    }
-
-    /**
-     * Convenience method to change all tags' log level to the
-     * given logLevel.
-     *
-     * @param logLevel Target log level to set to.
-     */
-    private void setAllTagPreferences(String logLevel) {
-      for (ListPreference preference : m_tagListPreferences) {
-        preference.setValue(logLevel);
-        m_tagPreferenceChangeListener.onPreferenceChange(preference, logLevel);
-      }
-    }
-
-    /** List of preferences for registering handlers */
-    private List<ListPreference> m_tagListPreferences;
-
-    /** Reset log level preference */
-    private Preference m_resetPreference;
-
-    /**
-     * Change listener that updates the summary text of the tag preferences.
-     */
-    private Preference.OnPreferenceChangeListener m_tagPreferenceChangeListener
-        = new Preference.OnPreferenceChangeListener() {
-      @Override
-      public boolean onPreferenceChange(Preference preference, Object value) {
-        // Get value of preference setting as a String
-        String displayString = value.toString();
-
-        // Deal with ListPreference
-        if (preference instanceof ListPreference) {
-          // Get display value
-          ListPreference listPreference = (ListPreference) preference;
-          int offset = listPreference.findIndexOfValue(displayString);
-          displayString = (offset != -1) ?
-              (String) listPreference.getEntries()[offset] :
-              null;
-        }
-
-        // Update UI
-        preference.setSummary(displayString);
-        return true;
-      }
-    };
-
-    /**
-     * Change listener that resets all preference tags' log levels.
-     */
-    private Preference.OnPreferenceChangeListener m_setAllPreferenceChangeListener
-        = new Preference.OnPreferenceChangeListener() {
-      @Override
-      public boolean onPreferenceChange(Preference preference, Object value) {
-        setAllTagPreferences(value.toString());
-        return true;
-      }
-    };
-  }
-}
diff --git a/app/src/main/java/net/named_data/nfd/LogcatSettingsFragment.java b/app/src/main/java/net/named_data/nfd/LogcatSettingsFragment.java
new file mode 100644
index 0000000..805a568
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/LogcatSettingsFragment.java
@@ -0,0 +1,341 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015 Regents of the University of California
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ *
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.ListFragment;
+import android.view.ActionMode;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+public class LogcatSettingsFragment extends ListFragment {
+
+  public static LogcatSettingsFragment newInstance() {
+    return new LogcatSettingsFragment();
+  }
+
+  @Override
+  public void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+
+    m_logcatSettingsManager = LogcatSettingsManager.get(getActivity());
+    m_logcatSettingItems = m_logcatSettingsManager.getLogcatSettingItems();
+
+    ArrayAdapter<LogcatSettingItem> adapter
+        = new LogcatSettingsAdapter(getActivity(), m_logcatSettingItems);
+
+    setListAdapter(adapter);
+    setRetainInstance(true);
+  }
+
+  @Override
+  public View onCreateView(LayoutInflater inflater,
+                           ViewGroup container,
+                           Bundle savedInstanceState) {
+    @SuppressLint("InflateParams")
+    View v = inflater.inflate(R.layout.fragment_logcat_tags_list, null);
+
+    // Set resetLogLevelButton listener
+    Button resetLogLevelButton = (Button) v.findViewById(R.id.logcat_reset_loglevel_defaults);
+    resetLogLevelButton.setOnClickListener(new View.OnClickListener() {
+      @Override
+      public void onClick(View v) {
+        FragmentManager fm = getActivity().getSupportFragmentManager();
+        ResetLogLevelDialog dialog
+            = ResetLogLevelDialog.newInstance(getString(R.string.reset_log_level_dialog_title));
+        dialog.setTargetFragment(LogcatSettingsFragment.this, REQUEST_CODE_DIALOG_RESET_ALL_LOG_LEVELS);
+        dialog.show(fm, DIALOG_RESET_ALL_LOG_LEVELS_TAG);
+      }
+    });
+
+    // Register context listener
+    ListView listView = (ListView) v.findViewById(android.R.id.list);
+    listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
+    listView.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
+      @Override
+      public void onItemCheckedStateChanged(ActionMode mode,
+                                            int position,
+                                            long id,
+                                            boolean checked)
+      {
+        // Nothing to do here
+      }
+
+      @Override
+      public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+        MenuInflater inflater = mode.getMenuInflater();
+        inflater.inflate(R.menu.menu_logcat_settings_multiple_modal_menu, menu);
+        return true;
+      }
+
+      @Override
+      public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+        return false;
+      }
+
+      @Override
+      public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+        switch (item.getItemId()) {
+          case R.id.menu_item_delete_setting_item:
+            // TODO: Delete log item tag
+            return true;
+          default:
+            return false;
+        }
+      }
+
+      @Override
+      public void onDestroyActionMode(ActionMode mode) {
+        // Nothing to do here
+      }
+    });
+
+    return v;
+  }
+
+  @Override
+  public void onListItemClick(ListView l, View v, int position, long id) {
+    final String logTag = m_logcatSettingItems.get(position).getLogTag();
+
+    final FragmentManager fm = getActivity().getSupportFragmentManager();
+    final ResetLogLevelDialog dialog = ResetLogLevelDialog.newInstance(logTag, position);
+
+    dialog.setTargetFragment(LogcatSettingsFragment.this, REQUEST_CODE_DIALOG_SET_LOG_LEVEL);
+    dialog.show(fm, DIALOG_SET_LOG_LEVEL_TAG);
+  }
+
+  @Override
+  public void onActivityResult(int requestCode, int resultCode, Intent data) {
+    if (resultCode != Activity.RESULT_OK) {
+      return;
+    }
+
+    String newLogLevel;
+    switch (requestCode) {
+    case REQUEST_CODE_DIALOG_RESET_ALL_LOG_LEVELS:
+      newLogLevel = data.getStringExtra(ResetLogLevelDialog.EXTRA_RESET_LOG_LEVEL_VALUE);
+
+      // Update settings
+      for (LogcatSettingItem item : m_logcatSettingItems) {
+        item.setLogLevel(newLogLevel);
+      }
+
+      // Update UI
+      updateListUI();
+
+      // Save setting items
+      m_logcatSettingsManager.saveSettingItems();
+      break;
+    case REQUEST_CODE_DIALOG_SET_LOG_LEVEL:
+      newLogLevel = data.getStringExtra(ResetLogLevelDialog.EXTRA_RESET_LOG_LEVEL_VALUE);
+      final int listPosition
+          = data.getIntExtra(ResetLogLevelDialog.EXTRA_LOG_ITEM_LIST_POSITION, -1);
+
+      if (listPosition != -1) {
+        m_logcatSettingItems.get(listPosition).setLogLevel(newLogLevel);
+
+        // Update UI
+        updateListUI();
+
+        // Save setting items
+        m_logcatSettingsManager.saveSettingItems();
+      }
+      break;
+    }
+  }
+
+  /**
+   * Convenience method that updates the UI by notifying the backing list adapter
+   * that changes has been made to the underlying data set.
+   */
+  private void updateListUI() {
+    ((LogcatSettingsAdapter) getListAdapter()).notifyDataSetChanged();
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
+
+  /**
+   * Adapter for use by thi ListFragment that display a list of LogcatSettingItem.
+   */
+  private static class LogcatSettingsAdapter extends ArrayAdapter<LogcatSettingItem> {
+
+    public LogcatSettingsAdapter(Context context, ArrayList<LogcatSettingItem> objects) {
+      super(context, 0, objects);
+      m_layoutInflater = LayoutInflater.from(context);
+    }
+
+    @SuppressLint("InflateParams")
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+      SettingItemHolder holder;
+
+      if (convertView == null) {
+        holder = new SettingItemHolder();
+
+        convertView = m_layoutInflater.inflate(R.layout.list_item_setting_item, null);
+        convertView.setTag(holder);
+
+        holder.m_logTag = (TextView) convertView.findViewById(R.id.list_item_log_tag);
+        holder.m_logLevel = (TextView) convertView.findViewById(R.id.list_item_setting_log_level);
+      } else {
+        holder = (SettingItemHolder) convertView.getTag();
+      }
+
+      LogcatSettingItem item = getItem(position);
+      holder.m_logTag.setText(item.getLogTag());
+      holder.m_logLevel.setText(item.getLogLevel());
+
+      return convertView;
+    }
+
+    private static class SettingItemHolder {
+      private TextView m_logTag;
+      private TextView m_logLevel;
+    }
+
+    private final LayoutInflater m_layoutInflater;
+  }
+
+  /**
+   * Convenient dialog fragment that prompts for the log level value
+   * to reset all tags to.
+   */
+  public static class ResetLogLevelDialog extends DialogFragment {
+
+    public static ResetLogLevelDialog newInstance(String dialogTitle) {
+      return newInstance(dialogTitle, -1);
+    }
+
+    public static ResetLogLevelDialog newInstance(String dialogTitle, int listPosition) {
+      final Bundle args = new Bundle();
+      args.putSerializable(BUNDLE_KEY_DIALOG_TITLE, dialogTitle);
+
+      if (listPosition != -1) {
+        args.putSerializable(BUNDLE_KEY_DIALOG_LIST_POSITION, listPosition);
+      }
+
+      final ResetLogLevelDialog fragment = new ResetLogLevelDialog();
+      fragment.setArguments(args);
+
+      return fragment;
+    }
+
+    @NonNull
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+      final String [] logLevelValues
+          = getResources().getStringArray(R.array.reset_log_level_values);
+
+      final String dialogTitle = getArguments().getString(BUNDLE_KEY_DIALOG_TITLE);
+
+      return new AlertDialog.Builder(getActivity())
+                            .setTitle(dialogTitle)
+                            .setItems(
+                                logLevelValues,
+                                new DialogInterface.OnClickListener() {
+                                  @Override
+                                  public void onClick(DialogInterface dialog, int which) {
+                                    sendResult(Activity.RESULT_OK, logLevelValues[which]);
+                                  }
+                                })
+                            .create();
+    }
+
+    /**
+     * Convenient method to send data back to the fragment that presents this
+     * dialog.
+     *
+     * @param resultCode Result code to be passed back
+     * @param logLevelValue Log level value to be passed back
+     */
+    private void sendResult(int resultCode, String logLevelValue) {
+      if (getTargetFragment() == null) {
+        return;
+      }
+
+      // Create intent
+      Intent intent = new Intent();
+      intent.putExtra(EXTRA_RESET_LOG_LEVEL_VALUE, logLevelValue);
+
+      // Fill item position if present
+      final int logItemPosition = getArguments().getInt(BUNDLE_KEY_DIALOG_LIST_POSITION, -1);
+      if (logItemPosition != -1) {
+        intent.putExtra(EXTRA_LOG_ITEM_LIST_POSITION, logItemPosition);
+      }
+
+      // Send results
+      getTargetFragment().onActivityResult(getTargetRequestCode(), resultCode, intent);
+    }
+
+    /** Unique extra name to be used */
+    public static final String EXTRA_RESET_LOG_LEVEL_VALUE
+        = "net.named_data.nfd.reset_log_level_value";
+
+    public static final String EXTRA_LOG_ITEM_LIST_POSITION
+        = "net.named_data.nfd.log_item_list_position";
+
+    private static final String BUNDLE_KEY_DIALOG_TITLE
+        = "BUNDLE_KEY_DIALOG_TITLE";
+
+    private static final String BUNDLE_KEY_DIALOG_LIST_POSITION
+        = "BUNDLE_KEY_DIALOG_LIST_POSITION";
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
+
+  /** Array list that contains all the tags that are logged */
+  private ArrayList<LogcatSettingItem> m_logcatSettingItems;
+
+  /** Reference to the currently used LogcatSettingsManager */
+  private LogcatSettingsManager m_logcatSettingsManager;
+
+  /** Request code for dialog that gets the new log level for all tags */
+  private static final int REQUEST_CODE_DIALOG_RESET_ALL_LOG_LEVELS = 1;
+
+  /** Request code for dialog that gets the new log level for a single tag */
+  private static final int REQUEST_CODE_DIALOG_SET_LOG_LEVEL = 2;
+
+  /** Unique tag that identifies dialog fragment in the fragment manager */
+  private static final String DIALOG_RESET_ALL_LOG_LEVELS_TAG = "ResetAllLogLevelDialog";
+
+  /** Unique tag that identifies dialog fragment in the fragment manager */
+  private static final String DIALOG_SET_LOG_LEVEL_TAG = "SetLogLevelDialog";
+}
diff --git a/app/src/main/java/net/named_data/nfd/LogcatSettingsManager.java b/app/src/main/java/net/named_data/nfd/LogcatSettingsManager.java
new file mode 100644
index 0000000..e6bffcb
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/LogcatSettingsManager.java
@@ -0,0 +1,391 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015 Regents of the University of California
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ *
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd;
+
+import android.content.Context;
+import android.text.TextUtils;
+
+import net.named_data.nfd.utils.G;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Manager that controls the loading and saving of tags that are being logged.
+ */
+public class LogcatSettingsManager {
+
+  private LogcatSettingsManager(Context context) {
+    m_context = context;
+    m_logcatSettingItems = new ArrayList<>();
+    m_logLevelMap = new HashMap<>();
+    m_logcatTagsJSONSerializer = new LogcatTagsJSONSerializer(context, NFD_LOGCAT_TAGS_FILENAME);
+
+    // Populate log level map
+    loadLogLevelMap();
+
+    // Check if previous tags setting exists; otherwise load defaults
+    if (m_logcatTagsJSONSerializer.logcatTagsFileExists()) {
+      loadSettingItems();
+    } else {
+      loadDefaultSettingItems();
+    }
+
+    // Save setting items to file
+    saveSettingItems();
+
+    // Sort log tag name lexicographically
+    Collections.sort(m_logcatSettingItems, new Comparator<LogcatSettingItem>() {
+      @Override
+      public int compare(LogcatSettingItem lhs, LogcatSettingItem rhs) {
+        return lhs.getLogTag().compareTo(rhs.getLogTag());
+      }
+    });
+  }
+
+  /**
+   * Gets the singleton logcat settings manager.
+   *
+   * @param context Current application context
+   * @return The singleton settings manager
+   */
+  public static LogcatSettingsManager get(Context context) {
+    if (s_logcatSettingsManager == null) {
+      s_logcatSettingsManager = new LogcatSettingsManager(context.getApplicationContext());
+    }
+    return s_logcatSettingsManager;
+  }
+
+  /**
+   * Return the current working copy of setting items that are managed by the
+   * settings manager.
+   *
+   * @return Current setting items that are loaded
+   */
+  public ArrayList<LogcatSettingItem> getLogcatSettingItems() {
+    return m_logcatSettingItems;
+  }
+
+  /**
+   * Generate a string representing all the tags to be filtered by logcat
+   * and the relevant log levels.
+   *
+   * An example of a string returned by this method is:
+   *
+   * <pre>
+   *    NFDService:S Strategy:S TcpChannel:S TcpFactory:S TcpLocalFace:S UdpFactory:S *:S
+   * </pre>
+   *
+   * @return String representation of the tags and their relevant log levels to be
+   * filtered.
+   */
+  public String getTags() {
+    ArrayList<String> arr = new ArrayList<>();
+    for (LogcatSettingItem item : m_logcatSettingItems) {
+      arr.add(String.format("%s:%s", item.getLogTag(), getPriorityName(item.getLogLevel())));
+    }
+
+    // Sort and silence everything else by default
+    Collections.sort(arr);
+    arr.add("*:S");
+
+    return TextUtils.join(" ", arr);
+  }
+
+  /**
+   * Convenience method that saves all tags present in m_logcatSettingItems
+   * to the persistent JSON storage file.
+   */
+  public void saveSettingItems() {
+    // Create tags map
+    Map<CharSequence, CharSequence> map = new HashMap<>();
+    for (LogcatSettingItem item : m_logcatSettingItems) {
+      map.put(item.getLogTag(), getPriorityName(item.getLogLevel()));
+    }
+
+    // Save tags
+    try {
+      m_logcatTagsJSONSerializer.saveTags(map);
+    } catch (IOException e) {
+      G.Log("saveSettingItems(): Error: " + e);
+    }
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
+
+  /**
+   * Convenience method that loads all possible log level priority values as
+   * specified by the string array: R.array.logcat_log_level_map.
+   */
+  private void loadLogLevelMap() {
+    for (String item : m_context.getResources().getStringArray(R.array.logcat_log_level_map)) {
+      String [] arr = item.split(":");
+      m_logLevelMap.put(arr[0], arr[1]);
+    }
+  }
+
+  /**
+   * Convenience method that loads default values for tags and log levels that
+   * should be used. This method should be invoked when the application does not
+   * have previous JSON state information of the tags and log levels.
+   */
+  private void loadDefaultSettingItems() {
+    String [] defaults
+        = m_context.getResources().getStringArray(R.array.default_log_tags_and_levels);
+    for (String item : defaults) {
+      String [] arr = item.split(":");
+      m_logcatSettingItems.add(new LogcatSettingItem(arr[0], getVerbosePriorityName(arr[1])));
+    }
+  }
+
+  /**
+   * Convenience method that loads all tags that were previously stored and recorded. This
+   * method populates m_logcatSettingItems for use in a ListView to present all loaded tags and
+   * their relevant log levels.
+   */
+  private void loadSettingItems() {
+    Map<CharSequence, CharSequence> map;
+
+    try {
+      map = m_logcatTagsJSONSerializer.loadTags();
+
+      for (Map.Entry<CharSequence, CharSequence> entry : map.entrySet()) {
+        m_logcatSettingItems
+            .add(new LogcatSettingItem(entry.getKey().toString(),
+                getVerbosePriorityName(entry.getValue().toString())));
+      }
+    } catch (IOException | NullPointerException e) {
+      G.Log("loadSettingItems(): Error in loading tags from file: " + e);
+    }
+  }
+
+  /**
+   * Convenience method to get the verbose priority name. For instance, if
+   * "V" were passed in, the returned string would be "Verbose". This is
+   * dependent on the data that is loaded into m_logLevelMap.
+   *
+   * @param priority Short form priority name, e.g. "V"
+   * @return Verbose priority name, e.g. "Verbose" for a priority argument of "V"
+   */
+  private String getVerbosePriorityName(String priority) {
+    for (Map.Entry<CharSequence, CharSequence> item : m_logLevelMap.entrySet()) {
+      if (item.getValue().equals(priority)) {
+        return item.getKey().toString();
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Convenience method that gets the priority name from the verbose name. For instance,
+   * if "Verbose" were passed in, the returned string would be "V". This is
+   * dependent on the data that is loaded into m_logLevelMap.
+   *
+   * @param priorityVerboseName Verbose priority name, e.g. "Verbose"
+   * @return Short form priority name, e.g. "V"
+   */
+  private String getPriorityName(String priorityVerboseName) {
+    return m_logLevelMap.get(priorityVerboseName).toString();
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
+
+  /**
+   * JSON Serializer used to store JSON Tags and relevant settings to
+   * a local file. The JSON file format that this serializer stores
+   * in the private file is as such:
+   *
+   * <pre>
+   *   [
+   *     {
+   *       "TcpFactory": "V",
+   *       "CommandValidator": "V",
+   *       "RibManager": "V",
+   *       "Strategy": "V",
+   *       "FaceTable": "V",
+   *       "FibManager": "V",
+   *       "FaceManager": "V",
+   *       "PrivilegeHelper": "V",
+   *       "ManagerBase": "V",
+   *       "TcpChannel": "V",
+   *       "InternalFace": "V",
+   *       "TcpLocalFace": "V",
+   *       "RemoteRegistrator": "V",
+   *       "GeneralConfigSection": "V",
+   *       "UdpFactory": "V",
+   *       "StrategyChoice": "V",
+   *       "TablesConfigSection": "V"
+   *     }
+   *   ]
+   * </pre>
+   *
+   * Each line represents a log tag that is to be monitored as well as the
+   * log level to be monitored (aka priority level of log cat). Tags can be any
+   * tag for the logger to filter.
+   *
+   * The log level should be one of the following form:
+   *
+   * <pre>
+   *     Log Level | Meaning
+   *     ===================
+   *       V       : Verbose
+   *       D       : Debug
+   *       I       : Info
+   *       W       : Warning
+   *       E       : Error
+   *       F       : Fatal
+   *       S       : Silent
+   * </pre>
+   *
+   */
+  private static class LogcatTagsJSONSerializer {
+
+    public LogcatTagsJSONSerializer(Context context, String filename) {
+      m_context = context;
+      m_filename = filename;
+    }
+
+    /**
+     * Convenience method to save all tags and their respective log levels from the
+     * given map. The map should contain key-value pairs of the following format:
+     *
+     * <pre>
+     *   "TagName": "V"
+     * </pre>
+     *
+     * @param map Map to be converted saved as a JSON file
+     * @throws IOException
+     */
+    public void saveTags(Map<CharSequence, CharSequence> map) throws IOException {
+      // Create JSON Array
+      JSONArray array = new JSONArray().put(new JSONObject(map));
+
+      BufferedWriter writer = null;
+      try {
+        OutputStream out = m_context.openFileOutput(m_filename, Context.MODE_PRIVATE);
+        writer = new BufferedWriter(new OutputStreamWriter(out));
+        writer.write(array.toString());
+      } catch (IOException e) {
+        G.Log(String.format("saveTags(): Error while writing to file: %s - %s",
+            m_filename, e));
+      } finally {
+        if (writer != null) {
+          writer.close();
+        }
+      }
+    }
+
+    /**
+     * Convenience method to load all tags that were previously saved to a JSON
+     * file. The format depends on what was previously stored in the JSON file,
+     * but it is recommended to following the format set out in
+     * {@link
+     * net.named_data.nfd.LogcatSettingsManager.LogcatTagsJSONSerializer#saveTags(java.util.Map)}
+     *
+     * @return Map that was previously stored in the JSON file
+     * @throws IOException
+     */
+    public Map<CharSequence, CharSequence> loadTags() throws IOException {
+      Map<CharSequence, CharSequence> map = new HashMap<>();
+
+      BufferedReader reader = null;
+      try {
+        InputStream inputStream = m_context.openFileInput(m_filename);
+        reader = new BufferedReader(new InputStreamReader(inputStream));
+        StringBuilder jsonString = new StringBuilder();
+        String line;
+        while ((line = reader.readLine()) != null) {
+          jsonString.append(line);
+        }
+
+        // Parse JSON array
+        JSONArray array = (JSONArray) new JSONTokener(jsonString.toString()).nextValue();
+
+        // Populate map
+        for (int i=0; i<array.length(); i++) {
+          JSONObject jsonObject = array.getJSONObject(i);
+          Iterator<String> iterator = jsonObject.keys();
+          while (iterator.hasNext()) {
+            String key = iterator.next();
+            map.put(key, (CharSequence) jsonObject.get(key));
+          }
+        }
+      } catch (JSONException | IOException e) {
+        G.Log(String.format("saveTags(): Error while reading from file: %s - %s",
+            m_filename, e));
+      } finally {
+        if (reader != null) {
+          reader.close();
+        }
+      }
+
+      return map;
+    }
+
+    /**
+     * @return true if a previously saved file exists; false otherwise
+     */
+    public boolean logcatTagsFileExists() {
+      return m_context.getFileStreamPath(m_filename).exists();
+    }
+
+    /** Context for storing and retrieving files */
+    private final Context m_context;
+
+    /** File name to store JSON */
+    private final String m_filename;
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
+
+  /** Context for storage and retrieval of logcat settings (e.g. from Preferences or Files) */
+  private final Context m_context;
+
+  /** Array list that contains all the tags that are logged */
+  private final ArrayList<LogcatSettingItem> m_logcatSettingItems;
+
+  /** Mapping of log tag description to logcat priority setting */
+  private final Map<CharSequence, CharSequence> m_logLevelMap;
+
+  /** Reference to JSON Serializer for use */
+  private final LogcatTagsJSONSerializer m_logcatTagsJSONSerializer;
+
+  /** Singleton reference to a LogcatSettingsManager */
+  private static LogcatSettingsManager s_logcatSettingsManager;
+
+  /** NFD Logcat Tags JSON filename */
+  private static final String NFD_LOGCAT_TAGS_FILENAME = "NFD_LOGCAT_TAGS_FILE";
+}
diff --git a/app/src/main/java/net/named_data/nfd/MainActivity.java b/app/src/main/java/net/named_data/nfd/MainActivity.java
index e079e7c..945165a 100644
--- a/app/src/main/java/net/named_data/nfd/MainActivity.java
+++ b/app/src/main/java/net/named_data/nfd/MainActivity.java
@@ -19,329 +19,173 @@
 
 package net.named_data.nfd;
 
-import android.app.Activity;
-import android.app.DialogFragment;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.preference.Preference;
-import android.preference.PreferenceFragment;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.ActionBarActivity;
+import android.view.Menu;
+import android.view.MenuItem;
 
-import net.named_data.nfd.service.NfdService;
-import net.named_data.nfd.utils.G;
+import com.intel.jndn.management.types.FaceStatus;
 
-public class MainActivity extends Activity {
+import java.util.ArrayList;
 
-  MainFragment m_main = new MainFragment();
+/**
+ * Main activity that is loaded for the NFD app.
+ */
+public class MainActivity extends ActionBarActivity
+    implements DrawerFragment.DrawerCallbacks,
+               LogcatFragment.Callbacks,
+               FaceListFragment.Callbacks
+{
+
+  @Override
+  protected void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    setContentView(R.layout.activity_main);
+
+    FragmentManager fragmentManager = getSupportFragmentManager();
+
+    if (savedInstanceState != null) {
+      m_drawerFragment = (DrawerFragment)fragmentManager.findFragmentByTag(DrawerFragment.class.toString());
+    }
+
+    if (m_drawerFragment == null) {
+      ArrayList<DrawerFragment.DrawerItem> items = new ArrayList<DrawerFragment.DrawerItem>();
+
+      items.add(new DrawerFragment.DrawerItem(R.string.drawer_item_general, 0,
+                                              DRAWER_ITEM_GENERAL));
+      items.add(new DrawerFragment.DrawerItem(R.string.drawer_item_faces, 0,
+                                              DRAWER_ITEM_FACES));
+      items.add(new DrawerFragment.DrawerItem(R.string.drawer_item_routes, 0,
+                                              DRAWER_ITEM_ROUTES));
+      //    items.add(new DrawerFragment.DrawerItem(R.string.drawer_item_strategies, 0,
+      //                                            DRAWER_ITEM_STRATEGIES));
+      items.add(new DrawerFragment.DrawerItem(R.string.drawer_item_logcat, 0,
+                                              DRAWER_ITEM_LOGCAT));
+
+      m_drawerFragment = DrawerFragment.newInstance(items);
+
+      fragmentManager
+        .beginTransaction()
+        .replace(R.id.navigation_drawer, m_drawerFragment, DrawerFragment.class.toString())
+        .commit();
+    }
+  }
+
+  @Override
+  public boolean onCreateOptionsMenu(Menu menu) {
+    if (!m_drawerFragment.isDrawerOpen()) {
+      // Inflate current Activity's menu only if the drawer is not
+      // displayed; otherwise, allow the drawer to inflate its own
+      // menu in the action bar. Inflate activity wide menu here.
+      updateActionBar();
+      return true;
+    }
+    return super.onCreateOptionsMenu(menu);
+  }
+
+  @Override
+  public boolean onOptionsItemSelected(MenuItem item) {
+    return super.onOptionsItemSelected(item);
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
+
+  /**
+   * Convenience method that updates and display the current title in the Action Bar
+   */
+  @SuppressWarnings("deprecation")
+  private void updateActionBar() {
+    ActionBar actionBar = getSupportActionBar();
+    actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
+    actionBar.setDisplayShowTitleEnabled(true);
+    if (m_actionBarTitleId != -1) {
+      actionBar.setTitle(m_actionBarTitleId);
+    }
+  }
+
+  /**
+   * Convenience method that replaces the main fragment container with the
+   * new fragment and adding the current transaction to the backstack.
+   *
+   * @param fragment Fragment to be displayed in the main fragment container.
+   */
+  private void replaceContentFragmentWithBackstack(Fragment fragment) {
+    FragmentManager fragmentManager = getSupportFragmentManager();
+    fragmentManager.beginTransaction()
+        .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
+        .replace(R.id.main_fragment_container, fragment)
+        .addToBackStack(null)
+        .commit();
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
 
   @Override
   public void
-  onCreate(Bundle savedInstanceState)
-  {
-    super.onCreate(savedInstanceState);
+  onDrawerItemSelected(int itemCode, int itemNameId) {
 
-    getFragmentManager()
-      .beginTransaction()
-      .replace(android.R.id.content, m_main)
+    String fragmentTag = "net.named-data.nfd.content-" + String.valueOf(itemCode);
+    FragmentManager fragmentManager = getSupportFragmentManager();
+
+    // Create fragment according to user's selection
+    Fragment fragment = fragmentManager.findFragmentByTag(fragmentTag);
+    if (fragment == null) {
+      switch (itemCode) {
+        case DRAWER_ITEM_GENERAL:
+          fragment = MainFragment.newInstance();
+          break;
+        case DRAWER_ITEM_FACES:
+          fragment = FaceListFragment.newInstance();
+          break;
+        case DRAWER_ITEM_ROUTES:
+          fragment = RouteListFragment.newInstance();
+          break;
+        // TODO: Placeholders; Fill these in when their fragments have been created
+        //    case DRAWER_ITEM_STRATEGIES:
+        //      break;
+        case DRAWER_ITEM_LOGCAT:
+          fragment = LogcatFragment.newInstance();
+          break;
+        default:
+          // Invalid; Nothing else needs to be done
+          return;
+      }
+    }
+
+    // Update ActionBar title
+    m_actionBarTitleId = itemNameId;
+
+    fragmentManager.beginTransaction()
+      .replace(R.id.main_fragment_container, fragment, fragmentTag)
       .commit();
   }
 
-  public static class MainFragment extends PreferenceFragment {
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState)
-    {
-      super.onActivityCreated(savedInstanceState);
-
-      ///////////////////////////////////////////////////////////////////////////
-      // General settings
-      ///////////////////////////////////////////////////////////////////////////
-      addPreferencesFromResource(R.xml.pref_general);
-
-      m_startStopPref = findPreference("start_stop");
-      m_startStopPref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener()
-      {
-        @Override
-        public boolean onPreferenceClick(Preference preference)
-        {
-          toggleNfdState();
-          return true;
-        }
-      });
-
-      ///////////////////////////////////////////////////////////////////////////
-      // Face settings
-      ///////////////////////////////////////////////////////////////////////////
-      addPreferencesFromResource(R.xml.pref_face);
-
-      findPreference("create_face")
-        .setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
-          @Override
-          public boolean onPreferenceClick(Preference preference)
-          {
-            DialogFragment dialog = new FaceCreateDialog();
-            dialog.show(getFragmentManager(), "FaceCreateFragment");
-            return true;
-          }
-        });
-
-      ///////////////////////////////////////////////////////////////////////////
-      // Routes settings
-      ///////////////////////////////////////////////////////////////////////////
-      addPreferencesFromResource(R.xml.pref_routes);
-
-      findPreference("create_route")
-        .setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
-          @Override
-          public boolean onPreferenceClick(Preference preference)
-          {
-            DialogFragment dialog = new RouteCreateDialog();
-            dialog.show(getFragmentManager(), "RouteCreateFragment");
-            return true;
-          }
-        });
-    }
-
-    @Override
-    public void onResume() {
-      super.onResume();
-
-      // Bind to NfdService
-      bindNfdService();
-    }
-
-    @Override
-    public void onPause() {
-      super.onPause();
-
-      // Unbind from NfdService
-      unbindNfdService();
-    }
-
-    /**
-     * Thread safe way to start and stop the NFD through
-     * the UI Button.
-     */
-    private synchronized void toggleNfdState() {
-      if (m_isNfdRunning) {
-        m_startStopPref.setTitle(R.string.stopping_nfd);
-        sendNfdServiceMessage(NfdService.MESSAGE_STOP_NFD_SERVICE);
-      } else {
-        m_startStopPref.setTitle(R.string.starting_nfd);
-        sendNfdServiceMessage(NfdService.MESSAGE_START_NFD_SERVICE);
-      }
-    }
-
-    /**
-     * Convenience method to send a message to the NfdService
-     * through a Messenger.
-     *
-     * @param message Message from a set of predefined NfdService messages.
-     */
-    private synchronized void sendNfdServiceMessage(int message) {
-      try {
-        Message msg = Message.obtain(null, message);
-        msg.replyTo = m_clientMessenger;
-        m_nfdServiceMessenger.send(msg);
-      } catch (RemoteException e) {
-        // If Service crashes, nothing to do here
-        G.Log("Service Disconnected: " + e);
-      }
-    }
-
-    /**
-     * Enable UI Button once critical operations are completed.
-     */
-    private void enableNfdButton() {
-      m_startStopPref.setEnabled(true);
-    }
-
-    /**
-     * Disable UI Button to ensure user is unable to hit the button mutiple times.
-     */
-    private void disableNfdButton() {
-      m_startStopPref.setEnabled(false);
-    }
-
-    /**
-     * Thread safe way of flagging that the NFD is running.
-     *
-     * @param isNfdRunning true if NFD is running; false otherwise
-     */
-    private synchronized void setNfdRunningState(boolean isNfdRunning) {
-      m_isNfdRunning = isNfdRunning;
-    }
-
-    /**
-     * Toggle UI Button text to inform user of the next possible action.
-     *
-     * @param isNfdRunning true if NFD is currently running; false otherwise
-     */
-    private void setNfdButtonText(boolean isNfdRunning) {
-      m_startStopPref.setTitle(isNfdRunning ? R.string.stop_nfd : R.string.start_nfd);
-    }
-
-    /**
-     * Thread safe way of flagging that application is successfully connected
-     * to the NfdService.
-     *
-     * @param isNfdServiceConnected true if successfully connected to the NfdService;
-     *                              false otherwise
-     */
-    private synchronized void setNfdServiceConnected(boolean isNfdServiceConnected) {
-      m_isNfdServiceConnected = isNfdServiceConnected;
-    }
-
-    /**
-     * Method that binds the current activity to the NfdService.
-     */
-    private synchronized void bindNfdService() {
-      if (m_isNfdServiceBound == false) {
-        // Bind to Service
-        m_isNfdServiceBound = getActivity().bindService(
-          new Intent(getActivity(), NfdService.class),
-          m_ServiceConnection, Context.BIND_AUTO_CREATE);
-
-        G.Log("MainActivity::bindNfdService()");
-      }
-    }
-
-    /**
-     * Method that unbinds the current activity from the NfdService.
-     */
-    private synchronized void unbindNfdService() {
-      if (m_isNfdServiceBound == true) {
-        // Unbind from Service
-        getActivity().unbindService(m_ServiceConnection);
-        m_isNfdServiceBound = false;
-
-        G.Log("MainActivity::unbindNfdService()");
-      }
-    }
-
-    /**
-     * Client Message Handler.
-     *
-     * This handler is used to handle messages that are being sent back
-     * from the NfdService to the current application.
-     */
-    class ClientHandler extends Handler
-    {
-      @Override
-      public void handleMessage(Message msg) {
-        switch (msg.what) {
-          case NfdService.MESSAGE_NFD_RUNNING:
-            setNfdRunningState(true);
-            setNfdButtonText(true);
-            G.Log("ClientHandler: NFD is Running.");
-            break;
-
-          case NfdService.MESSAGE_NFD_STOPPED:
-            setNfdRunningState(false);
-            setNfdButtonText(false);
-            G.Log("ClientHandler: NFD is Stopped.");
-            break;
-
-          default:
-            super.handleMessage(msg);
-            break;
-        }
-
-        enableNfdButton();
-      }
-    }
-
-    /**
-     * Client ServiceConnection to NfdService.
-     */
-    private ServiceConnection m_ServiceConnection = new ServiceConnection() {
-      @Override
-      public void onServiceConnected(ComponentName className, IBinder service) {
-        // Establish Messenger to the Service
-        m_nfdServiceMessenger = new Messenger(service);
-
-        // Set service connected flag
-        setNfdServiceConnected(true);
-
-        // Check if NFD Service is running
-        try {
-          Message msg = Message.obtain(null,
-                                       NfdService.MESSAGE_IS_NFD_RUNNING);
-          msg.replyTo = m_clientMessenger;
-          m_nfdServiceMessenger.send(msg);
-        } catch (RemoteException e) {
-          // If Service crashes, nothing to do here
-          G.Log("onServiceConnected(): " + e);
-        }
-
-        G.Log("m_ServiceConnection::onServiceConnected()");
-      }
-
-      @Override
-      public void onServiceDisconnected(ComponentName componentName) {
-        // In event of unexpected disconnection with the Service; Not expecting to get here.
-        G.Log("m_ServiceConnection::onServiceDisconnected()");
-
-        // Update UI
-        disableNfdButton();
-        m_startStopPref.setTitle(R.string.reconnect_to_nfd);
-
-        // Reconnect to NfdService
-        setNfdServiceConnected(false);
-        retryConnectionToNfdService();
-      }
-    };
-
-    /**
-     * Attempt to reconnect to the NfdService.
-     *
-     * This method attempts to reconnect the application to the NfdService
-     * when the NfdService has been killed (either by the user or by the OS).
-     */
-    private void retryConnectionToNfdService() {
-      new Thread(){
-        @Override
-        public void run() {
-          while (m_isNfdServiceConnected == false) {
-            G.Log("Retrying connection to NFD Service ...");
-            bindNfdService();
-
-            try {
-              Thread.sleep(1000);
-            } catch (InterruptedException e) {
-              // Nothing to do here; Keep going.
-            }
-          }
-
-          G.Log("Reconnection to NFD Service is successful.");
-        }
-      }.start();
-    }
-
-    //////////////////////////////////////////////////////////////////////////////
-
-    /** Button that starts and stops the NFD */
-    private Preference m_startStopPref;
-
-    /** Flag that marks that application is bound to the NfdService */
-    private boolean m_isNfdServiceBound = false;
-
-    /** Flag that marks that application is connected to the NfdService */
-    private boolean m_isNfdServiceConnected = false;
-
-    /** Client Message Handler */
-    private final Messenger m_clientMessenger = new Messenger(new ClientHandler());
-
-    /** Messenger connection to NfdService */
-    private Messenger m_nfdServiceMessenger = null;
-
-    /** Flag that marks if the NFD is running */
-    private boolean m_isNfdRunning = false;
+  @Override
+  public void onDisplayLogcatSettings() {
+    replaceContentFragmentWithBackstack(LogcatSettingsFragment.newInstance());
   }
+
+  @Override
+  public void onFaceItemSelected(FaceStatus faceStatus) {
+    replaceContentFragmentWithBackstack(FaceStatusFragment.newInstance(faceStatus));
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
+
+  /** Reference to drawer fragment */
+  private DrawerFragment m_drawerFragment;
+
+  /** Title that is to be displayed in the ActionBar */
+  private int m_actionBarTitleId = -1;
+
+  /** Item code for drawer items: For use in onDrawerItemSelected() callback */
+  public static final int DRAWER_ITEM_GENERAL = 1;
+  public static final int DRAWER_ITEM_FACES = 2;
+  public static final int DRAWER_ITEM_ROUTES = 3;
+  public static final int DRAWER_ITEM_STRATEGIES = 4;
+  public static final int DRAWER_ITEM_LOGCAT = 5;
 }
diff --git a/app/src/main/java/net/named_data/nfd/MainFragment.java b/app/src/main/java/net/named_data/nfd/MainFragment.java
new file mode 100644
index 0000000..878e7b1
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/MainFragment.java
@@ -0,0 +1,318 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015 Regents of the University of California
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ *
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd;
+
+import android.annotation.SuppressLint;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Switch;
+
+import net.named_data.nfd.service.NfdService;
+import net.named_data.nfd.utils.G;
+
+public class MainFragment extends Fragment {
+
+  public static MainFragment newInstance() {
+    // Create fragment arguments here (if necessary)
+    return new MainFragment();
+  }
+
+  @Override
+  public View onCreateView(LayoutInflater inflater,
+                           @Nullable ViewGroup container,
+                           @Nullable Bundle savedInstanceState)
+  {
+    @SuppressLint("InflateParams")
+    View v =  inflater.inflate(R.layout.fragment_main, null);
+
+    m_nfdStartStopSwitch = (Switch) v.findViewById(R.id.nfd_start_stop_switch);
+    m_nfdStartStopSwitch.setOnClickListener(new View.OnClickListener() {
+      @Override
+      public void onClick(View v) {
+        toggleNfdState();
+      }
+    });
+
+    return v;
+  }
+
+  @Override
+  public void onResume () {
+    super.onResume();
+
+    // Bind to NfdService
+    bindNfdService();
+  }
+
+  @Override
+  public void onPause () {
+    super.onPause();
+
+    // Unbind from NfdService
+    unbindNfdService();
+  }
+
+  /**
+   * Thread safe way to start and stop the NFD through
+   * the UI Button.
+   */
+  private synchronized void toggleNfdState() {
+    if (m_isNfdRunning) {
+      m_nfdStartStopSwitch.setText(R.string.stopping_nfd);
+      sendNfdServiceMessage(NfdService.MESSAGE_STOP_NFD_SERVICE);
+    } else {
+      m_nfdStartStopSwitch.setText(R.string.starting_nfd);
+      sendNfdServiceMessage(NfdService.MESSAGE_START_NFD_SERVICE);
+    }
+  }
+
+  /**
+   * Convenience method to send a message to the NfdService
+   * through a Messenger.
+   *
+   * @param message Message from a set of predefined NfdService messages.
+   */
+  private synchronized void sendNfdServiceMessage(int message) {
+    try {
+      Message msg = Message.obtain(null, message);
+      msg.replyTo = m_clientMessenger;
+      m_nfdServiceMessenger.send(msg);
+    } catch (RemoteException e) {
+      // If Service crashes, nothing to do here
+      G.Log("Service Disconnected: " + e);
+    }
+  }
+
+  /**
+   * Enable UI Switch once critical operations are completed.
+   */
+  private void enableNfdSwitch() {
+    m_nfdStartStopSwitch.setEnabled(true);
+  }
+
+  /**
+   * Disable UI Switch to ensure user is unable to hit the switch multiple times.
+   */
+  private void disableNfdSwitch() {
+    m_nfdStartStopSwitch.setEnabled(false);
+  }
+
+  /**
+   * Thread safe way of flagging that the NFD is running.
+   *
+   * @param isNfdRunning true if NFD is running; false otherwise
+   */
+  private synchronized void setNfdRunningState(boolean isNfdRunning) {
+    m_isNfdRunning = isNfdRunning;
+  }
+
+  /**
+   * Toggle UI Switch to inform user of the next possible action.
+   *
+   * @param isNfdRunning true if NFD is currently running; false otherwise
+   */
+  private void setNfdSwitchState(boolean isNfdRunning) {
+    m_nfdStartStopSwitch.setText(isNfdRunning ? R.string.nfd_started : R.string.nfd_stopped);
+    m_nfdStartStopSwitch.setChecked(isNfdRunning);
+  }
+
+  /**
+   * Update UI Switch to inform user that the NFD Service has been disconnected
+   * and an attempt is made to reconnect with the NFD Service.
+   */
+  private void setNfdDisconnectedSwitchState() {
+    disableNfdSwitch();
+    m_nfdStartStopSwitch.setText(R.string.reconnect_to_nfd);
+    m_nfdStartStopSwitch.setChecked(false);
+  }
+
+  /**
+   * Thread safe way of flagging that application is successfully connected
+   * to the NfdService.
+   *
+   * @param isNfdServiceConnected true if successfully connected to the NfdService;
+   *                              false otherwise
+   */
+  private synchronized void setNfdServiceConnected(boolean isNfdServiceConnected) {
+    m_isNfdServiceConnected = isNfdServiceConnected;
+  }
+
+  /**
+   * Method that binds the current activity to the NfdService.
+   */
+  private synchronized void bindNfdService() {
+    if (!m_isNfdServiceBound) {
+      // Bind to Service
+      m_isNfdServiceBound = getActivity().bindService(
+          new Intent(getActivity(), NfdService.class),
+          m_ServiceConnection, Context.BIND_AUTO_CREATE);
+
+      G.Log("MainFragment::bindNfdService()");
+    }
+  }
+
+  /**
+   * Method that unbinds the current activity from the NfdService.
+   */
+  private synchronized void unbindNfdService() {
+    if (m_isNfdServiceBound) {
+      // Unbind from Service
+      getActivity().unbindService(m_ServiceConnection);
+      m_isNfdServiceBound = false;
+
+      G.Log("MainFragment::unbindNfdService()");
+    }
+  }
+
+  /**
+   * Client Message Handler.
+   *
+   * This handler is used to handle messages that are being sent back
+   * from the NfdService to the current application.
+   */
+  private class ClientHandler extends Handler {
+    @Override
+    public void handleMessage(Message msg) {
+      switch (msg.what) {
+        case NfdService.MESSAGE_NFD_RUNNING:
+          setNfdRunningState(true);
+          setNfdSwitchState(true);
+          G.Log("ClientHandler: NFD is Running.");
+          break;
+
+        case NfdService.MESSAGE_NFD_STOPPED:
+          setNfdRunningState(false);
+          setNfdSwitchState(false);
+          G.Log("ClientHandler: NFD is Stopped.");
+          break;
+
+        default:
+          super.handleMessage(msg);
+          break;
+      }
+
+      enableNfdSwitch();
+    }
+  }
+
+  /**
+   * Client ServiceConnection to NfdService.
+   */
+  private final ServiceConnection m_ServiceConnection = new ServiceConnection() {
+    @Override
+    public void onServiceConnected(ComponentName className, IBinder service) {
+      // Establish Messenger to the Service
+      m_nfdServiceMessenger = new Messenger(service);
+
+      // Set service connected flag
+      setNfdServiceConnected(true);
+
+      // Check if NFD Service is running
+      try {
+        Message msg = Message.obtain(null,
+            NfdService.MESSAGE_IS_NFD_RUNNING);
+        msg.replyTo = m_clientMessenger;
+        m_nfdServiceMessenger.send(msg);
+      } catch (RemoteException e) {
+        // If Service crashes, nothing to do here
+        G.Log("onServiceConnected(): " + e);
+      }
+
+      G.Log("m_ServiceConnection::onServiceConnected()");
+    }
+
+    @Override
+    public void onServiceDisconnected(ComponentName componentName) {
+      // In event of unexpected disconnection with the Service; Not expecting to get here.
+      G.Log("m_ServiceConnection::onServiceDisconnected()");
+
+      // Update UI
+      setNfdDisconnectedSwitchState();
+
+      // Reconnect to NfdService
+      setNfdServiceConnected(false);
+      retryConnectionToNfdService();
+    }
+  };
+
+  /**
+   * Attempt to reconnect to the NfdService.
+   *
+   * This method attempts to reconnect the application to the NfdService
+   * when the NfdService has been killed (either by the user or by the OS).
+   */
+  private void retryConnectionToNfdService() {
+    new Thread(){
+      @Override
+      public void run() {
+        // TODO: Trying infinitely doesn't make sense.
+        // Convert this to an AsyncTask that:
+        //    - has a fixed number of retries
+        //    - update UI to inform user of the progress
+        //    - set switch to appropriate state when service fails to come online
+        while (!m_isNfdServiceConnected) {
+          G.Log("Retrying connection to NFD Service ...");
+          bindNfdService();
+
+          try {
+            Thread.sleep(1000);
+          } catch (InterruptedException e) {
+            // Nothing to do here; Keep going.
+          }
+        }
+
+        G.Log("Reconnection to NFD Service is successful.");
+      }
+    }.start();
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
+
+  /** Button that starts and stops the NFD */
+  private Switch m_nfdStartStopSwitch;
+
+  /** Flag that marks that application is bound to the NfdService */
+  private boolean m_isNfdServiceBound = false;
+
+  /** Flag that marks that application is connected to the NfdService */
+  private boolean m_isNfdServiceConnected = false;
+
+  /** Client Message Handler */
+  private final Messenger m_clientMessenger = new Messenger(new ClientHandler());
+
+  /** Messenger connection to NfdService */
+  private Messenger m_nfdServiceMessenger = null;
+
+  /** Flag that marks if the NFD is running */
+  private boolean m_isNfdRunning = false;
+}
diff --git a/app/src/main/java/net/named_data/nfd/RouteCreateDialog.java b/app/src/main/java/net/named_data/nfd/RouteCreateDialog.java
deleted file mode 100644
index aef371a..0000000
--- a/app/src/main/java/net/named_data/nfd/RouteCreateDialog.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2015 Regents of the University of California
- *
- * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
- * See AUTHORS.md for complete list of NFD Android authors and contributors.
- *
- * NFD Android is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NFD Android, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.named_data.nfd;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.WindowManager;
-import android.widget.EditText;
-
-import net.named_data.jndn.Name;
-import net.named_data.jndn_xx.util.FaceUri;
-import net.named_data.nfd.utils.Nfdc;
-import net.named_data.nfd.utils.NfdcAsyncTask;
-
-public class RouteCreateDialog extends DialogFragment
-{
-  @Override
-  public Dialog
-  onCreateDialog(Bundle savedInstanceState) {
-    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
-    LayoutInflater inflater = getActivity().getLayoutInflater();
-    builder
-      .setView(inflater.inflate(R.layout.create_route, null))
-      .setPositiveButton("Create route", new DialogInterface.OnClickListener()
-      {
-        @Override
-        public void onClick(DialogInterface dialog, int id)
-        {
-          EditText prefixBox = (EditText)getDialog().findViewById(R.id.prefix);
-          EditText uriBox = (EditText)getDialog().findViewById(R.id.faceUri);
-          final String prefix = prefixBox.getText().toString();
-          final String uri = uriBox.getText().toString();
-          new NfdcAsyncTask(getActivity(),
-                            new NfdcAsyncTask.Task()
-                            {
-                              public String
-                              runTask() throws Exception
-                              {
-                                try {
-                                  Nfdc nfdc = new Nfdc();
-                                  int faceId = nfdc.faceCreate(m_uri);
-                                  boolean ok = nfdc.ribRegisterPrefix(new Name(prefix), faceId, 10, true, false);
-                                  nfdc.shutdown();
-                                  if (ok) {
-                                    return "OK";
-                                  }
-                                  else {
-                                    return "Failed register prefix";
-                                  }
-                                } catch (FaceUri.CanonizeError e) {
-                                  return "Error creating face (" + e.getMessage() + ")";
-                                } catch (FaceUri.Error e) {
-                                  return "Error creating face (" + e.getMessage() + ")";
-                                }
-                              }
-
-                              ///////////////////////////
-                              private String m_uri = uri;
-                            }).execute();
-        }
-      })
-      .setNegativeButton("Cancel", new DialogInterface.OnClickListener()
-      {
-        public void onClick(DialogInterface dialog, int id)
-        {
-          RouteCreateDialog.this.getDialog().cancel();
-        }
-      })
-    ;
-
-    Dialog faceCreateDialog = builder.create();
-    faceCreateDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
-    return faceCreateDialog;
-  }
-}
diff --git a/app/src/main/java/net/named_data/nfd/RouteCreateDialogFragment.java b/app/src/main/java/net/named_data/nfd/RouteCreateDialogFragment.java
new file mode 100644
index 0000000..6517b32
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/RouteCreateDialogFragment.java
@@ -0,0 +1,77 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015 Regents of the University of California
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ *
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.DialogFragment;
+import android.view.LayoutInflater;
+import android.view.WindowManager;
+import android.widget.EditText;
+
+import net.named_data.jndn.Name;
+
+public class RouteCreateDialogFragment extends DialogFragment
+{
+  public static interface OnRouteCreateRequested {
+    public void
+    createRoute(Name prefix, String faceUri);
+  }
+
+  public static RouteCreateDialogFragment
+  newInstance() {
+    return new RouteCreateDialogFragment();
+  }
+
+  @NonNull @Override
+  public Dialog
+  onCreateDialog(Bundle savedInstanceState) {
+    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+    LayoutInflater inflater = getActivity().getLayoutInflater();
+    builder
+      .setView(inflater.inflate(R.layout.create_route, null))
+      .setPositiveButton(R.string.route_add_dialog_create_route, new DialogInterface.OnClickListener() {
+        @Override
+        public void onClick(DialogInterface dialog, int id)
+        {
+          EditText prefixBox = (EditText) getDialog().findViewById(R.id.prefix);
+          EditText uriBox = (EditText) getDialog().findViewById(R.id.faceUri);
+          final String prefix = prefixBox.getText().toString();
+          final String uri = uriBox.getText().toString();
+
+          ((OnRouteCreateRequested)getTargetFragment()).createRoute(new Name(prefix), uri);
+        }
+      })
+      .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+        public void onClick(DialogInterface dialog, int id)
+        {
+          RouteCreateDialogFragment.this.getDialog().cancel();
+        }
+      })
+    ;
+
+    Dialog faceCreateDialog = builder.create();
+    faceCreateDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
+    return faceCreateDialog;
+  }
+}
diff --git a/app/src/main/java/net/named_data/nfd/RouteListActivity.java b/app/src/main/java/net/named_data/nfd/RouteListActivity.java
deleted file mode 100644
index 5eca4cd..0000000
--- a/app/src/main/java/net/named_data/nfd/RouteListActivity.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2015 Regents of the University of California
- *
- * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
- * See AUTHORS.md for complete list of NFD Android authors and contributors.
- *
- * NFD Android is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NFD Android, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.named_data.nfd;
-
-import android.app.Activity;
-import android.app.FragmentManager;
-import android.app.FragmentTransaction;
-import android.app.ListFragment;
-import android.content.Context;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.TextView;
-
-import com.intel.jndn.management.types.RibEntry;
-import com.intel.jndn.management.types.Route;
-
-import net.named_data.nfd.utils.Nfdc;
-import net.named_data.nfd.utils.NfdcAsyncTask;
-
-import org.apache.commons.lang.StringUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class RouteListActivity extends Activity
-{
-  @Override
-  public void
-  onCreate(Bundle savedInstanceState)
-  {
-    super.onCreate(savedInstanceState);
-
-    FragmentManager fm = getFragmentManager();
-    FragmentTransaction ft = fm.beginTransaction();
-    if (fm.findFragmentById(android.R.id.content) == null) {
-      ft.add(android.R.id.content, m_routeListFragment);
-    }
-    else {
-      ft.replace(android.R.id.content, m_routeListFragment);
-    }
-    ft.commit();
-  }
-
-  public static class RouteListFragment extends ListFragment {
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState)
-    {
-      super.onActivityCreated(savedInstanceState);
-
-      RouteListAdapter adapter = new RouteListAdapter(getActivity());
-      setListAdapter(adapter);
-
-      adapter.updateFaceList();
-    }
-
-    private class RouteListAdapter extends BaseAdapter
-    {
-      public RouteListAdapter(Context context)
-      {
-        this.m_inflater = LayoutInflater.from(context);
-        this.m_context = context;
-      }
-
-      public void
-      updateFaceList()
-      {
-        new NfdcAsyncTask(m_context,
-                          new NfdcAsyncTask.Task() {
-                            public String
-                            runTask() throws Exception
-                            {
-                              synchronized (m_routesLock) {
-                                Nfdc nfdc = new Nfdc();
-                                m_routes = nfdc.ribList();
-                                nfdc.shutdown();
-                              }
-
-                              getActivity().runOnUiThread(new Runnable() {
-                                @Override
-                                public void run()
-                                {
-                                  notifyDataSetChanged();
-                                }
-                              });
-                              return null;
-                            }
-                          }).execute();
-      }
-
-      @Override
-      public int
-      getCount()
-      {
-        synchronized (m_routesLock) {
-          if (m_routes == null)
-            return 0;
-          else
-            return m_routes.size();
-        }
-      }
-
-      @Override
-      public Object
-      getItem(int position)
-      {
-        synchronized (m_routesLock) {
-          assert m_routes != null && position < m_routes.size();
-          return m_routes.get(position);
-        }
-      }
-
-      @Override
-      public long
-      getItemId(int position)
-      {
-        return position;
-      }
-
-      @Override
-      public View
-      getView(int position, View convertView, ViewGroup parent)
-      {
-        RouteListItemViewHolder holder;
-        if (convertView == null) {
-          convertView = this.m_inflater.inflate(android.R.layout.simple_list_item_2, parent, false);
-          holder = new RouteListItemViewHolder(convertView);
-          convertView.setTag(holder);
-        } else {
-          holder = (RouteListItemViewHolder)convertView.getTag();
-        }
-
-        RibEntry e;
-        synchronized (m_routesLock) {
-          e = m_routes.get(position);
-        }
-        holder.text1.setText(e.getName().toUri());
-
-        List<String> faceList = new ArrayList<>();
-        for (Route r : e.getRoutes()) {
-          faceList.add(String.valueOf(r.getFaceId()));
-        }
-        holder.text2.setText(StringUtils.join(faceList, ", "));
-
-        return convertView;
-      }
-
-      /////////////////////////////////////////////////////////////////////////
-      private LayoutInflater m_inflater;
-      private Context m_context;
-      private List<RibEntry> m_routes;
-      private final Object m_routesLock = new Object();
-
-    }
-
-    private static class RouteListItemViewHolder
-    {
-      RouteListItemViewHolder(View v)
-      {
-        text1 = (TextView)v.findViewById(android.R.id.text1);
-        text2 = (TextView)v.findViewById(android.R.id.text2);
-      }
-
-      /////////////////////////////////////////////////////////////////////////
-      public TextView text1;
-      public TextView text2;
-    }
-  }
-
-  /////////////////////////////////////////////////////////////////////////////
-  private RouteListFragment m_routeListFragment = new RouteListFragment();
-}
diff --git a/app/src/main/java/net/named_data/nfd/RouteListFragment.java b/app/src/main/java/net/named_data/nfd/RouteListFragment.java
new file mode 100644
index 0000000..f6c787f
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/RouteListFragment.java
@@ -0,0 +1,375 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015 Regents of the University of California
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ *
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd;
+
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.v4.app.ListFragment;
+import android.text.TextUtils;
+import android.util.Pair;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.intel.jndn.management.types.RibEntry;
+import com.intel.jndn.management.types.Route;
+
+import net.named_data.jndn.Name;
+import net.named_data.jndn_xx.util.FaceUri;
+import net.named_data.nfd.utils.Nfdc;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class RouteListFragment extends ListFragment implements RouteCreateDialogFragment.OnRouteCreateRequested {
+
+  public static RouteListFragment
+  newInstance() {
+    return new RouteListFragment();
+  }
+
+  @Override
+  public void
+  onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+
+    setListAdapter(new RouteListAdapter(getActivity()));
+  }
+
+  @Override
+  public View
+  onCreateView(LayoutInflater inflater,
+                           ViewGroup container,
+                           Bundle savedInstanceState)
+  {
+    View v = inflater.inflate(R.layout.fragment_route_list, null);
+    m_routeListInfoUnavailableView = v.findViewById(R.id.route_list_info_unavailable);
+
+    // Get progress bar spinner view
+    m_reloadingListProgressBar
+      = (ProgressBar)v.findViewById(R.id.route_list_reloading_list_progress_bar);
+
+    Button refreshRouteListButton = (Button) v.findViewById(R.id.route_list_refresh_button);
+    refreshRouteListButton.setOnClickListener(new View.OnClickListener() {
+      @Override
+      public void onClick(View v) {
+        retrieveRouteList();
+      }
+    });
+
+    Button addRouteButton = (Button)v.findViewById(R.id.route_list_add_button);
+    addRouteButton.setOnClickListener(new View.OnClickListener() {
+      @Override
+      public void onClick(View view)
+      {
+        RouteCreateDialogFragment dialog = RouteCreateDialogFragment.newInstance();
+        dialog.setTargetFragment(RouteListFragment.this, 0);
+        dialog.show(getFragmentManager(), "RouteCreateFragment");
+      }
+    });
+
+    return v;
+  }
+
+  @Override
+  public void
+  onResume() {
+    super.onResume();
+    startRouteListInfoRetrievalTask();
+  }
+
+  @Override
+  public void
+  onPause() {
+    super.onPause();
+    stopRouteListInfoRetrievalTask();
+
+    if (m_routeCreateAsyncTask != null) {
+      m_routeCreateAsyncTask.cancel(false);
+      m_routeCreateAsyncTask = null;
+    }
+  }
+
+  @Override
+  public void
+  createRoute(Name prefix, String faceUri)
+  {
+    m_routeCreateAsyncTask = new RouteCreateAsyncTask(prefix, faceUri);
+    m_routeCreateAsyncTask.execute();
+  }
+
+
+  /////////////////////////////////////////////////////////////////////////
+
+  /**
+   * Updates the underlying adapter with the given list of RibEntry.
+   *
+   * Note: This method should only be called from the UI thread.
+   *
+   * @param list Update ListView with the given List&lt;RibEntry&gt;
+   */
+  private void updateRouteList(List<RibEntry> list) {
+    if (list == null) {
+      m_routeListInfoUnavailableView.setVisibility(View.VISIBLE);
+      return;
+    }
+
+    ((RouteListAdapter)getListAdapter()).updateList(list);
+  }
+
+  /**
+   * Convenience method that starts the AsyncTask that retrieves the
+   * list of available routes.
+   */
+  private void retrieveRouteList() {
+    // Update UI
+    m_routeListInfoUnavailableView.setVisibility(View.GONE);
+
+    // Stop if running; before starting the new Task
+    stopRouteListInfoRetrievalTask();
+    startRouteListInfoRetrievalTask();
+  }
+
+  /**
+   * Create a new AsynTask for route list information retrieval.
+   */
+  private void startRouteListInfoRetrievalTask() {
+    m_routeListAsyncTask = new RouteListAsyncTask();
+    m_routeListAsyncTask.execute();
+  }
+
+  /**
+   * Stops a previously started AsyncTask.
+   */
+  private void stopRouteListInfoRetrievalTask() {
+    if (m_routeListAsyncTask != null) {
+      m_routeListAsyncTask.cancel(false);
+      m_routeListAsyncTask = null;
+    }
+  }
+
+  /////////////////////////////////////////////////////////////////////////
+
+  private static class RouteListAdapter extends BaseAdapter {
+
+    public RouteListAdapter(Context context) {
+      m_layoutInflater = LayoutInflater.from(context);
+    }
+
+    public void
+    updateList(List<RibEntry> ribEntries) {
+      m_ribEntries = ribEntries;
+      notifyDataSetChanged();
+    }
+
+    @Override
+    public int getCount()
+    {
+      return (m_ribEntries == null) ? 0 : m_ribEntries.size();
+    }
+
+    @Override
+    public RibEntry
+    getItem(int i)
+    {
+      assert m_ribEntries != null;
+      return m_ribEntries.get(i);
+    }
+
+    @Override
+    public long
+    getItemId(int i)
+    {
+      return i;
+    }
+
+    @Override
+    public View
+    getView(int position, View convertView, ViewGroup parent) {
+      RouteItemHolder holder;
+
+      if (convertView == null) {
+        holder = new RouteItemHolder();
+
+        convertView = m_layoutInflater.inflate(R.layout.list_item_route_item, null);
+        convertView.setTag(holder);
+
+        holder.m_uri = (TextView) convertView.findViewById(R.id.list_item_route_uri);
+        holder.m_faceList = (TextView) convertView.findViewById(R.id.list_item_face_list);
+      } else {
+        holder = (RouteItemHolder) convertView.getTag();
+      }
+
+      RibEntry entry = getItem(position);
+
+      // Prefix
+      holder.m_uri.setText(entry.getName().toUri());
+
+      // List of faces
+      List<String> faceList = new ArrayList<>();
+      for (Route r : entry.getRoutes()) {
+        faceList.add(String.valueOf(r.getFaceId()));
+      }
+      holder.m_faceList.setText(TextUtils.join(", ", faceList));
+
+      return convertView;
+    }
+
+    private static class RouteItemHolder {
+      private TextView m_uri;
+      private TextView m_faceList;
+    }
+
+    private final LayoutInflater m_layoutInflater;
+    private List<RibEntry> m_ribEntries;
+  }
+
+  private class RouteListAsyncTask extends AsyncTask<Void, Void, Pair<List<RibEntry>, Exception>> {
+    @Override
+    protected void
+    onPreExecute() {
+      // Display progress bar
+      m_reloadingListProgressBar.setVisibility(View.VISIBLE);
+    }
+
+    @Override
+    protected Pair<List<RibEntry>, Exception>
+    doInBackground(Void... params) {
+      Nfdc nfdc = new Nfdc();
+      Exception returnException = null;
+      List<RibEntry> routes = null;
+      try {
+        routes = nfdc.ribList();
+      }
+      catch (Exception e) {
+        returnException = e;
+      }
+      nfdc.shutdown();
+      return new Pair<>(routes, returnException);
+    }
+
+    @Override
+    protected void onCancelled() {
+      // Remove progress bar
+      m_reloadingListProgressBar.setVisibility(View.GONE);
+    }
+
+    @Override
+    protected void onPostExecute(Pair<List<RibEntry>, Exception> result) {
+      // Remove progress bar
+      m_reloadingListProgressBar.setVisibility(View.GONE);
+
+      if (result.second != null) {
+        Toast.makeText(getActivity(),
+                       "Error communicating with NFD (" + result.second.getMessage() + ")",
+                       Toast.LENGTH_LONG).show();
+      }
+
+      updateRouteList(result.first);
+    }
+  }
+
+
+  private class RouteCreateAsyncTask extends AsyncTask<Void, Void, String> {
+    public
+    RouteCreateAsyncTask(Name prefix, String faceUri)
+    {
+      m_prefix = prefix;
+      m_faceUri = faceUri;
+    }
+
+    @Override
+    protected String
+    doInBackground(Void... params)
+    {
+      try {
+        Nfdc nfdc = new Nfdc();
+        int faceId = nfdc.faceCreate(m_faceUri);
+        boolean ok = nfdc.ribRegisterPrefix(new Name(m_prefix), faceId, 10, true, false);
+        nfdc.shutdown();
+        if (ok) {
+          return "OK";
+        }
+        else {
+          return "Failed register prefix";
+        }
+      } catch (FaceUri.CanonizeError e) {
+        return "Error creating face (" + e.getMessage() + ")";
+      } catch (FaceUri.Error e) {
+        return "Error creating face (" + e.getMessage() + ")";
+      }
+      catch (Exception e) {
+        return "Error communicating with NFD (" + e.getMessage() + ")";
+      }
+    }
+
+    @Override
+    protected void
+    onPreExecute()
+    {
+      // Display progress bar
+      m_reloadingListProgressBar.setVisibility(View.VISIBLE);
+    }
+
+    @Override
+    protected void
+    onPostExecute(String status)
+    {
+      // Display progress bar
+      m_reloadingListProgressBar.setVisibility(View.VISIBLE);
+      Toast.makeText(getActivity(), status, Toast.LENGTH_LONG).show();
+
+      retrieveRouteList();
+    }
+
+    @Override
+    protected void
+    onCancelled()
+    {
+      // Remove progress bar
+      m_reloadingListProgressBar.setVisibility(View.GONE);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    private Name m_prefix;
+    private String m_faceUri;
+  }
+
+  /////////////////////////////////////////////////////////////////////////////
+
+  /** Reference to the most recent AsyncTask that was created for listing routes */
+  private RouteListAsyncTask m_routeListAsyncTask;
+
+  /** Reference to the view to be displayed when no information is available */
+  private View m_routeListInfoUnavailableView;
+
+  /** Progress bar spinner to display to user when destroying faces */
+  private ProgressBar m_reloadingListProgressBar;
+
+  /** Reference to the most recent AsyncTask that was created for creating a route */
+  private RouteCreateAsyncTask m_routeCreateAsyncTask;
+}
\ No newline at end of file
diff --git a/app/src/main/java/net/named_data/nfd/service/NfdService.java b/app/src/main/java/net/named_data/nfd/service/NfdService.java
index 2657790..069356b 100644
--- a/app/src/main/java/net/named_data/nfd/service/NfdService.java
+++ b/app/src/main/java/net/named_data/nfd/service/NfdService.java
@@ -32,7 +32,7 @@
 /**
  * NfdService that runs the native NFD.
  *
- * NfdSevice runs as an independent process within the Android OS that provides
+ * NfdService runs as an independent process within the Android OS that provides
  * service level features to start and stop the NFD native code through the
  * NFD JNI wrapper.
  *
@@ -44,7 +44,7 @@
    */
   static {
     // At least on Galaxy S3 (4.1.1), all shared library dependencies that are located
-    // in app's lib folder (not in /system/lib) need to be expliclity loaded.
+    // in app's lib folder (not in /system/lib) need to be explicitly loaded.
     // The script https://gist.github.com/cawka/11fe9c23b7a13960330b can be used to
     // calculate proper dependency load list.
     // For example:
@@ -144,7 +144,7 @@
    * started flag.
    */
   private synchronized void serviceStartNfd() {
-    if (m_isNfdStarted == false) {
+    if (!m_isNfdStarted) {
       m_isNfdStarted = true;
       startNfd(getFilesDir().getAbsolutePath());
 
@@ -165,7 +165,7 @@
    * started flag.
    */
   private synchronized void serviceStopNfd() {
-    if (m_isNfdStarted == true) {
+    if (m_isNfdStarted) {
       m_isNfdStarted = false;
 
       // TODO: Save NFD and NRD in memory data structures.
@@ -188,7 +188,7 @@
   /**
    * Message handler for the the NFD Service.
    */
-  class NfdServiceMessageHandler extends Handler {
+  private class NfdServiceMessageHandler extends Handler {
 
     @Override
     public void handleMessage(Message message) {
diff --git a/app/src/main/java/net/named_data/nfd/utils/LogcatTags.java b/app/src/main/java/net/named_data/nfd/utils/LogcatTags.java
deleted file mode 100644
index 8d20321..0000000
--- a/app/src/main/java/net/named_data/nfd/utils/LogcatTags.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2015 Regents of the University of California
- *
- * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
- * See AUTHORS.md for complete list of NFD Android authors and contributors.
- *
- * NFD Android is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NFD Android, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.named_data.nfd.utils;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.text.TextUtils;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Utility class to generate and retrieve log tags
- */
-public class LogcatTags
-{
-
-  /**
-   * Get tags that are stored for the given application's context.
-   *
-   * The method returns a string representation of tags that should be displayed
-   * to the UI. These tags and their output levels have been saved by the settings
-   * UI in the given context.
-   *
-   * An example of the return string is as such:
-   *
-   * <pre>
-   *    NFDService:S Strategy:S TcpChannel:S TcpFactory:S TcpLocalFace:S UdpFactory:S *:S
-   * </pre>
-   *
-   * @param context Current application context to retrieve log tags for
-   * @return String representation of log tags ready for use as arguments
-   *         to logcat.
-   */
-  public static String getTags(Context context) {
-    SharedPreferences preferences = context.getSharedPreferences(
-        PREFERENCE_NFD_TAGS_FILENAME, Context.MODE_PRIVATE);
-    String tagsString = preferences.getString(PREFERENCE_NFD_TAGS_KEY, "");
-
-    G.Log("loadCommand(): " + tagsString);
-
-    return tagsString;
-  }
-
-  /**
-   * Save tags as a string to the current context's preference.
-   *
-   * An example of a tag string that should be saved is shown as follows:
-   *
-   * <pre>
-   *    NFDService:S Strategy:S TcpChannel:S TcpFactory:S TcpLocalFace:S UdpFactory:S *:S
-   * </pre>
-   *
-   * NdfLogTagUtil.TagBuilder provides convenient methods to generate tag strings.
-   *
-   * @param context Current application context to save tag string to
-   * @param tagsString String representation of the tags to be saved
-   */
-  public static void saveTags(Context context, String tagsString) {
-    // Save preferred log level
-    SharedPreferences.Editor editor
-        = context.getSharedPreferences(
-        PREFERENCE_NFD_TAGS_FILENAME, Context.MODE_PRIVATE).edit();
-    editor.putString(PREFERENCE_NFD_TAGS_KEY, tagsString);
-    editor.commit();
-
-    G.Log("saveTags(): " + tagsString);
-  }
-
-  /**
-   * Convenience class to create and generate tags for use as arguments
-   * to logcat.
-   */
-  public static class TagBuilder {
-
-    private TagBuilder() {
-      m_tagMap = new HashMap<CharSequence, CharSequence>();
-    }
-
-    /**
-     * Get a new instance of a TagBuilder object.
-     *
-     * @return New TagBuilder ojbect for use.
-     */
-    public static TagBuilder getTagBuilder() {
-      return new TagBuilder();
-    }
-
-    /**
-     * @bried Add a tag with an associated log level value.
-     *
-     * Tag can be any tag for the logger to filter.
-     *
-     * The log level should be one of the following form:
-     * <pre>
-     *     Log Level | Meaning
-     *     ===================
-     *       V       : Verbose
-     *       D       : Debug
-     *       I       : Info
-     *       W       : Warning
-     *       E       : Error
-     *       F       : Fatal
-     *       S       : Silent
-     * </pre>
-     *
-     * @param tag Tag for the logger to filter for.
-     * @param logLevel Log level for specified tag.
-     */
-    public void addTag(CharSequence tag, CharSequence logLevel) {
-      m_tagMap.put(tag, logLevel);
-    }
-
-    /**
-     * Silence all tags that are not added to the current TagBuilder
-     * object.
-     */
-    public synchronized  void addSilenceNonRelatedTags() {
-      m_silenceNonRelatedTags = true;
-    }
-
-    /**
-     * Generate a string representing all the tags to be filtered and the
-     * relevant log levels.
-     *
-     * An example of a string returned by this method is:
-     *
-     * <pre>
-     *    NFDService:S Strategy:S TcpChannel:S TcpFactory:S TcpLocalFace:S UdpFactory:S *:S
-     * </pre>
-     *
-     * @return String representation of the tags and their relevant log levels to be
-     * filtered.
-     */
-    public String generateTagString() {
-      ArrayList<String> tags = new ArrayList<String>();
-      for (Map.Entry<CharSequence, CharSequence> item : m_tagMap.entrySet()) {
-        tags.add(String.format("%s:%s", item.getKey(), item.getValue()));
-      }
-
-      Collections.sort(tags);
-
-      if (m_silenceNonRelatedTags) {
-        tags.add("*:S");
-      }
-
-      return TextUtils.join(" ", tags);
-    }
-
-    /** Mapping of tag and log levels */
-    private Map<CharSequence, CharSequence> m_tagMap;
-
-    /** Flag that determines if all non related tags should be silenced */
-    private boolean m_silenceNonRelatedTags;
-  }
-
-  /** Preference filename */
-  private static final String PREFERENCE_NFD_TAGS_FILENAME = "NFD_TAGS_PREFERENCE_FILE";
-
-  /** Key in SharedPreference that stores the string of tags */
-  private static final String PREFERENCE_NFD_TAGS_KEY = "NFD_TAGS";
-}
diff --git a/app/src/main/java/net/named_data/nfd/utils/NfdcAsyncTask.java b/app/src/main/java/net/named_data/nfd/utils/NfdcAsyncTask.java
deleted file mode 100644
index cbf1406..0000000
--- a/app/src/main/java/net/named_data/nfd/utils/NfdcAsyncTask.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2015 Regents of the University of California
- *
- * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
- * See AUTHORS.md for complete list of NFD Android authors and contributors.
- *
- * NFD Android is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NFD Android, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.named_data.nfd.utils;
-
-import android.app.ProgressDialog;
-import android.content.Context;
-import android.os.AsyncTask;
-import android.widget.Toast;
-
-
-public class NfdcAsyncTask extends AsyncTask<Void, Void, String> {
-
-  public NfdcAsyncTask(Context context, Task task)
-  {
-    m_context = context;
-    m_progressBar = new ProgressDialog(m_context);
-    m_task = task;
-  }
-
-  public interface Task {
-    public String
-    runTask() throws Exception;
-  }
-
-  @Override
-  protected String
-  doInBackground(Void... params)
-  {
-    try {
-      return m_task.runTask();
-    }
-    catch (Exception e) {
-      return "Error communicating with NFD (" + e.getMessage() + ")";
-    }
-  }
-
-  @Override
-  protected void
-  onPostExecute(String result)
-  {
-    m_progressBar.dismiss();
-    if (result != null)
-      Toast.makeText(m_context.getApplicationContext(), result, Toast.LENGTH_LONG).show();
-  }
-
-  @Override
-  protected void
-  onPreExecute()
-  {
-    m_progressBar.setMessage("Communicating with NFD...");
-    m_progressBar.show();
-  }
-
-  @Override
-  protected void
-  onProgressUpdate(Void... values)
-  {
-  }
-
-  /////////////////////////////////////////////////////////////////////////////
-
-  private Context m_context = null;
-  private ProgressDialog m_progressBar = null;
-  private Task m_task = null;
-}
diff --git a/app/src/main/res/drawable/custom_white_button.xml b/app/src/main/res/drawable/custom_white_button.xml
new file mode 100644
index 0000000..8edfc54
--- /dev/null
+++ b/app/src/main/res/drawable/custom_white_button.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_pressed="true"
+        android:drawable="@android:color/darker_gray"
+        />
+    <item
+        android:state_pressed="false"
+        android:drawable="@android:color/transparent"
+        />
+</selector>
\ No newline at end of file
diff --git a/app/src/main/res/drawable/drawer_item_background_activated.xml b/app/src/main/res/drawable/drawer_item_background_activated.xml
new file mode 100644
index 0000000..1b497c0
--- /dev/null
+++ b/app/src/main/res/drawable/drawer_item_background_activated.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_activated="true"
+        android:drawable="@color/ndn_color_fire_bush"
+        />
+</selector>
\ No newline at end of file
diff --git a/app/src/main/res/drawable/face_list_item_background_activated.xml b/app/src/main/res/drawable/face_list_item_background_activated.xml
new file mode 100644
index 0000000..f6a9b13
--- /dev/null
+++ b/app/src/main/res/drawable/face_list_item_background_activated.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_activated="true"
+        android:drawable="@android:color/darker_gray"
+        />
+</selector>
\ No newline at end of file
diff --git a/app/src/main/res/drawable/item_background_activated.xml b/app/src/main/res/drawable/item_background_activated.xml
new file mode 100644
index 0000000..f6a9b13
--- /dev/null
+++ b/app/src/main/res/drawable/item_background_activated.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_activated="true"
+        android:drawable="@android:color/darker_gray"
+        />
+</selector>
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_fragment.xml b/app/src/main/res/layout/activity_fragment.xml
new file mode 100644
index 0000000..98ee139
--- /dev/null
+++ b/app/src/main/res/layout/activity_fragment.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/fragment_container"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    >
+
+</FrameLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..6d8b08a
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v4.widget.DrawerLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/drawer_layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".MainActivity">
+
+    <!-- Main container for fragments to be presented in -->
+    <FrameLayout
+        android:id="@+id/main_fragment_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        />
+
+    <!-- DrawerFragment for user navigation -->
+    <FrameLayout
+        android:id="@+id/navigation_drawer"
+        android:layout_width="@dimen/drawer_width"
+        android:layout_height="match_parent"
+        android:layout_gravity="start"
+        tools:layout="@layout/activity_main_drawer_listview"
+        />
+
+</android.support.v4.widget.DrawerLayout>
diff --git a/app/src/main/res/layout/activity_main_drawer_listview.xml b/app/src/main/res/layout/activity_main_drawer_listview.xml
new file mode 100644
index 0000000..7ca3700
--- /dev/null
+++ b/app/src/main/res/layout/activity_main_drawer_listview.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ListView 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"
+          android:choiceMode="singleChoice"
+          android:divider="@android:color/transparent"
+          android:dividerHeight="0dp"
+          android:background="#cccc"
+          tools:context=".DrawerFragment"
+/>
diff --git a/app/src/main/res/layout/create_face.xml b/app/src/main/res/layout/create_face.xml
index 6588dcc..02d40a6 100644
--- a/app/src/main/res/layout/create_face.xml
+++ b/app/src/main/res/layout/create_face.xml
@@ -7,7 +7,7 @@
     <TextView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        style="@style/dialog_margin"
+        style="@style/default_dialog_margin"
         android:textAppearance="?android:attr/textAppearanceMedium"
         android:text="Enter FaceUri for the remote NDN daemon"
         android:layout_gravity="center_horizontal"/>
@@ -16,7 +16,7 @@
         android:id="@+id/faceUri"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        style="@style/dialog_margin"
+        style="@style/default_dialog_margin"
         android:hint="Face URI"
         android:inputType="text"
         android:focusable="true"
diff --git a/app/src/main/res/layout/create_route.xml b/app/src/main/res/layout/create_route.xml
index ddf7fb2..67c7677 100644
--- a/app/src/main/res/layout/create_route.xml
+++ b/app/src/main/res/layout/create_route.xml
@@ -7,7 +7,7 @@
     <TextView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        style="@style/dialog_margin"
+        style="@style/default_dialog_margin"
         android:textAppearance="?android:attr/textAppearanceMedium"
         android:text="Enter prefix and FaceUri to register with NDN daemon"
         android:layout_gravity="center_horizontal"/>
@@ -16,7 +16,7 @@
         android:id="@+id/prefix"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        style="@style/dialog_margin"
+        style="@style/default_dialog_margin"
         android:hint="Prefix"
         android:inputType="text"
         android:focusable="true"
@@ -29,7 +29,7 @@
         android:id="@+id/faceUri"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        style="@style/dialog_margin"
+        style="@style/default_dialog_margin"
         android:hint="Face URI"
         android:inputType="text"
         android:focusable="true"
diff --git a/app/src/main/res/layout/face_status_item.xml b/app/src/main/res/layout/face_status_item.xml
deleted file mode 100644
index 9fd2a6b..0000000
--- a/app/src/main/res/layout/face_status_item.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="horizontal"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:padding="10dp">
-
-    <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textAppearance="?android:attr/textAppearanceMedium"
-        android:text="Title"
-        android:id="@+id/title"
-        android:textColor="@android:color/primary_text_light"/>
-
-    <TextView
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:textAppearance="?android:attr/textAppearanceMedium"
-        android:text="Value"
-        android:id="@+id/value"
-        android:textAlignment="gravity"
-        android:gravity="right"
-        android:textColor="@android:color/secondary_text_dark"
-        android:layout_gravity="right"/>
-</LinearLayout>
diff --git a/app/src/main/res/layout/fragment_face_detail.xml b/app/src/main/res/layout/fragment_face_detail.xml
new file mode 100644
index 0000000..c251036
--- /dev/null
+++ b/app/src/main/res/layout/fragment_face_detail.xml
@@ -0,0 +1,21 @@
+<?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"
+    style="@style/default_linear_layout_padding"
+    >
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/fragment_face_details_title"
+        style="?android:listSeparatorTextViewStyle"
+        />
+
+    <include
+        layout="@android:layout/list_content"
+        />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_face_list.xml b/app/src/main/res/layout/fragment_face_list.xml
new file mode 100644
index 0000000..f7c2388
--- /dev/null
+++ b/app/src/main/res/layout/fragment_face_list.xml
@@ -0,0 +1,80 @@
+<?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"
+    style="@style/default_linear_layout_padding"
+    >
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/face_list_actions_title"
+        style="?android:listSeparatorTextViewStyle"
+        />
+
+    <Button
+        android:id="@+id/face_list_refresh_button"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/face_list_refresh_face_list"
+        style="@style/default_custom_white_button"
+        />
+    <Button
+        android:id="@+id/face_list_add_button"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/face_list_add_face"
+        style="@style/default_custom_white_button"
+        />
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/face_list_list_of_faces_title"
+        style="?android:listSeparatorTextViewStyle"
+        />
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        >
+
+        <ProgressBar
+            android:id="@+id/face_list_reloading_list_progress_bar"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:visibility="gone"
+            />
+
+        <LinearLayout
+            android:id="@+id/face_list_info_unavailable"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="vertical"
+            android:visibility="gone"
+            >
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/oops"
+                />
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:text="@string/error_cannot_communicate_with_nfd"
+                />
+
+        </LinearLayout>
+
+        <include
+            layout="@android:layout/list_content"
+            />
+
+    </FrameLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_log.xml b/app/src/main/res/layout/fragment_logcat_output.xml
similarity index 100%
rename from app/src/main/res/layout/activity_log.xml
rename to app/src/main/res/layout/fragment_logcat_output.xml
diff --git a/app/src/main/res/layout/fragment_logcat_tags_list.xml b/app/src/main/res/layout/fragment_logcat_tags_list.xml
new file mode 100644
index 0000000..2688e0d
--- /dev/null
+++ b/app/src/main/res/layout/fragment_logcat_tags_list.xml
@@ -0,0 +1,36 @@
+<?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"
+    style="@style/default_linear_layout_padding"
+    >
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/fragment_logcat_general_operations_category"
+        style="?android:listSeparatorTextViewStyle"
+        />
+
+    <Button
+        android:id="@+id/logcat_reset_loglevel_defaults"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/logcat_reset_log_level_defaults"
+        style="@style/default_custom_white_button"
+        />
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/fragment_logcat_tags_n_log_levels"
+        style="?android:listSeparatorTextViewStyle"
+        />
+
+    <include
+        layout="@android:layout/list_content"
+        />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml
new file mode 100644
index 0000000..9fa4afa
--- /dev/null
+++ b/app/src/main/res/layout/fragment_main.xml
@@ -0,0 +1,25 @@
+<?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"
+    style="@style/default_linear_layout_padding"
+    >
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/pref_category_title_general"
+        style="?android:listSeparatorTextViewStyle"
+        />
+
+    <Switch
+        android:id="@+id/nfd_start_stop_switch"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal"
+        android:text="@string/checking_on_nfd"
+        />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_route_list.xml b/app/src/main/res/layout/fragment_route_list.xml
new file mode 100644
index 0000000..b1d481b
--- /dev/null
+++ b/app/src/main/res/layout/fragment_route_list.xml
@@ -0,0 +1,80 @@
+<?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"
+    style="@style/default_linear_layout_padding"
+    >
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/route_list_actions_title"
+        style="?android:listSeparatorTextViewStyle"
+        />
+
+    <Button
+        android:id="@+id/route_list_refresh_button"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/route_list_refresh_route_list"
+        style="@style/default_custom_white_button"
+        />
+    <Button
+        android:id="@+id/route_list_add_button"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/route_list_add_route"
+        style="@style/default_custom_white_button"
+        />
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/route_list_list_of_routes_title"
+        style="?android:listSeparatorTextViewStyle"
+        />
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        >
+
+        <ProgressBar
+            android:id="@+id/route_list_reloading_list_progress_bar"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:visibility="gone"
+            />
+
+        <LinearLayout
+            android:id="@+id/route_list_info_unavailable"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="vertical"
+            android:visibility="gone"
+            >
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/oops"
+                />
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:text="@string/error_cannot_communicate_with_nfd"
+                />
+
+        </LinearLayout>
+
+        <include
+            layout="@android:layout/list_content"
+            />
+
+    </FrameLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/list_item_drawer_item.xml b/app/src/main/res/layout/list_item_drawer_item.xml
new file mode 100644
index 0000000..08a598b
--- /dev/null
+++ b/app/src/main/res/layout/list_item_drawer_item.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="horizontal"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:background="@drawable/drawer_item_background_activated"
+              android:paddingLeft="@dimen/default_padding_left"
+              android:paddingRight="@dimen/default_padding_right"
+    >
+
+    <ImageView
+        android:id="@+id/drawer_item_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        />
+
+    <TextView
+        android:id="@+id/drawer_item_text"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:textAppearance="?android:attr/textAppearanceListItemSmall"
+        android:minHeight="?android:attr/listPreferredItemHeightSmall"
+        android:gravity="center_vertical"
+        />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/list_item_face_generic_item.xml b/app/src/main/res/layout/list_item_face_generic_item.xml
new file mode 100644
index 0000000..7683c68
--- /dev/null
+++ b/app/src/main/res/layout/list_item_face_generic_item.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    >
+
+    <TextView
+        android:id="@+id/list_item_generic_title"
+        android:text="Title"
+        style="@style/two_column_item_title"
+        />
+
+    <TextView
+        android:id="@+id/list_item_generic_value"
+        android:text="Value"
+        style="@style/two_column_item_secondary"
+        />
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/list_item_face_status_item.xml b/app/src/main/res/layout/list_item_face_status_item.xml
new file mode 100644
index 0000000..0b6a1d0
--- /dev/null
+++ b/app/src/main/res/layout/list_item_face_status_item.xml
@@ -0,0 +1,22 @@
+<?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="wrap_content"
+    android:background="@drawable/item_background_activated"
+    >
+
+    <TextView
+        android:id="@+id/list_item_face_uri"
+        android:text="FaceUri"
+        style="@style/two_row_item_title"
+        />
+
+    <TextView
+        android:id="@+id/list_item_face_id"
+        android:text="FaceId"
+        style="@style/two_row_item_secondary"
+        />
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/log_item.xml b/app/src/main/res/layout/list_item_log.xml
similarity index 100%
rename from app/src/main/res/layout/log_item.xml
rename to app/src/main/res/layout/list_item_log.xml
diff --git a/app/src/main/res/layout/list_item_route_item.xml b/app/src/main/res/layout/list_item_route_item.xml
new file mode 100644
index 0000000..baa6da0
--- /dev/null
+++ b/app/src/main/res/layout/list_item_route_item.xml
@@ -0,0 +1,22 @@
+<?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="wrap_content"
+    android:background="@drawable/item_background_activated"
+    >
+
+    <TextView
+        android:id="@+id/list_item_route_uri"
+        android:text="Route"
+        style="@style/two_row_item_title"
+        />
+
+    <TextView
+        android:id="@+id/list_item_face_list"
+        android:text="Faces"
+        style="@style/two_row_item_secondary"
+        />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/list_item_setting_item.xml b/app/src/main/res/layout/list_item_setting_item.xml
new file mode 100644
index 0000000..4d8498b
--- /dev/null
+++ b/app/src/main/res/layout/list_item_setting_item.xml
@@ -0,0 +1,20 @@
+<?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="wrap_content"
+    android:background="@drawable/item_background_activated"
+    >
+
+    <TextView
+        android:id="@+id/list_item_log_tag"
+        style="@style/two_row_item_title"
+        />
+
+    <TextView
+        android:id="@+id/list_item_setting_log_level"
+        style="@style/two_row_item_secondary"
+        />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_face_list_multiple_modal_menu.xml b/app/src/main/res/menu/menu_face_list_multiple_modal_menu.xml
new file mode 100644
index 0000000..1664611
--- /dev/null
+++ b/app/src/main/res/menu/menu_face_list_multiple_modal_menu.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+      xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <item
+        android:id="@+id/menu_item_delete_face_item"
+        android:icon="@android:drawable/ic_menu_delete"
+        android:title="@string/menu_item_delete_face_item"
+        app:showAsAction="ifRoom|withText"
+        />
+
+</menu>
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_log.xml b/app/src/main/res/menu/menu_log.xml
index b749bbe..a530929 100644
--- a/app/src/main/res/menu/menu_log.xml
+++ b/app/src/main/res/menu/menu_log.xml
@@ -7,6 +7,7 @@
         android:id="@+id/action_log_settings"
         android:title="@string/log_settings"
         android:orderInCategory="100"
-        app:showAsAction="never" />
+        app:showAsAction="never"
+        />
 
 </menu>
diff --git a/app/src/main/res/menu/menu_logcat_settings_multiple_modal_menu.xml b/app/src/main/res/menu/menu_logcat_settings_multiple_modal_menu.xml
new file mode 100644
index 0000000..2bbf336
--- /dev/null
+++ b/app/src/main/res/menu/menu_logcat_settings_multiple_modal_menu.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+      xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <item
+        android:id="@+id/menu_item_delete_setting_item"
+        android:icon="@android:drawable/ic_menu_delete"
+        android:title="@string/menu_item_delete_setting_item"
+        app:showAsAction="ifRoom|withText"
+        />
+
+</menu>
\ No newline at end of file
diff --git a/app/src/main/res/values-sw600dp/dimens.xml b/app/src/main/res/values-sw600dp/dimens.xml
new file mode 100644
index 0000000..a125d29
--- /dev/null
+++ b/app/src/main/res/values-sw600dp/dimens.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <dimen name="default_padding_left">48dp</dimen>
+    <dimen name="default_padding_right">48dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..92b2b4b
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <item name="ndn_color_fire_bush" type="color">#DB9710</item>
+    <item name="ndn_color_grenadier" type="color">#C04818</item>
+</resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 47c8224..5d96952 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -2,4 +2,8 @@
     <!-- Default screen margins, per the Android Design guidelines. -->
     <dimen name="activity_horizontal_margin">16dp</dimen>
     <dimen name="activity_vertical_margin">16dp</dimen>
+
+    <dimen name="default_padding_left">16dp</dimen>
+    <dimen name="default_padding_right">16dp</dimen>
+    <dimen name="drawer_width">192dp</dimen>
 </resources>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index a8f78fd..dc86688 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -2,15 +2,56 @@
     <string name="app_name">NFD</string>
     <string name="service_name">NFD Service</string>
     <string name="action_settings">Settings</string>
-    <string name="stop_nfd">Stop NFD</string>
-    <string name="start_nfd">Start NFD</string>
-    <string name="reconnecting_to_nfd">Please wait while we connect to the NFD Service.</string>
-    <string name="stopping_nfd">Stopping NFD ...</string>
-    <string name="starting_nfd">Starting NFD ...</string>
-    <string name="reconnect_to_nfd">Reconnecting to NFD Service</string>
-    <string name="loading_logger">Loading logger ...</string>
-    <string name="nfd_logger">NFD Logger</string>
+    <string name="nfd_stopped">NFD is stopped</string>
+    <string name="nfd_started">NFD is started</string>
+    <string name="stopping_nfd">Stopping NFD …</string>
+    <string name="starting_nfd">Starting NFD …</string>
+    <string name="reconnect_to_nfd">Reconnecting to NFD Service …</string>
+    <string name="loading_logger">Loading logger …</string>
     <string name="log_settings">Log Settings</string>
-    <string name="nfd_log_settings">NFD Log Settings</string>
-    <string name="checking_on_nfd">Checking on NFD Service ...</string>
+    <string name="checking_on_nfd">Checking on NFD Service …</string>
+    <string name="accessibility_open_drawer">Open NFD Navigation Drawer</string>
+    <string name="accessibility_close_drawer">Close NFD Navigation Drawer</string>
+    <string name="drawer_item_general">General</string>
+    <string name="drawer_item_faces">Faces</string>
+    <string name="drawer_item_routes">Routes</string>
+    <string name="drawer_item_strategies">Strategies</string>
+    <string name="drawer_item_logcat">Logcat</string>
+    <string name="menu_item_delete_setting_item">Delete</string>
+    <string name="fragment_logcat_general_operations_category">General Actions</string>
+    <string name="fragment_logcat_tags_n_log_levels">Tags &amp; Log Levels</string>
+    <string name="logcat_reset_log_level_defaults">Reset Log Level Defaults</string>
+    <string name="reset_log_level_dialog_title">Select default value for all tags</string>
+    <string name="route_list_actions_title">Route List Actions</string>
+    <string name="oops">Oops!</string>
+    <string name="error_cannot_communicate_with_nfd">Not available, check that NFD is started</string>
+    <string name="route_list_list_of_routes_title">List of Routes</string>
+    <string name="route_list_refresh_route_list">Refresh Route List</string>
+    <string name="route_list_add_route">Add Route</string>
+    <string name="face_list_actions_title">Face List Actions</string>
+    <string name="face_list_refresh_face_list">Refresh Face List</string>
+    <string name="face_list_add_face">Add Face</string>
+    <string name="face_list_list_of_faces_title">List of Faces</string>
+    <string name="fragment_face_details_title">Face Details</string>
+    <string name="menu_item_delete_face_item">Delete</string>
+    <string name="expire_never">Never</string>
+
+    <string-array name="face_scopes">
+        <item>Local</item>
+        <item>Non-local</item>
+    </string-array>
+
+    <string-array name="face_persistency">
+        <item>Persistent</item>
+        <item>On-demand</item>
+        <item>Permanent</item>
+    </string-array>
+
+    <string-array name="face_link_types">
+        <item>Point-to-point</item>
+        <item>Multi-access</item>
+    </string-array>
+
+    <string name="face_add_dialog_create_face">Create face</string>
+    <string name="route_add_dialog_create_route">Create route</string>
 </resources>
diff --git a/app/src/main/res/values/strings_logcat_settings_activity.xml b/app/src/main/res/values/strings_logcat_settings_activity.xml
index 36e89a7..47d5221 100644
--- a/app/src/main/res/values/strings_logcat_settings_activity.xml
+++ b/app/src/main/res/values/strings_logcat_settings_activity.xml
@@ -2,56 +2,8 @@
 
     <!-- Strings related to NFD Log Settings -->
 
-    <!-- Set all tags log level -->
-    <string name="pref_tags_log_level_title_key">All_Log_Levels_Key</string>
-    <string name="pref_tags_log_level_title">All Log Levels</string>
-    <string name="pref_tags_log_level_key">Reset_All_Tags_Log_Level_Key</string>
-    <string name="pref_tags_log_level">Reset</string>
-
-    <!-- Tags -->
-    <string name="pref_category_title_tags">NFD Tags &amp; Log Levels</string>
-    <string name="pref_category_title_tags_key">NFD_Tags_Key</string>
-
-    <!-- Tag Display Names -->
-    <string name="pref_tag_commandvalidator">CommandValidator</string>
-    <string name="pref_tag_facemanager">FaceManager</string>
-    <string name="pref_tag_facetable">FaceTable</string>
-    <string name="pref_tag_fibmanager">FibManager</string>
-    <string name="pref_tag_generalconfigsection">GeneralConfigSection</string>
-    <string name="pref_tag_internalface">InternalFace</string>
-    <string name="pref_tag_managerbase">ManagerBase</string>
-    <string name="pref_tag_privilegehelper">PrivilegeHelper</string>
-    <string name="pref_tag_remoteregistrator">RemoteRegistrator</string>
-    <string name="pref_tag_ribmanager">RibManager</string>
-    <string name="pref_tag_strategy">Strategy</string>
-    <string name="pref_tag_strategychoice">StrategyChoice</string>
-    <string name="pref_tag_tablesconfigsection">TablesConfigSection</string>
-    <string name="pref_tag_tcpchannel">TcpChannel</string>
-    <string name="pref_tag_tcpfactory">TcpFactory</string>
-    <string name="pref_tag_tcplocalface">TcpLocalFace</string>
-    <string name="pref_tag_udpfactory">UdpFactory</string>
-
-    <!-- Tag Keys -->
-    <string name="pref_tag_commandvalidator_key">CommandValidator</string>
-    <string name="pref_tag_facemanager_key">FaceManager</string>
-    <string name="pref_tag_facetable_key">FaceTable</string>
-    <string name="pref_tag_fibmanager_key">FibManager</string>
-    <string name="pref_tag_generalconfigsection_key">GeneralConfigSection</string>
-    <string name="pref_tag_internalface_key">InternalFace</string>
-    <string name="pref_tag_managerbase_key">ManagerBase</string>
-    <string name="pref_tag_privilegehelper_key">PrivilegeHelper</string>
-    <string name="pref_tag_remoteregistrator_key">RemoteRegistrator</string>
-    <string name="pref_tag_ribmanager_key">RibManager</string>
-    <string name="pref_tag_strategy_key">Strategy</string>
-    <string name="pref_tag_strategychoice_key">StrategyChoice</string>
-    <string name="pref_tag_tablesconfigsection_key">TablesConfigSection</string>
-    <string name="pref_tag_tcpchannel_key">TcpChannel</string>
-    <string name="pref_tag_tcpfactory_key">TcpFactory</string>
-    <string name="pref_tag_tcplocalface_key">TcpLocalFace</string>
-    <string name="pref_tag_udpfactory_key">UdpFactory</string>
-
     <!-- Log Levels -->
-    <string-array name="pref_log_levels">
+    <string-array name="reset_log_level_values">
         <item>Verbose</item>
         <item>Debug</item>
         <item>Info</item>
@@ -61,15 +13,40 @@
         <item>Silent</item>
     </string-array>
 
-    <!-- Log Level Command Tag-->
-    <string-array name="pref_log_level_values">
-        <item>V</item>
-        <item>D</item>
-        <item>I</item>
-        <item>W</item>
-        <item>E</item>
-        <item>F</item>
-        <item>S</item>
+    <!-- Log Level Mapping -->
+    <!--
+     This mapping is used for Logcat arguments and should be in the format of:
+        <display description>:<logcat priority value>
+    -->
+    <string-array name="logcat_log_level_map">
+        <item>Verbose:V</item>
+        <item>Debug:D</item>
+        <item>Info:I</item>
+        <item>Warn:W</item>
+        <item>Error:E</item>
+        <item>Fatal:F</item>
+        <item>Silent:S</item>
+    </string-array>
+
+    <!-- Default lag tags and log levels when no previous settings are found -->
+    <string-array name="default_log_tags_and_levels">
+        <item>CommandValidator:V</item>
+        <item>FaceManager:V</item>
+        <item>FaceTable:V</item>
+        <item>FibManager:V</item>
+        <item>GeneralConfigSection:V</item>
+        <item>InternalFace:V</item>
+        <item>ManagerBase:V</item>
+        <item>PrivilegeHelper:V</item>
+        <item>RemoteRegistrator:V</item>
+        <item>RibManager:V</item>
+        <item>Strategy:V</item>
+        <item>StrategyChoice:V</item>
+        <item>TablesConfigSection:V</item>
+        <item>TcpChannel:V</item>
+        <item>TcpFactory:V</item>
+        <item>TcpLocalFace:V</item>
+        <item>UdpFactory:V</item>
     </string-array>
 
 </resources>
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 9484591..08a8a5c 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -5,11 +5,57 @@
         <!-- Customize your theme here. -->
     </style>
 
-    <style name="dialog_margin">
+    <style name="default_dialog_margin">
         <item name="android:layout_marginTop">16dp</item>
         <item name="android:layout_marginLeft">4dp</item>
         <item name="android:layout_marginRight">4dp</item>
         <item name="android:layout_marginBottom">4dp</item>
     </style>
 
+    <style name="default_linear_layout_padding">
+        <item name="android:paddingLeft">@dimen/default_padding_left</item>
+        <item name="android:paddingRight">@dimen/default_padding_right</item>
+    </style>
+    
+    <style name="default_custom_white_button">
+        <item name="android:gravity">left|center_vertical</item>
+        <item name="android:background">@drawable/custom_white_button</item>
+        <item name="android:textColor">@android:color/primary_text_light</item>
+        <item name="android:textAppearance">?android:textAppearanceSmall</item>
+    </style>
+
+    <style name="item_title">
+        <item name="android:textAppearance">?android:textAppearanceSmall</item>
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:textColor">@android:color/primary_text_light</item>
+    </style>
+
+    <style name="item_secondary">
+        <item name="android:textAppearance">?android:textAppearanceSmall</item>
+        <item name="android:textColor">@android:color/secondary_text_dark</item>
+    </style>
+
+    <style name="two_row_item_title" parent="item_title">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+    </style>
+
+    <style name="two_row_item_secondary" parent="item_secondary">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+    </style>
+
+    <style name="two_column_item_title" parent="item_title">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+    </style>
+
+    <style name="two_column_item_secondary" parent="item_secondary">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:gravity">right</item>
+        <item name="android:layout_gravity">right</item>
+    </style>
+
 </resources>
diff --git a/app/src/main/res/xml/pref_general.xml b/app/src/main/res/xml/pref_general.xml
deleted file mode 100644
index 7195806..0000000
--- a/app/src/main/res/xml/pref_general.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
-  <!-- General section -->
-  <PreferenceCategory
-      android:key="@string/pref_category_title_general"
-      android:title="@string/pref_category_title_general">
-
-    <Preference
-      android:title="@string/start_nfd"
-      android:key="start_stop" />
-
-    <PreferenceScreen
-      android:title="NFD log"
-      android:key="nfd_log">
-        <intent
-            android:action="android.intent.action.VIEW"
-            android:targetPackage="net.named_data.nfd"
-            android:targetClass="net.named_data.nfd.LogcatActivity" />
-    </PreferenceScreen>
-
-  </PreferenceCategory>
-</PreferenceScreen>
diff --git a/app/src/main/res/xml/pref_nfd_log.xml b/app/src/main/res/xml/pref_nfd_log.xml
deleted file mode 100644
index 37a0432..0000000
--- a/app/src/main/res/xml/pref_nfd_log.xml
+++ /dev/null
@@ -1,175 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
-    <PreferenceCategory
-        android:key="@string/pref_tags_log_level_title_key"
-        android:title="@string/pref_tags_log_level_title">
-
-        <ListPreference
-            android:key="@string/pref_tags_log_level_key"
-            android:title="@string/pref_tags_log_level"
-            android:entries="@array/pref_log_levels"
-            android:entryValues="@array/pref_log_level_values"
-            android:negativeButtonText="@null"
-            android:positiveButtonText="@null" />
-
-    </PreferenceCategory>
-
-    <PreferenceCategory
-        android:key="@string/pref_category_title_tags_key"
-        android:title="@string/pref_category_title_tags">
-
-        <ListPreference
-            android:key="@string/pref_tag_commandvalidator_key"
-            android:title="@string/pref_tag_commandvalidator"
-            android:entries="@array/pref_log_levels"
-            android:entryValues="@array/pref_log_level_values"
-            android:defaultValue="I"
-            android:negativeButtonText="@null"
-            android:positiveButtonText="@null" />
-
-        <ListPreference
-            android:key="@string/pref_tag_facemanager_key"
-            android:title="@string/pref_tag_facemanager"
-            android:entries="@array/pref_log_levels"
-            android:entryValues="@array/pref_log_level_values"
-            android:defaultValue="I"
-            android:negativeButtonText="@null"
-            android:positiveButtonText="@null" />
-
-        <ListPreference
-            android:key="@string/pref_tag_facetable_key"
-            android:title="@string/pref_tag_facetable"
-            android:entries="@array/pref_log_levels"
-            android:entryValues="@array/pref_log_level_values"
-            android:defaultValue="I"
-            android:negativeButtonText="@null"
-            android:positiveButtonText="@null" />
-
-        <ListPreference
-            android:key="@string/pref_tag_fibmanager_key"
-            android:title="@string/pref_tag_fibmanager"
-            android:entries="@array/pref_log_levels"
-            android:entryValues="@array/pref_log_level_values"
-            android:defaultValue="I"
-            android:negativeButtonText="@null"
-            android:positiveButtonText="@null" />
-
-        <ListPreference
-            android:key="@string/pref_tag_generalconfigsection_key"
-            android:title="@string/pref_tag_generalconfigsection"
-            android:entries="@array/pref_log_levels"
-            android:entryValues="@array/pref_log_level_values"
-            android:defaultValue="I"
-            android:negativeButtonText="@null"
-            android:positiveButtonText="@null" />
-
-        <ListPreference
-            android:key="@string/pref_tag_internalface_key"
-            android:title="@string/pref_tag_internalface"
-            android:entries="@array/pref_log_levels"
-            android:entryValues="@array/pref_log_level_values"
-            android:defaultValue="I"
-            android:negativeButtonText="@null"
-            android:positiveButtonText="@null" />
-
-        <ListPreference
-            android:key="@string/pref_tag_managerbase_key"
-            android:title="@string/pref_tag_managerbase"
-            android:entries="@array/pref_log_levels"
-            android:entryValues="@array/pref_log_level_values"
-            android:defaultValue="I"
-            android:negativeButtonText="@null"
-            android:positiveButtonText="@null" />
-
-        <ListPreference
-            android:key="@string/pref_tag_privilegehelper_key"
-            android:title="@string/pref_tag_privilegehelper"
-            android:entries="@array/pref_log_levels"
-            android:entryValues="@array/pref_log_level_values"
-            android:defaultValue="I"
-            android:negativeButtonText="@null"
-            android:positiveButtonText="@null" />
-
-        <ListPreference
-            android:key="@string/pref_tag_remoteregistrator_key"
-            android:title="@string/pref_tag_remoteregistrator"
-            android:entries="@array/pref_log_levels"
-            android:entryValues="@array/pref_log_level_values"
-            android:defaultValue="I"
-            android:negativeButtonText="@null"
-            android:positiveButtonText="@null" />
-
-        <ListPreference
-            android:key="@string/pref_tag_ribmanager_key"
-            android:title="@string/pref_tag_ribmanager"
-            android:entries="@array/pref_log_levels"
-            android:entryValues="@array/pref_log_level_values"
-            android:defaultValue="I"
-            android:negativeButtonText="@null"
-            android:positiveButtonText="@null" />
-
-        <ListPreference
-            android:key="@string/pref_tag_strategy_key"
-            android:title="@string/pref_tag_strategy"
-            android:entries="@array/pref_log_levels"
-            android:entryValues="@array/pref_log_level_values"
-            android:defaultValue="I"
-            android:negativeButtonText="@null"
-            android:positiveButtonText="@null" />
-
-        <ListPreference
-            android:key="@string/pref_tag_strategychoice_key"
-            android:title="@string/pref_tag_strategychoice"
-            android:entries="@array/pref_log_levels"
-            android:entryValues="@array/pref_log_level_values"
-            android:defaultValue="I"
-            android:negativeButtonText="@null"
-            android:positiveButtonText="@null" />
-
-        <ListPreference
-            android:key="@string/pref_tag_tablesconfigsection_key"
-            android:title="@string/pref_tag_tablesconfigsection"
-            android:entries="@array/pref_log_levels"
-            android:entryValues="@array/pref_log_level_values"
-            android:defaultValue="I"
-            android:negativeButtonText="@null"
-            android:positiveButtonText="@null" />
-
-        <ListPreference
-            android:key="@string/pref_tag_tcpchannel_key"
-            android:title="@string/pref_tag_tcpchannel"
-            android:entries="@array/pref_log_levels"
-            android:entryValues="@array/pref_log_level_values"
-            android:defaultValue="I"
-            android:negativeButtonText="@null"
-            android:positiveButtonText="@null" />
-
-        <ListPreference
-            android:key="@string/pref_tag_tcpfactory_key"
-            android:title="@string/pref_tag_tcpfactory"
-            android:entries="@array/pref_log_levels"
-            android:entryValues="@array/pref_log_level_values"
-            android:defaultValue="I"
-            android:negativeButtonText="@null"
-            android:positiveButtonText="@null" />
-
-        <ListPreference
-            android:key="@string/pref_tag_tcplocalface_key"
-            android:title="@string/pref_tag_tcplocalface"
-            android:entries="@array/pref_log_levels"
-            android:entryValues="@array/pref_log_level_values"
-            android:defaultValue="I"
-            android:negativeButtonText="@null"
-            android:positiveButtonText="@null" />
-
-        <ListPreference
-            android:key="@string/pref_tag_udpfactory_key"
-            android:title="@string/pref_tag_udpfactory"
-            android:entries="@array/pref_log_levels"
-            android:entryValues="@array/pref_log_level_values"
-            android:defaultValue="I"
-            android:negativeButtonText="@null"
-            android:positiveButtonText="@null" />
-
-    </PreferenceCategory>
-</PreferenceScreen>