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
var eventBus: EventBus? = null
override fun onCreate() {
super.onCreate()
sharedApplication!!.componentApplication.inject(this)
@ -148,18 +147,26 @@ class MagicFirebaseMessagingService : FirebaseMessagingService() {
val pushUtils = PushUtils()
val privateKey = pushUtils.readKeyFromFile(false) as PrivateKey
try {
signatureVerification = pushUtils.verifySignature(base64DecodedSignature,
base64DecodedSubject)
signatureVerification = pushUtils.verifySignature(
base64DecodedSignature,
base64DecodedSubject
)
if (signatureVerification!!.signatureValid) {
val cipher = Cipher.getInstance("RSA/None/PKCS1Padding")
cipher.init(Cipher.DECRYPT_MODE, privateKey)
val decryptedSubject = cipher.doFinal(base64DecodedSubject)
decryptedPushMessage = LoganSquare.parse(String(decryptedSubject),
DecryptedPushMessage::class.java)
decryptedPushMessage = LoganSquare.parse(
String(decryptedSubject),
DecryptedPushMessage::class.java
)
decryptedPushMessage?.apply {
timestamp = System.currentTimeMillis()
if (delete) {
cancelExistingNotificationWithId(applicationContext, signatureVerification!!.userEntity, notificationId)
cancelExistingNotificationWithId(
applicationContext,
signatureVerification!!.userEntity,
notificationId
)
} else if (deleteAll) {
cancelAllNotificationsForAccount(applicationContext, signatureVerification!!.userEntity)
} else if (type == "call") {
@ -171,39 +178,66 @@ class MagicFirebaseMessagingService : FirebaseMessagingService() {
fullScreenIntent.putExtras(bundle)
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)
val ringtonePreferencesString: String? = appPreferences!!.callRingtoneUri
val soundUri = if (TextUtils.isEmpty(ringtonePreferencesString)) {
Uri.parse("android.resource://" + applicationContext.packageName +
"/raw/librem_by_feandesign_call")
Uri.parse(
"android.resource://" + applicationContext.packageName +
"/raw/librem_by_feandesign_call"
)
} else {
try {
val ringtoneSettings = LoganSquare.parse(ringtonePreferencesString, RingtoneSettings::class.java)
val ringtoneSettings =
LoganSquare.parse(ringtonePreferencesString, RingtoneSettings::class.java)
ringtoneSettings.ringtoneUri
} catch (exception: IOException) {
Uri.parse("android.resource://" + applicationContext.packageName + "/raw/librem_by_feandesign_call")
}
}
val notificationChannelId = NotificationUtils.getNotificationChannelId(applicationContext.resources
.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 notificationChannelId = NotificationUtils.getNotificationChannelId(
applicationContext.resources
.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
)
createNotificationChannel(applicationContext!!,
notificationChannelId, applicationContext.resources
.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)
createNotificationChannel(
applicationContext!!,
notificationChannelId,
applicationContext.resources
.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 baseUrl = uri.host
val notification = NotificationCompat.Builder(this@MagicFirebaseMessagingService, notificationChannelId)
val notification =
NotificationCompat.Builder(this@MagicFirebaseMessagingService, notificationChannelId)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_CALL)
.setSmallIcon(R.drawable.ic_call_black_24dp)
@ -213,7 +247,7 @@ class MagicFirebaseMessagingService : FirebaseMessagingService() {
.setContentTitle(EmojiCompat.get().process(decryptedPushMessage!!.subject))
.setAutoCancel(true)
.setOngoing(true)
//.setTimeoutAfter(45000L)
// .setTimeoutAfter(45000L)
.setContentIntent(fullScreenPendingIntent)
.setFullScreenIntent(fullScreenPendingIntent, true)
.setSound(soundUri)
@ -224,10 +258,12 @@ class MagicFirebaseMessagingService : FirebaseMessagingService() {
startForeground(decryptedPushMessage!!.timestamp.toInt(), notification)
} else {
val messageData = Data.Builder()
.putString(BundleKeys.KEY_NOTIFICATION_SUBJECT, subject)
.putString(BundleKeys.KEY_NOTIFICATION_SIGNATURE, signature)
.putString(BundleKeys.KEY_NOTIFICATION_SUBJECT, subject)
.putString(BundleKeys.KEY_NOTIFICATION_SIGNATURE, signature)
.build()
val pushNotificationWork =
OneTimeWorkRequest.Builder(NotificationWorker::class.java).setInputData(messageData)
.build()
val pushNotificationWork = OneTimeWorkRequest.Builder(NotificationWorker::class.java).setInputData(messageData).build()
WorkManager.getInstance().enqueue(pushNotificationWork)
}
}
@ -244,47 +280,59 @@ class MagicFirebaseMessagingService : FirebaseMessagingService() {
}
}
private fun checkIfCallIsActive(signatureVerification: SignatureVerification, decryptedPushMessage: DecryptedPushMessage) {
val ncApi = retrofit!!.newBuilder().client(okHttpClient!!.newBuilder().cookieJar(JavaNetCookieJar(CookieManager())).build()).build().create(NcApi::class.java)
private fun checkIfCallIsActive(
signatureVerification: SignatureVerification,
decryptedPushMessage: DecryptedPushMessage
) {
val ncApi = retrofit!!.newBuilder()
.client(okHttpClient!!.newBuilder().cookieJar(JavaNetCookieJar(CookieManager())).build()).build()
.create(NcApi::class.java)
var hasParticipantsInCall = false
var inCallOnDifferentDevice = false
ncApi.getPeersForCall(ApiUtils.getCredentials(signatureVerification.userEntity.username, signatureVerification.userEntity.token),
ApiUtils.getUrlForCall(signatureVerification.userEntity.baseUrl,
decryptedPushMessage.id))
.takeWhile {
isServiceInForeground
ncApi.getPeersForCall(
ApiUtils.getCredentials(signatureVerification.userEntity.username, signatureVerification.userEntity.token),
ApiUtils.getUrlForCall(
signatureVerification.userEntity.baseUrl,
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) {
val participantList: List<Participant> = participantsOverall.ocs.data
hasParticipantsInCall = participantList.isNotEmpty()
if (!hasParticipantsInCall) {
for (participant in participantList) {
if (participant.userId == signatureVerification.userEntity.userId) {
inCallOnDifferentDevice = true
break
}
override fun onNext(participantsOverall: ParticipantsOverall) {
val participantList: List<Participant> = participantsOverall.ocs.data
hasParticipantsInCall = participantList.isNotEmpty()
if (!hasParticipantsInCall) {
for (participant in participantList) {
if (participant.userId == signatureVerification.userEntity.userId) {
inCallOnDifferentDevice = true
break
}
}
}
if (!hasParticipantsInCall || inCallOnDifferentDevice) {
stopForeground(true)
handler.removeCallbacksAndMessages(null)
} else if (isServiceInForeground) {
handler.postDelayed({
if (!hasParticipantsInCall || inCallOnDifferentDevice) {
stopForeground(true)
handler.removeCallbacksAndMessages(null)
} else if (isServiceInForeground) {
handler.postDelayed(
{
checkIfCallIsActive(signatureVerification, decryptedPushMessage)
}, 5000)
}
},
5000
)
}
}
override fun onError(e: Throwable) {}
override fun onComplete() {
}
})
override fun onError(e: Throwable) {}
override fun onComplete() {
}
})
}
}
}

View File

@ -76,8 +76,11 @@ open class BaseActivity : AppCompatActivity() {
}
}
fun showCertificateDialog(cert: X509Certificate, magicTrustManager: MagicTrustManager,
sslErrorHandler: SslErrorHandler?) {
fun showCertificateDialog(
cert: X509Certificate,
magicTrustManager: MagicTrustManager,
sslErrorHandler: SslErrorHandler?
) {
val formatter = DateFormat.getDateInstance(DateFormat.LONG)
val validFrom = formatter.format(cert.notBefore)
val validUntil = formatter.format(cert.notAfter)
@ -101,30 +104,30 @@ open class BaseActivity : AppCompatActivity() {
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),
issuedBy, issuedFor, validFrom, validUntil)
issuedBy, issuedFor, validFrom, validUntil
)
LovelyStandardDialog(this)
.setTopColorRes(R.color.nc_darkRed)
.setNegativeButtonColorRes(R.color.nc_darkRed)
.setPositiveButtonColorRes(R.color.colorPrimary)
.setIcon(R.drawable.ic_security_white_24dp)
.setTitle(R.string.nc_certificate_dialog_title)
.setMessage(dialogText)
.setPositiveButton(R.string.nc_yes) { v ->
magicTrustManager.addCertInTrustStore(cert)
sslErrorHandler?.proceed()
}
.setNegativeButton(R.string.nc_no) { view1 ->
sslErrorHandler?.cancel()
}
.show()
.setTopColorRes(R.color.nc_darkRed)
.setNegativeButtonColorRes(R.color.nc_darkRed)
.setPositiveButtonColorRes(R.color.colorPrimary)
.setIcon(R.drawable.ic_security_white_24dp)
.setTitle(R.string.nc_certificate_dialog_title)
.setMessage(dialogText)
.setPositiveButton(R.string.nc_yes) { v ->
magicTrustManager.addCertInTrustStore(cert)
sslErrorHandler?.proceed()
}
.setNegativeButton(R.string.nc_no) { view1 ->
sslErrorHandler?.cancel()
}
.show()
} catch (e: CertificateParsingException) {
Log.d(TAG, "Failed to parse the certificate")
}
}
@Subscribe(threadMode = ThreadMode.MAIN)

View File

@ -38,7 +38,6 @@ import pl.droidsonroids.gif.GifDrawable
import pl.droidsonroids.gif.GifImageView
import java.io.File
class FullScreenImageActivity : AppCompatActivity() {
private lateinit var path: String
@ -55,9 +54,11 @@ class FullScreenImageActivity : AppCompatActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return if (item.itemId == R.id.share) {
val shareUri = FileProvider.getUriForFile(this,
BuildConfig.APPLICATION_ID,
File(path))
val shareUri = FileProvider.getUriForFile(
this,
BuildConfig.APPLICATION_ID,
File(path)
)
val shareIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND
@ -78,19 +79,19 @@ class FullScreenImageActivity : AppCompatActivity() {
setContentView(R.layout.activity_full_screen_image)
setSupportActionBar(findViewById(R.id.imageview_toolbar))
supportActionBar?.setDisplayShowTitleEnabled(false);
supportActionBar?.setDisplayShowTitleEnabled(false)
imageWrapperView = findViewById(R.id.image_wrapper_view)
photoView = findViewById(R.id.photo_view)
gifView = findViewById(R.id.gif_view)
photoView.setOnPhotoTapListener{ view, x, y ->
photoView.setOnPhotoTapListener { view, x, y ->
toggleFullscreen()
}
photoView.setOnOutsidePhotoTapListener{
photoView.setOnOutsidePhotoTapListener {
toggleFullscreen()
}
gifView.setOnClickListener{
gifView.setOnClickListener {
toggleFullscreen()
}
@ -115,29 +116,33 @@ class FullScreenImageActivity : AppCompatActivity() {
}
}
private fun toggleFullscreen(){
showFullscreen = !showFullscreen;
if (showFullscreen){
private fun toggleFullscreen() {
showFullscreen = !showFullscreen
if (showFullscreen) {
hideSystemUI()
supportActionBar?.hide()
} else{
} else {
showSystemUI()
supportActionBar?.show()
}
}
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_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_FULLSCREEN)
or View.SYSTEM_UI_FLAG_FULLSCREEN
)
}
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_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.os.Bundle
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.view.View
@ -33,7 +32,6 @@ import autodagger.AutoInjector
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.SimpleExoPlayer
import com.google.android.exoplayer2.ui.PlayerControlView
import com.google.android.exoplayer2.ui.StyledPlayerView
import com.nextcloud.talk.BuildConfig
import com.nextcloud.talk.R
@ -54,9 +52,11 @@ class FullScreenMediaActivity : AppCompatActivity(), Player.EventListener {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return if (item.itemId == R.id.share) {
val shareUri = FileProvider.getUriForFile(this,
BuildConfig.APPLICATION_ID,
File(path))
val shareUri = FileProvider.getUriForFile(
this,
BuildConfig.APPLICATION_ID,
File(path)
)
val shareIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND
@ -82,11 +82,11 @@ class FullScreenMediaActivity : AppCompatActivity(), Player.EventListener {
setContentView(R.layout.activity_full_screen_media)
setSupportActionBar(findViewById(R.id.mediaview_toolbar))
supportActionBar?.setDisplayShowTitleEnabled(false);
supportActionBar?.setDisplayShowTitleEnabled(false)
playerView = findViewById(R.id.player_view)
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
playerView.showController()
if (isAudioOnly) {
@ -121,7 +121,7 @@ class FullScreenMediaActivity : AppCompatActivity(), Player.EventListener {
private fun initializePlayer() {
player = SimpleExoPlayer.Builder(applicationContext).build()
playerView.player = player;
playerView.player = player
player.playWhenReady = true
player.addListener(this)
}
@ -131,17 +131,21 @@ class FullScreenMediaActivity : AppCompatActivity(), Player.EventListener {
}
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_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_FULLSCREEN)
or View.SYSTEM_UI_FLAG_FULLSCREEN
)
}
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_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 java.io.File
@AutoInjector(NextcloudTalkApplication::class)
class FullScreenTextViewerActivity : AppCompatActivity() {
@ -48,9 +47,11 @@ class FullScreenTextViewerActivity : AppCompatActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return if (item.itemId == R.id.share) {
val shareUri = FileProvider.getUriForFile(this,
BuildConfig.APPLICATION_ID,
File(path))
val shareUri = FileProvider.getUriForFile(
this,
BuildConfig.APPLICATION_ID,
File(path)
)
val shareIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND
@ -71,8 +72,7 @@ class FullScreenTextViewerActivity : AppCompatActivity() {
setContentView(R.layout.activity_full_screen_text)
setSupportActionBar(findViewById(R.id.textview_toolbar))
supportActionBar?.setDisplayShowTitleEnabled(false);
supportActionBar?.setDisplayShowTitleEnabled(false)
textView = findViewById(R.id.text_view)
val fileName = intent.getStringExtra("FILE_NAME")
@ -81,13 +81,12 @@ class FullScreenTextViewerActivity : AppCompatActivity() {
var text = readFile(path)
if (isMarkdown) {
val markwon = Markwon.create(applicationContext);
markwon.setMarkdown(textView, text);
val markwon = Markwon.create(applicationContext)
markwon.setMarkdown(textView, text)
} else {
textView.text = text
}
}
private fun readFile(fileName: String) = File(fileName).inputStream().readBytes().toString(Charsets.UTF_8)
}

View File

@ -48,7 +48,7 @@ class MagicCallActivity : BaseActivity() {
@BindView(R.id.controller_container)
lateinit var container: ViewGroup
@BindView(R.id.chatControllerView)
lateinit var chatContainer: ViewGroup
@ -60,10 +60,12 @@ class MagicCallActivity : BaseActivity() {
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
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_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
setContentView(R.layout.activity_magic_call)
@ -74,26 +76,32 @@ class MagicCallActivity : BaseActivity() {
if (!router!!.hasRootController()) {
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())
.popChangeHandler(HorizontalChangeHandler()))
.popChangeHandler(HorizontalChangeHandler())
)
} else {
router!!.setRoot(RouterTransaction.with(CallController(intent.extras))
router!!.setRoot(
RouterTransaction.with(CallController(intent.extras))
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler()))
.popChangeHandler(HorizontalChangeHandler())
)
}
}
val extras = intent.extras ?: Bundle()
extras.putBoolean("showToggleChat", true)
chatController = ChatController(extras)
chatRouter = Conductor.attachRouter(this, chatContainer, savedInstanceState)
chatRouter!!.setRoot(RouterTransaction.with(chatController)
chatRouter!!.setRoot(
RouterTransaction.with(chatController)
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler()))
.popChangeHandler(HorizontalChangeHandler())
)
}
fun showChat() {
chatContainer.visibility = View.VISIBLE
container.visibility = View.GONE

View File

@ -79,23 +79,31 @@ class MainActivity : BaseActivity(), ActionBarProvider {
@BindView(R.id.appBar)
lateinit var appBar: AppBarLayout
@BindView(R.id.toolbar)
lateinit var toolbar: MaterialToolbar
@BindView(R.id.home_toolbar)
lateinit var searchCardView: MaterialCardView
@BindView(R.id.search_text)
lateinit var searchInputText: MaterialTextView
@BindView(R.id.switch_account_button)
lateinit var settingsButton: MaterialButton
@BindView(R.id.controller_container)
lateinit var container: ViewGroup
@Inject
lateinit var userUtils: UserUtils
@Inject
lateinit var dataStore: ReactiveEntityStore<Persistable>
@Inject
lateinit var sqlCipherDatabaseSource: SqlCipherDatabaseSource
@Inject
lateinit var ncApi: NcApi
@ -124,39 +132,53 @@ class MainActivity : BaseActivity(), ActionBarProvider {
if (intent.hasExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL)) {
if (!router!!.hasRootController()) {
router!!.setRoot(RouterTransaction.with(ConversationsListController())
router!!.setRoot(
RouterTransaction.with(ConversationsListController())
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler()))
.popChangeHandler(HorizontalChangeHandler())
)
}
onNewIntent(intent)
} else if (!router!!.hasRootController()) {
if (hasDb) {
if (userUtils.anyUserExists()) {
router!!.setRoot(RouterTransaction.with(ConversationsListController())
router!!.setRoot(
RouterTransaction.with(ConversationsListController())
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler()))
.popChangeHandler(HorizontalChangeHandler())
)
} else {
if (!TextUtils.isEmpty(resources.getString(R.string.weblogin_url))) {
router!!.pushController(RouterTransaction.with(
WebViewLoginController(resources.getString(R.string.weblogin_url), false))
router!!.pushController(
RouterTransaction.with(
WebViewLoginController(resources.getString(R.string.weblogin_url), false)
)
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler()))
.popChangeHandler(HorizontalChangeHandler())
)
} else {
router!!.setRoot(RouterTransaction.with(ServerSelectionController())
router!!.setRoot(
RouterTransaction.with(ServerSelectionController())
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler()))
.popChangeHandler(HorizontalChangeHandler())
)
}
}
} else {
if (!TextUtils.isEmpty(resources.getString(R.string.weblogin_url))) {
router!!.pushController(RouterTransaction.with(
WebViewLoginController(resources.getString(R.string.weblogin_url), false))
router!!.pushController(
RouterTransaction.with(
WebViewLoginController(resources.getString(R.string.weblogin_url), false)
)
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler()))
.popChangeHandler(HorizontalChangeHandler())
)
} else {
router!!.setRoot(RouterTransaction.with(ServerSelectionController())
router!!.setRoot(
RouterTransaction.with(ServerSelectionController())
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler()))
.popChangeHandler(HorizontalChangeHandler())
)
}
}
}
@ -167,7 +189,7 @@ class MainActivity : BaseActivity(), ActionBarProvider {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
checkIfWeAreSecure()
}
handleActionFromContact(intent)
}
@ -182,7 +204,7 @@ class MainActivity : BaseActivity(), ActionBarProvider {
// userId @ server
userId = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DATA1))
}
cursor.close()
}
@ -193,65 +215,82 @@ class MainActivity : BaseActivity(), ActionBarProvider {
if (userUtils.currentUser?.baseUrl?.endsWith(baseUrl) == true) {
startConversation(user)
} else {
Snackbar.make(container, R.string.nc_phone_book_integration_account_not_found, Snackbar
.LENGTH_LONG).show()
Snackbar.make(
container, R.string.nc_phone_book_integration_account_not_found,
Snackbar.LENGTH_LONG
).show()
}
}
}
}
}
private fun startConversation(userId: String) {
val roomType = "1"
val currentUser = userUtils.currentUser ?: return
val credentials = ApiUtils.getCredentials(currentUser.username, currentUser.token)
val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(currentUser.baseUrl, roomType,
userId, null)
ncApi.createRoom(credentials,
retrofitBucket.url, retrofitBucket.queryMap)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<RoomOverall> {
override fun onSubscribe(d: Disposable) {}
override fun onNext(roomOverall: RoomOverall) {
val conversationIntent = Intent(context, MagicCallActivity::class.java)
val bundle = Bundle()
bundle.putParcelable(KEY_USER_ENTITY, currentUser)
bundle.putString(KEY_ROOM_TOKEN, roomOverall.ocs.data.token)
bundle.putString(KEY_ROOM_ID, roomOverall.ocs.data.roomId)
if (currentUser.hasSpreedFeatureCapability("chat-v2")) {
ncApi.getRoom(credentials,
ApiUtils.getRoom(currentUser.baseUrl,
roomOverall.ocs.data.token))
.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)
}
val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
currentUser.baseUrl, roomType,
userId, null
)
ncApi.createRoom(
credentials,
retrofitBucket.url, retrofitBucket.queryMap
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<RoomOverall> {
override fun onSubscribe(d: Disposable) {}
override fun onNext(roomOverall: RoomOverall) {
val conversationIntent = Intent(context, MagicCallActivity::class.java)
val bundle = Bundle()
bundle.putParcelable(KEY_USER_ENTITY, currentUser)
bundle.putString(KEY_ROOM_TOKEN, roomOverall.ocs.data.token)
bundle.putString(KEY_ROOM_ID, roomOverall.ocs.data.roomId)
if (currentUser.hasSpreedFeatureCapability("chat-v2")) {
ncApi.getRoom(
credentials,
ApiUtils.getRoom(
currentUser.baseUrl,
roomOverall.ocs.data.token
)
)
.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 onComplete() {}
})
} else {
conversationIntent.putExtras(bundle)
startActivity(conversationIntent)
Handler().postDelayed({
override fun onError(e: Throwable) {}
override fun onComplete() {}
})
} else {
conversationIntent.putExtras(bundle)
startActivity(conversationIntent)
Handler().postDelayed(
{
if (!isDestroyed) {
router!!.popCurrentController()
}
}, 100)
}
},
100
)
}
}
override fun onError(e: Throwable) {}
override fun onComplete() {}
})
override fun onError(e: Throwable) {}
override fun onComplete() {}
})
}
@RequiresApi(api = Build.VERSION_CODES.M)
@ -260,16 +299,17 @@ class MainActivity : BaseActivity(), ActionBarProvider {
if (keyguardManager.isKeyguardSecure && appPreferences.isScreenLocked) {
if (!SecurityUtils.checkIfWeAreAuthenticated(appPreferences.screenLockTimeout)) {
if (router != null && router!!.getControllerWithTag(LockedController.TAG) == null) {
router!!.pushController(RouterTransaction.with(LockedController())
router!!.pushController(
RouterTransaction.with(LockedController())
.pushChangeHandler(VerticalChangeHandler())
.popChangeHandler(VerticalChangeHandler())
.tag(LockedController.TAG))
.tag(LockedController.TAG)
)
}
}
}
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
@ -277,12 +317,16 @@ class MainActivity : BaseActivity(), ActionBarProvider {
if (intent.hasExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL)) {
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())
.popChangeHandler(HorizontalChangeHandler()))
.popChangeHandler(HorizontalChangeHandler())
)
} else {
ConductorRemapping.remapChatController(router!!, intent.getLongExtra(BundleKeys.KEY_INTERNAL_USER_ID, -1),
intent.getStringExtra(BundleKeys.KEY_ROOM_TOKEN), intent.extras!!, false)
ConductorRemapping.remapChatController(
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.ButterKnife
import coil.load
import coil.transform.CircleCropTransformation
import com.amulyakhare.textdrawable.TextDrawable
import com.facebook.drawee.view.SimpleDraweeView
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.preferences.AppPreferences
import com.stfalcon.chatkit.messages.MessageHolders
import com.stfalcon.chatkit.utils.DateFormatter
import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
@ -104,8 +102,8 @@ class MagicIncomingTextMessageViewHolder(incomingView: View) : MessageHolders
init {
ButterKnife.bind(
this,
itemView
this,
itemView
)
}
@ -131,13 +129,13 @@ class MagicIncomingTextMessageViewHolder(incomingView: View) : MessageHolders
messageUserAvatarView?.setImageDrawable(DisplayUtils.getRoundedDrawable(layerDrawable))
} else if (message.actorType == "bots") {
val drawable = TextDrawable.builder()
.beginConfig()
.bold()
.endConfig()
.buildRound(
">",
context!!.resources.getColor(R.color.black)
)
.beginConfig()
.bold()
.endConfig()
.buildRound(
">",
context!!.resources.getColor(R.color.black)
)
messageUserAvatarView!!.visibility = View.VISIBLE
messageUserAvatarView?.setImageDrawable(drawable)
}
@ -165,9 +163,9 @@ class MagicIncomingTextMessageViewHolder(incomingView: View) : MessageHolders
}
val bubbleDrawable = DisplayUtils.getMessageSelector(
bgBubbleColor,
resources.getColor(R.color.transparent),
bgBubbleColor, bubbleResource
bgBubbleColor,
resources.getColor(R.color.transparent),
bgBubbleColor, bubbleResource
)
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["id"] == message.activeUser!!.userId) {
messageString = DisplayUtils.searchAndReplaceWithMentionSpan(
messageText!!.context,
messageString,
individualHashMap["id"]!!,
individualHashMap["name"]!!,
individualHashMap["type"]!!,
message.activeUser!!,
R.xml.chip_you
messageText!!.context,
messageString,
individualHashMap["id"]!!,
individualHashMap["name"]!!,
individualHashMap["type"]!!,
message.activeUser!!,
R.xml.chip_you
)
} else {
messageString = DisplayUtils.searchAndReplaceWithMentionSpan(
messageText!!.context,
messageString,
individualHashMap["id"]!!,
individualHashMap["name"]!!,
individualHashMap["type"]!!,
message.activeUser!!,
R.xml.chip_others
messageText!!.context,
messageString,
individualHashMap["id"]!!,
individualHashMap["name"]!!,
individualHashMap["type"]!!,
message.activeUser!!,
R.xml.chip_others
)
}
} else if (individualHashMap["type"] == "file") {
@ -231,18 +229,21 @@ class MagicIncomingTextMessageViewHolder(incomingView: View) : MessageHolders
parentChatMessage.imageUrl?.let {
quotedMessagePreview?.visibility = View.VISIBLE
quotedMessagePreview?.load(it) {
addHeader("Authorization", ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token))
addHeader(
"Authorization",
ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token)
)
}
} ?: run {
quotedMessagePreview?.visibility = View.GONE
}
quotedUserName?.text = parentChatMessage.actorDisplayName
?: context!!.getText(R.string.nc_nick_guest)
?: context!!.getText(R.string.nc_nick_guest)
quotedMessage?.text = parentChatMessage.text
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)
} else {
quoteColoredView?.setBackgroundResource(R.color.textColorMaxContrast)

View File

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

View File

@ -90,6 +90,7 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
@Inject
lateinit var appPreferences: AppPreferences
@Inject
lateinit var okHttpClient: OkHttpClient
//endregion
@ -105,8 +106,10 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
WebRtcAudioManager.setBlacklistDeviceForOpenSLESUsage(true)
}
PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions.builder(this)
.createInitializationOptions())
PeerConnectionFactory.initialize(
PeerConnectionFactory.InitializationOptions.builder(this)
.createInitializationOptions()
)
} catch (e: UnsatisfiedLinkError) {
Log.w(TAG, e)
}
@ -120,8 +123,8 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
val securityKeyManager = SecurityKeyManager.getInstance()
val securityKeyConfig = SecurityKeyManagerConfig.Builder()
.setEnableDebugLogging(BuildConfig.DEBUG)
.build()
.setEnableDebugLogging(BuildConfig.DEBUG)
.build()
securityKeyManager.init(this, securityKeyConfig)
initializeWebRtc()
@ -136,13 +139,15 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
super.onCreate()
val imagePipelineConfig = ImagePipelineConfig.newBuilder(this)
.setNetworkFetcher(OkHttpNetworkFetcherWithCache(okHttpClient))
.setMainDiskCacheConfig(DiskCacheConfig.newBuilder(this)
.setMaxCacheSize(0)
.setMaxCacheSizeOnLowDiskSpace(0)
.setMaxCacheSizeOnVeryLowDiskSpace(0)
.build())
.build()
.setNetworkFetcher(OkHttpNetworkFetcherWithCache(okHttpClient))
.setMainDiskCacheConfig(
DiskCacheConfig.newBuilder(this)
.setMaxCacheSize(0)
.setMaxCacheSizeOnLowDiskSpace(0)
.setMaxCacheSizeOnVeryLowDiskSpace(0)
.build()
)
.build()
Fresco.initialize(this, imagePipelineConfig)
Security.insertProviderAt(Conscrypt.newProvider(), 1)
@ -152,8 +157,10 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
val pushRegistrationWork = OneTimeWorkRequest.Builder(PushRegistrationWorker::class.java).build()
val accountRemovalWork = OneTimeWorkRequest.Builder(AccountRemovalWorker::class.java).build()
val periodicCapabilitiesUpdateWork = PeriodicWorkRequest.Builder(CapabilitiesWorker::class.java,
12, TimeUnit.HOURS).build()
val periodicCapabilitiesUpdateWork = PeriodicWorkRequest.Builder(
CapabilitiesWorker::class.java,
12, TimeUnit.HOURS
).build()
val capabilitiesUpdateWork = OneTimeWorkRequest.Builder(CapabilitiesWorker::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(capabilitiesUpdateWork)
WorkManager.getInstance().enqueue(signalingSettingsWork)
WorkManager.getInstance().enqueueUniquePeriodicWork("DailyCapabilitiesUpdateWork", ExistingPeriodicWorkPolicy.REPLACE, periodicCapabilitiesUpdateWork)
WorkManager.getInstance().enqueueUniquePeriodicWork(
"DailyCapabilitiesUpdateWork",
ExistingPeriodicWorkPolicy.REPLACE,
periodicCapabilitiesUpdateWork
)
val config = BundledEmojiCompatConfig(this)
config.setReplaceAll(true)
@ -176,17 +187,16 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
}
//endregion
//region Protected methods
protected fun buildComponent() {
componentApplication = DaggerNextcloudTalkApplicationComponent.builder()
.busModule(BusModule())
.contextModule(ContextModule(applicationContext))
.databaseModule(DatabaseModule())
.restModule(RestModule(applicationContext))
.userModule(UserModule())
.arbitraryStorageModule(ArbitraryStorageModule())
.build()
.busModule(BusModule())
.contextModule(ContextModule(applicationContext))
.databaseModule(DatabaseModule())
.restModule(RestModule(applicationContext))
.userModule(UserModule())
.arbitraryStorageModule(ArbitraryStorageModule())
.build()
}
override fun attachBaseContext(base: Context) {
@ -196,19 +206,20 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
private fun buildDefaultImageLoader(): ImageLoader {
return ImageLoader.Builder(applicationContext)
.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.
.componentRegistry {
if (SDK_INT >= P) {
add(ImageDecoderDecoder(applicationContext))
} else {
add(GifDecoder())
}
add(SvgDecoder(applicationContext))
.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.
.componentRegistry {
if (SDK_INT >= P) {
add(ImageDecoderDecoder(applicationContext))
} else {
add(GifDecoder())
}
.okHttpClient(okHttpClient)
.build()
add(SvgDecoder(applicationContext))
}
.okHttpClient(okHttpClient)
.build()
}
companion object {
private val TAG = NextcloudTalkApplication::class.java.simpleName
//region Singleton

View File

@ -44,10 +44,6 @@ import android.widget.ImageView;
import android.widget.RelativeLayout;
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.changehandler.HorizontalChangeHandler;
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.json.conversations.Conversation;
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.ParticipantsOverall;
import com.nextcloud.talk.utils.ApiUtils;
@ -94,6 +89,9 @@ import java.util.List;
import javax.inject.Inject;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import autodagger.AutoInjector;
import butterknife.BindView;
import butterknife.OnClick;
@ -206,13 +204,13 @@ public class CallNotificationController extends BaseController {
originalBundle.putString(BundleKeys.INSTANCE.getKEY_CONVERSATION_NAME(), currentConversation.getDisplayName());
getRouter().replaceTopController(RouterTransaction.with(new CallController(originalBundle))
.popChangeHandler(new HorizontalChangeHandler())
.pushChangeHandler(new HorizontalChangeHandler()));
.popChangeHandler(new HorizontalChangeHandler())
.pushChangeHandler(new HorizontalChangeHandler()));
}
private void checkIfAnyParticipantsRemainInRoom() {
ncApi.getPeersForCall(credentials, ApiUtils.getUrlForCall(userBeingCalled.getBaseUrl(),
currentConversation.getToken()))
currentConversation.getToken()))
.subscribeOn(Schedulers.io())
.takeWhile(observable -> !leavingScreen)
.subscribe(new Observer<ParticipantsOverall>() {
@ -261,7 +259,7 @@ public class CallNotificationController extends BaseController {
private void handleFromNotification() {
boolean isConversationApiV3 = userBeingCalled.hasSpreedFeatureCapability("conversation-v3");
if(isConversationApiV3) {
if (isConversationApiV3) {
ncApi.getRoom(credentials, ApiUtils.getRoomV3(userBeingCalled.getBaseUrl(), roomId))
.subscribeOn(Schedulers.io())
.retry(3)
@ -279,12 +277,12 @@ public class CallNotificationController extends BaseController {
boolean hasCallFlags = userBeingCalled.hasSpreedFeatureCapability("conversation-call-flags");
if (hasCallFlags) {
if (isInCallWithVideo(currentConversation.callFlag)){
if (isInCallWithVideo(currentConversation.callFlag)) {
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 {
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 =
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();
DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline.fetchDecodedImage(imageRequest, null);
@ -422,11 +420,11 @@ public class CallNotificationController extends BaseController {
protected void onNewResultImpl(@Nullable Bitmap bitmap) {
if (avatarImageView != null) {
avatarImageView.getHierarchy().setImage(new BitmapDrawable(bitmap), 100,
true);
true);
if (getResources() != null) {
incomingTextRelativeLayout.setBackground(getResources().getDrawable(R.drawable
.incoming_gradient));
.incoming_gradient));
}
if ((AvatarStatusCodeHolder.getInstance().getStatusCode() == 200 || AvatarStatusCodeHolder.getInstance().getStatusCode() == 0) &&
@ -512,7 +510,7 @@ public class CallNotificationController extends BaseController {
if (TextUtils.isEmpty(callRingtonePreferenceString)) {
// play default sound
ringtoneUri = Uri.parse("android.resource://" + getApplicationContext().getPackageName() +
"/raw/librem_by_feandesign_call");
"/raw/librem_by_feandesign_call");
} else {
try {
RingtoneSettings ringtoneSettings = LoganSquare.parse(callRingtonePreferenceString, RingtoneSettings.class);
@ -520,7 +518,7 @@ public class CallNotificationController extends BaseController {
} catch (IOException e) {
Log.e(TAG, "Failed to parse ringtone settings");
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);
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.setOnPreparedListener(mp -> mediaPlayer.start());

View File

@ -85,50 +85,69 @@ import io.reactivex.schedulers.Schedulers
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
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 kotlin.collections.ArrayList
@AutoInjector(NextcloudTalkApplication::class)
class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleAdapter.OnItemClickListener {
@BindView(R.id.notification_settings)
lateinit var notificationsPreferenceScreen: MaterialPreferenceScreen
@BindView(R.id.progressBar)
lateinit var progressBar: ProgressBar
@BindView(R.id.conversation_info_message_notifications)
lateinit var messageNotificationLevel: MaterialChoicePreference
@BindView(R.id.webinar_settings)
lateinit var conversationInfoWebinar: MaterialPreferenceScreen
@BindView(R.id.conversation_info_lobby)
lateinit var conversationInfoLobby: MaterialSwitchPreference
@BindView(R.id.conversation_info_name)
lateinit var nameCategoryView: MaterialPreferenceCategory
@BindView(R.id.start_time_preferences)
lateinit var startTimeView: MaterialStandardPreference
@BindView(R.id.avatar_image)
lateinit var conversationAvatarImageView: SimpleDraweeView
@BindView(R.id.display_name_text)
lateinit var conversationDisplayName: EmojiTextView
@BindView(R.id.participants_list_category)
lateinit var participantsListCategory: MaterialPreferenceCategory
@BindView(R.id.addParticipantsAction)
lateinit var addParticipantsAction: MaterialStandardPreference;
lateinit var addParticipantsAction: MaterialStandardPreference
@BindView(R.id.recycler_view)
lateinit var recyclerView: RecyclerView
@BindView(R.id.deleteConversationAction)
lateinit var deleteConversationAction: MaterialStandardPreference
@BindView(R.id.leaveConversationAction)
lateinit var leaveConversationAction: MaterialStandardPreference
@BindView(R.id.ownOptions)
lateinit var ownOptionsCategory: MaterialPreferenceCategory
@BindView(R.id.muteCalls)
lateinit var muteCalls: MaterialSwitchPreference
@set:Inject
lateinit var ncApi: NcApi
@set:Inject
lateinit var context: Context
@set:Inject
lateinit var eventBus: EventBus
@ -164,7 +183,7 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
NextcloudTalkApplication.sharedApplication?.componentApplication?.inject(this)
conversationUser = args.getParcelable(BundleKeys.KEY_USER_ENTITY)
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)
}
@ -207,14 +226,19 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
}
private fun setupWebinaryView() {
if (conversationUser!!.hasSpreedFeatureCapability("webinary-lobby") && (conversation!!.type
== Conversation.ConversationType.ROOM_GROUP_CALL || conversation!!.type ==
Conversation.ConversationType.ROOM_PUBLIC_CALL) && conversation!!.canModerate(conversationUser)) {
if (conversationUser!!.hasSpreedFeatureCapability("webinary-lobby") &&
(
conversation!!.type == Conversation.ConversationType.ROOM_GROUP_CALL ||
conversation!!.type == Conversation.ConversationType.ROOM_PUBLIC_CALL
) &&
conversation!!.canModerate(conversationUser)
) {
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)
.isChecked = isLobbyOpenToModeratorsOnly
.isChecked = isLobbyOpenToModeratorsOnly
reconfigureLobbyTimerView()
@ -225,12 +249,17 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
currentTimeCalendar.timeInMillis = conversation!!.lobbyTimer * 1000
}
dateTimePicker(minDateTime = Calendar.getInstance(), requireFutureDateTime =
true, currentDateTime = currentTimeCalendar, dateTimeCallback = { _,
dateTime ->
reconfigureLobbyTimerView(dateTime)
submitLobbyChanges()
})
dateTimePicker(
minDateTime = Calendar.getInstance(),
requireFutureDateTime =
true,
currentDateTime = currentTimeCalendar,
dateTimeCallback = { _,
dateTime ->
reconfigureLobbyTimerView(dateTime)
submitLobbyChanges()
}
)
}
}
@ -253,7 +282,7 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
}
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) {
startTimeView.setSummary(DateUtils.getLocalDateStringFromTimestampForLobby(conversation!!.lobbyTimer))
@ -269,27 +298,34 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
}
fun submitLobbyChanges() {
val state = if ((conversationInfoLobby.findViewById<View>(R.id
.mp_checkable) as SwitchCompat).isChecked) 1 else 0
ncApi.setLobbyForConversation(ApiUtils.getCredentials(conversationUser!!.username,
conversationUser.token), ApiUtils.getUrlForLobbyForConversation
(conversationUser.baseUrl, conversation!!.token), state, conversation!!.lobbyTimer)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<GenericOverall> {
override fun onComplete() {
}
val state = if (
(
conversationInfoLobby.findViewById<View>(
R.id.mp_checkable
) as SwitchCompat
).isChecked
) 1 else 0
ncApi.setLobbyForConversation(
ApiUtils.getCredentials(conversationUser!!.username, conversationUser.token),
ApiUtils.getUrlForLobbyForConversation(conversationUser.baseUrl, conversation!!.token),
state,
conversation!!.lobbyTimer
)
.subscribeOn(Schedulers.io())
.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) {
@ -313,17 +349,21 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
private fun showDeleteConversationDialog(savedInstanceState: Bundle?) {
if (activity != null) {
LovelyStandardDialog(activity, LovelyStandardDialog.ButtonLayout.HORIZONTAL)
.setTopColorRes(R.color.nc_darkRed)
.setIcon(DisplayUtils.getTintedDrawable(context!!.resources,
R.drawable.ic_delete_black_24dp, R.color.bg_default))
.setPositiveButtonColor(context!!.resources.getColor(R.color.nc_darkRed))
.setTitle(R.string.nc_delete_call)
.setMessage(conversation!!.deleteWarningMessage)
.setPositiveButton(R.string.nc_delete) { deleteConversation() }
.setNegativeButton(R.string.nc_cancel, null)
.setInstanceStateHandler(ID_DELETE_CONVERSATION_DIALOG, saveStateHandler!!)
.setSavedInstanceState(savedInstanceState)
.show()
.setTopColorRes(R.color.nc_darkRed)
.setIcon(
DisplayUtils.getTintedDrawable(
context!!.resources,
R.drawable.ic_delete_black_24dp, R.color.bg_default
)
)
.setPositiveButtonColor(context!!.resources.getColor(R.color.nc_darkRed))
.setTitle(R.string.nc_delete_call)
.setMessage(conversation!!.deleteWarningMessage)
.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) {
super.onRestoreViewState(view, savedViewState)
if (LovelySaveStateHandler.wasDialogOnScreen(savedViewState)) {
//Dialog won't be restarted automatically, so we need to call this method.
//Each dialog knows how to restore its state
// Dialog won't be restarted automatically, so we need to call this method.
// Each dialog knows how to restore its state
showLovelyDialog(LovelySaveStateHandler.getSavedDialogId(savedViewState), savedViewState)
}
}
@ -397,27 +437,28 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
}
private fun getListOfParticipants() {
ncApi.getPeersForCall(credentials, ApiUtils.getUrlForParticipants(conversationUser!!.baseUrl, conversationToken))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<ParticipantsOverall> {
override fun onSubscribe(d: Disposable) {
participantsDisposable = d
}
ncApi.getPeersForCall(
credentials,
ApiUtils.getUrlForParticipants(conversationUser!!.baseUrl, conversationToken)
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<ParticipantsOverall> {
override fun onSubscribe(d: Disposable) {
participantsDisposable = d
}
override fun onNext(participantsOverall: ParticipantsOverall) {
handleParticipants(participantsOverall.ocs.data)
}
override fun onNext(participantsOverall: ParticipantsOverall) {
handleParticipants(participantsOverall.ocs.data)
}
override fun onError(e: Throwable) {
}
override fun onComplete() {
participantsDisposable!!.dispose()
}
})
override fun onError(e: Throwable) {
}
override fun onComplete() {
participantsDisposable!!.dispose()
}
})
}
@OnClick(R.id.addParticipantsAction)
@ -430,21 +471,33 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
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.putString(BundleKeys.KEY_TOKEN, conversation!!.token)
getRouter().pushController((RouterTransaction.with(ContactsController(bundle))
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler())))
getRouter().pushController(
(
RouterTransaction.with(
ContactsController(bundle)
)
.pushChangeHandler(
HorizontalChangeHandler()
)
.popChangeHandler(
HorizontalChangeHandler()
)
)
)
}
@OnClick(R.id.leaveConversationAction)
internal fun leaveConversation() {
workerData?.let {
WorkManager.getInstance().enqueue(OneTimeWorkRequest.Builder
(LeaveConversationWorker::class
.java).setInputData(it).build()
WorkManager.getInstance().enqueue(
OneTimeWorkRequest.Builder(
LeaveConversationWorker::class
.java
).setInputData(it).build()
)
popTwoLastControllers()
}
@ -452,8 +505,11 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
private fun deleteConversation() {
workerData?.let {
WorkManager.getInstance().enqueue(OneTimeWorkRequest.Builder
(DeleteConversationWorker::class.java).setInputData(it).build())
WorkManager.getInstance().enqueue(
OneTimeWorkRequest.Builder(
DeleteConversationWorker::class.java
).setInputData(it).build()
)
popTwoLastControllers()
}
}
@ -471,69 +527,67 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
private fun fetchRoomInfo() {
ncApi.getRoom(credentials, ApiUtils.getRoom(conversationUser!!.baseUrl, conversationToken))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<RoomOverall> {
override fun onSubscribe(d: Disposable) {
roomDisposable = d
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<RoomOverall> {
override fun onSubscribe(d: Disposable) {
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) {
conversation = roomOverall.ocs.data
if (isAttached && (!isBeingDestroyed || !isDestroyed)) {
ownOptionsCategory.visibility = View.VISIBLE
val conversationCopy = conversation
setupWebinaryView()
if (conversationCopy!!.canModerate(conversationUser)) {
addParticipantsAction.visibility = View.VISIBLE
if (!conversation!!.canLeave(conversationUser)) {
leaveConversationAction.visibility = View.GONE
} else {
addParticipantsAction.visibility = View.GONE
leaveConversationAction.visibility = View.VISIBLE
}
if (isAttached && (!isBeingDestroyed || !isDestroyed)) {
ownOptionsCategory.visibility = View.VISIBLE
setupWebinaryView()
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
if (!conversation!!.canModerate(conversationUser)) {
deleteConversationAction.visibility = View.GONE
} else {
deleteConversationAction.visibility = View.VISIBLE
}
}
override fun onError(e: Throwable) {
if (Conversation.ConversationType.ROOM_SYSTEM == conversation!!.type) {
muteCalls.visibility = View.GONE
}
}
getListOfParticipants()
override fun onComplete() {
roomDisposable!!.dispose()
progressBar.visibility = View.GONE
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() {
@ -543,12 +597,13 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
messageNotificationLevel.alpha = 1.0f
if (conversation!!.notificationLevel != Conversation.NotificationLevel.DEFAULT) {
val stringValue: String = when (EnumNotificationLevelConverter().convertToInt(conversation!!.notificationLevel)) {
1 -> "always"
2 -> "mention"
3 -> "never"
else -> "mention"
}
val stringValue: String =
when (EnumNotificationLevelConverter().convertToInt(conversation!!.notificationLevel)) {
1 -> "always"
2 -> "mention"
3 -> "never"
else -> "mention"
}
messageNotificationLevel.value = stringValue
} else {
@ -577,22 +632,38 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
private fun loadConversationAvatar() {
when (conversation!!.type) {
Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL -> if (!TextUtils.isEmpty
(conversation!!.name)) {
Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL -> if (
!TextUtils.isEmpty(conversation!!.name)
) {
val draweeController = Fresco.newDraweeControllerBuilder()
.setOldController(conversationAvatarImageView.controller)
.setAutoPlayAnimations(true)
.setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(conversationUser!!.baseUrl,
conversation!!.name, R.dimen.avatar_size_big), conversationUser))
.build()
.setOldController(conversationAvatarImageView.controller)
.setAutoPlayAnimations(true)
.setImageRequest(
DisplayUtils.getImageRequestForUrl(
ApiUtils.getUrlForAvatarWithName(
conversationUser!!.baseUrl,
conversation!!.name, R.dimen.avatar_size_big
),
conversationUser
)
)
.build()
conversationAvatarImageView.controller = draweeController
}
Conversation.ConversationType.ROOM_GROUP_CALL -> conversationAvatarImageView.hierarchy.setPlaceholderImage(DisplayUtils
.getRoundedBitmapDrawableFromVectorDrawableResource(resources,
R.drawable.ic_people_group_white_24px))
Conversation.ConversationType.ROOM_PUBLIC_CALL -> conversationAvatarImageView.hierarchy.setPlaceholderImage(DisplayUtils
.getRoundedBitmapDrawableFromVectorDrawableResource(resources,
R.drawable.ic_link_white_24px))
Conversation.ConversationType.ROOM_GROUP_CALL -> conversationAvatarImageView.hierarchy.setPlaceholderImage(
DisplayUtils
.getRoundedBitmapDrawableFromVectorDrawableResource(
resources,
R.drawable.ic_people_group_white_24px
)
)
Conversation.ConversationType.ROOM_PUBLIC_CALL -> conversationAvatarImageView.hierarchy.setPlaceholderImage(
DisplayUtils
.getRoundedBitmapDrawableFromVectorDrawableResource(
resources,
R.drawable.ic_link_white_24px
)
)
Conversation.ConversationType.ROOM_SYSTEM -> {
val layers = arrayOfNulls<Drawable>(2)
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 participant = userItem.model
if (participant.userId != conversationUser!!.userId) {
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_demote)),
BasicListItemWithImage(R.drawable.ic_delete_grey600_24dp,
context.getString(R.string.nc_remove_participant))
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_delete_grey600_24dp,
context.getString(R.string.nc_remove_participant)
)
)
if (!conversation!!.canModerate(conversationUser)) {
@ -629,7 +701,6 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
}
}
if (items.isNotEmpty()) {
MaterialDialog(activity!!, BottomSheet(WRAP_CONTENT)).show {
cornerRadius(res = R.dimen.corner_radius)
@ -639,38 +710,62 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
if (index == 0) {
if (participant.type == Participant.ParticipantType.MODERATOR) {
ncApi.demoteModeratorToUser(credentials, ApiUtils.getUrlForModerators(conversationUser.baseUrl, conversation!!.token), participant.userId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
getListOfParticipants()
}
ncApi.demoteModeratorToUser(
credentials,
ApiUtils.getUrlForModerators(conversationUser.baseUrl, conversation!!.token),
participant.userId
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
getListOfParticipants()
}
} else if (participant.type == Participant.ParticipantType.USER) {
ncApi.promoteUserToModerator(credentials, ApiUtils.getUrlForModerators(conversationUser.baseUrl, conversation!!.token), participant.userId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
getListOfParticipants()
}
ncApi.promoteUserToModerator(
credentials,
ApiUtils.getUrlForModerators(conversationUser.baseUrl, conversation!!.token),
participant.userId
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
getListOfParticipants()
}
}
} else if (index == 1) {
if (participant.type == Participant.ParticipantType.GUEST ||
participant.type == Participant.ParticipantType.USER_FOLLOWING_LINK) {
ncApi.removeParticipantFromConversation(credentials, ApiUtils.getUrlForRemovingParticipantFromConversation(conversationUser.baseUrl, conversation!!.token, true), participant.sessionId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
getListOfParticipants()
}
participant.type == Participant.ParticipantType.USER_FOLLOWING_LINK
) {
ncApi.removeParticipantFromConversation(
credentials,
ApiUtils.getUrlForRemovingParticipantFromConversation(
conversationUser.baseUrl,
conversation!!.token,
true
),
participant.sessionId
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
getListOfParticipants()
}
} else {
ncApi.removeParticipantFromConversation(credentials, ApiUtils.getUrlForRemovingParticipantFromConversation(conversationUser.baseUrl, conversation!!.token, false), participant.userId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
getListOfParticipants()
// get participants again
}
ncApi.removeParticipantFromConversation(
credentials,
ApiUtils.getUrlForRemovingParticipantFromConversation(
conversationUser.baseUrl,
conversation!!.token,
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 {
@ -709,7 +804,7 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
}
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 = null
}
}

View File

@ -29,10 +29,11 @@ interface ListItemWithImage {
}
data class BasicListItemWithImage(
@DrawableRes val iconRes: Int,
override val title: String) : ListItemWithImage {
@DrawableRes val iconRes: Int,
override val title: String
) : ListItemWithImage {
override fun populateIcon(imageView: ImageView) {
imageView.setImageResource(iconRes)
}
}
}

View File

@ -23,7 +23,6 @@ package com.nextcloud.talk.controllers.bottomsheet.items
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.afollestad.materialdialogs.MaterialDialog
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.utils.MDUtil.inflate
import com.afollestad.materialdialogs.utils.MDUtil.maybeSetTextColor
import com.google.android.material.textview.MaterialTextView
import com.nextcloud.talk.R
private const val KEY_ACTIVATED_INDEX = "activated_index"
internal class ListItemViewHolder(
itemView: View,
private val adapter: ListIconDialogAdapter<*>) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
itemView: View,
private val adapter: ListIconDialogAdapter<*>
) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
init {
itemView.setOnClickListener(this)
}
@ -53,11 +52,12 @@ internal class ListItemViewHolder(
}
internal class ListIconDialogAdapter<IT : ListItemWithImage>(
private var dialog: MaterialDialog,
private var items: List<IT>,
disabledItems: IntArray?,
private var waitForPositiveButton: Boolean,
private var selection: ListItemListener<IT>) : RecyclerView.Adapter<ListItemViewHolder>(), DialogAdapter<IT, ListItemListener<IT>> {
private var dialog: MaterialDialog,
private var items: List<IT>,
disabledItems: IntArray?,
private var waitForPositiveButton: Boolean,
private var selection: ListItemListener<IT>
) : RecyclerView.Adapter<ListItemViewHolder>(), DialogAdapter<IT, ListItemListener<IT>> {
private var disabledIndices: IntArray = disabledItems ?: IntArray(0)
@ -81,12 +81,13 @@ internal class ListIconDialogAdapter<IT : ListItemWithImage>(
}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int): ListItemViewHolder {
parent: ViewGroup,
viewType: Int
): ListItemViewHolder {
val listItemView: View = parent.inflate(dialog.windowContext, R.layout.menu_item_sheet)
val viewHolder = ListItemViewHolder(
itemView = listItemView,
adapter = this
itemView = listItemView,
adapter = this
)
viewHolder.titleView.maybeSetTextColor(dialog.windowContext, R.attr.md_color_content)
return viewHolder
@ -95,8 +96,9 @@ internal class ListIconDialogAdapter<IT : ListItemWithImage>(
override fun getItemCount() = items.size
override fun onBindViewHolder(
holder: ListItemViewHolder,
position: Int) {
holder: ListItemViewHolder,
position: Int
) {
holder.itemView.isEnabled = !disabledIndices.contains(position)
val currentItem = items[position]
@ -121,8 +123,9 @@ internal class ListIconDialogAdapter<IT : ListItemWithImage>(
}
override fun replaceItems(
items: List<IT>,
listener: ListItemListener<IT>) {
items: List<IT>,
listener: ListItemListener<IT>
) {
this.items = items
if (listener != null) {
this.selection = listener

View File

@ -28,37 +28,40 @@ import com.afollestad.materialdialogs.list.customListAdapter
import com.afollestad.materialdialogs.list.getListAdapter
typealias ListItemListener<IT> =
((dialog: MaterialDialog, index: Int, item: IT) -> Unit)?
((dialog: MaterialDialog, index: Int, item: IT) -> Unit)?
@CheckResult fun <IT : ListItemWithImage> MaterialDialog.listItemsWithImage(
items: List<IT>,
disabledIndices: IntArray? = null,
waitForPositiveButton: Boolean = true,
selection: ListItemListener<IT> = null): MaterialDialog {
@CheckResult
fun <IT : ListItemWithImage> MaterialDialog.listItemsWithImage(
items: List<IT>,
disabledIndices: IntArray? = null,
waitForPositiveButton: Boolean = true,
selection: ListItemListener<IT> = null
): MaterialDialog {
if (getListAdapter() != null) {
return updateListItemsWithImage(
items = items,
disabledIndices = disabledIndices
items = items,
disabledIndices = disabledIndices
)
}
val layoutManager = LinearLayoutManager(windowContext)
return customListAdapter(
adapter = ListIconDialogAdapter(
dialog = this,
items = items,
disabledItems = disabledIndices,
waitForPositiveButton = waitForPositiveButton,
selection = selection
),
layoutManager = layoutManager
adapter = ListIconDialogAdapter(
dialog = this,
items = items,
disabledItems = disabledIndices,
waitForPositiveButton = waitForPositiveButton,
selection = selection
),
layoutManager = layoutManager
)
}
fun MaterialDialog.updateListItemsWithImage(
items: List<ListItemWithImage>,
disabledIndices: IntArray? = null): MaterialDialog {
items: List<ListItemWithImage>,
disabledIndices: IntArray? = null
): MaterialDialog {
val adapter = getListAdapter()
check(adapter != null) {
"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
class CallNotificationClick {
}
class CallNotificationClick

View File

@ -33,7 +33,11 @@ import android.provider.ContactsContract
import android.util.Log
import androidx.core.content.ContextCompat
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 com.bluelinelabs.conductor.Controller
import com.google.gson.Gson
@ -54,10 +58,9 @@ import okhttp3.MediaType
import okhttp3.RequestBody
import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
class ContactAddressBookWorker(val context: Context, workerParameters: WorkerParameters) :
Worker(context, workerParameters) {
Worker(context, workerParameters) {
@Inject
lateinit var ncApi: NcApi
@ -85,7 +88,7 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
}
val deleteAll = inputData.getBoolean(DELETE_ALL, false)
if(deleteAll){
if (deleteAll) {
deleteAllLinkedAccounts()
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)
} else {
Log.d(TAG, "Account already exists")
@ -107,7 +110,7 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
val deviceContactsWithNumbers = collectContactsWithPhoneNumbersFromDevice()
if(deviceContactsWithNumbers.isNotEmpty()){
if (deviceContactsWithNumbers.isNotEmpty()) {
val currentLocale = ConfigurationCompat.getLocales(context.resources.configuration)[0].country
val map = mutableMapOf<String, Any>()
@ -117,28 +120,29 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
val json = Gson().toJson(map)
ncApi.searchContactsByPhoneNumber(
ApiUtils.getCredentials(currentUser.username, currentUser.token),
ApiUtils.getUrlForSearchByNumber(currentUser.baseUrl),
RequestBody.create(MediaType.parse("application/json"), json))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<ContactsByNumberOverall> {
override fun onComplete() {
}
ApiUtils.getCredentials(currentUser.username, currentUser.token),
ApiUtils.getUrlForSearchByNumber(currentUser.baseUrl),
RequestBody.create(MediaType.parse("application/json"), json)
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<ContactsByNumberOverall> {
override fun onComplete() {
}
override fun onSubscribe(d: Disposable) {
}
override fun onSubscribe(d: Disposable) {
}
override fun onNext(foundContacts: ContactsByNumberOverall) {
val contactsWithAssociatedPhoneNumbers = foundContacts.ocs.map
deleteLinkedAccounts(contactsWithAssociatedPhoneNumbers)
createLinkedAccounts(contactsWithAssociatedPhoneNumbers)
}
override fun onNext(foundContacts: ContactsByNumberOverall) {
val contactsWithAssociatedPhoneNumbers = foundContacts.ocs.map
deleteLinkedAccounts(contactsWithAssociatedPhoneNumbers)
createLinkedAccounts(contactsWithAssociatedPhoneNumbers)
}
override fun onError(e: Throwable) {
Log.e(javaClass.simpleName, "Failed to searchContactsByPhoneNumber", e)
}
})
override fun onError(e: Throwable) {
Log.e(javaClass.simpleName, "Failed to searchContactsByPhoneNumber", e)
}
})
}
// store timestamp
@ -151,11 +155,11 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
val deviceContactsWithNumbers: MutableMap<String, List<String>> = mutableMapOf()
val contactCursor = context.contentResolver.query(
ContactsContract.Contacts.CONTENT_URI,
null,
null,
null,
null
ContactsContract.Contacts.CONTENT_URI,
null,
null,
null,
null
)
if (contactCursor != null) {
@ -163,7 +167,8 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
contactCursor.moveToFirst()
for (i in 0 until contactCursor.count) {
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)
contactCursor.moveToNext()
}
@ -178,39 +183,50 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
Log.d(TAG, "deleteLinkedAccount")
fun deleteLinkedAccount(id: String) {
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()
.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 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(
rawContactUri,
null,
null,
null,
null
rawContactUri,
null,
null,
null,
null
)
if (rawContactsCursor != null) {
if (rawContactsCursor.count > 0) {
while (rawContactsCursor.moveToNext()) {
val lookupKey = rawContactsCursor.getString(rawContactsCursor.getColumnIndex(ContactsContract.Data.LOOKUP_KEY))
val contactId = rawContactsCursor.getString(rawContactsCursor.getColumnIndex(ContactsContract.Data.CONTACT_ID))
val lookupKey =
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)
}
}
@ -222,25 +238,29 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
}
private fun createLinkedAccounts(contactsWithAssociatedPhoneNumbers: Map<String, String>?) {
fun hasLinkedAccount(id: String) : Boolean {
fun hasLinkedAccount(id: String): Boolean {
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 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()
.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(
rawContactUri,
null,
where,
params,
null
rawContactUri,
null,
where,
params,
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 lookupContactUri = ContactsContract.Contacts.lookupContact(context.contentResolver, lookupUri)
val contactCursor = context.contentResolver.query(
lookupContactUri,
null,
null,
null,
null)
lookupContactUri,
null,
null,
null,
null
)
if (contactCursor != null) {
if (contactCursor.count > 0) {
contactCursor.moveToFirst()
val id = contactCursor.getString(contactCursor.getColumnIndex(ContactsContract.Contacts._ID))
if(hasLinkedAccount(id)){
if (hasLinkedAccount(id)) {
return
}
@ -285,34 +306,60 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
val rawContactsUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon().build()
val dataUri = ContactsContract.Data.CONTENT_URI.buildUpon().build()
ops.add(ContentProviderOperation
ops.add(
ContentProviderOperation
.newInsert(rawContactsUri)
.withValue(ContactsContract.RawContacts.ACCOUNT_NAME, accountName)
.withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType)
.withValue(ContactsContract.RawContacts.AGGREGATION_MODE,
ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT)
.withValue(
ContactsContract.RawContacts.AGGREGATION_MODE,
ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT
)
.withValue(ContactsContract.RawContacts.SYNC2, cloudId)
.build())
ops.add(ContentProviderOperation
.build()
)
ops.add(
ContentProviderOperation
.newInsert(dataUri)
.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])
.build())
ops.add(ContentProviderOperation
.build()
)
ops.add(
ContentProviderOperation
.newInsert(dataUri)
.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)
.build())
ops.add(ContentProviderOperation
.build()
)
ops.add(
ContentProviderOperation
.newInsert(dataUri)
.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.DATA2, String.format(context.resources.getString(R
.string.nc_phone_book_integration_chat_via), accountName))
.build())
.withValue(
ContactsContract.Data.DATA2,
String.format(
context.resources.getString(
R.string.nc_phone_book_integration_chat_via
),
accountName
)
)
.build()
)
try {
context.contentResolver.applyBatch(ContactsContract.AUTHORITY, ops)
@ -322,8 +369,11 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
Log.e(javaClass.simpleName, "", e)
}
Log.d(TAG, "added new entry for contact $displayName (cloudId: $cloudId | lookupKey: $lookupKey" +
" | id: $id)")
Log.d(
TAG,
"added new entry for contact $displayName (cloudId: $cloudId | lookupKey: $lookupKey" +
" | id: $id)"
)
}
contactCursor.close()
}
@ -341,18 +391,21 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
}
private fun getDisplayNameFromDeviceContact(id: String?): String? {
var displayName:String? = null
val whereName = ContactsContract.Data.MIMETYPE + " = ? AND " + ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID + " = ?"
var displayName: String? = null
val whereName =
ContactsContract.Data.MIMETYPE + " = ? AND " + ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID + " = ?"
val whereNameParams = arrayOf(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, id)
val nameCursor = context.contentResolver.query(
ContactsContract.Data.CONTENT_URI,
null,
whereName,
whereNameParams,
ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME)
ContactsContract.Data.CONTENT_URI,
null,
whereName,
whereNameParams,
ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME
)
if (nameCursor != null) {
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()
}
@ -362,11 +415,12 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
private fun getPhoneNumbersFromDeviceContact(id: String?): MutableList<String> {
val numbers = mutableListOf<String>()
val phonesNumbersCursor = context.contentResolver.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.Data.CONTACT_ID + " = " + id,
null,
null)
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.Data.CONTACT_ID + " = " + id,
null,
null
)
if (phonesNumbersCursor != null) {
while (phonesNumbersCursor.moveToNext()) {
@ -374,7 +428,7 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
}
phonesNumbersCursor.close()
}
if(numbers.size > 0){
if (numbers.size > 0) {
Log.d(TAG, "Found ${numbers.size} phone numbers for contact with id $id")
}
return numbers
@ -382,11 +436,11 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
fun deleteAllLinkedAccounts() {
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()
.buildUpon()
.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName)
.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType)
.build()
context.contentResolver.delete(rawContactUri, null, null)
Log.d(TAG, "deleted all linked accounts")
}
@ -398,42 +452,63 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
const val DELETE_ALL = "DELETE_ALL"
fun run(context: Context) {
if (ContextCompat.checkSelfPermission(context,
Manifest.permission.WRITE_CONTACTS) == PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(context,
Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
if (ContextCompat.checkSelfPermission(
context,
Manifest.permission.WRITE_CONTACTS
) == PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(
context,
Manifest.permission.READ_CONTACTS
) == PackageManager.PERMISSION_GRANTED
) {
WorkManager
.getInstance()
.enqueue(OneTimeWorkRequest.Builder(ContactAddressBookWorker::class.java)
.setInputData(Data.Builder().putBoolean(KEY_FORCE, false).build())
.build())
.getInstance()
.enqueue(
OneTimeWorkRequest.Builder(ContactAddressBookWorker::class.java)
.setInputData(Data.Builder().putBoolean(KEY_FORCE, false).build())
.build()
)
}
}
fun checkPermission(controller: Controller, context: Context): Boolean {
if (ContextCompat.checkSelfPermission(context,
Manifest.permission.WRITE_CONTACTS) != PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(context,
Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
controller.requestPermissions(arrayOf(Manifest.permission.WRITE_CONTACTS,
Manifest.permission.READ_CONTACTS), REQUEST_PERMISSION)
if (ContextCompat.checkSelfPermission(
context,
Manifest.permission.WRITE_CONTACTS
) != PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(
context,
Manifest.permission.READ_CONTACTS
) != PackageManager.PERMISSION_GRANTED
) {
controller.requestPermissions(
arrayOf(
Manifest.permission.WRITE_CONTACTS,
Manifest.permission.READ_CONTACTS
),
REQUEST_PERMISSION
)
return false
} else {
WorkManager
.getInstance()
.enqueue(OneTimeWorkRequest.Builder(ContactAddressBookWorker::class.java)
.setInputData(Data.Builder().putBoolean(KEY_FORCE, true).build())
.build())
.getInstance()
.enqueue(
OneTimeWorkRequest.Builder(ContactAddressBookWorker::class.java)
.setInputData(Data.Builder().putBoolean(KEY_FORCE, true).build())
.build()
)
return true
}
}
fun deleteAll() {
WorkManager
.getInstance()
.enqueue(OneTimeWorkRequest.Builder(ContactAddressBookWorker::class.java)
.setInputData(Data.Builder().putBoolean(DELETE_ALL, true).build())
.build())
WorkManager
.getInstance()
.enqueue(
OneTimeWorkRequest.Builder(ContactAddressBookWorker::class.java)
.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.preferences.AppPreferences
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
@AutoInjector(NextcloudTalkApplication::class)
class DownloadFileToCacheWorker(val context: Context, workerParameters: WorkerParameters) :
Worker(context, workerParameters) {
Worker(context, workerParameters) {
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 {
val downloadCall = ncApi.downloadFile(
ApiUtils.getCredentials(currentUser.username, currentUser.token),
url)
ApiUtils.getCredentials(currentUser.username, currentUser.token),
url
)
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 PROGRESS = "PROGRESS"
const val SUCCESS = "SUCCESS"
}
}

View File

@ -23,7 +23,11 @@ package com.nextcloud.talk.jobs
import android.content.Context
import android.net.Uri
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 com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.application.NextcloudTalkApplication
@ -46,13 +50,12 @@ import retrofit2.Response
import java.io.File
import java.io.FileOutputStream
import java.io.InputStream
import java.util.*
import java.util.ArrayList
import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerParameters) :
Worker(context, workerParameters) {
Worker(context, workerParameters) {
@Inject
lateinit var ncApi: NcApi
@ -107,31 +110,37 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
return requestBody
}
private fun uploadFile(currentUser: UserEntity, ncTargetpath: String?, filename: String, roomToken: String?,
requestBody: RequestBody?, sourcefileUri: Uri) {
private fun uploadFile(
currentUser: UserEntity,
ncTargetpath: String?,
filename: String,
roomToken: String?,
requestBody: RequestBody?,
sourcefileUri: Uri
) {
ncApi.uploadFile(
ApiUtils.getCredentials(currentUser.username, currentUser.token),
ApiUtils.getUrlForFileUpload(currentUser.baseUrl, currentUser.userId, ncTargetpath, filename),
requestBody
ApiUtils.getCredentials(currentUser.username, currentUser.token),
ApiUtils.getUrlForFileUpload(currentUser.baseUrl, currentUser.userId, ncTargetpath, filename),
requestBody
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<Response<GenericOverall>> {
override fun onSubscribe(d: Disposable) {
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<Response<GenericOverall>> {
override fun onSubscribe(d: Disposable) {
}
override fun onNext(t: Response<GenericOverall>) {
}
override fun onNext(t: Response<GenericOverall>) {
}
override fun onError(e: Throwable) {
Log.e(TAG, "failed to upload file $filename")
}
override fun onError(e: Throwable) {
Log.e(TAG, "failed to upload file $filename")
}
override fun onComplete() {
shareFile(roomToken, currentUser, ncTargetpath, filename)
copyFileToCache(sourcefileUri, filename)
}
})
override fun onComplete() {
shareFile(roomToken, currentUser, ncTargetpath, filename)
copyFileToCache(sourcefileUri, filename)
}
})
}
private fun copyFileToCache(sourceFileUri: Uri, filename: String) {
@ -151,13 +160,13 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
paths.add("$ncTargetpath/$filename")
val data = Data.Builder()
.putLong(KEY_INTERNAL_USER_ID, currentUser.id)
.putString(KEY_ROOM_TOKEN, roomToken)
.putStringArray(KEY_FILE_PATHS, paths.toTypedArray())
.build()
.putLong(KEY_INTERNAL_USER_ID, currentUser.id)
.putString(KEY_ROOM_TOKEN, roomToken)
.putStringArray(KEY_FILE_PATHS, paths.toTypedArray())
.build()
val shareWorker = OneTimeWorkRequest.Builder(ShareOperationWorker::class.java)
.setInputData(data)
.build()
.setInputData(data)
.build()
WorkManager.getInstance().enqueue(shareWorker)
}
@ -167,4 +176,4 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
const val NC_TARGETPATH = "NC_TARGETPATH"
const val ROOM_TOKEN = "ROOM_TOKEN"
}
}
}

View File

@ -25,7 +25,7 @@ package com.nextcloud.talk.models.json.chat
class ChatUtils {
companion object {
fun getParsedMessage(message: String?, messageParameters: HashMap<String?, HashMap<String?, String?>>?):
String? {
String? {
var resultMessage = message
if (messageParameters != null && messageParameters.size > 0) {
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.nextcloud.talk.models.json.chat.ChatMessage
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.*
import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.CALL_ENDED
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_renamed - {actor} renamed the conversation from "foo" to "bar"
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
moderator_promoted - {actor} promoted {user} to moderator
moderator_demoted - {actor} demoted {user} from moderator
*/
class EnumSystemMessageTypeConverter : StringBasedTypeConverter<ChatMessage.SystemMessageType>() {
override fun getFromString(string: String): ChatMessage.SystemMessageType {

View File

@ -20,12 +20,10 @@
package com.nextcloud.talk.receivers
import android.app.NotificationChannelGroup
import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.os.Build
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.database.user.UserUtils
import com.nextcloud.talk.utils.preferences.AppPreferences
import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
@ -50,16 +47,20 @@ class PackageReplacedReceiver : BroadcastReceiver() {
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
if (intent != null && intent.action != null &&
intent.action == "android.intent.action.MY_PACKAGE_REPLACED") {
intent.action == "android.intent.action.MY_PACKAGE_REPLACED"
) {
try {
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
if (packageInfo.versionCode > 43 && 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
if (!appPreferences.isNotificationChannelUpgradedToV2) {
for (notificationChannelGroup in notificationManager
.notificationChannelGroups) {
for (
notificationChannelGroup in notificationManager.notificationChannelGroups
) {
notificationManager.deleteNotificationChannelGroup(notificationChannelGroup.id)
}
@ -80,7 +81,6 @@ class PackageReplacedReceiver : BroadcastReceiver() {
} catch (e: PackageManager.NameNotFoundException) {
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.controllers.ChatController
class AttachmentDialog(val activity: Activity, var chatController: ChatController) : BottomSheetDialog(activity) {
@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
attachFromCloud?.text = chatController.resources?.let {
if(serverName.isNullOrEmpty()){
if (serverName.isNullOrEmpty()) {
serverName = it.getString(R.string.nc_server_product_name)
}
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.models.json.userprofile.Scope
class ScopeDialog(con: Context,
private val userInfoAdapter: ProfileController.UserInfoAdapter,
private val field: ProfileController.Field,
private val position: Int) :
BottomSheetDialog(con) {
class ScopeDialog(
con: Context,
private val userInfoAdapter: ProfileController.UserInfoAdapter,
private val field: ProfileController.Field,
private val position: Int
) :
BottomSheetDialog(con) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
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.models.ImportAccount
import com.nextcloud.talk.models.database.UserEntity
import java.util.*
import java.util.ArrayList
import java.util.Arrays
object AccountUtils {
@ -60,14 +61,15 @@ object AccountUtils {
break
}
} else {
if (internalUserEntity.username == importAccount.username && (internalUserEntity
.baseUrl == "http://" + importAccount.baseUrl ||
internalUserEntity.baseUrl == "https://" + importAccount
.baseUrl)) {
if (internalUserEntity.username == importAccount.username &&
(
internalUserEntity.baseUrl == "http://" + importAccount.baseUrl ||
internalUserEntity.baseUrl == "https://" + importAccount.baseUrl
)
) {
accountFound = true
break
}
}
} else {
accountFound = true
@ -88,8 +90,12 @@ object AccountUtils {
val packageManager = context.packageManager
var appName = ""
try {
appName = packageManager.getApplicationLabel(packageManager.getApplicationInfo(packageName,
PackageManager.GET_META_DATA)) as String
appName = packageManager.getApplicationLabel(
packageManager.getApplicationInfo(
packageName,
PackageManager.GET_META_DATA
)
) as String
} catch (e: PackageManager.NameNotFoundException) {
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)
if (packageInfo.versionCode >= 30060151) {
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)) {
val accMgr = AccountManager.get(context)
@ -118,7 +127,7 @@ object AccountUtils {
}
}
} catch (appNotFoundException: PackageManager.NameNotFoundException) {
// ignore
}
return false
@ -146,4 +155,3 @@ object AccountUtils {
return ImportAccount(username, password, urlString)
}
}

View File

@ -27,7 +27,13 @@ import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
import com.nextcloud.talk.controllers.ChatController
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"
if (router.getControllerWithTag(tag) != null) {
val backstack = router.backstack
@ -44,13 +50,17 @@ object ConductorRemapping {
router.setBackstack(backstack, HorizontalChangeHandler())
} else {
if (!replaceTop) {
router.pushController(RouterTransaction.with(ChatController(bundle))
router.pushController(
RouterTransaction.with(ChatController(bundle))
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler()).tag(tag))
.popChangeHandler(HorizontalChangeHandler()).tag(tag)
)
} else {
router.replaceTopController(RouterTransaction.with(ChatController(bundle))
router.replaceTopController(
RouterTransaction.with(ChatController(bundle))
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler()).tag(tag))
.popChangeHandler(HorizontalChangeHandler()).tag(tag)
)
}
}
}

View File

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

View File

@ -21,12 +21,10 @@
package com.nextcloud.talk.utils
import com.nextcloud.talk.R
import java.util.HashMap
object DrawableUtils {
fun getDrawableResourceIdForMimeType(mimetype: String): Int {
var localMimetype = mimetype
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.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.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.template.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_spreadsheet
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.addin.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_presentation
drawableMap["application/vnd.ms-powerpoint.presentation.macroEnabled.12"] = 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-powerpoint.addin.macroEnabled.12"] =
R.drawable.ic_mimetype_x_office_presentation
drawableMap["application/vnd.ms-powerpoint.presentation.macroEnabled.12"] =
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"] = 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-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-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-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-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-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.slideshow"] = 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.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.openxmlformats-officedocument.presentationml.presentation"] =
R.drawable.ic_mimetype_x_office_presentation
drawableMap["application/vnd.openxmlformats-officedocument.presentationml.slideshow"] =
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.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.wordperfect"] = R.drawable.ic_mimetype_x_office_document
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.IOException
import java.text.SimpleDateFormat
import java.util.*
import java.util.Date
object LoggingUtils {
fun writeLogEntryToFile(context: Context, logEntry: String) {
@ -38,8 +38,10 @@ object LoggingUtils {
val logEntryWithDateTime = dateFormat.format(date) + ": " + logEntry + "\n"
try {
val outputStream = context.openFileOutput("nc_log.txt",
Context.MODE_PRIVATE or Context.MODE_APPEND)
val outputStream = context.openFileOutput(
"nc_log.txt",
Context.MODE_PRIVATE or Context.MODE_APPEND
)
outputStream.write(logEntryWithDateTime.toByteArray())
outputStream.flush()
outputStream.close()
@ -48,13 +50,12 @@ object LoggingUtils {
} catch (e: IOException) {
e.printStackTrace()
}
}
fun sendMailWithAttachment(context: Context) {
val logFile = context.getFileStreamPath("nc_log.txt")
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_SUBJECT, "Talk logs")
emailIntent.type = "text/plain"

View File

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

View File

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

View File

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

View File

@ -13,11 +13,17 @@ import java.io.IOException
import java.net.InetAddress
import java.net.Socket
import java.security.GeneralSecurityException
import java.util.*
import javax.net.ssl.*
import java.util.LinkedList
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?,
trustManager: X509TrustManager) : SSLSocketFactory() {
class SSLSocketFactoryCompat(
keyManager: KeyManager?,
trustManager: X509TrustManager
) : SSLSocketFactory() {
private var delegate: SSLSocketFactory
@ -50,24 +56,24 @@ class SSLSocketFactoryCompat(keyManager: KeyManager?,
/* set up reasonable cipher suites */
val knownCiphers = arrayOf<String>(
// TLS 1.2
"TLS_RSA_WITH_AES_256_GCM_SHA384",
"TLS_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
// maximum interoperability
"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
"SSL_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_RSA_WITH_AES_128_CBC_SHA",
// additionally
"TLS_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"
// TLS 1.2
"TLS_RSA_WITH_AES_256_GCM_SHA384",
"TLS_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
// maximum interoperability
"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
"SSL_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_RSA_WITH_AES_128_CBC_SHA",
// additionally
"TLS_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"
)
val availableCiphers = socket.supportedCipherSuites
@ -89,31 +95,31 @@ class SSLSocketFactoryCompat(keyManager: KeyManager?,
} catch (e: IOException) {
// Exception is to be ignored
} finally {
socket?.close() // doesn't implement Closeable on all supported Android versions
socket?.close() // doesn't implement Closeable on all supported Android versions
}
}
}
}
init {
try {
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(
if (keyManager != null) arrayOf(keyManager) else null,
arrayOf(trustManager),
null)
if (keyManager != null) arrayOf(keyManager) else null,
arrayOf(trustManager),
null
)
delegate = sslContext.socketFactory
} catch (e: GeneralSecurityException) {
throw IllegalStateException() // system has no TLS
throw IllegalStateException() // system has no TLS
}
}
override fun getDefaultCipherSuites(): Array<String>? = cipherSuites
?: delegate.defaultCipherSuites
?: delegate.defaultCipherSuites
override fun getSupportedCipherSuites(): Array<String>? = cipherSuites
?: delegate.supportedCipherSuites
?: delegate.supportedCipherSuites
override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket {
val ssl = delegate.createSocket(s, host, port, autoClose)
@ -150,10 +156,8 @@ class SSLSocketFactoryCompat(keyManager: KeyManager?,
return ssl
}
private fun upgradeTLS(ssl: SSLSocket) {
protocols?.let { ssl.enabledProtocols = it }
cipherSuites?.let { ssl.enabledCipherSuites = it }
}
}