Simple client server on Android (Internet messenger)

Important. Everything written below does not represent any value for professionals, but it can serve as a useful example for beginner Android developers! In the code I tried to comment and log all actions.

Go. Many mobile applications (and not only) use the client-server architecture. The general scheme, I think, is understandable.



We pay attention to each element and note:

  • server - is a certain program running on a remote computer and that implements the “communication” functionality with client applications (listens to requests, recognizes the transferred parameters and values, correctly answers them);
  • client - in our case, a program on a mobile device that can generate a request that is understandable to the server and read the received response;
  • interaction interface - a certain format and method of transmitting / receiving requests / responses by both parties.

It doesn't matter how any of these elements are implemented, they are all present anyway. Let's implement a primitive server and an Android client working with it. As an example, we will use any popular mobile Internet messenger (Viber, ICQ), and the application will be called “Internet chat”.

The interaction scheme is as follows:



A client installed on device A sends a message to a client installed on device B. And vice versa. The server plays the role of a link between device A and B ... C, D ... etc. It also plays the role of a “drive" of messages, for their recovery, in case of deletion on one of the client devices.

To store messages we use SQL databases both on the server and on client devices (in principle, all the work of Internet messenger clients comes down to constant synchronization of the local and remote databases with messages). Additionally, our online chat will be able to start with the launch of the device and work in the background. Interaction will occur through HTTP requests and JSON responses.
It is more logical if synchronization occurs through a port / socket, this simplifies the task on the one hand (you do not need to send HTTP requests to check for new messages cyclically, just check the status of the listening socket), but on the other hand, it complicates the creation of the server side of the application.

Make server


To implement a “server”, we need to register on any hosting that allows us to work with SQL and PHP.

Create an empty SQL database, create a table in it.

Chat table.
CREATE TABLE `chat` (
  `_id` int(11) NOT NULL AUTO_INCREMENT,
  `author` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
  `client` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
  `data` bigint(20) NOT NULL,
  `text` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`_id`)
)


The structure is as follows:

  1. author - the author of the message;
  2. client - message recipient;
  3. data - time and date of receipt of the message on the server;
  4. text is a message.

In the following two files, it is necessary to change the variables containing the data for accessing the database to your own ones that you received when registering your “server”.

$mysql_host = "localhost"; // sql сервер, может быть локальным или внешним. например mysql5.000webhost.com
$mysql_user = "l29340eb_chat"; // пользователь
$mysql_password = "123456789"; // пароль
$mysql_database = "l29340eb_chat"; // имя базы данных на сервере SQL

The chat.php file is our api that implements the structure of server-friendly requests.
<?php // сохранить в utf-8 !
// ---------------------------------------------------------- эти значения задавались при создании БД на сервере
$mysql_host = "localhost"; // sql сервер
$mysql_user = "l29340eb_chat"; // пользователь
$mysql_password = "123456789"; // пароль
$mysql_database = "l29340eb_chat"; // имя базы данных chat
// ---------------------------------------------------------- проверяем переданные в строке запроса параметры
// например ...chat.php?action=select
//-----------------------------------------------------------
// переменная action может быть:
// select - формируем содержимое таблицы chat в JSON и отправляем назад
// insert - встваляем новую строку в таблицу chat, так же нужны 4 параметра : автор/получатель/время создания/сообщение
// ВАЖНО время создания мы не передаем в параметрах, его берем текущее на сервере
// delete - удаляет ВСЕ записи из таблицы chat - пусть будет для быстрой очистки
// ------------------------------------------- получим переданный action
if (isset($_GET["action"])) { 
    $action = $_GET['action'];
}
// ------------------------------------------- если action=insert тогда получим еще author|client|text
if (isset($_GET["author"])) { 
    $author = $_GET['author'];
}
if (isset($_GET["client"])) { 
    $client = $_GET['client'];
}
if (isset($_GET["text"])) { 
    $text = $_GET['text'];
}
// ------------------------------------------- если action=select тогда получим еще data - от после какого времени передавать ответ
if (isset($_GET["data"])) { 
    $data = $_GET['data'];
}
mysql_connect($mysql_host, $mysql_user, $mysql_password); // коннект к серверу SQL
mysql_select_db($mysql_database); // коннект к БД на сервере
mysql_set_charset('utf8'); // кодировка
// ------------------------------------------------------------ обрабатываем запрос если он был
if($action == select){ // если действие SELECT
if($data == null){
// выберем из таблицы chat ВСЕ данные что есть и вернем их в JSON
$q=mysql_query("SELECT * FROM chat");
}else{
// выберем из таблицы chat ВСЕ данные ПОЗНЕЕ ОПРЕДЕЛЕННОГО ВРЕМЕНИ и вернем их в JSON
$q=mysql_query("SELECT * FROM chat WHERE data > $data");	
}
while($e=mysql_fetch_assoc($q))
        $output[]=$e;
print(json_encode($output));
}
if($action == insert && $author != null && $client != null && $text != null){ // если действие INSERT и есть все что нужно
// время = время сервера а не клиента !
$current_time = round(microtime(1) * 1000);
// пример передачи скрипту данных:
// chat.php?action=insert&author=author&client=client&text=text
// вставим строку с переданными параметрами
mysql_query("INSERT INTO `chat`(`author`,`client`,`data`,`text`) VALUES ('$author','$client','$current_time','$text')");
}
if($action == delete){ // если действие DELETE
// полностью обнулим таблицу записей
mysql_query("TRUNCATE TABLE `chat`");	
}
mysql_close();
?>


