diff --git a/app/src/main/java/sushi/hardcore/aira/AIRADatabase.kt b/app/src/main/java/sushi/hardcore/aira/AIRADatabase.kt index 1f98086..87f7fac 100644 --- a/app/src/main/java/sushi/hardcore/aira/AIRADatabase.kt +++ b/app/src/main/java/sushi/hardcore/aira/AIRADatabase.kt @@ -12,7 +12,7 @@ object AIRADatabase { external fun setContactSeen(contactUuid: String, seen: Boolean): Boolean external fun changeContactName(contactUuid: String, newName: String): Boolean external fun setContactAvatar(contactUuid: String, avatarUuid: String?): Boolean - external fun storeMsg(contactUuid: String, outgoing: Boolean, data: ByteArray): Boolean + external fun storeMsg(contactUuid: String, outgoing: Boolean, timestamp: Long, data: ByteArray): Boolean external fun storeFile(contactUuid: String?, data: ByteArray): ByteArray? external fun loadMsgs(uuid: String, offset: Int, count: Int): ArrayList? external fun loadFile(rawUuid: ByteArray): ByteArray? diff --git a/app/src/main/java/sushi/hardcore/aira/ChatActivity.kt b/app/src/main/java/sushi/hardcore/aira/ChatActivity.kt index 67432d9..9819d09 100644 --- a/app/src/main/java/sushi/hardcore/aira/ChatActivity.kt +++ b/app/src/main/java/sushi/hardcore/aira/ChatActivity.kt @@ -23,6 +23,7 @@ import sushi.hardcore.aira.databinding.DialogFingerprintsBinding import sushi.hardcore.aira.databinding.DialogInfoBinding import sushi.hardcore.aira.utils.FileUtils import sushi.hardcore.aira.utils.StringUtils +import sushi.hardcore.aira.utils.TimeUtils class ChatActivity : ServiceBoundActivity() { private external fun generateFingerprint(publicKey: ByteArray): String @@ -77,7 +78,7 @@ class ChatActivity : ServiceBoundActivity() { val msg = binding.editMessage.text.toString() airaService.sendTo(sessionId, Protocol.newMessage(msg)) binding.editMessage.text.clear() - chatAdapter.newMessage(ChatItem(true, Protocol.newMessage(msg))) + chatAdapter.newMessage(ChatItem(true, TimeUtils.getTimestamp(), Protocol.newMessage(msg))) if (airaService.contacts.contains(sessionId)) { lastLoadedMessageOffset += 1 } @@ -178,10 +179,10 @@ class ChatActivity : ServiceBoundActivity() { } } } - override fun onNewMessage(sessionId: Int, data: ByteArray): Boolean { + override fun onNewMessage(sessionId: Int, timestamp: Long, data: ByteArray): Boolean { return if (this@ChatActivity.sessionId == sessionId) { runOnUiThread { - chatAdapter.newMessage(ChatItem(false, data)) + chatAdapter.newMessage(ChatItem(false, timestamp, data)) binding.recyclerChat.smoothScrollToPosition(chatAdapter.itemCount) } if (airaService.contacts.contains(sessionId)) { diff --git a/app/src/main/java/sushi/hardcore/aira/ChatItem.kt b/app/src/main/java/sushi/hardcore/aira/ChatItem.kt index 7847b73..0df9e3c 100644 --- a/app/src/main/java/sushi/hardcore/aira/ChatItem.kt +++ b/app/src/main/java/sushi/hardcore/aira/ChatItem.kt @@ -2,7 +2,7 @@ package sushi.hardcore.aira import sushi.hardcore.aira.background_service.Protocol -class ChatItem(val outgoing: Boolean, val data: ByteArray) { +class ChatItem(val outgoing: Boolean, val timestamp: Long, val data: ByteArray) { companion object { const val OUTGOING_MESSAGE = 0 const val INCOMING_MESSAGE = 1 diff --git a/app/src/main/java/sushi/hardcore/aira/MainActivity.kt b/app/src/main/java/sushi/hardcore/aira/MainActivity.kt index 057ff15..725a005 100644 --- a/app/src/main/java/sushi/hardcore/aira/MainActivity.kt +++ b/app/src/main/java/sushi/hardcore/aira/MainActivity.kt @@ -73,7 +73,7 @@ class MainActivity : ServiceBoundActivity() { onlineSessionAdapter.setAvatar(sessionId, avatar) } } - override fun onNewMessage(sessionId: Int, data: ByteArray): Boolean { + override fun onNewMessage(sessionId: Int, timestamp: Long, data: ByteArray): Boolean { runOnUiThread { onlineSessionAdapter.setSeen(sessionId, false) } diff --git a/app/src/main/java/sushi/hardcore/aira/SettingsActivity.kt b/app/src/main/java/sushi/hardcore/aira/SettingsActivity.kt index 3b12ff5..a7401a4 100644 --- a/app/src/main/java/sushi/hardcore/aira/SettingsActivity.kt +++ b/app/src/main/java/sushi/hardcore/aira/SettingsActivity.kt @@ -10,7 +10,6 @@ import android.widget.EditText import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity -import androidx.preference.EditTextPreference import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreferenceCompat diff --git a/app/src/main/java/sushi/hardcore/aira/adapters/ChatAdapter.kt b/app/src/main/java/sushi/hardcore/aira/adapters/ChatAdapter.kt index dba73bf..d3dde2a 100644 --- a/app/src/main/java/sushi/hardcore/aira/adapters/ChatAdapter.kt +++ b/app/src/main/java/sushi/hardcore/aira/adapters/ChatAdapter.kt @@ -1,5 +1,6 @@ package sushi.hardcore.aira.adapters +import android.annotation.SuppressLint import android.content.Context import android.graphics.PorterDuff import android.graphics.PorterDuffColorFilter @@ -15,6 +16,8 @@ import androidx.core.view.updatePadding import androidx.recyclerview.widget.RecyclerView import sushi.hardcore.aira.ChatItem import sushi.hardcore.aira.R +import sushi.hardcore.aira.utils.StringUtils +import java.util.* class ChatAdapter( private val context: Context, @@ -52,8 +55,9 @@ class ChatAdapter( itemView.updatePadding(left = BUBBLE_HORIZONTAL_PADDING) } } - protected fun configureBubble(context: Context, view: View, outgoing: Boolean) { - view.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).apply { + protected fun configureBubble(context: Context, outgoing: Boolean) { + val bubble = itemView.findViewById(R.id.bubble_content) + bubble.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).apply { gravity = if (outgoing) { marginStart = CONTAINER_MARGIN Gravity.END @@ -63,12 +67,21 @@ class ChatAdapter( } } if (!outgoing) { - view.background.colorFilter = PorterDuffColorFilter( + bubble.background.colorFilter = PorterDuffColorFilter( ContextCompat.getColor(context, R.color.incomingBubbleBackground), PorterDuff.Mode.SRC ) } } + protected fun setTimestamp(chatItem: ChatItem): TextView { + val calendar = Calendar.getInstance().apply { + time = Date(chatItem.timestamp * 1000) + } + val textView = itemView.findViewById(R.id.timestamp) + @SuppressLint("SetTextI18n") + textView.text = StringUtils.toTwoDigits(calendar.get(Calendar.HOUR_OF_DAY))+":"+StringUtils.toTwoDigits(calendar.get(Calendar.MINUTE)) + return textView + } } internal open class MessageViewHolder(itemView: View): BubbleViewHolder(itemView) { @@ -82,43 +95,58 @@ class ChatAdapter( internal class OutgoingMessageViewHolder(private val context: Context, itemView: View): MessageViewHolder(itemView) { fun bind(chatItem: ChatItem) { - configureBubble(context, bindMessage(chatItem).apply { - setLinkTextColor(ContextCompat.getColor(context, R.color.outgoingTextLink)) - }, true) + setTimestamp(chatItem).apply { + setTextColor(ContextCompat.getColor(context, R.color.outgoingTimestamp)) + } + configureBubble(context, true) + bindMessage(chatItem).apply { + setLinkTextColor(ContextCompat.getColor(context, R.color.outgoingTextLink)) + } setPadding(true) } } internal class IncomingMessageViewHolder(private val context: Context, itemView: View): MessageViewHolder(itemView) { fun bind(chatItem: ChatItem) { - configureBubble(context, bindMessage(chatItem).apply { + setTimestamp(chatItem).apply { + setTextColor(ContextCompat.getColor(context, R.color.incomingTimestamp)) + } + configureBubble(context, false) + bindMessage(chatItem).apply { setLinkTextColor(ContextCompat.getColor(context, R.color.incomingTextLink)) - }, false) + } setPadding(false) } } internal open class FileViewHolder(itemView: View, private val onSavingFile: (filename: String, rawUuid: ByteArray) -> Unit): BubbleViewHolder(itemView) { - protected fun bindFile(chatItem: ChatItem): LinearLayout { + protected fun bindFile(chatItem: ChatItem) { val filename = chatItem.data.sliceArray(17 until chatItem.data.size).decodeToString() itemView.findViewById(R.id.text_filename).text = filename itemView.findViewById(R.id.button_save).setOnClickListener { onSavingFile(filename, chatItem.data.sliceArray(1 until 17)) } - return itemView.findViewById(R.id.bubble_content) } } internal class OutgoingFileViewHolder(private val context: Context, itemView: View, onSavingFile: (filename: String, rawUuid: ByteArray) -> Unit): FileViewHolder(itemView, onSavingFile) { fun bind(chatItem: ChatItem) { - configureBubble(context, bindFile(chatItem), true) + setTimestamp(chatItem).apply { + setTextColor(ContextCompat.getColor(context, R.color.outgoingTimestamp)) + } + bindFile(chatItem) + configureBubble(context, true) setPadding(true) } } internal class IncomingFileViewHolder(private val context: Context, itemView: View, onSavingFile: (filename: String, rawUuid: ByteArray) -> Unit): FileViewHolder(itemView, onSavingFile) { fun bind(chatItem: ChatItem) { - configureBubble(context, bindFile(chatItem), false) + setTimestamp(chatItem).apply { + setTextColor(ContextCompat.getColor(context, R.color.incomingTimestamp)) + } + bindFile(chatItem) + configureBubble(context, false) setPadding(false) } } diff --git a/app/src/main/java/sushi/hardcore/aira/background_service/AIRAService.kt b/app/src/main/java/sushi/hardcore/aira/background_service/AIRAService.kt index 7f19ac6..966e757 100644 --- a/app/src/main/java/sushi/hardcore/aira/background_service/AIRAService.kt +++ b/app/src/main/java/sushi/hardcore/aira/background_service/AIRAService.kt @@ -17,6 +17,7 @@ import androidx.core.app.RemoteInput import sushi.hardcore.aira.* import sushi.hardcore.aira.utils.FileUtils import sushi.hardcore.aira.utils.StringUtils +import sushi.hardcore.aira.utils.TimeUtils import java.io.IOException import java.net.* import java.nio.channels.* @@ -108,7 +109,7 @@ class AIRAService : Service() { fun onSessionDisconnect(sessionId: Int) fun onNameTold(sessionId: Int, name: String) fun onAvatarChanged(sessionId: Int, avatar: ByteArray?) - fun onNewMessage(sessionId: Int, data: ByteArray): Boolean + fun onNewMessage(sessionId: Int, timestamp: Long, data: ByteArray): Boolean fun onAskLargeFiles(sessionId: Int, filesReceiver: FilesReceiver): Boolean } @@ -164,7 +165,7 @@ class AIRAService : Service() { sendFile.inputStream.close() sendTo(sessionId, Protocol.newFile(sendFile.fileName, buffer)) AIRADatabase.storeFile(contacts[sessionId]?.uuid, buffer)?.let { rawFileUuid -> - saveMsg(sessionId, byteArrayOf(Protocol.FILE) + rawFileUuid + sendFile.fileName.toByteArray()) + saveMsg(sessionId, TimeUtils.getTimestamp(), byteArrayOf(Protocol.FILE) + rawFileUuid + sendFile.fileName.toByteArray()) } } @@ -203,7 +204,7 @@ class AIRAService : Service() { contacts[sessionId] = contact savedMsgs.remove(sessionId)?.let { msgs -> for (msg in msgs) { - AIRADatabase.storeMsg(contact.uuid, msg.outgoing, msg.data) + AIRADatabase.storeMsg(contact.uuid, msg.outgoing, msg.timestamp, msg.data) } } savedNames.remove(sessionId) @@ -419,20 +420,20 @@ class AIRAService : Service() { ) } - private fun saveMsg(sessionId: Int, msg: ByteArray) { + private fun saveMsg(sessionId: Int, timestamp: Long, msg: ByteArray) { var msgSaved = false contacts[sessionId]?.uuid?.let { uuid -> - msgSaved = AIRADatabase.storeMsg(uuid, true, msg) + msgSaved = AIRADatabase.storeMsg(uuid, true, timestamp, msg) } if (!msgSaved) { - savedMsgs[sessionId]?.add(ChatItem(true, msg)) + savedMsgs[sessionId]?.add(ChatItem(true, timestamp, msg)) } } private fun sendAndSave(sessionId: Int, msg: ByteArray) { sessions[sessionId]?.encryptAndSend(msg, usePadding) if (msg[0] == Protocol.MESSAGE) { - saveMsg(sessionId, msg) + saveMsg(sessionId, TimeUtils.getTimestamp(), msg) } } @@ -810,17 +811,18 @@ class AIRAService : Service() { null } }?.let { handledMsg -> + val timestamp = TimeUtils.getTimestamp() var seen = false uiCallbacks?.let { uiCallbacks -> - seen = uiCallbacks.onNewMessage(sessionId, handledMsg) + seen = uiCallbacks.onNewMessage(sessionId, timestamp, handledMsg) } setSeen(sessionId, seen) var msgSaved = false contacts[sessionId]?.let { contact -> - msgSaved = AIRADatabase.storeMsg(contact.uuid, false, handledMsg) + msgSaved = AIRADatabase.storeMsg(contact.uuid, false, timestamp, handledMsg) } if (!msgSaved){ - savedMsgs[sessionId]?.add(ChatItem(false, handledMsg)) + savedMsgs[sessionId]?.add(ChatItem(false, timestamp, handledMsg)) } if (isAppInBackground) { sendNotification(sessionId, handledMsg) diff --git a/app/src/main/java/sushi/hardcore/aira/utils/StringUtils.kt b/app/src/main/java/sushi/hardcore/aira/utils/StringUtils.kt index 453e254..1483bf8 100644 --- a/app/src/main/java/sushi/hardcore/aira/utils/StringUtils.kt +++ b/app/src/main/java/sushi/hardcore/aira/utils/StringUtils.kt @@ -25,4 +25,12 @@ object StringUtils { fun sanitizeName(name: String): String { return name.replace('\n', ' ') } + + fun toTwoDigits(number: Int): String { + return if (number < 10) { + "0$number" + } else { + number.toString() + } + } } \ No newline at end of file diff --git a/app/src/main/java/sushi/hardcore/aira/utils/TimeUtils.kt b/app/src/main/java/sushi/hardcore/aira/utils/TimeUtils.kt new file mode 100644 index 0000000..3c54b22 --- /dev/null +++ b/app/src/main/java/sushi/hardcore/aira/utils/TimeUtils.kt @@ -0,0 +1,7 @@ +package sushi.hardcore.aira.utils + +object TimeUtils { + fun getTimestamp(): Long { + return System.currentTimeMillis()/1000 + } +} \ No newline at end of file diff --git a/app/src/main/native/src/identity.rs b/app/src/main/native/src/identity.rs index fd4c610..9fab132 100644 --- a/app/src/main/native/src/identity.rs +++ b/app/src/main/native/src/identity.rs @@ -43,12 +43,11 @@ fn get_database_path(database_folder: &str) -> String { Path::new(database_folder).join(DB_NAME).to_str().unwrap().to_owned() } -struct EncryptedIdentity { - name: String, - encrypted_keypair: Vec, - salt: Vec, - encrypted_master_key: Vec, - encrypted_use_padding: Vec, +#[derive(Debug, Clone)] +pub struct Message { + pub outgoing: bool, + pub timestamp: u64, + pub data: Vec, } pub struct Contact { @@ -60,6 +59,14 @@ pub struct Contact { pub seen: bool, } +struct EncryptedIdentity { + name: String, + encrypted_keypair: Vec, + salt: Vec, + encrypted_master_key: Vec, + encrypted_use_padding: Vec, +} + pub struct Identity { pub name: String, keypair: Keypair, @@ -251,16 +258,17 @@ impl Identity { Ok(file_uuid) } - pub fn store_msg(&self, contact_uuid: &Uuid, outgoing: bool, data: &[u8]) -> Result { + pub fn store_msg(&self, contact_uuid: &Uuid, message: Message) -> Result { let db = Connection::open(self.get_database_path())?; - db.execute(&format!("CREATE TABLE IF NOT EXISTS \"{}\" (outgoing BLOB, data BLOB)", contact_uuid), [])?; - let outgoing_byte: u8 = bool_to_byte(outgoing); + db.execute(&format!("CREATE TABLE IF NOT EXISTS \"{}\" (outgoing BLOB, timestamp BLOB, data BLOB)", contact_uuid), [])?; + let outgoing_byte: u8 = bool_to_byte(message.outgoing); let encrypted_outgoing = crypto::encrypt_data(&[outgoing_byte], &self.master_key).unwrap(); - let encrypted_data = crypto::encrypt_data(data, &self.master_key).unwrap(); - db.execute(&format!("INSERT INTO \"{}\" (outgoing, data) VALUES (?1, ?2)", contact_uuid), params![encrypted_outgoing, encrypted_data]) + let encrypted_timestamp = crypto::encrypt_data(&message.timestamp.to_be_bytes(), &self.master_key).unwrap(); + let encrypted_data = crypto::encrypt_data(&message.data, &self.master_key).unwrap(); + db.execute(&format!("INSERT INTO \"{}\" (outgoing, timestamp, data) VALUES (?1, ?2, ?3)", contact_uuid), params![encrypted_outgoing, encrypted_timestamp, encrypted_data]) } - pub fn load_msgs(&self, contact_uuid: &Uuid, offset: usize, mut count: usize) -> Option)>> { + pub fn load_msgs(&self, contact_uuid: &Uuid, offset: usize, mut count: usize) -> Option> { match Connection::open(self.get_database_path()) { Ok(db) => { if let Ok(mut stmt) = db.prepare(&format!("SELECT count(*) FROM \"{}\"", contact_uuid)) { @@ -271,7 +279,7 @@ impl Identity { if offset+count >= total { count = total-offset; } - let mut stmt = db.prepare(&format!("SELECT outgoing, data FROM \"{}\" LIMIT {} OFFSET {}", contact_uuid, count, total-offset-count)).unwrap(); + let mut stmt = db.prepare(&format!("SELECT outgoing, timestamp, data FROM \"{}\" LIMIT {} OFFSET {}", contact_uuid, count, total-offset-count)).unwrap(); let mut rows = stmt.query([]).unwrap(); let mut msgs = Vec::new(); while let Ok(Some(row)) = rows.next() { @@ -280,9 +288,19 @@ impl Identity { Ok(outgoing) => { match byte_to_bool(outgoing[0]) { Ok(outgoing) => { - let encrypted_data: Vec = row.get(1).unwrap(); - match crypto::decrypt_data(encrypted_data.as_slice(), &self.master_key) { - Ok(data) => msgs.push((outgoing, data)), + let encrypted_timestamp: Vec = row.get(1).unwrap(); + match crypto::decrypt_data(&encrypted_timestamp, &self.master_key) { + Ok(timestamp) => { + let encrypted_data: Vec = row.get(2).unwrap(); + match crypto::decrypt_data(encrypted_data.as_slice(), &self.master_key) { + Ok(data) => msgs.push(Message { + outgoing, + timestamp: u64::from_be_bytes(timestamp.try_into().unwrap()), + data, + }), + Err(e) => print_error!(e) + } + } Err(e) => print_error!(e) } } diff --git a/app/src/main/native/src/lib.rs b/app/src/main/native/src/lib.rs index aa5f1b4..101151f 100644 --- a/app/src/main/native/src/lib.rs +++ b/app/src/main/native/src/lib.rs @@ -7,7 +7,7 @@ use std::{convert::TryInto, fmt::Display, str::FromStr, sync::{Mutex}}; use lazy_static::lazy_static; use uuid::Uuid; use android_log; -use identity::{Identity, Contact}; +use identity::{Identity, Contact, Message}; use crate::crypto::{HandshakeKeys, ApplicationKeys}; lazy_static! { @@ -17,7 +17,7 @@ lazy_static! { #[cfg(target_os="android")] use jni::JNIEnv; use jni::objects::{JClass, JObject, JString, JList, JValue}; -use jni::sys::{jboolean, jint, jbyteArray, jobject}; +use jni::sys::{jboolean, jint, jlong, jbyteArray, jobject}; fn jstring_to_string(env: JNIEnv, input: JString) -> String { String::from(env.get_string(input).unwrap()) @@ -287,8 +287,13 @@ pub fn Java_sushi_hardcore_aira_AIRADatabase_setContactSeen(env: JNIEnv, _: JCla #[allow(non_snake_case)] #[no_mangle] -pub fn Java_sushi_hardcore_aira_AIRADatabase_storeMsg(env: JNIEnv, _: JClass, contactUuid: JString, outgoing: jboolean, data: jbyteArray) -> jboolean { - result_to_jboolean(loaded_identity.lock().unwrap().as_ref().unwrap().store_msg(&jstring_to_uuid(env, contactUuid).unwrap(), jboolean_to_bool(outgoing), &env.convert_byte_array(data).unwrap())) +pub fn Java_sushi_hardcore_aira_AIRADatabase_storeMsg(env: JNIEnv, _: JClass, contactUuid: JString, outgoing: jboolean, timestamp: jlong, data: jbyteArray) -> jboolean { + let message = Message { + outgoing: jboolean_to_bool(outgoing), + timestamp: timestamp as u64, + data: env.convert_byte_array(data).unwrap(), + }; + result_to_jboolean(loaded_identity.lock().unwrap().as_ref().unwrap().store_msg(&jstring_to_uuid(env, contactUuid).unwrap(), message)) } #[allow(non_snake_case)] @@ -301,7 +306,11 @@ pub fn Java_sushi_hardcore_aira_AIRADatabase_loadMsgs(env: JNIEnv, _: JClass, uu let array_list = JList::from_env(&env, array_list).unwrap(); let chat_item_class = env.find_class("sushi/hardcore/aira/ChatItem").unwrap(); for msg in msgs { - let chat_item_object = env.new_object(chat_item_class, "(Z[B)V", &[JValue::Bool(bool_to_jboolean(msg.0)), slice_to_jvalue(env, &msg.1)]).unwrap(); + let chat_item_object = env.new_object(chat_item_class, "(ZJ[B)V", &[ + JValue::Bool(bool_to_jboolean(msg.outgoing)), + JValue::Long(msg.timestamp as jlong), + slice_to_jvalue(env, &msg.data), + ]).unwrap(); array_list.add(chat_item_object).unwrap(); } *array_list diff --git a/app/src/main/res/layout/adapter_chat_file.xml b/app/src/main/res/layout/adapter_chat_file.xml index a95e6b3..c682681 100644 --- a/app/src/main/res/layout/adapter_chat_file.xml +++ b/app/src/main/res/layout/adapter_chat_file.xml @@ -8,26 +8,38 @@ android:id="@+id/bubble_content" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:orientation="horizontal" - android:gravity="center" - style="@style/Bubble" - android:paddingStart="6dp" - android:paddingVertical="5dp"> + android:orientation="vertical" + style="@style/Bubble"> - - - + android:gravity="center_vertical"> + + + + + + + + diff --git a/app/src/main/res/layout/adapter_chat_message.xml b/app/src/main/res/layout/adapter_chat_message.xml index 174075b..12a48ce 100644 --- a/app/src/main/res/layout/adapter_chat_message.xml +++ b/app/src/main/res/layout/adapter_chat_message.xml @@ -4,13 +4,27 @@ android:layout_height="wrap_content" android:paddingVertical="1dp"> - + android:orientation="vertical" + style="@style/Bubble"> + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 8fb0b8a..cfa2f55 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -12,4 +12,6 @@ #66666666 #00000000 #777777 + #777777 + #d7d7d7 \ No newline at end of file