Designing the App screens

One of the most important features in an app is displaying the data correctly and professionally. Parsing the data from the database is a significant feature of an app which needs to be handled, but still, showing the correct data to the users is utterly significant to develop a successfull app.

The newest version of most Android apps published by Google have started featuring cards. Many other apps being designed since late 2014, are also following this trend. A card is nothing more than a layout or a view with a background drawable. And that drawable can be defined in XML as a layer drawable.

The app will have the following screens:

  • Home
  • Event
  • Facebook
  • Map
  • Forum

Just like the screens in the login, registration and forget password ones are defined by a the minimalistic card layout an orange background, as required by the customer.

The reason for using a fragment instead of an activity, is because one activity (which is the main activity) can contain several fragments which interchange. A Fragment represents a behavior or a portion of user interface in an Activity. Multiple fragments can be combined in a single activity to build a multi-pane UI and reuse a fragment in multiple activities. You can think of a fragment as a modular section of an activity, which has its own lifecycle, receives its own input events, and which you can add or remove while the activity is running.

public boolean onCreateOptionsMenu(Menu menu) {
    if (!mNavigationDrawerFragment.isDrawerOpen()) {
        getMenuInflater().inflate(R.menu.main, menu);
        restoreActionBar();
        return true;
    }
    return super.onCreateOptionsMenu(menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();

    if (id == R.id.action_settings) {
        return true;
    }

    return super.onOptionsItemSelected(item);
}

public static class PlaceholderFragment extends Fragment {

    private static final String ARG_SECTION_NUMBER = "section_number";

    public static PlaceholderFragment newInstance(int sectionNumber) {
        PlaceholderFragment fragment = new PlaceholderFragment();
        Bundle args = new Bundle();
        args.putInt(ARG_SECTION_NUMBER, sectionNumber);
        fragment.setArguments(args);
        return fragment;
    }

    public PlaceholderFragment() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_main, container, false);
        return rootView;
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        ((MainActivity) activity).onSectionAttached(getArguments().getInt(ARG_SECTION_NUMBER));
    }
}

A fragment must always be embedded in an activity and the fragment’s lifecycle is directly affected by the host activity’s lifecycle. For example, when the activity is paused, so are all fragments in it, and when the activity is destroyed, so are all fragments. However, while an activity is running, you can manipulate each fragment independently, such as add or remove them. When you perform such a fragment transaction, you can also add it to a back stack that’s managed by the activity—each back stack entry in the activity is a record of the fragment transaction that occurred.

HomeFragment

The Home Fragment contains a daily quotation which changes each time the app is started. Following are the xml and java code which build this fragment:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/RelativeLayout1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#F38121">

    <ScrollView
        android:id="@+id/scrollView1"
        android:layout_width="wrap_content"
        android:layout_height="350dp"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center_horizontal"
            android:orientation="vertical"
            android:layout_margin="15dp"
            android:padding="15dp"
            android:background="@drawable/card_background"
            android:weightSum="1">>

            <TextView
                android:id="@+id/Quote"
                android:layout_width="match_parent"
                android:layout_height="250dp"
                android:gravity="center_horizontal"
                android:scrollY="@dimen/activity_vertical_margin"
                android:scrollbarAlwaysDrawVerticalTrack="true"
                android:scrollbarStyle="insideOverlay"
                android:scrollbars="vertical"
                android:textColor="#878787"
                android:textSize="25sp"
                android:typeface="serif"
                android:layout_weight="1" />

            <TextView
                android:id="@+id/Author"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:gravity="end"
                android:textAppearance="?android:attr/textAppearanceMedium"
                android:textColor="#878787"
                android:textStyle="italic" />
        </LinearLayout>
    </ScrollView>

    <ScrollView
        android:id="@+id/scrollView2"
        android:layout_width="match_parent"
        android:layout_height="fill_parent"
        android:layout_below="@id/scrollView1"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentBottom="true">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="fill_parent"
            android:orientation="vertical"
            android:layout_margin="15dp"
            android:padding="15dp"
            android:background="@drawable/card_background">

            <TextView
                android:id="@+id/info"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_gravity="center_horizontal"
                android:gravity="center|center_horizontal"
                android:text="Chaplaincy \n G.F. Abela Junior College \n Ġużè Debono Square \n Msida MSD 1252 \n \n email: chaplaincy.jc@um.edu.mt \n Chaplain: Rev. Marco Portelli"
                android:singleLine="false"
                android:textAppearance="?android:attr/textAppearanceMedium" />

        </LinearLayout>
    </ScrollView>

</RelativeLayout>
package com.chaplaincy.jc.tibiapp;

import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.method.ScrollingMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import org.apache.http.NameValuePair;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * Created by Michele La Ferla on 21/02/2015.
 */
public class HomeFragment extends Fragment {

    public static String name, author, conCat;
    private ProgressDialog pDialog;

    JSONParser jParser = new JSONParser();

    ArrayList<HashMap<String, String>> InspireList;
    private static final String url_quote_details = "http://tibi.scienceontheweb.net/qt_handler.php";

    private static final String CONNECTION_STATUS = "success";
    private static final String TABLE_QUOTE = "Quote";
    private static final String COL_DESC = "Quote_Desc";
    private static final String COL_AUTHOR = "Quote_Author";

    JSONArray Inspiration = null;

    TextView quote, writer;

    public static final String ARG_SECTION_NUMBER = "section_number";

    public HomeFragment() {

    }

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
    }

    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        View HomeView = inflater.inflate(R.layout.fragment_home, container,
                false);

        return HomeView;
    }

    public void onStart() {
        super.onStart();

        InspireList = new ArrayList<HashMap<String, String>>();
        new LoadQuote().execute();
    }

    class LoadQuote extends AsyncTask<String, String, String> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            pDialog = new ProgressDialog(getActivity());
            pDialog.setMessage("Just a moment...");
            pDialog.setIndeterminate(true);
            pDialog.setCancelable(true);
            pDialog.show();
        }

        protected String doInBackground(String... args) {
            List<NameValuePair> params = new ArrayList<NameValuePair>();
            JSONObject json = jParser.getJSONFromUrl(url_quote_details,"GET", params);

            try {
                int success = json.getInt(CONNECTION_STATUS);

                if (success == 1) {
                    Inspiration = json.getJSONArray(TABLE_QUOTE);
                    for (int i = 0; i < Inspiration.length(); i++) {
                        JSONObject insp = Inspiration.getJSONObject(i);

                        name = insp.getString(COL_DESC);
                        author = insp.getString(COL_AUTHOR);

                        conCat = name + "_" + author;

                    }
                } else {
                    pDialog.dismiss();
                }
            } catch (JSONException e) {
                pDialog.dismiss();
                e.printStackTrace();
            }
            return conCat;
        }

        protected void onPostExecute(final String result) {
            pDialog.dismiss();
            getActivity().runOnUiThread(new Runnable() {
                @Override
                public void run() {

                    String[] divorce = result.split("_");
                    divorce[0] = name;
                    divorce[1] = author;

                    quote = (TextView) getView().findViewById(R.id.Quote);
                    quote.setMovementMethod(new ScrollingMovementMethod( ));
                    writer = (TextView) getView().findViewById(R.id.Author);

                    quote.setText(name);
                    writer.setText(author);
                }
            });
        }
    }
}

Event Fragment

The events fragment contains details for each event including its location. This location is then displayed on the map fragment. This is the xml code for the event fragment:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="15dp"
    android:paddingRight="15dp"
    android:descendantFocusability="beforeDescendants">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="15dp"
        android:paddingTop="15dp"
        android:paddingBottom="15dp"
        android:paddingRight="15dp"
        android:background="@drawable/card_selector"
        android:descendantFocusability="afterDescendants">

        <TextView
            android:id="@+id/text1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <TextView
            android:id="@+id/text2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <TextView
            android:id="@+id/text3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </LinearLayout>
</FrameLayout>
package com.chaplaincy.jc.tibiapp;

import android.app.ProgressDialog;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;

import org.apache.http.NameValuePair;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * Created by mlaferla on 27/01/2015.
 */

public class EventFragment extends ListFragment {

    ArrayList<HashMap<String, String>> eventList;
    private String url_all_events = "http://tibi.scienceontheweb.net/evt_handler.php";
    private ProgressDialog pDialog;
    static String id;
    static String name;
    static String date;
    static String time;

    JSONParser jParser = new JSONParser();

    // JSON Node names
    private static final String CONNECTION_STATUS = "success";
    private static final String TABLE_EVENT = "Event";
    private static final String pid = "evtID";
    private static final String COL_NAME = "evtName";
    private static final String COL_DATE = "evtDate";
    private static final String COL_TIME = "evtTime";

    public final static String DATE = COL_DATE;
    public final static String TIME = COL_TIME;

    JSONArray Events = null;

    public static final String ARG_SECTION_NUMBER = "section_number";

    public EventFragment() {
    }

    public void onStart() {
        super.onStart();

        eventList = new ArrayList<HashMap<String, String>>();

        ListView lv = getListView();
    }

    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_events, container, false);

        Intent intent = new Intent(getActivity(), MainActivity.class);

        intent.putExtra(DATE, date);
        intent.putExtra(TIME, time);
        startActivity(intent);

        return rootView;
    }

    public class LoadAllEvents extends AsyncTask<String, String, String> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            pDialog = new ProgressDialog(getActivity());
            pDialog.setMessage("Just a moment...");
            pDialog.setIndeterminate(true);
            pDialog.setCancelable(true);
            pDialog.show();
        }

        protected String doInBackground(String... args) {
            List<NameValuePair> params = new ArrayList<NameValuePair>();
            JSONObject json = jParser.getJSONFromUrl(url_all_events, "GET", params);

            try {
                // Checking for SUCCESS TAG
                int success = json.getInt(CONNECTION_STATUS);

                if (success == 1) {
                    Events = json.getJSONArray(TABLE_EVENT);
                    for (int i = 0; i < Events.length(); i++) {
                        JSONObject evt = Events.getJSONObject(i);

                        id = evt.getString(pid);
                        name = evt.getString(COL_NAME);
                        date = evt.getString(COL_DATE);
                        time = evt.getString(COL_TIME);

                        HashMap<String, String> hmap = new HashMap<String, String>();

                        hmap.put(pid, id);
                        hmap.put(COL_NAME, name);
                        hmap.put(COL_DATE, date);
                        hmap.put(COL_TIME, time);

                        eventList.add(hmap);
                    }
                } else {
                    // Options are not available or server is down.
                    // Dismiss the loading dialog and display an alert
                    // onPostExecute
                    pDialog.dismiss();
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }
            return null;
        }

        protected void onPostExecute(String file_url) {
            pDialog.dismiss();
            getActivity().runOnUiThread(new Runnable() {
                public void run() {
                    ListAdapter adapter = new SimpleAdapter(getActivity(),
                            eventList, R.layout.test_card, new String[] {
                            pid, COL_NAME, COL_DATE, COL_TIME },
                            new int[] { R.id.pid, R.id.evtType, R.id.evtDate, R.id.evtTime});

                    setListAdapter(adapter);
                }
            });
        }
    }
}

Facebook Fragment

This fragment contains a link to the official Facebook page of the client, to be able to view posts, updates and information about the organisation.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:background="@drawable/card_background"
    android:layout_height="match_parent">

    <WebView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/webView"
        android:padding="15dp"
        android:layout_gravity="center_horizontal"
        android:background="@drawable/card_background" />
</LinearLayout>
package com.chaplaincy.jc.tibiapp;

/**
 * Created by mlaferla on 27/01/2015.
 */
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;

public class FacebookFragment extends Fragment {

    public static final String ARG_SECTION_NUMBER = "section_number";

    public FacebookFragment() {

    }

    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        View mainView = inflater.inflate(R.layout.fragment_facebook, container, false);
        WebView webView = (WebView) mainView.findViewById(R.id.webView);

        webView.setWebViewClient(new MyWebViewClient());
        webView.getSettings().setSupportZoom(false);
        webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
        webView.getSettings().setAllowFileAccess(true);
        webView.getSettings().setDomStorageEnabled(true);
        webView.loadUrl("https://www.facebook.com/TiBiGroup");

        return mainView;
    }
}

Map Fragment

The map contains the details of the events and according to the longitude and latitude values for each event. Below is the design code for this fragment:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#F38121" >

    <fragment
        android:id="@+id/mapView"
        android:name="com.google.android.gms.maps.SupportMapFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="15dp"
        android:padding="15dp" />

</RelativeLayout>

The actual code which changes the location of each event will be discussed in a later blog post.

Forum Fragment

The forum fragment will be available to all those using the app to use the forum to communicate with each other. Since the app will be distributed to a small number of people, which is closed to a small community of the group members. Below is the code for the design of the forum fragment:

<LinearLayout 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:background="@drawable/tile_bg"
    android:orientation="vertical" >

    <ListView
        android:id="@+id/list_view_messages"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="@null"
        android:divider="@null"
        android:transcriptMode="alwaysScroll"
        android:stackFromBottom="true">
    </ListView>

    <LinearLayout
        android:id="@+id/llMsgCompose"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="@color/white"
        android:orientation="horizontal"
        android:weightSum="3" >

        <EditText
            android:id="@+id/inputMsg"
            android:layout_width="0dp"
            android:layout_height="fill_parent"
            android:layout_weight="2"
            android:background="@color/bg_msg_input"
            android:textColor="@color/text_msg_input"
            android:paddingLeft="6dp"
            android:paddingRight="6dp"/>

        <Button
            android:id="@+id/btnSend"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="@color/bg_btn_join"
            android:textColor="@color/white"
            android:text="Send" />
    </LinearLayout>

