├── AndroidApp ├── .gitignore ├── .idea │ ├── codeStyles │ │ └── Project.xml │ ├── gradle.xml │ ├── misc.xml │ └── runConfigurations.xml ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── heyletscode │ │ │ └── chattutorial │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── heyletscode │ │ │ │ └── chattutorial │ │ │ │ ├── ChatActivity.java │ │ │ │ ├── MainActivity.java │ │ │ │ └── MessageAdapter.java │ │ └── res │ │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── drawable │ │ │ ├── edittext_design.xml │ │ │ ├── ic_image_black_24dp.xml │ │ │ └── ic_launcher_background.xml │ │ │ ├── layout │ │ │ ├── activity_chat.xml │ │ │ ├── activity_main.xml │ │ │ ├── item_received_message.xml │ │ │ ├── item_received_photo.xml │ │ │ ├── item_sent_image.xml │ │ │ └── item_sent_message.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── heyletscode │ │ └── chattutorial │ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── README.md └── server.js /AndroidApp/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | -------------------------------------------------------------------------------- /AndroidApp/.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | xmlns:android 14 | 15 | ^$ 16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 | xmlns:.* 25 | 26 | ^$ 27 | 28 | 29 | BY_NAME 30 | 31 |
32 |
33 | 34 | 35 | 36 | .*:id 37 | 38 | http://schemas.android.com/apk/res/android 39 | 40 | 41 | 42 |
43 |
44 | 45 | 46 | 47 | .*:name 48 | 49 | http://schemas.android.com/apk/res/android 50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 | 58 | name 59 | 60 | ^$ 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | style 70 | 71 | ^$ 72 | 73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 | .* 81 | 82 | ^$ 83 | 84 | 85 | BY_NAME 86 | 87 |
88 |
89 | 90 | 91 | 92 | .* 93 | 94 | http://schemas.android.com/apk/res/android 95 | 96 | 97 | ANDROID_ATTRIBUTE_ORDER 98 | 99 |
100 |
101 | 102 | 103 | 104 | .* 105 | 106 | .* 107 | 108 | 109 | BY_NAME 110 | 111 |
112 |
113 |
114 |
115 |
116 |
-------------------------------------------------------------------------------- /AndroidApp/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 15 | 16 | -------------------------------------------------------------------------------- /AndroidApp/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /AndroidApp/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /AndroidApp/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /AndroidApp/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 29 5 | buildToolsVersion "29.0.2" 6 | defaultConfig { 7 | applicationId "com.heyletscode.chattutorial" 8 | minSdkVersion 18 9 | targetSdkVersion 29 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | 21 | compileOptions { 22 | sourceCompatibility '1.8' 23 | targetCompatibility '1.8' 24 | } 25 | 26 | } 27 | 28 | dependencies { 29 | implementation fileTree(dir: 'libs', include: ['*.jar']) 30 | implementation 'androidx.appcompat:appcompat:1.1.0' 31 | implementation 'androidx.recyclerview:recyclerview:1.1.0' 32 | implementation 'com.squareup.okhttp3:okhttp:3.10.0' 33 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 34 | testImplementation 'junit:junit:4.12' 35 | androidTestImplementation 'androidx.test:runner:1.2.0' 36 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 37 | } 38 | -------------------------------------------------------------------------------- /AndroidApp/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /AndroidApp/app/src/androidTest/java/com/heyletscode/chattutorial/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.heyletscode.chattutorial; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | 25 | assertEquals("com.heyletscode.chattutorial", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /AndroidApp/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /AndroidApp/app/src/main/java/com/heyletscode/chattutorial/ChatActivity.java: -------------------------------------------------------------------------------- 1 | package com.heyletscode.chattutorial; 2 | 3 | import android.content.Intent; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapFactory; 6 | import android.os.Bundle; 7 | import android.text.Editable; 8 | import android.text.TextWatcher; 9 | import android.util.Base64; 10 | import android.view.View; 11 | import android.widget.EditText; 12 | import android.widget.Toast; 13 | 14 | import androidx.annotation.Nullable; 15 | import androidx.appcompat.app.AppCompatActivity; 16 | import androidx.recyclerview.widget.LinearLayoutManager; 17 | import androidx.recyclerview.widget.RecyclerView; 18 | 19 | import org.json.JSONException; 20 | import org.json.JSONObject; 21 | 22 | import java.io.ByteArrayOutputStream; 23 | import java.io.FileNotFoundException; 24 | import java.io.InputStream; 25 | 26 | import okhttp3.OkHttpClient; 27 | import okhttp3.Request; 28 | import okhttp3.Response; 29 | import okhttp3.WebSocket; 30 | import okhttp3.WebSocketListener; 31 | 32 | public class ChatActivity extends AppCompatActivity implements TextWatcher { 33 | 34 | private String name; 35 | private WebSocket webSocket; 36 | private String SERVER_PATH = "ws://SERVER-IP-HERE:PORT-NUMBER-HERE"; 37 | private EditText messageEdit; 38 | private View sendBtn, pickImgBtn; 39 | private RecyclerView recyclerView; 40 | private int IMAGE_REQUEST_ID = 1; 41 | private MessageAdapter messageAdapter; 42 | 43 | @Override 44 | protected void onCreate(Bundle savedInstanceState) { 45 | super.onCreate(savedInstanceState); 46 | setContentView(R.layout.activity_chat); 47 | 48 | name = getIntent().getStringExtra("name"); 49 | initiateSocketConnection(); 50 | 51 | } 52 | 53 | private void initiateSocketConnection() { 54 | 55 | OkHttpClient client = new OkHttpClient(); 56 | Request request = new Request.Builder().url(SERVER_PATH).build(); 57 | webSocket = client.newWebSocket(request, new SocketListener()); 58 | 59 | } 60 | 61 | @Override 62 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 63 | 64 | } 65 | 66 | @Override 67 | public void onTextChanged(CharSequence s, int start, int before, int count) { 68 | 69 | } 70 | 71 | @Override 72 | public void afterTextChanged(Editable s) { 73 | 74 | String string = s.toString().trim(); 75 | 76 | if (string.isEmpty()) { 77 | resetMessageEdit(); 78 | } else { 79 | 80 | sendBtn.setVisibility(View.VISIBLE); 81 | pickImgBtn.setVisibility(View.INVISIBLE); 82 | } 83 | 84 | } 85 | 86 | private void resetMessageEdit() { 87 | 88 | messageEdit.removeTextChangedListener(this); 89 | 90 | messageEdit.setText(""); 91 | sendBtn.setVisibility(View.INVISIBLE); 92 | pickImgBtn.setVisibility(View.VISIBLE); 93 | 94 | messageEdit.addTextChangedListener(this); 95 | 96 | } 97 | 98 | private class SocketListener extends WebSocketListener { 99 | 100 | @Override 101 | public void onOpen(WebSocket webSocket, Response response) { 102 | super.onOpen(webSocket, response); 103 | 104 | runOnUiThread(() -> { 105 | Toast.makeText(ChatActivity.this, 106 | "Socket Connection Successful!", 107 | Toast.LENGTH_SHORT).show(); 108 | 109 | initializeView(); 110 | }); 111 | 112 | } 113 | 114 | @Override 115 | public void onMessage(WebSocket webSocket, String text) { 116 | super.onMessage(webSocket, text); 117 | 118 | runOnUiThread(() -> { 119 | 120 | try { 121 | JSONObject jsonObject = new JSONObject(text); 122 | jsonObject.put("isSent", false); 123 | 124 | messageAdapter.addItem(jsonObject); 125 | 126 | recyclerView.smoothScrollToPosition(messageAdapter.getItemCount() - 1); 127 | 128 | } catch (JSONException e) { 129 | e.printStackTrace(); 130 | } 131 | 132 | }); 133 | 134 | } 135 | } 136 | 137 | private void initializeView() { 138 | 139 | messageEdit = findViewById(R.id.messageEdit); 140 | sendBtn = findViewById(R.id.sendBtn); 141 | pickImgBtn = findViewById(R.id.pickImgBtn); 142 | 143 | recyclerView = findViewById(R.id.recyclerView); 144 | 145 | messageAdapter = new MessageAdapter(getLayoutInflater()); 146 | recyclerView.setAdapter(messageAdapter); 147 | recyclerView.setLayoutManager(new LinearLayoutManager(this)); 148 | 149 | 150 | messageEdit.addTextChangedListener(this); 151 | 152 | sendBtn.setOnClickListener(v -> { 153 | 154 | JSONObject jsonObject = new JSONObject(); 155 | try { 156 | jsonObject.put("name", name); 157 | jsonObject.put("message", messageEdit.getText().toString()); 158 | 159 | webSocket.send(jsonObject.toString()); 160 | 161 | jsonObject.put("isSent", true); 162 | messageAdapter.addItem(jsonObject); 163 | 164 | recyclerView.smoothScrollToPosition(messageAdapter.getItemCount() - 1); 165 | 166 | resetMessageEdit(); 167 | 168 | } catch (JSONException e) { 169 | e.printStackTrace(); 170 | } 171 | 172 | }); 173 | 174 | pickImgBtn.setOnClickListener(v -> { 175 | 176 | Intent intent = new Intent(Intent.ACTION_GET_CONTENT); 177 | intent.setType("image/*"); 178 | 179 | startActivityForResult(Intent.createChooser(intent, "Pick image"), 180 | IMAGE_REQUEST_ID); 181 | 182 | }); 183 | 184 | } 185 | 186 | @Override 187 | protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { 188 | super.onActivityResult(requestCode, resultCode, data); 189 | 190 | if (requestCode == IMAGE_REQUEST_ID && resultCode == RESULT_OK) { 191 | 192 | try { 193 | InputStream is = getContentResolver().openInputStream(data.getData()); 194 | Bitmap image = BitmapFactory.decodeStream(is); 195 | 196 | sendImage(image); 197 | 198 | } catch (FileNotFoundException e) { 199 | e.printStackTrace(); 200 | } 201 | 202 | } 203 | 204 | } 205 | 206 | private void sendImage(Bitmap image) { 207 | 208 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 209 | image.compress(Bitmap.CompressFormat.JPEG, 50, outputStream); 210 | 211 | String base64String = Base64.encodeToString(outputStream.toByteArray(), 212 | Base64.DEFAULT); 213 | 214 | JSONObject jsonObject = new JSONObject(); 215 | 216 | try { 217 | jsonObject.put("name", name); 218 | jsonObject.put("image", base64String); 219 | 220 | webSocket.send(jsonObject.toString()); 221 | 222 | jsonObject.put("isSent", true); 223 | 224 | messageAdapter.addItem(jsonObject); 225 | 226 | recyclerView.smoothScrollToPosition(messageAdapter.getItemCount() - 1); 227 | 228 | } catch (JSONException e) { 229 | e.printStackTrace(); 230 | } 231 | 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /AndroidApp/app/src/main/java/com/heyletscode/chattutorial/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.heyletscode.chattutorial; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | import androidx.core.app.ActivityCompat; 5 | 6 | import android.Manifest; 7 | import android.content.Intent; 8 | import android.content.pm.PackageManager; 9 | import android.os.Bundle; 10 | import android.widget.EditText; 11 | 12 | public class MainActivity extends AppCompatActivity { 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_main); 18 | 19 | 20 | if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) 21 | != PackageManager.PERMISSION_GRANTED) 22 | ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 10); 23 | 24 | 25 | EditText editText = findViewById(R.id.editText); 26 | 27 | findViewById(R.id.enterBtn) 28 | .setOnClickListener(v -> { 29 | 30 | Intent intent = new Intent(this, ChatActivity.class); 31 | intent.putExtra("name", editText.getText().toString()); 32 | startActivity(intent); 33 | 34 | }); 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /AndroidApp/app/src/main/java/com/heyletscode/chattutorial/MessageAdapter.java: -------------------------------------------------------------------------------- 1 | package com.heyletscode.chattutorial; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.BitmapFactory; 5 | import android.util.Base64; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.ImageView; 10 | import android.widget.TextView; 11 | 12 | import androidx.annotation.NonNull; 13 | import androidx.recyclerview.widget.RecyclerView; 14 | 15 | import org.json.JSONException; 16 | import org.json.JSONObject; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | public class MessageAdapter extends RecyclerView.Adapter { 22 | 23 | private static final int TYPE_MESSAGE_SENT = 0; 24 | private static final int TYPE_MESSAGE_RECEIVED = 1; 25 | private static final int TYPE_IMAGE_SENT = 2; 26 | private static final int TYPE_IMAGE_RECEIVED = 3; 27 | 28 | private LayoutInflater inflater; 29 | private List messages = new ArrayList<>(); 30 | 31 | public MessageAdapter (LayoutInflater inflater) { 32 | this.inflater = inflater; 33 | } 34 | 35 | private class SentMessageHolder extends RecyclerView.ViewHolder { 36 | 37 | TextView messageTxt; 38 | 39 | public SentMessageHolder(@NonNull View itemView) { 40 | super(itemView); 41 | 42 | messageTxt = itemView.findViewById(R.id.sentTxt); 43 | } 44 | } 45 | 46 | private class SentImageHolder extends RecyclerView.ViewHolder { 47 | 48 | ImageView imageView; 49 | 50 | public SentImageHolder(@NonNull View itemView) { 51 | super(itemView); 52 | 53 | imageView = itemView.findViewById(R.id.imageView); 54 | } 55 | } 56 | 57 | private class ReceivedMessageHolder extends RecyclerView.ViewHolder { 58 | 59 | TextView nameTxt, messageTxt; 60 | 61 | public ReceivedMessageHolder(@NonNull View itemView) { 62 | super(itemView); 63 | 64 | nameTxt = itemView.findViewById(R.id.nameTxt); 65 | messageTxt = itemView.findViewById(R.id.receivedTxt); 66 | } 67 | } 68 | 69 | private class ReceivedImageHolder extends RecyclerView.ViewHolder { 70 | 71 | ImageView imageView; 72 | TextView nameTxt; 73 | 74 | public ReceivedImageHolder(@NonNull View itemView) { 75 | super(itemView); 76 | 77 | imageView = itemView.findViewById(R.id.imageView); 78 | nameTxt = itemView.findViewById(R.id.nameTxt); 79 | 80 | } 81 | } 82 | 83 | @Override 84 | public int getItemViewType(int position) { 85 | 86 | JSONObject message = messages.get(position); 87 | 88 | try { 89 | if (message.getBoolean("isSent")) { 90 | 91 | if (message.has("message")) 92 | return TYPE_MESSAGE_SENT; 93 | else 94 | return TYPE_IMAGE_SENT; 95 | 96 | } else { 97 | 98 | if (message.has("message")) 99 | return TYPE_MESSAGE_RECEIVED; 100 | else 101 | return TYPE_IMAGE_RECEIVED; 102 | 103 | } 104 | } catch (JSONException e) { 105 | e.printStackTrace(); 106 | } 107 | 108 | return -1; 109 | } 110 | 111 | @NonNull 112 | @Override 113 | public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 114 | 115 | View view; 116 | 117 | switch (viewType) { 118 | case TYPE_MESSAGE_SENT: 119 | view = inflater.inflate(R.layout.item_sent_message, parent, false); 120 | return new SentMessageHolder(view); 121 | case TYPE_MESSAGE_RECEIVED: 122 | 123 | view = inflater.inflate(R.layout.item_received_message, parent, false); 124 | return new ReceivedMessageHolder(view); 125 | 126 | case TYPE_IMAGE_SENT: 127 | 128 | view = inflater.inflate(R.layout.item_sent_image, parent, false); 129 | return new SentImageHolder(view); 130 | 131 | case TYPE_IMAGE_RECEIVED: 132 | 133 | view = inflater.inflate(R.layout.item_received_photo, parent, false); 134 | return new ReceivedImageHolder(view); 135 | 136 | } 137 | 138 | return null; 139 | } 140 | 141 | @Override 142 | public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { 143 | 144 | JSONObject message = messages.get(position); 145 | 146 | try { 147 | if (message.getBoolean("isSent")) { 148 | 149 | if (message.has("message")) { 150 | 151 | SentMessageHolder messageHolder = (SentMessageHolder) holder; 152 | messageHolder.messageTxt.setText(message.getString("message")); 153 | 154 | } else { 155 | 156 | SentImageHolder imageHolder = (SentImageHolder) holder; 157 | Bitmap bitmap = getBitmapFromString(message.getString("image")); 158 | 159 | imageHolder.imageView.setImageBitmap(bitmap); 160 | 161 | } 162 | 163 | } else { 164 | 165 | if (message.has("message")) { 166 | 167 | ReceivedMessageHolder messageHolder = (ReceivedMessageHolder) holder; 168 | messageHolder.nameTxt.setText(message.getString("name")); 169 | messageHolder.messageTxt.setText(message.getString("message")); 170 | 171 | } else { 172 | 173 | ReceivedImageHolder imageHolder = (ReceivedImageHolder) holder; 174 | imageHolder.nameTxt.setText(message.getString("name")); 175 | 176 | Bitmap bitmap = getBitmapFromString(message.getString("image")); 177 | imageHolder.imageView.setImageBitmap(bitmap); 178 | 179 | } 180 | 181 | } 182 | } catch (JSONException e) { 183 | e.printStackTrace(); 184 | } 185 | 186 | } 187 | 188 | private Bitmap getBitmapFromString(String image) { 189 | 190 | byte[] bytes = Base64.decode(image, Base64.DEFAULT); 191 | return BitmapFactory.decodeByteArray(bytes, 0, bytes.length); 192 | } 193 | 194 | @Override 195 | public int getItemCount() { 196 | return messages.size(); 197 | } 198 | 199 | public void addItem (JSONObject jsonObject) { 200 | messages.add(jsonObject); 201 | notifyDataSetChanged(); 202 | } 203 | 204 | } 205 | -------------------------------------------------------------------------------- /AndroidApp/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /AndroidApp/app/src/main/res/drawable/edittext_design.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /AndroidApp/app/src/main/res/drawable/ic_image_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /AndroidApp/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /AndroidApp/app/src/main/res/layout/activity_chat.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 15 | 16 | 28 | 29 | 40 | 41 | 52 | 53 | -------------------------------------------------------------------------------- /AndroidApp/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 19 | 20 |