diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index bd1ef180f..4a4c17575 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -214,6 +214,10 @@ android:name=".conversation.info.ConversationInfoActivity" android:theme="@style/AppTheme" /> + + diff --git a/app/src/main/java/com/nextcloud/talk/conversation/info/ConversationInfoActivity.kt b/app/src/main/java/com/nextcloud/talk/conversation/info/ConversationInfoActivity.kt index 614030faa..3615b2d75 100644 --- a/app/src/main/java/com/nextcloud/talk/conversation/info/ConversationInfoActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversation/info/ConversationInfoActivity.kt @@ -27,7 +27,6 @@ package com.nextcloud.talk.conversation.info import android.annotation.SuppressLint -import android.app.Activity import android.content.Intent import android.graphics.drawable.ColorDrawable import android.os.Bundle @@ -42,8 +41,6 @@ import android.view.View.VISIBLE import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.SwitchCompat -import androidx.core.net.toFile -import androidx.core.view.ViewCompat import androidx.work.Data import androidx.work.OneTimeWorkRequest import androidx.work.WorkManager @@ -52,7 +49,6 @@ import com.afollestad.materialdialogs.LayoutMode.WRAP_CONTENT import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.bottomsheets.BottomSheet import com.afollestad.materialdialogs.datetime.dateTimePicker -import com.github.dhaval2404.imagepicker.ImagePicker import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.nextcloud.talk.R import com.nextcloud.talk.activities.BaseActivity @@ -85,9 +81,6 @@ import com.nextcloud.talk.shareditems.activities.SharedItemsActivity import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.DateConstants import com.nextcloud.talk.utils.DateUtils -import com.nextcloud.talk.utils.DisplayUtils -import com.nextcloud.talk.utils.Mimetype -import com.nextcloud.talk.utils.PickImage import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew import com.nextcloud.talk.utils.preferences.preferencestorage.DatabaseStorageModule @@ -97,12 +90,9 @@ import io.reactivex.Observer import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers -import okhttp3.MediaType.Companion.toMediaTypeOrNull -import okhttp3.MultipartBody -import okhttp3.RequestBody.Companion.asRequestBody import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode -import java.io.File +import org.parceler.Parcels import java.util.Calendar import java.util.Collections import java.util.Locale @@ -137,11 +127,6 @@ class ConversationInfoActivity : private var adapter: FlexibleAdapter? = null private var userItems: MutableList = ArrayList() - private lateinit var optionsMenu: Menu - private var edit = false - - private lateinit var pickImage: PickImage - private val workerData: Data? get() { if (!TextUtils.isEmpty(conversationToken) && conversationUser != null) { @@ -176,8 +161,6 @@ class ConversationInfoActivity : databaseStorageModule = DatabaseStorageModule(conversationUser, conversationToken) } - setupAvatarOptions() - binding.notificationSettingsView.notificationSettings.setStorageModule(databaseStorageModule) binding.webinarInfoView.webinarSettings.setStorageModule(databaseStorageModule) binding.guestAccessView.guestAccessSettings.setStorageModule(databaseStorageModule) @@ -203,22 +186,6 @@ class ConversationInfoActivity : binding.progressBar.let { viewThemeUtils.platform.colorCircularProgressBar(it) } } - private fun setupAvatarOptions() { - pickImage = PickImage(this, conversationUser) - binding.avatarUpload.setOnClickListener { pickImage.selectLocal() } - binding.avatarChoose.setOnClickListener { pickImage.selectRemote() } - binding.avatarCamera.setOnClickListener { pickImage.takePicture() } - binding.avatarDelete.setOnClickListener { deleteAvatar() } - binding.avatarImage.let { ViewCompat.setTransitionName(it, "userAvatar.transitionTag") } - - binding.let { - viewThemeUtils.material.themeFAB(it.avatarUpload) - viewThemeUtils.material.themeFAB(it.avatarChoose) - viewThemeUtils.material.themeFAB(it.avatarCamera) - viewThemeUtils.material.themeFAB(it.avatarDelete) - } - } - private fun setupActionBar() { setSupportActionBar(binding.conversationInfoToolbar) binding.conversationInfoToolbar.setNavigationOnClickListener { @@ -239,7 +206,6 @@ class ConversationInfoActivity : super.onCreateOptionsMenu(menu) if (CapabilitiesUtilNew.isConversationAvatarEndpointAvailable(conversationUser)) { menuInflater.inflate(R.menu.menu_conversation_info, menu) - optionsMenu = menu return true } return false @@ -248,13 +214,23 @@ class ConversationInfoActivity : override fun onPrepareOptionsMenu(menu: Menu): Boolean { super.onPrepareOptionsMenu(menu) // menu.findItem(R.id.edit).isVisible = editableFields.size > 0 - setEditMode(false) + return true } override fun onOptionsItemSelected(item: MenuItem): Boolean { if (item.itemId == R.id.edit) { - toggleEditMode() + val bundle = Bundle() + bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, conversationUser) + bundle.putParcelable( + BundleKeys.KEY_ACTIVE_CONVERSATION, + Parcels.wrap(conversation) + ) + bundle.putString(BundleKeys.KEY_ROOM_TOKEN, conversationToken) + + val intent = Intent(this, ConversationInfoEditActivity::class.java) + intent.putExtras(bundle) + startActivity(intent) } return true } @@ -288,135 +264,6 @@ class ConversationInfoActivity : } } - private fun toggleEditMode() { - edit = !edit - if (edit) { - setEditMode(true) - } else { - setEditMode(false) - } - } - - private fun setEditMode(editing: Boolean) { - if (editing) { - optionsMenu.findItem(R.id.edit).setTitle(R.string.save) - binding.avatarUpload.visibility = VISIBLE - binding.avatarChoose.visibility = VISIBLE - binding.avatarCamera.visibility = VISIBLE - binding.avatarDelete.visibility = VISIBLE - edit = true - } else { - optionsMenu.findItem(R.id.edit).setTitle(R.string.edit) - binding.avatarUpload.visibility = GONE - binding.avatarChoose.visibility = GONE - binding.avatarCamera.visibility = GONE - binding.avatarDelete.visibility = GONE - edit = false - } - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - when (resultCode) { - Activity.RESULT_OK -> { - pickImage.handleActivityResult( - requestCode, - resultCode, - data - ) { uploadAvatar(it.toFile()) } - } - - ImagePicker.RESULT_ERROR -> { - Toast.makeText(this, ImagePicker.getError(data), Toast.LENGTH_SHORT).show() - } - - else -> { - Log.i(TAG, "Task Cancelled") - } - } - } - - private fun uploadAvatar(file: File?) { - val builder = MultipartBody.Builder() - builder.setType(MultipartBody.FORM) - builder.addFormDataPart( - "file", - file!!.name, - file.asRequestBody(Mimetype.IMAGE_PREFIX_GENERIC.toMediaTypeOrNull()) - ) - val filePart: MultipartBody.Part = MultipartBody.Part.createFormData( - "file", - file.name, - file.asRequestBody(Mimetype.IMAGE_JPG.toMediaTypeOrNull()) - ) - - // upload file - ncApi.uploadAvatar( - credentials, - ApiUtils.getUrlForConversationAvatar(1, conversationUser.baseUrl, conversation!!.token), - filePart - ) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } - - override fun onNext(genericOverall: GenericOverall) { - DisplayUtils.loadAvatarImage(conversationUser, binding.avatarImage, true) - } - - override fun onError(e: Throwable) { - Toast.makeText( - applicationContext, - context.getString(R.string.default_error_msg), - Toast.LENGTH_LONG - ).show() - Log.e(TAG, "Error uploading avatar", e) - } - - override fun onComplete() { - setEditMode(false) - } - }) - } - - private fun deleteAvatar() { - ncApi.deleteAvatar( - credentials, - ApiUtils.getUrlForConversationAvatar(1, conversationUser.baseUrl, conversationToken) - ) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } - - override fun onNext(genericOverall: GenericOverall) { - DisplayUtils.loadAvatarImage( - conversationUser, - binding.avatarImage, - true - ) - } - - override fun onError(e: Throwable) { - Toast.makeText( - applicationContext, - context.getString(R.string.default_error_msg), - Toast.LENGTH_LONG - ).show() - Log.e(TAG, "Failed to delete avatar", e) - } - - override fun onComplete() { - setEditMode(false) - } - }) - } - private fun showSharedItems() { val intent = Intent(this, SharedItemsActivity::class.java) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) @@ -816,11 +663,9 @@ class ConversationInfoActivity : } else { binding?.clearConversationHistory?.visibility = GONE } - binding.avatarButtons.visibility = VISIBLE } else { binding?.addParticipantsAction?.visibility = GONE binding?.clearConversationHistory?.visibility = GONE - binding.avatarButtons.visibility = GONE } if (!isDestroyed) { diff --git a/app/src/main/java/com/nextcloud/talk/conversation/info/ConversationInfoEditActivity.kt b/app/src/main/java/com/nextcloud/talk/conversation/info/ConversationInfoEditActivity.kt new file mode 100644 index 000000000..55718e9c3 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/conversation/info/ConversationInfoEditActivity.kt @@ -0,0 +1,327 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * @author Andy Scherzinger + * @author Tim Krüger + * @author Marcel Hibbe + * Copyright (C) 2022-2023 Marcel Hibbe (dev@mhibbe.de) + * Copyright (C) 2021-2022 Tim Krüger + * Copyright (C) 2021 Andy Scherzinger (info@andy-scherzinger.de) + * Copyright (C) 2017-2018 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.conversation.info + +import android.app.Activity +import android.content.Intent +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.text.TextUtils +import android.util.Log +import android.view.Menu +import android.widget.Toast +import androidx.core.net.toFile +import androidx.core.view.ViewCompat +import autodagger.AutoInjector +import com.github.dhaval2404.imagepicker.ImagePicker +import com.nextcloud.talk.R +import com.nextcloud.talk.activities.BaseActivity +import com.nextcloud.talk.api.NcApi +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.data.user.model.User +import com.nextcloud.talk.databinding.ActivityConversationInfoEditBinding +import com.nextcloud.talk.extensions.loadAvatar +import com.nextcloud.talk.extensions.loadConversationAvatar +import com.nextcloud.talk.extensions.loadSystemAvatar +import com.nextcloud.talk.models.json.conversations.Conversation +import com.nextcloud.talk.models.json.generic.GenericOverall +import com.nextcloud.talk.repositories.conversations.ConversationsRepository +import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.DateUtils +import com.nextcloud.talk.utils.DisplayUtils +import com.nextcloud.talk.utils.Mimetype +import com.nextcloud.talk.utils.PickImage +import com.nextcloud.talk.utils.bundle.BundleKeys +import io.reactivex.Observer +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody.Companion.asRequestBody +import org.parceler.Parcels +import java.io.File +import javax.inject.Inject + +@AutoInjector(NextcloudTalkApplication::class) +class ConversationInfoEditActivity : + BaseActivity() { + + private lateinit var binding: ActivityConversationInfoEditBinding + + @Inject + lateinit var ncApi: NcApi + + @Inject + lateinit var conversationsRepository: ConversationsRepository + + @Inject + lateinit var dateUtils: DateUtils + + private lateinit var conversationToken: String + private lateinit var conversationUser: User + private lateinit var credentials: String + + private var conversation: Conversation? = null + + private lateinit var optionsMenu: Menu + private var edit = false + + private lateinit var pickImage: PickImage + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) + + binding = ActivityConversationInfoEditBinding.inflate(layoutInflater) + setupActionBar() + setContentView(binding.root) + setupSystemColors() + + val extras: Bundle? = intent.extras + + conversationUser = extras?.getParcelable(BundleKeys.KEY_USER_ENTITY)!! + conversationToken = extras.getString(BundleKeys.KEY_ROOM_TOKEN)!! + + if (intent.hasExtra(BundleKeys.KEY_ACTIVE_CONVERSATION)) { + conversation = Parcels.unwrap(extras.getParcelable(BundleKeys.KEY_ACTIVE_CONVERSATION)) + } + + credentials = ApiUtils.getCredentials(conversationUser.username, conversationUser.token) + } + + override fun onResume() { + super.onResume() + + loadConversationAvatar() + + binding.displayNameText.text = conversation!!.displayName + + if (conversation!!.description != null && conversation!!.description!!.isNotEmpty()) { + binding.descriptionText.text = conversation!!.description + } + + setupAvatarOptions() + } + + private fun setupAvatarOptions() { + pickImage = PickImage(this, conversationUser) + binding.avatarUpload.setOnClickListener { pickImage.selectLocal() } + binding.avatarChoose.setOnClickListener { pickImage.selectRemote() } + binding.avatarCamera.setOnClickListener { pickImage.takePicture() } + binding.avatarDelete.setOnClickListener { deleteAvatar() } + binding.avatarImage.let { ViewCompat.setTransitionName(it, "userAvatar.transitionTag") } + + binding.let { + viewThemeUtils.material.themeFAB(it.avatarUpload) + viewThemeUtils.material.themeFAB(it.avatarChoose) + viewThemeUtils.material.themeFAB(it.avatarCamera) + viewThemeUtils.material.themeFAB(it.avatarDelete) + } + } + + private fun setupActionBar() { + setSupportActionBar(binding.conversationInfoEditToolbar) + binding.conversationInfoEditToolbar.setNavigationOnClickListener { + onBackPressed() + } + supportActionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.setDisplayShowHomeEnabled(true) + supportActionBar?.setIcon(ColorDrawable(resources!!.getColor(android.R.color.transparent))) + supportActionBar?.title = resources!!.getString(R.string.nc_conversation_menu_conversation_info) + + viewThemeUtils.material.themeToolbar(binding.conversationInfoEditToolbar) + } + + // override fun onCreateOptionsMenu(menu: Menu): Boolean { + // super.onCreateOptionsMenu(menu) + // + // menuInflater.inflate(R.menu.menu_conversation_info, menu) + // optionsMenu = menu + // return true + // } + // + // override fun onPrepareOptionsMenu(menu: Menu): Boolean { + // super.onPrepareOptionsMenu(menu) + // // menu.findItem(R.id.edit).isVisible = editableFields.size > 0 + // setEditMode(true) + // return true + // } + // + // override fun onOptionsItemSelected(item: MenuItem): Boolean { + // if (item.itemId == R.id.edit) { + // toggleEditMode() + // } + // return true + // } + // + // + // private fun toggleEditMode() { + // edit = !edit + // if (edit) { + // setEditMode(true) + // } else { + // setEditMode(false) + // } + // } + // + // private fun setEditMode(editing: Boolean) { + // if (editing) { + // optionsMenu.findItem(R.id.edit).setTitle(R.string.save) + // edit = true + // } else { + // optionsMenu.findItem(R.id.edit).setTitle(R.string.edit) + // edit = false + // } + // } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + when (resultCode) { + Activity.RESULT_OK -> { + pickImage.handleActivityResult( + requestCode, + resultCode, + data + ) { uploadAvatar(it.toFile()) } + } + + ImagePicker.RESULT_ERROR -> { + Toast.makeText(this, ImagePicker.getError(data), Toast.LENGTH_SHORT).show() + } + + else -> { + Log.i(TAG, "Task Cancelled") + } + } + } + + private fun uploadAvatar(file: File?) { + val builder = MultipartBody.Builder() + builder.setType(MultipartBody.FORM) + builder.addFormDataPart( + "file", + file!!.name, + file.asRequestBody(Mimetype.IMAGE_PREFIX_GENERIC.toMediaTypeOrNull()) + ) + val filePart: MultipartBody.Part = MultipartBody.Part.createFormData( + "file", + file.name, + file.asRequestBody(Mimetype.IMAGE_JPG.toMediaTypeOrNull()) + ) + + // upload file + ncApi.uploadAvatar( + credentials, + ApiUtils.getUrlForConversationAvatar(1, conversationUser.baseUrl, conversation!!.token), + filePart + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(genericOverall: GenericOverall) { + DisplayUtils.loadAvatarImage(conversationUser, binding.avatarImage, true) + } + + override fun onError(e: Throwable) { + Toast.makeText( + applicationContext, + context.getString(R.string.default_error_msg), + Toast.LENGTH_LONG + ).show() + Log.e(TAG, "Error uploading avatar", e) + } + + override fun onComplete() { + // setEditMode(false) + } + }) + } + + private fun deleteAvatar() { + ncApi.deleteAvatar( + credentials, + ApiUtils.getUrlForConversationAvatar(1, conversationUser.baseUrl, conversationToken) + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(genericOverall: GenericOverall) { + DisplayUtils.loadAvatarImage( + conversationUser, + binding.avatarImage, + true + ) + } + + override fun onError(e: Throwable) { + Toast.makeText( + applicationContext, + context.getString(R.string.default_error_msg), + Toast.LENGTH_LONG + ).show() + Log.e(TAG, "Failed to delete avatar", e) + } + + override fun onComplete() { + // setEditMode(false) + } + }) + } + + private fun loadConversationAvatar() { + when (conversation!!.type) { + Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL -> if (!TextUtils.isEmpty(conversation!!.name)) { + conversation!!.name?.let { binding.avatarImage.loadAvatar(conversationUser, it) } + } + + Conversation.ConversationType.ROOM_GROUP_CALL, Conversation.ConversationType.ROOM_PUBLIC_CALL -> { + binding.avatarImage.loadConversationAvatar(conversationUser, conversation!!) + } + + Conversation.ConversationType.ROOM_SYSTEM -> { + binding.avatarImage.loadSystemAvatar() + } + + else -> { + // unused atm + } + } + } + + companion object { + private const val TAG = "ConversationEditInfo" + } +} diff --git a/app/src/main/res/layout/activity_conversation_info.xml b/app/src/main/res/layout/activity_conversation_info.xml index 2ed5437ef..daf7f7964 100644 --- a/app/src/main/res/layout/activity_conversation_info.xml +++ b/app/src/main/res/layout/activity_conversation_info.xml @@ -106,66 +106,6 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +