SetStatusDialogFragment.kt 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. /*
  2. * Nextcloud Android client application
  3. *
  4. * @author Tobias Kaminsky
  5. * Copyright (C) 2020 Nextcloud GmbH
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  9. * License as published by the Free Software Foundation; either
  10. * version 3 of the License, or any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  16. *
  17. * You should have received a copy of the GNU Affero General Public
  18. * License along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. package com.nextcloud.ui
  21. import android.accounts.Account
  22. import android.annotation.SuppressLint
  23. import android.app.Dialog
  24. import android.content.Context
  25. import android.os.Bundle
  26. import android.view.LayoutInflater
  27. import android.view.View
  28. import android.view.ViewGroup
  29. import android.view.inputmethod.InputMethodManager
  30. import androidx.annotation.VisibleForTesting
  31. import androidx.emoji.bundled.BundledEmojiCompatConfig
  32. import androidx.emoji.text.EmojiCompat
  33. import androidx.fragment.app.DialogFragment
  34. import androidx.recyclerview.widget.LinearLayoutManager
  35. import com.google.android.material.dialog.MaterialAlertDialogBuilder
  36. import com.google.gson.Gson
  37. import com.google.gson.reflect.TypeToken
  38. import com.nextcloud.client.account.User
  39. import com.nextcloud.client.account.UserAccountManager
  40. import com.nextcloud.client.core.AsyncRunner
  41. import com.nextcloud.client.di.Injectable
  42. import com.nextcloud.client.network.ClientFactory
  43. import com.owncloud.android.R
  44. import com.owncloud.android.datamodel.ArbitraryDataProvider
  45. import com.owncloud.android.lib.common.OwnCloudClientFactory
  46. import com.owncloud.android.lib.resources.users.ClearStatusMessageRemoteOperation
  47. import com.owncloud.android.lib.resources.users.PredefinedStatus
  48. import com.owncloud.android.lib.resources.users.SetPredefinedCustomStatusMessageRemoteOperation
  49. import com.owncloud.android.lib.resources.users.SetStatusRemoteOperation
  50. import com.owncloud.android.lib.resources.users.SetUserDefinedCustomStatusMessageRemoteOperation
  51. import com.owncloud.android.lib.resources.users.Status
  52. import com.owncloud.android.lib.resources.users.StatusType
  53. import com.owncloud.android.ui.activity.BaseActivity
  54. import com.owncloud.android.ui.adapter.PredefinedStatusClickListener
  55. import com.owncloud.android.ui.adapter.PredefinedStatusListAdapter
  56. import com.vanniktech.emoji.EmojiManager
  57. import com.vanniktech.emoji.EmojiPopup
  58. import com.vanniktech.emoji.googlecompat.GoogleCompatEmojiProvider
  59. import kotlinx.android.synthetic.main.dialog_set_status.*
  60. import java.util.ArrayList
  61. import javax.inject.Inject
  62. private const val ARG_CURRENT_USER_PARAM = "currentUser"
  63. private const val ARG_CURRENT_STATUS_PARAM = "currentStatus"
  64. class SetStatusDialogFragment : DialogFragment(),
  65. PredefinedStatusClickListener,
  66. Injectable {
  67. private lateinit var dialogView: View
  68. private var currentUser: User? = null
  69. private var currentStatus: Status? = null
  70. private lateinit var accountManager: UserAccountManager
  71. private lateinit var predefinedStatus: ArrayList<PredefinedStatus>
  72. private lateinit var adapter: PredefinedStatusListAdapter
  73. private var selectedPredefinedMessageId: String? = null
  74. private lateinit var popup: EmojiPopup
  75. @Inject
  76. lateinit var arbitraryDataProvider: ArbitraryDataProvider
  77. @Inject
  78. lateinit var asyncRunner: AsyncRunner
  79. @Inject
  80. lateinit var clientFactory: ClientFactory
  81. override fun onCreate(savedInstanceState: Bundle?) {
  82. super.onCreate(savedInstanceState)
  83. arguments?.let {
  84. currentUser = it.getParcelable(ARG_CURRENT_USER_PARAM)
  85. currentStatus = it.getParcelable(ARG_CURRENT_STATUS_PARAM)
  86. val json = arbitraryDataProvider.getValue(currentUser, ArbitraryDataProvider.PREDEFINED_STATUS)
  87. if (json.isNotEmpty()) {
  88. val myType = object : TypeToken<ArrayList<PredefinedStatus>>() {}.type
  89. predefinedStatus = Gson().fromJson(json, myType)
  90. }
  91. }
  92. val config = BundledEmojiCompatConfig(requireContext())
  93. config.setReplaceAll(true)
  94. val emojiCompat = EmojiCompat.init(config)
  95. EmojiManager.install(GoogleCompatEmojiProvider(emojiCompat))
  96. }
  97. @SuppressLint("InflateParams")
  98. override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
  99. dialogView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_set_status, null)
  100. return MaterialAlertDialogBuilder(requireContext())
  101. .setView(dialogView)
  102. .create()
  103. }
  104. override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
  105. super.onViewCreated(view, savedInstanceState)
  106. accountManager = (activity as BaseActivity).userAccountManager
  107. currentStatus?.let {
  108. emoji.setText(it.icon)
  109. customStatusInput.text.clear()
  110. customStatusInput.setText(it.message)
  111. }
  112. adapter = PredefinedStatusListAdapter(this, requireContext())
  113. if (this::predefinedStatus.isInitialized) {
  114. adapter.list = predefinedStatus
  115. }
  116. predefinedStatusList.adapter = adapter
  117. predefinedStatusList.layoutManager = LinearLayoutManager(context)
  118. onlineStatus.setOnClickListener { setStatus(StatusType.ONLINE) }
  119. dndStatus.setOnClickListener { setStatus(StatusType.DND) }
  120. awayStatus.setOnClickListener { setStatus(StatusType.AWAY) }
  121. invisibleStatus.setOnClickListener { setStatus(StatusType.INVISIBLE) }
  122. clearStatus.setOnClickListener { clearStatus() }
  123. setStatus.setOnClickListener { setStatusMessage() }
  124. emoji.setOnClickListener { openEmojiPopup() }
  125. popup = EmojiPopup.Builder
  126. .fromRootView(view)
  127. .setOnEmojiClickListener { _, _ ->
  128. popup.dismiss()
  129. emoji.clearFocus()
  130. val imm: InputMethodManager = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as
  131. InputMethodManager
  132. imm.hideSoftInputFromWindow(emoji.windowToken, 0)
  133. }
  134. .build(emoji)
  135. emoji.disableKeyboardInput(popup)
  136. emoji.forceSingleEmoji()
  137. }
  138. private fun openEmojiPopup() {
  139. popup.show()
  140. }
  141. private fun clearStatus() {
  142. asyncRunner.postQuickTask(ClearStatusTask(accountManager.currentOwnCloudAccount?.savedAccount, context),
  143. { dismiss(it) })
  144. }
  145. private fun setStatus(statusType: StatusType) {
  146. asyncRunner.postQuickTask(
  147. SetStatusTask(
  148. statusType,
  149. accountManager.currentOwnCloudAccount?.savedAccount,
  150. context)
  151. )
  152. }
  153. private fun setStatusMessage() {
  154. if (selectedPredefinedMessageId != null) {
  155. asyncRunner.postQuickTask(
  156. SetPredefinedCustomStatusTask(
  157. selectedPredefinedMessageId!!,
  158. 1603719631,
  159. accountManager.currentOwnCloudAccount?.savedAccount,
  160. context),
  161. { dismiss(it) }
  162. )
  163. } else {
  164. asyncRunner.postQuickTask(
  165. SetUserDefinedCustomStatusTask(
  166. customStatusInput.text.toString(),
  167. emoji.text.toString(),
  168. 1603719631,
  169. accountManager.currentOwnCloudAccount?.savedAccount,
  170. context),
  171. { dismiss(it) }
  172. )
  173. }
  174. }
  175. private fun dismiss(boolean: Boolean) {
  176. if (boolean) {
  177. dismiss()
  178. }
  179. }
  180. private class SetPredefinedCustomStatusTask(val messageId: String,
  181. val clearAt: Long,
  182. val account: Account?,
  183. val context: Context?) : Function0<Boolean> {
  184. override fun invoke(): Boolean {
  185. val client = OwnCloudClientFactory.createNextcloudClient(account, context)
  186. return SetPredefinedCustomStatusMessageRemoteOperation(messageId, clearAt).execute(client).isSuccess
  187. }
  188. }
  189. private class SetUserDefinedCustomStatusTask(val message: String,
  190. val icon: String,
  191. val clearAt: Long,
  192. val account: Account?,
  193. val context: Context?) : Function0<Boolean> {
  194. override fun invoke(): Boolean {
  195. val client = OwnCloudClientFactory.createNextcloudClient(account, context)
  196. return SetUserDefinedCustomStatusMessageRemoteOperation(message, icon, clearAt).execute(client).isSuccess
  197. }
  198. }
  199. private class SetStatusTask(val statusType: StatusType,
  200. val account: Account?,
  201. val context: Context?) : Function0<Boolean> {
  202. override fun invoke(): Boolean {
  203. val client = OwnCloudClientFactory.createNextcloudClient(account, context)
  204. return SetStatusRemoteOperation(statusType).execute(client).isSuccess
  205. }
  206. }
  207. private class ClearStatusTask(val account: Account?, val context: Context?) : Function0<Boolean> {
  208. override fun invoke(): Boolean {
  209. val client = OwnCloudClientFactory.createNextcloudClient(account, context)
  210. return ClearStatusMessageRemoteOperation().execute(client).isSuccess
  211. }
  212. }
  213. /**
  214. * Fragment creator
  215. */
  216. companion object {
  217. @JvmStatic
  218. fun newInstance(user: User, status: Status?) =
  219. SetStatusDialogFragment().apply {
  220. arguments = Bundle().apply {
  221. putParcelable(ARG_CURRENT_USER_PARAM, user)
  222. putParcelable(ARG_CURRENT_STATUS_PARAM, status)
  223. }
  224. }
  225. }
  226. override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
  227. return dialogView
  228. }
  229. override fun onClick(predefinedStatus: PredefinedStatus) {
  230. selectedPredefinedMessageId = predefinedStatus.id
  231. emoji.setText(predefinedStatus.icon)
  232. customStatusInput.text.clear()
  233. customStatusInput.text.append(predefinedStatus.message)
  234. }
  235. @VisibleForTesting
  236. fun setPredefinedStatus(predefinedStatus: ArrayList<PredefinedStatus>) {
  237. adapter.list = predefinedStatus
  238. predefinedStatusList.adapter?.notifyDataSetChanged()
  239. }
  240. }