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)
@ -224,10 +258,12 @@ class MagicFirebaseMessagingService : FirebaseMessagingService() {
startForeground(decryptedPushMessage!!.timestamp.toInt(), notification) startForeground(decryptedPushMessage!!.timestamp.toInt(), notification)
} else { } else {
val messageData = Data.Builder() val messageData = Data.Builder()
.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()
val pushNotificationWork =
OneTimeWorkRequest.Builder(NotificationWorker::class.java).setInputData(messageData)
.build() .build()
val pushNotificationWork = OneTimeWorkRequest.Builder(NotificationWorker::class.java).setInputData(messageData).build()
WorkManager.getInstance().enqueue(pushNotificationWork) WorkManager.getInstance().enqueue(pushNotificationWork)
} }
} }
@ -244,47 +280,59 @@ 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(
.takeWhile { signatureVerification.userEntity.baseUrl,
isServiceInForeground decryptedPushMessage.id
)
)
.takeWhile {
isServiceInForeground
}
.subscribeOn(Schedulers.io())
.subscribe(object : Observer<ParticipantsOverall> {
override fun onSubscribe(d: Disposable) {
} }
.subscribeOn(Schedulers.io())
.subscribe(object : Observer<ParticipantsOverall> {
override fun onSubscribe(d: Disposable) {
}
override fun onNext(participantsOverall: ParticipantsOverall) { override fun onNext(participantsOverall: ParticipantsOverall) {
val participantList: List<Participant> = participantsOverall.ocs.data val participantList: List<Participant> = participantsOverall.ocs.data
hasParticipantsInCall = participantList.isNotEmpty() hasParticipantsInCall = participantList.isNotEmpty()
if (!hasParticipantsInCall) { if (!hasParticipantsInCall) {
for (participant in participantList) { for (participant in participantList) {
if (participant.userId == signatureVerification.userEntity.userId) { if (participant.userId == signatureVerification.userEntity.userId) {
inCallOnDifferentDevice = true inCallOnDifferentDevice = true
break break
}
} }
} }
}
if (!hasParticipantsInCall || inCallOnDifferentDevice) { if (!hasParticipantsInCall || inCallOnDifferentDevice) {
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
)
} }
}
override fun onError(e: Throwable) {} override fun onError(e: Throwable) {}
override fun onComplete() { override fun onComplete() {
} }
}) })
} }
} }

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,30 +104,30 @@ 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)
.setNegativeButtonColorRes(R.color.nc_darkRed) .setNegativeButtonColorRes(R.color.nc_darkRed)
.setPositiveButtonColorRes(R.color.colorPrimary) .setPositiveButtonColorRes(R.color.colorPrimary)
.setIcon(R.drawable.ic_security_white_24dp) .setIcon(R.drawable.ic_security_white_24dp)
.setTitle(R.string.nc_certificate_dialog_title) .setTitle(R.string.nc_certificate_dialog_title)
.setMessage(dialogText) .setMessage(dialogText)
.setPositiveButton(R.string.nc_yes) { v -> .setPositiveButton(R.string.nc_yes) { v ->
magicTrustManager.addCertInTrustStore(cert) magicTrustManager.addCertInTrustStore(cert)
sslErrorHandler?.proceed() sslErrorHandler?.proceed()
} }
.setNegativeButton(R.string.nc_no) { view1 -> .setNegativeButton(R.string.nc_no) { view1 ->
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(
BuildConfig.APPLICATION_ID, this,
File(path)) BuildConfig.APPLICATION_ID,
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(
BuildConfig.APPLICATION_ID, this,
File(path)) BuildConfig.APPLICATION_ID,
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(
BuildConfig.APPLICATION_ID, this,
File(path)) BuildConfig.APPLICATION_ID,
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,52 +230,67 @@ 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) )
.subscribeOn(Schedulers.io()) ncApi.createRoom(
.observeOn(AndroidSchedulers.mainThread()) credentials,
.subscribe(object : Observer<RoomOverall> { retrofitBucket.url, retrofitBucket.queryMap
override fun onSubscribe(d: Disposable) {} )
override fun onNext(roomOverall: RoomOverall) { .subscribeOn(Schedulers.io())
val conversationIntent = Intent(context, MagicCallActivity::class.java) .observeOn(AndroidSchedulers.mainThread())
val bundle = Bundle() .subscribe(object : Observer<RoomOverall> {
bundle.putParcelable(KEY_USER_ENTITY, currentUser) override fun onSubscribe(d: Disposable) {}
bundle.putString(KEY_ROOM_TOKEN, roomOverall.ocs.data.token) override fun onNext(roomOverall: RoomOverall) {
bundle.putString(KEY_ROOM_ID, roomOverall.ocs.data.roomId) val conversationIntent = Intent(context, MagicCallActivity::class.java)
if (currentUser.hasSpreedFeatureCapability("chat-v2")) { val bundle = Bundle()
ncApi.getRoom(credentials, bundle.putParcelable(KEY_USER_ENTITY, currentUser)
ApiUtils.getRoom(currentUser.baseUrl, bundle.putString(KEY_ROOM_TOKEN, roomOverall.ocs.data.token)
roomOverall.ocs.data.token)) bundle.putString(KEY_ROOM_ID, roomOverall.ocs.data.roomId)
.subscribeOn(Schedulers.io()) if (currentUser.hasSpreedFeatureCapability("chat-v2")) {
.observeOn(AndroidSchedulers.mainThread()) ncApi.getRoom(
.subscribe(object : Observer<RoomOverall> { credentials,
override fun onSubscribe(d: Disposable) {} ApiUtils.getRoom(
override fun onNext(roomOverall: RoomOverall) { currentUser.baseUrl,
bundle.putParcelable(KEY_ACTIVE_CONVERSATION, roomOverall.ocs.data.token
Parcels.wrap(roomOverall.ocs.data)) )
remapChatController(router!!, currentUser.id, )
roomOverall.ocs.data.token, bundle, true) .subscribeOn(Schedulers.io())
} .observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<RoomOverall> {
override fun onSubscribe(d: Disposable) {}
override fun onNext(roomOverall: RoomOverall) {
bundle.putParcelable(
KEY_ACTIVE_CONVERSATION,
Parcels.wrap(roomOverall.ocs.data)
)
remapChatController(
router!!, currentUser.id,
roomOverall.ocs.data.token, bundle, true
)
}
override fun onError(e: Throwable) {} override fun onError(e: Throwable) {}
override fun onComplete() {} override fun onComplete() {}
}) })
} 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
)
} }
}
override fun onError(e: Throwable) {} override fun onError(e: Throwable) {}
override fun onComplete() {} override fun onComplete() {}
}) })
} }
@RequiresApi(api = Build.VERSION_CODES.M) @RequiresApi(api = Build.VERSION_CODES.M)
@ -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)
@ -104,8 +102,8 @@ class MagicIncomingTextMessageViewHolder(incomingView: View) : MessageHolders
init { init {
ButterKnife.bind( ButterKnife.bind(
this, this,
itemView itemView
) )
} }
@ -131,13 +129,13 @@ class MagicIncomingTextMessageViewHolder(incomingView: View) : MessageHolders
messageUserAvatarView?.setImageDrawable(DisplayUtils.getRoundedDrawable(layerDrawable)) messageUserAvatarView?.setImageDrawable(DisplayUtils.getRoundedDrawable(layerDrawable))
} else if (message.actorType == "bots") { } else if (message.actorType == "bots") {
val drawable = TextDrawable.builder() val drawable = TextDrawable.builder()
.beginConfig() .beginConfig()
.bold() .bold()
.endConfig() .endConfig()
.buildRound( .buildRound(
">", ">",
context!!.resources.getColor(R.color.black) context!!.resources.getColor(R.color.black)
) )
messageUserAvatarView!!.visibility = View.VISIBLE messageUserAvatarView!!.visibility = View.VISIBLE
messageUserAvatarView?.setImageDrawable(drawable) messageUserAvatarView?.setImageDrawable(drawable)
} }
@ -165,9 +163,9 @@ class MagicIncomingTextMessageViewHolder(incomingView: View) : MessageHolders
} }
val bubbleDrawable = DisplayUtils.getMessageSelector( val bubbleDrawable = DisplayUtils.getMessageSelector(
bgBubbleColor, bgBubbleColor,
resources.getColor(R.color.transparent), resources.getColor(R.color.transparent),
bgBubbleColor, bubbleResource bgBubbleColor, bubbleResource
) )
ViewCompat.setBackground(bubble, bubbleDrawable) ViewCompat.setBackground(bubble, bubbleDrawable)
@ -187,23 +185,23 @@ class MagicIncomingTextMessageViewHolder(incomingView: View) : MessageHolders
if (individualHashMap["type"] == "user" || individualHashMap["type"] == "guest" || individualHashMap["type"] == "call") { if (individualHashMap["type"] == "user" || individualHashMap["type"] == "guest" || individualHashMap["type"] == "call") {
if (individualHashMap["id"] == message.activeUser!!.userId) { if (individualHashMap["id"] == message.activeUser!!.userId) {
messageString = DisplayUtils.searchAndReplaceWithMentionSpan( messageString = DisplayUtils.searchAndReplaceWithMentionSpan(
messageText!!.context, messageText!!.context,
messageString, messageString,
individualHashMap["id"]!!, individualHashMap["id"]!!,
individualHashMap["name"]!!, individualHashMap["name"]!!,
individualHashMap["type"]!!, individualHashMap["type"]!!,
message.activeUser!!, message.activeUser!!,
R.xml.chip_you R.xml.chip_you
) )
} else { } else {
messageString = DisplayUtils.searchAndReplaceWithMentionSpan( messageString = DisplayUtils.searchAndReplaceWithMentionSpan(
messageText!!.context, 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") {
@ -231,18 +229,21 @@ 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
} }
quotedUserName?.text = parentChatMessage.actorDisplayName quotedUserName?.text = parentChatMessage.actorDisplayName
?: context!!.getText(R.string.nc_nick_guest) ?: context!!.getText(R.string.nc_nick_guest)
quotedMessage?.text = parentChatMessage.text quotedMessage?.text = parentChatMessage.text
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, ) {
individualHashMap["id"]!!, messageString = searchAndReplaceWithMentionSpan(
individualHashMap["name"]!!, messageText!!.context,
individualHashMap["type"]!!, messageString,
message.activeUser, individualHashMap["id"]!!,
R.xml.chip_others) individualHashMap["name"]!!,
individualHashMap["type"]!!,
message.activeUser,
R.xml.chip_others
)
} else if (individualHashMap["type"] == "file") { } else if (individualHashMap["type"] == "file") {
realView.setOnClickListener(View.OnClickListener { v: View? -> realView.setOnClickListener(
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(individualHashMap["link"])) View.OnClickListener { v: View? ->
context!!.startActivity(browserIntent) val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(individualHashMap["link"]))
}) context!!.startActivity(browserIntent)
}
)
} }
} }
} }
@ -135,17 +140,19 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
} }
if (message.isGrouped) { if (message.isGrouped) {
val bubbleDrawable = getMessageSelector( val bubbleDrawable = getMessageSelector(
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,13 +167,16 @@ 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
} }
quotedUserName?.text = parentChatMessage.actorDisplayName quotedUserName?.text = parentChatMessage.actorDisplayName
?: context!!.getText(R.string.nc_nick_guest) ?: context!!.getText(R.string.nc_nick_guest)
quotedMessage?.text = parentChatMessage.text quotedMessage?.text = parentChatMessage.text
quotedMessage?.setTextColor(context!!.resources.getColor(R.color.nc_outcoming_text_default)) quotedMessage?.setTextColor(context!!.resources.getColor(R.color.nc_outcoming_text_default))
quotedUserName?.setTextColor(context!!.resources.getColor(R.color.nc_grey)) quotedUserName?.setTextColor(context!!.resources.getColor(R.color.nc_grey))

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)
} }
@ -120,8 +123,8 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
val securityKeyManager = SecurityKeyManager.getInstance() val securityKeyManager = SecurityKeyManager.getInstance()
val securityKeyConfig = SecurityKeyManagerConfig.Builder() val securityKeyConfig = SecurityKeyManagerConfig.Builder()
.setEnableDebugLogging(BuildConfig.DEBUG) .setEnableDebugLogging(BuildConfig.DEBUG)
.build() .build()
securityKeyManager.init(this, securityKeyConfig) securityKeyManager.init(this, securityKeyConfig)
initializeWebRtc() initializeWebRtc()
@ -136,13 +139,15 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
super.onCreate() super.onCreate()
val imagePipelineConfig = ImagePipelineConfig.newBuilder(this) val imagePipelineConfig = ImagePipelineConfig.newBuilder(this)
.setNetworkFetcher(OkHttpNetworkFetcherWithCache(okHttpClient)) .setNetworkFetcher(OkHttpNetworkFetcherWithCache(okHttpClient))
.setMainDiskCacheConfig(DiskCacheConfig.newBuilder(this) .setMainDiskCacheConfig(
.setMaxCacheSize(0) DiskCacheConfig.newBuilder(this)
.setMaxCacheSizeOnLowDiskSpace(0) .setMaxCacheSize(0)
.setMaxCacheSizeOnVeryLowDiskSpace(0) .setMaxCacheSizeOnLowDiskSpace(0)
.build()) .setMaxCacheSizeOnVeryLowDiskSpace(0)
.build() .build()
)
.build()
Fresco.initialize(this, imagePipelineConfig) Fresco.initialize(this, imagePipelineConfig)
Security.insertProviderAt(Conscrypt.newProvider(), 1) Security.insertProviderAt(Conscrypt.newProvider(), 1)
@ -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,17 +187,16 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
} }
//endregion //endregion
//region Protected methods //region Protected methods
protected fun buildComponent() { protected fun buildComponent() {
componentApplication = DaggerNextcloudTalkApplicationComponent.builder() componentApplication = DaggerNextcloudTalkApplicationComponent.builder()
.busModule(BusModule()) .busModule(BusModule())
.contextModule(ContextModule(applicationContext)) .contextModule(ContextModule(applicationContext))
.databaseModule(DatabaseModule()) .databaseModule(DatabaseModule())
.restModule(RestModule(applicationContext)) .restModule(RestModule(applicationContext))
.userModule(UserModule()) .userModule(UserModule())
.arbitraryStorageModule(ArbitraryStorageModule()) .arbitraryStorageModule(ArbitraryStorageModule())
.build() .build()
} }
override fun attachBaseContext(base: Context) { override fun attachBaseContext(base: Context) {
@ -196,19 +206,20 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
private fun buildDefaultImageLoader(): ImageLoader { private fun buildDefaultImageLoader(): ImageLoader {
return ImageLoader.Builder(applicationContext) return ImageLoader.Builder(applicationContext)
.availableMemoryPercentage(0.5) // Use 50% of the application's available memory. .availableMemoryPercentage(0.5) // Use 50% of the application's available memory.
.crossfade(true) // Show a short crossfade when loading images from network or disk into an ImageView. .crossfade(true) // Show a short crossfade when loading images from network or disk into an ImageView.
.componentRegistry { .componentRegistry {
if (SDK_INT >= P) { if (SDK_INT >= P) {
add(ImageDecoderDecoder(applicationContext)) add(ImageDecoderDecoder(applicationContext))
} else { } else {
add(GifDecoder()) add(GifDecoder())
}
add(SvgDecoder(applicationContext))
} }
.okHttpClient(okHttpClient) add(SvgDecoder(applicationContext))
.build() }
.okHttpClient(okHttpClient)
.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;
@ -206,13 +204,13 @@ public class CallNotificationController extends BaseController {
originalBundle.putString(BundleKeys.INSTANCE.getKEY_CONVERSATION_NAME(), currentConversation.getDisplayName()); originalBundle.putString(BundleKeys.INSTANCE.getKEY_CONVERSATION_NAME(), currentConversation.getDisplayName());
getRouter().replaceTopController(RouterTransaction.with(new CallController(originalBundle)) getRouter().replaceTopController(RouterTransaction.with(new CallController(originalBundle))
.popChangeHandler(new HorizontalChangeHandler()) .popChangeHandler(new HorizontalChangeHandler())
.pushChangeHandler(new HorizontalChangeHandler())); .pushChangeHandler(new HorizontalChangeHandler()));
} }
private void checkIfAnyParticipantsRemainInRoom() { private void checkIfAnyParticipantsRemainInRoom() {
ncApi.getPeersForCall(credentials, ApiUtils.getUrlForCall(userBeingCalled.getBaseUrl(), ncApi.getPeersForCall(credentials, ApiUtils.getUrlForCall(userBeingCalled.getBaseUrl(),
currentConversation.getToken())) currentConversation.getToken()))
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.takeWhile(observable -> !leavingScreen) .takeWhile(observable -> !leavingScreen)
.subscribe(new Observer<ParticipantsOverall>() { .subscribe(new Observer<ParticipantsOverall>() {
@ -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,12 +277,12 @@ 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 {
incomingCallVoiceOrVideoTextView.setText(String.format(getResources().getString(R.string.nc_call_voice), incomingCallVoiceOrVideoTextView.setText(String.format(getResources().getString(R.string.nc_call_voice),
getResources().getString(R.string.nc_app_name))); getResources().getString(R.string.nc_app_name)));
} }
} }
} }
@ -412,7 +410,7 @@ public class CallNotificationController extends BaseController {
ImageRequest imageRequest = ImageRequest imageRequest =
DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(userBeingCalled.getBaseUrl(), DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(userBeingCalled.getBaseUrl(),
currentConversation.getName(), R.dimen.avatar_size_very_big), null); currentConversation.getName(), R.dimen.avatar_size_very_big), null);
ImagePipeline imagePipeline = Fresco.getImagePipeline(); ImagePipeline imagePipeline = Fresco.getImagePipeline();
DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline.fetchDecodedImage(imageRequest, null); DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline.fetchDecodedImage(imageRequest, null);
@ -422,11 +420,11 @@ public class CallNotificationController extends BaseController {
protected void onNewResultImpl(@Nullable Bitmap bitmap) { protected void onNewResultImpl(@Nullable Bitmap bitmap) {
if (avatarImageView != null) { if (avatarImageView != null) {
avatarImageView.getHierarchy().setImage(new BitmapDrawable(bitmap), 100, avatarImageView.getHierarchy().setImage(new BitmapDrawable(bitmap), 100,
true); true);
if (getResources() != null) { if (getResources() != null) {
incomingTextRelativeLayout.setBackground(getResources().getDrawable(R.drawable incomingTextRelativeLayout.setBackground(getResources().getDrawable(R.drawable
.incoming_gradient)); .incoming_gradient));
} }
if ((AvatarStatusCodeHolder.getInstance().getStatusCode() == 200 || AvatarStatusCodeHolder.getInstance().getStatusCode() == 0) && if ((AvatarStatusCodeHolder.getInstance().getStatusCode() == 200 || AvatarStatusCodeHolder.getInstance().getStatusCode() == 0) &&
@ -512,7 +510,7 @@ public class CallNotificationController extends BaseController {
if (TextUtils.isEmpty(callRingtonePreferenceString)) { if (TextUtils.isEmpty(callRingtonePreferenceString)) {
// play default sound // play default sound
ringtoneUri = Uri.parse("android.resource://" + getApplicationContext().getPackageName() + ringtoneUri = Uri.parse("android.resource://" + getApplicationContext().getPackageName() +
"/raw/librem_by_feandesign_call"); "/raw/librem_by_feandesign_call");
} else { } else {
try { try {
RingtoneSettings ringtoneSettings = LoganSquare.parse(callRingtonePreferenceString, RingtoneSettings.class); RingtoneSettings ringtoneSettings = LoganSquare.parse(callRingtonePreferenceString, RingtoneSettings.class);
@ -520,7 +518,7 @@ public class CallNotificationController extends BaseController {
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, "Failed to parse ringtone settings"); Log.e(TAG, "Failed to parse ringtone settings");
ringtoneUri = Uri.parse("android.resource://" + getApplicationContext().getPackageName() + ringtoneUri = Uri.parse("android.resource://" + getApplicationContext().getPackageName() +
"/raw/librem_by_feandesign_call"); "/raw/librem_by_feandesign_call");
} }
} }
@ -531,7 +529,7 @@ public class CallNotificationController extends BaseController {
mediaPlayer.setLooping(true); mediaPlayer.setLooping(true);
AudioAttributes audioAttributes = new AudioAttributes.Builder().setContentType(AudioAttributes AudioAttributes audioAttributes = new AudioAttributes.Builder().setContentType(AudioAttributes
.CONTENT_TYPE_SONIFICATION).setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE).build(); .CONTENT_TYPE_SONIFICATION).setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE).build();
mediaPlayer.setAudioAttributes(audioAttributes); mediaPlayer.setAudioAttributes(audioAttributes);
mediaPlayer.setOnPreparedListener(mp -> mediaPlayer.start()); mediaPlayer.setOnPreparedListener(mp -> mediaPlayer.start());

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,14 +226,19 @@ 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
reconfigureLobbyTimerView() reconfigureLobbyTimerView()
@ -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(),
dateTime -> requireFutureDateTime =
reconfigureLobbyTimerView(dateTime) true,
submitLobbyChanges() currentDateTime = currentTimeCalendar,
}) dateTimeCallback = { _,
dateTime ->
reconfigureLobbyTimerView(dateTime)
submitLobbyChanges()
}
)
} }
} }
@ -253,7 +282,7 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
} }
conversation!!.lobbyState = if (isChecked) Conversation.LobbyState conversation!!.lobbyState = if (isChecked) Conversation.LobbyState
.LOBBY_STATE_MODERATORS_ONLY else Conversation.LobbyState.LOBBY_STATE_ALL_PARTICIPANTS .LOBBY_STATE_MODERATORS_ONLY else Conversation.LobbyState.LOBBY_STATE_ALL_PARTICIPANTS
if (conversation!!.lobbyTimer != null && conversation!!.lobbyTimer != java.lang.Long.MIN_VALUE && conversation!!.lobbyTimer != 0L) { if (conversation!!.lobbyTimer != null && conversation!!.lobbyTimer != java.lang.Long.MIN_VALUE && conversation!!.lobbyTimer != 0L) {
startTimeView.setSummary(DateUtils.getLocalDateStringFromTimestampForLobby(conversation!!.lobbyTimer)) startTimeView.setSummary(DateUtils.getLocalDateStringFromTimestampForLobby(conversation!!.lobbyTimer))
@ -269,27 +298,34 @@ 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
.subscribeOn(Schedulers.io()) ).isChecked
.observeOn(AndroidSchedulers.mainThread()) ) 1 else 0
.subscribe(object : Observer<GenericOverall> { ncApi.setLobbyForConversation(
override fun onComplete() { ApiUtils.getCredentials(conversationUser!!.username, conversationUser.token),
} ApiUtils.getUrlForLobbyForConversation(conversationUser.baseUrl, conversation!!.token),
state,
conversation!!.lobbyTimer
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<GenericOverall> {
override fun onComplete() {
}
override fun onSubscribe(d: Disposable) { override fun onSubscribe(d: Disposable) {
} }
override fun onNext(t: GenericOverall) { override fun onNext(t: GenericOverall) {
} }
override fun onError(e: Throwable) { override fun onError(e: Throwable) {
} }
})
})
} }
private fun showLovelyDialog(dialogId: Int, savedInstanceState: Bundle) { private fun showLovelyDialog(dialogId: Int, savedInstanceState: Bundle) {
@ -313,17 +349,21 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
private fun showDeleteConversationDialog(savedInstanceState: Bundle?) { private fun showDeleteConversationDialog(savedInstanceState: Bundle?) {
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(
.setPositiveButtonColor(context!!.resources.getColor(R.color.nc_darkRed)) context!!.resources,
.setTitle(R.string.nc_delete_call) R.drawable.ic_delete_black_24dp, R.color.bg_default
.setMessage(conversation!!.deleteWarningMessage) )
.setPositiveButton(R.string.nc_delete) { deleteConversation() } )
.setNegativeButton(R.string.nc_cancel, null) .setPositiveButtonColor(context!!.resources.getColor(R.color.nc_darkRed))
.setInstanceStateHandler(ID_DELETE_CONVERSATION_DIALOG, saveStateHandler!!) .setTitle(R.string.nc_delete_call)
.setSavedInstanceState(savedInstanceState) .setMessage(conversation!!.deleteWarningMessage)
.show() .setPositiveButton(R.string.nc_delete) { deleteConversation() }
.setNegativeButton(R.string.nc_cancel, null)
.setInstanceStateHandler(ID_DELETE_CONVERSATION_DIALOG, saveStateHandler!!)
.setSavedInstanceState(savedInstanceState)
.show()
} }
} }
@ -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,27 +437,28 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
} }
private fun getListOfParticipants() { private fun getListOfParticipants() {
ncApi.getPeersForCall(credentials, ApiUtils.getUrlForParticipants(conversationUser!!.baseUrl, conversationToken)) ncApi.getPeersForCall(
.subscribeOn(Schedulers.io()) credentials,
.observeOn(AndroidSchedulers.mainThread()) ApiUtils.getUrlForParticipants(conversationUser!!.baseUrl, conversationToken)
.subscribe(object : Observer<ParticipantsOverall> { )
override fun onSubscribe(d: Disposable) { .subscribeOn(Schedulers.io())
participantsDisposable = d .observeOn(AndroidSchedulers.mainThread())
} .subscribe(object : Observer<ParticipantsOverall> {
override fun onSubscribe(d: Disposable) {
participantsDisposable = d
}
override fun onNext(participantsOverall: ParticipantsOverall) { override fun onNext(participantsOverall: ParticipantsOverall) {
handleParticipants(participantsOverall.ocs.data) handleParticipants(participantsOverall.ocs.data)
} }
override fun onError(e: Throwable) { override fun onError(e: Throwable) {
}
}
override fun onComplete() {
participantsDisposable!!.dispose()
}
})
override fun onComplete() {
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()
} }
} }
@ -471,69 +527,67 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
private fun fetchRoomInfo() { private fun fetchRoomInfo() {
ncApi.getRoom(credentials, ApiUtils.getRoom(conversationUser!!.baseUrl, conversationToken)) ncApi.getRoom(credentials, ApiUtils.getRoom(conversationUser!!.baseUrl, conversationToken))
.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) {
roomDisposable = d roomDisposable = d
}
override fun onNext(roomOverall: RoomOverall) {
conversation = roomOverall.ocs.data
val conversationCopy = conversation
if (conversationCopy!!.canModerate(conversationUser)) {
addParticipantsAction.visibility = View.VISIBLE
} else {
addParticipantsAction.visibility = View.GONE
} }
override fun onNext(roomOverall: RoomOverall) { if (isAttached && (!isBeingDestroyed || !isDestroyed)) {
conversation = roomOverall.ocs.data ownOptionsCategory.visibility = View.VISIBLE
val conversationCopy = conversation setupWebinaryView()
if (conversationCopy!!.canModerate(conversationUser)) { if (!conversation!!.canLeave(conversationUser)) {
addParticipantsAction.visibility = View.VISIBLE leaveConversationAction.visibility = View.GONE
} else { } else {
addParticipantsAction.visibility = View.GONE leaveConversationAction.visibility = View.VISIBLE
} }
if (isAttached && (!isBeingDestroyed || !isDestroyed)) { if (!conversation!!.canModerate(conversationUser)) {
ownOptionsCategory.visibility = View.VISIBLE deleteConversationAction.visibility = View.GONE
} else {
setupWebinaryView() deleteConversationAction.visibility = View.VISIBLE
if (!conversation!!.canLeave(conversationUser)) {
leaveConversationAction.visibility = View.GONE
} else {
leaveConversationAction.visibility = View.VISIBLE
}
if (!conversation!!.canModerate(conversationUser)) {
deleteConversationAction.visibility = View.GONE
} else {
deleteConversationAction.visibility = View.VISIBLE
}
if (Conversation.ConversationType.ROOM_SYSTEM == conversation!!.type) {
muteCalls.visibility = View.GONE
}
getListOfParticipants()
progressBar.visibility = View.GONE
nameCategoryView.visibility = View.VISIBLE
conversationDisplayName.text = conversation!!.displayName
loadConversationAvatar()
adjustNotificationLevelUI()
notificationsPreferenceScreen.visibility = View.VISIBLE
} }
}
override fun onError(e: Throwable) { if (Conversation.ConversationType.ROOM_SYSTEM == conversation!!.type) {
muteCalls.visibility = View.GONE
}
} getListOfParticipants()
override fun onComplete() { progressBar.visibility = View.GONE
roomDisposable!!.dispose()
nameCategoryView.visibility = View.VISIBLE
conversationDisplayName.text = conversation!!.displayName
loadConversationAvatar()
adjustNotificationLevelUI()
notificationsPreferenceScreen.visibility = View.VISIBLE
} }
}) }
override fun onError(e: Throwable) {
}
override fun onComplete() {
roomDisposable!!.dispose()
}
})
} }
private fun adjustNotificationLevelUI() { private fun adjustNotificationLevelUI() {
@ -543,12 +597,13 @@ 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 =
1 -> "always" when (EnumNotificationLevelConverter().convertToInt(conversation!!.notificationLevel)) {
2 -> "mention" 1 -> "always"
3 -> "never" 2 -> "mention"
else -> "mention" 3 -> "never"
} else -> "mention"
}
messageNotificationLevel.value = stringValue messageNotificationLevel.value = stringValue
} else { } else {
@ -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(
.build() ApiUtils.getUrlForAvatarWithName(
conversationUser!!.baseUrl,
conversation!!.name, R.dimen.avatar_size_big
),
conversationUser
)
)
.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,38 +710,62 @@ 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(
.subscribeOn(Schedulers.io()) credentials,
.observeOn(AndroidSchedulers.mainThread()) ApiUtils.getUrlForModerators(conversationUser.baseUrl, conversation!!.token),
.subscribe { participant.userId
getListOfParticipants() )
} .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
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(
.subscribeOn(Schedulers.io()) credentials,
.observeOn(AndroidSchedulers.mainThread()) ApiUtils.getUrlForModerators(conversationUser.baseUrl, conversation!!.token),
.subscribe { participant.userId
getListOfParticipants() )
} .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
getListOfParticipants()
}
} }
} 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) ) {
.subscribeOn(Schedulers.io()) ncApi.removeParticipantFromConversation(
.observeOn(AndroidSchedulers.mainThread()) credentials,
.subscribe { ApiUtils.getUrlForRemovingParticipantFromConversation(
getListOfParticipants() conversationUser.baseUrl,
} conversation!!.token,
true
),
participant.sessionId
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
getListOfParticipants()
}
} else { } else {
ncApi.removeParticipantFromConversation(credentials, ApiUtils.getUrlForRemovingParticipantFromConversation(conversationUser.baseUrl, conversation!!.token, false), participant.userId) ncApi.removeParticipantFromConversation(
.subscribeOn(Schedulers.io()) credentials,
.observeOn(AndroidSchedulers.mainThread()) ApiUtils.getUrlForRemovingParticipantFromConversation(
.subscribe { conversationUser.baseUrl,
getListOfParticipants() conversation!!.token,
// get participants again false
} ),
participant.userId
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
getListOfParticipants()
// get participants again
}
} }
} }
} }
@ -678,7 +773,7 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
} }
} }
return true; return true
} }
companion object { companion object {
@ -709,7 +804,7 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
} }
return left.model.displayName.toLowerCase(Locale.ROOT).compareTo( return left.model.displayName.toLowerCase(Locale.ROOT).compareTo(
right.model.displayName.toLowerCase(Locale.ROOT) right.model.displayName.toLowerCase(Locale.ROOT)
) )
} }
} }