The structure of requests for api:

  • required action attribute - can be equal to select (the server will respond with a list of records from its database), insert (the server will add a new record to its database), delete (the server will clear its database)
  • if action = insert, we will need to pass additional parameters: author (who wrote the message), client (to whom the message is addressed), text (message)
  • action = select may contain an additional parameter data; in this case, the server response does not contain all messages from the database, but only those with a creation time later than that transmitted

Examples:

  • chat.php? action = delete - delete all entries on the server
  • chat.php? action = insert & author = Jon & client = Smith & text = Hello - will add a new entry on the server: author Jon, recipient Smith, content Hello
  • chat.php? action = select & data = 151351333 - will return all records received after the transmitted time in long format

The showBD.php file is an optional script for displaying the contents of the database in a browser.
<?php // сохранить utf-8 !
// -------------------------------------------------------------------------- логины пароли
$mysql_host = "localhost"; // sql сервер
$mysql_user = "l29340eb_chat"; // пользователь
$mysql_password = "123456789"; // пароль
$mysql_database = "l29340eb_chat"; // имя базы данных chat
// -------------------------------------------------------------------------- если база недоступна
if (!mysql_connect($mysql_host, $mysql_user, $mysql_password)){
echo "<h2>База недоступна!</h2>";
exit;
}else{
// -------------------------------------------------------------------------- если база доступна
echo "<h2>База доступна!</h2>";
mysql_select_db($mysql_database);
mysql_set_charset('utf8');
// -------------------------------------------------------------------------- выведем JSON
$q=mysql_query("SELECT * FROM chat");
echo "<h3>Json ответ:</h3>";
// Выводим json
while($e=mysql_fetch_assoc($q))
        $output[]=$e;
print(json_encode($output));
// -------------------------------------------------------------------------- выведем таблицу
$q=mysql_query("SELECT * FROM chat");
echo "<h3>Табличный вид:</h3>";
echo "<table border=\"1\" width=\"100%\" bgcolor=\"#999999\">";
echo "<tr><td>_id</td><td>author</td>";
echo "<td>client</td><td>data</td><td>text</td></tr>";
for ($c=0; $c<mysql_num_rows($q); $c++){
$f = mysql_fetch_array($q);
echo "<tr><td>$f[_id]</td><td>$f[author]</td><td>$f[client]</td><td>$f[data]</td><td>$f[text]</td></tr>";
}
 echo "</tr></table>";
}
mysql_close();
// -------------------------------------------------------------------------- разорвем соединение с БД
?>


Client part


Now the structure of the Android application:



In the background, FoneService.java is running, which, in a separate thread, makes a request to the server every 15 seconds. If the server response contains new messages, FoneService.java writes them to the local database and sends a ChatActivity.java message about the need to update the ListView, with messages. ChatActivity.java (if it is open at that moment) receives a message and updates the contents of the ListView from the local database.

A new message from ChatActivity.java is sent immediately to the server, bypassing FoneService.java. At the same time, our message is NOT written to the local database! There it will appear only after receiving it back as a server response. I used this implementation in connection with the important nuance of the work of any Internet chat - the mandatory grouping of messages by time. If you do not use time grouping, the message sequence will be disrupted. Considering that client applications simply physically cannot be synchronized with an accuracy of milliseconds, and may even work in different time zones, it will be most logical to use server time. So we do.

