Browse Source

added sending/displaying/fullscreen/receiving images with captions,
bug fixes,
removed extra images from apk,

need to encrypt tor files,

Sofian Benissa 3 years ago
parent
commit
7b7470b96d
45 changed files with 1324 additions and 216 deletions
  1. 18 0
      .idea/codeStyles/Project.xml
  2. 45 40
      T0rlib4Droid/src/main/java/net/sf/msopentech/thali/java/toronionproxy/OnionProxyManagerEventHandler.java
  3. 16 6
      app/src/main/AndroidManifest.xml
  4. 6 0
      app/src/main/java/com/dx/anonymousmessenger/AppActivity.java
  5. 4 1
      app/src/main/java/com/dx/anonymousmessenger/AppFragment.java
  6. 2 0
      app/src/main/java/com/dx/anonymousmessenger/CreateUserActivity.java
  7. 49 18
      app/src/main/java/com/dx/anonymousmessenger/DxApplication.java
  8. 3 0
      app/src/main/java/com/dx/anonymousmessenger/MainActivity.java
  9. 231 17
      app/src/main/java/com/dx/anonymousmessenger/MessageListActivity.java
  10. 30 24
      app/src/main/java/com/dx/anonymousmessenger/MyRecyclerViewAdapter.java
  11. 4 4
      app/src/main/java/com/dx/anonymousmessenger/MyService.java
  12. 2 0
      app/src/main/java/com/dx/anonymousmessenger/PasswordEntryFragment.java
  13. 141 0
      app/src/main/java/com/dx/anonymousmessenger/PictureViewerActivity.java
  14. 52 42
      app/src/main/java/com/dx/anonymousmessenger/SetupInProcess.java
  15. 1 1
      app/src/main/java/com/dx/anonymousmessenger/SetupUsernameFragment.java
  16. 22 5
      app/src/main/java/com/dx/anonymousmessenger/db/DbHelper.java
  17. 83 0
      app/src/main/java/com/dx/anonymousmessenger/media/MediaRecycleViewAdapter.java
  18. 83 0
      app/src/main/java/com/dx/anonymousmessenger/messages/MessageListAdapter.java
  19. 8 6
      app/src/main/java/com/dx/anonymousmessenger/messages/MessageSender.java
  20. 1 1
      app/src/main/java/com/dx/anonymousmessenger/messages/QuotedUserMessage.java
  21. 4 6
      app/src/main/java/com/dx/anonymousmessenger/tor/ServerSocketViaTor.java
  22. 1 1
      app/src/main/java/com/dx/anonymousmessenger/tor/TorClientSocks4.java
  23. 7 0
      app/src/main/res/anim/slide_down.xml
  24. 7 0
      app/src/main/res/anim/slide_up.xml
  25. BIN
      app/src/main/res/drawable/baseline_add_black_18dp.png
  26. BIN
      app/src/main/res/drawable/baseline_person_add_black_36dp.png
  27. BIN
      app/src/main/res/drawable/biglogo.png
  28. 5 0
      app/src/main/res/drawable/ic_baseline_attach_file_24.xml
  29. BIN
      app/src/main/res/drawable/logo_real.png
  30. BIN
      app/src/main/res/drawable/message_backgound_02.jpg
  31. 0 4
      app/src/main/res/drawable/test.xml
  32. 92 9
      app/src/main/res/layout/activity_message_list.xml
  33. 53 0
      app/src/main/res/layout/activity_picture_viewer.xml
  34. 3 3
      app/src/main/res/layout/fragment_setup_username.xml
  35. 104 0
      app/src/main/res/layout/item_media_message_received.xml
  36. 71 0
      app/src/main/res/layout/item_media_message_sent.xml
  37. 80 0
      app/src/main/res/layout/item_media_message_sent_ok.xml
  38. 11 9
      app/src/main/res/layout/item_message_sent_ok.xml
  39. 10 8
      app/src/main/res/layout/item_message_sent_ok_quote.xml
  40. 16 0
      app/src/main/res/layout/media_rv_item.xml
  41. 22 6
      app/src/main/res/layout/my_text_view.xml
  42. 16 0
      app/src/main/res/values-ar/strings.xml
  43. 18 1
      app/src/main/res/values/strings.xml
  44. 2 3
      app/src/main/res/values/styles.xml
  45. 1 1
      build.gradle

+ 18 - 0
.idea/codeStyles/Project.xml

@@ -1,5 +1,23 @@
 <component name="ProjectCodeStyleConfiguration">
   <code_scheme name="Project" version="173">
+    <JetCodeStyleSettings>
+      <option name="PACKAGES_TO_USE_STAR_IMPORTS">
+        <value>
+          <package name="java.util" alias="false" withSubpackages="false" />
+          <package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
+          <package name="io.ktor" alias="false" withSubpackages="true" />
+        </value>
+      </option>
+      <option name="PACKAGES_IMPORT_LAYOUT">
+        <value>
+          <package name="" alias="false" withSubpackages="true" />
+          <package name="java" alias="false" withSubpackages="true" />
+          <package name="javax" alias="false" withSubpackages="true" />
+          <package name="kotlin" alias="false" withSubpackages="true" />
+          <package name="" alias="true" withSubpackages="true" />
+        </value>
+      </option>
+    </JetCodeStyleSettings>
     <codeStyleSettings language="XML">
       <indentOptions>
         <option name="CONTINUATION_INDENT_SIZE" value="4" />

+ 45 - 40
T0rlib4Droid/src/main/java/net/sf/msopentech/thali/java/toronionproxy/OnionProxyManagerEventHandler.java