View File

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

View File

@ -29,8 +29,9 @@ 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)
} }
@ -53,11 +52,12 @@ internal class ListItemViewHolder(
} }
internal class ListIconDialogAdapter<IT : ListItemWithImage>( internal class ListIconDialogAdapter<IT : ListItemWithImage>(
private var dialog: MaterialDialog, private var dialog: MaterialDialog,
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)
@ -81,12 +81,13 @@ 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,
adapter = this adapter = this
) )
viewHolder.titleView.maybeSetTextColor(dialog.windowContext, R.attr.md_color_content) viewHolder.titleView.maybeSetTextColor(dialog.windowContext, R.attr.md_color_content)
return viewHolder return viewHolder
@ -95,8 +96,9 @@ internal class ListIconDialogAdapter<IT : ListItemWithImage>(
override fun getItemCount() = items.size override fun getItemCount() = items.size
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]
@ -121,8 +123,9 @@ 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

@ -28,37 +28,40 @@ import com.afollestad.materialdialogs.list.customListAdapter
import com.afollestad.materialdialogs.list.getListAdapter 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
items: List<IT>, fun <IT : ListItemWithImage> MaterialDialog.listItemsWithImage(
disabledIndices: IntArray? = null, items: List<IT>,
waitForPositiveButton: Boolean = true, disabledIndices: IntArray? = null,
selection: ListItemListener<IT> = null): MaterialDialog { waitForPositiveButton: Boolean = true,
selection: ListItemListener<IT> = null
): MaterialDialog {
if (getListAdapter() != null) { if (getListAdapter() != null) {
return updateListItemsWithImage( return updateListItemsWithImage(
items = items, items = items,
disabledIndices = disabledIndices disabledIndices = disabledIndices
) )
} }
val layoutManager = LinearLayoutManager(windowContext) val layoutManager = LinearLayoutManager(windowContext)
return customListAdapter( return customListAdapter(
adapter = ListIconDialogAdapter( adapter = ListIconDialogAdapter(
dialog = this, dialog = this,
items = items, items = items,
disabledItems = disabledIndices, disabledItems = disabledIndices,
waitForPositiveButton = waitForPositiveButton, waitForPositiveButton = waitForPositiveButton,
selection = selection selection = selection
), ),
layoutManager = layoutManager layoutManager = layoutManager
) )
} }
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,10 +58,9 @@ 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) {
@Inject @Inject
lateinit var ncApi: NcApi lateinit var ncApi: NcApi
@ -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>()
@ -117,28 +120,29 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
val json = Gson().toJson(map) val json = Gson().toJson(map)
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()) )
.observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.io())
.subscribe(object : Observer<ContactsByNumberOverall> { .observeOn(AndroidSchedulers.mainThread())
override fun onComplete() { .subscribe(object : Observer<ContactsByNumberOverall> {
} override fun onComplete() {
}
override fun onSubscribe(d: Disposable) { override fun onSubscribe(d: Disposable) {
} }
override fun onNext(foundContacts: ContactsByNumberOverall) { override fun onNext(foundContacts: ContactsByNumberOverall) {
val contactsWithAssociatedPhoneNumbers = foundContacts.ocs.map val contactsWithAssociatedPhoneNumbers = foundContacts.ocs.map
deleteLinkedAccounts(contactsWithAssociatedPhoneNumbers) deleteLinkedAccounts(contactsWithAssociatedPhoneNumbers)
createLinkedAccounts(contactsWithAssociatedPhoneNumbers) createLinkedAccounts(contactsWithAssociatedPhoneNumbers)
} }
override fun onError(e: Throwable) { override fun onError(e: Throwable) {
Log.e(javaClass.simpleName, "Failed to searchContactsByPhoneNumber", e) Log.e(javaClass.simpleName, "Failed to searchContactsByPhoneNumber", e)
} }
}) })
} }
// store timestamp // store timestamp
@ -151,11 +155,11 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
val deviceContactsWithNumbers: MutableMap<String, List<String>> = mutableMapOf() val deviceContactsWithNumbers: MutableMap<String, List<String>> = mutableMapOf()
val contactCursor = context.contentResolver.query( val contactCursor = context.contentResolver.query(
ContactsContract.Contacts.CONTENT_URI, ContactsContract.Contacts.CONTENT_URI,
null, null,
null, null,
null, null,
null null
) )
if (contactCursor != null) { if (contactCursor != null) {
@ -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()
} }
@ -178,39 +183,50 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
Log.d(TAG, "deleteLinkedAccount") Log.d(TAG, "deleteLinkedAccount")
fun deleteLinkedAccount(id: String) { fun deleteLinkedAccount(id: String) {
val rawContactUri = ContactsContract.RawContacts.CONTENT_URI val rawContactUri = ContactsContract.RawContacts.CONTENT_URI
.buildUpon()
.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName)
.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType)
.build()
val count = context.contentResolver.delete(rawContactUri, ContactsContract.RawContacts.CONTACT_ID + " " +
"LIKE \"" + id + "\"", null)
Log.d(TAG, "deleted $count linked accounts for id $id")
}
val rawContactUri = ContactsContract.Data.CONTENT_URI
.buildUpon() .buildUpon()
.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")
.build() .build()
val count = context.contentResolver.delete(
rawContactUri,
ContactsContract.RawContacts.CONTACT_ID + " " + "LIKE \"" + id + "\"",
null
)
Log.d(TAG, "deleted $count linked accounts for id $id")
}
val rawContactUri = ContactsContract.Data.CONTENT_URI
.buildUpon()
.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName)
.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType)
.appendQueryParameter(
ContactsContract.Data.MIMETYPE,
"vnd.android.cursor.item/vnd.com.nextcloud.talk2.chat"
)
.build()
val rawContactsCursor = context.contentResolver.query( val rawContactsCursor = context.contentResolver.query(
rawContactUri, rawContactUri,
null, null,
null, null,
null, null,
null null
) )
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,25 +238,29 @@ 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
.buildUpon() .buildUpon()
.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(
.build() ContactsContract.Data.MIMETYPE,
"vnd.android.cursor.item/vnd.com.nextcloud.talk2.chat"
)
.build()
val rawContactsCursor = context.contentResolver.query( val rawContactsCursor = context.contentResolver.query(
rawContactUri, rawContactUri,
null, null,
where, where,
params, params,
null null
) )
if (rawContactsCursor != null) { if (rawContactsCursor != null) {
@ -259,18 +279,19 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
val lookupUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookupKey) val lookupUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookupKey)
val lookupContactUri = ContactsContract.Contacts.lookupContact(context.contentResolver, lookupUri) val lookupContactUri = ContactsContract.Contacts.lookupContact(context.contentResolver, lookupUri)
val contactCursor = context.contentResolver.query( val contactCursor = context.contentResolver.query(
lookupContactUri, lookupContactUri,
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()
} }
@ -362,11 +415,12 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
private fun getPhoneNumbersFromDeviceContact(id: String?): MutableList<String> { private fun getPhoneNumbersFromDeviceContact(id: String?): MutableList<String> {
val numbers = mutableListOf<String>() val numbers = mutableListOf<String>()
val phonesNumbersCursor = context.contentResolver.query( val phonesNumbersCursor = context.contentResolver.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI, ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
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
@ -382,11 +436,11 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
fun deleteAllLinkedAccounts() { fun deleteAllLinkedAccounts() {
val rawContactUri = ContactsContract.RawContacts.CONTENT_URI val rawContactUri = ContactsContract.RawContacts.CONTENT_URI
.buildUpon() .buildUpon()
.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)
.build() .build()
context.contentResolver.delete(rawContactUri, null, null) context.contentResolver.delete(rawContactUri, null, null)
Log.d(TAG, "deleted all linked accounts") Log.d(TAG, "deleted all linked accounts")
} }
@ -398,42 +452,63 @@ 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(
.setInputData(Data.Builder().putBoolean(KEY_FORCE, false).build()) OneTimeWorkRequest.Builder(ContactAddressBookWorker::class.java)
.build()) .setInputData(Data.Builder().putBoolean(KEY_FORCE, false).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(
.setInputData(Data.Builder().putBoolean(KEY_FORCE, true).build()) OneTimeWorkRequest.Builder(ContactAddressBookWorker::class.java)
.build()) .setInputData(Data.Builder().putBoolean(KEY_FORCE, true).build())
.build()
)
return true return true
} }
} }
fun deleteAll() { fun deleteAll() {
WorkManager WorkManager
.getInstance() .getInstance()
.enqueue(OneTimeWorkRequest.Builder(ContactAddressBookWorker::class.java) .enqueue(
.setInputData(Data.Builder().putBoolean(DELETE_ALL, true).build()) OneTimeWorkRequest.Builder(ContactAddressBookWorker::class.java)
.build()) .setInputData(Data.Builder().putBoolean(DELETE_ALL, true).build())
.build()
)
} }
} }
} }

