reformat kotlin code to comply with ktlint

Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
This commit is contained in:
Andy Scherzinger 2021-04-27 16:32:08 +02:00
parent 0b1ca971dd
commit e82808080d
No known key found for this signature in database
GPG Key ID: 6CADC7E3523C308B
35 changed files with 1801 additions and 1232 deletions

View File

@ -104,7 +104,6 @@ class MagicFirebaseMessagingService : FirebaseMessagingService() {
@Inject @Inject
var eventBus: EventBus? = null var eventBus: EventBus? = null
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
sharedApplication!!.componentApplication.inject(this) sharedApplication!!.componentApplication.inject(this)
@ -148,18 +147,26 @@ class MagicFirebaseMessagingService : FirebaseMessagingService() {
val pushUtils = PushUtils() val pushUtils = PushUtils()
val privateKey = pushUtils.readKeyFromFile(false) as PrivateKey val privateKey = pushUtils.readKeyFromFile(false) as PrivateKey
try { try {
signatureVerification = pushUtils.verifySignature(base64DecodedSignature, signatureVerification = pushUtils.verifySignature(
base64DecodedSubject) base64DecodedSignature,
base64DecodedSubject
)
if (signatureVerification!!.signatureValid) { if (signatureVerification!!.signatureValid) {
val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") val cipher = Cipher.getInstance("RSA/None/PKCS1Padding")
cipher.init(Cipher.DECRYPT_MODE, privateKey) cipher.init(Cipher.DECRYPT_MODE, privateKey)
val decryptedSubject = cipher.doFinal(base64DecodedSubject) val decryptedSubject = cipher.doFinal(base64DecodedSubject)
decryptedPushMessage = LoganSquare.parse(String(decryptedSubject), decryptedPushMessage = LoganSquare.parse(
DecryptedPushMessage::class.java) String(decryptedSubject),
DecryptedPushMessage::class.java
)
decryptedPushMessage?.apply { decryptedPushMessage?.apply {
timestamp = System.currentTimeMillis() timestamp = System.currentTimeMillis()
if (delete) { if (delete) {
cancelExistingNotificationWithId(applicationContext, signatureVerification!!.userEntity, notificationId) cancelExistingNotificationWithId(
applicationContext,
signatureVerification!!.userEntity,
notificationId
)
} else if (deleteAll) { } else if (deleteAll) {
cancelAllNotificationsForAccount(applicationContext, signatureVerification!!.userEntity) cancelAllNotificationsForAccount(applicationContext, signatureVerification!!.userEntity)
} else if (type == "call") { } else if (type == "call") {
@ -171,39 +178,66 @@ class MagicFirebaseMessagingService : FirebaseMessagingService() {
fullScreenIntent.putExtras(bundle) fullScreenIntent.putExtras(bundle)
fullScreenIntent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK fullScreenIntent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
val fullScreenPendingIntent = PendingIntent.getActivity(this@MagicFirebaseMessagingService, 0, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT) val fullScreenPendingIntent = PendingIntent.getActivity(
this@MagicFirebaseMessagingService,
0,
fullScreenIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
val audioAttributesBuilder = AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) val audioAttributesBuilder =
AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
audioAttributesBuilder.setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) audioAttributesBuilder.setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST)
val ringtonePreferencesString: String? = appPreferences!!.callRingtoneUri val ringtonePreferencesString: String? = appPreferences!!.callRingtoneUri
val soundUri = if (TextUtils.isEmpty(ringtonePreferencesString)) { val soundUri = if (TextUtils.isEmpty(ringtonePreferencesString)) {
Uri.parse("android.resource://" + applicationContext.packageName + Uri.parse(
"/raw/librem_by_feandesign_call") "android.resource://" + applicationContext.packageName +
"/raw/librem_by_feandesign_call"
)
} else { } else {
try { try {
val ringtoneSettings = LoganSquare.parse(ringtonePreferencesString, RingtoneSettings::class.java) val ringtoneSettings =
LoganSquare.parse(ringtonePreferencesString, RingtoneSettings::class.java)
ringtoneSettings.ringtoneUri ringtoneSettings.ringtoneUri
} catch (exception: IOException) { } catch (exception: IOException) {
Uri.parse("android.resource://" + applicationContext.packageName + "/raw/librem_by_feandesign_call") Uri.parse("android.resource://" + applicationContext.packageName + "/raw/librem_by_feandesign_call")
} }
} }
val notificationChannelId = NotificationUtils.getNotificationChannelId(applicationContext.resources val notificationChannelId = NotificationUtils.getNotificationChannelId(
.getString(R.string.nc_notification_channel_calls), applicationContext.resources applicationContext.resources
.getString(R.string.nc_notification_channel_calls_description), true, .getString(R.string.nc_notification_channel_calls),
NotificationManagerCompat.IMPORTANCE_HIGH, soundUri!!, audioAttributesBuilder.build(), null, false) applicationContext.resources
.getString(R.string.nc_notification_channel_calls_description),
true,
NotificationManagerCompat.IMPORTANCE_HIGH,
soundUri!!,
audioAttributesBuilder.build(),
null,
false
)
createNotificationChannel(applicationContext!!, createNotificationChannel(
notificationChannelId, applicationContext.resources applicationContext!!,
.getString(R.string.nc_notification_channel_calls), applicationContext.resources notificationChannelId,
.getString(R.string.nc_notification_channel_calls_description), true, applicationContext.resources
NotificationManagerCompat.IMPORTANCE_HIGH, soundUri, audioAttributesBuilder.build(), null, false) .getString(R.string.nc_notification_channel_calls),
applicationContext.resources
.getString(R.string.nc_notification_channel_calls_description),
true,
NotificationManagerCompat.IMPORTANCE_HIGH,
soundUri,
audioAttributesBuilder.build(),
null,
false
)
val uri = Uri.parse(signatureVerification!!.userEntity.baseUrl) val uri = Uri.parse(signatureVerification!!.userEntity.baseUrl)
val baseUrl = uri.host val baseUrl = uri.host
val notification = NotificationCompat.Builder(this@MagicFirebaseMessagingService, notificationChannelId) val notification =
NotificationCompat.Builder(this@MagicFirebaseMessagingService, notificationChannelId)
.setPriority(NotificationCompat.PRIORITY_HIGH) .setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_CALL) .setCategory(NotificationCompat.CATEGORY_CALL)
.setSmallIcon(R.drawable.ic_call_black_24dp) .setSmallIcon(R.drawable.ic_call_black_24dp)
@ -213,7 +247,7 @@ class MagicFirebaseMessagingService : FirebaseMessagingService() {
.setContentTitle(EmojiCompat.get().process(decryptedPushMessage!!.subject)) .setContentTitle(EmojiCompat.get().process(decryptedPushMessage!!.subject))
.setAutoCancel(true) .setAutoCancel(true)
.setOngoing(true) .setOngoing(true)
//.setTimeoutAfter(45000L) // .setTimeoutAfter(45000L)
.setContentIntent(fullScreenPendingIntent) .setContentIntent(fullScreenPendingIntent)
.setFullScreenIntent(fullScreenPendingIntent, true) .setFullScreenIntent(fullScreenPendingIntent, true)
.setSound(soundUri) .setSound(soundUri)
@ -227,7 +261,9 @@ class MagicFirebaseMessagingService : FirebaseMessagingService() {
.putString(BundleKeys.KEY_NOTIFICATION_SUBJECT, subject) .putString(BundleKeys.KEY_NOTIFICATION_SUBJECT, subject)
.putString(BundleKeys.KEY_NOTIFICATION_SIGNATURE, signature) .putString(BundleKeys.KEY_NOTIFICATION_SIGNATURE, signature)
.build() .build()
val pushNotificationWork = OneTimeWorkRequest.Builder(NotificationWorker::class.java).setInputData(messageData).build() val pushNotificationWork =
OneTimeWorkRequest.Builder(NotificationWorker::class.java).setInputData(messageData)
.build()
WorkManager.getInstance().enqueue(pushNotificationWork) WorkManager.getInstance().enqueue(pushNotificationWork)
} }
} }
@ -244,14 +280,23 @@ class MagicFirebaseMessagingService : FirebaseMessagingService() {
} }
} }
private fun checkIfCallIsActive(signatureVerification: SignatureVerification, decryptedPushMessage: DecryptedPushMessage) { private fun checkIfCallIsActive(
val ncApi = retrofit!!.newBuilder().client(okHttpClient!!.newBuilder().cookieJar(JavaNetCookieJar(CookieManager())).build()).build().create(NcApi::class.java) signatureVerification: SignatureVerification,
decryptedPushMessage: DecryptedPushMessage
) {
val ncApi = retrofit!!.newBuilder()
.client(okHttpClient!!.newBuilder().cookieJar(JavaNetCookieJar(CookieManager())).build()).build()
.create(NcApi::class.java)
var hasParticipantsInCall = false var hasParticipantsInCall = false
var inCallOnDifferentDevice = false var inCallOnDifferentDevice = false
ncApi.getPeersForCall(ApiUtils.getCredentials(signatureVerification.userEntity.username, signatureVerification.userEntity.token), ncApi.getPeersForCall(
ApiUtils.getUrlForCall(signatureVerification.userEntity.baseUrl, ApiUtils.getCredentials(signatureVerification.userEntity.username, signatureVerification.userEntity.token),
decryptedPushMessage.id)) ApiUtils.getUrlForCall(
signatureVerification.userEntity.baseUrl,
decryptedPushMessage.id
)
)
.takeWhile { .takeWhile {
isServiceInForeground isServiceInForeground
} }
@ -276,9 +321,12 @@ class MagicFirebaseMessagingService : FirebaseMessagingService() {
stopForeground(true) stopForeground(true)
handler.removeCallbacksAndMessages(null) handler.removeCallbacksAndMessages(null)
} else if (isServiceInForeground) { } else if (isServiceInForeground) {
handler.postDelayed({ handler.postDelayed(
{
checkIfCallIsActive(signatureVerification, decryptedPushMessage) checkIfCallIsActive(signatureVerification, decryptedPushMessage)
}, 5000) },
5000
)
} }
} }

View File

@ -76,8 +76,11 @@ open class BaseActivity : AppCompatActivity() {
} }
} }
fun showCertificateDialog(cert: X509Certificate, magicTrustManager: MagicTrustManager, fun showCertificateDialog(
sslErrorHandler: SslErrorHandler?) { cert: X509Certificate,
magicTrustManager: MagicTrustManager,
sslErrorHandler: SslErrorHandler?
) {
val formatter = DateFormat.getDateInstance(DateFormat.LONG) val formatter = DateFormat.getDateInstance(DateFormat.LONG)
val validFrom = formatter.format(cert.notBefore) val validFrom = formatter.format(cert.notBefore)
val validUntil = formatter.format(cert.notAfter) val validUntil = formatter.format(cert.notAfter)
@ -101,9 +104,11 @@ open class BaseActivity : AppCompatActivity() {
issuedFor = cert.subjectDN.name issuedFor = cert.subjectDN.name
} }
@SuppressLint("StringFormatMatches") val dialogText = String.format(resources @SuppressLint("StringFormatMatches") val dialogText = String.format(
resources
.getString(R.string.nc_certificate_dialog_text), .getString(R.string.nc_certificate_dialog_text),
issuedBy, issuedFor, validFrom, validUntil) issuedBy, issuedFor, validFrom, validUntil
)
LovelyStandardDialog(this) LovelyStandardDialog(this)
.setTopColorRes(R.color.nc_darkRed) .setTopColorRes(R.color.nc_darkRed)
@ -120,11 +125,9 @@ open class BaseActivity : AppCompatActivity() {
sslErrorHandler?.cancel() sslErrorHandler?.cancel()
} }
.show() .show()
} catch (e: CertificateParsingException) { } catch (e: CertificateParsingException) {
Log.d(TAG, "Failed to parse the certificate") Log.d(TAG, "Failed to parse the certificate")
} }
} }
@Subscribe(threadMode = ThreadMode.MAIN) @Subscribe(threadMode = ThreadMode.MAIN)

View File

@ -38,7 +38,6 @@ import pl.droidsonroids.gif.GifDrawable
import pl.droidsonroids.gif.GifImageView import pl.droidsonroids.gif.GifImageView
import java.io.File import java.io.File
class FullScreenImageActivity : AppCompatActivity() { class FullScreenImageActivity : AppCompatActivity() {
private lateinit var path: String private lateinit var path: String
@ -55,9 +54,11 @@ class FullScreenImageActivity : AppCompatActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
return if (item.itemId == R.id.share) { return if (item.itemId == R.id.share) {
val shareUri = FileProvider.getUriForFile(this, val shareUri = FileProvider.getUriForFile(
this,
BuildConfig.APPLICATION_ID, BuildConfig.APPLICATION_ID,
File(path)) File(path)
)
val shareIntent: Intent = Intent().apply { val shareIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND action = Intent.ACTION_SEND
@ -78,19 +79,19 @@ class FullScreenImageActivity : AppCompatActivity() {
setContentView(R.layout.activity_full_screen_image) setContentView(R.layout.activity_full_screen_image)
setSupportActionBar(findViewById(R.id.imageview_toolbar)) setSupportActionBar(findViewById(R.id.imageview_toolbar))
supportActionBar?.setDisplayShowTitleEnabled(false); supportActionBar?.setDisplayShowTitleEnabled(false)
imageWrapperView = findViewById(R.id.image_wrapper_view) imageWrapperView = findViewById(R.id.image_wrapper_view)
photoView = findViewById(R.id.photo_view) photoView = findViewById(R.id.photo_view)
gifView = findViewById(R.id.gif_view) gifView = findViewById(R.id.gif_view)
photoView.setOnPhotoTapListener{ view, x, y -> photoView.setOnPhotoTapListener { view, x, y ->
toggleFullscreen() toggleFullscreen()
} }
photoView.setOnOutsidePhotoTapListener{ photoView.setOnOutsidePhotoTapListener {
toggleFullscreen() toggleFullscreen()
} }
gifView.setOnClickListener{ gifView.setOnClickListener {
toggleFullscreen() toggleFullscreen()
} }
@ -115,29 +116,33 @@ class FullScreenImageActivity : AppCompatActivity() {
} }
} }
private fun toggleFullscreen(){ private fun toggleFullscreen() {
showFullscreen = !showFullscreen; showFullscreen = !showFullscreen
if (showFullscreen){ if (showFullscreen) {
hideSystemUI() hideSystemUI()
supportActionBar?.hide() supportActionBar?.hide()
} else{ } else {
showSystemUI() showSystemUI()
supportActionBar?.show() supportActionBar?.show()
} }
} }
private fun hideSystemUI() { private fun hideSystemUI() {
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE window.decorView.systemUiVisibility = (
View.SYSTEM_UI_FLAG_IMMERSIVE
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_FULLSCREEN) or View.SYSTEM_UI_FLAG_FULLSCREEN
)
} }
private fun showSystemUI() { private fun showSystemUI() {
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE window.decorView.systemUiVisibility = (
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
)
} }
} }

View File

