Commit 190d0dc5 authored by divyanshu's avatar divyanshu

resolved conflicts

parent 74031054
...@@ -3,7 +3,6 @@ apply plugin: 'io.fabric' ...@@ -3,7 +3,6 @@ apply plugin: 'io.fabric'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
apply plugin: 'realm-android'
android { android {
compileSdkVersion versions.compileSdk compileSdkVersion versions.compileSdk
...@@ -11,11 +10,11 @@ android { ...@@ -11,11 +10,11 @@ android {
defaultConfig { defaultConfig {
applicationId "chat.rocket.android" applicationId "chat.rocket.android"
minSdkVersion 21 minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk targetSdkVersion versions.targetSdk
versionCode 2022 versionCode 2030
versionName "2.3.0" versionName "2.4.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true multiDexEnabled true
} }
...@@ -26,12 +25,19 @@ android { ...@@ -26,12 +25,19 @@ android {
keyAlias System.getenv("KEY_ALIAS") keyAlias System.getenv("KEY_ALIAS")
keyPassword System.getenv("KEY_PASSWORD") keyPassword System.getenv("KEY_PASSWORD")
} }
debug {
storeFile project.rootProject.file('debug.keystore').getCanonicalFile()
storePassword "android"
keyAlias "androiddebugkey"
keyPassword "android"
}
} }
buildTypes { buildTypes {
release { release {
buildConfigField "String", "REQUIRED_SERVER_VERSION", '"0.62.0"' buildConfigField "String", "REQUIRED_SERVER_VERSION", '"0.62.0"'
buildConfigField "String", "RECOMMENDED_SERVER_VERSION", '"0.63.0"' buildConfigField "String", "RECOMMENDED_SERVER_VERSION", '"0.64.2"'
signingConfig signingConfigs.release signingConfig signingConfigs.release
minifyEnabled false minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
...@@ -39,13 +45,15 @@ android { ...@@ -39,13 +45,15 @@ android {
debug { debug {
buildConfigField "String", "REQUIRED_SERVER_VERSION", '"0.62.0"' buildConfigField "String", "REQUIRED_SERVER_VERSION", '"0.62.0"'
buildConfigField "String", "RECOMMENDED_SERVER_VERSION", '"0.63.0"' buildConfigField "String", "RECOMMENDED_SERVER_VERSION", '"0.64.2"'
signingConfig signingConfigs.debug
applicationIdSuffix ".dev" applicationIdSuffix ".dev"
} }
} }
packagingOptions { packagingOptions {
exclude 'META-INF/core.kotlin_module' exclude 'META-INF/core.kotlin_module'
exclude 'META-INF/main.kotlin_module'
} }
} }
...@@ -53,6 +61,8 @@ dependencies { ...@@ -53,6 +61,8 @@ dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation project(':player') implementation project(':player')
implementation project(':emoji')
implementation project(':draw')
implementation libraries.kotlin implementation libraries.kotlin
implementation libraries.coroutines implementation libraries.coroutines
...@@ -60,11 +70,11 @@ dependencies { ...@@ -60,11 +70,11 @@ dependencies {
implementation libraries.appCompat implementation libraries.appCompat
implementation libraries.recyclerview implementation libraries.recyclerview
implementation libraries.design implementation libraries.material
implementation libraries.constraintLayout implementation libraries.constraintlayout
implementation libraries.cardView implementation libraries.cardview
implementation libraries.flexbox implementation libraries.flexbox
implementation libraries.customTabs implementation libraries.browser
implementation libraries.androidKtx implementation libraries.androidKtx
...@@ -73,12 +83,11 @@ dependencies { ...@@ -73,12 +83,11 @@ dependencies {
kapt libraries.daggerProcessor kapt libraries.daggerProcessor
kapt libraries.daggerAndroidApt kapt libraries.daggerAndroidApt
implementation libraries.playServicesGcm implementation libraries.fcm
implementation libraries.playServicesAuth implementation libraries.playServicesAuth
implementation libraries.room implementation libraries.room
kapt libraries.roomProcessor kapt libraries.roomProcessor
implementation libraries.roomRxjava
implementation libraries.lifecycleExtensions implementation libraries.lifecycleExtensions
kapt libraries.lifecycleCompiler kapt libraries.lifecycleCompiler
...@@ -106,11 +115,11 @@ dependencies { ...@@ -106,11 +115,11 @@ dependencies {
implementation libraries.markwon implementation libraries.markwon
implementation libraries.sheetMenu
implementation libraries.aVLoadingIndicatorView implementation libraries.aVLoadingIndicatorView
implementation('com.crashlytics.sdk.android:crashlytics:2.6.8@aar') { implementation "com.github.luciofm:livedata-ktx:b1e8bbc25a"
implementation('com.crashlytics.sdk.android:crashlytics:2.9.2@aar') {
transitive = true transitive = true
} }
......
package chat.rocket.android.chatroom.ui package chat.rocket.android.chatroom.ui
import android.Manifest
import android.app.Activity import android.app.Activity
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Environment
import android.os.Handler import android.os.Handler
import android.support.annotation.DrawableRes
import android.support.v4.app.Fragment
import android.support.v7.widget.DefaultItemAnimator
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.view.* import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.annotation.DrawableRes
import androidx.core.text.bold import androidx.core.text.bold
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatroom.adapter.* import chat.rocket.android.chatroom.adapter.ChatRoomAdapter
import chat.rocket.android.chatroom.adapter.CommandSuggestionsAdapter
import chat.rocket.android.chatroom.adapter.PEOPLE
import chat.rocket.android.chatroom.adapter.PeopleSuggestionsAdapter
import chat.rocket.android.chatroom.adapter.RoomSuggestionsAdapter
import chat.rocket.android.chatroom.presentation.ChatRoomPresenter import chat.rocket.android.chatroom.presentation.ChatRoomPresenter
import chat.rocket.android.chatroom.presentation.ChatRoomView import chat.rocket.android.chatroom.presentation.ChatRoomView
import chat.rocket.android.chatroom.viewmodel.BaseViewModel import chat.rocket.android.chatroom.uimodel.BaseUiModel
import chat.rocket.android.chatroom.viewmodel.MessageViewModel import chat.rocket.android.chatroom.uimodel.MessageUiModel
import chat.rocket.android.chatroom.viewmodel.suggestion.ChatRoomSuggestionViewModel import chat.rocket.android.chatroom.uimodel.suggestion.ChatRoomSuggestionUiModel
import chat.rocket.android.chatroom.viewmodel.suggestion.CommandSuggestionViewModel import chat.rocket.android.chatroom.uimodel.suggestion.CommandSuggestionUiModel
import chat.rocket.android.chatroom.viewmodel.suggestion.PeopleSuggestionViewModel import chat.rocket.android.chatroom.uimodel.suggestion.PeopleSuggestionUiModel
import chat.rocket.android.draw.DrawingActivity
import chat.rocket.android.emoji.ComposerEditText
import chat.rocket.android.emoji.Emoji
import chat.rocket.android.emoji.EmojiKeyboardListener
import chat.rocket.android.emoji.EmojiKeyboardPopup
import chat.rocket.android.emoji.EmojiParser
import chat.rocket.android.emoji.EmojiPickerPopup
import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.android.helper.AndroidPermissionsHelper
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.helper.KeyboardHelper import chat.rocket.android.helper.KeyboardHelper
import chat.rocket.android.helper.MessageParser import chat.rocket.android.helper.MessageParser
import chat.rocket.android.util.extensions.* import chat.rocket.android.util.extensions.asObservable
import chat.rocket.android.widget.emoji.* import chat.rocket.android.util.extensions.circularRevealOrUnreveal
import chat.rocket.android.util.extensions.fadeIn
import chat.rocket.android.util.extensions.fadeOut
import chat.rocket.android.util.extensions.hideKeyboard
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.rotateBy
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.util.extensions.ui
import chat.rocket.common.model.RoomType
import chat.rocket.common.model.roomTypeOf
import chat.rocket.core.internal.realtime.socket.model.State import chat.rocket.core.internal.realtime.socket.model.State
import chat.rocket.core.model.ChatRoom import chat.rocket.core.model.ChatRoom
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
...@@ -41,6 +74,9 @@ import kotlinx.android.synthetic.main.fragment_chat_room.* ...@@ -41,6 +74,9 @@ import kotlinx.android.synthetic.main.fragment_chat_room.*
import kotlinx.android.synthetic.main.message_attachment_options.* import kotlinx.android.synthetic.main.message_attachment_options.*
import kotlinx.android.synthetic.main.message_composer.* import kotlinx.android.synthetic.main.message_composer.*
import kotlinx.android.synthetic.main.message_list.* import kotlinx.android.synthetic.main.message_list.*
import java.io.File
import java.io.FileOutputStream
import java.util.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import javax.inject.Inject import javax.inject.Inject
...@@ -49,10 +85,11 @@ fun newInstance( ...@@ -49,10 +85,11 @@ fun newInstance(
chatRoomId: String, chatRoomId: String,
chatRoomName: String, chatRoomName: String,
chatRoomType: String, chatRoomType: String,
isChatRoomReadOnly: Boolean, isReadOnly: Boolean,
chatRoomLastSeen: Long, chatRoomLastSeen: Long,
isSubscribed: Boolean = true, isSubscribed: Boolean = true,
isChatRoomCreator: Boolean = false, isCreator: Boolean = false,
isFavorite: Boolean = false,
chatRoomMessage: String? = null chatRoomMessage: String? = null
): Fragment { ): Fragment {
return ChatRoomFragment().apply { return ChatRoomFragment().apply {
...@@ -60,10 +97,11 @@ fun newInstance( ...@@ -60,10 +97,11 @@ fun newInstance(
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId) putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
putString(BUNDLE_CHAT_ROOM_NAME, chatRoomName) putString(BUNDLE_CHAT_ROOM_NAME, chatRoomName)
putString(BUNDLE_CHAT_ROOM_TYPE, chatRoomType) putString(BUNDLE_CHAT_ROOM_TYPE, chatRoomType)
putBoolean(BUNDLE_IS_CHAT_ROOM_READ_ONLY, isChatRoomReadOnly) putBoolean(BUNDLE_IS_CHAT_ROOM_READ_ONLY, isReadOnly)
putLong(BUNDLE_CHAT_ROOM_LAST_SEEN, chatRoomLastSeen) putLong(BUNDLE_CHAT_ROOM_LAST_SEEN, chatRoomLastSeen)
putBoolean(BUNDLE_CHAT_ROOM_IS_SUBSCRIBED, isSubscribed) putBoolean(BUNDLE_CHAT_ROOM_IS_SUBSCRIBED, isSubscribed)
putBoolean(BUNDLE_CHAT_ROOM_IS_CREATOR, isChatRoomCreator) putBoolean(BUNDLE_CHAT_ROOM_IS_CREATOR, isCreator)
putBoolean(BUNDLE_CHAT_ROOM_IS_FAVORITE, isFavorite)
putString(BUNDLE_CHAT_ROOM_MESSAGE, chatRoomMessage) putString(BUNDLE_CHAT_ROOM_MESSAGE, chatRoomMessage)
} }
} }
...@@ -74,11 +112,20 @@ private const val BUNDLE_CHAT_ROOM_NAME = "chat_room_name" ...@@ -74,11 +112,20 @@ private const val BUNDLE_CHAT_ROOM_NAME = "chat_room_name"
private const val BUNDLE_CHAT_ROOM_TYPE = "chat_room_type" private const val BUNDLE_CHAT_ROOM_TYPE = "chat_room_type"
private const val BUNDLE_IS_CHAT_ROOM_READ_ONLY = "is_chat_room_read_only" private const val BUNDLE_IS_CHAT_ROOM_READ_ONLY = "is_chat_room_read_only"
private const val REQUEST_CODE_FOR_PERFORM_SAF = 42 private const val REQUEST_CODE_FOR_PERFORM_SAF = 42
private const val REQUEST_CODE_FOR_DRAW = 101
private const val BUNDLE_CHAT_ROOM_LAST_SEEN = "chat_room_last_seen" private const val BUNDLE_CHAT_ROOM_LAST_SEEN = "chat_room_last_seen"
private const val BUNDLE_CHAT_ROOM_IS_SUBSCRIBED = "chat_room_is_subscribed" private const val BUNDLE_CHAT_ROOM_IS_SUBSCRIBED = "chat_room_is_subscribed"
private const val BUNDLE_CHAT_ROOM_IS_CREATOR = "chat_room_is_creator" private const val BUNDLE_CHAT_ROOM_IS_CREATOR = "chat_room_is_creator"
private const val BUNDLE_CHAT_ROOM_IS_FAVORITE = "chat_room_is_favorite"
private const val BUNDLE_CHAT_ROOM_MESSAGE = "chat_room_message" private const val BUNDLE_CHAT_ROOM_MESSAGE = "chat_room_message"
private const val MENU_ACTION_FAVORITE_UNFAVORITE_CHAT = 1
private const val MENU_ACTION_MEMBER = 2
private const val MENU_ACTION_MENTIONS = 3
private const val MENU_ACTION_PINNED_MESSAGES = 4
private const val MENU_ACTION_FAVORITE_MESSAGES = 5
private const val MENU_ACTION_FILES = 6
class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiReactionListener { class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiReactionListener {
@Inject @Inject
...@@ -91,8 +138,9 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -91,8 +138,9 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
private lateinit var chatRoomType: String private lateinit var chatRoomType: String
private var chatRoomMessage: String? = null private var chatRoomMessage: String? = null
private var isSubscribed: Boolean = true private var isSubscribed: Boolean = true
private var isChatRoomReadOnly: Boolean = false private var isReadOnly: Boolean = false
private var isChatRoomCreator: Boolean = false private var isCreator: Boolean = false
private var isFavorite: Boolean = false
private var isBroadcastChannel: Boolean = false private var isBroadcastChannel: Boolean = false
private lateinit var emojiKeyboardPopup: EmojiKeyboardPopup private lateinit var emojiKeyboardPopup: EmojiKeyboardPopup
private var chatRoomLastSeen: Long = -1 private var chatRoomLastSeen: Long = -1
...@@ -131,10 +179,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -131,10 +179,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
chatRoomId = bundle.getString(BUNDLE_CHAT_ROOM_ID) chatRoomId = bundle.getString(BUNDLE_CHAT_ROOM_ID)
chatRoomName = bundle.getString(BUNDLE_CHAT_ROOM_NAME) chatRoomName = bundle.getString(BUNDLE_CHAT_ROOM_NAME)
chatRoomType = bundle.getString(BUNDLE_CHAT_ROOM_TYPE) chatRoomType = bundle.getString(BUNDLE_CHAT_ROOM_TYPE)
isChatRoomReadOnly = bundle.getBoolean(BUNDLE_IS_CHAT_ROOM_READ_ONLY) isReadOnly = bundle.getBoolean(BUNDLE_IS_CHAT_ROOM_READ_ONLY)
isSubscribed = bundle.getBoolean(BUNDLE_CHAT_ROOM_IS_SUBSCRIBED) isSubscribed = bundle.getBoolean(BUNDLE_CHAT_ROOM_IS_SUBSCRIBED)
chatRoomLastSeen = bundle.getLong(BUNDLE_CHAT_ROOM_LAST_SEEN) chatRoomLastSeen = bundle.getLong(BUNDLE_CHAT_ROOM_LAST_SEEN)
isChatRoomCreator = bundle.getBoolean(BUNDLE_CHAT_ROOM_IS_CREATOR) isCreator = bundle.getBoolean(BUNDLE_CHAT_ROOM_IS_CREATOR)
isFavorite = bundle.getBoolean(BUNDLE_CHAT_ROOM_IS_FAVORITE)
chatRoomMessage = bundle.getString(BUNDLE_CHAT_ROOM_MESSAGE) chatRoomMessage = bundle.getString(BUNDLE_CHAT_ROOM_MESSAGE)
} else { } else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" } requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
...@@ -187,64 +236,141 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -187,64 +236,141 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
if (requestCode == REQUEST_CODE_FOR_PERFORM_SAF && resultCode == Activity.RESULT_OK) { if (resultData != null && resultCode == Activity.RESULT_OK) {
if (resultData != null) { when(requestCode){
uploadFile(resultData.data) REQUEST_CODE_FOR_PERFORM_SAF -> {
uploadFile(resultData.data)
}
REQUEST_CODE_FOR_DRAW -> {
val result= resultData.getByteArrayExtra("bitmap")
val bitmap = BitmapFactory.decodeByteArray(result, 0, result.size)
val uri = saveImage(bitmap)
uploadFile(uri)
}
} }
} }
} }
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { private fun saveImage(bitmap: Bitmap): Uri {
super.onCreateOptionsMenu(menu, inflater) val imageDir = "${Environment.DIRECTORY_PICTURES}/Rocket.Chat Images/"
inflater.inflate(R.menu.chatroom_actions, menu) val path = Environment.getExternalStoragePublicDirectory(imageDir)
menu.findItem(R.id.action_members_list)?.isVisible = !isBroadcastChannel val file = File(path, UUID.randomUUID().toString()+".png")
path.mkdirs()
file.createNewFile()
val outputStream = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.PNG,100,outputStream)
outputStream.flush()
outputStream.close()
return Uri.fromFile(file)
}
override fun onPrepareOptionsMenu(menu: Menu) {
menu.clear()
if (isFavorite) {
menu.add(
Menu.NONE,
MENU_ACTION_FAVORITE_UNFAVORITE_CHAT,
Menu.NONE,
R.string.title_unfavorite_chat
)
.setIcon(R.drawable.ic_star_yellow_24dp)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM)
} else {
menu.add(
Menu.NONE,
MENU_ACTION_FAVORITE_UNFAVORITE_CHAT,
Menu.NONE,
R.string.title_favorite_chat
)
.setIcon(R.drawable.ic_star_border_white_24dp)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM)
}
menu.add(
Menu.NONE,
MENU_ACTION_MEMBER,
Menu.NONE,
R.string.title_members_list
)
menu.add(
Menu.NONE,
MENU_ACTION_MENTIONS,
Menu.NONE,
R.string.msg_mentions
)
menu.add(
Menu.NONE,
MENU_ACTION_PINNED_MESSAGES,
Menu.NONE,
R.string.title_pinned_messages
)
menu.add(
Menu.NONE,
MENU_ACTION_FAVORITE_MESSAGES,
Menu.NONE,
R.string.title_favorite_messages
)
menu.add(
Menu.NONE,
MENU_ACTION_FILES,
Menu.NONE,
R.string.title_files
)
super.onPrepareOptionsMenu(menu)
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
R.id.action_members_list -> { MENU_ACTION_FAVORITE_UNFAVORITE_CHAT -> {
presenter.toMembersList(chatRoomId) presenter.toggleFavoriteChatRoom(chatRoomId, isFavorite)
}
R.id.action_pinned_messages -> {
presenter.toPinnedMessageList(chatRoomId)
}
R.id.action_favorite_messages -> {
presenter.toFavoriteMessageList(chatRoomId)
}
R.id.action_files -> {
presenter.toFileList(chatRoomId)
} }
MENU_ACTION_MEMBER -> presenter.toMembersList(chatRoomId)
MENU_ACTION_MENTIONS -> presenter.toMentions(chatRoomId)
MENU_ACTION_PINNED_MESSAGES -> presenter.toPinnedMessageList(chatRoomId)
MENU_ACTION_FAVORITE_MESSAGES -> presenter.toFavoriteMessageList(chatRoomId)
MENU_ACTION_FILES -> presenter.toFileList(chatRoomId)
} }
return true return true
} }
override fun showMessages(dataSet: List<BaseViewModel<*>>) { override fun showFavoriteIcon(isFavorite: Boolean) {
this.isFavorite = isFavorite
activity?.invalidateOptionsMenu()
}
override fun showMessages(dataSet: List<BaseUiModel<*>>) {
ui { ui {
// track the message sent immediately after the current message // track the message sent immediately after the current message
var prevMessageViewModel: MessageViewModel? = null var prevMessageUiModel: MessageUiModel? = null
// Loop over received messages to determine first unread // Loop over received messages to determine first unread
for (i in dataSet.indices) { for (i in dataSet.indices) {
val msgModel = dataSet[i] val msgModel = dataSet[i]
if (msgModel is MessageViewModel) { if (msgModel is MessageUiModel) {
val msg = msgModel.rawData val msg = msgModel.rawData
if (msg.timestamp < chatRoomLastSeen) { if (msg.timestamp < chatRoomLastSeen) {
// This message was sent before the last seen of the room. Hence, it was seen. // This message was sent before the last seen of the room. Hence, it was seen.
// if there is a message after (below) this, mark it firstUnread. // if there is a message after (below) this, mark it firstUnread.
if (prevMessageViewModel != null) { if (prevMessageUiModel != null) {
prevMessageViewModel.isFirstUnread = true prevMessageUiModel.isFirstUnread = true
} }
break break
} }
prevMessageViewModel = msgModel prevMessageUiModel = msgModel
} }
} }
if (recycler_view.adapter == null) { if (recycler_view.adapter == null) {
adapter = ChatRoomAdapter( adapter = ChatRoomAdapter(
chatRoomType, chatRoomName, presenter, chatRoomType,
reactionListener = this@ChatRoomFragment chatRoomName,
presenter,
reactionListener = this@ChatRoomFragment,
context = context
) )
recycler_view.adapter = adapter recycler_view.adapter = adapter
if (dataSet.size >= 30) { if (dataSet.size >= 30) {
...@@ -261,7 +387,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -261,7 +387,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
verticalScrollOffset.set(0) verticalScrollOffset.set(0)
} }
presenter.loadActiveMembers(chatRoomId, chatRoomType, filterSelfOut = true) presenter.loadActiveMembers(chatRoomId, chatRoomType, filterSelfOut = true)
toggleNoChatView(adapter.itemCount) empty_chat_view.isVisible = adapter.itemCount == 0
} }
} }
...@@ -272,27 +398,17 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -272,27 +398,17 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
) { ) {
// TODO: We should rely solely on the user being able to post, but we cannot guarantee // TODO: We should rely solely on the user being able to post, but we cannot guarantee
// that the "(channels|groups).roles" endpoint is supported by the server in use. // that the "(channels|groups).roles" endpoint is supported by the server in use.
setupMessageComposer(userCanPost) ui {
isBroadcastChannel = channelIsBroadcast setupMessageComposer(userCanPost)
if (isBroadcastChannel && !userCanMod) activity?.invalidateOptionsMenu() isBroadcastChannel = channelIsBroadcast
if (isBroadcastChannel && !userCanMod) activity?.invalidateOptionsMenu()
}
} }
override fun openDirectMessage(chatRoom: ChatRoom, permalink: String) { override fun openDirectMessage(chatRoom: ChatRoom, permalink: String) {
} }
private fun toggleNoChatView(size: Int) {
if (size == 0) {
image_chat_icon.setVisible(true)
text_chat_title.setVisible(true)
text_chat_description.setVisible(true)
} else {
image_chat_icon.setVisible(false)
text_chat_title.setVisible(false)
text_chat_description.setVisible(false)
}
}
private val layoutChangeListener = private val layoutChangeListener =
View.OnLayoutChangeListener { _, _, _, _, bottom, _, _, _, oldBottom -> View.OnLayoutChangeListener { _, _, _, _, bottom, _, _, _, oldBottom ->
val y = oldBottom - bottom val y = oldBottom - bottom
...@@ -361,7 +477,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -361,7 +477,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
} }
override fun showTypingStatus(usernameList: ArrayList<String>) { override fun showTypingStatus(usernameList: List<String>) {
ui { ui {
when (usernameList.size) { when (usernameList.size) {
1 -> { 1 -> {
...@@ -401,12 +517,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -401,12 +517,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
showMessage(getString(R.string.msg_invalid_file)) showMessage(getString(R.string.msg_invalid_file))
} }
override fun showNewMessage(message: List<BaseViewModel<*>>) { override fun showNewMessage(message: List<BaseUiModel<*>>) {
ui { ui {
adapter.prependData(message) adapter.prependData(message)
recycler_view.scrollToPosition(0)
verticalScrollOffset.set(0) verticalScrollOffset.set(0)
toggleNoChatView(adapter.itemCount) empty_chat_view.isVisible = adapter.itemCount == 0
} }
} }
...@@ -434,7 +549,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -434,7 +549,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
} }
override fun dispatchUpdateMessage(index: Int, message: List<BaseViewModel<*>>) { override fun dispatchUpdateMessage(index: Int, message: List<BaseUiModel<*>>) {
ui { ui {
adapter.updateItem(message.last()) adapter.updateItem(message.last())
if (message.size > 1) { if (message.size > 1) {
...@@ -464,11 +579,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -464,11 +579,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
override fun showLoading() { override fun showLoading() {
ui { view_loading.setVisible(true) } ui { view_loading.isVisible = true }
} }
override fun hideLoading() { override fun hideLoading() {
ui { view_loading.setVisible(false) } ui { view_loading.isVisible = false }
} }
override fun showMessage(message: String) { override fun showMessage(message: String) {
...@@ -485,19 +600,19 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -485,19 +600,19 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
override fun showGenericErrorMessage() = showMessage(getString(R.string.msg_generic_error)) override fun showGenericErrorMessage() = showMessage(getString(R.string.msg_generic_error))
override fun populatePeopleSuggestions(members: List<PeopleSuggestionViewModel>) { override fun populatePeopleSuggestions(members: List<PeopleSuggestionUiModel>) {
ui { ui {
suggestions_view.addItems("@", members) suggestions_view.addItems("@", members)
} }
} }
override fun populateRoomSuggestions(chatRooms: List<ChatRoomSuggestionViewModel>) { override fun populateRoomSuggestions(chatRooms: List<ChatRoomSuggestionUiModel>) {
ui { ui {
suggestions_view.addItems("#", chatRooms) suggestions_view.addItems("#", chatRooms)
} }
} }
override fun populateCommandSuggestions(commands: List<CommandSuggestionViewModel>) { override fun populateCommandSuggestions(commands: List<CommandSuggestionUiModel>) {
ui { ui {
suggestions_view.addItems("/", commands) suggestions_view.addItems("/", commands)
} }
...@@ -507,6 +622,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -507,6 +622,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
ui { ui {
val clipboard = it.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clipboard = it.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
clipboard.primaryClip = ClipData.newPlainText("", message) clipboard.primaryClip = ClipData.newPlainText("", message)
showToast(R.string.msg_message_copied)
} }
} }
...@@ -524,7 +640,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -524,7 +640,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
override fun onEmojiAdded(emoji: Emoji) { override fun onEmojiAdded(emoji: Emoji) {
val cursorPosition = text_message.selectionStart val cursorPosition = text_message.selectionStart
if (cursorPosition > -1) { if (cursorPosition > -1) {
text_message.text.insert(cursorPosition, EmojiParser.parse(emoji.shortname)) text_message.text?.insert(cursorPosition, EmojiParser.parse(emoji.shortname))
text_message.setSelection(cursorPosition + emoji.unicode.length) text_message.setSelection(cursorPosition + emoji.unicode.length)
} }
} }
...@@ -532,7 +648,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -532,7 +648,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
override fun onNonEmojiKeyPressed(keyCode: Int) { override fun onNonEmojiKeyPressed(keyCode: Int) {
when (keyCode) { when (keyCode) {
KeyEvent.KEYCODE_BACK -> with(text_message) { KeyEvent.KEYCODE_BACK -> with(text_message) {
if (selectionStart > 0) text.delete(selectionStart - 1, selectionStart) if (selectionStart > 0) text?.delete(selectionStart - 1, selectionStart)
} }
else -> throw IllegalArgumentException("pressed key not expected") else -> throw IllegalArgumentException("pressed key not expected")
} }
...@@ -549,7 +665,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -549,7 +665,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
override fun showReactionsPopup(messageId: String) { override fun showReactionsPopup(messageId: String) {
ui { ui {
val emojiPickerPopup = EmojiPickerPopup(it) val emojiPickerPopup = EmojiPickerPopup(it)
emojiPickerPopup.listener = object : EmojiListenerAdapter() { emojiPickerPopup.listener = object : EmojiKeyboardListener {
override fun onEmojiAdded(emoji: Emoji) { override fun onEmojiAdded(emoji: Emoji) {
onReactionAdded(messageId, emoji) onReactionAdded(messageId, emoji)
} }
...@@ -593,23 +709,23 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -593,23 +709,23 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
handler.postDelayed(dismissStatus, 2000) handler.postDelayed(dismissStatus, 2000)
} }
is State.Disconnected -> connection_status_text.text = is State.Disconnected -> connection_status_text.text =
getString(R.string.status_disconnected) getString(R.string.status_disconnected)
is State.Connecting -> connection_status_text.text = is State.Connecting -> connection_status_text.text =
getString(R.string.status_connecting) getString(R.string.status_connecting)
is State.Authenticating -> connection_status_text.text = is State.Authenticating -> connection_status_text.text =
getString(R.string.status_authenticating) getString(R.string.status_authenticating)
is State.Disconnecting -> connection_status_text.text = is State.Disconnecting -> connection_status_text.text =
getString(R.string.status_disconnecting) getString(R.string.status_disconnecting)
is State.Waiting -> connection_status_text.text = is State.Waiting -> connection_status_text.text =
getString(R.string.status_waiting, state.seconds) getString(R.string.status_waiting, state.seconds)
} }
} }
} }
override fun onJoined(userCanPost: Boolean) { override fun onJoined(userCanPost: Boolean) {
ui { ui {
input_container.setVisible(true) input_container.isVisible = true
button_join_chat.setVisible(false) button_join_chat.isVisible = false
isSubscribed = true isSubscribed = true
setupMessageComposer(userCanPost) setupMessageComposer(userCanPost)
} }
...@@ -621,13 +737,13 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -621,13 +737,13 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
private fun setupRecyclerView() { private fun setupRecyclerView() {
// Initialize the endlessRecyclerViewScrollListener so we don't NPE at onDestroyView // Initialize the endlessRecyclerViewScrollListener so we don't NPE at onDestroyView
val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, true) val linearLayoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, true)
linearLayoutManager.stackFromEnd = true linearLayoutManager.stackFromEnd = true
recycler_view.layoutManager = linearLayoutManager recycler_view.layoutManager = linearLayoutManager
recycler_view.itemAnimator = DefaultItemAnimator() recycler_view.itemAnimator = DefaultItemAnimator()
endlessRecyclerViewScrollListener = object : endlessRecyclerViewScrollListener = object :
EndlessRecyclerViewScrollListener(recycler_view.layoutManager as LinearLayoutManager) { EndlessRecyclerViewScrollListener(recycler_view.layoutManager as LinearLayoutManager) {
override fun onLoadMore(page: Int, totalItemsCount: Int, recyclerView: RecyclerView?) { override fun onLoadMore(page: Int, totalItemsCount: Int, recyclerView: RecyclerView) {
presenter.loadMessages(chatRoomId, chatRoomType, page * 30L) presenter.loadMessages(chatRoomId, chatRoomType, page * 30L)
} }
} }
...@@ -643,22 +759,21 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -643,22 +759,21 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
private fun setupMessageComposer(canPost: Boolean) { private fun setupMessageComposer(canPost: Boolean) {
if (isChatRoomReadOnly && !canPost) { if (isReadOnly && !canPost) {
text_room_is_read_only.setVisible(true) text_room_is_read_only.isVisible = true
input_container.setVisible(false) input_container.isVisible = false
} else if (!isSubscribed) { } else if (!isSubscribed && roomTypeOf(chatRoomType) !is RoomType.DirectMessage) {
input_container.setVisible(false) input_container.isVisible = false
button_join_chat.setVisible(true) button_join_chat.isVisible = true
button_join_chat.setOnClickListener { presenter.joinChat(chatRoomId) } button_join_chat.setOnClickListener { presenter.joinChat(chatRoomId) }
} else { } else {
button_send.alpha = 0f button_send.isVisible = false
button_send.setVisible(false)
button_show_attachment_options.alpha = 1f button_show_attachment_options.alpha = 1f
button_show_attachment_options.setVisible(true) button_show_attachment_options.isVisible = true
subscribeComposeTextMessage() subscribeComposeTextMessage()
emojiKeyboardPopup = emojiKeyboardPopup =
EmojiKeyboardPopup(activity!!, activity!!.findViewById(R.id.fragment_container)) EmojiKeyboardPopup(activity!!, activity!!.findViewById(R.id.fragment_container))
emojiKeyboardPopup.listener = this emojiKeyboardPopup.listener = this
text_message.listener = object : ComposerEditText.ComposerEditTextListener { text_message.listener = object : ComposerEditText.ComposerEditTextListener {
override fun onKeyboardOpened() { override fun onKeyboardOpened() {
...@@ -708,6 +823,30 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -708,6 +823,30 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
button_add_reaction.setOnClickListener { view -> button_add_reaction.setOnClickListener { view ->
openEmojiKeyboardPopup() openEmojiKeyboardPopup()
} }
button_drawing.setOnClickListener {
if (!canWriteToExternalStorage()) {
checkWritingPermission()
}else{
val intent = Intent(activity, DrawingActivity::class.java)
startActivityForResult(intent, REQUEST_CODE_FOR_DRAW)
}
handler.postDelayed({
hideAttachmentOptions()
}, 400)
}
}
}
private fun canWriteToExternalStorage(): Boolean {
return context?.let { AndroidPermissionsHelper.checkPermission(it, Manifest.permission.WRITE_EXTERNAL_STORAGE) }!!
}
private fun checkWritingPermission() {
activity?.let {
AndroidPermissionsHelper.requestPermission(it,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
AndroidPermissionsHelper.WRITE_EXTERNAL_STORAGE_CODE)
} }
} }
...@@ -756,10 +895,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -756,10 +895,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
private fun setupActionSnackbar() { private fun setupActionSnackbar() {
actionSnackbar = ActionSnackbar.make(message_list_container, parser = parser) actionSnackbar = ActionSnackbar.make(message_list_container, parser = parser)
actionSnackbar.cancelView.setOnClickListener({ actionSnackbar.cancelView.setOnClickListener {
clearMessageComposition() clearMessageComposition()
KeyboardHelper.showSoftKeyboard(text_message) KeyboardHelper.showSoftKeyboard(text_message)
}) }
} }
private fun subscribeComposeTextMessage() { private fun subscribeComposeTextMessage() {
...@@ -787,14 +926,14 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -787,14 +926,14 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
private fun setupComposeButtons(charSequence: CharSequence) { private fun setupComposeButtons(charSequence: CharSequence) {
if (charSequence.isNotEmpty() && playComposeMessageButtonsAnimation) { if (charSequence.isNotEmpty() && playComposeMessageButtonsAnimation) {
button_show_attachment_options.fadeOut(1F, 0F, 120) button_show_attachment_options.isVisible = false
button_send.fadeIn(0F, 1F, 120) button_send.isVisible = true
playComposeMessageButtonsAnimation = false playComposeMessageButtonsAnimation = false
} }
if (charSequence.isEmpty()) { if (charSequence.isEmpty()) {
button_send.fadeOut(1F, 0F, 120) button_send.isVisible = false
button_show_attachment_options.fadeIn(0F, 1F, 120) button_show_attachment_options.isVisible = true
playComposeMessageButtonsAnimation = true playComposeMessageButtonsAnimation = true
} }
} }
...@@ -808,7 +947,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -808,7 +947,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
private fun showAttachmentOptions() { private fun showAttachmentOptions() {
view_dim.setVisible(true) view_dim.isVisible = true
// Play anim. // Play anim.
button_show_attachment_options.rotateBy(45F) button_show_attachment_options.rotateBy(45F)
...@@ -820,10 +959,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -820,10 +959,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
button_show_attachment_options.rotateBy(-45F) button_show_attachment_options.rotateBy(-45F)
layout_message_attachment_options.circularRevealOrUnreveal(centerX, centerY, max, 0F) layout_message_attachment_options.circularRevealOrUnreveal(centerX, centerY, max, 0F)
view_dim.setVisible(false) view_dim.isVisible = false
} }
private fun setupToolbar(toolbarTitle: String) { private fun setupToolbar(toolbarTitle: String) {
(activity as ChatRoomActivity).showToolbarTitle(toolbarTitle) (activity as ChatRoomActivity).showToolbarTitle(toolbarTitle)
} }
} }
\ No newline at end of file
...@@ -25,8 +25,8 @@ dependencies { ...@@ -25,8 +25,8 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation libraries.appCompat implementation libraries.appCompat
implementation libraries.constraintLayout
implementation libraries.kotlin implementation libraries.kotlin
implementation libraries.constraintlayout
implementation libraries.androidKtx implementation libraries.androidKtx
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
......
package chat.rocket.android.draw;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("chat.rocket.android.draw.test", appContext.getPackageName());
}
}
package chat.rocket.android.draw package chat.rocket.android.draw
import android.app.Activity
import android.content.Intent
import android.content.res.Resources import android.content.res.Resources
import android.support.v7.app.AppCompatActivity import android.graphics.Bitmap
import android.os.Bundle import android.os.Bundle
import android.support.v4.content.res.ResourcesCompat
import android.view.View import android.view.View
import android.widget.SeekBar import android.widget.SeekBar
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import kotlinx.android.synthetic.main.activity_drawing.* import kotlinx.android.synthetic.main.activity_drawing.*
import kotlinx.android.synthetic.main.color_palette_view.* import kotlinx.android.synthetic.main.color_palette_view.*
import android.content.Intent
import android.graphics.Bitmap
import android.app.Activity
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
class DrawingActivity : AppCompatActivity() { class DrawingActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
......
package chat.rocket.android.draw.widget package chat.rocket.android.draw.widget
import android.content.Context import android.content.Context
import android.graphics.* import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet import android.util.AttributeSet
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View import android.view.View
import java.util.* import androidx.annotation.ColorInt
import android.support.v4.graphics.ColorUtils import androidx.core.graphics.ColorUtils
import android.support.annotation.ColorInt
class CustomDrawView(context: Context, attrs: AttributeSet) : View(context, attrs) { class CustomDrawView(context: Context, attrs: AttributeSet) : View(context, attrs) {
var mPaths = LinkedHashMap<MyPath, PaintOptions>() var mPaths = LinkedHashMap<MyPath, PaintOptions>()
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
...@@ -36,7 +37,7 @@ ...@@ -36,7 +37,7 @@
android:background="@color/color_white" android:background="@color/color_white"
android:foreground="?selectableItemBackgroundBorderless" /> android:foreground="?selectableItemBackgroundBorderless" />
<android.support.constraint.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/draw_tools" android:id="@+id/draw_tools"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
...@@ -155,6 +156,6 @@ ...@@ -155,6 +156,6 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/image_draw_eraser" /> app:layout_constraintTop_toBottomOf="@id/image_draw_eraser" />
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file \ No newline at end of file
package chat.rocket.android.draw;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}
\ No newline at end of file
include ':app', ':player', ':wear' include ':app', ':player', ':emoji', ':draw' //, ':wear'
\ No newline at end of file \ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment