Unverified Commit 98b0b8f6 authored by divyanshu bhargava's avatar divyanshu bhargava Committed by GitHub

Merge pull request #39 from RocketChat/develop

merge
parents 4a647259 47106322
......@@ -69,6 +69,8 @@ dependencies {
implementation project(':player')
implementation project(':emoji')
implementation project(':draw')
implementation project(':util')
implementation project(':core')
implementation libraries.kotlin
implementation libraries.coroutines
......
<?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="chat.rocket.android">
<uses-permission android:name="android.permission.INTERNET" />
......@@ -8,6 +9,7 @@
<application
android:name=".app.RocketChatApplication"
tools:replace="android:name"
android:allowBackup="true"
android:fullBackupContent="@xml/backup_config"
android:icon="@mipmap/ic_launcher"
......
......@@ -8,6 +8,7 @@ import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.*
import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.*
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatAuthException
......@@ -17,7 +18,6 @@ import chat.rocket.common.model.Token
import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.*
import com.google.android.gms.auth.api.credentials.Credential
import kotlinx.coroutines.experimental.delay
import timber.log.Timber
import java.util.concurrent.TimeUnit
......
......@@ -7,7 +7,7 @@ import chat.rocket.android.server.domain.*
import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.registerPushToken
import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.util.retryIO
......
......@@ -3,10 +3,9 @@ package chat.rocket.android.authentication.resetpassword.presentation
import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.server.domain.GetConnectingServerInteractor
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.isEmail
import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException
import chat.rocket.common.RocketChatInvalidResponseException
......
......@@ -10,10 +10,11 @@ import chat.rocket.android.server.domain.SaveConnectingServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.presentation.CheckServerPresenter
import chat.rocket.android.util.extensions.isValidUrl
import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.extension.launchUI
import javax.inject.Inject
class ServerPresenter @Inject constructor(private val view: ServerView,
class ServerPresenter @Inject constructor(
private val view: ServerView,
private val strategy: CancelStrategy,
private val navigator: AuthenticationNavigator,
private val serverInteractor: SaveConnectingServerInteractor,
......
......@@ -6,6 +6,7 @@ import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.*
import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.*
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException
......
......@@ -7,7 +7,7 @@ import chat.rocket.android.server.domain.*
import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.registerPushToken
import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.util.retryIO
......
......@@ -3,7 +3,6 @@ package chat.rocket.android.chatroom.domain
import android.content.Context
import android.net.Uri
import chat.rocket.android.util.extensions.*
import java.io.File
import javax.inject.Inject
class UriInteractor @Inject constructor(private val context: Context) {
......@@ -34,9 +33,4 @@ class UriInteractor @Inject constructor(private val context: Context) {
* Note: It should be an image.
*/
fun getBitmap(uri: Uri) = uri.getBitmpap(context)
/**
* Returns the Uri from the [File].
*/
fun getUri(file: File) = Uri.fromFile(file)
}
\ No newline at end of file
package chat.rocket.android.chatroom.presentation
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Environment
import chat.rocket.android.R
import chat.rocket.android.chatroom.adapter.AutoCompleteType
import chat.rocket.android.chatroom.adapter.PEOPLE
......@@ -33,9 +31,9 @@ import chat.rocket.android.server.domain.uploadMimeTypeFilter
import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infraestructure.state
import chat.rocket.android.util.extension.compressImageAndGetInputStream
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.getCompressFormat
import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException
import chat.rocket.common.model.RoomType
......@@ -77,11 +75,7 @@ import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.withContext
import org.threeten.bp.Instant
import timber.log.Timber
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.InputStream
import java.io.File
import java.io.FileOutputStream
import java.util.*
import javax.inject.Inject
......@@ -172,10 +166,12 @@ class ChatRoomPresenter @Inject constructor(
try {
if (offset == 0L) {
val localMessages = messagesRepository.getByRoomId(chatRoomId)
val oldMessages = mapper.map(localMessages, RoomUiModel(
val oldMessages = mapper.map(
localMessages, RoomUiModel(
roles = chatRoles,
isBroadcast = chatIsBroadcast, isRoom = true
))
)
)
if (oldMessages.isNotEmpty()) {
view.showMessages(oldMessages)
loadMissingMessages()
......@@ -259,10 +255,9 @@ class ChatRoomPresenter @Inject constructor(
messagesRepository.save(newMessage)
view.showNewMessage(
mapper.map(
newMessage, RoomUiModel(
roles = chatRoles, isBroadcast = chatIsBroadcast
)
)
newMessage,
RoomUiModel(roles = chatRoles, isBroadcast = chatIsBroadcast)
), false
)
client.sendMessage(id, chatRoomId, text)
} catch (ex: Exception) {
......@@ -317,10 +312,13 @@ class ChatRoomPresenter @Inject constructor(
var inputStream: InputStream? = uriInteractor.getInputStream(uri)
if (mimeType.contains("image")) {
compressImage(uri, mimeType)?.let {
uriInteractor.getBitmap(uri)?.let {
it.compressImageAndGetInputStream(mimeType)?.let {
inputStream = it
}
}
}
retryIO("uploadFile($roomId, $fileName, $mimeType") {
client.uploadFile(
roomId,
......@@ -347,23 +345,45 @@ class ChatRoomPresenter @Inject constructor(
}
}
// Returns an InputStream of a compressed image.
private suspend fun compressImage(uri: Uri, mimeType: String): InputStream? {
var inputStream: InputStream? = null
uriInteractor.getBitmap(uri)?.let {
fun uploadDrawingImage(roomId: String, byteArray: ByteArray, msg: String) {
launchUI(strategy) {
view.showLoading()
try {
withContext(DefaultDispatcher) {
val byteArrayOutputStream = ByteArrayOutputStream()
// TODO: Add an option the the app to the user be able to select the quality of the compressed image
val isCompressed =
it.compress(it.getCompressFormat(mimeType), 70, byteArrayOutputStream)
if (isCompressed) {
inputStream = ByteArrayInputStream(byteArrayOutputStream.toByteArray())
val fileName = UUID.randomUUID().toString() + ".png"
val fileSize = byteArray.size
val mimeType = "image/png"
val maxFileSizeAllowed = settings.uploadMaxFileSize()
when {
fileSize > maxFileSizeAllowed -> {
view.showInvalidFileSize(fileSize, maxFileSizeAllowed)
}
else -> {
retryIO("uploadFile($roomId, $fileName, $mimeType") {
client.uploadFile(
roomId,
fileName,
mimeType,
msg,
description = fileName
) {
byteArray.inputStream()
}
}
}
}
return inputStream
}
} catch (ex: Exception) {
Timber.d(ex, "Error uploading file")
when (ex) {
is RocketChatException -> view.showMessage(ex)
else -> view.showGenericErrorMessage()
}
} finally {
view.hideLoading()
}
}
}
fun sendTyping() {
......@@ -452,7 +472,7 @@ class ChatRoomPresenter @Inject constructor(
messagesRepository.saveAll(messages.result)
launchUI(strategy) {
view.showNewMessage(models)
view.showNewMessage(models, true)
}
if (messages.result.size == 50) {
......@@ -513,7 +533,8 @@ class ChatRoomPresenter @Inject constructor(
val id = msg.id
val username = msg.sender?.username ?: ""
val mention = if (mentionAuthor && currentUsername != username) "@$username" else ""
val room = if (roomTypeOf(roomType) is RoomType.DirectMessage) username else roomName
val room =
if (roomTypeOf(roomType) is RoomType.DirectMessage) username else roomName
val chatRoomType = when (roomTypeOf(roomType)) {
is RoomType.DirectMessage -> "direct"
is RoomType.PrivateGroup -> "group"
......@@ -524,10 +545,12 @@ class ChatRoomPresenter @Inject constructor(
view.showReplyingAction(
username = getDisplayName(msg.sender),
replyMarkdown = "[ ]($currentServer/$chatRoomType/$room?msg=$id) $mention ",
quotedMessage = mapper.map(message, RoomUiModel(
quotedMessage = mapper.map(
message, RoomUiModel(
roles = chatRoles,
isBroadcast = chatIsBroadcast
)).last().preview?.message ?: ""
)
).last().preview?.message ?: ""
)
}
}
......@@ -903,12 +926,6 @@ class ChatRoomPresenter @Inject constructor(
}
}
fun getDrawingImageUri(byteArray: ByteArray): Uri {
val bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)
val file = saveDrawingImage(bitmap)
return uriInteractor.getUri(file)
}
private suspend fun subscribeTypingStatus() {
launch(CommonPool + strategy.jobs) {
client.subscribeTypingStatus(chatRoomId.toString()) { _, id ->
......@@ -957,9 +974,11 @@ class ChatRoomPresenter @Inject constructor(
private fun updateMessage(streamedMessage: Message) {
launchUI(strategy) {
val viewModelStreamedMessage = mapper.map(streamedMessage, RoomUiModel(
val viewModelStreamedMessage = mapper.map(
streamedMessage, RoomUiModel(
roles = chatRoles, isBroadcast = chatIsBroadcast
))
)
)
val roomMessages = messagesRepository.getByRoomId(streamedMessage.roomId)
val index = roomMessages.indexOfFirst { msg -> msg.id == streamedMessage.id }
......@@ -970,21 +989,8 @@ class ChatRoomPresenter @Inject constructor(
} else {
Timber.d("Adding new message")
messagesRepository.save(streamedMessage)
view.showNewMessage(viewModelStreamedMessage)
}
view.showNewMessage(viewModelStreamedMessage, true)
}
}
private fun saveDrawingImage(bitmap: Bitmap): File {
val imageDir = "${Environment.DIRECTORY_PICTURES}/Rocket.Chat Images/"
val path = Environment.getExternalStoragePublicDirectory(imageDir)
val file = File(path, UUID.randomUUID().toString()+".png")
path.mkdirs()
file.createNewFile()
val outputStream = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.PNG,70, outputStream)
outputStream.flush()
outputStream.close()
return file
}
}
\ No newline at end of file
......@@ -50,14 +50,6 @@ interface ChatRoomView : LoadingView, MessageView {
*/
fun showFileSelection(filter: Array<String>?)
/**
* Uploads a file to a chat room.
*
* @param uri The file URI to send.
* @param msg Message to send with attachments
*/
fun uploadFile(uri: Uri, msg: String)
/**
* Shows a invalid file message.
*/
......@@ -68,7 +60,7 @@ interface ChatRoomView : LoadingView, MessageView {
*
* @param message The (recent) message sent to a chat room.
*/
fun showNewMessage(message: List<BaseUiModel<*>>)
fun showNewMessage(message: List<BaseUiModel<*>>, isMessageReceived: Boolean)
/**
* Dispatch to the recycler views adapter that we should remove a message.
......@@ -119,7 +111,7 @@ interface ChatRoomView : LoadingView, MessageView {
/**
* Clears the message composition.
*/
fun clearMessageComposition()
fun clearMessageComposition(deleteMessage: Boolean)
fun showInvalidFileSize(fileSize: Int, maxFileSize: Int)
......
package chat.rocket.android.chatroom.ui
import android.graphics.drawable.Drawable
import android.net.Uri
import androidx.core.view.isVisible
import chat.rocket.android.util.extensions.getFileName
import chat.rocket.android.util.extensions.getMimeType
fun ChatRoomFragment.showFileAttachmentDialog(uri: Uri) {
activity?.let { fragmentActivity ->
uri.getMimeType(fragmentActivity).let { mimeType ->
description.text.clear()
when {
mimeType.startsWith("image") -> {
imagePreview.isVisible = true
imagePreview.setImageURI(uri)
}
mimeType.startsWith("video") -> {
audioVideoAttachment.isVisible = true
}
else -> {
textFile.isVisible = true
textFile.text = uri.getFileName(fragmentActivity)
}
}
}
}
sendButton.setOnClickListener {
presenter.uploadFile(chatRoomId, uri, (citation ?: "") + description.text.toString())
alertDialog.dismiss()
}
cancelButton.setOnClickListener { alertDialog.dismiss() }
alertDialog.show()
}
fun ChatRoomFragment.showDrawAttachmentDialog(byteArray: ByteArray) {
description.text.clear()
imagePreview.isVisible = true
imagePreview.setImageDrawable(Drawable.createFromStream(byteArray.inputStream(), ""))
sendButton.setOnClickListener {
presenter.uploadDrawingImage(
chatRoomId,
byteArray,
(citation ?: "") + description.text.toString()
)
alertDialog.dismiss()
}
cancelButton.setOnClickListener { alertDialog.dismiss() }
alertDialog.show()
}
\ No newline at end of file
......@@ -12,7 +12,7 @@ import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.server.domain.useSpecialCharsOnRoom
import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.server.infraestructure.ConnectionManager
import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException
import chat.rocket.common.model.RoomType
......
......@@ -5,7 +5,7 @@ import chat.rocket.android.main.presentation.MainNavigator
import chat.rocket.android.members.uimodel.MemberUiModelMapper
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.extension.launchUI
import chat.rocket.common.RocketChatException
import chat.rocket.common.model.RoomType
import chat.rocket.common.util.ifNull
......
......@@ -14,6 +14,8 @@ import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.chatrooms.di.ChatRoomsFragmentProvider
import chat.rocket.android.createchannel.di.CreateChannelProvider
import chat.rocket.android.dagger.scope.PerActivity
import chat.rocket.android.draw.main.di.DrawModule
import chat.rocket.android.draw.main.ui.DrawingActivity
import chat.rocket.android.favoritemessages.di.FavoriteMessagesFragmentProvider
import chat.rocket.android.files.di.FilesFragmentProvider
import chat.rocket.android.main.di.MainModule
......@@ -78,4 +80,8 @@ abstract class ActivityBuilder {
@PerActivity
@ContributesAndroidInjector(modules = [ChangeServerModule::class])
abstract fun bindChangeServerActivity(): ChangeServerActivity
@PerActivity
@ContributesAndroidInjector(modules = [DrawModule::class])
abstract fun bindDrawingActivity(): DrawingActivity
}
\ No newline at end of file
......@@ -4,7 +4,7 @@ import chat.rocket.android.chatroom.uimodel.UiModelMapper
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.extension.launchUI
import chat.rocket.common.RocketChatException
import chat.rocket.common.model.roomTypeOf
import chat.rocket.common.util.ifNull
......
......@@ -6,7 +6,7 @@ import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.files.uimodel.FileUiModel
import chat.rocket.android.files.uimodel.FileUiModelMapper
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.extension.launchUI
import chat.rocket.common.RocketChatException
import chat.rocket.common.model.roomTypeOf
import chat.rocket.common.util.ifNull
......
package chat.rocket.android.main.di
import androidx.lifecycle.LifecycleOwner
import android.content.Context
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerActivity
import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.main.presentation.MainNavigator
import chat.rocket.android.main.presentation.MainView
import chat.rocket.android.main.ui.MainActivity
......@@ -30,5 +28,6 @@ class MainModule {
fun provideLifecycleOwner(activity: MainActivity): LifecycleOwner = activity
@Provides
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy = CancelStrategy(owner, jobs)
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy =
CancelStrategy(owner, jobs)
}
\ No newline at end of file
......@@ -17,7 +17,7 @@ import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.presentation.CheckServerPresenter
import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.registerPushToken
import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.util.retryIO
......
......@@ -5,7 +5,7 @@ import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.members.uimodel.MemberUiModel
import chat.rocket.android.members.uimodel.MemberUiModelMapper
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.extension.launchUI
import chat.rocket.common.RocketChatException
import chat.rocket.common.model.roomTypeOf
import chat.rocket.common.util.ifNull
......
......@@ -3,7 +3,7 @@ package chat.rocket.android.mentions.presentention
import chat.rocket.android.chatroom.uimodel.UiModelMapper
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.extension.launchUI
import chat.rocket.common.RocketChatException
import chat.rocket.common.util.ifNull
import chat.rocket.core.internal.rest.getMentions
......
......@@ -4,7 +4,7 @@ import chat.rocket.android.chatroom.uimodel.UiModelMapper
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.extension.launchUI
import chat.rocket.common.RocketChatException
import chat.rocket.common.model.roomTypeOf
import chat.rocket.common.util.ifNull
......
......@@ -4,8 +4,8 @@ import chat.rocket.android.core.behaviours.showMessage
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException
import chat.rocket.common.util.ifNull
......@@ -15,10 +15,12 @@ import chat.rocket.core.internal.rest.setAvatar
import chat.rocket.core.internal.rest.updateProfile
import javax.inject.Inject
class ProfilePresenter @Inject constructor(private val view: ProfileView,
class ProfilePresenter @Inject constructor(
private val view: ProfileView,
private val strategy: CancelStrategy,
serverInteractor: GetCurrentServerInteractor,
factory: RocketChatClientFactory) {
factory: RocketChatClientFactory
) {
private val serverUrl = serverInteractor.get()!!
private val client: RocketChatClient = factory.create(serverUrl)
private lateinit var myselfId: String
......@@ -55,11 +57,14 @@ class ProfilePresenter @Inject constructor(private val view: ProfileView,
launchUI(strategy) {
view.showLoading()
try {
if(avatarUrl!="") {
if (avatarUrl != "") {
retryIO { client.setAvatar(avatarUrl) }
}
val user = retryIO { client.updateProfile(
userId = myselfId, email = email, name = name, username = username) }
val user = retryIO {
client.updateProfile(
userId = myselfId, email = email, name = name, username = username
)
}
view.showProfileUpdateSuccessfullyMessage()
loadUserProfile()
} catch (exception: RocketChatException) {
......
......@@ -4,7 +4,7 @@ import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.*
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.extension.launchUI
import chat.rocket.common.util.ifNull
import javax.inject.Inject
......
......@@ -5,15 +5,13 @@ import chat.rocket.android.authentication.server.presentation.VersionCheckView
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.VersionInfo
import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatInvalidProtocolException
import chat.rocket.common.model.ServerInfo
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.serverInfo
import kotlinx.coroutines.experimental.Deferred
import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.async
import timber.log.Timber
abstract class CheckServerPresenter constructor(private val strategy: CancelStrategy,
......@@ -50,7 +48,7 @@ abstract class CheckServerPresenter constructor(private val strategy: CancelStra
}
} catch (ex: Exception) {
Timber.d(ex, "Error getting server info")
when(ex) {
when (ex) {
is RocketChatInvalidProtocolException -> {
view.errorInvalidProtocol()
}
......
......@@ -3,7 +3,7 @@ package chat.rocket.android.settings.password.presentation
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException
import chat.rocket.core.RocketChatClient
......
package chat.rocket.android.util.extensions
import android.graphics.Bitmap
fun Bitmap.getCompressFormat(mimeType: String): Bitmap.CompressFormat {
return when {
mimeType.contains("jpeg") -> Bitmap.CompressFormat.JPEG
mimeType.contains("webp") -> Bitmap.CompressFormat.WEBP
else -> Bitmap.CompressFormat.PNG
}
}
\ No newline at end of file
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/colorAccent" />
<size
android:width="20dp"
android:height="20dp" />
</shape>
\ No newline at end of file
......@@ -10,18 +10,40 @@
android:layout_height="match_parent"
android:scrollbars="vertical" />
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
app:layout_anchor="@id/recycler_view"
app:layout_anchorGravity="bottom|end">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/button_fab"
android:layout_alignParentEnd="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible"
android:layout_margin="16dp"
android:src="@drawable/ic_arrow_downward_24dp"
android:theme="@style/Theme.AppCompat"
android:tint="@color/actionMenuColor"
android:visibility="invisible"
app:backgroundTint="@color/colorWhite"
app:fabSize="mini"
app:layout_anchor="@id/recycler_view"
app:layout_anchorGravity="bottom|end" />
app:fabSize="mini" />
<TextView
android:id="@+id/text_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:elevation="7dp"
android:padding="2dp"
android:gravity="center"
android:layout_margin="10dp"
android:textColor="@color/colorWhite"
android:visibility="invisible"
android:layout_alignParentEnd="true"
android:textSize="@dimen/message_time_text_size"
android:background="@drawable/round_textview" />
</RelativeLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file
......@@ -5,8 +5,8 @@ buildscript {
repositories {
google()
jcenter()
maven { url 'https://maven.fabric.io/public' }
mavenCentral()
maven { url 'https://maven.fabric.io/public' }
}
dependencies {
......
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion versions.compileSdk
defaultConfig {
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
versionCode 1
versionName "1.0.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation libraries.kotlin
implementation libraries.coroutines
implementation libraries.lifecycleExtensions
kapt libraries.lifecycleCompiler
}
\ No newline at end of file
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="chat.rocket.android.core" />
......@@ -5,9 +5,8 @@ import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.OnLifecycleEvent
import kotlinx.coroutines.experimental.Job
import javax.inject.Inject
class CancelStrategy @Inject constructor(owner: LifecycleOwner, val jobs: Job) : LifecycleObserver {
class CancelStrategy(owner: LifecycleOwner, val jobs: Job) : LifecycleObserver {
init {
owner.lifecycle.addObserver(this)
......
<resources>
<string name="app_name">Core</string>
</resources>
......@@ -22,7 +22,7 @@ ext {
playServices : '15.0.0',
firebase : '15.0.0',
room : '2.0.0-alpha1',
lifecycle : '2.0.0-alpha1',
lifecycle : '2.0.0-beta01',
rxKotlin : '2.2.0',
rxAndroid : '2.0.2',
moshi : '1.6.0',
......
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion versions.compileSdk
buildToolsVersion versions.buildTools
defaultConfig {
minSdkVersion 21
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
versionName "1.0.0"
}
buildTypes {
......@@ -18,21 +20,24 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation libraries.appCompat
implementation project(':util')
implementation project(':core')
implementation libraries.kotlin
implementation libraries.coroutines
implementation libraries.appCompat
implementation libraries.constraintlayout
implementation libraries.androidKtx
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation libraries.dagger
implementation libraries.daggerSupport
kapt libraries.daggerProcessor
kapt libraries.daggerAndroidApt
}
\ No newline at end of file
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
\ No newline at end of file
......@@ -2,9 +2,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="chat.rocket.android.draw">
<application>
<application android:name=".DrawApplication">
<activity
android:name=".DrawingActivity"
android:name=".main.ui.DrawingActivity"
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
</application>
......
package chat.rocket.android.draw
import chat.rocket.android.draw.dagger.DaggerAppComponent
import dagger.android.AndroidInjector
import dagger.android.support.DaggerApplication
class DrawApplication : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out DaggerApplication> =
DaggerAppComponent.builder().create(this)
}
\ No newline at end of file
package chat.rocket.android.draw.dagger
import chat.rocket.android.draw.dagger.module.ActivityBuilderModule
import dagger.Component
import dagger.android.AndroidInjector
import dagger.android.support.AndroidSupportInjectionModule
import dagger.android.support.DaggerApplication
@Component(modules = [AndroidSupportInjectionModule::class, ActivityBuilderModule::class])
interface AppComponent : AndroidInjector<DaggerApplication> {
@Component.Builder
abstract class Builder : AndroidInjector.Builder<DaggerApplication>()
}
\ No newline at end of file
package chat.rocket.android.draw.dagger.module
import chat.rocket.android.draw.main.di.DrawModule
import chat.rocket.android.draw.main.ui.DrawingActivity
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module
abstract class ActivityBuilderModule {
@ContributesAndroidInjector(modules = [DrawModule::class])
abstract fun contributeDrawingActivityInjector(): DrawingActivity
}
\ No newline at end of file
package chat.rocket.android.draw.main.di
import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.draw.main.presenter.DrawView
import chat.rocket.android.draw.main.ui.DrawingActivity
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module
class DrawModule {
@Provides
fun provideMainView(activity: DrawingActivity): DrawView = activity
@Provides
fun provideJob() = Job()
@Provides
fun provideLifecycleOwner(activity: DrawingActivity): LifecycleOwner = activity
@Provides
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy =
CancelStrategy(owner, jobs)
}
\ No newline at end of file
package chat.rocket.android.draw.main.presenter
import android.graphics.Bitmap
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.util.extension.compressImageAndGetByteArray
import chat.rocket.android.util.extension.launchUI
import javax.inject.Inject
class DrawPresenter @Inject constructor(
private val view: DrawView,
private val strategy: CancelStrategy
) {
fun processDrawingImage(bitmap: Bitmap) {
launchUI(strategy) {
val byteArray = bitmap.compressImageAndGetByteArray("image/png")
if (byteArray != null) {
view.sendByteArray(byteArray)
} else {
view.showWrongProcessingMessage()
}
}
}
}
\ No newline at end of file
package chat.rocket.android.draw.main.presenter
interface DrawView {
/**
* Sends the [ByteArray] of the processed draw image (compressed).
*/
fun sendByteArray(byteArray: ByteArray)
/**
* Shows a message indicating that something was wrong while processing the draw image.
*/
fun showWrongProcessingMessage()
}
\ No newline at end of file
package chat.rocket.android.draw
package chat.rocket.android.draw.main.ui
import android.app.Activity
import android.content.Intent
import android.content.res.Resources
import android.graphics.Bitmap
import android.os.Bundle
import android.view.View
import android.widget.SeekBar
import androidx.appcompat.app.AppCompatActivity
import android.widget.Toast
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.isVisible
import chat.rocket.android.draw.R
import chat.rocket.android.draw.main.presenter.DrawPresenter
import chat.rocket.android.draw.main.presenter.DrawView
import dagger.android.support.DaggerAppCompatActivity
import kotlinx.android.synthetic.main.activity_drawing.*
import kotlinx.android.synthetic.main.color_palette_view.*
import java.io.ByteArrayOutputStream
import javax.inject.Inject
class DrawingActivity : AppCompatActivity() {
const val DRAWING_BYTE_ARRAY_EXTRA_DATA: String = "chat.rocket.android.DrawingByteArray"
class DrawingActivity : DaggerAppCompatActivity(), DrawView {
@Inject
lateinit var presenter: DrawPresenter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_drawing)
image_close_drawing.setOnClickListener {
finish()
setupListeners()
setupDrawTools()
colorSelector()
setPaintAlpha()
setPaintWidth()
}
image_send_drawing.setOnClickListener {
val bStream = ByteArrayOutputStream()
val bitmap = custom_draw_view.getBitmap()
bitmap.compress(Bitmap.CompressFormat.PNG, 70, bStream)
val byteArray = bStream.toByteArray()
val returnIntent = Intent()
returnIntent.putExtra("bitmap", byteArray)
setResult(Activity.RESULT_OK,returnIntent)
override fun sendByteArray(byteArray: ByteArray) {
setResult(Activity.RESULT_OK, Intent().putExtra(DRAWING_BYTE_ARRAY_EXTRA_DATA, byteArray))
finish()
}
setUpDrawTools()
colorSelector()
override fun showWrongProcessingMessage() {
Toast.makeText(this, getText(R.string.msg_wrong_processing_draw_image), Toast.LENGTH_SHORT)
.show()
}
setPaintAlpha()
private fun setupListeners() {
image_close_drawing.setOnClickListener { finish() }
setPaintWidth()
image_send_drawing.setOnClickListener {
presenter.processDrawingImage(custom_draw_view.getBitmap())
}
}
private fun setUpDrawTools() {
private fun setupDrawTools() {
image_draw_eraser.setOnClickListener {
custom_draw_view.clearCanvas()
toggleDrawTools(draw_tools,false)
toggleDrawTools(draw_tools, false)
}
image_draw_width.setOnClickListener {
if (draw_tools.translationY == (56).toPx){
toggleDrawTools(draw_tools,true)
}else if (draw_tools.translationY == (0).toPx && seekBar_width.isVisible){
toggleDrawTools(draw_tools,false)
if (draw_tools.translationY == (56).toPx) {
toggleDrawTools(draw_tools, true)
} else if (draw_tools.translationY == (0).toPx && seekBar_width.isVisible) {
toggleDrawTools(draw_tools, false)
}
seekBar_width.isVisible = true
seekBar_opacity.isVisible = false
draw_color_palette.isVisible = false
}
image_draw_opacity.setOnClickListener {
if (draw_tools.translationY == (56).toPx){
toggleDrawTools(draw_tools,true)
}else if (draw_tools.translationY == (0).toPx && seekBar_opacity.isVisible){
toggleDrawTools(draw_tools,false)
if (draw_tools.translationY == (56).toPx) {
toggleDrawTools(draw_tools, true)
} else if (draw_tools.translationY == (0).toPx && seekBar_opacity.isVisible) {
toggleDrawTools(draw_tools, false)
}
seekBar_width.isVisible = false
seekBar_opacity.isVisible = true
draw_color_palette.isVisible = false
}
image_draw_color.setOnClickListener {
if (draw_tools.translationY == (56).toPx){
toggleDrawTools(draw_tools,true)
}else if (draw_tools.translationY == (0).toPx && draw_color_palette.isVisible){
toggleDrawTools(draw_tools,false)
if (draw_tools.translationY == (56).toPx) {
toggleDrawTools(draw_tools, true)
} else if (draw_tools.translationY == (0).toPx && draw_color_palette.isVisible) {
toggleDrawTools(draw_tools, false)
}
seekBar_width.isVisible = false
seekBar_opacity.isVisible = false
draw_color_palette.isVisible = true
}
image_draw_undo.setOnClickListener {
custom_draw_view.undo()
toggleDrawTools(draw_tools,false)
toggleDrawTools(draw_tools, false)
}
image_draw_redo.setOnClickListener {
custom_draw_view.redo()
toggleDrawTools(draw_tools,false)
toggleDrawTools(draw_tools, false)
}
}
private fun toggleDrawTools(view: View, showView: Boolean = true) {
if (showView){
if (showView) {
view.animate().translationY((0).toPx)
}else{
} else {
view.animate().translationY((56).toPx)
}
}
private fun colorSelector() {
image_color_black.setOnClickListener {
custom_draw_view.setColor(ResourcesCompat.getColor(resources, R.color.color_black,null))
scaleColorView(image_color_black)
}
image_color_red.setOnClickListener {
custom_draw_view.setColor(ResourcesCompat.getColor(resources, R.color.color_red,null))
custom_draw_view.setColor(
ResourcesCompat.getColor(resources, R.color.color_red, null)
)
scaleColorView(image_color_red)
}
image_color_yellow.setOnClickListener {
custom_draw_view.setColor(ResourcesCompat.getColor(resources, R.color.color_yellow,null))
custom_draw_view.setColor(
ResourcesCompat.getColor(
resources,
R.color.color_yellow, null
)
)
scaleColorView(image_color_yellow)
}
image_color_green.setOnClickListener {
custom_draw_view.setColor(ResourcesCompat.getColor(resources, R.color.color_green,null))
custom_draw_view.setColor(
ResourcesCompat.getColor(
resources,
R.color.color_green, null
)
)
scaleColorView(image_color_green)
}
image_color_blue.setOnClickListener {
custom_draw_view.setColor(ResourcesCompat.getColor(resources, R.color.color_blue,null))
custom_draw_view.setColor(
ResourcesCompat.getColor(resources, R.color.color_blue, null)
)
scaleColorView(image_color_blue)
}
image_color_pink.setOnClickListener {
custom_draw_view.setColor(ResourcesCompat.getColor(resources, R.color.color_pink,null))
custom_draw_view.setColor(
ResourcesCompat.getColor(
resources,
R.color.color_pink, null
)
)
scaleColorView(image_color_pink)
}
image_color_brown.setOnClickListener {
custom_draw_view.setColor(ResourcesCompat.getColor(resources, R.color.color_brown,null))
custom_draw_view.setColor(
ResourcesCompat.getColor(
resources,
R.color.color_brown, null
)
)
scaleColorView(image_color_brown)
}
}
......@@ -156,7 +195,7 @@ class DrawingActivity : AppCompatActivity() {
}
private fun setPaintWidth() {
seekBar_width.setOnSeekBarChangeListener(object: SeekBar.OnSeekBarChangeListener{
seekBar_width.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
custom_draw_view.setStrokeWidth(progress.toFloat())
}
......@@ -168,7 +207,7 @@ class DrawingActivity : AppCompatActivity() {
}
private fun setPaintAlpha() {
seekBar_opacity.setOnSeekBarChangeListener(object: SeekBar.OnSeekBarChangeListener{
seekBar_opacity.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
custom_draw_view.setAlpha(progress)
}
......
......@@ -6,5 +6,4 @@ import java.io.Serializable
interface Action : Serializable {
fun perform(path: Path)
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".DrawingActivity">
tools:context=".ui.DrawingActivity">
<chat.rocket.android.draw.widget.CustomDrawView
android:id="@+id/custom_draw_view"
......@@ -17,143 +16,143 @@
android:id="@+id/image_close_drawing"
android:layout_width="56dp"
android:layout_height="56dp"
android:background="@color/color_white"
android:foreground="?selectableItemBackgroundBorderless"
android:padding="16dp"
android:src="@drawable/ic_close_black_24dp"
android:tint="@color/icon_color"
android:padding="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:background="@color/color_white"
android:foreground="?selectableItemBackgroundBorderless" />
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/image_send_drawing"
android:layout_width="56dp"
android:layout_height="56dp"
android:src="@drawable/ic_send_black_24dp"
android:background="@color/color_white"
android:foreground="?selectableItemBackgroundBorderless"
android:padding="16dp"
android:src="@drawable/ic_send_black_24dp"
android:tint="@color/colorAccent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:background="@color/color_white"
android:foreground="?selectableItemBackgroundBorderless" />
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/draw_tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:background="@color/color_white"
android:elevation="4dp"
android:translationY="56dp" >
android:translationY="56dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<ImageView
android:id="@+id/image_draw_eraser"
android:layout_width="56dp"
android:layout_height="56dp"
android:src="@drawable/ic_eraser_black_24dp"
android:foreground="?selectableItemBackground"
android:padding="16dp"
android:src="@drawable/ic_eraser_black_24dp"
android:tint="@color/icon_color"
android:foreground="?selectableItemBackground"
app:layout_constraintEnd_toStartOf="@id/image_draw_width"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/image_draw_width"
app:layout_constraintTop_toTopOf="parent"/>
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/image_draw_width"
android:layout_width="56dp"
android:layout_height="56dp"
android:src="@drawable/ic_adjust_black_24dp"
android:foreground="?selectableItemBackground"
android:padding="16dp"
android:src="@drawable/ic_adjust_black_24dp"
android:tint="@color/icon_color"
android:foreground="?selectableItemBackground"
app:layout_constraintStart_toEndOf="@id/image_draw_eraser"
app:layout_constraintEnd_toStartOf="@id/image_draw_color"
app:layout_constraintTop_toTopOf="parent"/>
app:layout_constraintStart_toEndOf="@id/image_draw_eraser"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/image_draw_color"
android:layout_width="56dp"
android:layout_height="56dp"
android:src="@drawable/ic_color_lens_black_24dp"
android:foreground="?selectableItemBackground"
android:padding="16dp"
android:src="@drawable/ic_color_lens_black_24dp"
android:tint="@color/icon_color"
android:foreground="?selectableItemBackground"
app:layout_constraintStart_toEndOf="@id/image_draw_width"
app:layout_constraintEnd_toStartOf="@id/image_draw_opacity"
app:layout_constraintTop_toTopOf="parent"/>
app:layout_constraintStart_toEndOf="@id/image_draw_width"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/image_draw_opacity"
android:layout_width="56dp"
android:layout_height="56dp"
android:src="@drawable/ic_opacity_black_24dp"
android:foreground="?selectableItemBackground"
android:padding="16dp"
android:src="@drawable/ic_opacity_black_24dp"
android:tint="@color/icon_color"
android:foreground="?selectableItemBackground"
app:layout_constraintStart_toEndOf="@id/image_draw_color"
app:layout_constraintEnd_toStartOf="@id/image_draw_undo"
app:layout_constraintTop_toTopOf="parent"/>
app:layout_constraintStart_toEndOf="@id/image_draw_color"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/image_draw_undo"
android:layout_width="56dp"
android:layout_height="56dp"
android:src="@drawable/ic_undo_black_24dp"
android:foreground="?selectableItemBackground"
android:padding="16dp"
android:src="@drawable/ic_undo_black_24dp"
android:tint="@color/icon_color"
android:foreground="?selectableItemBackground"
app:layout_constraintStart_toEndOf="@id/image_draw_opacity"
app:layout_constraintEnd_toStartOf="@id/image_draw_redo"
app:layout_constraintStart_toEndOf="@id/image_draw_opacity"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/image_draw_redo"
android:layout_width="56dp"
android:layout_height="56dp"
android:src="@drawable/ic_redo_black_24dp"
android:foreground="?selectableItemBackground"
android:padding="16dp"
android:src="@drawable/ic_redo_black_24dp"
android:tint="@color/icon_color"
android:foreground="?selectableItemBackground"
app:layout_constraintStart_toEndOf="@id/image_draw_undo"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/image_draw_undo"
app:layout_constraintTop_toTopOf="parent" />
<SeekBar
android:id="@+id/seekBar_width"
android:layout_width="0dp"
android:layout_height="56dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/image_draw_eraser"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:progress="8"
android:progressTint="@color/colorAccent"
android:thumbTint="@color/colorAccent"
android:progress="8"
android:paddingStart="16dp"
android:paddingEnd="16dp" />
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/image_draw_eraser" />
<SeekBar
android:id="@+id/seekBar_opacity"
android:layout_width="0dp"
android:layout_height="56dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/image_draw_eraser"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:progress="100"
android:progressTint="@color/colorAccent"
android:thumbTint="@color/colorAccent"
android:paddingStart="16dp"
android:paddingEnd="16dp" />
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/image_draw_eraser" />
<include
android:id="@+id/draw_color_palette"
layout="@layout/color_palette_view"
android:layout_width="0dp"
android:layout_height="56dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/image_draw_eraser" />
</androidx.constraintlayout.widget.ConstraintLayout>
......
<resources>
<string name="app_name">Draw</string>
// TODO: Add other translations like we did for the app module.
<string name="msg_wrong_processing_draw_image">Something was wrong while processing the draw image. Please, try again later.</string>
</resources>
include ':app', ':player', ':emoji', ':draw' //, ':wear'
\ No newline at end of file
include ':app', ':player', ':emoji', ':draw', ':util', ':core' //, ':wear'
\ No newline at end of file
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion versions.compileSdk
defaultConfig {
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
versionCode 1
versionName "1.0.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':core')
implementation libraries.kotlin
implementation libraries.coroutines
implementation libraries.coroutinesAndroid
// TODO This is a dependency from the core module, but the util module are unable to get that dependencies. Check why it is occurring since transitive is enable by default
implementation libraries.lifecycleExtensions
kapt libraries.lifecycleCompiler
}
\ No newline at end of file
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="chat.rocket.android.util" />
package chat.rocket.android.util.extensions
package chat.rocket.android.util.extension
import chat.rocket.android.core.lifecycle.CancelStrategy
import kotlinx.coroutines.experimental.CoroutineScope
......
package chat.rocket.android.util.extension
import android.graphics.Bitmap
import kotlinx.coroutines.experimental.DefaultDispatcher
import kotlinx.coroutines.experimental.withContext
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.InputStream
/**
* Compress a [Bitmap] image.
*
* @param mimeType The MimeType of what the compressed image should be.
* @return An [InputStream] of a compressed image, otherwise null if the compression couldn't be done.
*/
suspend fun Bitmap.compressImageAndGetInputStream(mimeType: String): InputStream? {
var inputStream: InputStream? = null
withContext(DefaultDispatcher) {
val byteArrayOutputStream = ByteArrayOutputStream()
// TODO: Add an option the the app to the user be able to select the quality of the compressed image
val isCompressed =
this.compress(mimeType.getCompressFormat(), 70, byteArrayOutputStream)
if (isCompressed) {
inputStream = ByteArrayInputStream(byteArrayOutputStream.toByteArray())
}
}
return inputStream
}
/**
* Compress a [Bitmap] image.
*
* @param mimeType The MimeType of what the compressed image should be.
* @return An [ByteArray] of a compressed image, otherwise null if the compression couldn't be done.
*/
suspend fun Bitmap.compressImageAndGetByteArray(mimeType: String): ByteArray? {
var byteArray: ByteArray? = null
withContext(DefaultDispatcher) {
val byteArrayOutputStream = ByteArrayOutputStream()
// TODO: Add an option the the app to the user be able to select the quality of the compressed image
val isCompressed =
this.compress(mimeType.getCompressFormat(), 70, byteArrayOutputStream)
if (isCompressed) {
byteArray = byteArrayOutputStream.toByteArray()
}
}
return byteArray
}
/**
* Gets the [Bitmap.CompressFormat] based on the image MimeType.
* Note: Supported formats are: PNG, JPEG and WEBP.
*/
fun String.getCompressFormat(): Bitmap.CompressFormat {
return when {
this.contains("jpeg") -> Bitmap.CompressFormat.JPEG
this.contains("webp") -> Bitmap.CompressFormat.WEBP
else -> Bitmap.CompressFormat.PNG
}
}
\ No newline at end of file
<resources>
<string name="app_name">Util</string>
</resources>
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