@ -22,7 +22,6 @@ package com.nextcloud.talk.activities
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
@ -33,7 +32,6 @@ import autodagger.AutoInjector
import com.google.android.exoplayer2.MediaItem import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.Player import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.SimpleExoPlayer import com.google.android.exoplayer2.SimpleExoPlayer
import com.google.android.exoplayer2.ui.PlayerControlView
import com.google.android.exoplayer2.ui.StyledPlayerView import com.google.android.exoplayer2.ui.StyledPlayerView
import com.nextcloud.talk.BuildConfig import com.nextcloud.talk.BuildConfig
import com.nextcloud.talk.R import com.nextcloud.talk.R
@ -54,9 +52,11 @@ class FullScreenMediaActivity : AppCompatActivity(), Player.EventListener {
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
return if (item.itemId == R.id.share) { return if (item.itemId == R.id.share) {
val shareUri = FileProvider.getUriForFile(this, val shareUri = FileProvider.getUriForFile(
this,
BuildConfig.APPLICATION_ID, BuildConfig.APPLICATION_ID,
File(path)) File(path)
)
val shareIntent: Intent = Intent().apply { val shareIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND action = Intent.ACTION_SEND
@ -82,11 +82,11 @@ class FullScreenMediaActivity : AppCompatActivity(), Player.EventListener {
setContentView(R.layout.activity_full_screen_media) setContentView(R.layout.activity_full_screen_media)
setSupportActionBar(findViewById(R.id.mediaview_toolbar)) setSupportActionBar(findViewById(R.id.mediaview_toolbar))
supportActionBar?.setDisplayShowTitleEnabled(false); supportActionBar?.setDisplayShowTitleEnabled(false)
playerView = findViewById(R.id.player_view) playerView = findViewById(R.id.player_view)
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
playerView.showController() playerView.showController()
if (isAudioOnly) { if (isAudioOnly) {
@ -121,7 +121,7 @@ class FullScreenMediaActivity : AppCompatActivity(), Player.EventListener {
private fun initializePlayer() { private fun initializePlayer() {
player = SimpleExoPlayer.Builder(applicationContext).build() player = SimpleExoPlayer.Builder(applicationContext).build()
playerView.player = player; playerView.player = player
player.playWhenReady = true player.playWhenReady = true
player.addListener(this) player.addListener(this)
} }
@ -131,17 +131,21 @@ class FullScreenMediaActivity : AppCompatActivity(), Player.EventListener {
} }
private fun hideSystemUI() { private fun hideSystemUI() {
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE window.decorView.systemUiVisibility = (
View.SYSTEM_UI_FLAG_IMMERSIVE
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_FULLSCREEN) or View.SYSTEM_UI_FLAG_FULLSCREEN
)
} }
private fun showSystemUI() { private fun showSystemUI() {
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE window.decorView.systemUiVisibility = (
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
)
} }
} }

View File

@ -34,7 +34,6 @@ import com.nextcloud.talk.application.NextcloudTalkApplication
import io.noties.markwon.Markwon import io.noties.markwon.Markwon
import java.io.File import java.io.File
@AutoInjector(NextcloudTalkApplication::class) @AutoInjector(NextcloudTalkApplication::class)
class FullScreenTextViewerActivity : AppCompatActivity() { class FullScreenTextViewerActivity : AppCompatActivity() {
@ -48,9 +47,11 @@ class FullScreenTextViewerActivity : AppCompatActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
return if (item.itemId == R.id.share) { return if (item.itemId == R.id.share) {
val shareUri = FileProvider.getUriForFile(this, val shareUri = FileProvider.getUriForFile(
this,
BuildConfig.APPLICATION_ID, BuildConfig.APPLICATION_ID,
File(path)) File(path)
)
val shareIntent: Intent = Intent().apply { val shareIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND action = Intent.ACTION_SEND
@ -71,8 +72,7 @@ class FullScreenTextViewerActivity : AppCompatActivity() {
setContentView(R.layout.activity_full_screen_text) setContentView(R.layout.activity_full_screen_text)
setSupportActionBar(findViewById(R.id.textview_toolbar)) setSupportActionBar(findViewById(R.id.textview_toolbar))
supportActionBar?.setDisplayShowTitleEnabled(false); supportActionBar?.setDisplayShowTitleEnabled(false)
textView = findViewById(R.id.text_view) textView = findViewById(R.id.text_view)
val fileName = intent.getStringExtra("FILE_NAME") val fileName = intent.getStringExtra("FILE_NAME")
@ -81,13 +81,12 @@ class FullScreenTextViewerActivity : AppCompatActivity() {
var text = readFile(path) var text = readFile(path)
if (isMarkdown) { if (isMarkdown) {
val markwon = Markwon.create(applicationContext); val markwon = Markwon.create(applicationContext)
markwon.setMarkdown(textView, text); markwon.setMarkdown(textView, text)
} else { } else {
textView.text = text textView.text = text
} }
} }
private fun readFile(fileName: String) = File(fileName).inputStream().readBytes().toString(Charsets.UTF_8) private fun readFile(fileName: String) = File(fileName).inputStream().readBytes().toString(Charsets.UTF_8)
} }

View File

@ -60,10 +60,12 @@ class MagicCallActivity : BaseActivity() {
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
requestWindowFeature(Window.FEATURE_NO_TITLE) requestWindowFeature(Window.FEATURE_NO_TITLE)
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN or window.addFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN or
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
)
window.decorView.systemUiVisibility = systemUiVisibility window.decorView.systemUiVisibility = systemUiVisibility
setContentView(R.layout.activity_magic_call) setContentView(R.layout.activity_magic_call)
@ -74,13 +76,17 @@ class MagicCallActivity : BaseActivity() {
if (!router!!.hasRootController()) { if (!router!!.hasRootController()) {
if (intent.getBooleanExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL, false)) { if (intent.getBooleanExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL, false)) {
router!!.setRoot(RouterTransaction.with(CallNotificationController(intent.extras)) router!!.setRoot(
RouterTransaction.with(CallNotificationController(intent.extras))
.pushChangeHandler(HorizontalChangeHandler()) .pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler())) .popChangeHandler(HorizontalChangeHandler())
)
} else { } else {
router!!.setRoot(RouterTransaction.with(CallController(intent.extras)) router!!.setRoot(
RouterTransaction.with(CallController(intent.extras))
.pushChangeHandler(HorizontalChangeHandler()) .pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler())) .popChangeHandler(HorizontalChangeHandler())
)
} }
} }
@ -89,9 +95,11 @@ class MagicCallActivity : BaseActivity() {
chatController = ChatController(extras) chatController = ChatController(extras)
chatRouter = Conductor.attachRouter(this, chatContainer, savedInstanceState) chatRouter = Conductor.attachRouter(this, chatContainer, savedInstanceState)
chatRouter!!.setRoot(RouterTransaction.with(chatController) chatRouter!!.setRoot(
RouterTransaction.with(chatController)
.pushChangeHandler(HorizontalChangeHandler()) .pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler())) .popChangeHandler(HorizontalChangeHandler())
)
} }
fun showChat() { fun showChat() {

View File

@ -79,23 +79,31 @@ class MainActivity : BaseActivity(), ActionBarProvider {
@BindView(R.id.appBar) @BindView(R.id.appBar)
lateinit var appBar: AppBarLayout lateinit var appBar: AppBarLayout
@BindView(R.id.toolbar) @BindView(R.id.toolbar)
lateinit var toolbar: MaterialToolbar lateinit var toolbar: MaterialToolbar
@BindView(R.id.home_toolbar) @BindView(R.id.home_toolbar)
lateinit var searchCardView: MaterialCardView lateinit var searchCardView: MaterialCardView
@BindView(R.id.search_text) @BindView(R.id.search_text)
lateinit var searchInputText: MaterialTextView lateinit var searchInputText: MaterialTextView
@BindView(R.id.switch_account_button) @BindView(R.id.switch_account_button)
lateinit var settingsButton: MaterialButton lateinit var settingsButton: MaterialButton
@BindView(R.id.controller_container) @BindView(R.id.controller_container)
lateinit var container: ViewGroup lateinit var container: ViewGroup
@Inject @Inject
lateinit var userUtils: UserUtils lateinit var userUtils: UserUtils
@Inject @Inject
lateinit var dataStore: ReactiveEntityStore<Persistable> lateinit var dataStore: ReactiveEntityStore<Persistable>
@Inject @Inject
lateinit var sqlCipherDatabaseSource: SqlCipherDatabaseSource lateinit var sqlCipherDatabaseSource: SqlCipherDatabaseSource
@Inject @Inject
lateinit var ncApi: NcApi lateinit var ncApi: NcApi
@ -124,39 +132,53 @@ class MainActivity : BaseActivity(), ActionBarProvider {
if (intent.hasExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL)) { if (intent.hasExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL)) {
if (!router!!.hasRootController()) { if (!router!!.hasRootController()) {
router!!.setRoot(RouterTransaction.with(ConversationsListController()) router!!.setRoot(
RouterTransaction.with(ConversationsListController())
.pushChangeHandler(HorizontalChangeHandler()) .pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler())) .popChangeHandler(HorizontalChangeHandler())
)
} }
onNewIntent(intent) onNewIntent(intent)
} else if (!router!!.hasRootController()) { } else if (!router!!.hasRootController()) {
if (hasDb) { if (hasDb) {
if (userUtils.anyUserExists()) { if (userUtils.anyUserExists()) {
router!!.setRoot(RouterTransaction.with(ConversationsListController()) router!!.setRoot(
RouterTransaction.with(ConversationsListController())
.pushChangeHandler(HorizontalChangeHandler()) .pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler())) .popChangeHandler(HorizontalChangeHandler())
)
} else { } else {
if (!TextUtils.isEmpty(resources.getString(R.string.weblogin_url))) { if (!TextUtils.isEmpty(resources.getString(R.string.weblogin_url))) {
router!!.pushController(RouterTransaction.with( router!!.pushController(
WebViewLoginController(resources.getString(R.string.weblogin_url), false)) RouterTransaction.with(
WebViewLoginController(resources.getString(R.string.weblogin_url), false)
)
.pushChangeHandler(HorizontalChangeHandler()) .pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler())) .popChangeHandler(HorizontalChangeHandler())
)
} else { } else {
router!!.setRoot(RouterTransaction.with(ServerSelectionController()) router!!.setRoot(
RouterTransaction.with(ServerSelectionController())
.pushChangeHandler(HorizontalChangeHandler()) .pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler())) .popChangeHandler(HorizontalChangeHandler())
)
} }
} }
} else { } else {
if (!TextUtils.isEmpty(resources.getString(R.string.weblogin_url))) { if (!TextUtils.isEmpty(resources.getString(R.string.weblogin_url))) {
router!!.pushController(RouterTransaction.with( router!!.pushController(
WebViewLoginController(resources.getString(R.string.weblogin_url), false)) RouterTransaction.with(
WebViewLoginController(resources.getString(R.string.weblogin_url), false)
)
.pushChangeHandler(HorizontalChangeHandler()) .pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler())) .popChangeHandler(HorizontalChangeHandler())
)
} else { } else {
router!!.setRoot(RouterTransaction.with(ServerSelectionController()) router!!.setRoot(
RouterTransaction.with(ServerSelectionController())
.pushChangeHandler(HorizontalChangeHandler()) .pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler())) .popChangeHandler(HorizontalChangeHandler())
)
} }
} }
} }
@ -193,8 +215,10 @@ class MainActivity : BaseActivity(), ActionBarProvider {
if (userUtils.currentUser?.baseUrl?.endsWith(baseUrl) == true) { if (userUtils.currentUser?.baseUrl?.endsWith(baseUrl) == true) {
startConversation(user) startConversation(user)
} else { } else {
Snackbar.make(container, R.string.nc_phone_book_integration_account_not_found, Snackbar Snackbar.make(
.LENGTH_LONG).show() container, R.string.nc_phone_book_integration_account_not_found,
Snackbar.LENGTH_LONG
).show()
} }
} }
} }
@ -206,10 +230,14 @@ class MainActivity : BaseActivity(), ActionBarProvider {
val currentUser = userUtils.currentUser ?: return val currentUser = userUtils.currentUser ?: return
val credentials = ApiUtils.getCredentials(currentUser.username, currentUser.token) val credentials = ApiUtils.getCredentials(currentUser.username, currentUser.token)
val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(currentUser.baseUrl, roomType, val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
userId, null) currentUser.baseUrl, roomType,
ncApi.createRoom(credentials, userId, null
retrofitBucket.url, retrofitBucket.queryMap) )
ncApi.createRoom(
credentials,
retrofitBucket.url, retrofitBucket.queryMap
)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<RoomOverall> { .subscribe(object : Observer<RoomOverall> {
@ -221,18 +249,26 @@ class MainActivity : BaseActivity(), ActionBarProvider {
bundle.putString(KEY_ROOM_TOKEN, roomOverall.ocs.data.token) bundle.putString(KEY_ROOM_TOKEN, roomOverall.ocs.data.token)
bundle.putString(KEY_ROOM_ID, roomOverall.ocs.data.roomId) bundle.putString(KEY_ROOM_ID, roomOverall.ocs.data.roomId)
if (currentUser.hasSpreedFeatureCapability("chat-v2")) { if (currentUser.hasSpreedFeatureCapability("chat-v2")) {
ncApi.getRoom(credentials, ncApi.getRoom(
ApiUtils.getRoom(currentUser.baseUrl, credentials,
roomOverall.ocs.data.token)) ApiUtils.getRoom(
currentUser.baseUrl,
roomOverall.ocs.data.token
)
)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<RoomOverall> { .subscribe(object : Observer<RoomOverall> {
override fun onSubscribe(d: Disposable) {} override fun onSubscribe(d: Disposable) {}
override fun onNext(roomOverall: RoomOverall) { override fun onNext(roomOverall: RoomOverall) {
bundle.putParcelable(KEY_ACTIVE_CONVERSATION, bundle.putParcelable(
Parcels.wrap(roomOverall.ocs.data)) KEY_ACTIVE_CONVERSATION,
remapChatController(router!!, currentUser.id, Parcels.wrap(roomOverall.ocs.data)
roomOverall.ocs.data.token, bundle, true) )
remapChatController(
router!!, currentUser.id,
roomOverall.ocs.data.token, bundle, true
)
} }
override fun onError(e: Throwable) {} override fun onError(e: Throwable) {}
@ -241,11 +277,14 @@ class MainActivity : BaseActivity(), ActionBarProvider {
} else { } else {
conversationIntent.putExtras(bundle) conversationIntent.putExtras(bundle)
startActivity(conversationIntent) startActivity(conversationIntent)
Handler().postDelayed({ Handler().postDelayed(
{
if (!isDestroyed) { if (!isDestroyed) {
router!!.popCurrentController() router!!.popCurrentController()
} }
}, 100) },
100
)
} }
} }
@ -260,16 +299,17 @@ class MainActivity : BaseActivity(), ActionBarProvider {
if (keyguardManager.isKeyguardSecure && appPreferences.isScreenLocked) { if (keyguardManager.isKeyguardSecure && appPreferences.isScreenLocked) {
if (!SecurityUtils.checkIfWeAreAuthenticated(appPreferences.screenLockTimeout)) { if (!SecurityUtils.checkIfWeAreAuthenticated(appPreferences.screenLockTimeout)) {
if (router != null && router!!.getControllerWithTag(LockedController.TAG) == null) { if (router != null && router!!.getControllerWithTag(LockedController.TAG) == null) {
router!!.pushController(RouterTransaction.with(LockedController()) router!!.pushController(
RouterTransaction.with(LockedController())
.pushChangeHandler(VerticalChangeHandler()) .pushChangeHandler(VerticalChangeHandler())
.popChangeHandler(VerticalChangeHandler()) .popChangeHandler(VerticalChangeHandler())
.tag(LockedController.TAG)) .tag(LockedController.TAG)
)
} }
} }
} }
} }
override fun onNewIntent(intent: Intent) { override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent) super.onNewIntent(intent)
@ -277,12 +317,16 @@ class MainActivity : BaseActivity(), ActionBarProvider {
if (intent.hasExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL)) { if (intent.hasExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL)) {
if (intent.getBooleanExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL, false)) { if (intent.getBooleanExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL, false)) {
router!!.pushController(RouterTransaction.with(CallNotificationController(intent.extras)) router!!.pushController(
RouterTransaction.with(CallNotificationController(intent.extras))
.pushChangeHandler(HorizontalChangeHandler()) .pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler())) .popChangeHandler(HorizontalChangeHandler())
)
} else { } else {
ConductorRemapping.remapChatController(router!!, intent.getLongExtra(BundleKeys.KEY_INTERNAL_USER_ID, -1), ConductorRemapping.remapChatController(
intent.getStringExtra(BundleKeys.KEY_ROOM_TOKEN), intent.extras!!, false) router!!, intent.getLongExtra(BundleKeys.KEY_INTERNAL_USER_ID, -1),
intent.getStringExtra(BundleKeys.KEY_ROOM_TOKEN), intent.extras!!, false
)
} }
} }
} }

View File