Creating a new message, we send a request to the server: the name of the author of the message, the name of the recipient of the message, the message text. Getting this record back, in the form of a server response, we get what we sent + the fourth parameter: the time the server received the message.

In MainActivity.java, for clarity, I added the ability to delete messages from the local database - this is equivalent to a clean installation of the application (in this case, FoneService will send a request to the server to receive all messages of the selected chat). It is also possible to send a request to delete all messages from the database located on the server.

Activity Code:

FoneService.java
package by.andreidanilevich.temp_chat;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import org.json.JSONArray;
import org.json.JSONObject;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.BitmapFactory;
import android.os.IBinder;
import android.util.Log;
public class FoneService extends Service {
	// ИМЯ СЕРВЕРА (url зарегистрированного нами сайта)
	// например http://l29340eb.bget.ru
	String server_name = "http://l29340eb.bget.ru";
	SQLiteDatabase chatDBlocal;
	HttpURLConnection conn;
	Cursor cursor;
	Thread thr;
	ContentValues new_mess;
	Long last_time; // время последней записи в БД, отсекаем по нему что нам
					// тянуть с сервера, а что уже есть
	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}
	public void onStart(Intent intent, int startId) {
		Log.i("chat", "+ FoneService - запуск сервиса");
		chatDBlocal = openOrCreateDatabase("chatDBlocal.db",
				Context.MODE_PRIVATE, null);
		chatDBlocal
				.execSQL("CREATE TABLE IF NOT EXISTS chat (_id integer primary key autoincrement, author, client, data, text)");
		// создадим и покажем notification
		// это позволит стать сервису "бессмертным"
		// и будет визуально видно в трее
		Intent iN = new Intent(getApplicationContext(), MainActivity.class);
		intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
				| Intent.FLAG_ACTIVITY_SINGLE_TOP);
		PendingIntent pI = PendingIntent.getActivity(getApplicationContext(),
				0, iN, PendingIntent.FLAG_CANCEL_CURRENT);
		Notification.Builder bI = new Notification.Builder(
				getApplicationContext());
		bI.setContentIntent(pI)
				.setSmallIcon(R.drawable.ic_launcher)
				.setLargeIcon(
						BitmapFactory.decodeResource(getApplicationContext()
								.getResources(), R.drawable.ic_launcher))
				.setAutoCancel(true)
				.setContentTitle(getResources().getString(R.string.app_name))
				.setContentText("работаю...");
		Notification notification = bI.build();
		startForeground(101, notification);
		startLoop();
	}
	// запуск потока, внутри которого будет происходить
	// регулярное соединение с сервером для чтения новых
	// сообщений.
	// если сообщения найдены - отправим броадкаст для обновления
	// ListView в ChatActivity
	private void startLoop() {
		thr = new Thread(new Runnable() {
			// ansver = ответ на запрос
			// lnk = линк с параметрами
			String ansver, lnk;
			public void run() {
				while (true) { // стартуем бесконечный цикл
					// глянем локальную БД на наличие сообщщений чата
					cursor = chatDBlocal.rawQuery(
							"SELECT * FROM chat ORDER BY data", null);
					// если какие-либо сообщения есть - формируем запрос
					// по которому получим только новые сообщения
					if (cursor.moveToLast()) {
						last_time = cursor.getLong(cursor
								.getColumnIndex("data"));
						lnk = server_name + "/chat.php?action=select&data="
								+ last_time.toString();
						// если сообщений в БД нет - формируем запрос
						// по которому получим всё
					} else {
						lnk = server_name + "/chat.php?action=select";
					}
					cursor.close();
					// создаем соединение ---------------------------------->
					try {
						Log.i("chat",
								"+ FoneService --------------- ОТКРОЕМ СОЕДИНЕНИЕ");
						conn = (HttpURLConnection) new URL(lnk)
								.openConnection();
						conn.setReadTimeout(10000);
						conn.setConnectTimeout(15000);
						conn.setRequestMethod("POST");
						conn.setRequestProperty("User-Agent", "Mozilla/5.0");
						conn.setDoInput(true);
						conn.connect();
					} catch (Exception e) {
						Log.i("chat", "+ FoneService ошибка: " + e.getMessage());
					}
					// получаем ответ ---------------------------------->
					try {
						InputStream is = conn.getInputStream();
						BufferedReader br = new BufferedReader(
								new InputStreamReader(is, "UTF-8"));
						StringBuilder sb = new StringBuilder();
						String bfr_st = null;
						while ((bfr_st = br.readLine()) != null) {
							sb.append(bfr_st);
						}
						Log.i("chat", "+ FoneService - полный ответ сервера:\n"
								+ sb.toString());
						// сформируем ответ сервера в string
						// обрежем в полученном ответе все, что находится за "]"
						// это необходимо, т.к. json ответ приходит с мусором
						// и если этот мусор не убрать - будет невалидным
						ansver = sb.toString();
						ansver = ansver.substring(0, ansver.indexOf("]") + 1);
						is.close(); // закроем поток
						br.close(); // закроем буфер
					} catch (Exception e) {
						Log.i("chat", "+ FoneService ошибка: " + e.getMessage());
					} finally {
						conn.disconnect();
						Log.i("chat",
								"+ FoneService --------------- ЗАКРОЕМ СОЕДИНЕНИЕ");
					}
					// запишем ответ в БД ---------------------------------->
					if (ansver != null && !ansver.trim().equals("")) {
						Log.i("chat",
								"+ FoneService ---------- ответ содержит JSON:");
						try {
							// ответ превратим в JSON массив
							JSONArray ja = new JSONArray(ansver);
							JSONObject jo;
							Integer i = 0;
							while (i < ja.length()) {
								// разберем JSON массив построчно
								jo = ja.getJSONObject(i);
								Log.i("chat",
										"=================>>> "
												+ jo.getString("author")
												+ " | "
												+ jo.getString("client")
												+ " | " + jo.getLong("data")
												+ " | " + jo.getString("text"));
								// создадим новое сообщение
								new_mess = new ContentValues();
								new_mess.put("author", jo.getString("author"));
								new_mess.put("client", jo.getString("client"));
								new_mess.put("data", jo.getLong("data"));
								new_mess.put("text", jo.getString("text"));
								// запишем новое сообщение в БД
								chatDBlocal.insert("chat", null, new_mess);
								new_mess.clear();
								i++;
								// отправим броадкаст для ChatActivity
								// если она открыта - она обновить ListView
								sendBroadcast(new Intent(
										"by.andreidanilevich.action.UPDATE_ListView"));
							}
						} catch (Exception e) {
							// если ответ сервера не содержит валидный JSON
							Log.i("chat",
									"+ FoneService ---------- ошибка ответа сервера:\n"
											+ e.getMessage());
						}
					} else {
						// если ответ сервера пустой
						Log.i("chat",
								"+ FoneService ---------- ответ не содержит JSON!");
					}
					try {
						Thread.sleep(15000);
					} catch (Exception e) {
						Log.i("chat",
								"+ FoneService - ошибка процесса: "
										+ e.getMessage());
					}
				}
			}
		});
		thr.setDaemon(true);
		thr.start();
	}
}


ChatActivity.java
package by.andreidanilevich.temp_chat;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.Toast;
public class ChatActivity extends Activity {
	// ИМЯ СЕРВЕРА (url зарегистрированного нами сайта)
	// например http://l29340eb.bget.ru
	String server_name = "http://l29340eb.bget.ru";
	ListView lv; // полоса сообщений
	EditText et;
	Button bt;
	SQLiteDatabase chatDBlocal;
	String author, client;
	INSERTtoChat insert_to_chat; // класс отправляет новое сообщение на сервер
	UpdateReceiver upd_res; // класс ждет сообщение от сервиса и получив его -
							// обновляет ListView
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.chat);
		// получим 2 переменные по которым будем отбирать инфу из БД:
		// author - от чьего имени идет чат
		// client - с кем чатимся
		Intent intent = getIntent();
		author = intent.getStringExtra("author");
		client = intent.getStringExtra("client");
		Log.i("chat", "+ ChatActivity - открыт author = " + author
				+ " | client = " + client);
		lv = (ListView) findViewById(R.id.lv);
		et = (EditText) findViewById(R.id.et);
		bt = (Button) findViewById(R.id.bt);
		chatDBlocal = openOrCreateDatabase("chatDBlocal.db",
				Context.MODE_PRIVATE, null);
		chatDBlocal
				.execSQL("CREATE TABLE IF NOT EXISTS chat (_id integer primary key autoincrement, author, client, data, text)");
		// Создаём и регистрируем широковещательный приёмник
		upd_res = new UpdateReceiver();
		registerReceiver(upd_res, new IntentFilter(
				"by.andreidanilevich.action.UPDATE_ListView"));
		create_lv();
	}
	// обновим lv = заполним нужные позиции в lv информацией из БД
	@SuppressLint("SimpleDateFormat")
	public void create_lv() {
		Cursor cursor = chatDBlocal.rawQuery(
				"SELECT * FROM chat WHERE author = '" + author
						+ "' OR author = '" + client + "' ORDER BY data", null);
		if (cursor.moveToFirst()) {
			// если в базе есть элементы соответствующие
			// нашим критериям отбора
			// создадим массив, создадим hashmap и заполним его результатом
			// cursor
			ArrayList<HashMap<String, Object>> mList = new ArrayList<HashMap<String, Object>>();
			HashMap<String, Object> hm;
			do {
				// мое сообщение !!!
				// если автор сообщения = автор
				// и получатель сообщения = клиент
				if (cursor.getString(cursor.getColumnIndex("author")).equals(
						author)
						&& cursor.getString(cursor.getColumnIndex("client"))
								.equals(client)) {
					hm = new HashMap<>();
					hm.put("author", author);
					hm.put("client", "");
					hm.put("list_client", "");
					hm.put("list_client_time", "");
					hm.put("list_author",
							cursor.getString(cursor.getColumnIndex("text")));
					hm.put("list_author_time", new SimpleDateFormat(
							"HH:mm - dd.MM.yyyy").format(new Date(cursor
							.getLong(cursor.getColumnIndex("data")))));
					mList.add(hm);
				}
				// сообщение мне !!!!!!!
				// если автор сообщения = клиент
				// и если получатель сообщения = автор
				if (cursor.getString(cursor.getColumnIndex("author")).equals(
						client)
						&& cursor.getString(cursor.getColumnIndex("client"))
								.equals(author)) {
					hm = new HashMap<>();
					hm.put("author", "");
					hm.put("client", client);
					hm.put("list_author", "");
					hm.put("list_author_time", "");
					hm.put("list_client",
							cursor.getString(cursor.getColumnIndex("text")));
					hm.put("list_client_time", new SimpleDateFormat(
							"HH:mm - dd.MM.yyyy").format(new Date(cursor
							.getLong(cursor.getColumnIndex("data")))));
					mList.add(hm);
				}
			} while (cursor.moveToNext());
			// покажем lv
			SimpleAdapter adapter = new SimpleAdapter(getApplicationContext(),
					mList, R.layout.list, new String[] { "list_author",
							"list_author_time", "list_client",
							"list_client_time", "author", "client" },
					new int[] { R.id.list_author, R.id.list_author_time,
							R.id.list_client, R.id.list_client_time,
							R.id.author, R.id.client });
			lv.setAdapter(adapter);
			cursor.close();
		}
		Log.i("chat",
				"+ ChatActivity ======================== обновили поле чата");
	}
	public void send(View v) {
		// запишем наше новое сообщение
		// вначале проверим на пустоту
		if (!et.getText().toString().trim().equals("")) {
			// кнопку сделаем неактивной
			bt.setEnabled(false);
			// если чтото есть - действуем!
			insert_to_chat = new INSERTtoChat();
			insert_to_chat.execute();
		} else {
			// если ничего нет - нечего и писать
			et.setText("");
		}
	}
	// отправим сообщение на сервер
	private class INSERTtoChat extends AsyncTask<Void, Void, Integer> {
		HttpURLConnection conn;
		Integer res;
		protected Integer doInBackground(Void... params) {
			try {
				// соберем линк для передачи новой строки
				String post_url = server_name
						+ "/chat.php?action=insert&author="
						+ URLEncoder.encode(author, "UTF-8")
						+ "&client="
						+ URLEncoder.encode(client, "UTF-8")
						+ "&text="
						+ URLEncoder.encode(et.getText().toString().trim(),
								"UTF-8");
				Log.i("chat",
						"+ ChatActivity - отправляем на сервер новое сообщение: "
								+ et.getText().toString().trim());
				URL url = new URL(post_url);
				conn = (HttpURLConnection) url.openConnection();
				conn.setConnectTimeout(10000); // ждем 10сек
				conn.setRequestMethod("POST");
				conn.setRequestProperty("User-Agent", "Mozilla/5.0");
				conn.connect();
				res = conn.getResponseCode();
				Log.i("chat", "+ ChatActivity - ответ сервера (200 - все ОК): "
						+ res.toString());
			} catch (Exception e) {
				Log.i("chat",
						"+ ChatActivity - ошибка соединения: " + e.getMessage());
			} finally {
				// закроем соединение
				conn.disconnect();
			}
			return res;
		}
		protected void onPostExecute(Integer result) {
			try {
				if (result == 200) {
					Log.i("chat", "+ ChatActivity - сообщение успешно ушло.");
					// сбросим набранный текст
					et.setText("");
				}
			} catch (Exception e) {
				Log.i("chat", "+ ChatActivity - ошибка передачи сообщения:\n"
						+ e.getMessage());
				Toast.makeText(getApplicationContext(),
						"ошибка передачи сообщения", Toast.LENGTH_SHORT).show();
			} finally {
				// активируем кнопку
				bt.setEnabled(true);
			}
		}
	}
	// ресивер приёмник ждет сообщения от FoneService
	// если сообщение пришло, значит есть новая запись в БД - обновим ListView
	public class UpdateReceiver extends BroadcastReceiver {
		@Override
		public void onReceive(Context context, Intent intent) {
			Log.i("chat",
					"+ ChatActivity - ресивер получил сообщение - обновим ListView");
			create_lv();
		}
	}
	// выходим из чата
	public void onBackPressed() {
		Log.i("chat", "+ ChatActivity - закрыт");
		unregisterReceiver(upd_res);
		finish();
	}
}