View File

@ -33,13 +33,16 @@ 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) {
private var totalFileSize: Int = -1 private var totalFileSize: Int = -1
@ -86,8 +89,9 @@ 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,13 +50,12 @@ 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) {
@Inject @Inject
lateinit var ncApi: NcApi lateinit var ncApi: NcApi
@ -107,31 +110,37 @@ 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),
requestBody requestBody
) )
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<Response<GenericOverall>> { .subscribe(object : Observer<Response<GenericOverall>> {
override fun onSubscribe(d: Disposable) { override fun onSubscribe(d: Disposable) {
} }
override fun onNext(t: Response<GenericOverall>) { override fun onNext(t: Response<GenericOverall>) {
} }
override fun onError(e: Throwable) { override fun onError(e: Throwable) {
Log.e(TAG, "failed to upload file $filename") Log.e(TAG, "failed to upload file $filename")
} }
override fun onComplete() { override fun onComplete() {
shareFile(roomToken, currentUser, ncTargetpath, filename) shareFile(roomToken, currentUser, ncTargetpath, filename)
copyFileToCache(sourcefileUri, filename) copyFileToCache(sourcefileUri, filename)
} }
}) })
} }
private fun copyFileToCache(sourceFileUri: Uri, filename: String) { private fun copyFileToCache(sourceFileUri: Uri, filename: String) {
@ -151,13 +160,13 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
paths.add("$ncTargetpath/$filename") paths.add("$ncTargetpath/$filename")
val data = Data.Builder() val data = Data.Builder()
.putLong(KEY_INTERNAL_USER_ID, currentUser.id) .putLong(KEY_INTERNAL_USER_ID, currentUser.id)
.putString(KEY_ROOM_TOKEN, roomToken) .putString(KEY_ROOM_TOKEN, roomToken)
.putStringArray(KEY_FILE_PATHS, paths.toTypedArray()) .putStringArray(KEY_FILE_PATHS, paths.toTypedArray())
.build() .build()
val shareWorker = OneTimeWorkRequest.Builder(ShareOperationWorker::class.java) val shareWorker = OneTimeWorkRequest.Builder(ShareOperationWorker::class.java)
.setInputData(data) .setInputData(data)
.build() .build()
WorkManager.getInstance().enqueue(shareWorker) WorkManager.getInstance().enqueue(shareWorker)
} }