</LinearLayout>

User Registration and Handling

In the app which is being designed, one of the main features which are needed is user creation and management. Particularly, the app will need to handle the following:

  • Creation of a new user.
  • Forgetting a password.
  • Checking if a user exists.
  • Keeping the user logged into his/her account when the user returns to the app (using SharedPreferences).

To interact with MySQL database we need to build an API first. API job is to get the request from client, interact with database and finally give the response back to client. So we’ll create a simple PHP, MySQL API first. The Android Volley does the below jobs.

  1. Accepts requests in GET/POST methods.
  2. Interact with database by inserting / fetching data.
  3. Finally will give response back in JSON format.

Android Volley will be discussed in the next blog post, but for the time being we will be only discussing parsing through JSON.

Creation of a new User

Below is the creation of a new user screen on the application client tier. The fields in the screen accept values as required to be entered into the database.

Full name          String with capitalisation on first letters of new words

Email Address   String which must be entered in email format

Password           A masked string

Below is how the registration would look like:

registration

The data is then parsed and uploaded through the app through the following method:

public JSONObject getJSONFromUrl(String url, String method, List<NameValuePair> params) {

        // Making HTTP request
        try {

            // check for request method
            if (method == "POST") {
                // request method is POST
                // defaultHttpClient
                DefaultHttpClient httpClient = new DefaultHttpClient();
                HttpPost httpPost = new HttpPost(url);
                httpPost.setEntity(new UrlEncodedFormEntity(params));

                HttpResponse httpResponse = httpClient.execute(httpPost);
                HttpEntity httpEntity = httpResponse.getEntity();
                is = httpEntity.getContent();

            } else if (method == "GET") {
                // request method is GET
                DefaultHttpClient httpClient = new DefaultHttpClient();
                String paramString = URLEncodedUtils.format(params, "utf-8");
                url += "?" + paramString;
                HttpGet httpGet = new HttpGet(url);

                HttpResponse httpResponse = httpClient.execute(httpGet);
                HttpEntity httpEntity = httpResponse.getEntity();
                is = httpEntity.getContent();
            }

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(
                    is, "iso-8859-1"), 8);
            StringBuilder sb = new StringBuilder();
            String line = null;
            while ((line = reader.readLine()) != null) {
                sb.append(line + "\n");
            }
            is.close();
            json = sb.toString();
        } catch (Exception e) {
            Log.e("Buffer Error", "Error converting result " + e.toString());
        }

        // try parse the string to a JSON object
        try {
            jObj = new JSONObject(json);
        } catch (JSONException e) {
            Log.e("JSON Parser", "Error parsing data " + e.toString());
        }

        // return JSON String
        return jObj;

}

This then adds a new record to the database through the following PHP code:

public function saveUser($name, $password) {
 $usrID = uniqid('', true);
 $hash = $this->hashSSHA($password);
 $encrypted_password = $hash["encrypted"];
 $salt = $hash["salt"];
 $result = $con->query("INSERT INTO User (userID, fullName, userEmail, password, salt, dateCreated, dateModified) VALUES('NULL', '$usrName', '$usrEmail', '$encrypted_password', '$hash' CURRENT_TIMESTAMP, NOW())");
 
 if ($result) {
 $uid = mysqli_insert_id();
 $result = $con->query("SELECT * FROM User WHERE userID = '$usrID'");
 
 return mysqli_fetch_array($result, MYSQL_ASSOC);
 } else {
 return false;
 }
 }

This inserts a new record into the database.

Forgetting a Password

When a user forgets a password, the app has the facility of entering a new password through the following screen:

forget

The code behind this screen calls a PHP function which updates the record of the user with a new password. The scripting for this function has already been discussed in an earlier tutorial.

Checking if the User Exists

One of the most important steps in the handling of users is the validation of the correct username and password. This is done through the following scripts:

 public function getUser($usrEmail, $password) {
 
 $usrEmail = mysqli_real_escape_string($userEmail);
 $password = mysqli_real_escape_string($password);
 $query = "SELECT userEmail, password FROM User WHERE userEmail = '$usrEmail'";
 
 $result = mysqli_query($con, $query)or die(mysqli_error());
 
 $rowNum = mysqli_num_rows($result);
 
 if ($rowNum == 1) {
 $result = mysqli_fetch_array($result, MYSQL_ASSOC);
 $salt = $result['salt'];
 $encrypted_password = $result['encrypted_password'];
 $hash = $this->checkhashSSHA($salt, $password);
 
 if ($encrypted_password == $hash) {
 return $result;
 }
 } else {
 return false;
 }
 }
 
 public function isExisting($email) {
 $result = $con->query("SELECT userEmail from User WHERE userEmail = '$usrEmail'");
 $rowNum = mysqli_num_rows($result);
 
 if ($rowNum > 0) {
 return true;
 } else {
 return false;
 }
 }

Using Shared Preferences

With the usage of Shared Preferences a users’ details can easily pass data from one screen to another in an Android app. If you have a relatively small collection of key-values that you’d like to save, you should use the SharedPreferences APIs. A SharedPreferences object points to a file containing key-value pairs and provides simple methods to read and write them. Each SharedPreferences file is managed by the framework and can be private or shared.

This is particularly important when using the chat screen in the app, to identify the user name. Below is the code for this:

public class SessionManager {

        SharedPreferences pref;
        Editor editor;
        Context _context;
        int PRIVATE_MODE = 0;

        private static final String PREF_NAME = "usrLogin";
        private static final String IS_LOGIN = "IsLoggedIn";
        public static final String KEY_EMAIL = "email";

        public SessionManager(Context context){
            this._context = context;
            pref = _context.getSharedPreferences(PREF_NAME, PRIVATE_MODE);
            editor = pref.edit();
        }

        public void createLoginSession(String email){
            editor.putBoolean(IS_LOGIN, true);
            editor.putString(KEY_EMAIL, email);
            editor.commit();
        }

        public void checkLogin(){
            if(!this.isLoggedIn()){
                // user is not logged in redirect him to Login Activity
                Intent i = new Intent(_context, LoginActivity.class);

                i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

                // Staring Login Activity
                _context.startActivity(i);
            }

        }

        public HashMap<String, String> getUserDetails(){
            HashMap<String, String> user = new HashMap<String, String>();
            // user user email
            user.put(KEY_EMAIL, pref.getString(KEY_EMAIL, null));

            // return user
            return user;
        }

        public void logoutUser(){
            editor.clear();
            editor.commit();

            Intent i = new Intent(_context, LoginActivity.class);
            i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

            _context.startActivity(i);
        }

        public boolean isLoggedIn(){
            return pref.getBoolean(IS_LOGIN, false);
        }
}

Parsing the data to the App

After scripting the database through PHP, the next step would be to parse the data from the database to be viewable to the app. This can be through two main methods; either by parsing the data through XML, or else through JSON. Below I will be discussing the the pros and cons of both methods, and then give reasons why I chose to parse the data through JSON over XML.

XML Parsing

XML parsing in android is done using the DOM parser. The parser class mainly deals the following operations:

  • Getting XML content by making HTTP request.
  • Parsing XML content and getting DOM element of xml.
  • Get each xml child element value by passing element node name.

The xml document is parsed by making http requests through Android code. A typical http request would be the following:

public String getXmlFromUrl(String url) {
String xml = null;
        try {
            // defaultHttpClient
            DefaultHttpClient httpClient = new DefaultHttpClient();
            HttpPost httpPost = new HttpPost(url);
            HttpResponse httpResponse = httpClient.execute(httpPost);
            HttpEntity httpEntity = httpResponse.getEntity();
            xml = EntityUtils.toString(httpEntity);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // return XML
        return xml;
}
After getting the XML content, the next would be to get the DOM element of the XML file. Finally the data elements need to be getting each xml child element value by passing element node name. The code in the app would then call the DOM file to update the data.
One of the main advantages of XML parsing is that it is visually more readable.
On the other hand, it is more difficult to code-wise to parse the data through xml.
JSON Parsing 
JSON is very light weight, structured, easy to parse and much human readable. JSON is best alternative to XML when the android app needs to interchange data and get information from the server. The following code will parse the data from the data with regards to the daily random quotations, from the following URL: http://tibi.scienceontheweb.net/qt_handler.php
{
 "Quote": [
 {
 "quoteID": "1",
 "quoteName": "For God so loved the world that he gave his one and only Son, that whoever believes in him shall not perish but have eternal life.",
 "quoteAuthor": "John 3:16"
 }
 ],
 "success": 1
}

The above is the data parsed from the Quote table in the database.

In general all the JSON nodes will start with a square bracket or with a curly bracket. The difference between [ and { is, the square bracket ([) represents starting of an JSONArray node whereas curly bracket ({) represents JSONObject. So while accessing these nodes we need to call appropriate method to access the data.

The following is the code for parsing the data using the JSONParser class in Android:

public JSONObject getJSONFromUrl(String url, List<NameValuePair> params) {
    try {
        DefaultHttpClient httpClient = new DefaultHttpClient();
        HttpPost httpPost = new HttpPost(url);
        httpPost.setEntity(new UrlEncodedFormEntity(params));

        HttpResponse httpResponse = httpClient.execute(httpPost);
        HttpEntity httpEntity = httpResponse.getEntity();
        is = httpEntity.getContent();

    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    } catch (ClientProtocolException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

    try {
        BufferedReader reader = new BufferedReader(new InputStreamReader(
                is, "iso-8859-1"), 8);
        StringBuilder sb = new StringBuilder();
        String line = null;
        while ((line = reader.readLine()) != null) {
            sb.append(line + "\n");
        }
        is.close();
        json = sb.toString();
        Log.e("JSON", json);
    } catch (Exception e) {
        Log.e("Buffer Error", "Error converting result " + e.toString());
    }

    // try parse the string to a JSON object
    try {
        jObj = new JSONObject(json);
    } catch (JSONException e) {
        Log.e("JSON Parser", "Error parsing data " + e.toString());
    }

    // return JSON String
    return jObj;
}

Finally, the app will show the data in the following doInBackground method:

protected String doInBackground(String... args) {
    List<NameValuePair> params = new ArrayList<NameValuePair>();
    JSONObject json = jParser.getJSONFromUrl(url_quote_details,"GET", params);

    try {
        int success = json.getInt(CONNECTION_STATUS);

        if (success == 1) {
            Inspiration = json.getJSONArray(TABLE_QUOTE);
            for (int i = 0; i < Inspiration.length(); i++) {
                JSONObject insp = Inspiration.getJSONObject(i);

                name = insp.getString(COL_DESC);
                author = insp.getString(COL_AUTHOR);

                conCat = name + "_" + author;

            }
        } else {
            pDialog.dismiss();
        }
    } catch (JSONException e) {
        pDialog.dismiss();
        e.printStackTrace();
    }
    return conCat;
}

The app shows the data in the following method:

protected String doInBackground(String... args) {
    List<NameValuePair> params = new ArrayList<NameValuePair>();
    JSONObject json = jParser.getJSONFromUrl(url_quote_details,"GET", params);

    try {
        int success = json.getInt(CONNECTION_STATUS);

        if (success == 1) {
            Inspiration = json.getJSONArray(TABLE_QUOTE);
            for (int i = 0; i < Inspiration.length(); i++) {
                JSONObject insp = Inspiration.getJSONObject(i);

                name = insp.getString(COL_DESC);
                author = insp.getString(COL_AUTHOR);

                conCat = name + "_" + author;

            }
        } else {
            pDialog.dismiss();
        }
    } catch (JSONException e) {
        pDialog.dismiss();
        e.printStackTrace();
    }
    return conCat;
}

Below is how the parsed text would look in the App:

Capture

Some basic PHP scripting

As an app, the business logic will be retrieving data from the database, and displaying it in a list, with the relevant details. The database is needed for the following pages of the app:

  • getting the list of events available to the public
  • displaying a random inspirational quote each time the app is opened by the user
  • handling the user logins and registastations
  • saving feedback and sending it by email to the administators

The above features of the app, all need to be backed by qualitative sql statements to get the relevant data in the least time possible. These will be handled through PHP Server side scripting. PHP was chosen for such a project for the following reasons:

  • PHP is an ope source language which runs on various platforms (Windows, Linux, Unix, Mac OS X, etc.)
  • Since it is, open source, it is also compatible with almost all servers used nowadays (Apache, IIS, etc.)
  • PHP supports a wide range of databases engines, although it has its own database named mySQL.

Starting with the most basic php function, we will now explain how the php script getting the events will work. This will get the event name coupled with the date and time, and type. The type will show the type of event which the public will be attending to, so they cna be prepared beforehand of what to expect. This data is ordered in an ascending order to display the most forthcoming events first, and the later ones later.

<?php
require_once __DIR__ . '/config/db_connect.php';
$results_array = array();
$response = array();
if ($result = $con->query("SELECT eventID, eventName, eventType, DATE_FORMAT( eventDate, '%d/%m/%Y' ) AS DATE, TIME_FORMAT( eventDate, '%H:%i' ) AS TIME FROM Event WHERE eventDate >=CURRENT_TIMESTAMP( ) ORDER BY eventDate ASC LIMIT 0 , 5") 
or die('Table is currently not accessible')) {
if ($count = $result -> num_rows) {
$response["Event"] = array();
while ($rows = $result -> fetch_assoc()){
$results_array[] = $rows;
array_push($response["Event"], $rows);
}
 
$response["success"] = 1;
echo json_encode($response);
 
} else {
$response["success"] = 0;
$response["message"] = "No events found";
 
// echo no users JSON
echo json_encode($response);
}
}

While the first part of the code above connects to the database, whose details are saved in a seperate file, the script then select the relevant data from the Events table and displays it in an array. Finally, the script encodes the data in JSON format, to then be parsed later on, by the app. We will be discussing JSON in the next blog post.

Next is displaying a random daily inspirational quote each time the app is opened. In this case, the following php script is used:

<?php 
require_once __DIR__ . '/config/db_connect.php';
$results_array = array();
$response = array();
if ($result = $con->query("SELECT quoteID, quoteName, quoteAuthor FROM Quote WHERE RAND()<
(SELECT ((1/COUNT(*))*10) FROM Quote) ORDER BY RAND() LIMIT 1") 
or die('Table is currently not accessible')) {
if ($count = $result -> num_rows) {
$response["Quote"] = array();
 
while ($rows = $result -> fetch_assoc()){
$results_array[] = $rows;
array_push($response["Quote"], $rows);
}

$response["success"] = 1;
echo json_encode($response);
} else {
$response["success"] = 0;
$response["message"] = "No Quotes found";
 
// echo no users JSON
echo json_encode($response);
}
}

Here, the select statement uses the SQLI random function to select a quote at random and displays it to the user. The database table is populated with several inspirational quotes to choose from, and each quote is chosen at random by the script. Since some select statements consume more resources than others on the database, especially when it come to using the random function, therefore the best random select statement as used. Again here, the result of the statment needs to be encoded in JSON for use by the app, later on.

When it comes to user handling, the app will be needing 3 functions with which to relate to the database. These are:

  • Registering a new user.
  • Verifying if  a username and password entered by the user are correct.
  • Resetting the password for a user who has forgotten it.

These are all handled in one php file as shown in the following code snippet:

public function saveUser($name, $password) {
$usrID = uniqid('', true);
$hash = $this->hashSSHA($password);
$encrypted_password = $hash["encrypted"];
$salt = $hash["salt"];
$result = $con->query("INSERT INTO User (userID, fullName, userEmail, password, dateCreated,dateModified) VALUES('$usrID', '$usrName', '$usrEmail', '$encrypted_password', CURRENT_TIMESTAMP, NOW())");
 
if ($result) {
$uid = mysqli_insert_id();
$result = $con->query("SELECT * FROM User WHERE userID = '$usrID'");
 
return mysqli_fetch_array($result, MYSQL_ASSOC);
} else {
return false;
}
}
public function getUser($usrEmail, $password) {
 
$usrEmail = mysqli_real_escape_string($userEmail);
$password = mysqli_real_escape_string($password);
$query = "SELECT userEmail, password FROM User WHERE userEmail = '$usrEmail'"; 
$result = mysqli_query($con, $query)or die(mysqli_error());
 
$rowNum = mysqli_num_rows($result);
if ($rowNum == 1) {
$result = mysqli_fetch_array($result, MYSQL_ASSOC);
$salt = $result['salt'];
$encrypted_password = $result['encrypted_password'];
$hash = $this->checkhashSSHA($salt, $password);
 
if ($encrypted_password == $hash) {
return $result;
}
} else {
return false;
}
}
public function isExisting($email) {
$result = $con->query("SELECT userEmail from User WHERE userEmail = '$usrEmail'");
$rowNum = mysqli_num_rows($result);
 
if ($rowNum > 0) {
return true;
} else {
return false;
}
}

The code above is also responsible for hashing the password in such a way that it is not readable shown a security breach on the database occur.

The last script will handle the anonymous feedback messaging, whereby a user can anonymously post feedback about the experience in the app or about the group’s activities in general. Below is the php code:

$result = $con->query("INSERT INTO `Feedback` (`messageID`, `messageSubject`, `messageContent`, `dateCreated`, `messageSent`) VALUES (NULL, '$subject', '$message', CURRENT_TIMESTAMP, '0')");
 
 if ($result) {
 $uid = mysqli_insert_id();
 
 return mysqli_fetch_array($result, MYSQL_ASSOC);
 } else {
 return false;
 }
 
 $query = "SELECT `messageID`, `messageSubject`, `messageContent` FROM `Feedback` WHERE `mes sageSent` = '0' LIMIT 1";
 $result = mysqli_query($con, $query)or die(mysqli_error());
 
 $rowNum = mysqli_num_rows($result);
 
 if ($rowNum == 1) {
 $result = mysqli_fetch_array($result, MYSQL_ASSOC);
 } else {
 return false;
 }
$messageID = mysqli_real_escape_string('messageID');
$to = "michele.laferla@gmail.com"; 
 $subject = 'messageSubject'; 
 $email = $_POST['Email'] ; 
 $message = $_POST['messageContent'] ; 
 $headers = "From: noreply@tibiapp.com"; 
 $sent = mail($to, $subject, $message, $headers) ;
if($sent) {
 $result = $con->query("UPDATE `Feedback` SET `messageSent`= 1 WHERE `messageID` = 'messageID'");
 }

On of the most important considerations being taken when parsing data from the database onto the app is the fact that the queries can sometimes be resource comsuming, hence increasing the connectivity relay between the database and the app, which occurs over a normal internet connection. As we will see further on, all these php files discussed above will serve as a web service to get the data from the database and pass it over onto the app.

For this reason, the queries need to be as fast as possible, so as not to increase the the consumption of resources, which could result in a connection timeout with the server.

A second project – Material Design

With the introduction of the Lollipop; the latest mobile OS for Android devices, Google developers decided to introduce a minimalistic interior design. The idea behind it was to create a visual language for users, so much that it synthesizes the classic principles of attractive design with the innovation and possibility of technology and science introduced in the new operating system. This is called material design. In simpler words, material design is a comprehensive guide for visual, motion, and interaction design across platforms and devices. Android provides the following elements for you to build material design apps:

  • A new theme

The material theme provides a new style, system widgets that let developers and designers set their colour palette, and default animations for touch feedback and activity transitions.

  • New widgets for complex views

Android provides two new widgets for displaying cards and lists with material design styles and animations. These are the recycler and card views. In the app which we will be creating, we will mostly use the Card layout implemented in the CardView. CardView extends the FrameLayout class and lets you show information inside cards that have a consistent look across the platform. CardView widgets can have shadows and rounded corners.

  • New APIs for custom shadows and animations

In addition to the X and Y properties, views in Android now have a Z property. This new property represents the elevation of a view, which determines the size of the shadow and the drawing order. This gives a 3D look to each element in the layout. The new animation APIs let developers create custom animations for touch feedback in UI controls. Touch feedback animations are built into several standard views, such as buttons. The project will consist of a native Android application, which connects to a mySQL database through PHP scripting. The app will be developed for a closed audience group of youths who attend a particular religious oriented group, named TiBi. It will consist of the following features:

  1. Login for users including user registration
  2. A main page with contact information and a daily inspirational quote chosen randomly from a list of inspirational quotes.
  3. A list of events oragnised by the group with event details in the form of a list.
  4. A chat page, where all users can chat in a group.
  5. A map showing the location of the meeting places.
  6. A link to the Facebook page of the group.
  7. Push notification to inform users when an event is created.
  8. The ability to send anonymous feedback in the App.

The project will follow the Gantt Chart below to be able to be completed on time: http://publish.smartsheet.com/d246556440e64ed389d2a99fb1c42302 On the database end, the app will be needing the following tables to be able to communicate with the database:

  • Users
  • Events
  • Feedback
  • Quotes

ERD tibiApp

An ERD with the above table can be seen in the following link: Each of the tables will have the following columns with their data types:

User table
Column Name Data Type Parameters Description
userID INTEGER PK, Not null Contains unique ID for each user.
fullName VARCHAR(50) Not null Contains user name and surname.
userEmail VARCHAR(50) Unique, Not null Contains user’s email.
password VARCHAR(60) Not null Contains hashed password.
dateCreated TIMESTAMP Not null Date user was created in database.
dateModified TIMESTAMP Can be null Date user details were modified in database.
Event table
Column Name Data Type Parameters Description
eventID INTEGER PK, Not null Contains unique ID for each event.
eventName VARCHAR(50) Not null Contains an event’s name.
eventType VARCHAR(20) Not null Contains an event type for each event.
eventDate DATETIME Not null Contains event date and time for each event.
dateCreated TIMESTAMP Not null Date event was created in database.
dateModified TIMESTAMP Can be null Date event was modified in database.
Quotes table
Column Name Data Type Parameters Description
quoteID INTEGER PK, Not null Contains unique ID for each quote.
quoteName VARCHAR(250) Not null Contains a quote’s content.
quoteAuthor VARCHAR(20) Not null Contains the author of the quote.
dateCreated TIMESTAMP Not null Date quote was created in database.
Feedback table
Column Name Data Type Parameters Description
messageID INTEGER PK, Not null Contains unique ID for each feedback message.
messageContent VARCHAR(250) Not null Contains an message content.
dateCreated TIMESTAMP Not null Date message was created in database.
messageSent TINYINT Not null Controls if email with message was sent to Admin.

Tools which have been used

As a final post I will be including the different software applications which have been used to build the FireFIle application. I will be including the tools used to build both the front end, as used in the first 6 weeks to build the client side, and also the server side tools, used during the second 6 wekes of development.

Brackets

Brackets is an open source software application which is oriented to build client side applications with specific focus on JavaScript programming. This tools has helped me understand better the HTML and CSS technologies, since I had very little beckground of the technologies until then. Most importantly, it helped me in my testing in JavaScript functions, to make sure that the client side scripting worked perfectly.

Sublime Text

In some occasions, the free version of sublime text was used to explain JavaScript functions in details and be able to code them. This tool would be better in coding jQuery functions and 3rd party JavaScript libraries, but since it was required that we could not use these in the project, therefore, Sublime’s technology could not be used to the max.

Browsers

The mainstream browsers such as Google Chrome, Firefox, Internet Explorer and Safari were all used to test the viewability of the website across these different browsers. Since not all browsers support the same functions and libraries, it was therefore important for the site to be tested on all of these browsers. Sometimes, the client side code needed to cater for different browsers through the CSS scripts. This has already been discussed in earlier blog posts, when Client Side development was being discussed.

Also, the website needed to be tested on mobile devices, therefore a different css file needed to be included to handle smaller screens. This has also been discussed earlier.

XAMPP

Passing onto server side scripting, XAMPP was used as a package to create a local php server and develop the server side scripting. The program was also used to create the SSL certificate used to secure the website access and prevent it from being hacked by unwanted users. Bundled within the XAMPP package was the PHPmyAdmin database which was used to create the database to store the tables which contained the relevant data related to the FireFIle project.

PHPStorm

The server side scripting of the FireFile project was done by PHPStorm, which is a robust IDE specifically designed to code for PHP scripts. When the database was connected to the IDE, it was much easier to code the several functions for selections, insertions, updates and deletes.

Further Improvements

Nearing the end of the implementation of the FireFile project, this blog post will be dedicated to possible future features which could be implemented to the system. In some of these implementations, code will also be included to show that these could be implemented, had the time to implement the project been longer.

File Encryption/Decryption

This feature has already been discussed in details in the 8th blog with sample code to implement that feature in the post itself. Therefore here, we will go more in detail about the theoretical aspect behind the encryption/decryption of the files.

The below is a defenition of the crypt function which is a PHP function taken from W3 schools:

The crypt() function returns a string encrypted using DES, Blowfish, or MD5 algorithms. This function behaves different on different operating systems, some operating systems supports more than one type of encryption. PHP checks what algorithms are available and what algorithms to use when it is installed. The exact algorithm depends on the format and length of the salt parameter. Salts help make the encryption more secure by increasing the number of encrypted strings that can be generated for one specific string with one specific encryption method.

File Sharing

Although this has not been discussed beforehand in the php blog posts, the FireFile system is already prepared to implement this feature on the database end. What this feature basically does is actually give the ability to share files or entire directories with other users of FireFile. The restriction which FireFile will have is that in order to share files and folders, the other user need to have an account too. This can be done simply by entering an SQL statement to add the user_Id of the user whom the owner of the file wishes to access the file with. The user would then see the files and folders that have been shared with him/her in a sepearate section of the main page.

Inform the user by Email

There are functions in which FireFile can use the users’ email to inform them of certain communications. Some of these are the following:

1. When a new account is created

2. When a file/folder is shared with a user

3. To recover a lost password

4. To market promotions

5. Any form of communication with the users is ideally done through emails

The code which could be used to implement such features is the following:

<?php $to = 'joedoe@gmail.com'; 
$subject = 'FireFile - Account recovery'; 
$message = 'hello'; 
$headers = 'From: webmaster@example.com' . "\r\n" . 
'Reply-To: webmaster@example.com' . "\r\n" . 
'X-Mailer: PHP/' 
.phpversion(); mail($to, $subject, $message, $headers); 
?>

Create a mobile App

One of the most over-used technologies nowadays, is mobile development. A mobile application would make the whole repository accessible even on the go. All that is needed is a client side application for Android and Apple devices, since the server side coding has already been done by PHP. To our advantage is the fact that through JSON technology, the data can be accessed very easily from the database through PHP.

The Actual File Management System

Finally, we have come to the blog post where the actual file system will be created. This blog post will show how an actual file can be uploaded, registered into the database and downloaded onto the user’s PC. A file repository is very useful for several functions including a cloud repository for important files, file sharing with other users (which can be implemented as a future functionality in this project) and remote access to the files and folders from different locations.

A convention which was used in this project was that all php code was included in seperate files, and a call was made from the php files containing the HTML scripts to call the files. A simple file upload for example can be seen in the following code:

<?php $cam = "";
if (isset($_POST['folder'])){
$cam = "";
$path = $path . "/" . $_POST['folder'];
$cam = "?cam=".$_POST['folder'];
header("Location: index.php".$cam);
}?>
<?php
$uploadFolder = $path ."/";
entryLog($path . " -- ".gethostbyaddr(getenv("REMOTE_ADDR"))." ======== ".$_FILES['userFile']['name'] ." =========> UPLOAD");
if (!move_uploaded_file($_FILES['userFile']['tmp_name'], $uploadFolder . $_FILES['userFile']['name'])) {
print("<center><b>FireFile could not make this operation</b></center>");
}
else{
header("Location: main.php".$cam);
}
?>

The code above is able to upload a file and set it in a folder which contains the user_id. It does not contain the username for security reasons. Therefore, if there is any  security breaches into the FireFile system, the user’s Email would not be visible to the hackers. Instead they would only see the id.

The file location is inserted in the database through the following query:

On the other hand, the system gets the files already uploaded through the following code.

 $folderName = sqlQuery("SELECT folderName FROM FILES WHERE user_id = $user_id");
 $fileName = sqlQuery("SELECT fileName FROm Files WHERE user_id = $user_id");
 $fileLocation = "SELECT location from Files WHERE ID = $userId";

It then displays them in the HTML code through the following HTML code:

 <div id= "main-container">
 <nav class="main-menu">
 <ul>
 <li>
 <a href="#">
 <span class="nav-text">
 Download file
 </span>
 </a>
 </li>
 <li class="has-subnav">
 <a href="#">
 <span class="nav-text">
 Delete file
 </span>
 </a>
 </li>
 </ul>
 </nav>
 <section id = "control-container">
 <div id = "image-container">
 <?php
 foreach ($folderName as $foName){
 echo'<section>';
 echo'<img src="images/Red%20icon.png" style="width:100px;height:80px">';
 echo'<h3>'. $fileName['folderName']."</h3>";
 echo'</section>';
 }
 ?>
 </div>
 <div id = "image-container">
 <?php
 foreach ($fileName as $fiName){
 echo'<section>';
 echo'<img src="images/File.png" style="width:100px;height:80px">';
 echo'<h3>'. $fileName['folderName']."</h3>";
 echo'</section>';
 }
 ?>
 </div>
 </section>
 </div>

The files can also be saved in a directory through the following code:

if (isset($_GET['cam']))
 $user_id = $_GET['user_id'];
 $user_id = $_GET['cam'];
$pathFolder= $path."/";
if (isset($_GET['folderName']))
{
 $pathFolder = $pathFolder.$user_id."/".$_GET['folderName'];
 
 if (!file_exists($pathFolder))
 mkdir($pathFolder,0777,TRUE);
 $newFolder = sqlQuery("INSERT INTO Folder SET folder_Name = 'folderName', Location = $pathFolder, user_id = $user_id, createdDate = ");
}
header("Location: main.php?cam=".$user_id);

As the Files and folders can be created, they can also be deleted. The files can be deleted through the following code:

if (isset($_GET['cam']))
$user_id = $_GET['cam'];
$delFile = "DELETE FROM Files WHERE file_id = $cam";
header("Location: main.php?cam=".$user_id);
?>

In the same way, a folder can be deleted through the following code:

if (isset($_GET['cam']))
$user_id = $_GET['user_id'];
$user_id = $_GET['cam'];
$pathFolder= $path."/";
if (isset($_GET['folderName'])){
$pathFolder = $pathFolder.$user_id."/".$_GET['folderName'];
 
if (!file_exists($pathFolder))
mkdir($pathFolder,0777,TRUE);
$newFolder = sqlQuery("INSERT INTO Folder SET folder_Name = 'folderName', Location = $pathFolder, user_id = $user_id, createdDate = ");
}
header("Location: main.php?cam=".$user_id);

On the database end the deletion is made through a simple SQL statement.

Giving life to a static website

What gives life to the a website nowadays is the functionality of server side scripting languages such as PHP, ASP and JSP languages. Although HTML, CSS and Javascript have developed so much that that they can visually function as a great asset to create attractive websites, when it comes to passing sessions from one page to another, communucating with the database and performing certain functions, it is best to use server side scripting languages to perform such actions.

While ASP and JSP are proprietary programming languages, on the other hand, PHP is an open source language. Therefore most of its develepment is made by developers all around the globe. This makes PHP more oriented to make improvements on old libraries, develop new features and fix any bugs found. PHP development had its initialisation in 1994 when Rasmus Lerdorf wrote a series of Common Gateway Interface commands, which he used to maintain his personal homepage. He extended them to add the ability to work with web forms in the HTML of those days and enabled them to communicate with databases. At the time, programming had been purely procedural, since Object Oriented programming was still in its very early stages.

PHP developed together in line with other programming languages to communicate with relational databases, and even 4th generation data repositories. It was also developed itself into an object oriented programming langauge, as will be shown in the code snippets below.

User Access

In order to register a new user in the FireFile and verify the username and password, PHP fetches the username and password for the registered user and confronts them with the ones entered by the user in the form. This is done through the following code:

if (isset($_POST['email'])&& isset($_POST['password'])) {
$email = htmlspecialchars($email);
 $password = htmlspecialchars($password);
$email = $_POST['email']; //$email here is the name of the textbox in the form
 $password = $_POST['password']; //$password here is the name of the textbox in the form
if (!empty($email) && !empty($password)) {
$chkUser = "SELECT 'user_Id' FROM 'Users' WHERE 'username' = '$email' AND 'password' = '$password'";
if ($chkUserValidate = mysql_query($chkUser)) {
 $row_count = mysql_num_rows($chkUserValidate);
if ($row_count == 0) {
 echo 'Invalid username or password';
 } else {
 $user_Id = mysql_result($chkUserValidate, 0, 'user_id');
 $_SESSION['user_Id'] = user_id;
 session_start();
 header("Location: main.php");
 }
 } else {
 echo 'You must supply a username and password';
 }
 }
}

The code above checks if the user is already registered in the database and logs the user in if the account exists and takes him/her to the main page. Otherwise prompts the user with the relevant error messages.

The register page has the following php code to create a new user:

if ($num_rows > 0) {
$errorMessage = "Username already taken";
}
else {
$SQL = "INSERT INTO 'Users'('name', 'Surname', 'userEmail', 'password', 'mobile', 'createdDate')
VALUES($name, $surname,$userEmail, md5($password), $mobile,$createdDate)";
$result = mysql_query($SQL);
session_start();
$_SESSION['user_id'] = user_id;
header ("Location: main.php");
}
?>

Session Management

In the code above, apart from checking for the correct username and password, we also created a session with the user_Id which we got from the database. This is used throughout all the period the user is logged into the database, to make sure that only data related to the user who is logged in is shown. This is called session management.

Session variables store user information to be used across multiple pages (e.g. username, favorite color, etc). By default, session variables last until the user closes the browser. In this case, the user_Id is passed on through the different pages of the site to show the files, folders and settings for that particular user:

if ($num_rows > 0) {
 $errorMessage = "Username already taken";
}
else {
$SQL = "INSERT INTO 'Users'('name', 'Surname', 'userEmail', 'password', 'mobile', 'createdDate')
VALUES($name, $surname,$userEmail, md5($password), $mobile,$createdDate)";
$result = mysql_query($SQL);
session_start();
$_SESSION['user_id'] = user_id;
header ("Location: main.php");
}
?>

In the main page then, the user_Id is retrieved from the logged session using the following code:

<?php
 session_start();
$user_id = $_GET['user_id'];
$_SESSION['user_id'] = $user_id;
if (!(isset($_SESSION['user_id']) && $_SESSION['user_id'] != '')) {
header ("Location: login.php");
}

Then, in the same file, the Session Data is used to show the list of files which can be accessed by the user:

$uQuery = sqlQuery("SELECT userEmail FROM Users WHERE id = $user_id");
$folderLoc = sqlQuery("SELECT location FROM Files WHERE user_id = $user_id");
$folderArray = array();
$fileArray = array();
$folderName = sqlQuery("SELECT folderName FROM FILES WHERE user_id = $user_id");
$fileName = sqlQuery("SELECT fileName FROm Files WHERE user_id = $user_id");
$fileLocation = "SELECT location from Files WHERE ID = $userId";
if (isset($_GET['cam'])) {
$ncam = "". $_GET['cam'];
?>
<p>
<a class="fadeLink" onClick="msgProcessed(true);" href="index.php?cam=<?php echo setUpperPath($_GET['cam'])?>"><img src="images/open.gif" alt="Folder" width="16" height="16" border="0" align="absmiddle"><?php echo ($_GET['cam'])?>/..</a>
</p>
<?php
}

Object Oriented PHP classes

Just like in other programming languages, it would very untidy to write PHP code in the same file as the HTML code. Therefore, in this project, where php was needed to operate lengthy functions such as file upload or the file management system, the code was written in a seperate PHP file and call in the php file containing the HTML scripts.

An example of this is in the file upload code:

if (isset($_POST['folder'])){
$path = $path . "/" . $_POST['folder'];
$cam = "?cam=".$_POST['folder'];
}?>

<?php
$uploadFolder = $path ."/";
entryLog($path . " -- ".gethostbyaddr(getenv("REMOTE_ADDR"))." ======== ".$_FILES['userFile']['name'] ." =========> UPLOAD");
if (!move_uploaded_file($_FILES['userFile']['tmp_name'], $uploadFolder . $_FILES['userFile']['name'])) {
print("<center><b>FireFile could not make this operation</b></center>");
}else{
header("Location: main.php".$cam);
}
?>

Then, in the actual php file containing the HTML, the following code would be used to call the function:

while ($row = mysql_fetch_assoc($folderName)) {
 $folderArray = array_merge($folderArray, array_map('trim', explode(",", $row['folderName'])));
 }
while ($row = mysql_fetch_assoc($fileName)) {
 $fileArray = array_merge($fileArray, array_map('trim', explode(",", $row['fileName'])));
 }
if($error==UPLOAD_ERR_OK){
 $file = fopen($tempFileName,"r"); $content = fread($file,filesize($tempFileName)); $encryptedContent = base64_encode($content);
 $encryptedFileSaveName=$uploadDirectory.md5($insert_id).".data";
 $encryptedFile = fopen($encryptedFileSaveName,'w'); fwrite($encryptedFile,$encryptedContent); fclose($encryptedFile); print("File has been upload and encrypted successfully.");
 }else{ print("Error while uploading......"); }
?>

A Secure Repository

One of the main requirements of the project was to include an SSL certificate in the website to be able to secure the files which need to be uploaded. This would enable secure https access to the website and increase the security of the system. A dummy SSL certificate was created by using OpenSSL.

Normally, web traffic is sent in an unencrypted way accross the Internet. That is, anyone with access to the right tools can snoop all of that traffic. This is not ideal in a environment such as the one in FireFile where security and privacy are necessary to give an established repository for files and folders. The Secure Socket Layer is used to encrypt the data being transferred between the web server where the actual files are and the front end.

SSL makes use of public key cryptography (PKI) to create private and public keys, so that anything that is encrypted with these keys can only be decrypted with its corresponding key. So if a message or data stream were encrypted with the server’s private key, it can be decrypted with its corresponding public key. This ensures that the data only could have come from the server.

If SSL utilizes public key cryptography to encrypt the data stream traveling over an Internet connection. The certificate which is by default signed by a trusted Certificate Authority (CA), has the capability to make sure that the certificate holder is in reality the correct entity accessing the data. Without a trusted signed certificate, data may be encrypted, however, the party you are communicating with may not be verified. Without certificates, hacker attacks would be much more common on the Firefile system.

A possible enhancement for the future would be to encyrpt the files and folders uploaded online. This could be done through 2 ways.

Hashing the files and folders uploaded with a password

The uploaded files can be encypted using the same password for the user account using a password which is similar to the example below:

session_start();
$pass[] = "123456";
#create an array of passwords
#add as many as you want

#used for the logout link, changes the session 'loggedin' = false if ?action=logout
if($_GET['action'] == "logout"){
  $_SESSION['loggedin'] = false;
  $_SESSION['username'] = "";
}

if($_SESSION['loggedin'] == false){ #checks if the user isnt logged in
   if(!$_POST['s1']){     #checks if the form has been submitted, if not show it
   }else{
     #username check
     $tmppass = $_POST['pass']; #the password the user has submitted
     $t = count($pass); #count the total passwords
     $i = 0;
#create a loop to go through each password and compare it with the name pass which was submitted
     while($i <= $t){
       if($tmppass !== ""){
        if($tmppass == $pass[$i]){

          $_SESSION['loggedin'] = true;
          header('Location: ?'); #reload the page
          }
        }
      $i++;
     }
    echo "Invalid password 0.o"; #show an error message if the user enters an incorrect pass
   }
die;

The idea behind the method above, is to encrypt the uploaded file or folder with the same password used by the user to log into the system.

Encrypting the files using a javascript class, before it is uploaded using JQuery

The FileReader object used in HTML 5 lets us read the contents of local files using JavaScript, but only if these files have been selected explicitly by the user through the file input’s browse dialog. Below is just a sample code of how could this could be implemented:

if(body.hasClass('encrypt')){
reader.onload = function(e){
var encrypted = CryptoJS.AES.encrypt(e.target.result, password);
a.attr('href', 'data:application/octet-stream,' + encrypted);
a.attr('download', file.name + '.encrypted');
};
reader.readAsDataURL(file);
}