From 399c0b1226dd8913873bc5d208fee5e990cd5fcd Mon Sep 17 00:00:00 2001 From: Tiberiu Iustin Zulean Date: Wed, 13 Nov 2019 23:32:31 +0000 Subject: [PATCH] Implemented local database (chat list table with associated CRUD operations) and additon and deletion of chats from chat list --- Android/app/build.gradle | 13 +- .../src/main/java/database/AppDatabase.java | 9 ++ .../main/java/database/AppDatabaseClient.java | 30 ++++ .../main/java/database/StoredChatList.java | 62 +++++++ .../main/java/database/StoredChatListDao.java | 31 ++++ .../java/teamzero/chat/mobile/ChatList.java | 153 +++++++++++------- .../java/teamzero/chat/mobile/NewChat.java | 53 +++++- .../res/drawable/ic_user_default_avatar.xml | 9 ++ Android/app/src/main/res/menu/menu_layout.xml | 10 +- Android/app/src/main/res/values/strings.xml | 2 + Android/build.gradle | 4 + 11 files changed, 310 insertions(+), 66 deletions(-) create mode 100644 Android/app/src/main/java/database/AppDatabase.java create mode 100644 Android/app/src/main/java/database/AppDatabaseClient.java create mode 100644 Android/app/src/main/java/database/StoredChatList.java create mode 100644 Android/app/src/main/java/database/StoredChatListDao.java create mode 100644 Android/app/src/main/res/drawable/ic_user_default_avatar.xml diff --git a/Android/app/build.gradle b/Android/app/build.gradle index 59fa6f3..9da29a6 100644 --- a/Android/app/build.gradle +++ b/Android/app/build.gradle @@ -23,9 +23,20 @@ dependencies { implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'com.android.support:design:28.0.0' - implementation 'com.android.volley:volley:1.1.0' implementation 'org.java-websocket:Java-WebSocket:1.3.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' + + implementation "android.arch.persistence.room:runtime:$roomVersion" + annotationProcessor "android.arch.persistence.room:compiler:$roomVersion" + + // RxJava support for Room + implementation "android.arch.persistence.room:rxjava2:$roomVersion" + + // Guava support for Room, including Optional and ListenableFuture + implementation "android.arch.persistence.room:guava:$roomVersion" + + // Test helpers + testImplementation "android.arch.persistence.room:testing:$roomVersion" } diff --git a/Android/app/src/main/java/database/AppDatabase.java b/Android/app/src/main/java/database/AppDatabase.java new file mode 100644 index 0000000..dffb97f --- /dev/null +++ b/Android/app/src/main/java/database/AppDatabase.java @@ -0,0 +1,9 @@ +package database; + +import android.arch.persistence.room.Database; +import android.arch.persistence.room.RoomDatabase; + +@Database(entities = {StoredChatList.class}, version = 1) +public abstract class AppDatabase extends RoomDatabase { + public abstract StoredChatListDao storedChatListDao(); +} \ No newline at end of file diff --git a/Android/app/src/main/java/database/AppDatabaseClient.java b/Android/app/src/main/java/database/AppDatabaseClient.java new file mode 100644 index 0000000..ab4b249 --- /dev/null +++ b/Android/app/src/main/java/database/AppDatabaseClient.java @@ -0,0 +1,30 @@ +package database; + +import android.arch.persistence.room.Room; +import android.content.Context; + +public class AppDatabaseClient { + + private Context mCtx; + private static AppDatabaseClient mInstance; + + private AppDatabase appDatabase; + + private AppDatabaseClient(Context mCtx) { + + this.mCtx = mCtx; + + appDatabase = Room.databaseBuilder(mCtx, AppDatabase.class, "MyToDos").build(); + } + + public static synchronized AppDatabaseClient getInstance(Context mCtx) { + if (mInstance == null) { + mInstance = new AppDatabaseClient(mCtx); + } + return mInstance; + } + + public AppDatabase getAppDatabase() { + return appDatabase; + } +} \ No newline at end of file diff --git a/Android/app/src/main/java/database/StoredChatList.java b/Android/app/src/main/java/database/StoredChatList.java new file mode 100644 index 0000000..06b78d8 --- /dev/null +++ b/Android/app/src/main/java/database/StoredChatList.java @@ -0,0 +1,62 @@ +package database; + +import android.arch.persistence.room.ColumnInfo; +import android.arch.persistence.room.Entity; +import android.arch.persistence.room.PrimaryKey; + +import java.io.Serializable; + +@Entity +public class StoredChatList implements Serializable { + + @PrimaryKey(autoGenerate = true) + private int id; + + @ColumnInfo(name = "username") + private String username; + + @ColumnInfo(name = "last_message_content") + private String lastMessageContent; + + @ColumnInfo(name = "last_message_date") + private String lastMessageDate; + + /* * + * + * Getters and Setters + * + * */ + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getLastMessageContent() { + return lastMessageContent; + } + + public void setLastMessageContent(String lastMessageContent) { + this.lastMessageContent = lastMessageContent; + } + + public String getLastMessageDate() { + return lastMessageDate; + } + + public void setLastMessageDate(String messageDate) { + this.lastMessageDate = lastMessageDate; + } + +} diff --git a/Android/app/src/main/java/database/StoredChatListDao.java b/Android/app/src/main/java/database/StoredChatListDao.java new file mode 100644 index 0000000..12805b4 --- /dev/null +++ b/Android/app/src/main/java/database/StoredChatListDao.java @@ -0,0 +1,31 @@ +package database; + +import android.arch.persistence.room.Dao; +import android.arch.persistence.room.Delete; +import android.arch.persistence.room.Insert; +import android.arch.persistence.room.Query; +import android.arch.persistence.room.Update; + +import java.util.List; + +// Interface for the CRUD operations on StoredChatList table + +@Dao +public interface StoredChatListDao { + + @Query("SELECT * FROM storedchatlist") + List getAll(); + + @Query("DELETE FROM storedchatlist") + void deleteAll(); + + @Insert + void insert(StoredChatList storedChatList); + + @Delete + void delete(StoredChatList storedChatList); + + @Update + void update(StoredChatList storedChatList); + +} \ No newline at end of file diff --git a/Android/app/src/main/java/teamzero/chat/mobile/ChatList.java b/Android/app/src/main/java/teamzero/chat/mobile/ChatList.java index e1e1b9b..626f866 100644 --- a/Android/app/src/main/java/teamzero/chat/mobile/ChatList.java +++ b/Android/app/src/main/java/teamzero/chat/mobile/ChatList.java @@ -5,11 +5,13 @@ import android.content.DialogInterface; import android.content.Intent; + import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; +import android.os.AsyncTask; import android.view.Menu; import android.view.MenuInflater; @@ -24,19 +26,11 @@ import android.widget.TextView; import android.widget.Toast; -import com.android.volley.Request; -import com.android.volley.RequestQueue; -import com.android.volley.Response; -import com.android.volley.VolleyError; -import com.android.volley.toolbox.StringRequest; -import com.android.volley.toolbox.Volley; - -import org.json.JSONException; -import org.json.JSONObject; - import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; +import java.util.List; + +import database.AppDatabaseClient; +import database.StoredChatList; /* TODO: 1) Make mock login with welcome message to test out UserDetails population [X] @@ -51,8 +45,7 @@ public class ChatList extends AppCompatActivity { TextView noChatsFoundTextDisplay; FloatingActionButton newChatBtn; - ArrayList> chatList = new ArrayList<>(); - int totalUsers = 0; + List storedChatList = new ArrayList<>(); ProgressDialog pd; @Override @@ -71,30 +64,14 @@ protected void onCreate(Bundle savedInstanceState) { Snackbar.make(findViewById(R.id.usersList), "Welcome back " + UserDetails.username, Snackbar.LENGTH_SHORT) .setAction("Action", null).show(); - // TODO: Change URL to access JSON content from our server - String url = "https://api.myjson.com/bins/lj6f8"; - - StringRequest request = new StringRequest(Request.Method.GET, url, new Response.Listener() { - @Override - public void onResponse(String s) { - doOnSuccess(s); - } - }, new Response.ErrorListener() { - @Override - public void onErrorResponse(VolleyError volleyError) { - System.out.println("" + volleyError); - } - }); - - RequestQueue rQueue = Volley.newRequestQueue(ChatList.this); - rQueue.add(request); + getStoredChatList(); // When the user clicks on a specific chat from his history, go to that conversation usersList.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { - UserDetails.chatWith = chatList.get(position).get("username"); + UserDetails.chatWith = storedChatList.get(position).getUsername(); // TODO: Start Activity --> Goto chat page with X person startActivity(new Intent(ChatList.this, Chat.class)); @@ -110,33 +87,36 @@ public void onClick(View view) { }); } - public void doOnSuccess(String s) { - - try { - JSONObject obj = new JSONObject(s); - - Iterator i = obj.keys(); - String key = ""; + private void getStoredChatList() { + class GetStoredChatList extends AsyncTask> { - HashMap specificUserDetails; - - while(i.hasNext()){ - specificUserDetails = new HashMap<>(); - key = i.next().toString(); - // Populate the hashmap representing chat list details to a specific chat from the list - specificUserDetails.put("username", key); - specificUserDetails.put("last_message", obj.getJSONObject(key).getString("last_message")); - // Add a chat from the list to the final array - chatList.add(specificUserDetails); - totalUsers++; + @Override + protected List doInBackground(Void... voids) { + // SELECT * FROM storedchatlist + List scl = AppDatabaseClient + .getInstance(getApplicationContext()) + .getAppDatabase() + .storedChatListDao() + .getAll(); + return scl; } - } catch (JSONException e) { - e.printStackTrace(); + @Override + protected void onPostExecute(List scl) { + super.onPostExecute(scl); + // Get the chat list and store it on main thread + storedChatList = scl; + displayChatList(); + } } - // If there are no chats with other users found in history, display a suggestive text - if(totalUsers < 1) { + GetStoredChatList gt = new GetStoredChatList(); + gt.execute(); + } + + public void displayChatList() { + + if(storedChatList.size() == 0) { noChatsFoundTextDisplay.setVisibility(View.VISIBLE); usersList.setVisibility(View.GONE); } @@ -146,22 +126,24 @@ public void doOnSuccess(String s) { usersList.setVisibility(View.VISIBLE); // Overridden the getView of ArrayAdapter in order to access both text1 and text2 (from simple_list_item_2) and write on them right away - ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_2, android.R.id.text1, chatList) { + ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_2, android.R.id.text1, storedChatList) { @Override public View getView(int position, View convertView, ViewGroup parent) { View view = super.getView(position, convertView, parent); TextView text1 = (TextView) view.findViewById(android.R.id.text1); TextView text2 = (TextView) view.findViewById(android.R.id.text2); - text1.setText(chatList.get(position).get("username")); - text2.setText(chatList.get(position).get("last_message")); + text1.setText(storedChatList.get(position).getUsername()); + text2.setText(storedChatList.get(position).getLastMessageContent()); return view; } }; usersList.setAdapter(adapter); } + // Close the progress dialog when action is finished pd.dismiss(); + } // TODO: Make searching through chats functional @@ -196,6 +178,14 @@ public boolean onOptionsItemSelected(MenuItem item) { signOutOrUnregister("Unregister"); return true; + case R.id.profile_picture: + // TODO: Choose profile picture -- Goto profile page + return true; + + case R.id.delete_all_chats: + deleteAllChatsPrompt(); + return true; + default: return super.onOptionsItemSelected(item); } @@ -235,4 +225,53 @@ public void onClick(DialogInterface dialog, int id) { dialog.show(); } + public void deleteAllChatsPrompt() { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + + builder.setPositiveButton("DELETE", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + // User clicked DELETE button + deleteAllChats(); + } + }); + + builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + // User cancelled the dialog, go back + } + }); + + // Create the AlertDialog + AlertDialog dialog = builder.create(); + dialog.setTitle("Delete all chats"); + dialog.setMessage("Are you sure you want to delete all the chats?"); + dialog.show(); + } + + private void deleteAllChats() { + class DeleteAllChats extends AsyncTask> { + + @Override + protected List doInBackground(Void... voids) { + // DELETE FROM storedchatlist + AppDatabaseClient + .getInstance(getApplicationContext()) + .getAppDatabase() + .storedChatListDao() + .deleteAll(); + return null; + } + + @Override + protected void onPostExecute(List scl) { + super.onPostExecute(scl); + // Refresh + startActivity(new Intent(ChatList.this, ChatList.class)); + } + } + + DeleteAllChats gt = new DeleteAllChats(); + gt.execute(); + } + } \ No newline at end of file diff --git a/Android/app/src/main/java/teamzero/chat/mobile/NewChat.java b/Android/app/src/main/java/teamzero/chat/mobile/NewChat.java index 6cfd6a4..3310cc4 100644 --- a/Android/app/src/main/java/teamzero/chat/mobile/NewChat.java +++ b/Android/app/src/main/java/teamzero/chat/mobile/NewChat.java @@ -1,13 +1,21 @@ package teamzero.chat.mobile; import android.content.Intent; + +import android.os.AsyncTask; import android.os.Bundle; + import android.support.design.widget.FloatingActionButton; -import android.support.design.widget.Snackbar; import android.support.v7.app.AppCompatActivity; + import android.view.View; + import android.widget.EditText; import android.widget.TextView; +import android.widget.Toast; + +import database.AppDatabaseClient; +import database.StoredChatList; public class NewChat extends AppCompatActivity { @@ -24,17 +32,50 @@ protected void onCreate(Bundle savedInstanceState) { searchUsersBtn = (FloatingActionButton) findViewById(R.id.SearchUsersButton); searchUsersEditText = (EditText) findViewById(R.id.SearchUsersEditText); - Snackbar.make(findViewById(R.id.recyclerViewUsersList), "Currently Under Development . . . New Chat Feature", Snackbar.LENGTH_SHORT) - .setAction("Action", null).show(); - - // TODO: Search for users in database, display user inside RecyclerView and let the user start a chat - // TODO IDEA: Use ListView instead of RecyclerView searchUsersBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { suggestiveTextTextView.setVisibility(View.GONE); + // TODO: Fetch list of users from server and display -- Try using ListView instead of RecyclerView + // Start testing area -- Delete after testing ends + addUserToStoredChatList(); + // End testing area } }); } + private void addUserToStoredChatList() { + + final String searchUsersEditTextString = searchUsersEditText.getText().toString(); + + class AddUserToStoredChatList extends AsyncTask { + + @Override + protected Void doInBackground(Void... voids) { + + StoredChatList scl = new StoredChatList(); + scl.setUsername(searchUsersEditTextString); + scl.setLastMessageContent("Last message here"); + scl.setLastMessageDate(null); + + // Add the user into the local chat list database + AppDatabaseClient.getInstance(getApplicationContext()).getAppDatabase() + .storedChatListDao() + .insert(scl); + return null; + } + + @Override + protected void onPostExecute(Void aVoid) { + super.onPostExecute(aVoid); + finish(); + startActivity(new Intent(getApplicationContext(), ChatList.class)); + Toast.makeText(getApplicationContext(), "Chat created", Toast.LENGTH_LONG).show(); + } + } + + AddUserToStoredChatList st = new AddUserToStoredChatList(); + st.execute(); + } + } diff --git a/Android/app/src/main/res/drawable/ic_user_default_avatar.xml b/Android/app/src/main/res/drawable/ic_user_default_avatar.xml new file mode 100644 index 0000000..9e96ad9 --- /dev/null +++ b/Android/app/src/main/res/drawable/ic_user_default_avatar.xml @@ -0,0 +1,9 @@ + + + diff --git a/Android/app/src/main/res/menu/menu_layout.xml b/Android/app/src/main/res/menu/menu_layout.xml index 970eae0..5b26b2e 100644 --- a/Android/app/src/main/res/menu/menu_layout.xml +++ b/Android/app/src/main/res/menu/menu_layout.xml @@ -4,14 +4,20 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> + + android:title="@string/unregister_text" /> + Settings Sign Out Unregister + Profile picture + Delete all chats diff --git a/Android/build.gradle b/Android/build.gradle index 48947c3..a2cd327 100644 --- a/Android/build.gradle +++ b/Android/build.gradle @@ -25,3 +25,7 @@ allprojects { task clean(type: Delete) { delete rootProject.buildDir } + +ext { + roomVersion = '1.1.1' +} \ No newline at end of file