View File

@ -25,7 +25,7 @@ package com.nextcloud.talk.models.json.chat
class ChatUtils { class ChatUtils {
companion object { companion object {
fun getParsedMessage(message: String?, messageParameters: HashMap<String?, HashMap<String?, String?>>?): fun getParsedMessage(message: String?, messageParameters: HashMap<String?, HashMap<String?, String?>>?):
String? { String? {
var resultMessage = message var resultMessage = message
if (messageParameters != null && messageParameters.size > 0) { if (messageParameters != null && messageParameters.size > 0) {
for (key in messageParameters.keys) { for (key in messageParameters.keys) {
@ -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,12 +30,13 @@ 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)
val view = layoutInflater.inflate(R.layout.dialog_scope, null) val view = layoutInflater.inflate(R.layout.dialog_scope, null)

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(
conversationUser: UserEntity, context: Context?,
roomTokenOrId: String): StatusBarNotification? { conversationUser: UserEntity,
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
@ -50,24 +56,24 @@ class SSLSocketFactoryCompat(keyManager: KeyManager?,
/* set up reasonable cipher suites */ /* set up reasonable cipher suites */
val knownCiphers = arrayOf<String>( val knownCiphers = arrayOf<String>(
// TLS 1.2 // TLS 1.2
"TLS_RSA_WITH_AES_256_GCM_SHA384", "TLS_RSA_WITH_AES_256_GCM_SHA384",
"TLS_RSA_WITH_AES_128_GCM_SHA256", "TLS_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
// maximum interoperability // maximum interoperability
"TLS_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
"SSL_RSA_WITH_3DES_EDE_CBC_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_128_CBC_SHA",
// additionally // additionally
"TLS_RSA_WITH_AES_256_CBC_SHA", "TLS_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"
) )
val availableCiphers = socket.supportedCipherSuites val availableCiphers = socket.supportedCipherSuites
@ -89,31 +95,31 @@ class SSLSocketFactoryCompat(keyManager: KeyManager?,
} catch (e: IOException) { } catch (e: IOException) {
// Exception is to be ignored // Exception is to be ignored
} finally { } finally {
socket?.close() // doesn't implement Closeable on all supported Android versions socket?.close() // doesn't implement Closeable on all supported Android versions
} }
} }
} }
} }
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
} }
} }
override fun getDefaultCipherSuites(): Array<String>? = cipherSuites override fun getDefaultCipherSuites(): Array<String>? = cipherSuites
?: delegate.defaultCipherSuites ?: delegate.defaultCipherSuites
override fun getSupportedCipherSuites(): Array<String>? = cipherSuites override fun getSupportedCipherSuites(): Array<String>? = cipherSuites
?: delegate.supportedCipherSuites ?: delegate.supportedCipherSuites
override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket { override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket {
val ssl = delegate.createSocket(s, host, port, autoClose) val ssl = delegate.createSocket(s, host, port, autoClose)
@ -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 }
} }
} }