MainActivity.java
package by.andreidanilevich.temp_chat;
import java.net.HttpURLConnection;
import java.net.URL;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.Toast;
public class MainActivity extends Activity {
	// ИМЯ СЕРВЕРА (url зарегистрированного нами сайта)
	// например http://l29340eb.bget.ru
	String server_name = "http://l29340eb.bget.ru";
	Spinner spinner_author, spinner_client;
	String author, client;
	Button open_chat_btn, open_chat_reverce_btn, delete_server_chat;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		Log.i("chat", "+ MainActivity - запуск приложения");
		open_chat_btn = (Button) findViewById(R.id.open_chat_btn);
		open_chat_reverce_btn = (Button) findViewById(R.id.open_chat_reverce_btn);
		delete_server_chat = (Button) findViewById(R.id.delete_server_chat);
		// запустим FoneService
		this.startService(new Intent(this, FoneService.class));
		// заполним 2 выпадающих меню для выбора автора и получателя сообщения
		// 5 мужских и 5 женских имен
		// установим слушателей
		spinner_author = (Spinner) findViewById(R.id.spinner_author);
		spinner_client = (Spinner) findViewById(R.id.spinner_client);
		spinner_author.setAdapter(new ArrayAdapter<String>(this,
				android.R.layout.simple_spinner_item, new String[] { "Петя",
						"Вася", "Коля", "Андрей", "Сергей", "Оля", "Лена",
						"Света", "Марина", "Наташа" }));
		spinner_client.setAdapter(new ArrayAdapter<String>(this,
				android.R.layout.simple_spinner_item, new String[] { "Петя",
						"Вася", "Коля", "Андрей", "Сергей", "Оля", "Лена",
						"Света", "Марина", "Наташа" }));
		spinner_client.setSelection(5);
		open_chat_btn.setText("Открыть чат: "
				+ spinner_author.getSelectedItem().toString() + " > "
				+ spinner_client.getSelectedItem().toString());
		open_chat_reverce_btn.setText("Открыть чат: "
				+ spinner_client.getSelectedItem().toString() + " > "
				+ spinner_author.getSelectedItem().toString());
		spinner_author
				.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
					public void onItemSelected(AdapterView<?> parent,
							View itemSelected, int selectedItemPosition,
							long selectedId) {
						author = spinner_author.getSelectedItem().toString();
						open_chat_btn.setText("Открыть чат: "
								+ spinner_author.getSelectedItem().toString()
								+ " > "
								+ spinner_client.getSelectedItem().toString());
						open_chat_reverce_btn.setText("Открыть чат: "
								+ spinner_client.getSelectedItem().toString()
								+ " > "
								+ spinner_author.getSelectedItem().toString());
					}
					public void onNothingSelected(AdapterView<?> parent) {
					}
				});
		spinner_client
				.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
					public void onItemSelected(AdapterView<?> parent,
							View itemSelected, int selectedItemPosition,
							long selectedId) {
						client = spinner_client.getSelectedItem().toString();
						open_chat_btn.setText("Открыть чат: "
								+ spinner_author.getSelectedItem().toString()
								+ " > "
								+ spinner_client.getSelectedItem().toString());
						open_chat_reverce_btn.setText("Открыть чат: "
								+ spinner_client.getSelectedItem().toString()
								+ " > "
								+ spinner_author.getSelectedItem().toString());
					}
					public void onNothingSelected(AdapterView<?> parent) {
					}
				});
	}
	// откроем чат с выбранным автором и получателем
	public void open_chat(View v) {
		// быстрая проверка
		if (author.equals(client)) {
			// если автор и получатель одинаковы
			// чат не открываем
			Toast.makeText(this, "author = client !", Toast.LENGTH_SHORT)
					.show();
		} else {
			// откроем нужный чат author > client
			Intent intent = new Intent(MainActivity.this, ChatActivity.class);
			intent.putExtra("author", author);
			intent.putExtra("client", client);
			startActivity(intent);
		}
	}
	// откроем чат с выбранным автором и получателем, только наоборот
	public void open_chat_reverce(View v) {
		// быстрая проверка
		if (author.equals(client)) {
			// если автор и получатель одинаковы
			// чат не открываем
			Toast.makeText(this, "author = client !", Toast.LENGTH_SHORT)
					.show();
		} else {
			// откроем нужный чат client > author
			Intent intent = new Intent(MainActivity.this, ChatActivity.class);
			intent.putExtra("author", client);
			intent.putExtra("client", author);
			startActivity(intent);
		}
	}
	// отправим запрос на сервер о удалении таблицы с чатами
	public void delete_server_chats(View v) {
		Log.i("chat", "+ MainActivity - запрос на удаление чата с сервера");
		delete_server_chat.setEnabled(false);
		delete_server_chat.setText("Запрос отправлен. Ожидайте...");
		DELETEfromChat delete_from_chat = new DELETEfromChat();
		delete_from_chat.execute();
	}
	// удалим локальную таблицу чатов
	// и создадим такуюже новую
	public void delete_local_chats(View v) {
		Log.i("chat", "+ MainActivity - удаление чата с этого устройства");
		SQLiteDatabase chatDBlocal;
		chatDBlocal = openOrCreateDatabase("chatDBlocal.db",
				Context.MODE_PRIVATE, null);
		chatDBlocal.execSQL("drop table chat");
		chatDBlocal
				.execSQL("CREATE TABLE IF NOT EXISTS chat (_id integer primary key autoincrement, author, client, data, text)");
		Toast.makeText(getApplicationContext(),
				"Чат на этом устройстве удален!", Toast.LENGTH_SHORT).show();
	}
	// отправим запрос на сервер о удалении таблицы с чатами
	// если он пройдет - таблица будет удалена
	// если не пройдет (например нет интернета или сервер недоступен)
	// - покажет сообщение
	private class DELETEfromChat extends AsyncTask<Void, Void, Integer> {
		Integer res;
		HttpURLConnection conn;
		protected Integer doInBackground(Void... params) {
			try {
				URL url = new URL(server_name + "/chat.php?action=delete");
				conn = (HttpURLConnection) url.openConnection();
				conn.setConnectTimeout(10000); // ждем 10сек
				conn.setRequestMethod("POST");
				conn.setRequestProperty("User-Agent", "Mozilla/5.0");
				conn.connect();
				res = conn.getResponseCode();
				Log.i("chat", "+ MainActivity - ответ сервера (200 = ОК): "
						+ res.toString());
			} catch (Exception e) {
				Log.i("chat",
						"+ MainActivity - ответ сервера ОШИБКА: "
								+ e.getMessage());
			} finally {
				conn.disconnect();
			}
			return res;
		}
		protected void onPostExecute(Integer result) {
			try {
				if (result == 200) {
					Toast.makeText(getApplicationContext(),
							"Чат на сервере удален!", Toast.LENGTH_SHORT)
							.show();
				}
			} catch (Exception e) {
				Toast.makeText(getApplicationContext(),
						"Ошибка выполнения запроса.", Toast.LENGTH_SHORT)
						.show();
			} finally {
				// сделаем кнопку активной
				delete_server_chat.setEnabled(true);
				delete_server_chat.setText("Удалить все чаты на сервере!");
			}
		}
	}
	public void onBackPressed() {
		Log.i("chat", "+ MainActivity - выход из приложения");
		finish();
	}
}


