Skip to content

Commit

Permalink
Added limits and some user growth features. (#284)
Browse files Browse the repository at this point in the history
  • Loading branch information
jorgeblacio authored and not-gabriel committed Oct 3, 2018
1 parent b64cd64 commit efb5123
Show file tree
Hide file tree
Showing 26 changed files with 187 additions and 35 deletions.
4 changes: 3 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,13 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"

// instrumentation tests
implementation 'com.google.firebase:firebase-messaging:17.0.0'
androidTestImplementation 'com.android.support:support-annotations:26.1.0'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test:rules:1.0.2'

//Firebase
implementation 'com.google.firebase:firebase-messaging:17.0.0'

// Room
implementation 'android.arch.persistence.room:runtime:1.1.1'
implementation 'android.arch.persistence.room:rxjava2:1.1.1'
Expand Down
23 changes: 14 additions & 9 deletions src/main/kotlin/com/criptext/mail/BaseActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,17 @@ import com.criptext.mail.scenes.mailbox.MailboxActivity
import com.criptext.mail.scenes.mailbox.MailboxSceneModel
import com.criptext.mail.scenes.params.*
import com.criptext.mail.scenes.search.SearchSceneModel
import com.criptext.mail.scenes.settings.SettingsActivity
import com.criptext.mail.scenes.settings.SettingsModel
import com.criptext.mail.scenes.settings.changepassword.ChangePasswordActivity
import com.criptext.mail.scenes.settings.changepassword.ChangePasswordModel
import com.criptext.mail.scenes.settings.recovery_email.RecoveryEmailModel
import com.criptext.mail.scenes.settings.signature.SignatureModel
import com.criptext.mail.scenes.signin.SignInActivity
import com.criptext.mail.scenes.signin.SignInSceneModel
import com.criptext.mail.scenes.signup.SignUpActivity
import com.criptext.mail.scenes.signup.SignUpSceneModel
import com.criptext.mail.services.MessagingInstance
import com.criptext.mail.utils.PhotoUtil
import com.criptext.mail.utils.UIMessage
import com.criptext.mail.utils.compat.PermissionUtilsCompat
Expand All @@ -36,12 +41,6 @@ import droidninja.filepicker.FilePickerBuilder
import uk.co.chrisjenx.calligraphy.CalligraphyContextWrapper
import java.io.File
import java.util.*
import com.criptext.mail.scenes.settings.SettingsActivity
import com.criptext.mail.scenes.settings.changepassword.ChangePasswordActivity
import com.criptext.mail.scenes.settings.changepassword.ChangePasswordModel
import com.criptext.mail.scenes.settings.recovery_email.RecoveryEmailActivity
import com.criptext.mail.scenes.settings.recovery_email.RecoveryEmailModel
import com.criptext.mail.services.MessagingInstance


/**
Expand Down Expand Up @@ -221,14 +220,14 @@ abstract class BaseActivity: AppCompatActivity(), IHostActivity {
when(params){
is ExternalActivityParams.FilePicker -> {
FilePickerBuilder.getInstance()
.setMaxCount(5)
.setMaxCount(params.remaining)
.setActivityTheme(R.style.PickerTheme)
.pickFile(this)
}
is ExternalActivityParams.ImagePicker -> {
FilePickerBuilder.getInstance()
.enableVideoPicker(true)
.setMaxCount(10)
.setMaxCount(params.remaining)
.setActivityTheme(R.style.AppTheme)
.enableCameraSupport(false)
.pickPhoto(this)
Expand All @@ -240,13 +239,19 @@ abstract class BaseActivity: AppCompatActivity(), IHostActivity {
if (photoIntent.resolveActivity(this.packageManager) != null)
startActivityForResult(photoIntent, PhotoUtil.REQUEST_CODE_CAMERA)
}

}
is ExternalActivityParams.FilePresent -> {
val file = File(params.filepath)
val newIntent = IntentUtils.createIntentToOpenFileInExternalApp(this, file)
startActivity(newIntent)
}
is ExternalActivityParams.InviteFriend -> {
val share = Intent(android.content.Intent.ACTION_SEND)
share.type = "text/plain"
share.putExtra(Intent.EXTRA_SUBJECT, "Invite a Friend")
share.putExtra(Intent.EXTRA_TEXT, getString(R.string.invite_text))
startActivity(Intent.createChooser(share, getString(R.string.invite_title)))
}
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/main/kotlin/com/criptext/mail/ExternalActivityParams.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package com.criptext.mail

sealed class ExternalActivityParams {
class FilePicker: ExternalActivityParams()
class ImagePicker: ExternalActivityParams()
data class FilePicker(val remaining: Int): ExternalActivityParams()
data class ImagePicker(val remaining: Int): ExternalActivityParams()
class Camera: ExternalActivityParams()
class InviteFriend: ExternalActivityParams()
class FilePresent(val filepath: String, val mimeType: String): ExternalActivityParams()
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.criptext.mail.scenes.params.EmailDetailParams
import com.criptext.mail.scenes.params.LinkingParams
import com.criptext.mail.scenes.params.MailboxParams
import com.criptext.mail.scenes.params.SignInParams
import com.criptext.mail.utils.EmailUtils
import com.criptext.mail.utils.UIMessage
import com.criptext.mail.utils.generaldatasource.data.GeneralRequest
import com.criptext.mail.utils.generaldatasource.data.GeneralResult
Expand Down Expand Up @@ -54,11 +55,13 @@ class ComposerController(private val model: ComposerModel,
}

override fun onNewFileAttachmentRequested() {
host.launchExternalActivityForResult(ExternalActivityParams.FilePicker())
val remaining = EmailUtils.ATTACHMENT_LIMIT - model.attachments.size
host.launchExternalActivityForResult(ExternalActivityParams.FilePicker(remaining))
}

override fun onNewGalleryAttachmentRequested() {
host.launchExternalActivityForResult(ExternalActivityParams.ImagePicker())
val remaining = EmailUtils.ATTACHMENT_LIMIT - model.attachments.size
host.launchExternalActivityForResult(ExternalActivityParams.ImagePicker(remaining))
}

override fun sendDialogButtonPressed() {
Expand Down Expand Up @@ -112,7 +115,10 @@ class ComposerController(private val model: ComposerModel,
override fun onAttachmentButtonClicked() {
if(host.checkPermissions(BaseActivity.RequestCode.writeAccess.ordinal,
Manifest.permission.WRITE_EXTERNAL_STORAGE)){
scene.showAttachmentsBottomDialog(this)
if(model.attachments.size < EmailUtils.ATTACHMENT_LIMIT)
scene.showAttachmentsBottomDialog(this)
else
scene.showError(UIMessage(R.string.attachment_limit_reached, arrayOf(EmailUtils.ATTACHMENT_LIMIT)))
}
}

Expand Down Expand Up @@ -218,6 +224,9 @@ class ComposerController(private val model: ComposerModel,
host.exitToScene(MailboxParams(), sendMailMessage, false)
}
}
is ComposerResult.SaveEmail.TooManyRecipients ->
scene.showError(UIMessage(R.string.error_saving_too_many_recipients,
arrayOf(EmailUtils.RECIPIENT_LIMIT)))
is ComposerResult.SaveEmail.Failure -> {
scene.showError(UIMessage(R.string.error_saving_as_draft))
}
Expand Down Expand Up @@ -502,7 +511,10 @@ class ComposerController(private val model: ComposerModel,
scene.showError(UIMessage(R.string.permission_filepicker_rationale))
return
}
scene.showAttachmentsBottomDialog(observer)
if(model.attachments.size < EmailUtils.ATTACHMENT_LIMIT)
scene.showAttachmentsBottomDialog(observer)
else
scene.showError(UIMessage(R.string.attachment_limit_reached, arrayOf(EmailUtils.ATTACHMENT_LIMIT)))

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ sealed class ComposerResult {
val onlySave: Boolean, val attachments: List<ComposerAttachment>,
val fileKey: String?) : SaveEmail()

class TooManyRecipients: SaveEmail()
class Failure: SaveEmail()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.criptext.mail.db.models.*
import com.criptext.mail.scenes.composer.Validator
import com.criptext.mail.scenes.mailbox.data.EmailInsertionSetup
import com.criptext.mail.utils.DateUtils
import com.criptext.mail.utils.EmailUtils
import com.criptext.mail.utils.file.FileUtils
import java.util.*

Expand All @@ -36,6 +37,9 @@ class SaveEmailWorker(

override fun work(reporter: ProgressReporter<ComposerResult.SaveEmail>)
: ComposerResult.SaveEmail? {

if(isRecipientLimitReached()) return ComposerResult.SaveEmail.TooManyRecipients()

val (newEmailId, savedMailThreadId) = saveEmail()
return ComposerResult.SaveEmail.Success(emailId = newEmailId, threadId = savedMailThreadId,
onlySave = onlySave, composerInputData = composerInputData, attachments = attachments,
Expand All @@ -47,6 +51,12 @@ class SaveEmailWorker(
}
private val selectEmail: (Contact) -> String = { contact -> contact.email }

private fun isRecipientLimitReached(): Boolean{
val sumOfContacts = composerInputData.to.size + composerInputData.cc.size +
composerInputData.bcc.size
return sumOfContacts > EmailUtils.RECIPIENT_LIMIT
}

private fun isSecure(): Boolean{
if(!composerInputData.passwordForNonCriptextUsers.isNullOrEmpty())
return true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ interface DrawerMenuItemListener {
fun onNavigationItemClick(navigationMenuOptions: NavigationMenuOptions)
fun onCustomLabelClicked(label: Label)
fun onSettingsOptionClicked()
fun onInviteFriendOptionClicked()
fun onSupportOptionClicked()
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ import com.criptext.mail.utils.generaldatasource.data.GeneralRequest
import com.criptext.mail.utils.generaldatasource.data.GeneralResult
import com.criptext.mail.websocket.WebSocketEventListener
import com.criptext.mail.websocket.WebSocketEventPublisher
import android.content.Intent
import android.support.v4.content.ContextCompat.startActivity
import com.criptext.mail.BaseActivity
import com.criptext.mail.ExternalActivityParams


/**
* Created by sebas on 1/30/18.
Expand Down Expand Up @@ -166,6 +171,10 @@ class MailboxSceneController(private val scene: MailboxScene,
host.goToScene(SettingsParams(), true)
}

override fun onInviteFriendOptionClicked() {
host.launchExternalActivityForResult(ExternalActivityParams.InviteFriend())
}

override fun onSupportOptionClicked() {
host.goToScene(ComposerParams(type = ComposerType.Support()), true)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ class ResendEmailsWorker(
encodedParams = encodedParams, mimeTypeSource = mimeTypeSource)
)
}
return bodyWithAttachments.toString()
return HTMLUtils.addCriptextFooter(bodyWithAttachments.toString())
}

private val createErrorMessage: (ex: Exception) -> UIMessage = { ex ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ class SendMailWorker(private val signalClient: SignalClient,
MailboxResult.SendMail.Unauthorized(UIMessage(R.string.device_removed_remotely_exception))
ex.errorCode == ServerErrorCodes.Forbidden ->
MailboxResult.SendMail.Forbidden()
ex.errorCode == ServerErrorCodes.TooManyRequests ->
MailboxResult.SendMail.Failure(UIMessage(R.string.send_limit_reached))

else -> MailboxResult.SendMail.Failure(createErrorMessage(ex))
}
}else {
Expand Down Expand Up @@ -222,7 +225,7 @@ class SendMailWorker(private val signalClient: SignalClient,
val (salt, iv, encryptedSession) =
AESUtil.encryptWithPassword(composerInputData.passwordForNonCriptextUsers, sessionToEncrypt)
val encryptedBody = signalClient.encryptMessage(composerInputData.passwordForNonCriptextUsers,
1,composerInputData.body).encryptedB64
1,HTMLUtils.addCriptextFooter(composerInputData.body)).encryptedB64
val externalSession = EmailExternalSession(0, emailId = emailId, iv = iv, salt = salt,
encryptedBody = encryptedBody, encryptedSession = encryptedSession)
db.saveExternalSession(externalSession)
Expand Down Expand Up @@ -261,7 +264,7 @@ class SendMailWorker(private val signalClient: SignalClient,
encodedParams = encodedParams, mimeTypeSource = mimeTypeSource)
)
}
return bodyWithAttachments.toString()
return HTMLUtils.addCriptextFooter(bodyWithAttachments.toString())
}

private fun getSignalSessionJSON(tempUser: DummyUser, keyBundleFromTempUser: PreKeyBundleShareData.DownloadBundle):JSONObject{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class DrawerMenuView(navigationView: NavigationView,
private val sliderAllMail : LinearLayout
private val sliderLabels : LinearLayout
private val sliderSettings : LinearLayout
private val sliderInviteFriend : LinearLayout
private val sliderSupport : LinearLayout

private val recyclerViewLabels : RecyclerView
Expand Down Expand Up @@ -126,6 +127,10 @@ class DrawerMenuView(navigationView: NavigationView,
drawerMenuItemListener.onSettingsOptionClicked()
}

sliderInviteFriend.setOnClickListener{
drawerMenuItemListener.onInviteFriendOptionClicked()
}

sliderSupport.setOnClickListener {
drawerMenuItemListener.onSupportOptionClicked()
}
Expand All @@ -145,6 +150,7 @@ class DrawerMenuView(navigationView: NavigationView,
sliderAllMail = navigationView.findViewById(R.id.slider_all_mail)
sliderLabels = navigationView.findViewById(R.id.slider_labels)
sliderSettings = navigationView.findViewById(R.id.slider_settings)
sliderInviteFriend = navigationView.findViewById(R.id.slider_invite_friend)
sliderSupport = navigationView.findViewById(R.id.slider_support)

imageViewArrow = navigationView.findViewById(R.id.imageViewArrow)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,21 @@ class SignInSceneController(
handleNewTemporalWebSocket()
dataSource.submitRequest(SignInRequest.LinkAuth(currentState.username, model.ephemeralJwt))
}
is SignInResult.LinkBegin.Failure -> {
is SignInResult.LinkBegin.Failure -> returnToStart(result.message)
is SignInResult.LinkBegin.NoDevicesAvailable -> {
val currentState = model.state as SignInLayoutState.LoginValidation
onAcceptPasswordLogin(currentState.username)
}
}
}

private fun returnToStart(message: UIMessage){
val currentState = model.state as SignInLayoutState.LoginValidation
model.state = SignInLayoutState.Start(currentState.username, false)
scene.initLayout(model.state, uiObserver)
scene.showError(message)
}

private fun onLinkAuth(result: SignInResult.LinkAuth) {
scene.toggleResendClickable(true)
when (result) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package com.criptext.mail.scenes.signin.data

import com.criptext.mail.R
import com.criptext.mail.api.HttpClient
import com.criptext.mail.api.ServerErrorException
import com.criptext.mail.bgworker.BackgroundWorker
import com.criptext.mail.bgworker.ProgressReporter
import com.criptext.mail.scenes.signup.data.SignUpAPIClient
import com.criptext.mail.utils.ServerErrorCodes
import com.criptext.mail.utils.UIMessage
import com.github.kittinunf.result.Result
import com.github.kittinunf.result.flatMap
Expand All @@ -20,7 +22,14 @@ class LinkBeginWorker(val httpClient: HttpClient,
override val canBeParallelized = false

override fun catchException(ex: Exception): SignInResult.LinkBegin {
return SignInResult.LinkBegin.Failure(createErrorMessage(ex), ex)
when(ex){
is ServerErrorException -> {
when(ex.errorCode){
ServerErrorCodes.BadRequest -> SignInResult.LinkBegin.NoDevicesAvailable(createErrorMessage(ex))
}
}
}
return SignInResult.LinkBegin.Failure(createErrorMessage(ex))
}

override fun work(reporter: ProgressReporter<SignInResult.LinkBegin>): SignInResult.LinkBegin? {
Expand All @@ -39,7 +48,17 @@ class LinkBeginWorker(val httpClient: HttpClient,
}

private val createErrorMessage: (ex: Exception) -> UIMessage = { ex ->
UIMessage(resId = R.string.forgot_password_error)
when(ex){
is ServerErrorException -> {
when(ex.errorCode){
ServerErrorCodes.BadRequest -> UIMessage(resId = R.string.no_devices_available)
ServerErrorCodes.TooManyRequests -> UIMessage(resId = R.string.too_many_login_attempts)
ServerErrorCodes.TooManyDevices -> UIMessage(resId = R.string.too_many_devices)
else -> UIMessage(resId = R.string.server_bad_status, args = arrayOf(ex.errorCode))
}
}
else -> UIMessage(resId = R.string.forgot_password_error)
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ sealed class SignInResult {

sealed class LinkBegin: SignInResult() {
data class Success(val ephemeralJwt: String): LinkBegin()
data class Failure(val message: UIMessage,
val exception: Exception): LinkBegin()
data class NoDevicesAvailable(val message: UIMessage): LinkBegin()
data class Failure(val message: UIMessage): LinkBegin()
}

sealed class LinkAuth: SignInResult() {
Expand Down
Loading

0 comments on commit efb5123

Please sign in to comment.