@@ -40,7 +40,6 @@ import net.sf.freehaven.tor.control.EventHandler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -54,43 +53,42 @@ public class OnionProxyManagerEventHandler implements EventHandler {
     private NetLayerStatus listener;
     private boolean hsPublished;
     private OnionProxyContext onionProxyContext;
-    private volatile List<String> statuses = new ArrayList<>();
-    private volatile boolean lastMessage;
-
-    private void sendStatuses(){
-        new Thread(new Runnable() {
-            @Override
-            public void run() {
-                while (!lastMessage){
-                    try{
-                        Thread.sleep(500);
-                    }catch (Exception ignored) {}
-                }
-                for (int i=0;i<statuses.size();i++){
-                    Intent gcm_rec = new Intent("tor_status");
-                    gcm_rec.putExtra("tor_status",statuses.get(i));
-                    LocalBroadcastManager.getInstance(onionProxyContext.ctx).sendBroadcast(gcm_rec);
-                    if(statuses.get(i).contains("100%")){
-                        return;
-                    }
-                    try{
-                        Thread.sleep(500);
-                    }catch (Exception ignored) {}
+//    private volatile List<String> statuses = new ArrayList<>();
+//    private volatile boolean lastMessage;
+
+//    private void sendStatuses(){
+//        new Thread(new Runnable() {
+//            @Override
+//            public void run() {
+//                while (!lastMessage){
+//                    try{
+//                        Thread.sleep(500);
+//                    }catch (Exception ignored) {}
+//                }
+//                for (int i=0;i<statuses.size();i++){
+//                    Intent gcm_rec = new Intent("tor_status");
+//                    gcm_rec.putExtra("tor_status",statuses.get(i));
+//                    LocalBroadcastManager.getInstance(onionProxyContext.ctx).sendBroadcast(gcm_rec);
+//                    if(statuses.get(i).contains("100%")){
+//                        return;
+//                    }
+//                    try{
+//                        Thread.sleep(500);
+//                    }catch (Exception ignored) {}
 //                    while (i==(statuses.size()-1)){
 //                        System.out.println("running in while last");
 //                        try{
 //                            Thread.sleep(500);
 //                        }catch (Exception ignored) {}
 //                    }
-                }
-            }
-        }).start();
-
-    }
+//                }
+//            }
+//        }).start();
+//    }
 
     public OnionProxyManagerEventHandler(OnionProxyContext onionProxyContext) {
         this.onionProxyContext = onionProxyContext;
-        sendStatuses();
+//        sendStatuses();
     }
 
     public void setHStoWatchFor(ServiceDescriptor hs, NetLayerStatus listener) {
@@ -121,9 +119,16 @@ public class OnionProxyManagerEventHandler implements EventHandler {
     @Override
     public void circuitStatus(String status, String circID, String path) {
         LOG.info("status: " + status + ", circID: " + circID + ", path: " + path);
-        Intent gcm_rec = new Intent("tor_status1");
-        gcm_rec.putExtra("tor_status1","status: " + status + ", circID: " + circID + ", path: " + path);
-        LocalBroadcastManager.getInstance(onionProxyContext.ctx).sendBroadcast(gcm_rec);
+        try{
+            Intent gcm_rec = new Intent("tor_status1");
+            StringBuilder output = new StringBuilder();
+            for (String relay: path.split(",")){
+                output.append(relay.split("~")[1]);
+                output.append(" > ");
+            }
+            gcm_rec.putExtra("tor_status1","circ: " + circID + " "+status+", (" + output + ")");
+            LocalBroadcastManager.getInstance(onionProxyContext.ctx).sendBroadcast(gcm_rec);
+        }catch (Exception ignored) {}
     }
 
     public void streamStatus(String status, String id, String target) {
@@ -162,14 +167,14 @@ public class OnionProxyManagerEventHandler implements EventHandler {
     //fetch Exit Node
     public void message(String severity, String msg) {
         LOG.info("message: severity: " + severity + ", msg: " + msg);
-        String out = "message: " + severity + ", msg: " + msg;
-        if(out.contains("100%")){
-            lastMessage = true;
-        }
-        statuses.add(out);
-//        Intent gcm_rec = new Intent("tor_status");
-//        gcm_rec.putExtra("tor_status","message: " + severity + ", msg: " + msg);
-//        LocalBroadcastManager.getInstance(onionProxyContext.ctx).sendBroadcast(gcm_rec);
+//        String out = "message: " + severity + ", msg: " + msg;
+//        if(out.contains("100%")){
+//            lastMessage = true;
+//        }
+//        statuses.add(out);
+        Intent gcm_rec = new Intent("tor_status");
+        gcm_rec.putExtra("tor_status","message: " + severity + ", msg: " + msg);
+        LocalBroadcastManager.getInstance(onionProxyContext.ctx).sendBroadcast(gcm_rec);
     }
 
     public void unrecognized(String type, String msg) {

+ 16 - 6
app/src/main/AndroidManifest.xml

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     package="com.dx.anonymousmessenger">
-
     <!-- <uses-feature -->
     <!-- android:name="android.hardware.camera" -->
     <!-- android:required="false" /> -->
@@ -17,26 +17,35 @@
     <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
     <uses-permission android:name="android.permission.RECORD_AUDIO" />
     <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 
     <uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
     <uses-permission-sdk-23 android:name="android.permission.FOREGROUND_SERVICE" />
 
     <application
         android:name=".DxApplication"
-        android:allowBackup="true"
+        android:allowBackup="false"
+        android:fullBackupContent="false"
         android:icon="@drawable/notification"
         android:label="@string/app_name"
         android:roundIcon="@mipmap/ic_launcher"
         android:supportsRtl="true"
         android:theme="@style/AppTheme">
-
+        <activity android:name=".PictureViewerActivity" />
         <activity android:name=".RingingActivity" />
         <activity android:name=".CallActivity" />
-        <activity android:name=".AddContactActivity"/>
+        <activity android:name=".AddContactActivity" />
         <activity android:name=".VerifyIdentityActivity" />
         <activity android:name=".MyIdentityActivity" />
         <activity android:name=".SetupInProcess" />
-        <activity android:name=".MessageListActivity" />
+        <activity android:name=".MessageListActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.PICK" />
+
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
         <activity android:name=".AppActivity" />
 
         <receiver android:name=".NotificationHiderReceiver" />
@@ -44,7 +53,8 @@
             android:name=".NetworkChangeReceiver"
             android:label="NetworkChangeReceiver">
             <intent-filter>
-                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
+                <action android:name="android.net.conn.CONNECTIVITY_CHANGE"
+                    tools:ignore="BatteryLife" />
                 <action android:name="android.net.wifi.WIFI_STATE_CHANGED" />
             </intent-filter>
         </receiver>

+ 6 - 0
app/src/main/java/com/dx/anonymousmessenger/AppActivity.java

@@ -24,6 +24,11 @@ public class AppActivity extends AppCompatActivity implements ComponentCallbacks
         setContentView(R.layout.activity_app);
 
         new Thread(()->{
+            if(((DxApplication) this.getApplication()).isExitingHoldup()){
+                loadAppFragment();
+                ((DxApplication) this.getApplication()).setExitingHoldup(false);
+                return;
+            }
             ((DxApplication) this.getApplication()).setExitingHoldup(false);
             //has logged in?
             if(((DxApplication) this.getApplication()).getAccount()!=null){
@@ -72,6 +77,7 @@ public class AppActivity extends AppCompatActivity implements ComponentCallbacks
     public void goToTorActivity() {
         Intent tor = new Intent(this, SetupInProcess.class);
         tor.putExtra("first_time",false);
+        tor.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
         startActivity(tor);
         finish();
     }

+ 4 - 1
app/src/main/java/com/dx/anonymousmessenger/AppFragment.java

@@ -162,6 +162,7 @@ public class AppFragment extends Fragment {
         FloatingActionButton btnAddContact = rootView.findViewById(R.id.btn_add_contact);
         btnAddContact.setOnClickListener(v -> {
             Intent intent = new Intent(v.getContext(), AddContactActivity.class);
+            intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
             v.getContext().startActivity(intent);
         });
 
@@ -257,6 +258,7 @@ public class AppFragment extends Fragment {
                         mAdapter = new MyRecyclerViewAdapter((DxApplication) getActivity().getApplication(),lst,this);
                         recyclerView.setAdapter(mAdapter);
                         mAdapter.notifyDataSetChanged();
+                        ((DxApplication)getActivity().getApplication()).clearMessageNotification();
                     } catch (Exception ignored) {}
                 });
             }catch (Exception ignored){}
@@ -355,8 +357,9 @@ public class AppFragment extends Fragment {
                         onlineImg.setVisibility(View.GONE);
                         offlineImg.setVisibility(View.VISIBLE);
                         onlineToolbar.setVisibility(View.VISIBLE);
-                        Intent intent = new Intent((DxApplication)getActivity().getApplication(), SetupInProcess.class);
+                        Intent intent = new Intent(getActivity().getApplication(), SetupInProcess.class);
                         intent.putExtra("first_time",false);
+                        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
                         startActivity(intent);
                     } catch (Exception ignored) {}
                 });

+ 2 - 0
app/src/main/java/com/dx/anonymousmessenger/CreateUserActivity.java

@@ -77,6 +77,7 @@ public class CreateUserActivity extends AppCompatActivity {
         try{
             ((DxApplication)getApplication()).createAccount(password,nickname);
             Intent intent = new Intent(this, SetupInProcess.class);
+            intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
             startActivity(intent);
             finish();
         }catch(Exception e){
@@ -86,6 +87,7 @@ public class CreateUserActivity extends AppCompatActivity {
 
     public void switchToAppView(){
         Intent intent = new Intent(this, AppActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
         startActivity(intent);
         finish();
     }

+ 49 - 18
app/src/main/java/com/dx/anonymousmessenger/DxApplication.java

@@ -49,8 +49,15 @@ public class DxApplication extends Application {
     private Entity entity;
     private CallController cc;
     private boolean exitingHoldup;
-    private volatile List<QuotedUserMessage> messageQueue = new ArrayList<>();
+    private List<QuotedUserMessage> messageQueue = new ArrayList<>();
     private volatile boolean syncing;
+    public List<String> onlineList = new ArrayList<>();
+
+    public void addToOnlineList(String address){
+        if(!onlineList.contains(address)){
+            onlineList.add(address);
+        }
+    }
 
     public void addToMessagesQueue(List<QuotedUserMessage> messages){
         for (QuotedUserMessage message:messages) {
@@ -94,18 +101,26 @@ public class DxApplication extends Application {
                 }
                 for (String[] contact: contactsList){
                     List<QuotedUserMessage> undeliveredMessageList = DbHelper.getUndeliveredMessageList(this, contact[1]);
-                    if (undeliveredMessageList.size()==0){
-                        return;
-                    }
-                    addToMessagesQueue(undeliveredMessageList);
-                    boolean b = new TorClientSocks4().testAddress(this, contact[1]);
+                    boolean b = TorClientSocks4.testAddress(this, contact[1]);
                     if(!b){
-                        return;
+                        onlineList.remove(contact[1]);
+                        Intent gcm_rec = new Intent("your_action");
+                        LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(gcm_rec);
+                        continue;
+                    }
+                    if(!onlineList.contains(contact[1])){
+                        onlineList.add(contact[1]);
+                        Intent gcm_rec = new Intent("your_action");
+                        LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(gcm_rec);
                     }
+                    if(undeliveredMessageList.size()==0){
+                        continue;
+                    }
+                    addToMessagesQueue(undeliveredMessageList);
                     sendQueuedMessages();
-                    syncing = false;
                 }
-            }catch (Exception ignored) {}
+                syncing = false;
+            }catch (Exception ignored) {syncing = false;}
         }).start();
     }
 
@@ -116,15 +131,25 @@ public class DxApplication extends Application {
                     Thread.sleep(1000);
                 }catch (Exception ignored) {}
             }
-            syncing = true;
-            List<QuotedUserMessage> undeliveredMessageList = DbHelper.getUndeliveredMessageList(this, address);
-            addToMessagesQueue(undeliveredMessageList);
-            boolean b = new TorClientSocks4().testAddress(this, address);
-            if(!b){
-                return;
-            }
-            sendQueuedMessages();
-            syncing = false;
+            try{
+                syncing = true;
+                List<QuotedUserMessage> undeliveredMessageList = DbHelper.getUndeliveredMessageList(this, address);
+                if(undeliveredMessageList.size()==0){
+                    return;
+                }
+                boolean b = TorClientSocks4.testAddress(this, address);
+                if(!b){
+                    syncing = false;
+                    onlineList.remove(address);
+                    return;
+                }
+                if(!onlineList.contains(address)){
+                    onlineList.add(address);
+                }
+                addToMessagesQueue(undeliveredMessageList);
+                sendQueuedMessages();
+                syncing = false;
+            }catch (Exception ignored) {syncing = false;}
         }).start();
     }
 
@@ -241,6 +266,12 @@ public class DxApplication extends Application {
         LocalBroadcastManager.getInstance(this).sendBroadcast(gcm_rec);
     }
 
+    public void clearMessageNotification(){
+        NotificationManager mNotificationManager =
+                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+        mNotificationManager.cancel(1);
+    }
+
     public void sendNotification(String title, String msg){
         Intent resultIntent = new Intent(this, MainActivity.class);
         // Create the TaskStackBuilder and add the intent, which inflates the back stack

+ 3 - 0
app/src/main/java/com/dx/anonymousmessenger/MainActivity.java

@@ -74,18 +74,21 @@ public class MainActivity extends AppCompatActivity {
 
     public void onNextClick(View view){
         Intent intent = new Intent(this, CreateUserActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
         startActivity(intent);
         finish();
     }
 
     public void switchToAppView(){
         Intent intent = new Intent(this, AppActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
         startActivity(intent);
         finish();
     }
 
     public void switchToSetupInProcess(){
         Intent intent = new Intent(this, SetupInProcess.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
         startActivity(intent);
         finish();
     }

+ 231 - 17
app/src/main/java/com/dx/anonymousmessenger/MessageListActivity.java

@@ -9,12 +9,15 @@ import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.net.Uri;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.Looper;
+import android.provider.MediaStore;
 import android.text.Editable;
 import android.text.TextWatcher;
 import android.transition.Explode;
-import android.util.Log;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.MotionEvent;
@@ -23,6 +26,7 @@ import android.view.Window;
 import android.view.WindowManager;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
 import android.widget.Button;
 import android.widget.EditText;
 import android.widget.LinearLayout;
@@ -39,9 +43,11 @@ import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.dx.anonymousmessenger.db.DbHelper;
+import com.dx.anonymousmessenger.media.MediaRecycleViewAdapter;
 import com.dx.anonymousmessenger.messages.MessageListAdapter;
 import com.dx.anonymousmessenger.messages.MessageSender;
 import com.dx.anonymousmessenger.messages.QuotedUserMessage;
+import com.dx.anonymousmessenger.tor.TorClientSocks4;
 import com.google.android.material.floatingactionbutton.FloatingActionButton;
 import com.google.android.material.snackbar.Snackbar;
 
@@ -52,9 +58,10 @@ import java.util.Date;
 import java.util.List;
 import java.util.Objects;
 
-public class MessageListActivity extends AppCompatActivity implements ActivityCompat.OnRequestPermissionsResultCallback, ComponentCallbacks2 {
+public class MessageListActivity extends AppCompatActivity implements ActivityCompat.OnRequestPermissionsResultCallback, ComponentCallbacks2, MyRecyclerViewAdapter.ItemClickListener {
 
     private static final int REQUEST_CODE = 1;
+    private static final int READ_STORAGE_REQUEST_CODE = 2;
     public TextView quoteTextTyping;
     public TextView quoteSenderTyping;
     private RecyclerView mMessageRecycler;
@@ -66,10 +73,16 @@ public class MessageListActivity extends AppCompatActivity implements ActivityCo
     private Button send = null;
     private EditText txt = null;
     private TextView txtAudioTimer = null;
-    private FloatingActionButton audio;
+    private FloatingActionButton audio, file;
     private LinearLayout audioLayout;
     private FloatingActionButton scrollDownFab;
     private BroadcastReceiver timeBroadcastReceiver;
+    private TextView status;
+    private RecyclerView mediaRecyclerView;
+    private LinearLayout picsHelp;
+    private String address;
+    private String nickname;
+//    private TextView picsHelpText;
 //    private ImageView syncedImg;
 //    private ImageView unsyncedImg;
 //    private TextView syncTxt;
@@ -86,6 +99,8 @@ public class MessageListActivity extends AppCompatActivity implements ActivityCo
         Objects.requireNonNull(getSupportActionBar()).setTitle(getIntent().getStringExtra("nickname"));
         getSupportActionBar().setSubtitle(getIntent().getStringExtra("address"));
         getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+        address = getIntent().getStringExtra("address");
+        nickname = getIntent().getStringExtra("nickname");
         quoteTextTyping = findViewById(R.id.quote_text_typing);
         quoteSenderTyping = findViewById(R.id.quote_sender_typing);
         quoteTextTyping.setVisibility(View.GONE);
@@ -95,8 +110,13 @@ public class MessageListActivity extends AppCompatActivity implements ActivityCo
         send = findViewById(R.id.button_chatbox_send);
         txt = findViewById(R.id.edittext_chatbox);
         audio = findViewById(R.id.fab_audio);
+        file = findViewById(R.id.fab_file);
+        mediaRecyclerView = findViewById(R.id.rv_media);
+        picsHelp = findViewById(R.id.layout_pics);
         audioLayout = findViewById(R.id.layout_audio);
         txtAudioTimer = findViewById(R.id.txt_audio_timer);
+        status = findViewById(R.id.txt_status);
+        status.setOnClickListener((v)-> status.setVisibility(View.GONE));
 //        syncedImg = findViewById(R.id.synced_image);
 //        unsyncedImg = findViewById(R.id.unsynced_image);
 //        syncTxt = findViewById(R.id.sync_text);
@@ -114,7 +134,7 @@ public class MessageListActivity extends AppCompatActivity implements ActivityCo
                     return;
                 }
                 if (dy > 0
-                        && ((LinearLayoutManager)recyclerView.getLayoutManager())!=null
+                        && recyclerView.getLayoutManager() !=null
                         && ((LinearLayoutManager)recyclerView.getLayoutManager()).findLastVisibleItemPosition()!=(messageList.size()-1)) { // scrolling down
                     if(scrollDownFab.getVisibility()==View.VISIBLE){
                         return;
@@ -128,7 +148,7 @@ public class MessageListActivity extends AppCompatActivity implements ActivityCo
                     scrollDownFab.setVisibility(View.VISIBLE);
                     scrollDownFab.startAnimation(animation);
                 } else if (dy < 0
-                        && ((LinearLayoutManager)recyclerView.getLayoutManager())!=null
+                        && recyclerView.getLayoutManager() !=null
                         && ((LinearLayoutManager)recyclerView.getLayoutManager()).findLastVisibleItemPosition()!=(messageList.size()-1)) { // scrolling up
                     if(scrollDownFab.getVisibility()==View.VISIBLE){
                         return;
@@ -154,7 +174,7 @@ public class MessageListActivity extends AppCompatActivity implements ActivityCo
                     return;
                 }
                 if (newState == RecyclerView.SCROLL_STATE_IDLE
-                        && ((LinearLayoutManager)recyclerView.getLayoutManager())!=null
+                        && recyclerView.getLayoutManager() !=null
                         && ((LinearLayoutManager)recyclerView.getLayoutManager()).findLastVisibleItemPosition()==(messageList.size()-1)) { // No scrolling
                     scrollDownFab.clearAnimation();
                     final Animation animation = new AlphaAnimation(1f, 0f);
@@ -181,7 +201,7 @@ public class MessageListActivity extends AppCompatActivity implements ActivityCo
             TextView quoteSenderTyping = findViewById(R.id.quote_sender_typing);
             TextView quoteTextTyping = findViewById(R.id.quote_text_typing);
             QuotedUserMessage msg =
-                    new QuotedUserMessage(quoteSenderTyping.getText().toString().equals(R.string.you)?
+                    new QuotedUserMessage(quoteSenderTyping.getText().toString().equals(getString(R.string.you))?
                             ((DxApplication)getApplication()).getAccount().getNickname():getIntent().getStringExtra("nickname"),quoteTextTyping.getText().toString(),((DxApplication)getApplication()).getHostname(),txt.getText().toString(),((DxApplication)getApplication()).getAccount().getNickname(),new Date().getTime(),false,getIntent().getStringExtra("address"),false);
             messageList.add(msg);
             new Thread(()-> MessageSender.sendMessage(msg,((DxApplication)getApplication()),getIntent().getStringExtra("address"))).start();
@@ -191,6 +211,7 @@ public class MessageListActivity extends AppCompatActivity implements ActivityCo
             quoteSenderTyping.setVisibility(View.GONE);
             quoteTextTyping.setVisibility(View.GONE);
             audio.setVisibility(View.VISIBLE);
+            file.setVisibility(View.VISIBLE);
             mMessageAdapter.notifyItemInserted(messageList.size()-1);
             mMessageRecycler.smoothScrollToPosition(messageList.size() - 1);
         });
@@ -216,8 +237,10 @@ public class MessageListActivity extends AppCompatActivity implements ActivityCo
             public void onTextChanged(CharSequence s, int start, int before, int count) {
                 if(s.length()==0){
                     audio.setVisibility(View.VISIBLE);
+                    file.setVisibility(View.VISIBLE);
                 }else{
                     audio.setVisibility(View.GONE);
+                    file.setVisibility(View.GONE);
                 }
             }
 
@@ -229,6 +252,8 @@ public class MessageListActivity extends AppCompatActivity implements ActivityCo
         audio.setOnTouchListener((arg0, arg1) -> {
             if (arg1.getAction()== MotionEvent.ACTION_DOWN){
                 txt.setVisibility(View.GONE);
+                quoteTextTyping.setVisibility(View.GONE);
+                quoteSenderTyping.setVisibility(View.GONE);
                 audioLayout.setVisibility(View.VISIBLE);
                 if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) {
                     new Thread(()->{
@@ -265,6 +290,10 @@ public class MessageListActivity extends AppCompatActivity implements ActivityCo
             }else if(arg1.getAction()== MotionEvent.ACTION_UP){
                 audioLayout.setVisibility(View.GONE);
                 txt.setVisibility(View.VISIBLE);
+                if(quoteTextTyping.getText().length()>0){
+                    quoteTextTyping.setVisibility(View.VISIBLE);
+                    quoteSenderTyping.setVisibility(View.VISIBLE);
+                }
                 Intent intent = new Intent(this, AudioRecordingService.class);
                 stopService(intent);
                 LocalBroadcastManager.getInstance(this).unregisterReceiver(timeBroadcastReceiver);
@@ -273,6 +302,10 @@ public class MessageListActivity extends AppCompatActivity implements ActivityCo
             }else if(arg1.getAction()== MotionEvent.ACTION_CANCEL){
                 audioLayout.setVisibility(View.GONE);
                 txt.setVisibility(View.VISIBLE);
+                if(quoteTextTyping.getText().length()>0){
+                    quoteTextTyping.setVisibility(View.VISIBLE);
+                    quoteSenderTyping.setVisibility(View.VISIBLE);
+                }
                 Intent intent = new Intent(this, AudioRecordingService.class);
                 stopService(intent);
                 LocalBroadcastManager.getInstance(this).unregisterReceiver(timeBroadcastReceiver);
@@ -285,7 +318,65 @@ public class MessageListActivity extends AppCompatActivity implements ActivityCo
 //            txt.setVisibility(View.GONE);
 //            audioLayout.setVisibility(View.VISIBLE);
 //        });
+        picsHelp.setOnClickListener(v -> {
+            mediaRecyclerView.setVisibility(View.GONE);
+            send.setVisibility(View.VISIBLE);
+            audio.setVisibility(View.VISIBLE);
+            file.setVisibility(View.VISIBLE);
+            txt.setVisibility(View.VISIBLE);
+            if(quoteTextTyping.getText().length()>0){
+                quoteTextTyping.setVisibility(View.VISIBLE);
+                quoteSenderTyping.setVisibility(View.VISIBLE);
+            }
+            picsHelp.setVisibility(View.GONE);
+        });
+        file.setOnClickListener(v -> {
+//            pickImage();
+            mediaRecyclerView.setVisibility(View.VISIBLE);
+            send.setVisibility(View.GONE);
+            audio.setVisibility(View.GONE);
+            file.setVisibility(View.GONE);
+            txt.setVisibility(View.GONE);
+            quoteTextTyping.setVisibility(View.GONE);
+            quoteSenderTyping.setVisibility(View.GONE);
+            picsHelp.setVisibility(View.VISIBLE);
+
+            new Thread(()->{
+//            ArrayList<Bitmap> pics = new ArrayList<>();
+                ArrayList<String> paths = new ArrayList<>();
+                Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+                String[] projection = { MediaStore.Images.ImageColumns.DATA ,MediaStore.Images.Media.DISPLAY_NAME};
+                Cursor cursor = this.getContentResolver().query(uri, projection, null, null, MediaStore.Images.Media._ID + " DESC");
+                try {
+                    if (cursor != null) {
+                        cursor.moveToFirst();
+                        do{
+//                        String name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME));
+                            String path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA));
+                            paths.add(path);
+//                        pics.add(BitmapFactory.decodeFile(path));
+                        }while(cursor.moveToNext());
+                        cursor.close();
+                    }
+                    // set up the RecyclerView
+                    new Handler(Looper.getMainLooper()).post(()->{
+
+                        LinearLayoutManager horizontalLayoutManager
+                                = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
+                        mediaRecyclerView.setLayoutManager(horizontalLayoutManager);
+                        MediaRecycleViewAdapter adapter = new MediaRecycleViewAdapter(this, paths);
+                        adapter.setClickListener(this::onItemClick);
+                        mediaRecyclerView.setAdapter(adapter);
+                    });
+
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }).start();
+        });
+
         updateUi();
+        ping();
 
         //run db message checker to delete any old messages then tell us to update ui
         checkMessages();
@@ -308,6 +399,88 @@ public class MessageListActivity extends AppCompatActivity implements ActivityCo
         });
     }
 
+    @Override
+    public void onItemClick(View view, int position) {
+        mediaRecyclerView.setVisibility(View.GONE);
+        send.setVisibility(View.VISIBLE);
+        audio.setVisibility(View.VISIBLE);
+        file.setVisibility(View.VISIBLE);
+        txt.setVisibility(View.VISIBLE);
+        if(quoteTextTyping.getText().length()>0){
+            quoteTextTyping.setVisibility(View.VISIBLE);
+            quoteSenderTyping.setVisibility(View.VISIBLE);
+        }
+        picsHelp.setVisibility(View.GONE);
+        Intent intent = new Intent(this,PictureViewerActivity.class);
+        intent.putExtra("address",address);
+        intent.putExtra("nickname",nickname);
+        if(mediaRecyclerView!=null && mediaRecyclerView.getAdapter()!=null){
+            String path = ((MediaRecycleViewAdapter)mediaRecyclerView.getAdapter()).mPaths.get(position);
+            intent.putExtra("path",path);
+            intent.putExtra("type","image");
+            startActivity(intent);
+        }
+    }
+
+//    public void pickImage() {
+//        try{
+//            if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
+//                Intent intent = new Intent(Intent.ACTION_PICK);
+//                intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,"image/*");
+//                intent.putExtra("crop", "true");
+//                intent.putExtra("scale", true);
+//                intent.putExtra("outputX", 256);
+//                intent.putExtra("outputY", 256);
+//                intent.putExtra("aspectX", 1);
+//                intent.putExtra("aspectY", 1);
+//                intent.putExtra("return-data", true);
+//                if (intent.resolveActivity(getPackageManager()) != null) {
+//                    startActivityForResult(intent, READ_STORAGE_REQUEST_CODE);
+//                }else{
+//                    Snackbar.make(file,"You do not have a gallery application installed",Snackbar.LENGTH_LONG).show();
+//                }
+//            }else{
+//                getReadStoragePerms();
+//            }
+//        }catch (Exception e){
+//            e.printStackTrace();
+//        }
+//    }
+
+    public void ping(){
+        new Thread(()->{
+            boolean b = TorClientSocks4.testAddress(((DxApplication) getApplication()), getIntent().getStringExtra("address"));
+            try {
+                Thread.sleep(500);
+            } catch (Exception ignored) {}
+            Handler h = new Handler(Looper.getMainLooper());
+            h.post(()->{
+                try{
+                    if(b){
+                        ((DxApplication)getApplication()).addToOnlineList(getIntent().getStringExtra("address"));
+                        status.setText(R.string.user_is_online);
+                    }else{
+                        ((DxApplication)getApplication()).onlineList.remove(getIntent().getStringExtra("address"));
+                        status.setText(R.string.user_is_offline);
+                    }
+                    status.setVisibility(View.VISIBLE);
+                    Animation slideUp = AnimationUtils.loadAnimation(this, R.anim.slide_up);
+                    status.startAnimation(slideUp);
+                }catch (Exception ignored) {}
+            });
+            try {
+                Thread.sleep(2000);
+            } catch (Exception ignored) {}
+            h.post(()->{
+                try {
+                    Animation slideDown = AnimationUtils.loadAnimation(this, R.anim.slide_down);
+                    status.startAnimation(slideDown);
+                    status.setVisibility(View.GONE);
+                }catch (Exception ignored) {}
+            });
+        }).start();
+    }
+
     public void checkMessages(){
         if(messageChecker!=null){
             return;
@@ -352,6 +525,7 @@ public class MessageListActivity extends AppCompatActivity implements ActivityCo
                     mMessageAdapter.notifyDataSetChanged();
                     if(!messageList.isEmpty()){
                         mMessageRecycler.scrollToPosition(messageList.size() - 1);
+                        ((DxApplication)getApplication()).clearMessageNotification();
                         if(!messageList.get(messageList.size()-1).getAddress().equals(((DxApplication)getApplication()).getHostname())){
                             String newName = messageList.get(messageList.size()-1).getSender();
                             Objects.requireNonNull(getSupportActionBar()).setTitle(newName);
@@ -448,11 +622,11 @@ public class MessageListActivity extends AppCompatActivity implements ActivityCo
         onSupportNavigateUp();
     }
 
-    @Override
-    protected void onDestroy() {
-        onSupportNavigateUp();
-        super.onDestroy();
-    }
+//    @Override
+//    protected void onDestroy() {
+//        onSupportNavigateUp();
+//        super.onDestroy();
+//    }
 
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
@@ -483,10 +657,14 @@ public class MessageListActivity extends AppCompatActivity implements ActivityCo
                 break;
             case R.id.action_settings:
                 //add the function to perform here
-                DxApplication app =  ((DxApplication) getApplication());
-                app.getEntity().getStore().deleteSession(new SignalProtocolAddress(getIntent().getStringExtra("address"),1));
+                try{
+                    DxApplication app =  ((DxApplication) getApplication());
+                    if(app.getEntity()==null){
+                        break;
+                    }
+                    app.getEntity().getStore().deleteSession(new SignalProtocolAddress(getIntent().getStringExtra("address"),1));
+                }catch (Exception ignored) {}
                 //((DxSignalKeyStore)app.getEntity().getStore()).removeIdentity(new SignalProtocolAddress(getIntent().getStringExtra("address"),1));
-                Log.e("RESET SESSION","RESET SESSION with : "+getIntent().getStringExtra("address"));
                 break;
             case R.id.action_verify_identity:
                 stopCheckingMessages();
@@ -524,9 +702,45 @@ public class MessageListActivity extends AppCompatActivity implements ActivityCo
                 // system settings in an effort to convince the user to change
                 // their decision.
 //            }
+        }//else if(requestCode == READ_STORAGE_REQUEST_CODE){
+        //}
+    }
+
+//    @Override
+//    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+//        super.onActivityResult(requestCode, resultCode, data);
+//        if (resultCode != RESULT_OK) {
+//            return;
+//        }
+//        if (requestCode == READ_STORAGE_REQUEST_CODE) {
+//            final Bundle extras = data.getExtras();
+//            if (extras != null) {
+//                //Get image
+//                Bitmap image = extras.getParcelable("data");
+//                ImageView img2send = findViewById(R.id.img_to_send);
+//                ImageView img2sendIcon = findViewById(R.id.img_to_send_icon);
+//                img2sendIcon.setVisibility(View.VISIBLE);
+//                img2send.setVisibility(View.VISIBLE);
+//                img2send.setImageBitmap(image);
+//            }
+//        }
+//    }
+
+    public void getReadStoragePerms(){
+        if (shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE)) {
+            new AlertDialog.Builder(getApplicationContext(),R.style.AppAlertDialog)
+                    .setTitle(R.string.read_storage_perm_ask_title)
+                    .setMessage(R.string.why_need_read_storage)
+                    .setIcon(android.R.drawable.ic_dialog_alert)
+                    .setPositiveButton(R.string.ask_for_mic_btn, (dialog, which) -> requestPermissions(
+                            new String[] { Manifest.permission.READ_EXTERNAL_STORAGE },
+                            READ_STORAGE_REQUEST_CODE))
+                    .setNegativeButton(R.string.no_thanks, (dialog, which) -> {
+
+                    });
+        } else {
+            requestPermissions(new String[] { Manifest.permission.READ_EXTERNAL_STORAGE }, REQUEST_CODE);
         }
-        // Other 'case' lines to check for other
-        // permissions this app might request.
     }
 
     public void getMicrophonePerms(){

+ 30 - 24
app/src/main/java/com/dx/anonymousmessenger/MyRecyclerViewAdapter.java

@@ -3,7 +3,6 @@ package com.dx.anonymousmessenger;
 import android.app.AlertDialog;
 import android.content.Intent;
 import android.graphics.Typeface;
-import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -13,25 +12,27 @@ import android.widget.TextView;
 
 import androidx.annotation.NonNull;
 import androidx.core.content.ContextCompat;
-import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.Adapter;
 
 import com.dx.anonymousmessenger.db.DbHelper;
 import com.dx.anonymousmessenger.util.Utils;
 
 import java.util.List;
 
-public class MyRecyclerViewAdapter extends RecyclerView.Adapter {
+import static androidx.recyclerview.widget.RecyclerView.ViewHolder;
+
+public class MyRecyclerViewAdapter extends Adapter {
     private static final int VIEW_TYPE_READ = 1;
     private static final int VIEW_TYPE_UNREAD = 2;
     private List<String[]> mData;
-    private LayoutInflater mInflater;
+//    private LayoutInflater mInflater;
     private View.OnClickListener mClickListener;
     private DxApplication app;
     private AppFragment appFragment;
 
     // data is passed into the constructor
     MyRecyclerViewAdapter(DxApplication app, List<String[]> data, AppFragment appFragment) {
-        this.mInflater = LayoutInflater.from(app);
+//        this.mInflater = LayoutInflater.from(app);
         this.app = app;
         this.mData = data;
         this.appFragment = appFragment;
@@ -51,7 +52,7 @@ public class MyRecyclerViewAdapter extends RecyclerView.Adapter {
     // inflates the row layout from xml when needed
     @NonNull
     @Override
-    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
         View view;
         if (viewType == VIEW_TYPE_READ) {
             view = LayoutInflater.from(parent.getContext())
@@ -63,14 +64,17 @@ public class MyRecyclerViewAdapter extends RecyclerView.Adapter {
                     .inflate(R.layout.my_text_view, parent, false);
             view.setOnClickListener(mClickListener);
             return new MyRecyclerViewAdapter.UnreadContactHolder(view);
+        } else{
+            view = LayoutInflater.from(parent.getContext())
+                    .inflate(R.layout.my_text_view, parent, false);
+            view.setOnClickListener(mClickListener);
+            return new MyRecyclerViewAdapter.ReadContactHolder(view);
         }
-        Log.e("finding contact type","something went wrong");
-        return null;
     }
 
     // binds the data to the TextView in each row
     @Override
-    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
         String[] contact = mData.get(position);
         long createdAt = 0;
         try {
@@ -80,7 +84,7 @@ public class MyRecyclerViewAdapter extends RecyclerView.Adapter {
         }catch (Exception ignored){}
         switch (holder.getItemViewType()) {
             case VIEW_TYPE_READ://String msg, String send_to, long createdAt, boolean received
-                ((MyRecyclerViewAdapter.ReadContactHolder) holder).bind(contact[0].equals("")?contact[1]:contact[0],contact[3],contact[4],createdAt,contact[6].equals("true"));
+                ((MyRecyclerViewAdapter.ReadContactHolder) holder).bind(contact[0].equals("")?contact[1]:contact[0],contact[3],contact[4],createdAt,contact[6].equals("true"),contact[1]);
                 holder.itemView.setOnClickListener(v -> {
                     appFragment.stopCheckingMessages();
                     int position1 = holder.getAdapterPosition();
@@ -92,7 +96,7 @@ public class MyRecyclerViewAdapter extends RecyclerView.Adapter {
                 holder.itemView.setOnLongClickListener(new ListItemOnClickListener(holder.itemView,mData.get(holder.getAdapterPosition())[1]));
                 break;
             case VIEW_TYPE_UNREAD:
-                ((MyRecyclerViewAdapter.UnreadContactHolder) holder).bind(contact[0].equals("")?contact[1]:contact[0],contact[3],contact[4],createdAt,contact[6].equals("true"));
+                ((MyRecyclerViewAdapter.UnreadContactHolder) holder).bind(contact[0].equals("")?contact[1]:contact[0],contact[3],contact[4],createdAt,contact[6].equals("true"),contact[1]);
                 holder.itemView.setOnClickListener(v -> {
                     appFragment.stopCheckingMessages();
                     int position12 = holder.getAdapterPosition();
@@ -161,34 +165,32 @@ public class MyRecyclerViewAdapter extends RecyclerView.Adapter {
             popup.inflate(R.menu.contact_menu);
 
             popup.setOnMenuItemClickListener(item -> {
-                switch (item.getItemId()) {
-                    case R.id.delete_contact:
-                        new AlertDialog.Builder(itemView.getContext(),R.style.AppAlertDialog)
+                if (item.getItemId() == R.id.delete_contact) {
+                    new AlertDialog.Builder(itemView.getContext(), R.style.AppAlertDialog)
                             .setTitle("Delete this contact?")
                             .setMessage("this can't be undone and will also delete this contact's session and conversation")
                             .setIcon(android.R.drawable.ic_dialog_alert)
                             .setPositiveButton(android.R.string.yes, (dialog, whichButton) -> {
                                 DbHelper.deleteContact(address, app);
-                                DbHelper.clearConversation(address,app);
+                                DbHelper.clearConversation(address, app);
                                 Intent intent = new Intent("your_action");
                                 itemView.getContext().sendBroadcast(intent);
                             })
-                            .setNegativeButton(android.R.string.no, (dialog, whichButton)->{
+                            .setNegativeButton(android.R.string.no, (dialog, whichButton) -> {
 
                             }).show();
-                        return true;
-                    default:
-                        return false;
+                    return true;
                 }
+                return false;
             });
             popup.show();
             return true;
         }
     }
 
-    private class ReadContactHolder extends RecyclerView.ViewHolder {
+    private class ReadContactHolder extends ViewHolder {
         TextView contactName,msgText,timeText;
-        ImageView imageView,seen;
+        ImageView imageView,seen,contactOnline;
 
         ReadContactHolder(View itemView) {
             super(itemView);
@@ -197,15 +199,19 @@ public class MyRecyclerViewAdapter extends RecyclerView.Adapter {
             timeText = itemView.findViewById(R.id.time_text);
             msgText = itemView.findViewById(R.id.message_text);
             seen = itemView.findViewById(R.id.seen);
+            contactOnline = itemView.findViewById(R.id.contact_online);
         }
 
-        void bind(String title, String msg, String send_to, long createdAt, boolean received) {
+        void bind(String title, String msg, String send_to, long createdAt, boolean received, String address) {
             contactName.setText(title);
             contactName.setTypeface(null, Typeface.NORMAL);
             imageView.setVisibility(View.INVISIBLE);
             msgText.setText(msg);
             timeText.setText(createdAt>0?Utils.formatDateTime(createdAt):"");
             seen.setVisibility(send_to.equals(app.getHostname())?View.GONE:received?View.VISIBLE:View.GONE);
+            if(app.onlineList.contains(address)){
+                contactOnline.setVisibility(View.VISIBLE);
+            }
         }
     }
 
@@ -215,9 +221,9 @@ public class MyRecyclerViewAdapter extends RecyclerView.Adapter {
             super(itemView);
         }
 
-        void bind(String title, String msg, String send_to, long createdAt, boolean received) {
+        void bind(String title, String msg, String send_to, long createdAt, boolean received, String address) {
 //            imageView.setVisibility(View.VISIBLE);
-            super.bind(title,msg,send_to,createdAt,received);
+            super.bind(title,msg,send_to,createdAt,received,address);
             itemView.setBackground(ContextCompat.getDrawable(app,R.drawable.rounded_rectangle_steel));
             imageView.setVisibility(View.VISIBLE);
             seen.setVisibility(View.INVISIBLE);

+ 4 - 4
app/src/main/java/com/dx/anonymousmessenger/MyService.java

@@ -76,11 +76,11 @@ public class MyService extends Service {
         // Wait for shutdown to complete, then exit
         new Thread(() -> {
             try {
-                if(csm!=null){
-                    csm.disable();
-                }
+                csm.disable();
                 if(app!=null){
-                    app.getAndroidTorRelay().shutDown();
+                    if(app.getAndroidTorRelay()!=null){
+                        app.getAndroidTorRelay().shutDown();
+                    }
                     app.emptyVars();
                     app.shutdown(0);
                 }

+ 2 - 0
app/src/main/java/com/dx/anonymousmessenger/PasswordEntryFragment.java

@@ -112,6 +112,7 @@ public class PasswordEntryFragment extends Fragment {
 //                            txtPassword.setError("An unexpected error happened");
 //                        });
                         Intent intent = new Intent(getActivity(), AppActivity.class);
+                        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
                         startActivity(intent);
                         getActivity().finish();
                     }
@@ -142,6 +143,7 @@ public class PasswordEntryFragment extends Fragment {
 //                            txtPassword.setError("An unexpected error happened");
 //                        });
                         Intent intent = new Intent(getActivity(), AppActivity.class);
+                        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
                         startActivity(intent);
                         try{
                             Objects.requireNonNull(getActivity()).finish();

+ 141 - 0
app/src/main/java/com/dx/anonymousmessenger/PictureViewerActivity.java

@@ -0,0 +1,141 @@
+package com.dx.anonymousmessenger;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.ImageView;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.dx.anonymousmessenger.file.FileHelper;
+import com.dx.anonymousmessenger.messages.MessageSender;
+import com.dx.anonymousmessenger.messages.QuotedUserMessage;
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+import com.google.android.material.textfield.TextInputEditText;
+import com.google.android.material.textfield.TextInputLayout;
+
+import java.io.ByteArrayOutputStream;
+import java.security.NoSuchAlgorithmException;
+import java.util.Date;
+import java.util.Objects;
+
+public class PictureViewerActivity extends AppCompatActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        try{
+            getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
+//            Objects.requireNonNull(getSupportActionBar()).hide();
+        }catch (Exception ignored){}
+        setContentView(R.layout.activity_picture_viewer);
+
+        try{
+            if(getSupportActionBar()!=null){
+                getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+                if(getIntent().getStringExtra("address")==null || Objects.equals(getIntent().getStringExtra("address"), "")){
+                    getSupportActionBar().setTitle(R.string.picure_view);
+                }else{
+                    getSupportActionBar().setTitle(Objects.equals(getIntent().getStringExtra("nickname"), "") ?getIntent().getStringExtra("address"):getIntent().getStringExtra("nickname"));
+                }
+            }
+        }catch (Exception ignored){}
+
+        if(getIntent().getBooleanExtra("appData",false)){
+            new Thread(()->{
+                Bitmap image;
+                try{
+                    byte[] file = FileHelper.getFile(getIntent().getStringExtra("path"),(DxApplication) getApplication());
+                    if(file==null){
+                        return;
+                    }
+                    image = BitmapFactory.decodeByteArray(file, 0, file.length);
+                }catch (Exception e){
+                    e.printStackTrace();
+                    return;
+                }
+                if(image==null){
+                    return;
+                }
+                new Handler(Looper.getMainLooper()).post(()->{
+                    ImageView img = findViewById(R.id.img_to_send);
+                    img.setImageBitmap(image);
+                    TextInputLayout textInputLayout = findViewById(R.id.txt_layout_caption);
+                    textInputLayout.setVisibility(View.VISIBLE);
+                    TextInputEditText msg = findViewById(R.id.txt_caption);
+                    msg.setText(getIntent().getStringExtra("message"));
+                    msg.setEnabled(false);
+                });
+            }).start();
+            return;
+        }
+
+        new Thread(()->{
+            Bitmap image;
+            try{
+                image = BitmapFactory.decodeFile(getIntent().getStringExtra("path"));
+            }catch (Exception e){
+                e.printStackTrace();
+                return;
+            }
+            if(image==null){
+                return;
+            }
+            new Handler(Looper.getMainLooper()).post(()->{
+                ImageView img = findViewById(R.id.img_to_send);
+                img.setImageBitmap(image);
+            });
+        }).start();
+
+        TextInputLayout textInputLayout = findViewById(R.id.txt_layout_caption);
+        textInputLayout.setVisibility(View.VISIBLE);
+        TextInputEditText msg = findViewById(R.id.txt_caption);
+        FloatingActionButton fabSendMedia = findViewById(R.id.btn_send_media);
+        fabSendMedia.setVisibility(View.VISIBLE);
+        fabSendMedia.setOnClickListener(v -> {
+            msg.setEnabled(false);
+            new Thread(()->{
+                DxApplication app = (DxApplication) getApplication();
+                //get time ready
+                long time = new Date().getTime();
+                //save bytes encrypted into a file and get path
+                String filename = String.valueOf(time);
+                String path = null;
+                try {
+                    Bitmap image = BitmapFactory.decodeFile(getIntent().getStringExtra("path"));
+                    if(image==null){
+                        return;
+                    }
+                    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+                    image.compress(Bitmap.CompressFormat.JPEG, 100, stream);
+                    byte[] byteArray = stream.toByteArray();
+                    path = FileHelper.saveFile(byteArray,app,filename);
+                } catch (NoSuchAlgorithmException e) {
+                    e.printStackTrace();
+                }
+                if(path==null){
+                    return;
+                }
+                String txtMsg = "";
+                if(msg.getText()!=null){
+                    txtMsg = msg.getText().toString();
+                }
+                //save metadata in encrypted database with reference to encrypted file
+                QuotedUserMessage qum = new QuotedUserMessage("","",app.getHostname(),txtMsg,app.getAccount().getNickname(),time,false,getIntent().getStringExtra("address"),false,filename,path,"image");
+                //send message and get received status
+                MessageSender.sendMediaMessage(qum,app,getIntent().getStringExtra("address"));
+            }).start();
+            finish();
+        });
+    }
+
+    @Override
+    public boolean onSupportNavigateUp() {
+        finish();
+        return super.onSupportNavigateUp();
+    }
+}

+ 52 - 42
app/src/main/java/com/dx/anonymousmessenger/SetupInProcess.java

@@ -18,55 +18,31 @@ public class SetupInProcess extends AppCompatActivity implements ComponentCallba
 
     private BroadcastReceiver mMyBroadcastReceiver;
     private TextView statusText;
-    private Button gotoContact,restartTorButton;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
         setContentView(R.layout.activity_setup_in_process);
-        new Thread(()->{
-            if(((DxApplication) getApplication()).isServerReady()){
-                Intent intent = new Intent(this, AppActivity.class);
-                startActivity(intent);
-                finish();
-            }
-        }).start();
+
         statusText = findViewById(R.id.status_text);
-        gotoContact = findViewById(R.id.btn_goto_contacts);
-        restartTorButton = findViewById(R.id.btn_restart_tor);
+        Button gotoContact = findViewById(R.id.btn_goto_contacts);
+        Button restartTorButton = findViewById(R.id.btn_restart_tor);
+        restartTorButton.setVisibility(View.VISIBLE);
+        restartTorButton.setOnClickListener(v -> ((DxApplication)getApplication()).restartTor());
         if(!getIntent().getBooleanExtra("first_time",true)){
             gotoContact.setVisibility(View.VISIBLE);
             gotoContact.setOnClickListener(v -> {
                 ((DxApplication)getApplication()).setExitingHoldup(true);
                 Intent intent = new Intent(this, AppActivity.class);
+                intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
                 intent.putExtra("force_app",true);
                 startActivity(intent);
                 finish();
             });
-            restartTorButton.setVisibility(View.VISIBLE);
-            restartTorButton.setOnClickListener(v -> ((DxApplication)getApplication()).restartTor());
         }
 
-        if(mMyBroadcastReceiver!=null){
-            return;
-        }
-        mMyBroadcastReceiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent)
-            {
-                new Thread(()->{
-                    try{
-                        updateUi(intent.getStringExtra("tor_status"));
-                    }catch (Exception ignored) {}
-                }).start();
-            }
-        };
-        try {
-            LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(mMyBroadcastReceiver,new IntentFilter("tor_status"));
-        } catch (Exception e){
-            e.printStackTrace();
-        }
+
     }
 
     @Override
@@ -74,14 +50,47 @@ public class SetupInProcess extends AppCompatActivity implements ComponentCallba
 
     }
 
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if(mMyBroadcastReceiver==null){
+            return;
+        }
+        LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(mMyBroadcastReceiver);
+        mMyBroadcastReceiver = null;
+    }
+
     @Override
     public void onResume() {
         super.onResume();
-        if(((DxApplication) getApplication()).isServerReady()){
-            Intent intent = new Intent(this, AppActivity.class);
-            startActivity(intent);
-            finish();
-        }
+        new Thread(()->{
+            if(((DxApplication) getApplication()).isServerReady()){
+                Intent intent = new Intent(this, AppActivity.class);
+                intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+                startActivity(intent);
+                finish();
+                return;
+            }
+            if(mMyBroadcastReceiver!=null){
+                return;
+            }
+            mMyBroadcastReceiver = new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent)
+                {
+                    new Thread(()->{
+                        try{
+                            updateUi(intent.getStringExtra("tor_status"));
+                        }catch (Exception ignored) {}
+                    }).start();
+                }
+            };
+            try {
+                LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(mMyBroadcastReceiver,new IntentFilter("tor_status"));
+            } catch (Exception e){
+                e.printStackTrace();
+            }
+        }).start();
     }
 
     public void updateUi(String torStatus){
@@ -97,25 +106,26 @@ public class SetupInProcess extends AppCompatActivity implements ComponentCallba
         if(torStatus.contains("DisableNetwork is set")){
             torStatus = getString(R.string.waiting_for_tor);
         }
-        if(torStatus.contains("Bootstrapped 100%")){
+        if(torStatus.contains("Bootstrapped 100%") || torStatus.contains("ALL GOOD")){
             ((DxApplication)getApplication()).setExitingHoldup(true);
+            LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(mMyBroadcastReceiver);
+            if(getIntent().getBooleanExtra("first_time",true)){
+                ((DxApplication) this.getApplication()).sendNotification("Ready to chat securely!",
+                        "You got all you need to chat securely with your friends!",false);
+            }
             String finalTorStatus = torStatus;
             runOnUiThread(()->{
                 try {
                     statusText.setText(finalTorStatus);
                 }catch (Exception ignored) {}
             });
-            LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(mMyBroadcastReceiver);
-            if(getIntent().getBooleanExtra("first_time",true)){
-                ((DxApplication) this.getApplication()).sendNotification("Ready to chat securely!",
-                        "You got all you need to chat securely with your friends!",false);
-            }
             try {
                 Thread.sleep(500);
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
             Intent intent = new Intent(this, AppActivity.class);
+            intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
             startActivity(intent);
             finish();
         }else{

+ 1 - 1
app/src/main/java/com/dx/anonymousmessenger/SetupUsernameFragment.java

@@ -41,7 +41,7 @@ public class SetupUsernameFragment extends Fragment {
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
                              Bundle savedInstanceState) {
         final View rootView = inflater.inflate(R.layout.fragment_setup_username, container, false);
-        final TextInputEditText txtNickname = rootView.findViewById(R.id.txt_nickname);
+        final TextInputEditText txtNickname = rootView.findViewById(R.id.txt_caption);
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
             txtNickname.setImeOptions(IME_FLAG_NO_PERSONALIZED_LEARNING);
         }

+ 22 - 5
app/src/main/java/com/dx/anonymousmessenger/db/DbHelper.java

@@ -3,6 +3,7 @@ package com.dx.anonymousmessenger.db;
 import android.util.Log;
 
 import com.dx.anonymousmessenger.DxApplication;
+import com.dx.anonymousmessenger.R;
 import com.dx.anonymousmessenger.crypto.DxSignalKeyStore;
 import com.dx.anonymousmessenger.file.FileHelper;
 import com.dx.anonymousmessenger.messages.QuotedUserMessage;
@@ -55,11 +56,11 @@ public class DbHelper {
                 database.execSQL(DbHelper.getMessageTableSqlCreate());
                 //delete old messages
                 database.execSQL("DELETE FROM message WHERE conversation=? AND pinned=? AND created_at<?", new Object[]{address,0,new Date().getTime() - app.getTime2delete()});
-                Cursor cr2 = database.rawQuery("SELECT * FROM message WHERE conversation=? ORDER BY created_at DESC LIMIT 1;",new Object[]{address});
+                Cursor cr2 = database.rawQuery("SELECT * FROM message WHERE conversation=? ORDER BY rowid DESC LIMIT 1;",new Object[]{address});
 
                 if (cr2.moveToFirst()) {
                     QuotedUserMessage message = new QuotedUserMessage(cr2.getString(9),cr2.getString(8),cr2.getString(0),cr2.getString(3),cr2.getString(4),
-                            cr2.getLong(5),cr2.getInt(7)>0,cr2.getString(1),cr2.getInt(10)>0);
+                            cr2.getLong(5),cr2.getInt(7)>0,cr2.getString(1),cr2.getInt(10)>0,cr2.getString(11),cr2.getString(12),cr2.getString(13));
                     if ((new Date().getTime() - message.getCreatedAt()) >= app.getTime2delete()) {
                         if(!message.isPinned()) {
                             deleteMessage(message, app);
@@ -70,8 +71,18 @@ public class DbHelper {
                     }
                     cr2.close();
 
-                    contacts.add(new String[]{cr.getString(0), address, cr.getInt(2) > 0 ? "unread" : "read",message.getMessage(),message.getTo(), String.valueOf(message.getCreatedAt()),message.isReceived()?"true":"false"});
+                    String msg;
+                    if(message.getType()!=null && message.getType().equals("audio")){
+                        msg = app.getString(R.string.audio_message);
+                    }else if(message.getType()!=null && message.getType().equals("image")){
+                        msg = app.getString(R.string.media_message);
+                    }else{
+                        msg = message.getMessage();
+                    }
+
+                    contacts.add(new String[]{cr.getString(0), address, cr.getInt(2) > 0 ? "unread" : "read",msg,message.getTo(), String.valueOf(message.getCreatedAt()),message.isReceived()?"true":"false"});
                 }else{
+                    cr2.close();
                     contacts.add(new String[]{cr.getString(0), address, cr.getInt(2) > 0 ? "unread" : "read","","","",""});
                 }
             } while (cr.moveToNext());
@@ -319,6 +330,10 @@ public class DbHelper {
         return new Object[]{from,to,number,msg,sender,createdAt,conversation,received,quote,quoteSender,false,null,null,null};
     }
 
+    public static Object[] getFullMessageSqlValues(String from, String to, String number, String msg, String sender, long createdAt, String conversation, boolean received, String quote, String quoteSender, String filename, String path, String type){
+        return new Object[]{from,to,number,msg,sender,createdAt,conversation,received,quote,quoteSender,false,filename,path,type};
+    }
+
     public static Object[] getMediaMessageSqlValues(String from, String to, String number, String sender, long createdAt, String conversation, boolean received, String filename, String path, String type){
         return new Object[]{from,to,number,"",sender,createdAt,conversation,received,"","",false,filename,path,type};
     }
@@ -371,7 +386,7 @@ public class DbHelper {
                 QuotedUserMessage message = new QuotedUserMessage(cr.getString(9),cr.getString(8),cr.getString(0),cr.getString(3),cr.getString(4),
                         cr.getLong(5),cr.getInt(7)>0,cr.getString(1),cr.getInt(10)>0,cr.getString(11),cr.getString(12),cr.getString(13));
 
-                if ((new Date().getTime() - message.getCreatedAt()) < (3*60*1000)) {
+                if ((new Date().getTime() - message.getCreatedAt()) < (2*60*1000)) {
                     continue;
                 }
 
@@ -446,7 +461,8 @@ public class DbHelper {
         if(msg.getType()==null || msg.getType().equals("")){
             database.execSQL(DbHelper.getMessageSqlInsert(),DbHelper.getMessageSqlValues(msg.getAddress(),msg.getTo(),"1",msg.getMessage(),msg.getSender(),msg.getCreatedAt(),conversation,received,msg.getQuotedMessage(),msg.getQuoteSender()));
         }else{
-            database.execSQL(DbHelper.getMessageSqlInsert(),DbHelper.getMediaMessageSqlValues(msg.getAddress(),msg.getTo(),"1",msg.getSender(),msg.getCreatedAt(),conversation,received,msg.getFilename(),msg.getPath(),msg.getType()));
+            database.execSQL(DbHelper.getMessageSqlInsert(),DbHelper.getFullMessageSqlValues(msg.getAddress(),msg.getTo(),"1",msg.getMessage(),msg.getSender(),msg.getCreatedAt(),conversation,received,msg.getQuotedMessage(),msg.getQuoteSender(),msg.getFilename(),msg.getPath(),msg.getType()));
+//            database.execSQL(DbHelper.getMessageSqlInsert(),DbHelper.getMediaMessageSqlValues(msg.getAddress(),msg.getTo(),"1",msg.getSender(),msg.getCreatedAt(),conversation,received,msg.getFilename(),msg.getPath(),msg.getType()));
         }
 
         return true;
@@ -513,6 +529,7 @@ public class DbHelper {
 
     public static void setMessageReceived(QuotedUserMessage msg, DxApplication app, String conversation, boolean received){
         SQLiteDatabase database = app.getDb();
+        System.out.println("message received is "+msg.getMessage());
         database.execSQL("UPDATE message SET received=? WHERE send_to=? AND created_at=? AND send_from=? AND msg=? AND conversation=?", new Object[]{received,msg.getTo(),msg.getCreatedAt(),msg.getAddress(),msg.getMessage(),conversation});
     }
 

+ 83 - 0
app/src/main/java/com/dx/anonymousmessenger/media/MediaRecycleViewAdapter.java

@@ -0,0 +1,83 @@
+package com.dx.anonymousmessenger.media;
+
+import android.content.Context;
+import android.graphics.BitmapFactory;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.dx.anonymousmessenger.R;
+
+import java.util.List;
+
+public class MediaRecycleViewAdapter extends RecyclerView.Adapter<MediaRecycleViewAdapter.ViewHolder> {
+
+    public List<String> mPaths;
+    private LayoutInflater mInflater;
+    private ItemClickListener mClickListener;
+
+    // data is passed into the constructor
+    public MediaRecycleViewAdapter(Context context, List<String> paths) {
+        this.mInflater = LayoutInflater.from(context);
+        this.mPaths = paths;
+    }
+
+    // inflates the row layout from xml when needed
+    @Override
+    @NonNull
+    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        View view = mInflater.inflate(R.layout.media_rv_item, parent, false);
+        return new ViewHolder(view);
+    }
+
+    // binds the data to the view and textview in each row
+    @Override
+    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+//        Bitmap image = mImages.get(position);
+        String path = mPaths.get(position);
+        BitmapFactory.Options options = new BitmapFactory.Options();
+        options.inSampleSize = 8;
+        holder.myView.setImageBitmap(BitmapFactory.decodeFile(path,options));
+    }
+
+    // total number of rows
+    @Override
+    public int getItemCount() {
+        return mPaths.size();
+    }
+
+    // stores and recycles views as they are scrolled off screen
+    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
+        ImageView myView;
+
+        ViewHolder(View itemView) {
+            super(itemView);
+            myView = itemView.findViewById(R.id.view_image);
+            itemView.setOnClickListener(this);
+        }
+
+        @Override
+        public void onClick(View view) {
+            if (mClickListener != null) mClickListener.onItemClick(view, getAdapterPosition());
+        }
+    }
+
+    // convenience method for getting data at click position
+    public String getItem(int id) {
+        return mPaths.get(id);
+    }
+
+    // allows clicks events to be caught
+    public void setClickListener(ItemClickListener itemClickListener) {
+        this.mClickListener = itemClickListener;
+    }
+
+    // parent activity will implement this method to respond to click events
+    public interface ItemClickListener {
+        void onItemClick(View view, int position);
+    }
+}

+ 83 - 0
app/src/main/java/com/dx/anonymousmessenger/messages/MessageListAdapter.java

@@ -3,6 +3,9 @@ package com.dx.anonymousmessenger.messages;
 import android.content.ClipData;
 import android.content.ClipboardManager;
 import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Log;
@@ -20,7 +23,9 @@ import androidx.recyclerview.widget.RecyclerView;
 
 import com.dx.anonymousmessenger.DxApplication;
 import com.dx.anonymousmessenger.MessageListActivity;
+import com.dx.anonymousmessenger.PictureViewerActivity;
 import com.dx.anonymousmessenger.R;
+import com.dx.anonymousmessenger.file.FileHelper;
 import com.dx.anonymousmessenger.media.AudioPlayer;
 import com.dx.anonymousmessenger.util.CallBack;
 import com.dx.anonymousmessenger.util.Utils;
@@ -41,6 +46,9 @@ public class MessageListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
     private static final int VIEW_TYPE_AUDIO_MESSAGE_SENT = 7;
     private static final int VIEW_TYPE_AUDIO_MESSAGE_SENT_OK = 8;
     private static final int VIEW_TYPE_AUDIO_MESSAGE_RECEIVED = 9;
+    private static final int VIEW_TYPE_MEDIA_MESSAGE_SENT = 10;
+    private static final int VIEW_TYPE_MEDIA_MESSAGE_SENT_OK = 11;
+    private static final int VIEW_TYPE_MEDIA_MESSAGE_RECEIVED = 12;
 
     private Context mContext;
     private RecyclerView mMessageRecycler;
@@ -74,6 +82,8 @@ public class MessageListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
             if(message.isReceived()){
                 if(message.getType()!=null && message.getType().equals("audio")){
                     return VIEW_TYPE_AUDIO_MESSAGE_SENT_OK;
+                }else if(message.getType()!=null && message.getType().equals("image")){
+                    return VIEW_TYPE_MEDIA_MESSAGE_SENT_OK;
                 }else if(message.getQuotedMessage()!= null && message.getQuotedMessage().equals("")){
                     return VIEW_TYPE_MESSAGE_SENT_OK;
                 }else if(message.getQuotedMessage()!=null){
@@ -83,6 +93,8 @@ public class MessageListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
             }else{
                 if(message.getType()!=null && message.getType().equals("audio")){
                     return VIEW_TYPE_AUDIO_MESSAGE_SENT;
+                }else if(message.getType()!=null && message.getType().equals("image")){
+                    return VIEW_TYPE_MEDIA_MESSAGE_SENT;
                 }else if(message.getQuotedMessage()!= null && message.getQuotedMessage().equals("")){
                     return VIEW_TYPE_MESSAGE_SENT;
                 }else if(message.getQuotedMessage()!=null){
@@ -94,6 +106,8 @@ public class MessageListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
             // If some other user sent the message
             if(message.getType()!=null && message.getType().equals("audio")){
                     return VIEW_TYPE_AUDIO_MESSAGE_RECEIVED;
+            }else if(message.getType()!=null && message.getType().equals("image")){
+                return VIEW_TYPE_MEDIA_MESSAGE_RECEIVED;
             }else if(message.getQuotedMessage()!= null && message.getQuotedMessage().equals("")){
                 return VIEW_TYPE_MESSAGE_RECEIVED;
             }else if(message.getQuotedMessage()!=null){
@@ -145,6 +159,18 @@ public class MessageListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
             view = LayoutInflater.from(parent.getContext())
                     .inflate(R.layout.item_audio_message_received, parent, false);
             return new AudioMessageHolder(view);
+        }else if(viewType == VIEW_TYPE_MEDIA_MESSAGE_RECEIVED){
+            view = LayoutInflater.from(parent.getContext())
+                    .inflate(R.layout.item_media_message_received, parent, false);
+            return new MediaMessageHolder(view);
+        }else if(viewType == VIEW_TYPE_MEDIA_MESSAGE_SENT){
+            view = LayoutInflater.from(parent.getContext())
+                    .inflate(R.layout.item_media_message_sent, parent, false);
+            return new MediaMessageHolder(view);
+        }else if(viewType == VIEW_TYPE_MEDIA_MESSAGE_SENT_OK){
+            view = LayoutInflater.from(parent.getContext())
+                    .inflate(R.layout.item_media_message_sent_ok, parent, false);
+            return new MediaMessageHolder(view);
         }
         Log.e("finding message type","something went wrong");
         view = LayoutInflater.from(parent.getContext())
@@ -180,6 +206,10 @@ public class MessageListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
             case VIEW_TYPE_AUDIO_MESSAGE_RECEIVED:
                 ((AudioMessageHolder) holder).bind(message);
                 break;
+            case VIEW_TYPE_MEDIA_MESSAGE_RECEIVED:
+            case VIEW_TYPE_MEDIA_MESSAGE_SENT:
+            case VIEW_TYPE_MEDIA_MESSAGE_SENT_OK:
+                ((MediaMessageHolder) holder).bind(message);
         }
     }
 
@@ -349,6 +379,59 @@ public class MessageListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
         }
     }
 
+    private class MediaMessageHolder extends RecyclerView.ViewHolder {
+        TextView timeText,nameText,messageText;
+        ImageView imageHolder;
+
+        MediaMessageHolder(View itemView){
+            super(itemView);
+            timeText = itemView.findViewById(R.id.text_message_time);
+            nameText = itemView.findViewById(R.id.text_message_name);
+            messageText = itemView.findViewById(R.id.text_message_body);
+            imageHolder = itemView.findViewById(R.id.img_holder);
+        }
+
+        void bind(QuotedUserMessage message) {
+            if(nameText!=null){
+                nameText.setText(message.getSender());
+            }
+            timeText.setText(Utils.formatDateTime(message.getCreatedAt()));
+            if(message.getMessage()!=null && !message.getMessage().equals("")){
+                messageText.setVisibility(View.VISIBLE);
+                messageText.setText(message.getMessage());
+            }else{
+                messageText.setVisibility(View.GONE);
+            }
+            new Thread(()->{
+                byte[] img_bin = FileHelper.getFile(message.getPath(), app);
+                if(img_bin == null){
+                    return;
+                }
+//                YuvImage yuvImage = new YuvImage(img_bin, ImageFormat.NV21, 100, 100, null);
+//                ByteArrayOutputStream baos = new ByteArrayOutputStream();
+//                yuvImage.compressToJpeg(new Rect(0, 0, 100, 100), 80, baos);
+//                byte[] jdata = baos.toByteArray();
+                Bitmap bitmap = BitmapFactory.decodeByteArray(img_bin, 0, img_bin.length);
+                if(bitmap==null){
+                    return;
+                }
+                new Handler(Looper.getMainLooper()).post(()->{
+                    if(imageHolder==null){
+                        return;
+                    }
+                    imageHolder.setImageBitmap(bitmap);
+                    imageHolder.setOnClickListener(v -> {
+                        Intent intent = new Intent(app, PictureViewerActivity.class);
+                        intent.putExtra("appData",true);
+                        intent.putExtra("path",message.getPath());
+                        intent.putExtra("message",message.getMessage());
+                        app.startActivity(intent);
+                    });
+                });
+            }).start();
+        }
+    }
+
     private class AudioMessageHolder extends RecyclerView.ViewHolder {
         TextView timeText,nameText;
         ImageView playPauseButton;

+ 8 - 6
app/src/main/java/com/dx/anonymousmessenger/messages/MessageSender.java

@@ -6,6 +6,7 @@ import android.util.Log;
 import androidx.localbroadcastmanager.content.LocalBroadcastManager;
 
 import com.dx.anonymousmessenger.DxApplication;
+import com.dx.anonymousmessenger.R;
 import com.dx.anonymousmessenger.crypto.AddressedEncryptedMessage;
 import com.dx.anonymousmessenger.crypto.AddressedKeyExchangeMessage;
 import com.dx.anonymousmessenger.crypto.KeyExchangeMessage;
@@ -42,7 +43,7 @@ public class MessageSender {
                 received = new TorClientSocks4().Init(to,app,aem.toJson().toString());
             }catch (Exception e){
                 Intent gcm_rec = new Intent("your_action");
-                gcm_rec.putExtra("error","Couldn't encrypt message");
+                gcm_rec.putExtra("error",app.getString(R.string.cant_encrypt_message));
                 LocalBroadcastManager.getInstance(app.getApplicationContext()).sendBroadcast(gcm_rec);
                 e.printStackTrace();
                 Log.e("MESSAGE SENDER","SENDING MESSAGE FAILED WITH ENCRYPTION");
@@ -69,7 +70,7 @@ public class MessageSender {
                 received = new TorClientSocks4().Init(to,app,aem.toJson().toString());
             }catch (Exception e){
                 Intent gcm_rec = new Intent("your_action");
-                gcm_rec.putExtra("error","Couldn't encrypt message");
+                gcm_rec.putExtra("error",app.getString(R.string.cant_encrypt_message));
                 LocalBroadcastManager.getInstance(app.getApplicationContext()).sendBroadcast(gcm_rec);
                 e.printStackTrace();
                 Log.e("MESSAGE SENDER","SENDING MESSAGE FAILED WITH ENCRYPTION");
@@ -147,7 +148,7 @@ public class MessageSender {
 
     public static void sendKeyExchangeMessage(DxApplication app, String to){
         try {
-            QuotedUserMessage msg = new QuotedUserMessage("","",app.getHostname(),"Key Exchange Message", app.getHostname(),new Date().getTime(),false,to,false);
+            QuotedUserMessage msg = new QuotedUserMessage("","",app.getHostname(),app.getString(R.string.key_exchange_message), app.getHostname(),new Date().getTime(),false,to,false);
             DbHelper.saveMessage(msg,app,to,false);
             Intent gcm_rec = new Intent("your_action");
             LocalBroadcastManager.getInstance(app.getApplicationContext()).sendBroadcast(gcm_rec);
@@ -181,7 +182,7 @@ public class MessageSender {
 
     public static void sendProcessedKeyExchangeMessage(AddressedKeyExchangeMessage akem, DxApplication app){
         try {
-            DbHelper.saveMessage(new QuotedUserMessage(app.getHostname(),"Response Key Exchange Message",akem.getAddress()),app,akem.getAddress(),false);
+            DbHelper.saveMessage(new QuotedUserMessage(app.getHostname(),app.getString(R.string.resp_key_exchange),akem.getAddress()),app,akem.getAddress(),false);
             KeyExchangeMessage kem = new SessionBuilder(app.getEntity().getStore(), new SignalProtocolAddress(akem.getAddress(),1)).process(akem.getKem());
             Log.d("KEM INITIATE", "messageReceiver: "+ Arrays.toString(kem.serialize()));
             AddressedKeyExchangeMessage akem3 = new AddressedKeyExchangeMessage(kem,app.getHostname(),true);
@@ -204,6 +205,7 @@ public class MessageSender {
             }else{
                 return;
             }
+            app.sendNotification("New Message!","you have a new secret message");
             if(json.has("kem")){
 //                if(app.getEntity().getStore().containsSession(new SignalProtocolAddress(json.getString("address"),1))){
 //                    if(!app.getEntity().getStore().loadSession(new SignalProtocolAddress(json.getString("address"),1)).getSessionState().hasPendingKeyExchange()){
@@ -223,7 +225,7 @@ public class MessageSender {
                         SessionBuilder sb = new SessionBuilder(app.getEntity().getStore(), new SignalProtocolAddress(akem.getAddress(),1));
                         sb.process(akem.getKem());
 
-                        DbHelper.saveMessage(new QuotedUserMessage("","",json.getString("address"),"Response Key Exchange Message", json.getString("address"),new Date().getTime(),false,json.getString("address"),false),app,json.getString("address"),false);
+                        DbHelper.saveMessage(new QuotedUserMessage("","",json.getString("address"),app.getString(R.string.resp_key_exchange), json.getString("address"),new Date().getTime(),false,json.getString("address"),false),app,json.getString("address"),false);
                         Intent gcm_rec = new Intent("your_action");
                         LocalBroadcastManager.getInstance(app.getApplicationContext()).sendBroadcast(gcm_rec);
 
@@ -235,7 +237,7 @@ public class MessageSender {
                         Log.e("MESSAGE RECEIVER ERROR", "FAILED!!! Received Response Key Exchange Message : ");
                     }
                 }else{
-                    DbHelper.saveMessage(new QuotedUserMessage("","",json.getString("address"),"Key Exchange Message", json.getString("address"),new Date().getTime(),false,json.getString("address"),false),app,json.getString("address"),false);
+                    DbHelper.saveMessage(new QuotedUserMessage("","",json.getString("address"),app.getString(R.string.key_exchange_message), json.getString("address"),new Date().getTime(),false,json.getString("address"),false),app,json.getString("address"),false);
                     sendKeyExchangeMessage(app,akem.getAddress(),akem.getKem());
                     Log.e("MESSAGE RECEIVER", "GOT KEM: SENDING KEM BACK TO : "+json.getString("address"));
                 }

+ 1 - 1
app/src/main/java/com/dx/anonymousmessenger/messages/QuotedUserMessage.java

@@ -45,7 +45,7 @@ public class QuotedUserMessage extends UserMessage {
 //        this.type = "";
     }
 
-    //for media messages
+    //for media only messages
     public QuotedUserMessage(String address, String sender, long createdAt, boolean received, String to, String filename, String path, String type){
         super(address, "", sender, createdAt, received, to);
         this.filename = filename;

+ 4 - 6
app/src/main/java/com/dx/anonymousmessenger/tor/ServerSocketViaTor.java

@@ -158,7 +158,7 @@ public class ServerSocketViaTor {
             try {
                 while (true) {
                     Socket sock = socket.accept();
-                    Log.e("SERVER CONNECTION", "SOMETHING IS HAPPENING");
+                    Log.d("SERVER CONNECTION", "RECEIVING SOMETHING");
                     try{
                         new Thread(()->{
                             try{
@@ -172,10 +172,11 @@ public class ServerSocketViaTor {
 //                                    } catch (Exception e) {
 //                                        e.printStackTrace();
 //                                    }
-                                    sock.close();
+
                                     if(DbHelper.contactExists(msg.replace("hello-",""),app)){
                                         app.queueUnsentMessages(msg.replace("hello-",""));
                                     }
+                                    sock.close();
                                     return;
                                 }else if(msg.equals("call")){
                                     try {
@@ -251,10 +252,7 @@ public class ServerSocketViaTor {
 //                                while(!msg.equals("nuf"))
 //                                {
                                     final String rec = msg;
-                                    new Thread(()->{
-                                        MessageSender.messageReceiver(rec,app);
-                                        app.sendNotification("New Message!","you have a new secret message");
-                                    }).start();
+                                    new Thread(()-> MessageSender.messageReceiver(rec,app)).start();
                                     outputStream.writeUTF("ack3");
                                     outputStream.flush();
 //                                    msg = in.readUTF();

+ 1 - 1
app/src/main/java/com/dx/anonymousmessenger/tor/TorClientSocks4.java

@@ -140,7 +140,7 @@ public class TorClientSocks4 {
         }
     }
 
-    public boolean testAddress(DxApplication app, String address){
+    public static boolean testAddress(DxApplication app, String address){
         Socket socket;
         try {
             socket = Utilities.socks4aSocketConnection(address, 5780, "127.0.0.1",app.getAndroidTorRelay().getSocksPort());

+ 7 - 0
app/src/main/res/anim/slide_down.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <translate
+        android:duration="500"
+        android:fromYDelta="0"
+        android:toYDelta="-100%" />
+</set>

+ 7 - 0
app/src/main/res/anim/slide_up.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <translate
+            android:duration="500"
+            android:fromYDelta="-100%"
+            android:toYDelta="0" />
+</set>

BIN
app/src/main/res/drawable/baseline_add_black_18dp.png


BIN
app/src/main/res/drawable/baseline_person_add_black_36dp.png


BIN
app/src/main/res/drawable/biglogo.png


+ 5 - 0
app/src/main/res/drawable/ic_baseline_attach_file_24.xml

@@ -0,0 +1,5 @@
+<vector android:height="24dp" android:tint="#FFFFFF"
+    android:viewportHeight="24" android:viewportWidth="24"
+    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@android:color/white" android:pathData="M16.5,6v11.5c0,2.21 -1.79,4 -4,4s-4,-1.79 -4,-4V5c0,-1.38 1.12,-2.5 2.5,-2.5s2.5,1.12 2.5,2.5v10.5c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1V6H10v9.5c0,1.38 1.12,2.5 2.5,2.5s2.5,-1.12 2.5,-2.5V5c0,-2.21 -1.79,-4 -4,-4S7,2.79 7,5v12.5c0,3.04 2.46,5.5 5.5,5.5s5.5,-2.46 5.5,-5.5V6h-1.5z"/>
+</vector>

BIN
app/src/main/res/drawable/logo_real.png


BIN
app/src/main/res/drawable/message_backgound_02.jpg


+ 0 - 4
app/src/main/res/drawable/test.xml

@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<selector>
-
-</selector>

+ 92 - 9
app/src/main/res/layout/activity_message_list.xml

@@ -87,11 +87,12 @@
         app:layout_constraintRight_toRightOf="parent">
 
         <androidx.constraintlayout.widget.ConstraintLayout
+            android:id="@+id/constraintLayout"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:background="@drawable/rounded_rectangle_steel"
             app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintEnd_toStartOf="@+id/fab_audio"
+            app:layout_constraintEnd_toStartOf="@+id/fab_file"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toTopOf="parent">
 
@@ -102,6 +103,7 @@
                 android:layout_gravity="center"
                 android:layout_margin="4dp"
                 android:layout_weight="1"
+                android:animateLayoutChanges="true"
                 android:autofillHints="@string/enter_message"
                 android:background="@drawable/rounded_rectangle_steel"
                 android:hint="@string/enter_message"
@@ -111,7 +113,6 @@
                 android:textColor="#ffffff"
                 android:textColorHint="#ffffff"
                 android:visibility="visible"
-                android:animateLayoutChanges="true"
                 app:layout_constraintBottom_toBottomOf="parent"
                 app:layout_constraintEnd_toEndOf="parent"
                 app:layout_constraintStart_toStartOf="parent" />
@@ -120,11 +121,11 @@
                 android:id="@+id/quote_sender_typing"
                 android:layout_width="0dp"
                 android:layout_height="wrap_content"
+                android:animateLayoutChanges="true"
                 android:paddingStart="10dp"
                 android:paddingEnd="6dp"
                 android:text="@string/textview"
                 android:textColor="#ffffff"
-                android:animateLayoutChanges="true"
                 app:layout_constraintBottom_toTopOf="@+id/quote_text_typing"
                 app:layout_constraintEnd_toEndOf="@+id/quote_text_typing"
                 app:layout_constraintHorizontal_bias="0.0"
@@ -137,13 +138,13 @@
                 android:layout_width="0dp"
                 android:layout_height="wrap_content"
                 android:alpha="0.75"
+                android:animateLayoutChanges="true"
                 android:background="@drawable/rounded_rectangle_dark"
                 android:maxWidth="300dp"
                 android:padding="8dp"
                 android:text="@string/textview"
                 android:textColor="#ffffff"
                 android:textSize="14sp"
-                android:animateLayoutChanges="true"
                 app:layout_constraintBottom_toTopOf="@+id/edittext_chatbox"
                 app:layout_constraintEnd_toEndOf="@+id/edittext_chatbox"
                 app:layout_constraintStart_toStartOf="parent" />
@@ -154,24 +155,40 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_margin="4dp"
+            android:animateLayoutChanges="true"
             android:backgroundTint="@android:color/transparent"
             android:clickable="true"
             android:focusable="true"
-            android:animateLayoutChanges="true"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintEnd_toStartOf="@+id/button_chatbox_send"
             app:layout_constraintTop_toTopOf="parent"
             app:rippleColor="@color/red_500"
             app:srcCompat="@drawable/ic_baseline_mic_24" />
 
+        <com.google.android.material.floatingactionbutton.FloatingActionButton
+            android:id="@+id/fab_file"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="4dp"
+            android:layout_marginEnd="1dp"
+            android:animateLayoutChanges="true"
+            android:backgroundTint="@android:color/transparent"
+            android:clickable="true"
+            android:focusable="true"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toStartOf="@+id/fab_audio"
+            app:layout_constraintTop_toTopOf="parent"
+            app:rippleColor="@color/red_500"
+            app:srcCompat="@drawable/ic_baseline_attach_file_24" />
+
         <Button
             android:id="@+id/button_chatbox_send"
             style="@style/DXSendButton"
             android:layout_width="64dp"
             android:layout_height="45dp"
+            android:animateLayoutChanges="true"
             android:background="@drawable/button_ripple"
             android:text="@string/send"
-            android:animateLayoutChanges="true"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintTop_toTopOf="parent" />
@@ -180,11 +197,11 @@
             android:id="@+id/layout_audio"
             android:layout_width="0dp"
             android:layout_height="match_parent"
+            android:animateLayoutChanges="true"
             android:orientation="horizontal"
             android:visibility="gone"
-            android:animateLayoutChanges="true"
             app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintEnd_toStartOf="@+id/fab_audio"
+            app:layout_constraintEnd_toEndOf="@+id/constraintLayout"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toTopOf="parent">
 
@@ -193,8 +210,8 @@
                 android:layout_width="0dp"
                 android:layout_height="wrap_content"
                 android:layout_weight="0.25"
-                android:contentDescription="@string/recording"
                 android:animateLayoutChanges="true"
+                android:contentDescription="@string/recording"
                 app:srcCompat="@drawable/ic_baseline_mic_red_36" />
 
             <TextView
@@ -202,11 +219,45 @@
                 android:layout_width="0dp"
                 android:layout_height="match_parent"
                 android:layout_weight="1"
+                android:animateLayoutChanges="true"
                 android:fontFamily="sans-serif-condensed"
                 android:gravity="center"
                 android:text="@string/_00_00"
                 android:textColor="@color/red_500"
+                android:textStyle="bold" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/layout_pics"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:animateLayoutChanges="true"
+            android:orientation="horizontal"
+            android:visibility="gone"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="@+id/constraintLayout"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent">
+
+            <ImageView
+                android:id="@+id/imageView4"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="0.25"
                 android:animateLayoutChanges="true"
+                android:contentDescription="@string/recording"
+                app:srcCompat="@android:drawable/ic_menu_report_image" />
+
+            <TextView
+                android:id="@+id/txt_pic_help"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:animateLayoutChanges="true"
+                android:fontFamily="sans-serif-condensed"
+                android:gravity="center"
+                android:text="@string/pic_help"
+                android:textColor="@color/red_500"
                 android:textStyle="bold" />
         </LinearLayout>
 
@@ -227,4 +278,36 @@
         app:layout_constraintStart_toStartOf="parent"
         app:srcCompat="@drawable/ic_baseline_arrow_downward_24" />
 
+    <TextView
+        android:id="@+id/txt_status"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_margin="4dp"
+        android:background="@drawable/contact_item_back"
+        android:elevation="18dp"
+        android:fontFamily="monospace"
+        android:padding="14dp"
+        android:text="@string/user_is_online"
+        android:textSize="12sp"
+        android:visibility="gone"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <RelativeLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="2dp"
+        android:visibility="visible"
+        app:layout_constraintBottom_toTopOf="@+id/layout_chatbox"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent">
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/rv_media"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:visibility="gone" />
+    </RelativeLayout>
+
 </androidx.constraintlayout.widget.ConstraintLayout>

+ 53 - 0
app/src/main/res/layout/activity_picture_viewer.xml

@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context="com.dx.anonymousmessenger.PictureViewerActivity">
+
+    <com.google.android.material.textfield.TextInputLayout
+        android:id="@+id/txt_layout_caption"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:elevation="5dp"
+        android:visibility="gone"
+        app:layout_constraintBottom_toBottomOf="@+id/img_to_send"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent">
+
+        <com.google.android.material.textfield.TextInputEditText
+            android:id="@+id/txt_caption"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:hint="@string/caption"
+            android:imeOptions="actionDone"
+            android:maxLines="1" />
+    </com.google.android.material.textfield.TextInputLayout>
+
+    <ImageView
+        android:id="@+id/img_to_send"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:srcCompat="@android:drawable/ic_menu_report_image"
+        android:contentDescription="@string/image_to_send" />
+
+    <com.google.android.material.floatingactionbutton.FloatingActionButton
+        android:id="@+id/btn_send_media"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="64dp"
+        android:layout_marginBottom="64dp"
+        android:clickable="true"
+        android:focusable="true"
+        android:visibility="gone"
+        app:fabSize="auto"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:maxImageSize="64dp"
+        app:srcCompat="@android:drawable/ic_menu_send" />
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 3 - 3
app/src/main/res/layout/fragment_setup_username.xml

@@ -32,7 +32,7 @@
             tools:enabled="true" />
 
         <com.google.android.material.textfield.TextInputLayout
-            android:id="@+id/textInputLayout"
+            android:id="@+id/txt_layout_caption"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             app:layout_constraintEnd_toEndOf="parent"
@@ -40,7 +40,7 @@
             app:layout_constraintTop_toTopOf="parent">
 
             <com.google.android.material.textfield.TextInputEditText
-                android:id="@+id/txt_nickname"
+                android:id="@+id/txt_caption"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:hint="@string/nickname"
@@ -56,7 +56,7 @@
             android:text="@string/nickname_help"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toBottomOf="@+id/textInputLayout" />
+            app:layout_constraintTop_toBottomOf="@+id/txt_layout_caption" />
     </androidx.constraintlayout.widget.ConstraintLayout>
 
 </ScrollView>

+ 104 - 0
app/src/main/res/layout/item_media_message_received.xml

@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <FrameLayout
+        android:id="@+id/frameLayout"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:background="@drawable/rounded_rectangle_steel"
+        android:padding="8dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent">
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+
+            <TextView
+                android:id="@+id/text_message_body"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:autoLink="all"
+                android:background="@drawable/rounded_rectangle_steel"
+                android:maxWidth="240dp"
+                android:padding="8dp"
+                android:text="@string/msg_place_holder"
+                android:textColor="#ffffff"
+                android:textSize="20sp"
+                android:textStyle="bold"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/text_message_name" />
+
+            <TextView
+                android:id="@+id/text_message_name"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:background="@drawable/rounded_rectangle_steel"
+                android:padding="8dp"
+                android:text="@string/john_doe"
+                android:textColor="#ffffff"
+                android:textSize="12sp"
+                app:layout_constraintStart_toEndOf="@+id/image_message_profile"
+                app:layout_constraintTop_toTopOf="parent" />
+
+            <ImageView
+                android:id="@+id/image_message_profile"
+                android:layout_width="28dp"
+                android:layout_height="28dp"
+                android:background="@drawable/circle"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="parent"
+                android:contentDescription="@string/profile_image" />
+
+            <ImageView
+                android:id="@+id/img_seen2"
+                android:layout_width="16dp"
+                android:layout_height="19dp"
+                android:elevation="10dp"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/img_holder"
+                app:srcCompat="@android:drawable/ic_lock_lock"
+                android:contentDescription="@string/seen_image" />
+
+            <TextView
+                android:id="@+id/text_message_time"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/_11_40"
+                android:textColor="#ffffff"
+                android:textSize="12sp"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintStart_toEndOf="@+id/img_seen2"
+                app:layout_constraintTop_toBottomOf="@+id/img_holder" />
+
+            <ImageView
+                android:id="@+id/img_holder"
+                android:layout_width="200dp"
+                android:layout_height="100dp"
+                android:scaleType="centerCrop"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/text_message_body"
+                app:srcCompat="@android:drawable/ic_menu_report_image"
+                android:contentDescription="@string/image_to_send" />
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+    </FrameLayout>
+
+    <!--    <TextView-->
+    <!--        android:id="@+id/textViewOptions"-->
+    <!--        android:layout_width="wrap_content"-->
+    <!--        android:layout_height="wrap_content"-->
+    <!--        android:layout_alignParentTop="true"-->
+    <!--        android:layout_alignParentRight="true"-->
+    <!--        android:paddingLeft="@dimen/margin_activity_horizontal"-->
+    <!--        android:text="&#8942;"-->
+    <!--        android:textAppearance="?android:textAppearanceLarge"-->
+    <!--        app:layout_constraintBottom_toBottomOf="parent"-->
+    <!--        app:layout_constraintStart_toStartOf="parent" />-->
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 71 - 0
app/src/main/res/layout/item_media_message_sent.xml

@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <FrameLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:background="@drawable/rounded_rectangle_dark"
+        android:padding="8dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="parent">
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <ImageView
+                android:id="@+id/img_holder"
+                android:layout_width="200dp"
+                android:layout_height="200dp"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/text_message_body"
+                app:srcCompat="@android:drawable/ic_menu_report_image"
+                android:contentDescription="@string/image_to_send" />
+
+            <ImageView
+                android:id="@+id/img_seen"
+                android:layout_width="20dp"
+                android:layout_height="20dp"
+                android:contentDescription="@string/seen_image"
+                android:elevation="10dp"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/img_holder"
+                app:srcCompat="@android:drawable/ic_lock_lock" />
+
+            <TextView
+                android:id="@+id/text_message_body"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:background="@drawable/rounded_rectangle_dark"
+                android:maxWidth="240dp"
+                android:padding="8dp"
+                android:text="@string/hello_hello"
+                android:textColor="#ffffff"
+                android:textSize="20sp"
+                android:textStyle="bold"
+                android:autoLink="all"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintTop_toTopOf="parent" />
+
+            <TextView
+                android:id="@+id/text_message_time"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/_11_40"
+                android:textColor="#ffffff"
+                android:textSize="12sp"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toStartOf="@+id/img_seen"
+                app:layout_constraintTop_toBottomOf="@+id/img_holder" />
+        </androidx.constraintlayout.widget.ConstraintLayout>
+    </FrameLayout>
+
+
+
+
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 80 - 0
app/src/main/res/layout/item_media_message_sent_ok.xml

@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <FrameLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:background="@drawable/rounded_rectangle_dark"
+        android:padding="8dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="parent">
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <ImageView
+                android:id="@+id/view_image3"
+                android:layout_width="22dp"
+                android:layout_height="20dp"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toStartOf="@+id/img_seen"
+                app:srcCompat="@drawable/ic_launcher_foreground"
+                android:contentDescription="@string/seen_image" />
+
+            <ImageView
+                android:id="@+id/img_holder"
+                android:layout_width="200dp"
+                android:layout_height="200dp"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/text_message_body"
+                app:srcCompat="@android:drawable/ic_menu_report_image"
+                android:contentDescription="@string/image_to_send" />
+
+            <ImageView
+                android:id="@+id/img_seen"
+                android:layout_width="20dp"
+                android:layout_height="20dp"
+                android:contentDescription="@string/seen_image"
+                android:elevation="10dp"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/img_holder"
+                app:srcCompat="@android:drawable/ic_lock_lock" />
+
+            <TextView
+                android:id="@+id/text_message_body"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:background="@drawable/rounded_rectangle_dark"
+                android:maxWidth="240dp"
+                android:padding="8dp"
+                android:text="@string/hello_hello"
+                android:textColor="#ffffff"
+                android:textSize="20sp"
+                android:textStyle="bold"
+                android:autoLink="all"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintTop_toTopOf="parent" />
+
+            <TextView
+                android:id="@+id/text_message_time"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/_11_40"
+                android:textColor="#ffffff"
+                android:textSize="12sp"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toStartOf="@+id/view_image3"
+                app:layout_constraintTop_toBottomOf="@+id/img_holder" />
+        </androidx.constraintlayout.widget.ConstraintLayout>
+    </FrameLayout>
+
+
+
+
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 11 - 9
app/src/main/res/layout/item_message_sent_ok.xml

@@ -22,44 +22,46 @@
                 android:layout_width="20dp"
                 android:layout_height="20dp"
                 android:elevation="10dp"
-                app:layout_constraintBottom_toBottomOf="@+id/imageView"
+                app:layout_constraintBottom_toBottomOf="@+id/view_image"
                 app:layout_constraintEnd_toEndOf="parent"
                 app:layout_constraintTop_toBottomOf="@+id/text_message_body"
-                app:srcCompat="@android:drawable/ic_lock_lock" />
+                app:srcCompat="@android:drawable/ic_lock_lock"
+                android:contentDescription="@string/seen_image" />
 
             <TextView
                 android:id="@+id/text_message_body"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
+                android:autoLink="all"
                 android:background="@drawable/rounded_rectangle_dark"
                 android:maxWidth="240dp"
                 android:padding="8dp"
-                android:text="hello, hello!"
+                android:text="@string/hello_hello"
                 android:textColor="#ffffff"
                 android:textSize="20sp"
                 android:textStyle="bold"
-                android:autoLink="all"
                 app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintTop_toBottomOf="@+id/quote_text" />
+                app:layout_constraintTop_toTopOf="parent" />
 
             <TextView
                 android:id="@+id/text_message_time"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:text="11:40"
+                android:text="@string/_11_40"
                 android:textColor="#ffffff"
                 android:textSize="12sp"
                 app:layout_constraintBottom_toBottomOf="parent"
-                app:layout_constraintEnd_toStartOf="@+id/imageView"
+                app:layout_constraintEnd_toStartOf="@+id/view_image"
                 app:layout_constraintTop_toBottomOf="@+id/text_message_body" />
 
             <ImageView
-                android:id="@+id/imageView"
+                android:id="@+id/view_image"
                 android:layout_width="22dp"
                 android:layout_height="20dp"
                 app:layout_constraintBottom_toBottomOf="parent"
                 app:layout_constraintEnd_toStartOf="@+id/img_seen"
-                app:srcCompat="@drawable/ic_launcher_foreground" />
+                app:srcCompat="@drawable/ic_launcher_foreground"
+                android:contentDescription="@string/seen_image" />
 
         </androidx.constraintlayout.widget.ConstraintLayout>
     </FrameLayout>

+ 10 - 8
app/src/main/res/layout/item_message_sent_ok_quote.xml

@@ -27,7 +27,8 @@
                 app:layout_constraintBottom_toBottomOf="parent"
                 app:layout_constraintEnd_toEndOf="parent"
                 app:layout_constraintTop_toBottomOf="@+id/text_message_body"
-                app:srcCompat="@android:drawable/ic_lock_lock" />
+                app:srcCompat="@android:drawable/ic_lock_lock"
+                android:contentDescription="@string/seen_image" />
 
             <TextView
                 android:id="@+id/quote_text"
@@ -37,7 +38,7 @@
                 android:background="@drawable/rounded_rectangle_dark"
                 android:maxWidth="240dp"
                 android:padding="8dp"
-                android:text="TextView"
+                android:text="@string/textview"
 
                 android:textColor="#ffffff"
                 android:textSize="14sp"
@@ -51,7 +52,7 @@
                 android:background="@drawable/rounded_rectangle_dark"
                 android:maxWidth="240dp"
                 android:padding="8dp"
-                android:text="hello, hello!"
+                android:text="@string/hello_hello"
                 android:textColor="#ffffff"
                 android:textSize="20sp"
                 android:textStyle="bold"
@@ -64,26 +65,27 @@
                 android:id="@+id/text_message_time"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:text="11:40"
+                android:text="@string/_11_40"
                 android:textColor="#ffffff"
                 android:textSize="12sp"
                 app:layout_constraintBottom_toBottomOf="parent"
-                app:layout_constraintEnd_toStartOf="@+id/imageView"
+                app:layout_constraintEnd_toStartOf="@+id/view_image"
                 app:layout_constraintTop_toBottomOf="@+id/text_message_body" />
 
             <ImageView
-                android:id="@+id/imageView"
+                android:id="@+id/view_image"
                 android:layout_width="20dp"
                 android:layout_height="20dp"
                 app:layout_constraintBottom_toBottomOf="parent"
                 app:layout_constraintEnd_toStartOf="@+id/img_seen"
-                app:srcCompat="@drawable/ic_launcher_foreground" />
+                app:srcCompat="@drawable/ic_launcher_foreground"
+                android:contentDescription="@string/seen_image" />
 
             <TextView
                 android:id="@+id/quote_sender"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:text="TextView"
+                android:text="@string/textview"
                 android:textColor="@color/dx_white"
                 app:layout_constraintEnd_toStartOf="@+id/quote_text"
                 app:layout_constraintTop_toTopOf="parent" />

+ 16 - 0
app/src/main/res/layout/media_rv_item.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:padding="10dp">
+
+    <ImageView
+        android:id="@+id/view_image"
+        android:layout_width="100dp"
+        android:layout_height="100dp"
+        android:background="@drawable/rounded_rectangle_dark"
+        android:contentDescription="@string/image"
+        android:padding="14dp" />
+
+</LinearLayout>

+ 22 - 6
app/src/main/res/layout/my_text_view.xml

@@ -16,6 +16,20 @@
         android:orientation="horizontal"
         app:layout_constraintTop_toTopOf="parent">
 
+        <ImageView
+            android:id="@+id/contact_online"
+            android:layout_width="20dp"
+            android:layout_height="20dp"
+            android:layout_alignParentEnd="true"
+            android:layout_alignParentBottom="true"
+            android:layout_marginEnd="3dp"
+            android:contentDescription="@string/online"
+            android:elevation="18dp"
+            android:src="@drawable/ic_online"
+            android:visibility="gone"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toStartOf="@+id/message_text" />
+
         <ImageView
             android:id="@+id/seen"
             android:layout_width="24dp"
@@ -30,15 +44,16 @@
 
         <TextView
             android:id="@+id/message_text"
-            android:layout_width="wrap_content"
+            android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_marginStart="10dp"
             android:ellipsize="end"
-            android:maxLength="25"
             android:maxLines="1"
             android:text="@string/text_msg_placeholder"
             app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintStart_toEndOf="@+id/image_message_profile2" />
+            app:layout_constraintEnd_toEndOf="@+id/contact_name"
+            app:layout_constraintStart_toEndOf="@+id/image_message_profile2"
+            app:layout_constraintTop_toBottomOf="@+id/contact_name" />
 
         <ImageView
             android:id="@+id/image_message_profile2"
@@ -47,21 +62,21 @@
             android:layout_margin="4dp"
             android:background="@drawable/circle"
             android:contentDescription="@string/profile_image"
-            app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toTopOf="parent" />
 
         <TextView
             android:id="@+id/contact_name"
-            android:layout_width="wrap_content"
+            android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_marginStart="10dp"
             android:ellipsize="end"
-            android:maxLength="25"
             android:maxLines="1"
             android:text="@string/really_long_word"
+            android:textAlignment="viewStart"
             android:textSize="18sp"
             android:textStyle="bold"
+            app:layout_constraintEnd_toStartOf="@+id/time_text"
             app:layout_constraintStart_toEndOf="@+id/image_message_profile2"
             app:layout_constraintTop_toTopOf="parent" />
 
@@ -79,6 +94,7 @@
             android:id="@+id/time_text"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:padding="3dp"
             android:text="@string/time_passed_placeholder"
             app:layout_constraintEnd_toStartOf="@+id/contact_unread_circle"
             app:layout_constraintTop_toTopOf="parent" />

+ 16 - 0
app/src/main/res/values-ar/strings.xml

@@ -92,4 +92,20 @@
     <string name="crash_message">المسنجر المجهول: لقد نفذت الرام لديك</string>
     <string name="no_encryption_yet">التشفير ليس جاهزاََ بعد</string>
     <string name="no_session">لا توجد جلسة تشفير, سيتم بدء جلسة تشفير الآن</string>
+    <string name="audio_message">رسالة صوتية</string>
+    <string name="user_is_online">متصل الآن</string>
+    <string name="user_is_offline">غير متصل الآن</string>
+    <string name="cant_encrypt_message">ﻻ يمكن تشفير الرسالة, عليك انتظار تبادل المفاتيح</string>
+    <string name="resp_key_exchange">رسالة رد تبادل المفاتيح</string>
+    <string name="key_exchange_message">رسالة تبادل مفاتيح</string>
+    <string name="image_to_send">الصورة المرسلة</string>
+    <string name="read_storage_perm_ask_title">إذن قراءة وحدة التخزين المشتركة</string>
+    <string name="why_need_read_storage">هذا البرنامج يحتاج الوصول إلى الصور ليستطيع إرسالها</string>
+    <string name="icon_for_image_to_send">أيقونة للصورة المرسلة</string>
+    <string name="image">صورة</string>
+    <string name="pic_help">إلغاء</string>
+    <string name="send_media_to">إرسال الوسائط إلى</string>
+    <string name="picure_view">رؤية الصورة</string>
+    <string name="media_message">رسالة وسائط</string>
+    <string name="caption">عنوان</string>
 </resources>

+ 18 - 1
app/src/main/res/values/strings.xml

@@ -31,7 +31,7 @@
     <string name="action_search">Search</string>
     <string name="action_shutdown">Shutdown</string>
     <string name="action_my_identity">My Identity</string>
-    <string name="action_restart_tor">restart tor</string>
+    <string name="action_restart_tor">Restart tor</string>
     <string name="my_identity_explanation">Public key used to verify identity</string>
     <string name="verify_identity_explanation">Both public keys together to verify both identities</string>
     <string name="action_call">Call</string>
@@ -95,4 +95,21 @@
     <string name="crash_message">Anonymous Messenger: you are out of RAM !</string>
     <string name="no_encryption_yet">Not ready for encryption yet</string>
     <string name="no_session">Session doesn\'t exist, starting session now</string>
+    <string name="audio_message">audio message</string>
+    <string name="user_is_online">User is online</string>
+    <string name="user_is_offline">User is offline</string>
+    <string name="cant_encrypt_message">Couldn\'t encrypt message, you have to wait for key exchange</string>
+    <string name="resp_key_exchange">Response Key Exchange Message</string>
+    <string name="key_exchange_message">Key Exchange Message</string>
+    <string name="image_to_send">image to send</string>
+    <string name="read_storage_perm_ask_title">Read Storage Permission</string>
+    <string name="why_need_read_storage">the app needs this to be able to send images
+        stored on your phone</string>
+    <string name="icon_for_image_to_send">icon for image to send</string>
+    <string name="image">image</string>
+    <string name="pic_help">Cancel</string>
+    <string name="send_media_to">"Send Media to "</string>
+    <string name="picure_view">Image View</string>
+    <string name="media_message">media message</string>
+    <string name="caption">Caption this</string>
 </resources>

+ 2 - 3
app/src/main/res/values/styles.xml

@@ -15,9 +15,8 @@
         <item name="alertDialogTheme">@style/AppAlertDialog</item>
     </style>
 
-    <style name="splashScreenTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
-        <item name="android:windowBackground">@drawable/biglogo</item>
-    </style>
+<!--    <style name="splashScreenTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">-->
+<!--    </style>-->
 
     <style name="ButtonTheme" parent="Theme.AppCompat.DayNight">
         <!-- A strange hack needed only to override button color on all API levels -->

+ 1 - 1
build.gradle

@@ -5,7 +5,7 @@ buildscript {
         jcenter()
     }
     dependencies {
-        classpath "com.android.tools.build:gradle:4.0.1"
+        classpath 'com.android.tools.build:gradle:4.0.2'
 
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files