@ -39,7 +39,6 @@ import autodagger.AutoInjector
import butterknife.BindView import butterknife.BindView
import butterknife.ButterKnife import butterknife.ButterKnife
import coil.load import coil.load
import coil.transform.CircleCropTransformation
import com.amulyakhare.textdrawable.TextDrawable import com.amulyakhare.textdrawable.TextDrawable
import com.facebook.drawee.view.SimpleDraweeView import com.facebook.drawee.view.SimpleDraweeView
import com.nextcloud.talk.R import com.nextcloud.talk.R
@ -51,7 +50,6 @@ import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.TextMatchers import com.nextcloud.talk.utils.TextMatchers
import com.nextcloud.talk.utils.preferences.AppPreferences import com.nextcloud.talk.utils.preferences.AppPreferences
import com.stfalcon.chatkit.messages.MessageHolders import com.stfalcon.chatkit.messages.MessageHolders
import com.stfalcon.chatkit.utils.DateFormatter
import javax.inject.Inject import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class) @AutoInjector(NextcloudTalkApplication::class)
@ -231,7 +229,10 @@ class MagicIncomingTextMessageViewHolder(incomingView: View) : MessageHolders
parentChatMessage.imageUrl?.let { parentChatMessage.imageUrl?.let {
quotedMessagePreview?.visibility = View.VISIBLE quotedMessagePreview?.visibility = View.VISIBLE
quotedMessagePreview?.load(it) { quotedMessagePreview?.load(it) {
addHeader("Authorization", ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token)) addHeader(
"Authorization",
ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token)
)
} }
} ?: run { } ?: run {
quotedMessagePreview?.visibility = View.GONE quotedMessagePreview?.visibility = View.GONE
@ -242,7 +243,7 @@ class MagicIncomingTextMessageViewHolder(incomingView: View) : MessageHolders
quotedUserName?.setTextColor(context!!.resources.getColor(R.color.textColorMaxContrast)) quotedUserName?.setTextColor(context!!.resources.getColor(R.color.textColorMaxContrast))
if(parentChatMessage.actorId?.equals(message.activeUser.userId) == true) { if (parentChatMessage.actorId?.equals(message.activeUser.userId) == true) {
quoteColoredView?.setBackgroundResource(R.color.colorPrimary) quoteColoredView?.setBackgroundResource(R.color.colorPrimary)
} else { } else {
quoteColoredView?.setBackgroundResource(R.color.textColorMaxContrast) quoteColoredView?.setBackgroundResource(R.color.textColorMaxContrast)

View File

@ -36,7 +36,6 @@ import autodagger.AutoInjector
import butterknife.BindView import butterknife.BindView
import butterknife.ButterKnife import butterknife.ButterKnife
import coil.load import coil.load
import coil.transform.CircleCropTransformation
import com.google.android.flexbox.FlexboxLayout import com.google.android.flexbox.FlexboxLayout
import com.nextcloud.talk.R import com.nextcloud.talk.R
import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication
@ -48,8 +47,7 @@ import com.nextcloud.talk.utils.DisplayUtils.getMessageSelector
import com.nextcloud.talk.utils.DisplayUtils.searchAndReplaceWithMentionSpan import com.nextcloud.talk.utils.DisplayUtils.searchAndReplaceWithMentionSpan
import com.nextcloud.talk.utils.TextMatchers import com.nextcloud.talk.utils.TextMatchers
import com.stfalcon.chatkit.messages.MessageHolders.OutcomingTextMessageViewHolder import com.stfalcon.chatkit.messages.MessageHolders.OutcomingTextMessageViewHolder
import com.stfalcon.chatkit.utils.DateFormatter import java.util.HashMap
import java.util.*
import javax.inject.Inject import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class) @AutoInjector(NextcloudTalkApplication::class)
@ -57,6 +55,7 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
@JvmField @JvmField
@BindView(R.id.messageText) @BindView(R.id.messageText)
var messageText: EmojiTextView? = null var messageText: EmojiTextView? = null
@JvmField @JvmField
@BindView(R.id.messageTime) @BindView(R.id.messageTime)
var messageTimeView: TextView? = null var messageTimeView: TextView? = null
@ -104,20 +103,26 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
for (key in messageParameters.keys) { for (key in messageParameters.keys) {
val individualHashMap: HashMap<String, String>? = message.messageParameters[key] val individualHashMap: HashMap<String, String>? = message.messageParameters[key]
if (individualHashMap != null) { if (individualHashMap != null) {
if (individualHashMap["type"] == "user" || (individualHashMap["type"] if (individualHashMap["type"] == "user" || (
== "guest") || individualHashMap["type"] == "call") { individualHashMap["type"] == "guest"
messageString = searchAndReplaceWithMentionSpan(messageText!!.context, ) || individualHashMap["type"] == "call"
) {
messageString = searchAndReplaceWithMentionSpan(
messageText!!.context,
messageString, messageString,
individualHashMap["id"]!!, individualHashMap["id"]!!,
individualHashMap["name"]!!, individualHashMap["name"]!!,
individualHashMap["type"]!!, individualHashMap["type"]!!,
message.activeUser, message.activeUser,
R.xml.chip_others) R.xml.chip_others
)
} else if (individualHashMap["type"] == "file") { } else if (individualHashMap["type"] == "file") {
realView.setOnClickListener(View.OnClickListener { v: View? -> realView.setOnClickListener(
View.OnClickListener { v: View? ->
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(individualHashMap["link"])) val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(individualHashMap["link"]))
context!!.startActivity(browserIntent) context!!.startActivity(browserIntent)
}) }
)
} }
} }
} }
@ -138,14 +143,16 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
bgBubbleColor, bgBubbleColor,
resources.getColor(R.color.transparent), resources.getColor(R.color.transparent),
bgBubbleColor, bgBubbleColor,
R.drawable.shape_grouped_outcoming_message) R.drawable.shape_grouped_outcoming_message
)
ViewCompat.setBackground(bubble, bubbleDrawable) ViewCompat.setBackground(bubble, bubbleDrawable)
} else { } else {
val bubbleDrawable = getMessageSelector( val bubbleDrawable = getMessageSelector(
bgBubbleColor, bgBubbleColor,
resources.getColor(R.color.transparent), resources.getColor(R.color.transparent),
bgBubbleColor, bgBubbleColor,
R.drawable.shape_outcoming_message) R.drawable.shape_outcoming_message
)
ViewCompat.setBackground(bubble, bubbleDrawable) ViewCompat.setBackground(bubble, bubbleDrawable)
} }
messageText!!.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize) messageText!!.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
@ -160,7 +167,10 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
parentChatMessage.imageUrl?.let { parentChatMessage.imageUrl?.let {
quotedMessagePreview?.visibility = View.VISIBLE quotedMessagePreview?.visibility = View.VISIBLE
quotedMessagePreview?.load(it) { quotedMessagePreview?.load(it) {
addHeader("Authorization", ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token)) addHeader(
"Authorization",
ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token)
)
} }
} ?: run { } ?: run {
quotedMessagePreview?.visibility = View.GONE quotedMessagePreview?.visibility = View.GONE

View File

@ -90,6 +90,7 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
@Inject @Inject
lateinit var appPreferences: AppPreferences lateinit var appPreferences: AppPreferences
@Inject @Inject
lateinit var okHttpClient: OkHttpClient lateinit var okHttpClient: OkHttpClient
//endregion //endregion
@ -105,8 +106,10 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
WebRtcAudioManager.setBlacklistDeviceForOpenSLESUsage(true) WebRtcAudioManager.setBlacklistDeviceForOpenSLESUsage(true)
} }
PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions.builder(this) PeerConnectionFactory.initialize(
.createInitializationOptions()) PeerConnectionFactory.InitializationOptions.builder(this)
.createInitializationOptions()
)
} catch (e: UnsatisfiedLinkError) { } catch (e: UnsatisfiedLinkError) {
Log.w(TAG, e) Log.w(TAG, e)
} }
@ -137,11 +140,13 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
val imagePipelineConfig = ImagePipelineConfig.newBuilder(this) val imagePipelineConfig = ImagePipelineConfig.newBuilder(this)
.setNetworkFetcher(OkHttpNetworkFetcherWithCache(okHttpClient)) .setNetworkFetcher(OkHttpNetworkFetcherWithCache(okHttpClient))
.setMainDiskCacheConfig(DiskCacheConfig.newBuilder(this) .setMainDiskCacheConfig(
DiskCacheConfig.newBuilder(this)
.setMaxCacheSize(0) .setMaxCacheSize(0)
.setMaxCacheSizeOnLowDiskSpace(0) .setMaxCacheSizeOnLowDiskSpace(0)
.setMaxCacheSizeOnVeryLowDiskSpace(0) .setMaxCacheSizeOnVeryLowDiskSpace(0)
.build()) .build()
)
.build() .build()
Fresco.initialize(this, imagePipelineConfig) Fresco.initialize(this, imagePipelineConfig)
@ -152,8 +157,10 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
val pushRegistrationWork = OneTimeWorkRequest.Builder(PushRegistrationWorker::class.java).build() val pushRegistrationWork = OneTimeWorkRequest.Builder(PushRegistrationWorker::class.java).build()
val accountRemovalWork = OneTimeWorkRequest.Builder(AccountRemovalWorker::class.java).build() val accountRemovalWork = OneTimeWorkRequest.Builder(AccountRemovalWorker::class.java).build()
val periodicCapabilitiesUpdateWork = PeriodicWorkRequest.Builder(CapabilitiesWorker::class.java, val periodicCapabilitiesUpdateWork = PeriodicWorkRequest.Builder(
12, TimeUnit.HOURS).build() CapabilitiesWorker::class.java,
12, TimeUnit.HOURS
).build()
val capabilitiesUpdateWork = OneTimeWorkRequest.Builder(CapabilitiesWorker::class.java).build() val capabilitiesUpdateWork = OneTimeWorkRequest.Builder(CapabilitiesWorker::class.java).build()
val signalingSettingsWork = OneTimeWorkRequest.Builder(SignalingSettingsWorker::class.java).build() val signalingSettingsWork = OneTimeWorkRequest.Builder(SignalingSettingsWorker::class.java).build()
@ -161,7 +168,11 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
WorkManager.getInstance().enqueue(accountRemovalWork) WorkManager.getInstance().enqueue(accountRemovalWork)
WorkManager.getInstance().enqueue(capabilitiesUpdateWork) WorkManager.getInstance().enqueue(capabilitiesUpdateWork)
WorkManager.getInstance().enqueue(signalingSettingsWork) WorkManager.getInstance().enqueue(signalingSettingsWork)
WorkManager.getInstance().enqueueUniquePeriodicWork("DailyCapabilitiesUpdateWork", ExistingPeriodicWorkPolicy.REPLACE, periodicCapabilitiesUpdateWork) WorkManager.getInstance().enqueueUniquePeriodicWork(
"DailyCapabilitiesUpdateWork",
ExistingPeriodicWorkPolicy.REPLACE,
periodicCapabilitiesUpdateWork
)
val config = BundledEmojiCompatConfig(this) val config = BundledEmojiCompatConfig(this)
config.setReplaceAll(true) config.setReplaceAll(true)
@ -176,7 +187,6 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
} }
//endregion //endregion
//region Protected methods //region Protected methods
protected fun buildComponent() { protected fun buildComponent() {
componentApplication = DaggerNextcloudTalkApplicationComponent.builder() componentApplication = DaggerNextcloudTalkApplicationComponent.builder()
@ -209,6 +219,7 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
.okHttpClient(okHttpClient) .okHttpClient(okHttpClient)
.build() .build()
} }
companion object { companion object {
private val TAG = NextcloudTalkApplication::class.java.simpleName private val TAG = NextcloudTalkApplication::class.java.simpleName
//region Singleton //region Singleton

View File

@ -44,10 +44,6 @@ import android.widget.ImageView;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.bluelinelabs.conductor.RouterTransaction; import com.bluelinelabs.conductor.RouterTransaction;
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler; import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
import com.bluelinelabs.logansquare.LoganSquare; import com.bluelinelabs.logansquare.LoganSquare;
@ -71,7 +67,6 @@ import com.nextcloud.talk.models.RingtoneSettings;
import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.database.UserEntity;
import com.nextcloud.talk.models.json.conversations.Conversation; import com.nextcloud.talk.models.json.conversations.Conversation;
import com.nextcloud.talk.models.json.conversations.RoomOverall; import com.nextcloud.talk.models.json.conversations.RoomOverall;
import com.nextcloud.talk.models.json.conversations.RoomsOverall;
import com.nextcloud.talk.models.json.participants.Participant; import com.nextcloud.talk.models.json.participants.Participant;
import com.nextcloud.talk.models.json.participants.ParticipantsOverall; import com.nextcloud.talk.models.json.participants.ParticipantsOverall;
import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.ApiUtils;
@ -94,6 +89,9 @@ import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import autodagger.AutoInjector; import autodagger.AutoInjector;
import butterknife.BindView; import butterknife.BindView;
import butterknife.OnClick; import butterknife.OnClick;
@ -261,7 +259,7 @@ public class CallNotificationController extends BaseController {
private void handleFromNotification() { private void handleFromNotification() {
boolean isConversationApiV3 = userBeingCalled.hasSpreedFeatureCapability("conversation-v3"); boolean isConversationApiV3 = userBeingCalled.hasSpreedFeatureCapability("conversation-v3");
if(isConversationApiV3) { if (isConversationApiV3) {
ncApi.getRoom(credentials, ApiUtils.getRoomV3(userBeingCalled.getBaseUrl(), roomId)) ncApi.getRoom(credentials, ApiUtils.getRoomV3(userBeingCalled.getBaseUrl(), roomId))
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.retry(3) .retry(3)
@ -279,7 +277,7 @@ public class CallNotificationController extends BaseController {
boolean hasCallFlags = userBeingCalled.hasSpreedFeatureCapability("conversation-call-flags"); boolean hasCallFlags = userBeingCalled.hasSpreedFeatureCapability("conversation-call-flags");
if (hasCallFlags) { if (hasCallFlags) {
if (isInCallWithVideo(currentConversation.callFlag)){ if (isInCallWithVideo(currentConversation.callFlag)) {
incomingCallVoiceOrVideoTextView.setText(String.format(getResources().getString(R.string.nc_call_video), incomingCallVoiceOrVideoTextView.setText(String.format(getResources().getString(R.string.nc_call_video),
getResources().getString(R.string.nc_app_name))); getResources().getString(R.string.nc_app_name)));
} else { } else {

View File

@ -40,8 +40,22 @@ import android.text.TextUtils
import android.text.TextWatcher import android.text.TextWatcher
import android.util.Log import android.util.Log
import android.util.TypedValue import android.util.TypedValue
import android.view.* import android.view.Gravity
import android.widget.* 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 android.widget.AbsListView
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.PopupMenu
import android.widget.ProgressBar
import android.widget.RelativeLayout
import android.widget.Space
import android.widget.TextView
import android.widget.Toast
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
import androidx.emoji.text.EmojiCompat import androidx.emoji.text.EmojiCompat
import androidx.emoji.widget.EmojiEditText import androidx.emoji.widget.EmojiEditText
@ -55,7 +69,6 @@ import autodagger.AutoInjector
import butterknife.BindView import butterknife.BindView
import butterknife.OnClick import butterknife.OnClick
import coil.load import coil.load
import coil.transform.CircleCropTransformation
import com.bluelinelabs.conductor.RouterTransaction import com.bluelinelabs.conductor.RouterTransaction
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler
@ -69,7 +82,12 @@ import com.facebook.imagepipeline.image.CloseableImage
import com.google.android.flexbox.FlexboxLayout import com.google.android.flexbox.FlexboxLayout
import com.nextcloud.talk.R import com.nextcloud.talk.R
import com.nextcloud.talk.activities.MagicCallActivity import com.nextcloud.talk.activities.MagicCallActivity
import com.nextcloud.talk.adapters.messages.* import com.nextcloud.talk.adapters.messages.MagicIncomingTextMessageViewHolder
import com.nextcloud.talk.adapters.messages.MagicOutcomingTextMessageViewHolder
import com.nextcloud.talk.adapters.messages.MagicPreviewMessageViewHolder
import com.nextcloud.talk.adapters.messages.MagicSystemMessageViewHolder
import com.nextcloud.talk.adapters.messages.MagicUnreadNoticeMessageViewHolder
import com.nextcloud.talk.adapters.messages.TalkMessagesListAdapter
import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.callbacks.MentionAutocompleteCallback import com.nextcloud.talk.callbacks.MentionAutocompleteCallback
@ -91,7 +109,14 @@ import com.nextcloud.talk.models.json.generic.GenericOverall
import com.nextcloud.talk.models.json.mention.Mention import com.nextcloud.talk.models.json.mention.Mention
import com.nextcloud.talk.presenters.MentionAutocompletePresenter import com.nextcloud.talk.presenters.MentionAutocompletePresenter
import com.nextcloud.talk.ui.dialog.AttachmentDialog import com.nextcloud.talk.ui.dialog.AttachmentDialog
import com.nextcloud.talk.utils.* import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.ConductorRemapping
import com.nextcloud.talk.utils.DateUtils
import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.KeyboardUtils
import com.nextcloud.talk.utils.MagicCharPolicy
import com.nextcloud.talk.utils.NotificationUtils
import com.nextcloud.talk.utils.UriUtils
import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.bundle.BundleKeys
import com.nextcloud.talk.utils.database.user.UserUtils import com.nextcloud.talk.utils.database.user.UserUtils
import com.nextcloud.talk.utils.preferences.AppPreferences import com.nextcloud.talk.utils.preferences.AppPreferences
@ -121,14 +146,20 @@ import org.parceler.Parcels
import retrofit2.HttpException import retrofit2.HttpException
import retrofit2.Response import retrofit2.Response
import java.net.HttpURLConnection import java.net.HttpURLConnection
import java.util.* import java.util.ArrayList
import java.util.Date
import java.util.HashMap
import java.util.Objects
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class) @AutoInjector(NextcloudTalkApplication::class)
class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter class ChatController(args: Bundle) :
.OnLoadMoreListener, MessagesListAdapter.Formatter<Date>, MessagesListAdapter BaseController(args),
.OnMessageViewLongClickListener<IMessage>, MessageHolders.ContentChecker<IMessage> { MessagesListAdapter.OnLoadMoreListener,
MessagesListAdapter.Formatter<Date>,
MessagesListAdapter.OnMessageViewLongClickListener<IMessage>,
MessageHolders.ContentChecker<IMessage> {
@Inject @Inject
@JvmField @JvmField
@ -236,7 +267,8 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
this.roomToken = args.getString(BundleKeys.KEY_ROOM_TOKEN, "") this.roomToken = args.getString(BundleKeys.KEY_ROOM_TOKEN, "")
if (args.containsKey(BundleKeys.KEY_ACTIVE_CONVERSATION)) { if (args.containsKey(BundleKeys.KEY_ACTIVE_CONVERSATION)) {
this.currentConversation = Parcels.unwrap<Conversation>(args.getParcelable<Parcelable>(BundleKeys.KEY_ACTIVE_CONVERSATION)) this.currentConversation =
Parcels.unwrap<Conversation>(args.getParcelable<Parcelable>(BundleKeys.KEY_ACTIVE_CONVERSATION))
} }
this.roomPassword = args.getString(BundleKeys.KEY_CONVERSATION_PASSWORD, "") this.roomPassword = args.getString(BundleKeys.KEY_CONVERSATION_PASSWORD, "")
@ -260,9 +292,9 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
checkingLobbyStatus = true checkingLobbyStatus = true
} }
if (conversationUser != null) { if (conversationUser != null) {
ncApi?.getRoom(credentials, ApiUtils.getRoom(conversationUser.baseUrl, roomToken))?.subscribeOn(Schedulers.io()) ncApi?.getRoom(credentials, ApiUtils.getRoom(conversationUser.baseUrl, roomToken))
?.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread()) ?.observeOn(AndroidSchedulers.mainThread())
?.subscribe(object : Observer<RoomOverall> { ?.subscribe(object : Observer<RoomOverall> {
override fun onSubscribe(d: Disposable) { override fun onSubscribe(d: Disposable) {
@ -281,11 +313,9 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
if (!inConversation) { if (!inConversation) {
joinRoomWithPassword() joinRoomWithPassword()
} }
} }
override fun onError(e: Throwable) { override fun onError(e: Throwable) {
} }
override fun onComplete() { override fun onComplete() {
@ -303,7 +333,8 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
private fun handleFromNotification() { private fun handleFromNotification() {
ncApi?.getRooms(credentials, ApiUtils.getUrlForGetRooms(conversationUser?.baseUrl)) ncApi?.getRooms(credentials, ApiUtils.getUrlForGetRooms(conversationUser?.baseUrl))
?.subscribeOn(Schedulers.io())?.observeOn(AndroidSchedulers.mainThread())?.subscribe(object : Observer<RoomsOverall> { ?.subscribeOn(Schedulers.io())?.observeOn(AndroidSchedulers.mainThread())
?.subscribe(object : Observer<RoomsOverall> {
override fun onSubscribe(d: Disposable) { override fun onSubscribe(d: Disposable) {
disposableList.add(d) disposableList.add(d)
} }
@ -321,11 +352,9 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
} }
override fun onError(e: Throwable) { override fun onError(e: Throwable) {
} }
override fun onComplete() { override fun onComplete() {
} }
}) })
} }
@ -336,16 +365,25 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
private fun loadAvatarForStatusBar() { private fun loadAvatarForStatusBar() {
if (inOneToOneCall() && activity != null && conversationVoiceCallMenuItem != null) { if (inOneToOneCall() && activity != null && conversationVoiceCallMenuItem != null) {
val avatarSize = DisplayUtils.convertDpToPixel(conversationVoiceCallMenuItem?.icon!! val avatarSize = DisplayUtils.convertDpToPixel(
.intrinsicWidth.toFloat(), activity).toInt() conversationVoiceCallMenuItem?.icon!!
.intrinsicWidth.toFloat(),
activity
).toInt()
val imageRequest = DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithNameAndPixels(conversationUser?.baseUrl, val imageRequest = DisplayUtils.getImageRequestForUrl(
currentConversation?.name, avatarSize / 2), conversationUser!!) ApiUtils.getUrlForAvatarWithNameAndPixels(
conversationUser?.baseUrl,
currentConversation?.name, avatarSize / 2
),
conversationUser!!
)
val imagePipeline = Fresco.getImagePipeline() val imagePipeline = Fresco.getImagePipeline()
val dataSource = imagePipeline.fetchDecodedImage(imageRequest, null) val dataSource = imagePipeline.fetchDecodedImage(imageRequest, null)
dataSource.subscribe(object : BaseBitmapDataSubscriber() { dataSource.subscribe(
object : BaseBitmapDataSubscriber() {
override fun onNewResultImpl(bitmap: Bitmap?) { override fun onNewResultImpl(bitmap: Bitmap?) {
if (actionBar != null && bitmap != null && resources != null) { if (actionBar != null && bitmap != null && resources != null) {
val roundedBitmapDrawable = RoundedBitmapDrawableFactory.create(resources!!, bitmap) val roundedBitmapDrawable = RoundedBitmapDrawableFactory.create(resources!!, bitmap)
@ -356,7 +394,9 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
} }
override fun onFailureImpl(dataSource: DataSource<CloseableReference<CloseableImage>>) {} override fun onFailureImpl(dataSource: DataSource<CloseableReference<CloseableImage>>) {}
}, UiThreadImmediateExecutorService.getInstance()) },
UiThreadImmediateExecutorService.getInstance()
)
} }
} }
@ -374,21 +414,40 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
adapterWasNull = true adapterWasNull = true
val messageHolders = MessageHolders() val messageHolders = MessageHolders()
messageHolders.setIncomingTextConfig(MagicIncomingTextMessageViewHolder::class.java, R.layout.item_custom_incoming_text_message) messageHolders.setIncomingTextConfig(
messageHolders.setOutcomingTextConfig(MagicOutcomingTextMessageViewHolder::class.java, R.layout.item_custom_outcoming_text_message) MagicIncomingTextMessageViewHolder::class.java,
R.layout.item_custom_incoming_text_message
)
messageHolders.setOutcomingTextConfig(
MagicOutcomingTextMessageViewHolder::class.java,
R.layout.item_custom_outcoming_text_message
)
messageHolders.setIncomingImageConfig(MagicPreviewMessageViewHolder::class.java, R.layout.item_custom_incoming_preview_message) messageHolders.setIncomingImageConfig(
messageHolders.setOutcomingImageConfig(MagicPreviewMessageViewHolder::class.java, R.layout.item_custom_outcoming_preview_message) MagicPreviewMessageViewHolder::class.java,
R.layout.item_custom_incoming_preview_message
)
messageHolders.setOutcomingImageConfig(
MagicPreviewMessageViewHolder::class.java,
R.layout.item_custom_outcoming_preview_message
)
messageHolders.registerContentType(CONTENT_TYPE_SYSTEM_MESSAGE, MagicSystemMessageViewHolder::class.java, messageHolders.registerContentType(
CONTENT_TYPE_SYSTEM_MESSAGE, MagicSystemMessageViewHolder::class.java,
R.layout.item_system_message, MagicSystemMessageViewHolder::class.java, R.layout.item_system_message, R.layout.item_system_message, MagicSystemMessageViewHolder::class.java, R.layout.item_system_message,
this) this
)
messageHolders.registerContentType(CONTENT_TYPE_UNREAD_NOTICE_MESSAGE, messageHolders.registerContentType(
CONTENT_TYPE_UNREAD_NOTICE_MESSAGE,
MagicUnreadNoticeMessageViewHolder::class.java, R.layout.item_date_header, MagicUnreadNoticeMessageViewHolder::class.java, R.layout.item_date_header,
MagicUnreadNoticeMessageViewHolder::class.java, R.layout.item_date_header, this) MagicUnreadNoticeMessageViewHolder::class.java, R.layout.item_date_header, this
)
adapter = TalkMessagesListAdapter(conversationUser?.userId, messageHolders, ImageLoader { imageView, url, payload -> adapter = TalkMessagesListAdapter(
conversationUser?.userId,
messageHolders,
ImageLoader { imageView, url, payload ->
val draweeController = Fresco.newDraweeControllerBuilder() val draweeController = Fresco.newDraweeControllerBuilder()
.setImageRequest(DisplayUtils.getImageRequestForUrl(url, conversationUser)) .setImageRequest(DisplayUtils.getImageRequestForUrl(url, conversationUser))
.setControllerListener(DisplayUtils.getImageControllerListener(imageView)) .setControllerListener(DisplayUtils.getImageControllerListener(imageView))
@ -396,7 +455,8 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
.setAutoPlayAnimations(true) .setAutoPlayAnimations(true)
.build() .build()
imageView.controller = draweeController imageView.controller = draweeController
}) }
)
} else { } else {
messagesListView?.visibility = View.VISIBLE messagesListView?.visibility = View.VISIBLE
} }
@ -449,7 +509,6 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
} }
}) })
val filters = arrayOfNulls<InputFilter>(1) val filters = arrayOfNulls<InputFilter>(1)
val lengthFilter = conversationUser?.messageMaxLength ?: 1000 val lengthFilter = conversationUser?.messageMaxLength ?: 1000
@ -458,27 +517,34 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
messageInput?.addTextChangedListener(object : TextWatcher { messageInput?.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
} }
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
if (s.length >= lengthFilter) { if (s.length >= lengthFilter) {
messageInput?.error = String.format(Objects.requireNonNull<Resources> messageInput?.error = String.format(
(resources).getString(R.string.nc_limit_hit), Integer.toString(lengthFilter)) Objects.requireNonNull<Resources>
(resources).getString(R.string.nc_limit_hit),
Integer.toString(lengthFilter)
)
} else { } else {
messageInput?.error = null messageInput?.error = null
} }
val editable = messageInput?.editableText val editable = messageInput?.editableText
if (editable != null && messageInput != null) { if (editable != null && messageInput != null) {
val mentionSpans = editable.getSpans(0, messageInput!!.length(), val mentionSpans = editable.getSpans(
Spans.MentionChipSpan::class.java) 0, messageInput!!.length(),
Spans.MentionChipSpan::class.java
)
var mentionSpan: Spans.MentionChipSpan var mentionSpan: Spans.MentionChipSpan
for (i in mentionSpans.indices) { for (i in mentionSpans.indices) {
mentionSpan = mentionSpans[i] mentionSpan = mentionSpans[i]
if (start >= editable.getSpanStart(mentionSpan) && start < editable.getSpanEnd(mentionSpan)) { if (start >= editable.getSpanStart(mentionSpan) && start < editable.getSpanEnd(mentionSpan)) {
if (editable.subSequence(editable.getSpanStart(mentionSpan), if (editable.subSequence(
editable.getSpanEnd(mentionSpan)).toString().trim { it <= ' ' } != mentionSpan.label) { editable.getSpanStart(mentionSpan),
editable.getSpanEnd(mentionSpan)
).toString().trim { it <= ' ' } != mentionSpan.label
) {
editable.removeSpan(mentionSpan) editable.removeSpan(mentionSpan)
} }
} }
@ -487,18 +553,19 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
} }
override fun afterTextChanged(s: Editable) { override fun afterTextChanged(s: Editable) {
} }
}) })
messageInputView?.setAttachmentsListener { messageInputView?.setAttachmentsListener {
activity?.let { AttachmentDialog(it, this).show() }; activity?.let { AttachmentDialog(it, this).show() }
} }
messageInputView?.button?.setOnClickListener { v -> submitMessage() } messageInputView?.button?.setOnClickListener { v -> submitMessage() }
messageInputView?.button?.contentDescription = resources?.getString(R.string messageInputView?.button?.contentDescription = resources?.getString(
.nc_description_send_message_button) R.string
.nc_description_send_message_button
)
if (currentConversation != null && currentConversation?.roomId != null) { if (currentConversation != null && currentConversation?.roomId != null) {
loadAvatarForStatusBar() loadAvatarForStatusBar()
@ -516,7 +583,6 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
super.onViewBound(view) super.onViewBound(view)
} }
private fun checkReadOnlyState() { private fun checkReadOnlyState() {
if (currentConversation != null) { if (currentConversation != null) {
if (currentConversation?.shouldShowLobby(conversationUser) ?: false || currentConversation?.conversationReadOnlyState != null && currentConversation?.conversationReadOnlyState == Conversation.ConversationReadOnlyState.CONVERSATION_READ_ONLY) { if (currentConversation?.shouldShowLobby(conversationUser) ?: false || currentConversation?.conversationReadOnlyState != null && currentConversation?.conversationReadOnlyState == Conversation.ConversationReadOnlyState.CONVERSATION_READ_ONLY) {
@ -524,7 +590,6 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
conversationVoiceCallMenuItem?.icon?.alpha = 99 conversationVoiceCallMenuItem?.icon?.alpha = 99
conversationVideoMenuItem?.icon?.alpha = 99 conversationVideoMenuItem?.icon?.alpha = 99
messageInputView?.visibility = View.GONE messageInputView?.visibility = View.GONE
} else { } else {
if (conversationVoiceCallMenuItem != null) { if (conversationVoiceCallMenuItem != null) {
conversationVoiceCallMenuItem?.icon?.alpha = 255 conversationVoiceCallMenuItem?.icon?.alpha = 255
@ -535,7 +600,8 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
} }
if (currentConversation != null && currentConversation!!.shouldShowLobby if (currentConversation != null && currentConversation!!.shouldShowLobby
(conversationUser)) { (conversationUser)
) {
messageInputView?.visibility = View.GONE messageInputView?.visibility = View.GONE
} else { } else {
messageInputView?.visibility = View.VISIBLE messageInputView?.visibility = View.VISIBLE
@ -558,9 +624,15 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
loadingProgressBar?.visibility = View.GONE loadingProgressBar?.visibility = View.GONE
if (currentConversation?.lobbyTimer != null && currentConversation?.lobbyTimer != if (currentConversation?.lobbyTimer != null && currentConversation?.lobbyTimer !=
0L) { 0L
conversationLobbyText?.text = String.format(resources!!.getString(R.string.nc_lobby_waiting_with_date), DateUtils.getLocalDateStringFromTimestampForLobby(currentConversation?.lobbyTimer ) {
?: 0)) conversationLobbyText?.text = String.format(
resources!!.getString(R.string.nc_lobby_waiting_with_date),
DateUtils.getLocalDateStringFromTimestampForLobby(
currentConversation?.lobbyTimer
?: 0
)
)
} else { } else {
conversationLobbyText?.setText(R.string.nc_lobby_waiting) conversationLobbyText?.setText(R.string.nc_lobby_waiting)
} }
@ -607,7 +679,7 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
filenamesWithLinebreaks += filename + "\n" filenamesWithLinebreaks += filename + "\n"
} }
val confirmationQuestion = when(files.size) { val confirmationQuestion = when (files.size) {
1 -> context?.resources?.getString(R.string.nc_upload_confirm_send_single)?.let { 1 -> context?.resources?.getString(R.string.nc_upload_confirm_send_single)?.let {
String.format(it, title) String.format(it, title)
} }
@ -622,16 +694,20 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
.setMessage(filenamesWithLinebreaks) .setMessage(filenamesWithLinebreaks)
.setPositiveButton(R.string.nc_yes) { v -> .setPositiveButton(R.string.nc_yes) { v ->
uploadFiles(files) uploadFiles(files)
Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_in_progess), Toast Toast.makeText(
.LENGTH_LONG).show(); context, context?.resources?.getString(R.string.nc_upload_in_progess),
Toast.LENGTH_LONG
).show()
} }
.setNegativeButton(R.string.nc_no) {} .setNegativeButton(R.string.nc_no) {}
.show() .show()
} catch (e: IllegalStateException) { } catch (e: IllegalStateException) {
Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG).show() Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG)
.show()
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e) Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG).show() Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG)
.show()
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e) Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
} }
} }
@ -662,8 +738,15 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
addCategory(Intent.CATEGORY_OPENABLE) addCategory(Intent.CATEGORY_OPENABLE)
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
} }
startActivityForResult(Intent.createChooser(action, context?.resources?.getString( startActivityForResult(
R.string.nc_upload_choose_local_files)), REQUEST_CODE_CHOOSE_FILE); Intent.createChooser(
action,
context?.resources?.getString(
R.string.nc_upload_choose_local_files
)
),
REQUEST_CODE_CHOOSE_FILE
)
} }
fun showBrowserScreen(browserType: BrowserController.BrowserType) { fun showBrowserScreen(browserType: BrowserController.BrowserType) {
@ -671,19 +754,23 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
bundle.putParcelable(BundleKeys.KEY_BROWSER_TYPE, Parcels.wrap<BrowserController.BrowserType>(browserType)) bundle.putParcelable(BundleKeys.KEY_BROWSER_TYPE, Parcels.wrap<BrowserController.BrowserType>(browserType))
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap<UserEntity>(conversationUser)) bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap<UserEntity>(conversationUser))
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken) bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
router.pushController(RouterTransaction.with(BrowserForSharingController(bundle)) router.pushController(
RouterTransaction.with(BrowserForSharingController(bundle))
.pushChangeHandler(VerticalChangeHandler()) .pushChangeHandler(VerticalChangeHandler())
.popChangeHandler(VerticalChangeHandler())) .popChangeHandler(VerticalChangeHandler())
)
} }
private fun showConversationInfoScreen() { private fun showConversationInfoScreen() {
val bundle = Bundle() val bundle = Bundle()
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, conversationUser) bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, conversationUser)
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken) bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
bundle.putBoolean(BundleKeys.KEY_ROOM_ONE_TO_ONE, inOneToOneCall()); bundle.putBoolean(BundleKeys.KEY_ROOM_ONE_TO_ONE, inOneToOneCall())
router.pushController(RouterTransaction.with(ConversationInfoController(bundle)) router.pushController(
RouterTransaction.with(ConversationInfoController(bundle))
.pushChangeHandler(HorizontalChangeHandler()) .pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler())) .popChangeHandler(HorizontalChangeHandler())
)
} }
private fun setupMentionAutocomplete() { private fun setupMentionAutocomplete() {
@ -691,8 +778,10 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
resources?.let { resources?.let {
val backgroundDrawable = ColorDrawable(it.getColor(R.color.bg_default)) val backgroundDrawable = ColorDrawable(it.getColor(R.color.bg_default))
val presenter = MentionAutocompletePresenter(applicationContext, roomToken) val presenter = MentionAutocompletePresenter(applicationContext, roomToken)
val callback = MentionAutocompleteCallback(activity, val callback = MentionAutocompleteCallback(
conversationUser, messageInput) activity,
conversationUser, messageInput
)
if (mentionAutocomplete == null && messageInput != null) { if (mentionAutocomplete == null && messageInput != null) {
mentionAutocomplete = Autocomplete.on<Mention>(messageInput) mentionAutocomplete = Autocomplete.on<Mention>(messageInput)
@ -730,8 +819,10 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
smileyButton?.setColorFilter(resources!!.getColor(R.color.colorPrimary), PorterDuff.Mode.SRC_IN) smileyButton?.setColorFilter(resources!!.getColor(R.color.colorPrimary), PorterDuff.Mode.SRC_IN)
} }
}.setOnEmojiPopupDismissListener { }.setOnEmojiPopupDismissListener {
smileyButton?.setColorFilter(resources!!.getColor(R.color.emoji_icons), smileyButton?.setColorFilter(
PorterDuff.Mode.SRC_IN) resources!!.getColor(R.color.emoji_icons),
PorterDuff.Mode.SRC_IN
)
}.setOnEmojiClickListener { emoji, imageView -> messageInput?.editableText?.append(" ") }.build(it) }.setOnEmojiClickListener { emoji, imageView -> messageInput?.editableText?.append(" ") }.build(it)
} }
@ -753,11 +844,15 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
private fun cancelNotificationsForCurrentConversation() { private fun cancelNotificationsForCurrentConversation() {
if (conversationUser != null) { if (conversationUser != null) {
if (!conversationUser.hasSpreedFeatureCapability("no-ping") && !TextUtils.isEmpty(roomId)) { if (!conversationUser.hasSpreedFeatureCapability("no-ping") && !TextUtils.isEmpty(roomId)) {
NotificationUtils.cancelExistingNotificationsForRoom(applicationContext, NotificationUtils.cancelExistingNotificationsForRoom(
conversationUser, roomId) applicationContext,
conversationUser, roomId
)
} else if (!TextUtils.isEmpty(roomToken)) { } else if (!TextUtils.isEmpty(roomToken)) {
NotificationUtils.cancelExistingNotificationsForRoom(applicationContext, NotificationUtils.cancelExistingNotificationsForRoom(
conversationUser, roomToken!!) applicationContext,
conversationUser, roomToken!!
)
} }
} }
} }
@ -775,8 +870,12 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
activity?.findViewById<View>(R.id.toolbar)?.setOnClickListener(null) activity?.findViewById<View>(R.id.toolbar)?.setOnClickListener(null)
} }
if (conversationUser != null && conversationUser?.hasSpreedFeatureCapability("no-ping") if (conversationUser != null &&
&& activity != null && !activity?.isChangingConfigurations!! && !isLeavingForConversation) { conversationUser.hasSpreedFeatureCapability("no-ping") &&
activity != null &&
!activity?.isChangingConfigurations!! &&
!isLeavingForConversation
) {
wasDetached = true wasDetached = true
leaveRoom() leaveRoom()
} }
@ -819,8 +918,13 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
private fun startPing() { private fun startPing() {
if (conversationUser != null && !conversationUser.hasSpreedFeatureCapability("no-ping")) { if (conversationUser != null && !conversationUser.hasSpreedFeatureCapability("no-ping")) {
ncApi?.pingCall(credentials, ApiUtils.getUrlForCallPing(conversationUser.baseUrl, ncApi?.pingCall(
roomToken)) credentials,
ApiUtils.getUrlForCallPing(
conversationUser.baseUrl,
roomToken
)
)
?.subscribeOn(Schedulers.io()) ?.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread()) ?.observeOn(AndroidSchedulers.mainThread())
?.repeatWhen { observable -> observable.delay(5000, TimeUnit.MILLISECONDS) } ?.repeatWhen { observable -> observable.delay(5000, TimeUnit.MILLISECONDS) }
@ -832,7 +936,6 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
} }
override fun onNext(genericOverall: GenericOverall) { override fun onNext(genericOverall: GenericOverall) {
} }
override fun onError(e: Throwable) {} override fun onError(e: Throwable) {}
@ -850,9 +953,12 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
private fun joinRoomWithPassword() { private fun joinRoomWithPassword() {
if (currentConversation == null || TextUtils.isEmpty(currentConversation?.sessionId) || if (currentConversation == null || TextUtils.isEmpty(currentConversation?.sessionId) ||
currentConversation?.sessionId == "0") { currentConversation?.sessionId == "0"
ncApi?.joinRoom(credentials, ) {
ApiUtils.getUrlForSettingMyselfAsActiveParticipant(conversationUser?.baseUrl, roomToken), roomPassword) ncApi?.joinRoom(
credentials,
ApiUtils.getUrlForSettingMyselfAsActiveParticipant(conversationUser?.baseUrl, roomToken), roomPassword
)
?.subscribeOn(Schedulers.io()) ?.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread()) ?.observeOn(AndroidSchedulers.mainThread())
?.retry(3) ?.retry(3)
@ -879,7 +985,10 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
} }
if (magicWebSocketInstance != null) { if (magicWebSocketInstance != null) {
magicWebSocketInstance?.joinRoomWithRoomTokenAndSession(roomToken, currentConversation?.sessionId) magicWebSocketInstance?.joinRoomWithRoomTokenAndSession(
roomToken,
currentConversation?.sessionId
)
} }
if (startCallFromNotification != null && startCallFromNotification ?: false) { if (startCallFromNotification != null && startCallFromNotification ?: false) {
startCallFromNotification = false startCallFromNotification = false
@ -888,19 +997,19 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
} }
override fun onError(e: Throwable) { override fun onError(e: Throwable) {
} }
override fun onComplete() { override fun onComplete() {
} }
}) })
} else { } else {
inConversation = true inConversation = true
ApplicationWideCurrentRoomHolder.getInstance().session = currentConversation?.sessionId ApplicationWideCurrentRoomHolder.getInstance().session = currentConversation?.sessionId
if (magicWebSocketInstance != null) { if (magicWebSocketInstance != null) {
magicWebSocketInstance?.joinRoomWithRoomTokenAndSession(roomToken, magicWebSocketInstance?.joinRoomWithRoomTokenAndSession(
currentConversation?.sessionId) roomToken,
currentConversation?.sessionId
)
} }
startPing() startPing()
if (isFirstMessagesProcessing) { if (isFirstMessagesProcessing) {
@ -912,9 +1021,13 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
} }
private fun leaveRoom() { private fun leaveRoom() {
ncApi?.leaveRoom(credentials, ncApi?.leaveRoom(
ApiUtils.getUrlForSettingMyselfAsActiveParticipant(conversationUser?.baseUrl, credentials,
roomToken)) ApiUtils.getUrlForSettingMyselfAsActiveParticipant(
conversationUser?.baseUrl,
roomToken
)
)
?.subscribeOn(Schedulers.io()) ?.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread()) ?.observeOn(AndroidSchedulers.mainThread())
?.subscribe(object : Observer<GenericOverall> { ?.subscribe(object : Observer<GenericOverall> {
@ -930,8 +1043,10 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
} }
if (magicWebSocketInstance != null && currentConversation != null) { if (magicWebSocketInstance != null && currentConversation != null) {
magicWebSocketInstance?.joinRoomWithRoomTokenAndSession("", magicWebSocketInstance?.joinRoomWithRoomTokenAndSession(
currentConversation?.sessionId) "",
currentConversation?.sessionId
)
} }
if (!isDestroyed && !isBeingDestroyed && !wasDetached) { if (!isDestroyed && !isBeingDestroyed && !wasDetached) {
@ -957,14 +1072,15 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
} catch (e: IllegalAccessException) { } catch (e: IllegalAccessException) {
Log.w(TAG, "Failed to access and set field") Log.w(TAG, "Failed to access and set field")
} }
} }
private fun submitMessage() { private fun submitMessage() {
if (messageInput != null) { if (messageInput != null) {
val editable = messageInput!!.editableText val editable = messageInput!!.editableText
val mentionSpans = editable.getSpans(0, editable.length, val mentionSpans = editable.getSpans(
Spans.MentionChipSpan::class.java) 0, editable.length,
Spans.MentionChipSpan::class.java
)
var mentionSpan: Spans.MentionChipSpan var mentionSpan: Spans.MentionChipSpan
for (i in mentionSpans.indices) { for (i in mentionSpans.indices) {
mentionSpan = mentionSpans[i] mentionSpan = mentionSpans[i]
@ -977,7 +1093,10 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
messageInput?.setText("") messageInput?.setText("")
val replyMessageId: Int? = view?.findViewById<RelativeLayout>(R.id.quotedChatMessageView)?.tag as Int? val replyMessageId: Int? = view?.findViewById<RelativeLayout>(R.id.quotedChatMessageView)?.tag as Int?
sendMessage(editable, if (view?.findViewById<RelativeLayout>(R.id.quotedChatMessageView)?.visibility == View.VISIBLE) replyMessageId else null) sendMessage(
editable,
if (view?.findViewById<RelativeLayout>(R.id.quotedChatMessageView)?.visibility == View.VISIBLE) replyMessageId else null
)
cancelReply() cancelReply()
} }
} }
@ -996,7 +1115,6 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
?.observeOn(AndroidSchedulers.mainThread()) ?.observeOn(AndroidSchedulers.mainThread())
?.subscribe(object : Observer<GenericOverall> { ?.subscribe(object : Observer<GenericOverall> {
override fun onSubscribe(d: Disposable) { override fun onSubscribe(d: Disposable) {
} }
override fun onNext(genericOverall: GenericOverall) { override fun onNext(genericOverall: GenericOverall) {
@ -1025,7 +1143,6 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
} }
override fun onComplete() { override fun onComplete() {
} }
}) })
} }
@ -1034,7 +1151,8 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
private fun setupWebsocket() { private fun setupWebsocket() {
if (conversationUser != null) { if (conversationUser != null) {
if (WebSocketConnectionHelper.getMagicWebSocketInstanceForUserId(conversationUser.id) != null) { if (WebSocketConnectionHelper.getMagicWebSocketInstanceForUserId(conversationUser.id) != null) {
magicWebSocketInstance = WebSocketConnectionHelper.getMagicWebSocketInstanceForUserId(conversationUser.id) magicWebSocketInstance =
WebSocketConnectionHelper.getMagicWebSocketInstanceForUserId(conversationUser.id)
} else { } else {
magicWebSocketInstance = null magicWebSocketInstance = null
} }
@ -1047,7 +1165,7 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
} }
if (currentConversation != null && currentConversation!!.shouldShowLobby(conversationUser)) { if (currentConversation != null && currentConversation!!.shouldShowLobby(conversationUser)) {
//return // return
} }
val fieldMap = HashMap<String, Int>() val fieldMap = HashMap<String, Int>()
@ -1090,8 +1208,10 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
if (!wasDetached) { if (!wasDetached) {
if (lookIntoFuture > 0) { if (lookIntoFuture > 0) {
val finalTimeout = timeout val finalTimeout = timeout
ncApi?.pullChatMessages(credentials, ncApi?.pullChatMessages(
ApiUtils.getUrlForChat(conversationUser?.baseUrl, roomToken), fieldMap) credentials,
ApiUtils.getUrlForChat(conversationUser?.baseUrl, roomToken), fieldMap
)
?.subscribeOn(Schedulers.io()) ?.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread()) ?.observeOn(AndroidSchedulers.mainThread())
?.takeWhile { observable -> inConversation && !wasDetached } ?.takeWhile { observable -> inConversation && !wasDetached }
@ -1114,13 +1234,13 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
} }
override fun onComplete() { override fun onComplete() {
} }
}) })
} else { } else {
ncApi?.pullChatMessages(credentials, ncApi?.pullChatMessages(
ApiUtils.getUrlForChat(conversationUser?.baseUrl, roomToken), fieldMap) credentials,
ApiUtils.getUrlForChat(conversationUser?.baseUrl, roomToken), fieldMap
)
?.subscribeOn(Schedulers.io()) ?.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread()) ?.observeOn(AndroidSchedulers.mainThread())
?.takeWhile { observable -> inConversation && !wasDetached } ?.takeWhile { observable -> inConversation && !wasDetached }
@ -1141,7 +1261,6 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
} }
override fun onComplete() { override fun onComplete() {
} }
}) })
} }
@ -1180,7 +1299,6 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
loadingProgressBar?.visibility = View.GONE loadingProgressBar?.visibility = View.GONE
messagesListView?.visibility = View.VISIBLE messagesListView?.visibility = View.VISIBLE
} }
var countGroupedMessages = 0 var countGroupedMessages = 0
@ -1191,9 +1309,12 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
if (TextUtils.isEmpty(chatMessageList[i].systemMessage) && if (TextUtils.isEmpty(chatMessageList[i].systemMessage) &&
TextUtils.isEmpty(chatMessageList[i + 1].systemMessage) && TextUtils.isEmpty(chatMessageList[i + 1].systemMessage) &&
chatMessageList[i + 1].actorId == chatMessageList[i].actorId && chatMessageList[i + 1].actorId == chatMessageList[i].actorId &&
countGroupedMessages < 4 && DateFormatter.isSameDay(chatMessageList[i].createdAt, countGroupedMessages < 4 && DateFormatter.isSameDay(
chatMessageList[i + 1].createdAt)) { chatMessageList[i].createdAt,
chatMessageList[i].isGrouped = true; chatMessageList[i + 1].createdAt
)
) {
chatMessageList[i].isGrouped = true
countGroupedMessages++ countGroupedMessages++
} else { } else {
countGroupedMessages = 0 countGroupedMessages = 0
@ -1201,7 +1322,8 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
} }
val chatMessage = chatMessageList[i] val chatMessage = chatMessageList[i]
chatMessage.isOneToOneConversation = currentConversation?.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL chatMessage.isOneToOneConversation =
currentConversation?.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
chatMessage.isLinkPreviewAllowed = isLinkPreviewAllowed chatMessage.isLinkPreviewAllowed = isLinkPreviewAllowed
chatMessage.activeUser = conversationUser chatMessage.activeUser = conversationUser
} }
@ -1209,7 +1331,6 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
if (adapter != null) { if (adapter != null) {
adapter?.addToEnd(chatMessageList, false) adapter?.addToEnd(chatMessageList, false)
} }
} else { } else {
var chatMessage: ChatMessage var chatMessage: ChatMessage
@ -1225,7 +1346,8 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
adapter?.addToStart(unreadChatMessage, false) adapter?.addToStart(unreadChatMessage, false)
} }
val isThereANewNotice = shouldAddNewMessagesNotice || adapter?.getMessagePositionByIdInReverse("-1") != -1 val isThereANewNotice =
shouldAddNewMessagesNotice || adapter?.getMessagePositionByIdInReverse("-1") != -1
for (i in chatMessageList.indices) { for (i in chatMessageList.indices) {
chatMessage = chatMessageList[i] chatMessage = chatMessageList[i]
@ -1241,7 +1363,8 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
} }
} }
val shouldScroll = !isThereANewNotice && !shouldAddNewMessagesNotice && layoutManager?.findFirstVisibleItemPosition() == 0 || adapter != null && adapter?.itemCount == 0 val shouldScroll =
!isThereANewNotice && !shouldAddNewMessagesNotice && layoutManager?.findFirstVisibleItemPosition() == 0 || adapter != null && adapter?.itemCount == 0
if (!shouldAddNewMessagesNotice && !shouldScroll && popupBubble != null) { if (!shouldAddNewMessagesNotice && !shouldScroll && popupBubble != null) {
if (!popupBubble!!.isShown) { if (!popupBubble!!.isShown) {
@ -1255,18 +1378,24 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
} }
if (adapter != null) { if (adapter != null) {
chatMessage.isGrouped = (adapter!!.isPreviousSameAuthor(chatMessage chatMessage.isGrouped = (
.actorId, -1) && adapter!!.getSameAuthorLastMessagesCount(chatMessage.actorId) % 5 > 0) adapter!!.isPreviousSameAuthor(
chatMessage.isOneToOneConversation = (currentConversation?.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) chatMessage.actorId,
-1
) && adapter!!.getSameAuthorLastMessagesCount(chatMessage.actorId) % 5 > 0
)
chatMessage.isOneToOneConversation =
(currentConversation?.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL)
adapter?.addToStart(chatMessage, shouldScroll) adapter?.addToStart(chatMessage, shouldScroll)
} }
} }
if (shouldAddNewMessagesNotice && adapter != null && messagesListView != null) { if (shouldAddNewMessagesNotice && adapter != null && messagesListView != null) {
layoutManager?.scrollToPositionWithOffset(adapter!!.getMessagePositionByIdInReverse("-1"), messagesListView!!.height / 2) layoutManager?.scrollToPositionWithOffset(
adapter!!.getMessagePositionByIdInReverse("-1"),
messagesListView!!.height / 2
)
} }
} }
// update read status of all messages // update read status of all messages
@ -1310,7 +1439,6 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
} }
} }
override fun format(date: Date): String { override fun format(date: Date): String {
return if (DateFormatter.isToday(date)) { return if (DateFormatter.isToday(date)) {
resources!!.getString(R.string.nc_date_header_today) resources!!.getString(R.string.nc_date_header_today)
@ -1344,7 +1472,6 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
} }
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
android.R.id.home -> { android.R.id.home -> {
@ -1397,7 +1524,6 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
.SystemMessageType.PARENT_MESSAGE_DELETED .SystemMessageType.PARENT_MESSAGE_DELETED
} }
private fun startACall(isVoiceOnlyCall: Boolean) { private fun startACall(isVoiceOnlyCall: Boolean) {
isLeavingForConversation = true isLeavingForConversation = true
val callIntent = getIntentForCall(isVoiceOnlyCall) val callIntent = getIntentForCall(isVoiceOnlyCall)
@ -1427,7 +1553,7 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
} else { } else {
null null
} }
} ?:run { } ?: run {
return null return null
} }
} }
@ -1440,7 +1566,11 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
} }
override fun onMessageViewLongClick(view: View?, message: IMessage?) { override fun onMessageViewLongClick(view: View?, message: IMessage?) {
PopupMenu(this.context, view, if (message?.user?.id == conversationUser?.userId) Gravity.END else Gravity.START).apply { PopupMenu(
this.context,
view,
if (message?.user?.id == conversationUser?.userId) Gravity.END else Gravity.START
).apply {
setOnMenuItemClickListener { item -> setOnMenuItemClickListener { item ->
when (item?.itemId) { when (item?.itemId) {
@ -1456,28 +1586,40 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
chatMessage?.let { chatMessage?.let {
messageInputView?.findViewById<ImageButton>(R.id.attachmentButton)?.visibility = View.GONE messageInputView?.findViewById<ImageButton>(R.id.attachmentButton)?.visibility = View.GONE
messageInputView?.findViewById<Space>(R.id.attachmentButtonSpace)?.visibility = View.GONE messageInputView?.findViewById<Space>(R.id.attachmentButtonSpace)?.visibility = View.GONE
messageInputView?.findViewById<ImageButton>(R.id.cancelReplyButton)?.visibility = View.VISIBLE messageInputView?.findViewById<ImageButton>(R.id.cancelReplyButton)?.visibility =
View.VISIBLE
messageInputView?.findViewById<EmojiTextView>(R.id.quotedMessage)?.maxLines = 2 messageInputView?.findViewById<EmojiTextView>(R.id.quotedMessage)?.maxLines = 2
messageInputView?.findViewById<EmojiTextView>(R.id.quotedMessage)?.ellipsize = TextUtils.TruncateAt.END messageInputView?.findViewById<EmojiTextView>(R.id.quotedMessage)?.ellipsize =
TextUtils.TruncateAt.END
messageInputView?.findViewById<EmojiTextView>(R.id.quotedMessage)?.text = it.text messageInputView?.findViewById<EmojiTextView>(R.id.quotedMessage)?.text = it.text
messageInputView?.findViewById<EmojiTextView>(R.id.quotedMessageAuthor)?.text = it.actorDisplayName messageInputView?.findViewById<EmojiTextView>(R.id.quotedMessageAuthor)?.text =
?: context!!.getText(R.string.nc_nick_guest) it.actorDisplayName ?: context!!.getText(R.string.nc_nick_guest)
conversationUser?.let { currentUser -> conversationUser?.let { currentUser ->
chatMessage.imageUrl?.let { previewImageUrl -> chatMessage.imageUrl?.let { previewImageUrl ->
messageInputView?.findViewById<ImageView>(R.id.quotedMessageImage)?.visibility = View.VISIBLE messageInputView?.findViewById<ImageView>(R.id.quotedMessageImage)?.visibility =
View.VISIBLE
val px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 96f, resources?.displayMetrics) val px = TypedValue.applyDimension(
messageInputView?.findViewById<ImageView>(R.id.quotedMessageImage)?.maxHeight = px.toInt() TypedValue.COMPLEX_UNIT_DIP,
val layoutParams = messageInputView?.findViewById<ImageView>(R.id.quotedMessageImage)?.layoutParams as FlexboxLayout.LayoutParams 96f,
resources?.displayMetrics
)
messageInputView?.findViewById<ImageView>(R.id.quotedMessageImage)?.maxHeight =
px.toInt()
val layoutParams =
messageInputView?.findViewById<ImageView>(R.id.quotedMessageImage)?.layoutParams as FlexboxLayout.LayoutParams
layoutParams.flexGrow = 0f layoutParams.flexGrow = 0f
messageInputView?.findViewById<ImageView>(R.id.quotedMessageImage)?.layoutParams = layoutParams messageInputView?.findViewById<ImageView>(R.id.quotedMessageImage)?.layoutParams =
messageInputView?.findViewById<ImageView>(R.id.quotedMessageImage)?.load(previewImageUrl) { layoutParams
messageInputView?.findViewById<ImageView>(R.id.quotedMessageImage)
?.load(previewImageUrl) {
addHeader("Authorization", credentials!!) addHeader("Authorization", credentials!!)
} }
} ?: run { } ?: run {
messageInputView?.findViewById<ImageView>(R.id.quotedMessageImage)?.visibility = View.GONE messageInputView?.findViewById<ImageView>(R.id.quotedMessageImage)?.visibility =
View.GONE
} }
} }
@ -1498,14 +1640,20 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
override fun onNext(t: ChatOverallSingleMessage) { override fun onNext(t: ChatOverallSingleMessage) {
if (t.ocs.meta.statusCode == HttpURLConnection.HTTP_ACCEPTED) { if (t.ocs.meta.statusCode == HttpURLConnection.HTTP_ACCEPTED) {
Toast.makeText(context, R.string.nc_delete_message_leaked_to_matterbridge, Toast.makeText(
Toast.LENGTH_LONG).show() context, R.string.nc_delete_message_leaked_to_matterbridge,
Toast.LENGTH_LONG
).show()
} }
} }
override fun onError(e: Throwable) { override fun onError(e: Throwable) {
Log.e(TAG, "Something went wrong when trying to delete message with id " + Log.e(
message?.id, e) TAG,
"Something went wrong when trying to delete message with id " +
message?.id,
e
)
Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show() Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
} }
@ -1531,7 +1679,8 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
val messageTemp = message as ChatMessage val messageTemp = message as ChatMessage
messageTemp.isDeleted = true messageTemp.isDeleted = true
messageTemp.isOneToOneConversation = currentConversation?.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL messageTemp.isOneToOneConversation =
currentConversation?.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
messageTemp.isLinkPreviewAllowed = isLinkPreviewAllowed messageTemp.isLinkPreviewAllowed = isLinkPreviewAllowed
messageTemp.activeUser = conversationUser messageTemp.activeUser = conversationUser
@ -1561,7 +1710,6 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
return true return true
} }
override fun hasContentFor(message: IMessage, type: Byte): Boolean { override fun hasContentFor(message: IMessage, type: Byte): Boolean {
when (type) { when (type) {
CONTENT_TYPE_SYSTEM_MESSAGE -> return !TextUtils.isEmpty(message.systemMessage) CONTENT_TYPE_SYSTEM_MESSAGE -> return !TextUtils.isEmpty(message.systemMessage)
@ -1589,19 +1737,22 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
@Subscribe(threadMode = ThreadMode.BACKGROUND) @Subscribe(threadMode = ThreadMode.BACKGROUND)
fun onMessageEvent(userMentionClickEvent: UserMentionClickEvent) { fun onMessageEvent(userMentionClickEvent: UserMentionClickEvent) {
if (currentConversation?.type != Conversation.ConversationType if (currentConversation?.type != Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL ||
.ROOM_TYPE_ONE_TO_ONE_CALL || currentConversation?.name != currentConversation?.name != userMentionClickEvent.userId
userMentionClickEvent.userId) { ) {
val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(conversationUser?.baseUrl, "1", val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
userMentionClickEvent.userId, null) conversationUser?.baseUrl, "1",
userMentionClickEvent.userId, null
)
ncApi?.createRoom(credentials, ncApi?.createRoom(
retrofitBucket.url, retrofitBucket.queryMap) credentials,
retrofitBucket.url, retrofitBucket.queryMap
)
?.subscribeOn(Schedulers.io()) ?.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread()) ?.observeOn(AndroidSchedulers.mainThread())
?.subscribe(object : Observer<RoomOverall> { ?.subscribe(object : Observer<RoomOverall> {
override fun onSubscribe(d: Disposable) { override fun onSubscribe(d: Disposable) {
} }
override fun onNext(roomOverall: RoomOverall) { override fun onNext(roomOverall: RoomOverall) {
@ -1613,27 +1764,32 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
if (conversationUser != null) { if (conversationUser != null) {
if (conversationUser.hasSpreedFeatureCapability("chat-v2")) { if (conversationUser.hasSpreedFeatureCapability("chat-v2")) {
bundle.putParcelable(BundleKeys.KEY_ACTIVE_CONVERSATION, bundle.putParcelable(
Parcels.wrap(roomOverall.ocs.data)) BundleKeys.KEY_ACTIVE_CONVERSATION,
Parcels.wrap(roomOverall.ocs.data)
)
conversationIntent.putExtras(bundle) conversationIntent.putExtras(bundle)
ConductorRemapping.remapChatController(router, conversationUser.id, ConductorRemapping.remapChatController(
roomOverall.ocs.data.token, bundle, false) router, conversationUser.id,
roomOverall.ocs.data.token, bundle, false
)
} }
} else { } else {
conversationIntent.putExtras(bundle) conversationIntent.putExtras(bundle)
startActivity(conversationIntent) startActivity(conversationIntent)
Handler().postDelayed({ Handler().postDelayed(
{
if (!isDestroyed && !isBeingDestroyed) { if (!isDestroyed && !isBeingDestroyed) {
router.popCurrentController() router.popCurrentController()
} }
}, 100) },
100
)
} }
} }
override fun onError(e: Throwable) { override fun onError(e: Throwable) {
} }
override fun onComplete() {} override fun onComplete() {}

View File

@ -85,50 +85,69 @@ import io.reactivex.schedulers.Schedulers
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode import org.greenrobot.eventbus.ThreadMode
import java.util.* import java.util.Calendar
import java.util.Collections
import java.util.Comparator
import java.util.Locale
import javax.inject.Inject import javax.inject.Inject
import kotlin.collections.ArrayList
@AutoInjector(NextcloudTalkApplication::class) @AutoInjector(NextcloudTalkApplication::class)
class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleAdapter.OnItemClickListener { class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleAdapter.OnItemClickListener {
@BindView(R.id.notification_settings) @BindView(R.id.notification_settings)
lateinit var notificationsPreferenceScreen: MaterialPreferenceScreen lateinit var notificationsPreferenceScreen: MaterialPreferenceScreen
@BindView(R.id.progressBar) @BindView(R.id.progressBar)
lateinit var progressBar: ProgressBar lateinit var progressBar: ProgressBar
@BindView(R.id.conversation_info_message_notifications) @BindView(R.id.conversation_info_message_notifications)
lateinit var messageNotificationLevel: MaterialChoicePreference lateinit var messageNotificationLevel: MaterialChoicePreference
@BindView(R.id.webinar_settings) @BindView(R.id.webinar_settings)
lateinit var conversationInfoWebinar: MaterialPreferenceScreen lateinit var conversationInfoWebinar: MaterialPreferenceScreen
@BindView(R.id.conversation_info_lobby) @BindView(R.id.conversation_info_lobby)
lateinit var conversationInfoLobby: MaterialSwitchPreference lateinit var conversationInfoLobby: MaterialSwitchPreference
@BindView(R.id.conversation_info_name) @BindView(R.id.conversation_info_name)
lateinit var nameCategoryView: MaterialPreferenceCategory lateinit var nameCategoryView: MaterialPreferenceCategory
@BindView(R.id.start_time_preferences) @BindView(R.id.start_time_preferences)
lateinit var startTimeView: MaterialStandardPreference lateinit var startTimeView: MaterialStandardPreference
@BindView(R.id.avatar_image) @BindView(R.id.avatar_image)
lateinit var conversationAvatarImageView: SimpleDraweeView lateinit var conversationAvatarImageView: SimpleDraweeView
@BindView(R.id.display_name_text) @BindView(R.id.display_name_text)
lateinit var conversationDisplayName: EmojiTextView lateinit var conversationDisplayName: EmojiTextView
@BindView(R.id.participants_list_category) @BindView(R.id.participants_list_category)
lateinit var participantsListCategory: MaterialPreferenceCategory lateinit var participantsListCategory: MaterialPreferenceCategory
@BindView(R.id.addParticipantsAction) @BindView(R.id.addParticipantsAction)
lateinit var addParticipantsAction: MaterialStandardPreference; lateinit var addParticipantsAction: MaterialStandardPreference
@BindView(R.id.recycler_view) @BindView(R.id.recycler_view)
lateinit var recyclerView: RecyclerView lateinit var recyclerView: RecyclerView
@BindView(R.id.deleteConversationAction) @BindView(R.id.deleteConversationAction)
lateinit var deleteConversationAction: MaterialStandardPreference lateinit var deleteConversationAction: MaterialStandardPreference
@BindView(R.id.leaveConversationAction) @BindView(R.id.leaveConversationAction)
lateinit var leaveConversationAction: MaterialStandardPreference lateinit var leaveConversationAction: MaterialStandardPreference
@BindView(R.id.ownOptions) @BindView(R.id.ownOptions)
lateinit var ownOptionsCategory: MaterialPreferenceCategory lateinit var ownOptionsCategory: MaterialPreferenceCategory
@BindView(R.id.muteCalls) @BindView(R.id.muteCalls)
lateinit var muteCalls: MaterialSwitchPreference lateinit var muteCalls: MaterialSwitchPreference
@set:Inject @set:Inject
lateinit var ncApi: NcApi lateinit var ncApi: NcApi
@set:Inject @set:Inject
lateinit var context: Context lateinit var context: Context
@set:Inject @set:Inject
lateinit var eventBus: EventBus lateinit var eventBus: EventBus
@ -164,7 +183,7 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
NextcloudTalkApplication.sharedApplication?.componentApplication?.inject(this) NextcloudTalkApplication.sharedApplication?.componentApplication?.inject(this)
conversationUser = args.getParcelable(BundleKeys.KEY_USER_ENTITY) conversationUser = args.getParcelable(BundleKeys.KEY_USER_ENTITY)
conversationToken = args.getString(BundleKeys.KEY_ROOM_TOKEN) conversationToken = args.getString(BundleKeys.KEY_ROOM_TOKEN)
hasAvatarSpacing = args.getBoolean(BundleKeys.KEY_ROOM_ONE_TO_ONE, false); hasAvatarSpacing = args.getBoolean(BundleKeys.KEY_ROOM_ONE_TO_ONE, false)
credentials = ApiUtils.getCredentials(conversationUser!!.username, conversationUser.token) credentials = ApiUtils.getCredentials(conversationUser!!.username, conversationUser.token)
} }
@ -207,12 +226,17 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
} }
private fun setupWebinaryView() { private fun setupWebinaryView() {
if (conversationUser!!.hasSpreedFeatureCapability("webinary-lobby") && (conversation!!.type if (conversationUser!!.hasSpreedFeatureCapability("webinary-lobby") &&
== Conversation.ConversationType.ROOM_GROUP_CALL || conversation!!.type == (
Conversation.ConversationType.ROOM_PUBLIC_CALL) && conversation!!.canModerate(conversationUser)) { conversation!!.type == Conversation.ConversationType.ROOM_GROUP_CALL ||
conversation!!.type == Conversation.ConversationType.ROOM_PUBLIC_CALL
) &&
conversation!!.canModerate(conversationUser)
) {
conversationInfoWebinar.visibility = View.VISIBLE conversationInfoWebinar.visibility = View.VISIBLE
val isLobbyOpenToModeratorsOnly = conversation!!.lobbyState == Conversation.LobbyState.LOBBY_STATE_MODERATORS_ONLY val isLobbyOpenToModeratorsOnly =
conversation!!.lobbyState == Conversation.LobbyState.LOBBY_STATE_MODERATORS_ONLY
(conversationInfoLobby.findViewById<View>(R.id.mp_checkable) as SwitchCompat) (conversationInfoLobby.findViewById<View>(R.id.mp_checkable) as SwitchCompat)
.isChecked = isLobbyOpenToModeratorsOnly .isChecked = isLobbyOpenToModeratorsOnly
@ -225,12 +249,17 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
currentTimeCalendar.timeInMillis = conversation!!.lobbyTimer * 1000 currentTimeCalendar.timeInMillis = conversation!!.lobbyTimer * 1000
} }
dateTimePicker(minDateTime = Calendar.getInstance(), requireFutureDateTime = dateTimePicker(
true, currentDateTime = currentTimeCalendar, dateTimeCallback = { _, minDateTime = Calendar.getInstance(),
requireFutureDateTime =
true,
currentDateTime = currentTimeCalendar,
dateTimeCallback = { _,
dateTime -> dateTime ->
reconfigureLobbyTimerView(dateTime) reconfigureLobbyTimerView(dateTime)
submitLobbyChanges() submitLobbyChanges()
}) }
)
} }
} }
@ -269,11 +298,19 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
} }
fun submitLobbyChanges() { fun submitLobbyChanges() {
val state = if ((conversationInfoLobby.findViewById<View>(R.id val state = if (
.mp_checkable) as SwitchCompat).isChecked) 1 else 0 (
ncApi.setLobbyForConversation(ApiUtils.getCredentials(conversationUser!!.username, conversationInfoLobby.findViewById<View>(
conversationUser.token), ApiUtils.getUrlForLobbyForConversation R.id.mp_checkable
(conversationUser.baseUrl, conversation!!.token), state, conversation!!.lobbyTimer) ) as SwitchCompat
).isChecked
) 1 else 0
ncApi.setLobbyForConversation(
ApiUtils.getCredentials(conversationUser!!.username, conversationUser.token),
ApiUtils.getUrlForLobbyForConversation(conversationUser.baseUrl, conversation!!.token),
state,
conversation!!.lobbyTimer
)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<GenericOverall> { .subscribe(object : Observer<GenericOverall> {
@ -288,7 +325,6 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
override fun onError(e: Throwable) { override fun onError(e: Throwable) {
} }
}) })
} }
@ -314,8 +350,12 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
if (activity != null) { if (activity != null) {
LovelyStandardDialog(activity, LovelyStandardDialog.ButtonLayout.HORIZONTAL) LovelyStandardDialog(activity, LovelyStandardDialog.ButtonLayout.HORIZONTAL)
.setTopColorRes(R.color.nc_darkRed) .setTopColorRes(R.color.nc_darkRed)
.setIcon(DisplayUtils.getTintedDrawable(context!!.resources, .setIcon(
R.drawable.ic_delete_black_24dp, R.color.bg_default)) DisplayUtils.getTintedDrawable(
context!!.resources,
R.drawable.ic_delete_black_24dp, R.color.bg_default
)
)
.setPositiveButtonColor(context!!.resources.getColor(R.color.nc_darkRed)) .setPositiveButtonColor(context!!.resources.getColor(R.color.nc_darkRed))
.setTitle(R.string.nc_delete_call) .setTitle(R.string.nc_delete_call)
.setMessage(conversation!!.deleteWarningMessage) .setMessage(conversation!!.deleteWarningMessage)
@ -335,8 +375,8 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
override fun onRestoreViewState(view: View, savedViewState: Bundle) { override fun onRestoreViewState(view: View, savedViewState: Bundle) {
super.onRestoreViewState(view, savedViewState) super.onRestoreViewState(view, savedViewState)
if (LovelySaveStateHandler.wasDialogOnScreen(savedViewState)) { if (LovelySaveStateHandler.wasDialogOnScreen(savedViewState)) {
//Dialog won't be restarted automatically, so we need to call this method. // Dialog won't be restarted automatically, so we need to call this method.
//Each dialog knows how to restore its state // Each dialog knows how to restore its state
showLovelyDialog(LovelySaveStateHandler.getSavedDialogId(savedViewState), savedViewState) showLovelyDialog(LovelySaveStateHandler.getSavedDialogId(savedViewState), savedViewState)
} }
} }
@ -397,7 +437,10 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
} }
private fun getListOfParticipants() { private fun getListOfParticipants() {
ncApi.getPeersForCall(credentials, ApiUtils.getUrlForParticipants(conversationUser!!.baseUrl, conversationToken)) ncApi.getPeersForCall(
credentials,
ApiUtils.getUrlForParticipants(conversationUser!!.baseUrl, conversationToken)
)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<ParticipantsOverall> { .subscribe(object : Observer<ParticipantsOverall> {
@ -410,14 +453,12 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
} }
override fun onError(e: Throwable) { override fun onError(e: Throwable) {
} }
override fun onComplete() { override fun onComplete() {
participantsDisposable!!.dispose() participantsDisposable!!.dispose()
} }
}) })
} }
@OnClick(R.id.addParticipantsAction) @OnClick(R.id.addParticipantsAction)
@ -430,21 +471,33 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
existingParticipantsId.add(userItem.model.userId) existingParticipantsId.add(userItem.model.userId)
} }
bundle.putBoolean(BundleKeys.KEY_ADD_PARTICIPANTS, true); bundle.putBoolean(BundleKeys.KEY_ADD_PARTICIPANTS, true)
bundle.putStringArrayList(BundleKeys.KEY_EXISTING_PARTICIPANTS, existingParticipantsId) bundle.putStringArrayList(BundleKeys.KEY_EXISTING_PARTICIPANTS, existingParticipantsId)
bundle.putString(BundleKeys.KEY_TOKEN, conversation!!.token) bundle.putString(BundleKeys.KEY_TOKEN, conversation!!.token)
getRouter().pushController((RouterTransaction.with(ContactsController(bundle)) getRouter().pushController(
.pushChangeHandler(HorizontalChangeHandler()) (
.popChangeHandler(HorizontalChangeHandler()))) RouterTransaction.with(
ContactsController(bundle)
)
.pushChangeHandler(
HorizontalChangeHandler()
)
.popChangeHandler(
HorizontalChangeHandler()
)
)
)
} }
@OnClick(R.id.leaveConversationAction) @OnClick(R.id.leaveConversationAction)
internal fun leaveConversation() { internal fun leaveConversation() {
workerData?.let { workerData?.let {
WorkManager.getInstance().enqueue(OneTimeWorkRequest.Builder WorkManager.getInstance().enqueue(
(LeaveConversationWorker::class OneTimeWorkRequest.Builder(
.java).setInputData(it).build() LeaveConversationWorker::class
.java
).setInputData(it).build()
) )
popTwoLastControllers() popTwoLastControllers()
} }
@ -452,8 +505,11 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
private fun deleteConversation() { private fun deleteConversation() {
workerData?.let { workerData?.let {
WorkManager.getInstance().enqueue(OneTimeWorkRequest.Builder WorkManager.getInstance().enqueue(
(DeleteConversationWorker::class.java).setInputData(it).build()) OneTimeWorkRequest.Builder(
DeleteConversationWorker::class.java
).setInputData(it).build()
)
popTwoLastControllers() popTwoLastControllers()
} }
} }
@ -518,7 +574,6 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
conversationDisplayName.text = conversation!!.displayName conversationDisplayName.text = conversation!!.displayName
loadConversationAvatar() loadConversationAvatar()
adjustNotificationLevelUI() adjustNotificationLevelUI()
@ -527,7 +582,6 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
} }
override fun onError(e: Throwable) { override fun onError(e: Throwable) {
} }
override fun onComplete() { override fun onComplete() {
@ -543,7 +597,8 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
messageNotificationLevel.alpha = 1.0f messageNotificationLevel.alpha = 1.0f
if (conversation!!.notificationLevel != Conversation.NotificationLevel.DEFAULT) { if (conversation!!.notificationLevel != Conversation.NotificationLevel.DEFAULT) {
val stringValue: String = when (EnumNotificationLevelConverter().convertToInt(conversation!!.notificationLevel)) { val stringValue: String =
when (EnumNotificationLevelConverter().convertToInt(conversation!!.notificationLevel)) {
1 -> "always" 1 -> "always"
2 -> "mention" 2 -> "mention"
3 -> "never" 3 -> "never"
@ -577,22 +632,38 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
private fun loadConversationAvatar() { private fun loadConversationAvatar() {
when (conversation!!.type) { when (conversation!!.type) {
Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL -> if (!TextUtils.isEmpty Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL -> if (
(conversation!!.name)) { !TextUtils.isEmpty(conversation!!.name)
) {
val draweeController = Fresco.newDraweeControllerBuilder() val draweeController = Fresco.newDraweeControllerBuilder()
.setOldController(conversationAvatarImageView.controller) .setOldController(conversationAvatarImageView.controller)
.setAutoPlayAnimations(true) .setAutoPlayAnimations(true)
.setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(conversationUser!!.baseUrl, .setImageRequest(
conversation!!.name, R.dimen.avatar_size_big), conversationUser)) DisplayUtils.getImageRequestForUrl(
ApiUtils.getUrlForAvatarWithName(
conversationUser!!.baseUrl,
conversation!!.name, R.dimen.avatar_size_big
),
conversationUser
)
)
.build() .build()
conversationAvatarImageView.controller = draweeController conversationAvatarImageView.controller = draweeController
} }
Conversation.ConversationType.ROOM_GROUP_CALL -> conversationAvatarImageView.hierarchy.setPlaceholderImage(DisplayUtils Conversation.ConversationType.ROOM_GROUP_CALL -> conversationAvatarImageView.hierarchy.setPlaceholderImage(
.getRoundedBitmapDrawableFromVectorDrawableResource(resources, DisplayUtils
R.drawable.ic_people_group_white_24px)) .getRoundedBitmapDrawableFromVectorDrawableResource(
Conversation.ConversationType.ROOM_PUBLIC_CALL -> conversationAvatarImageView.hierarchy.setPlaceholderImage(DisplayUtils resources,
.getRoundedBitmapDrawableFromVectorDrawableResource(resources, R.drawable.ic_people_group_white_24px
R.drawable.ic_link_white_24px)) )
)
Conversation.ConversationType.ROOM_PUBLIC_CALL -> conversationAvatarImageView.hierarchy.setPlaceholderImage(
DisplayUtils
.getRoundedBitmapDrawableFromVectorDrawableResource(
resources,
R.drawable.ic_link_white_24px
)
)
Conversation.ConversationType.ROOM_SYSTEM -> { Conversation.ConversationType.ROOM_SYSTEM -> {
val layers = arrayOfNulls<Drawable>(2) val layers = arrayOfNulls<Drawable>(2)
layers[0] = context.getDrawable(R.drawable.ic_launcher_background) layers[0] = context.getDrawable(R.drawable.ic_launcher_background)
@ -610,13 +681,14 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
val userItem = adapter?.getItem(position) as UserItem val userItem = adapter?.getItem(position) as UserItem
val participant = userItem.model val participant = userItem.model
if (participant.userId != conversationUser!!.userId) { if (participant.userId != conversationUser!!.userId) {
var items = mutableListOf( var items = mutableListOf(
BasicListItemWithImage(R.drawable.ic_pencil_grey600_24dp, context.getString(R.string.nc_promote)), BasicListItemWithImage(R.drawable.ic_pencil_grey600_24dp, context.getString(R.string.nc_promote)),
BasicListItemWithImage(R.drawable.ic_pencil_grey600_24dp, context.getString(R.string.nc_demote)), BasicListItemWithImage(R.drawable.ic_pencil_grey600_24dp, context.getString(R.string.nc_demote)),
BasicListItemWithImage(R.drawable.ic_delete_grey600_24dp, BasicListItemWithImage(
context.getString(R.string.nc_remove_participant)) R.drawable.ic_delete_grey600_24dp,
context.getString(R.string.nc_remove_participant)
)
) )
if (!conversation!!.canModerate(conversationUser)) { if (!conversation!!.canModerate(conversationUser)) {
@ -629,7 +701,6 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
} }
} }
if (items.isNotEmpty()) { if (items.isNotEmpty()) {
MaterialDialog(activity!!, BottomSheet(WRAP_CONTENT)).show { MaterialDialog(activity!!, BottomSheet(WRAP_CONTENT)).show {
cornerRadius(res = R.dimen.corner_radius) cornerRadius(res = R.dimen.corner_radius)
@ -639,14 +710,22 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
if (index == 0) { if (index == 0) {
if (participant.type == Participant.ParticipantType.MODERATOR) { if (participant.type == Participant.ParticipantType.MODERATOR) {
ncApi.demoteModeratorToUser(credentials, ApiUtils.getUrlForModerators(conversationUser.baseUrl, conversation!!.token), participant.userId) ncApi.demoteModeratorToUser(
credentials,
ApiUtils.getUrlForModerators(conversationUser.baseUrl, conversation!!.token),
participant.userId
)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe { .subscribe {
getListOfParticipants() getListOfParticipants()
} }
} else if (participant.type == Participant.ParticipantType.USER) { } else if (participant.type == Participant.ParticipantType.USER) {
ncApi.promoteUserToModerator(credentials, ApiUtils.getUrlForModerators(conversationUser.baseUrl, conversation!!.token), participant.userId) ncApi.promoteUserToModerator(
credentials,
ApiUtils.getUrlForModerators(conversationUser.baseUrl, conversation!!.token),
participant.userId
)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe { .subscribe {
@ -655,16 +734,32 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
} }
} else if (index == 1) { } else if (index == 1) {
if (participant.type == Participant.ParticipantType.GUEST || if (participant.type == Participant.ParticipantType.GUEST ||
participant.type == Participant.ParticipantType.USER_FOLLOWING_LINK) { participant.type == Participant.ParticipantType.USER_FOLLOWING_LINK
ncApi.removeParticipantFromConversation(credentials, ApiUtils.getUrlForRemovingParticipantFromConversation(conversationUser.baseUrl, conversation!!.token, true), participant.sessionId) ) {
ncApi.removeParticipantFromConversation(
credentials,
ApiUtils.getUrlForRemovingParticipantFromConversation(
conversationUser.baseUrl,
conversation!!.token,
true
),
participant.sessionId
)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe { .subscribe {
getListOfParticipants() getListOfParticipants()
} }
} else { } else {
ncApi.removeParticipantFromConversation(credentials, ApiUtils.getUrlForRemovingParticipantFromConversation(conversationUser.baseUrl, conversation!!.token, false), participant.userId) ncApi.removeParticipantFromConversation(
credentials,
ApiUtils.getUrlForRemovingParticipantFromConversation(
conversationUser.baseUrl,
conversation!!.token,
false
),
participant.userId
)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe { .subscribe {
@ -678,7 +773,7 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
} }
} }
return true; return true
} }
companion object { companion object {

View File

@ -53,5 +53,4 @@ abstract class ButterKnifeController : Controller {
unbinder!!.unbind() unbinder!!.unbind()
unbinder = null unbinder = null
} }
} }

View File

@ -30,7 +30,8 @@ interface ListItemWithImage {
data class BasicListItemWithImage( data class BasicListItemWithImage(
@DrawableRes val iconRes: Int, @DrawableRes val iconRes: Int,
override val title: String) : ListItemWithImage { override val title: String
) : ListItemWithImage {
override fun populateIcon(imageView: ImageView) { override fun populateIcon(imageView: ImageView) {
imageView.setImageResource(iconRes) imageView.setImageResource(iconRes)

View File

@ -23,7 +23,6 @@ package com.nextcloud.talk.controllers.bottomsheet.items
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.WhichButton import com.afollestad.materialdialogs.WhichButton
@ -34,14 +33,14 @@ import com.afollestad.materialdialogs.internal.rtl.RtlTextView
import com.afollestad.materialdialogs.list.getItemSelector import com.afollestad.materialdialogs.list.getItemSelector
import com.afollestad.materialdialogs.utils.MDUtil.inflate import com.afollestad.materialdialogs.utils.MDUtil.inflate
import com.afollestad.materialdialogs.utils.MDUtil.maybeSetTextColor import com.afollestad.materialdialogs.utils.MDUtil.maybeSetTextColor
import com.google.android.material.textview.MaterialTextView
import com.nextcloud.talk.R import com.nextcloud.talk.R
private const val KEY_ACTIVATED_INDEX = "activated_index" private const val KEY_ACTIVATED_INDEX = "activated_index"
internal class ListItemViewHolder( internal class ListItemViewHolder(
itemView: View, itemView: View,
private val adapter: ListIconDialogAdapter<*>) : RecyclerView.ViewHolder(itemView), View.OnClickListener { private val adapter: ListIconDialogAdapter<*>
) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
init { init {
itemView.setOnClickListener(this) itemView.setOnClickListener(this)
} }
@ -57,7 +56,8 @@ internal class ListIconDialogAdapter<IT : ListItemWithImage>(
private var items: List<IT>, private var items: List<IT>,
disabledItems: IntArray?, disabledItems: IntArray?,
private var waitForPositiveButton: Boolean, private var waitForPositiveButton: Boolean,
private var selection: ListItemListener<IT>) : RecyclerView.Adapter<ListItemViewHolder>(), DialogAdapter<IT, ListItemListener<IT>> { private var selection: ListItemListener<IT>
) : RecyclerView.Adapter<ListItemViewHolder>(), DialogAdapter<IT, ListItemListener<IT>> {
private var disabledIndices: IntArray = disabledItems ?: IntArray(0) private var disabledIndices: IntArray = disabledItems ?: IntArray(0)
@ -82,7 +82,8 @@ internal class ListIconDialogAdapter<IT : ListItemWithImage>(
override fun onCreateViewHolder( override fun onCreateViewHolder(
parent: ViewGroup, parent: ViewGroup,
viewType: Int): ListItemViewHolder { viewType: Int
): ListItemViewHolder {
val listItemView: View = parent.inflate(dialog.windowContext, R.layout.menu_item_sheet) val listItemView: View = parent.inflate(dialog.windowContext, R.layout.menu_item_sheet)
val viewHolder = ListItemViewHolder( val viewHolder = ListItemViewHolder(
itemView = listItemView, itemView = listItemView,
@ -96,7 +97,8 @@ internal class ListIconDialogAdapter<IT : ListItemWithImage>(
override fun onBindViewHolder( override fun onBindViewHolder(
holder: ListItemViewHolder, holder: ListItemViewHolder,
position: Int) { position: Int
) {
holder.itemView.isEnabled = !disabledIndices.contains(position) holder.itemView.isEnabled = !disabledIndices.contains(position)
val currentItem = items[position] val currentItem = items[position]
@ -122,7 +124,8 @@ internal class ListIconDialogAdapter<IT : ListItemWithImage>(
override fun replaceItems( override fun replaceItems(
items: List<IT>, items: List<IT>,
listener: ListItemListener<IT>) { listener: ListItemListener<IT>
) {
this.items = items this.items = items
if (listener != null) { if (listener != null) {
this.selection = listener this.selection = listener

View File

@ -30,11 +30,13 @@ import com.afollestad.materialdialogs.list.getListAdapter
typealias ListItemListener<IT> = typealias ListItemListener<IT> =
((dialog: MaterialDialog, index: Int, item: IT) -> Unit)? ((dialog: MaterialDialog, index: Int, item: IT) -> Unit)?
@CheckResult fun <IT : ListItemWithImage> MaterialDialog.listItemsWithImage( @CheckResult
fun <IT : ListItemWithImage> MaterialDialog.listItemsWithImage(
items: List<IT>, items: List<IT>,
disabledIndices: IntArray? = null, disabledIndices: IntArray? = null,
waitForPositiveButton: Boolean = true, waitForPositiveButton: Boolean = true,
selection: ListItemListener<IT> = null): MaterialDialog { selection: ListItemListener<IT> = null
): MaterialDialog {
if (getListAdapter() != null) { if (getListAdapter() != null) {
return updateListItemsWithImage( return updateListItemsWithImage(
@ -58,7 +60,8 @@ typealias ListItemListener<IT> =
fun MaterialDialog.updateListItemsWithImage( fun MaterialDialog.updateListItemsWithImage(
items: List<ListItemWithImage>, items: List<ListItemWithImage>,
disabledIndices: IntArray? = null): MaterialDialog { disabledIndices: IntArray? = null
): MaterialDialog {
val adapter = getListAdapter() val adapter = getListAdapter()
check(adapter != null) { check(adapter != null) {
"updateGridItems(...) can't be used before you've created a bottom sheet grid dialog." "updateGridItems(...) can't be used before you've created a bottom sheet grid dialog."

View File

@ -20,6 +20,4 @@
package com.nextcloud.talk.events package com.nextcloud.talk.events
class CallNotificationClick { class CallNotificationClick
}

View File

@ -33,7 +33,11 @@ import android.provider.ContactsContract
import android.util.Log import android.util.Log
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.os.ConfigurationCompat import androidx.core.os.ConfigurationCompat
import androidx.work.* import androidx.work.Data
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import androidx.work.Worker
import androidx.work.WorkerParameters
import autodagger.AutoInjector import autodagger.AutoInjector
import com.bluelinelabs.conductor.Controller import com.bluelinelabs.conductor.Controller
import com.google.gson.Gson import com.google.gson.Gson
@ -54,7 +58,6 @@ import okhttp3.MediaType
import okhttp3.RequestBody import okhttp3.RequestBody
import javax.inject.Inject import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class) @AutoInjector(NextcloudTalkApplication::class)
class ContactAddressBookWorker(val context: Context, workerParameters: WorkerParameters) : class ContactAddressBookWorker(val context: Context, workerParameters: WorkerParameters) :
Worker(context, workerParameters) { Worker(context, workerParameters) {
@ -85,7 +88,7 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
} }
val deleteAll = inputData.getBoolean(DELETE_ALL, false) val deleteAll = inputData.getBoolean(DELETE_ALL, false)
if(deleteAll){ if (deleteAll) {
deleteAllLinkedAccounts() deleteAllLinkedAccounts()
return Result.success() return Result.success()
} }
@ -99,7 +102,7 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
} }
} }
if(AccountManager.get(context).getAccountsByType(accountType).isEmpty()){ if (AccountManager.get(context).getAccountsByType(accountType).isEmpty()) {
AccountManager.get(context).addAccountExplicitly(Account(accountName, accountType), "", null) AccountManager.get(context).addAccountExplicitly(Account(accountName, accountType), "", null)
} else { } else {
Log.d(TAG, "Account already exists") Log.d(TAG, "Account already exists")
@ -107,7 +110,7 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
val deviceContactsWithNumbers = collectContactsWithPhoneNumbersFromDevice() val deviceContactsWithNumbers = collectContactsWithPhoneNumbersFromDevice()
if(deviceContactsWithNumbers.isNotEmpty()){ if (deviceContactsWithNumbers.isNotEmpty()) {
val currentLocale = ConfigurationCompat.getLocales(context.resources.configuration)[0].country val currentLocale = ConfigurationCompat.getLocales(context.resources.configuration)[0].country
val map = mutableMapOf<String, Any>() val map = mutableMapOf<String, Any>()
@ -119,7 +122,8 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
ncApi.searchContactsByPhoneNumber( ncApi.searchContactsByPhoneNumber(
ApiUtils.getCredentials(currentUser.username, currentUser.token), ApiUtils.getCredentials(currentUser.username, currentUser.token),
ApiUtils.getUrlForSearchByNumber(currentUser.baseUrl), ApiUtils.getUrlForSearchByNumber(currentUser.baseUrl),
RequestBody.create(MediaType.parse("application/json"), json)) RequestBody.create(MediaType.parse("application/json"), json)
)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<ContactsByNumberOverall> { .subscribe(object : Observer<ContactsByNumberOverall> {
@ -163,7 +167,8 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
contactCursor.moveToFirst() contactCursor.moveToFirst()
for (i in 0 until contactCursor.count) { for (i in 0 until contactCursor.count) {
val id = contactCursor.getString(contactCursor.getColumnIndex(ContactsContract.Contacts._ID)) val id = contactCursor.getString(contactCursor.getColumnIndex(ContactsContract.Contacts._ID))
val lookup = contactCursor.getString(contactCursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY)) val lookup =
contactCursor.getString(contactCursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY))
deviceContactsWithNumbers[lookup] = getPhoneNumbersFromDeviceContact(id) deviceContactsWithNumbers[lookup] = getPhoneNumbersFromDeviceContact(id)
contactCursor.moveToNext() contactCursor.moveToNext()
} }
@ -183,8 +188,11 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName) .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName)
.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType) .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType)
.build() .build()
val count = context.contentResolver.delete(rawContactUri, ContactsContract.RawContacts.CONTACT_ID + " " + val count = context.contentResolver.delete(
"LIKE \"" + id + "\"", null) rawContactUri,
ContactsContract.RawContacts.CONTACT_ID + " " + "LIKE \"" + id + "\"",
null
)
Log.d(TAG, "deleted $count linked accounts for id $id") Log.d(TAG, "deleted $count linked accounts for id $id")
} }
@ -193,7 +201,10 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true") .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName) .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName)
.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType) .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType)
.appendQueryParameter(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/vnd.com.nextcloud.talk2.chat") .appendQueryParameter(
ContactsContract.Data.MIMETYPE,
"vnd.android.cursor.item/vnd.com.nextcloud.talk2.chat"
)
.build() .build()
val rawContactsCursor = context.contentResolver.query( val rawContactsCursor = context.contentResolver.query(
@ -207,10 +218,15 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
if (rawContactsCursor != null) { if (rawContactsCursor != null) {
if (rawContactsCursor.count > 0) { if (rawContactsCursor.count > 0) {
while (rawContactsCursor.moveToNext()) { while (rawContactsCursor.moveToNext()) {
val lookupKey = rawContactsCursor.getString(rawContactsCursor.getColumnIndex(ContactsContract.Data.LOOKUP_KEY)) val lookupKey =
val contactId = rawContactsCursor.getString(rawContactsCursor.getColumnIndex(ContactsContract.Data.CONTACT_ID)) rawContactsCursor.getString(rawContactsCursor.getColumnIndex(ContactsContract.Data.LOOKUP_KEY))
val contactId =
rawContactsCursor.getString(rawContactsCursor.getColumnIndex(ContactsContract.Data.CONTACT_ID))
if (contactsWithAssociatedPhoneNumbers == null || !contactsWithAssociatedPhoneNumbers.containsKey(lookupKey)) { if (contactsWithAssociatedPhoneNumbers == null || !contactsWithAssociatedPhoneNumbers.containsKey(
lookupKey
)
) {
deleteLinkedAccount(contactId) deleteLinkedAccount(contactId)
} }
} }
@ -222,9 +238,10 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
} }
private fun createLinkedAccounts(contactsWithAssociatedPhoneNumbers: Map<String, String>?) { private fun createLinkedAccounts(contactsWithAssociatedPhoneNumbers: Map<String, String>?) {
fun hasLinkedAccount(id: String) : Boolean { fun hasLinkedAccount(id: String): Boolean {
var hasLinkedAccount = false var hasLinkedAccount = false
val where = ContactsContract.Data.MIMETYPE + " = ? AND " + ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID + " = ?" val where =
ContactsContract.Data.MIMETYPE + " = ? AND " + ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID + " = ?"
val params = arrayOf(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, id) val params = arrayOf(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, id)
val rawContactUri = ContactsContract.Data.CONTENT_URI val rawContactUri = ContactsContract.Data.CONTENT_URI
@ -232,7 +249,10 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true") .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName) .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName)
.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType) .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType)
.appendQueryParameter(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/vnd.com.nextcloud.talk2.chat") .appendQueryParameter(
ContactsContract.Data.MIMETYPE,
"vnd.android.cursor.item/vnd.com.nextcloud.talk2.chat"
)
.build() .build()
val rawContactsCursor = context.contentResolver.query( val rawContactsCursor = context.contentResolver.query(
@ -263,14 +283,15 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
null, null,
null, null,
null, null,
null) null
)
if (contactCursor != null) { if (contactCursor != null) {
if (contactCursor.count > 0) { if (contactCursor.count > 0) {
contactCursor.moveToFirst() contactCursor.moveToFirst()
val id = contactCursor.getString(contactCursor.getColumnIndex(ContactsContract.Contacts._ID)) val id = contactCursor.getString(contactCursor.getColumnIndex(ContactsContract.Contacts._ID))
if(hasLinkedAccount(id)){ if (hasLinkedAccount(id)) {
return return
} }
@ -285,34 +306,60 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
val rawContactsUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon().build() val rawContactsUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon().build()
val dataUri = ContactsContract.Data.CONTENT_URI.buildUpon().build() val dataUri = ContactsContract.Data.CONTENT_URI.buildUpon().build()
ops.add(ContentProviderOperation ops.add(
ContentProviderOperation
.newInsert(rawContactsUri) .newInsert(rawContactsUri)
.withValue(ContactsContract.RawContacts.ACCOUNT_NAME, accountName) .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, accountName)
.withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType) .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType)
.withValue(ContactsContract.RawContacts.AGGREGATION_MODE, .withValue(
ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT) ContactsContract.RawContacts.AGGREGATION_MODE,
ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT
)
.withValue(ContactsContract.RawContacts.SYNC2, cloudId) .withValue(ContactsContract.RawContacts.SYNC2, cloudId)
.build()) .build()
ops.add(ContentProviderOperation )
ops.add(
ContentProviderOperation
.newInsert(dataUri) .newInsert(dataUri)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) .withValue(
ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
)
.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, numbers[0]) .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, numbers[0])
.build()) .build()
ops.add(ContentProviderOperation )
ops.add(
ContentProviderOperation
.newInsert(dataUri) .newInsert(dataUri)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) .withValue(
ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE
)
.withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName) .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName)
.build()) .build()
ops.add(ContentProviderOperation )
ops.add(
ContentProviderOperation
.newInsert(dataUri) .newInsert(dataUri)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
.withValue(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/vnd.com.nextcloud.talk2.chat") .withValue(
ContactsContract.Data.MIMETYPE,
"vnd.android.cursor.item/vnd.com.nextcloud.talk2.chat"
)
.withValue(ContactsContract.Data.DATA1, cloudId) .withValue(ContactsContract.Data.DATA1, cloudId)
.withValue(ContactsContract.Data.DATA2, String.format(context.resources.getString(R .withValue(
.string.nc_phone_book_integration_chat_via), accountName)) ContactsContract.Data.DATA2,
.build()) String.format(
context.resources.getString(
R.string.nc_phone_book_integration_chat_via
),
accountName
)
)
.build()
)
try { try {
context.contentResolver.applyBatch(ContactsContract.AUTHORITY, ops) context.contentResolver.applyBatch(ContactsContract.AUTHORITY, ops)
@ -322,8 +369,11 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
Log.e(javaClass.simpleName, "", e) Log.e(javaClass.simpleName, "", e)
} }
Log.d(TAG, "added new entry for contact $displayName (cloudId: $cloudId | lookupKey: $lookupKey" + Log.d(
" | id: $id)") TAG,
"added new entry for contact $displayName (cloudId: $cloudId | lookupKey: $lookupKey" +
" | id: $id)"
)
} }
contactCursor.close() contactCursor.close()
} }
@ -341,18 +391,21 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
} }
private fun getDisplayNameFromDeviceContact(id: String?): String? { private fun getDisplayNameFromDeviceContact(id: String?): String? {
var displayName:String? = null var displayName: String? = null
val whereName = ContactsContract.Data.MIMETYPE + " = ? AND " + ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID + " = ?" val whereName =
ContactsContract.Data.MIMETYPE + " = ? AND " + ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID + " = ?"
val whereNameParams = arrayOf(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, id) val whereNameParams = arrayOf(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, id)
val nameCursor = context.contentResolver.query( val nameCursor = context.contentResolver.query(
ContactsContract.Data.CONTENT_URI, ContactsContract.Data.CONTENT_URI,
null, null,
whereName, whereName,
whereNameParams, whereNameParams,
ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME) ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME
)
if (nameCursor != null) { if (nameCursor != null) {
while (nameCursor.moveToNext()) { while (nameCursor.moveToNext()) {
displayName = nameCursor.getString(nameCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME)) displayName =
nameCursor.getString(nameCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME))
} }
nameCursor.close() nameCursor.close()
} }
@ -366,7 +419,8 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
null, null,
ContactsContract.Data.CONTACT_ID + " = " + id, ContactsContract.Data.CONTACT_ID + " = " + id,
null, null,
null) null
)
if (phonesNumbersCursor != null) { if (phonesNumbersCursor != null) {
while (phonesNumbersCursor.moveToNext()) { while (phonesNumbersCursor.moveToNext()) {
@ -374,7 +428,7 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
} }
phonesNumbersCursor.close() phonesNumbersCursor.close()
} }
if(numbers.size > 0){ if (numbers.size > 0) {
Log.d(TAG, "Found ${numbers.size} phone numbers for contact with id $id") Log.d(TAG, "Found ${numbers.size} phone numbers for contact with id $id")
} }
return numbers return numbers
@ -398,32 +452,51 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
const val DELETE_ALL = "DELETE_ALL" const val DELETE_ALL = "DELETE_ALL"
fun run(context: Context) { fun run(context: Context) {
if (ContextCompat.checkSelfPermission(context, if (ContextCompat.checkSelfPermission(
Manifest.permission.WRITE_CONTACTS) == PackageManager.PERMISSION_GRANTED && context,
ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_CONTACTS
Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) { ) == PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(
context,
Manifest.permission.READ_CONTACTS
) == PackageManager.PERMISSION_GRANTED
) {
WorkManager WorkManager
.getInstance() .getInstance()
.enqueue(OneTimeWorkRequest.Builder(ContactAddressBookWorker::class.java) .enqueue(
OneTimeWorkRequest.Builder(ContactAddressBookWorker::class.java)
.setInputData(Data.Builder().putBoolean(KEY_FORCE, false).build()) .setInputData(Data.Builder().putBoolean(KEY_FORCE, false).build())
.build()) .build()
)
} }
} }
fun checkPermission(controller: Controller, context: Context): Boolean { fun checkPermission(controller: Controller, context: Context): Boolean {
if (ContextCompat.checkSelfPermission(context, if (ContextCompat.checkSelfPermission(
Manifest.permission.WRITE_CONTACTS) != PackageManager.PERMISSION_GRANTED || context,
ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_CONTACTS
Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) { ) != PackageManager.PERMISSION_GRANTED ||
controller.requestPermissions(arrayOf(Manifest.permission.WRITE_CONTACTS, ContextCompat.checkSelfPermission(
Manifest.permission.READ_CONTACTS), REQUEST_PERMISSION) context,
Manifest.permission.READ_CONTACTS
) != PackageManager.PERMISSION_GRANTED
) {
controller.requestPermissions(
arrayOf(
Manifest.permission.WRITE_CONTACTS,
Manifest.permission.READ_CONTACTS
),
REQUEST_PERMISSION
)
return false return false
} else { } else {
WorkManager WorkManager
.getInstance() .getInstance()
.enqueue(OneTimeWorkRequest.Builder(ContactAddressBookWorker::class.java) .enqueue(
OneTimeWorkRequest.Builder(ContactAddressBookWorker::class.java)
.setInputData(Data.Builder().putBoolean(KEY_FORCE, true).build()) .setInputData(Data.Builder().putBoolean(KEY_FORCE, true).build())
.build()) .build()
)
return true return true
} }
} }
@ -431,9 +504,11 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
fun deleteAll() { fun deleteAll() {
WorkManager WorkManager
.getInstance() .getInstance()
.enqueue(OneTimeWorkRequest.Builder(ContactAddressBookWorker::class.java) .enqueue(
OneTimeWorkRequest.Builder(ContactAddressBookWorker::class.java)
.setInputData(Data.Builder().putBoolean(DELETE_ALL, true).build()) .setInputData(Data.Builder().putBoolean(DELETE_ALL, true).build())
.build()) .build()
)
} }
} }
} }

View File

@ -33,10 +33,13 @@ import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.database.user.UserUtils import com.nextcloud.talk.utils.database.user.UserUtils
import com.nextcloud.talk.utils.preferences.AppPreferences import com.nextcloud.talk.utils.preferences.AppPreferences
import okhttp3.ResponseBody import okhttp3.ResponseBody
import java.io.* import java.io.BufferedInputStream
import java.io.File
import java.io.FileOutputStream
import java.io.InputStream
import java.io.OutputStream
import javax.inject.Inject import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class) @AutoInjector(NextcloudTalkApplication::class)
class DownloadFileToCacheWorker(val context: Context, workerParameters: WorkerParameters) : class DownloadFileToCacheWorker(val context: Context, workerParameters: WorkerParameters) :
Worker(context, workerParameters) { Worker(context, workerParameters) {
@ -87,7 +90,8 @@ class DownloadFileToCacheWorker(val context: Context, workerParameters: WorkerPa
private fun downloadFile(currentUser: UserEntity, url: String, fileName: String): Result { private fun downloadFile(currentUser: UserEntity, url: String, fileName: String): Result {
val downloadCall = ncApi.downloadFile( val downloadCall = ncApi.downloadFile(
ApiUtils.getCredentials(currentUser.username, currentUser.token), ApiUtils.getCredentials(currentUser.username, currentUser.token),
url) url
)
return executeDownload(downloadCall.execute().body(), fileName) return executeDownload(downloadCall.execute().body(), fileName)
} }
@ -152,6 +156,5 @@ class DownloadFileToCacheWorker(val context: Context, workerParameters: WorkerPa
const val KEY_FILE_SIZE = "KEY_FILE_SIZE" const val KEY_FILE_SIZE = "KEY_FILE_SIZE"
const val PROGRESS = "PROGRESS" const val PROGRESS = "PROGRESS"
const val SUCCESS = "SUCCESS" const val SUCCESS = "SUCCESS"
} }
} }

View File

@ -23,7 +23,11 @@ package com.nextcloud.talk.jobs
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import android.util.Log import android.util.Log
import androidx.work.* import androidx.work.Data
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import androidx.work.Worker
import androidx.work.WorkerParameters
import autodagger.AutoInjector import autodagger.AutoInjector
import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication
@ -46,10 +50,9 @@ import retrofit2.Response
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.InputStream import java.io.InputStream
import java.util.* import java.util.ArrayList
import javax.inject.Inject import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class) @AutoInjector(NextcloudTalkApplication::class)
class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerParameters) : class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerParameters) :
Worker(context, workerParameters) { Worker(context, workerParameters) {
@ -107,8 +110,14 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
return requestBody return requestBody
} }
private fun uploadFile(currentUser: UserEntity, ncTargetpath: String?, filename: String, roomToken: String?, private fun uploadFile(
requestBody: RequestBody?, sourcefileUri: Uri) { currentUser: UserEntity,
ncTargetpath: String?,
filename: String,
roomToken: String?,
requestBody: RequestBody?,
sourcefileUri: Uri
) {
ncApi.uploadFile( ncApi.uploadFile(
ApiUtils.getCredentials(currentUser.username, currentUser.token), ApiUtils.getCredentials(currentUser.username, currentUser.token),
ApiUtils.getUrlForFileUpload(currentUser.baseUrl, currentUser.userId, ncTargetpath, filename), ApiUtils.getUrlForFileUpload(currentUser.baseUrl, currentUser.userId, ncTargetpath, filename),

View File

@ -46,4 +46,3 @@ class ChatUtils {
} }
} }
} }

View File

@ -22,11 +22,28 @@ package com.nextcloud.talk.models.json.converters
import com.bluelinelabs.logansquare.typeconverters.StringBasedTypeConverter import com.bluelinelabs.logansquare.typeconverters.StringBasedTypeConverter
import com.nextcloud.talk.models.json.chat.ChatMessage import com.nextcloud.talk.models.json.chat.ChatMessage
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.CALL_ENDED
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.* import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.CALL_JOINED
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.CALL_LEFT
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.CALL_STARTED
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.CONVERSATION_CREATED
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.CONVERSATION_RENAMED
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.DUMMY
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.FILE_SHARED
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.GUESTS_ALLOWED
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.GUESTS_DISALLOWED
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.LOBBY_NONE
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.LOBBY_NON_MODERATORS
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.LOBBY_OPEN_TO_EVERYONE
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.MODERATOR_DEMOTED
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.MODERATOR_PROMOTED
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.PARENT_MESSAGE_DELETED
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.PASSWORD_REMOVED
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.PASSWORD_SET
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.USER_ADDED
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.USER_REMOVED
/* /*
conversation_created - {actor} created the conversation conversation_created - {actor} created the conversation
conversation_renamed - {actor} renamed the conversation from "foo" to "bar" conversation_renamed - {actor} renamed the conversation from "foo" to "bar"
call_joined - {actor} joined the call call_joined - {actor} joined the call
@ -40,7 +57,6 @@ import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.*
user_removed - {actor} removed {user} from the conversation user_removed - {actor} removed {user} from the conversation
moderator_promoted - {actor} promoted {user} to moderator moderator_promoted - {actor} promoted {user} to moderator
moderator_demoted - {actor} demoted {user} from moderator moderator_demoted - {actor} demoted {user} from moderator
*/ */
class EnumSystemMessageTypeConverter : StringBasedTypeConverter<ChatMessage.SystemMessageType>() { class EnumSystemMessageTypeConverter : StringBasedTypeConverter<ChatMessage.SystemMessageType>() {
override fun getFromString(string: String): ChatMessage.SystemMessageType { override fun getFromString(string: String): ChatMessage.SystemMessageType {

View File

@ -20,12 +20,10 @@
package com.nextcloud.talk.receivers package com.nextcloud.talk.receivers
import android.app.NotificationChannelGroup
import android.app.NotificationManager import android.app.NotificationManager
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageInfo
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import android.util.Log import android.util.Log
@ -34,7 +32,6 @@ import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.utils.NotificationUtils import com.nextcloud.talk.utils.NotificationUtils
import com.nextcloud.talk.utils.database.user.UserUtils import com.nextcloud.talk.utils.database.user.UserUtils
import com.nextcloud.talk.utils.preferences.AppPreferences import com.nextcloud.talk.utils.preferences.AppPreferences
import javax.inject.Inject import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class) @AutoInjector(NextcloudTalkApplication::class)
@ -50,16 +47,20 @@ class PackageReplacedReceiver : BroadcastReceiver() {
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
if (intent != null && intent.action != null && if (intent != null && intent.action != null &&
intent.action == "android.intent.action.MY_PACKAGE_REPLACED") { intent.action == "android.intent.action.MY_PACKAGE_REPLACED"
) {
try { try {
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0) val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
if (packageInfo.versionCode > 43 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (packageInfo.versionCode > 43 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationManager = context.getSystemService(Context val notificationManager = context.getSystemService(
.NOTIFICATION_SERVICE) as NotificationManager Context
.NOTIFICATION_SERVICE
) as NotificationManager
if (!appPreferences.isNotificationChannelUpgradedToV2) { if (!appPreferences.isNotificationChannelUpgradedToV2) {
for (notificationChannelGroup in notificationManager for (
.notificationChannelGroups) { notificationChannelGroup in notificationManager.notificationChannelGroups
) {
notificationManager.deleteNotificationChannelGroup(notificationChannelGroup.id) notificationManager.deleteNotificationChannelGroup(notificationChannelGroup.id)
} }
@ -80,7 +81,6 @@ class PackageReplacedReceiver : BroadcastReceiver() {
} catch (e: PackageManager.NameNotFoundException) { } catch (e: PackageManager.NameNotFoundException) {
Log.e(TAG, "Failed to fetch package info") Log.e(TAG, "Failed to fetch package info")
} }
} }
} }

View File

@ -32,7 +32,6 @@ import com.nextcloud.talk.R
import com.nextcloud.talk.components.filebrowser.controllers.BrowserController import com.nextcloud.talk.components.filebrowser.controllers.BrowserController
import com.nextcloud.talk.controllers.ChatController import com.nextcloud.talk.controllers.ChatController
class AttachmentDialog(val activity: Activity, var chatController: ChatController) : BottomSheetDialog(activity) { class AttachmentDialog(val activity: Activity, var chatController: ChatController) : BottomSheetDialog(activity) {
@BindView(R.id.txt_attach_file_from_local) @BindView(R.id.txt_attach_file_from_local)
@ -54,7 +53,7 @@ class AttachmentDialog(val activity: Activity, var chatController: ChatControlle
var serverName = chatController.conversationUser?.serverName var serverName = chatController.conversationUser?.serverName
attachFromCloud?.text = chatController.resources?.let { attachFromCloud?.text = chatController.resources?.let {
if(serverName.isNullOrEmpty()){ if (serverName.isNullOrEmpty()) {
serverName = it.getString(R.string.nc_server_product_name) serverName = it.getString(R.string.nc_server_product_name)
} }
String.format(it.getString(R.string.nc_upload_from_cloud), serverName) String.format(it.getString(R.string.nc_upload_from_cloud), serverName)

View File

@ -30,11 +30,12 @@ import com.nextcloud.talk.R
import com.nextcloud.talk.controllers.ProfileController import com.nextcloud.talk.controllers.ProfileController
import com.nextcloud.talk.models.json.userprofile.Scope import com.nextcloud.talk.models.json.userprofile.Scope
class ScopeDialog(
class ScopeDialog(con: Context, con: Context,
private val userInfoAdapter: ProfileController.UserInfoAdapter, private val userInfoAdapter: ProfileController.UserInfoAdapter,
private val field: ProfileController.Field, private val field: ProfileController.Field,
private val position: Int) : private val position: Int
) :
BottomSheetDialog(con) { BottomSheetDialog(con) {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)

View File

@ -32,7 +32,8 @@ import com.nextcloud.talk.R
import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.models.ImportAccount import com.nextcloud.talk.models.ImportAccount
import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.models.database.UserEntity
import java.util.* import java.util.ArrayList
import java.util.Arrays
object AccountUtils { object AccountUtils {
@ -60,14 +61,15 @@ object AccountUtils {
break break
} }
} else { } else {
if (internalUserEntity.username == importAccount.username && (internalUserEntity if (internalUserEntity.username == importAccount.username &&
.baseUrl == "http://" + importAccount.baseUrl || (
internalUserEntity.baseUrl == "https://" + importAccount internalUserEntity.baseUrl == "http://" + importAccount.baseUrl ||
.baseUrl)) { internalUserEntity.baseUrl == "https://" + importAccount.baseUrl
)
) {
accountFound = true accountFound = true
break break
} }
} }
} else { } else {
accountFound = true accountFound = true
@ -88,8 +90,12 @@ object AccountUtils {
val packageManager = context.packageManager val packageManager = context.packageManager
var appName = "" var appName = ""
try { try {
appName = packageManager.getApplicationLabel(packageManager.getApplicationInfo(packageName, appName = packageManager.getApplicationLabel(
PackageManager.GET_META_DATA)) as String packageManager.getApplicationInfo(
packageName,
PackageManager.GET_META_DATA
)
) as String
} catch (e: PackageManager.NameNotFoundException) { } catch (e: PackageManager.NameNotFoundException) {
Log.e(TAG, "Failed to get app name based on package") Log.e(TAG, "Failed to get app name based on package")
} }
@ -103,7 +109,10 @@ object AccountUtils {
val packageInfo = pm.getPackageInfo(context.getString(R.string.nc_import_accounts_from), 0) val packageInfo = pm.getPackageInfo(context.getString(R.string.nc_import_accounts_from), 0)
if (packageInfo.versionCode >= 30060151) { if (packageInfo.versionCode >= 30060151) {
val ownSignatures = pm.getPackageInfo(context.packageName, PackageManager.GET_SIGNATURES).signatures val ownSignatures = pm.getPackageInfo(context.packageName, PackageManager.GET_SIGNATURES).signatures
val filesAppSignatures = pm.getPackageInfo(context.getString(R.string.nc_import_accounts_from), PackageManager.GET_SIGNATURES).signatures val filesAppSignatures = pm.getPackageInfo(
context.getString(R.string.nc_import_accounts_from),
PackageManager.GET_SIGNATURES
).signatures
if (Arrays.equals(ownSignatures, filesAppSignatures)) { if (Arrays.equals(ownSignatures, filesAppSignatures)) {
val accMgr = AccountManager.get(context) val accMgr = AccountManager.get(context)
@ -118,7 +127,7 @@ object AccountUtils {
} }
} }
} catch (appNotFoundException: PackageManager.NameNotFoundException) { } catch (appNotFoundException: PackageManager.NameNotFoundException) {
// ignore
} }
return false return false
@ -146,4 +155,3 @@ object AccountUtils {
return ImportAccount(username, password, urlString) return ImportAccount(username, password, urlString)
} }
} }

View File

@ -27,7 +27,13 @@ import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
import com.nextcloud.talk.controllers.ChatController import com.nextcloud.talk.controllers.ChatController
object ConductorRemapping { object ConductorRemapping {
fun remapChatController(router: Router, internalUserId: Long, roomTokenOrId: String, bundle: Bundle, replaceTop: Boolean) { fun remapChatController(
router: Router,
internalUserId: Long,
roomTokenOrId: String,
bundle: Bundle,
replaceTop: Boolean
) {
val tag = "$internalUserId@$roomTokenOrId" val tag = "$internalUserId@$roomTokenOrId"
if (router.getControllerWithTag(tag) != null) { if (router.getControllerWithTag(tag) != null) {
val backstack = router.backstack val backstack = router.backstack
@ -44,13 +50,17 @@ object ConductorRemapping {
router.setBackstack(backstack, HorizontalChangeHandler()) router.setBackstack(backstack, HorizontalChangeHandler())
} else { } else {
if (!replaceTop) { if (!replaceTop) {
router.pushController(RouterTransaction.with(ChatController(bundle)) router.pushController(
RouterTransaction.with(ChatController(bundle))
.pushChangeHandler(HorizontalChangeHandler()) .pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler()).tag(tag)) .popChangeHandler(HorizontalChangeHandler()).tag(tag)
)
} else { } else {
router.replaceTopController(RouterTransaction.with(ChatController(bundle)) router.replaceTopController(
RouterTransaction.with(ChatController(bundle))
.pushChangeHandler(HorizontalChangeHandler()) .pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler()).tag(tag)) .popChangeHandler(HorizontalChangeHandler()).tag(tag)
)
} }
} }
} }

View File

@ -21,8 +21,9 @@
package com.nextcloud.talk.utils package com.nextcloud.talk.utils
import java.text.DateFormat import java.text.DateFormat
import java.util.* import java.util.Calendar
import java.util.Date
import java.util.Locale
object DateUtils { object DateUtils {
fun getLocalDateTimeStringFromTimestamp(timestamp: Long): String { fun getLocalDateTimeStringFromTimestamp(timestamp: Long): String {
@ -30,14 +31,16 @@ object DateUtils {
val tz = cal.timeZone val tz = cal.timeZone
/* date formatter in local timezone */ /* date formatter in local timezone */
val format = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.SHORT, Locale val format = DateFormat.getDateTimeInstance(
.getDefault()) DateFormat.DEFAULT, DateFormat.SHORT,
Locale.getDefault()
)
format.timeZone = tz format.timeZone = tz
return format.format(Date(timestamp)) return format.format(Date(timestamp))
} }
fun getLocalDateStringFromTimestampForLobby(timestamp: Long): String { fun getLocalDateStringFromTimestampForLobby(timestamp: Long): String {
return getLocalDateTimeStringFromTimestamp(timestamp * 1000); return getLocalDateTimeStringFromTimestamp(timestamp * 1000)
} }
} }

View File

@ -21,12 +21,10 @@
package com.nextcloud.talk.utils package com.nextcloud.talk.utils
import com.nextcloud.talk.R import com.nextcloud.talk.R
import java.util.HashMap import java.util.HashMap
object DrawableUtils { object DrawableUtils {
fun getDrawableResourceIdForMimeType(mimetype: String): Int { fun getDrawableResourceIdForMimeType(mimetype: String): Int {
var localMimetype = mimetype var localMimetype = mimetype
val drawableMap = HashMap<String, Int>() val drawableMap = HashMap<String, Int>()
@ -55,15 +53,20 @@ object DrawableUtils {
drawableMap["application/vnd.google-earth.kmz"] = R.drawable.ic_mimetype_location drawableMap["application/vnd.google-earth.kmz"] = R.drawable.ic_mimetype_location
drawableMap["application/vnd.ms-excel"] = R.drawable.ic_mimetype_x_office_spreadsheet drawableMap["application/vnd.ms-excel"] = R.drawable.ic_mimetype_x_office_spreadsheet
drawableMap["application/vnd.ms-excel.addin.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_spreadsheet drawableMap["application/vnd.ms-excel.addin.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_spreadsheet
drawableMap["application/vnd.ms-excel.sheet.binary.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_spreadsheet drawableMap["application/vnd.ms-excel.sheet.binary.macroEnabled.12"] =
R.drawable.ic_mimetype_x_office_spreadsheet
drawableMap["application/vnd.ms-excel.sheet.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_spreadsheet drawableMap["application/vnd.ms-excel.sheet.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_spreadsheet
drawableMap["application/vnd.ms-excel.template.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_spreadsheet drawableMap["application/vnd.ms-excel.template.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_spreadsheet
drawableMap["application/vnd.ms-fontobject"] = R.drawable.ic_mimetype_image drawableMap["application/vnd.ms-fontobject"] = R.drawable.ic_mimetype_image
drawableMap["application/vnd.ms-powerpoint"] = R.drawable.ic_mimetype_x_office_presentation drawableMap["application/vnd.ms-powerpoint"] = R.drawable.ic_mimetype_x_office_presentation
drawableMap["application/vnd.ms-powerpoint.addin.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_presentation drawableMap["application/vnd.ms-powerpoint.addin.macroEnabled.12"] =
drawableMap["application/vnd.ms-powerpoint.presentation.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_presentation R.drawable.ic_mimetype_x_office_presentation
drawableMap["application/vnd.ms-powerpoint.slideshow.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_presentation drawableMap["application/vnd.ms-powerpoint.presentation.macroEnabled.12"] =
drawableMap["application/vnd.ms-powerpoint.template.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_presentation R.drawable.ic_mimetype_x_office_presentation
drawableMap["application/vnd.ms-powerpoint.slideshow.macroEnabled.12"] =
R.drawable.ic_mimetype_x_office_presentation
drawableMap["application/vnd.ms-powerpoint.template.macroEnabled.12"] =
R.drawable.ic_mimetype_x_office_presentation
drawableMap["application/vnd.ms-visio.drawing.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_document drawableMap["application/vnd.ms-visio.drawing.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_document
drawableMap["application/vnd.ms-visio.drawing"] = R.drawable.ic_mimetype_x_office_document drawableMap["application/vnd.ms-visio.drawing"] = R.drawable.ic_mimetype_x_office_document
drawableMap["application/vnd.ms-visio.stencil.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_document drawableMap["application/vnd.ms-visio.stencil.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_document
@ -72,20 +75,29 @@ object DrawableUtils {
drawableMap["application/vnd.ms-visio.template"] = R.drawable.ic_mimetype_x_office_document drawableMap["application/vnd.ms-visio.template"] = R.drawable.ic_mimetype_x_office_document
drawableMap["application/vnd.ms-word.template.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_document drawableMap["application/vnd.ms-word.template.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_document
drawableMap["application/vnd.oasis.opendocument.presentation"] = R.drawable.ic_mimetype_x_office_presentation drawableMap["application/vnd.oasis.opendocument.presentation"] = R.drawable.ic_mimetype_x_office_presentation
drawableMap["application/vnd.oasis.opendocument.presentation-template"] = R.drawable.ic_mimetype_x_office_presentation drawableMap["application/vnd.oasis.opendocument.presentation-template"] =
R.drawable.ic_mimetype_x_office_presentation
drawableMap["application/vnd.oasis.opendocument.spreadsheet"] = R.drawable.ic_mimetype_x_office_spreadsheet drawableMap["application/vnd.oasis.opendocument.spreadsheet"] = R.drawable.ic_mimetype_x_office_spreadsheet
drawableMap["application/vnd.oasis.opendocument.spreadsheet-template"] = R.drawable.ic_mimetype_x_office_spreadsheet drawableMap["application/vnd.oasis.opendocument.spreadsheet-template"] =
R.drawable.ic_mimetype_x_office_spreadsheet
drawableMap["application/vnd.oasis.opendocument.text"] = R.drawable.ic_mimetype_x_office_document drawableMap["application/vnd.oasis.opendocument.text"] = R.drawable.ic_mimetype_x_office_document
drawableMap["application/vnd.oasis.opendocument.text-master"] = R.drawable.ic_mimetype_x_office_document drawableMap["application/vnd.oasis.opendocument.text-master"] = R.drawable.ic_mimetype_x_office_document
drawableMap["application/vnd.oasis.opendocument.text-template"] = R.drawable.ic_mimetype_x_office_document drawableMap["application/vnd.oasis.opendocument.text-template"] = R.drawable.ic_mimetype_x_office_document
drawableMap["application/vnd.oasis.opendocument.text-web"] = R.drawable.ic_mimetype_x_office_document drawableMap["application/vnd.oasis.opendocument.text-web"] = R.drawable.ic_mimetype_x_office_document
drawableMap["application/vnd.openxmlformats-officedocument.presentationml.presentation"] = R.drawable.ic_mimetype_x_office_presentation drawableMap["application/vnd.openxmlformats-officedocument.presentationml.presentation"] =
drawableMap["application/vnd.openxmlformats-officedocument.presentationml.slideshow"] = R.drawable.ic_mimetype_x_office_presentation R.drawable.ic_mimetype_x_office_presentation
drawableMap["application/vnd.openxmlformats-officedocument.presentationml.template"] = R.drawable.ic_mimetype_x_office_presentation drawableMap["application/vnd.openxmlformats-officedocument.presentationml.slideshow"] =
drawableMap["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"] = R.drawable.ic_mimetype_x_office_spreadsheet R.drawable.ic_mimetype_x_office_presentation
drawableMap["application/vnd.openxmlformats-officedocument.spreadsheetml.template"] = R.drawable.ic_mimetype_x_office_spreadsheet drawableMap["application/vnd.openxmlformats-officedocument.presentationml.template"] =
drawableMap["application/vnd.openxmlformats-officedocument.wordprocessingml.document"] = R.drawable.ic_mimetype_x_office_document R.drawable.ic_mimetype_x_office_presentation
drawableMap["application/vnd.openxmlformats-officedocument.wordprocessingml.template"] = R.drawable.ic_mimetype_x_office_document drawableMap["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"] =
R.drawable.ic_mimetype_x_office_spreadsheet
drawableMap["application/vnd.openxmlformats-officedocument.spreadsheetml.template"] =
R.drawable.ic_mimetype_x_office_spreadsheet
drawableMap["application/vnd.openxmlformats-officedocument.wordprocessingml.document"] =
R.drawable.ic_mimetype_x_office_document
drawableMap["application/vnd.openxmlformats-officedocument.wordprocessingml.template"] =
R.drawable.ic_mimetype_x_office_document
drawableMap["application/vnd.visio"] = R.drawable.ic_mimetype_x_office_document drawableMap["application/vnd.visio"] = R.drawable.ic_mimetype_x_office_document
drawableMap["application/vnd.wordperfect"] = R.drawable.ic_mimetype_x_office_document drawableMap["application/vnd.wordperfect"] = R.drawable.ic_mimetype_x_office_document
drawableMap["application/x-7z-compressed"] = R.drawable.ic_mimetype_package_x_generic drawableMap["application/x-7z-compressed"] = R.drawable.ic_mimetype_package_x_generic

View File

@ -29,7 +29,7 @@ import com.nextcloud.talk.BuildConfig
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.io.IOException import java.io.IOException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.Date
object LoggingUtils { object LoggingUtils {
fun writeLogEntryToFile(context: Context, logEntry: String) { fun writeLogEntryToFile(context: Context, logEntry: String) {
@ -38,8 +38,10 @@ object LoggingUtils {
val logEntryWithDateTime = dateFormat.format(date) + ": " + logEntry + "\n" val logEntryWithDateTime = dateFormat.format(date) + ": " + logEntry + "\n"
try { try {
val outputStream = context.openFileOutput("nc_log.txt", val outputStream = context.openFileOutput(
Context.MODE_PRIVATE or Context.MODE_APPEND) "nc_log.txt",
Context.MODE_PRIVATE or Context.MODE_APPEND
)
outputStream.write(logEntryWithDateTime.toByteArray()) outputStream.write(logEntryWithDateTime.toByteArray())
outputStream.flush() outputStream.flush()
outputStream.close() outputStream.close()
@ -48,13 +50,12 @@ object LoggingUtils {
} catch (e: IOException) { } catch (e: IOException) {
e.printStackTrace() e.printStackTrace()
} }
} }
fun sendMailWithAttachment(context: Context) { fun sendMailWithAttachment(context: Context) {
val logFile = context.getFileStreamPath("nc_log.txt") val logFile = context.getFileStreamPath("nc_log.txt")
val emailIntent = Intent(Intent.ACTION_SEND) val emailIntent = Intent(Intent.ACTION_SEND)
val mailto = "mario@nextcloud.com" val mailto = "android@nextcloud.com"
emailIntent.putExtra(Intent.EXTRA_EMAIL, arrayOf(mailto)) emailIntent.putExtra(Intent.EXTRA_EMAIL, arrayOf(mailto))
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Talk logs") emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Talk logs")
emailIntent.type = "text/plain" emailIntent.type = "text/plain"

View File

@ -33,7 +33,7 @@ import android.service.notification.StatusBarNotification
import com.nextcloud.talk.R import com.nextcloud.talk.R
import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.models.database.UserEntity
import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.bundle.BundleKeys
import java.util.* import java.util.Objects
object NotificationUtils { object NotificationUtils {
val NOTIFICATION_CHANNEL_CALLS = "NOTIFICATION_CHANNEL_CALLS" val NOTIFICATION_CHANNEL_CALLS = "NOTIFICATION_CHANNEL_CALLS"
@ -47,25 +47,50 @@ object NotificationUtils {
return longArrayOf(0L, 400L, 800L, 600L, 800L, 800L, 800L, 1000L) return longArrayOf(0L, 400L, 800L, 600L, 800L, 800L, 800L, 1000L)
} }
fun getNotificationChannelId(channelName: String, fun getNotificationChannelId(
channelDescription: String, enableLights: Boolean, channelName: String,
importance: Int, sound: Uri, audioAttributes: AudioAttributes, vibrationPattern: LongArray?, bypassDnd: Boolean): String { channelDescription: String,
return Objects.hash(channelName, channelDescription, enableLights, importance, sound, audioAttributes, vibrationPattern, bypassDnd).toString() enableLights: Boolean,
importance: Int,
sound: Uri,
audioAttributes: AudioAttributes,
vibrationPattern: LongArray?,
bypassDnd: Boolean
): String {
return Objects.hash(
channelName,
channelDescription,
enableLights,
importance,
sound,
audioAttributes,
vibrationPattern,
bypassDnd
).toString()
} }
@TargetApi(Build.VERSION_CODES.O) @TargetApi(Build.VERSION_CODES.O)
fun createNotificationChannel(context: Context, fun createNotificationChannel(
channelId: String, channelName: String, context: Context,
channelDescription: String, enableLights: Boolean, channelId: String,
importance: Int, sound: Uri, audioAttributes: AudioAttributes, channelName: String,
vibrationPattern: LongArray?, bypassDnd: Boolean = false) { channelDescription: String,
enableLights: Boolean,
importance: Int,
sound: Uri,
audioAttributes: AudioAttributes,
vibrationPattern: LongArray?,
bypassDnd: Boolean = false
) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && notificationManager.getNotificationChannel(channelId) == null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && notificationManager.getNotificationChannel(channelId) == null) {
val channel = NotificationChannel(channelId, channelName, val channel = NotificationChannel(
importance) channelId, channelName,
importance
)
channel.description = channelDescription channel.description = channelDescription
channel.enableLights(enableLights) channel.enableLights(enableLights)
@ -84,8 +109,11 @@ object NotificationUtils {
} }
@TargetApi(Build.VERSION_CODES.O) @TargetApi(Build.VERSION_CODES.O)
fun createNotificationChannelGroup(context: Context, fun createNotificationChannelGroup(
groupId: String, groupName: CharSequence) { context: Context,
groupId: String,
groupName: CharSequence
) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
@ -113,12 +141,12 @@ object NotificationUtils {
} }
} }
} }
} }
fun cancelExistingNotificationWithId(context: Context?, conversationUser: UserEntity, notificationId: Long) { fun cancelExistingNotificationWithId(context: Context?, conversationUser: UserEntity, notificationId: Long) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && conversationUser.id != -1L && if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && conversationUser.id != -1L &&
context != null) { context != null
) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
@ -128,7 +156,10 @@ object NotificationUtils {
notification = statusBarNotification.notification notification = statusBarNotification.notification
if (notification != null && !notification.extras.isEmpty) { if (notification != null && !notification.extras.isEmpty) {
if (conversationUser.id == notification.extras.getLong(BundleKeys.KEY_INTERNAL_USER_ID) && notificationId == notification.extras.getLong(BundleKeys.KEY_NOTIFICATION_ID)) { if (conversationUser.id == notification.extras.getLong(BundleKeys.KEY_INTERNAL_USER_ID) && notificationId == notification.extras.getLong(
BundleKeys.KEY_NOTIFICATION_ID
)
) {
notificationManager.cancel(statusBarNotification.id) notificationManager.cancel(statusBarNotification.id)
} }
} }
@ -136,11 +167,14 @@ object NotificationUtils {
} }
} }
fun findNotificationForRoom(context: Context?, fun findNotificationForRoom(
context: Context?,
conversationUser: UserEntity, conversationUser: UserEntity,
roomTokenOrId: String): StatusBarNotification? { roomTokenOrId: String
): StatusBarNotification? {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && conversationUser.id != -1L && if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && conversationUser.id != -1L &&
context != null) { context != null
) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
@ -150,7 +184,10 @@ object NotificationUtils {
notification = statusBarNotification.notification notification = statusBarNotification.notification
if (notification != null && !notification.extras.isEmpty) { if (notification != null && !notification.extras.isEmpty) {
if (conversationUser.id == notification.extras.getLong(BundleKeys.KEY_INTERNAL_USER_ID) && roomTokenOrId == statusBarNotification.notification.extras.getString(BundleKeys.KEY_ROOM_TOKEN)) { if (conversationUser.id == notification.extras.getLong(BundleKeys.KEY_INTERNAL_USER_ID) && roomTokenOrId == statusBarNotification.notification.extras.getString(
BundleKeys.KEY_ROOM_TOKEN
)
) {
return statusBarNotification return statusBarNotification
} }
} }
@ -160,10 +197,14 @@ object NotificationUtils {
return null return null
} }
fun cancelExistingNotificationsForRoom(context: Context?, conversationUser: UserEntity, fun cancelExistingNotificationsForRoom(
roomTokenOrId: String) { context: Context?,
conversationUser: UserEntity,
roomTokenOrId: String
) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && conversationUser.id != -1L && if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && conversationUser.id != -1L &&
context != null) { context != null
) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
@ -173,7 +214,11 @@ object NotificationUtils {
notification = statusBarNotification.notification notification = statusBarNotification.notification
if (notification != null && !notification.extras.isEmpty) { if (notification != null && !notification.extras.isEmpty) {
if (conversationUser.id == notification.extras.getLong(BundleKeys.KEY_INTERNAL_USER_ID) && roomTokenOrId == statusBarNotification.notification.extras.getString(BundleKeys.KEY_ROOM_TOKEN)) { if (conversationUser.id == notification.extras.getLong(BundleKeys.KEY_INTERNAL_USER_ID) &&
roomTokenOrId == statusBarNotification.notification.extras.getString(
BundleKeys.KEY_ROOM_TOKEN
)
) {
notificationManager.cancel(statusBarNotification.id) notificationManager.cancel(statusBarNotification.id)
} }
} }

View File

@ -25,7 +25,6 @@ import android.database.Cursor
import android.net.Uri import android.net.Uri
import android.provider.OpenableColumns import android.provider.OpenableColumns
import android.util.Log import android.util.Log
import com.nextcloud.talk.jobs.UploadAndShareFilesWorker
object UriUtils { object UriUtils {
@ -51,5 +50,4 @@ object UriUtils {
} }
return filename return filename
} }
} }

View File

@ -21,8 +21,8 @@
package com.nextcloud.talk.utils.bundle package com.nextcloud.talk.utils.bundle
object BundleKeys { object BundleKeys {
val KEY_SELECTED_USERS = "KEY_SELECTED_USERS"; val KEY_SELECTED_USERS = "KEY_SELECTED_USERS"
val KEY_SELECTED_GROUPS = "KEY_SELECTED_GROUPS"; val KEY_SELECTED_GROUPS = "KEY_SELECTED_GROUPS"
val KEY_USERNAME = "KEY_USERNAME" val KEY_USERNAME = "KEY_USERNAME"
val KEY_TOKEN = "KEY_TOKEN" val KEY_TOKEN = "KEY_TOKEN"
val KEY_BASE_URL = "KEY_BASE_URL" val KEY_BASE_URL = "KEY_BASE_URL"

View File

@ -13,11 +13,17 @@ import java.io.IOException
import java.net.InetAddress import java.net.InetAddress
import java.net.Socket import java.net.Socket
import java.security.GeneralSecurityException import java.security.GeneralSecurityException
import java.util.* import java.util.LinkedList
import javax.net.ssl.* import javax.net.ssl.KeyManager
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSocket
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.X509TrustManager
class SSLSocketFactoryCompat(keyManager: KeyManager?, class SSLSocketFactoryCompat(
trustManager: X509TrustManager) : SSLSocketFactory() { keyManager: KeyManager?,
trustManager: X509TrustManager
) : SSLSocketFactory() {
private var delegate: SSLSocketFactory private var delegate: SSLSocketFactory
@ -95,14 +101,14 @@ class SSLSocketFactoryCompat(keyManager: KeyManager?,
} }
} }
init { init {
try { try {
val sslContext = SSLContext.getInstance("TLS") val sslContext = SSLContext.getInstance("TLS")
sslContext.init( sslContext.init(
if (keyManager != null) arrayOf(keyManager) else null, if (keyManager != null) arrayOf(keyManager) else null,
arrayOf(trustManager), arrayOf(trustManager),
null) null
)
delegate = sslContext.socketFactory delegate = sslContext.socketFactory
} catch (e: GeneralSecurityException) { } catch (e: GeneralSecurityException) {
throw IllegalStateException() // system has no TLS throw IllegalStateException() // system has no TLS
@ -150,10 +156,8 @@ class SSLSocketFactoryCompat(keyManager: KeyManager?,
return ssl return ssl
} }
private fun upgradeTLS(ssl: SSLSocket) { private fun upgradeTLS(ssl: SSLSocket) {
protocols?.let { ssl.enabledProtocols = it } protocols?.let { ssl.enabledProtocols = it }
cipherSuites?.let { ssl.enabledCipherSuites = it } cipherSuites?.let { ssl.enabledCipherSuites = it }
} }
} }