AutoRun.java
package by.andreidanilevich.temp_chat;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class AutoRun extends BroadcastReceiver {
	@Override
	public void onReceive(Context context, Intent intent) {
		if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
			// получили boot_completed - запустили FoneService
			context.startService(new Intent(context, FoneService.class));
			Log.i("chat", "+ AutoRun - отработал");
		}
	}
}
<b>Манифест:</b>


AndroidManifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="by.andreidanilevich.temp_chat"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-sdk
        android:minSdkVersion="16"
        android:targetSdkVersion="22" />
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:screenOrientation="portrait" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".ChatActivity"
            android:label="@string/app_name"
            android:screenOrientation="portrait" >
        </activity>
        <receiver
            android:name=".AutoRun"
            android:enabled="true"
            android:exported="false" >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
        <service android:name=".FoneService" />
    </application>
</manifest>


Markup:

Chat.xml markup
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#999999" >
    <ListView
        android:id="@+id/lv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginBottom="60dp"
        android:scrollbars="none"
        android:stackFromBottom="true"
        android:transcriptMode="alwaysScroll" >
    </ListView>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_alignParentBottom="true"
        android:background="#ffffff" >
        <EditText
            android:id="@+id/et"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="top"
            android:layout_weight="1"
            android:ems="10" >
        </EditText>
        <Button
            android:id="@+id/bt"
            android:layout_width="50dp"
            android:layout_height="wrap_content"
            android:layout_gravity="top"
            android:onClick="send"
            android:text=">" />
    </LinearLayout>
</RelativeLayout>


Markup main.xml
<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:orientation="vertical"
    tools:context="${relativePackage}.${activityClass}" >
    <Spinner
        android:id="@+id/spinner_author"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <Spinner
        android:id="@+id/spinner_client"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <Button
        android:id="@+id/open_chat_btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="open_chat" />
    <Button
        android:id="@+id/open_chat_reverce_btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="open_chat_reverce" />
    <Button
        android:id="@+id/delete_server_chat"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:onClick="delete_server_chats"
        android:text="Удалить все чаты на сервере!" />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="delete_local_chats"
        android:text="Удалить все чаты на этом устройстве!" />
</LinearLayout>


Markup list.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#999999"
    android:orientation="vertical" >
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp" >
        <TextView
            android:id="@+id/author"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="left"
            android:textColor="#c6c6c6"
            android:textSize="12sp" />
        <TextView
            android:id="@+id/client"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="left"
            android:textColor="#c6c6c6"
            android:textSize="12sp" />
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="5dp"
        android:layout_marginTop="5dp" >
        <TextView
            android:id="@+id/list_author"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="left"
            android:textColor="#0000ff"
            android:textSize="14sp"
            android:textStyle="bold" />
        <TextView
            android:id="@+id/list_client"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="left"
            android:textColor="#ffff00"
            android:textSize="14sp"
            android:textStyle="bold" />
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="5dp" >
        <TextView
            android:id="@+id/list_author_time"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="right"
            android:textColor="#c6c6c6"
            android:textSize="12sp" />
        <TextView
            android:id="@+id/list_client_time"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="right"
            android:textColor="#c6c6c6"
            android:textSize="12sp" />
    </LinearLayout>
</LinearLayout>


And all together (+ apk):

github.com/andreidanilevich/temp_chat

P.S .:

1. If you downloaded apk from the link above, first check the availability of my “server” at l29340eb.bget.ru/showBD.php .
2. The code was written for a long time, of course, not everything is beautiful and according to the canons, maybe not all exceptions are processed and there are errors. This is a draft. Everything worked for me on real devices and on an emulator.
3. If someone comes in handy - I will be glad. For criticism - thanks, for constructive messages - thanks twice as much. I will try to answer the questions.

Also popular now: