CS 491 Lecture 9 – On Today and JSON

Agenda

  • what ?s
  • tracking memories on this day in history
  • database
  • getting and putting memories through $_REQUEST
  • exchanging data with JSON
  • an Android client

Code

schema.sql

DROP TABLE IF EXISTS memory;

CREATE TABLE memory (
  id INT AUTO_INCREMENT PRIMARY KEY,
  year INT,
  month INT,
  day INT,
  log TEXT
);

put.php

<?php

require_once('globals.php');

$feedback = array();
$_REQUEST = json_decode(file_get_contents("php://input"), true);

$db = new mysqli('localhost', 'twodeeor_vanuser', PASSWORD, 'twodeeor_ontoday');
if ($db->connect_errno) {
  $feedback['ok'] = false;
  $feedback['error'] = $db->connect_errno;
} else {

  // Editing existing...
  if ($_REQUEST['id'] < 0) {
    $statement = $db->prepare("INSERT INTO memory (year, month, day, log) VALUES (?, ?, ?, ?)");
    if ($statement) {
      $statement->bind_param('iiis', $_REQUEST['year'], $_REQUEST['month'], $_REQUEST['day'], $_REQUEST['log']);
    }
  }

  // Adding new...
  else {
    $statement = $db->prepare("UPDATE memory SET year = ?, month = ?, day = ?, log = ? WHERE id = ?");
    if ($statement) {
      $statement->bind_param('iiisi', $_REQUEST['year'], $_REQUEST['month'], $_REQUEST['day'], $_REQUEST['log'], $_REQUEST['id']);
    }
  }

  if (!$statement) {
    $feedback['ok'] = false;
    $feedback['error'] = $db->error;
  } else {
    $ok = $statement->execute();
    $statement->free_result();
    $feedback['ok'] = true;
  }

  $db->close();
}

echo json_encode($feedback);

?>

get.php

<?php

require_once('globals.php');

$feedback = array();

$_REQUEST = json_decode(file_get_contents("php://input"), true);
$db = new mysqli('localhost', 'twodeeor_vanuser', PASSWORD, 'twodeeor_ontoday');

if ($db->connect_errno) {
  $feedback['ok'] = false;
  $feedback['error'] = $db->connect_errno;
} else {

  $statement = $db->prepare("SELECT * FROM memory WHERE month = ? AND day = ? ORDER BY year DESC");
  if (!$statement) {
    $feedback['ok'] = false;
    $feedback['error'] = $db->error;
  } else {
    $statement->bind_param('ii', $_REQUEST['month'], $_REQUEST['day']);
    $ok = $statement->execute();
    $statement->bind_result($id, $year, $month, $day, $log);
    $feedback['memories'] = array();
    while ($statement->fetch()) {
      $memory = array();
      $memory['id'] = $id;
      $memory['year'] = $year;
      $memory['month'] = $month;
      $memory['day'] = $day;
      $memory['log'] = $log;
      array_push($feedback['memories'], $memory);
    }
    $statement->free_result();
    $feedback['ok'] = true;
  }

  $db->close();
}

echo json_encode($feedback);

?>

edit.xml

<?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">
  <EditText
    android:id="@+id/logEditor"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:ems="10"
    android:hint="Enter memory..."
    android:lines="5"
    android:inputType="textMultiLine">
    <requestFocus />
  </EditText>

</LinearLayout>

activity_main.xml

<RelativeLayout
  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"
  tools:context=".MainActivity">
  <DatePicker
    android:id="@+id/datePicker"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="18dp" />
  <Button
    android:id="@+id/addTodayButton"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentLeft="true"
    android:layout_below="@+id/datePicker"
    android:layout_marginTop="20dp"
    android:text="Add today's memory" />
  <ListView
    android:id="@+id/logView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_alignParentLeft="true"
    android:layout_below="@+id/addTodayButton"></ListView>
</RelativeLayout>

Memory.java

package org.twodee.ontoday;

import org.json.JSONException;
import org.json.JSONObject;

public class Memory {
  private int id;
  private int year;
  private int month;
  private int day;
  private String log;

  public Memory(int id,
                int year,
                int month,
                int day,
                String log) {
    super();
    this.id = id;
    this.year = year;
    this.month = month;
    this.day = day;
    this.log = log;
  }

  public int getId() {
    return id;
  }

  public int getYear() {
    return year;
  }

  public int getMonth() {
    return month;
  }

  public int getDay() {
    return day;
  }

  public String getLog() {
    return log;
  }

  public void setLog(String log) {
    this.log = log;
  }

  public String toString() {
    return year + " - " + log;
  }

  public JSONObject toJSON() {
    JSONObject object = new JSONObject();
    try {
      object.put("id", id);
      object.put("year", year);
      object.put("month", month);
      object.put("day", day);
      object.put("log", log);
    } catch (JSONException e) {
      e.printStackTrace();
    }
    return object;
  }

  public String getDateString() {
    return String.format("%d/%d/%d", year, month, day);
  }
}

MainActivity.java

package org.twodee.ontoday;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Scanner;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicHeader;
import org.apache.http.protocol.HTTP;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.DatePicker.OnDateChangedListener;
import android.widget.EditText;
import android.widget.ListView;

public class MainActivity extends Activity {
  private ListView logView;
  private ArrayAdapter<Memory> logAdapter;
  private DatePicker datePicker;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    datePicker = (DatePicker) findViewById(R.id.datePicker);
    logView = (ListView) findViewById(R.id.logView);

    final Button addButton = (Button) findViewById(R.id.addTodayButton);
    addButton.setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View view) {
        edit(new Memory(-1, datePicker.getYear(), datePicker.getMonth() + 1, datePicker.getDayOfMonth(), ""));
      }
    });

    logAdapter = new ArrayAdapter<Memory>(this, android.R.layout.simple_list_item_1);
    logView.setAdapter(logAdapter);

    final Calendar today = Calendar.getInstance();

    datePicker.init(today.get(Calendar.YEAR), today.get(Calendar.MONTH), today.get(Calendar.DAY_OF_MONTH), new OnDateChangedListener() {
      @Override
      public void onDateChanged(DatePicker picker,
                                int year,
                                int month,
                                int day) {
        load(month + 1, day);
      }
    });

    load(10, 1);
  }

  protected void load(final int month,
                      final int day) {
    new AsyncTask<Void, Void, ArrayList<Memory>>() {
      @Override
      protected ArrayList<Memory> doInBackground(Void... args) {
        ArrayList<Memory> memories = new ArrayList<Memory>();

        try {
          JSONObject json = new JSONObject();
          json.put("month", month);
          json.put("day", day);
          json = makeRequest("http://www.twodee.org/ontoday/get.php", json);

          if (json.has("memories")) {
            JSONArray memoriesJSON = json.getJSONArray("memories");
            for (int i = 0; i < memoriesJSON.length(); ++i) {
              JSONObject memoryJSON = memoriesJSON.getJSONObject(i);
              memories.add(new Memory(memoryJSON.getInt("id"),
                                      memoryJSON.getInt("year"),
                                      memoryJSON.getInt("month"),
                                      memoryJSON.getInt("day"),
                                      memoryJSON.getString("log")));
            }
          }
        } catch (JSONException e) {
          Log.d("FOO", "Bad JSON Parsing");
        }

        return memories;
      }

      @Override
      protected void onPostExecute(ArrayList<Memory> memories) {
        logAdapter.clear();
        logAdapter.addAll(memories);
      }
    }.execute();
  }

  private void edit(final Memory memory) {
    AlertDialog.Builder dialog = new AlertDialog.Builder(this);

    dialog.setTitle("Memory for " + memory.getDateString());
    View body = LayoutInflater.from(this).inflate(R.layout.edit, null);
    final EditText logEditor = (EditText) body.findViewById(R.id.logEditor);
    dialog.setView(body);

    dialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
      @Override
      public void onClick(DialogInterface dialog,
                          int which) {
        memory.setLog(logEditor.getText().toString());
        upload(memory);
      }
    });

    dialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
      @Override
      public void onClick(DialogInterface dialog,
                          int which) {
        dialog.cancel();
      }
    });

    dialog.show();
  }

  private void upload(final Memory memory) {
    new AsyncTask<Void, Void, Void>() {
      @Override
      protected Void doInBackground(Void... arg0) {
        makeRequest("http://www.twodee.org/ontoday/put.php", memory.toJSON());
        return null;
      }

      @Override
      protected void onPostExecute(Void result) {
        load(datePicker.getMonth() + 1, datePicker.getDayOfMonth());
      }
    }.execute();
  }

  private JSONObject makeRequest(String url, JSONObject json) {
    try {
      URI uri = new URI(String.format(url));
      HttpPost post = new HttpPost(uri);
      StringEntity entity = new StringEntity(json.toString());

      Log.d("FOO", "sending " + json.toString());

      entity.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
      post.setEntity(entity);

      HttpClient client = new DefaultHttpClient();
      HttpResponse response = client.execute(post);

      Scanner in = new Scanner(response.getEntity().getContent());
      in.useDelimiter("\\Z");
      String body = in.next();
      in.close();

      Log.d("FOO", body);

      return new JSONObject(body);
    } catch (URISyntaxException e) {
      e.printStackTrace();
    } catch (UnsupportedEncodingException e) {
      e.printStackTrace();
    } catch (ClientProtocolException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    } catch (JSONException e) {
      e.printStackTrace();
    }

    return null;
  }
}

Haiku

Mom feared the teen years
“Grow, but let’s not stop talking.”
She named me JSON

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *