diff --git a/app/src/main/java/com/nextcloud/talk/account/WebViewLoginActivity.kt b/app/src/main/java/com/nextcloud/talk/account/WebViewLoginActivity.kt index 7d98d59c6..85d124a95 100644 --- a/app/src/main/java/com/nextcloud/talk/account/WebViewLoginActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/account/WebViewLoginActivity.kt @@ -286,6 +286,7 @@ class WebViewLoginActivity : BaseActivity() { } } + @SuppressLint("DiscouragedPrivateApi") @Suppress("Detekt.TooGenericExceptionCaught") override fun onReceivedSslError(view: WebView, handler: SslErrorHandler, error: SslError) { try { diff --git a/app/src/main/java/com/nextcloud/talk/bottomsheet/items/ListIconDialogAdapter.kt b/app/src/main/java/com/nextcloud/talk/bottomsheet/items/ListIconDialogAdapter.kt index d0c9ef1d4..0d869e392 100644 --- a/app/src/main/java/com/nextcloud/talk/bottomsheet/items/ListIconDialogAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/bottomsheet/items/ListIconDialogAdapter.kt @@ -6,6 +6,7 @@ */ package com.nextcloud.talk.bottomsheet.items +import android.annotation.SuppressLint import android.view.View import android.view.ViewGroup import android.widget.ImageView @@ -65,6 +66,7 @@ internal class ListIconDialogAdapter( } } + @SuppressLint("RestrictedApi") override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListItemViewHolder { val listItemView: View = parent.inflate(dialog.windowContext, R.layout.menu_item_sheet) val viewHolder = ListItemViewHolder( diff --git a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt index 3f3289e10..bffb3136c 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt @@ -16,7 +16,6 @@ package com.nextcloud.talk.chat import android.Manifest import android.annotation.SuppressLint -import android.app.Activity import android.content.ClipData import android.content.ClipboardManager import android.content.Context @@ -363,6 +362,7 @@ class ChatActivity : var startCallFromRoomSwitch: Boolean = false var voiceOnly: Boolean = true + var focusInput: Boolean = false private lateinit var path: String var myFirstMessage: CharSequence? = null @@ -546,6 +546,8 @@ class ChatActivity : startCallFromRoomSwitch = extras?.getBoolean(KEY_START_CALL_AFTER_ROOM_SWITCH, false) == true voiceOnly = extras?.getBoolean(KEY_CALL_VOICE_ONLY, false) == true + + focusInput = extras?.getBoolean(BundleKeys.KEY_FOCUS_INPUT) == true } override fun onStart() { @@ -637,12 +639,17 @@ class ChatActivity : supportFragmentManager.commit { setReorderingAllowed(true) // optimizes out redundant replace operations replace(R.id.fragment_container_activity_chat, messageInputFragment) + runOnCommit { + if (focusInput) { + messageInputFragment.binding.fragmentMessageInputView.requestFocus() + } + } } joinRoomWithPassword() if (conversationUser?.userId != "?" && - CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.MENTION_FLAG) + hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.MENTION_FLAG) ) { binding.chatToolbar.setOnClickListener { _ -> showConversationInfoScreen() } } @@ -2046,7 +2053,7 @@ class ChatActivity : private fun shouldShowLobby(): Boolean { if (currentConversation != null) { - return CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.WEBINARY_LOBBY) && + return hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.WEBINARY_LOBBY) && currentConversation?.lobbyState == ConversationEnums.LobbyState.LOBBY_STATE_MODERATORS_ONLY && !ConversationUtils.canModerate(currentConversation!!, spreedCapabilities) && !participantPermissions.canIgnoreLobby() @@ -2302,7 +2309,7 @@ class ChatActivity : } private fun executeIfResultOk(result: ActivityResult, onResult: (intent: Intent?) -> Unit) { - if (result.resultCode == Activity.RESULT_OK) { + if (result.resultCode == RESULT_OK) { onResult(result.data) } else { Log.e(TAG, "resultCode for received intent was != ok") @@ -2796,7 +2803,7 @@ class ChatActivity : } if (this::spreedCapabilities.isInitialized) { - if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.MESSAGE_EXPIRATION)) { + if (hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.MESSAGE_EXPIRATION)) { deleteExpiredMessages() } } else { @@ -3064,7 +3071,7 @@ class ChatActivity : super.onPrepareOptionsMenu(menu) if (this::spreedCapabilities.isInitialized) { - if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.READ_ONLY_ROOMS)) { + if (hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.READ_ONLY_ROOMS)) { checkShowCallButtons() } @@ -3085,7 +3092,7 @@ class ChatActivity : }.collect() } - if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.SILENT_CALL)) { + if (hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.SILENT_CALL)) { Handler().post { findViewById(R.id.conversation_voice_call)?.setOnLongClickListener { showCallButtonMenu(true) @@ -3144,6 +3151,7 @@ class ChatActivity : else -> super.onOptionsItemSelected(item) } + @SuppressLint("InflateParams") private fun showPopupWindow(anchorView: View) { val popupView = layoutInflater.inflate(R.layout.item_event_schedule, null) @@ -3595,7 +3603,7 @@ class ChatActivity : fun copyMessage(message: IMessage?) { val clipboardManager = - getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + getSystemService(CLIPBOARD_SERVICE) as ClipboardManager val clipData = ClipData.newPlainText( resources?.getString(R.string.nc_app_product_name), message?.text @@ -3909,7 +3917,7 @@ class ChatActivity : val isOlderThanSixHours = message .createdAt .before(Date(System.currentTimeMillis() - AGE_THRESHOLD_FOR_DELETE_MESSAGE)) - val hasDeleteMessagesUnlimitedCapability = CapabilitiesUtil.hasSpreedFeatureCapability( + val hasDeleteMessagesUnlimitedCapability = hasSpreedFeatureCapability( spreedCapabilities, SpreedFeatures.DELETE_MESSAGES_UNLIMITED ) @@ -3919,7 +3927,7 @@ class ChatActivity : !hasDeleteMessagesUnlimitedCapability && isOlderThanSixHours -> false message.systemMessageType != ChatMessage.SystemMessageType.DUMMY -> false message.isDeleted -> false - !CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.DELETE_MESSAGES) -> false + !hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.DELETE_MESSAGES) -> false !participantPermissions.hasChatPermission() -> false hasDeleteMessagesUnlimitedCapability -> true else -> true diff --git a/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt index 1086d9d8e..7ca3d8f4e 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt @@ -16,7 +16,6 @@ import android.animation.AnimatorInflater import android.annotation.SuppressLint import android.app.SearchManager import android.content.ActivityNotFoundException -import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.graphics.drawable.Drawable @@ -41,6 +40,9 @@ import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.SearchView import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.platform.ViewCompositionStrategy +import androidx.core.content.pm.ShortcutInfoCompat +import androidx.core.content.pm.ShortcutManagerCompat +import androidx.core.graphics.drawable.IconCompat import androidx.core.graphics.drawable.toDrawable import androidx.core.net.toUri import androidx.core.os.bundleOf @@ -409,6 +411,10 @@ class ConversationsListActivity : conversationsListViewModel.getRoomsFlow .onEach { list -> setConversationList(list) + val noteToSelf = list + .firstOrNull { ConversationUtils.isNoteToSelfConversation(it) } + val isNoteToSelfAvailable = noteToSelf != null + handleNoteToSelfShortcut(isNoteToSelfAvailable, noteToSelf?.token ?: "") }.collect() } @@ -525,6 +531,30 @@ class ConversationsListActivity : } } + private fun handleNoteToSelfShortcut(noteToSelfAvailable: Boolean, noteToSelfToken: String) { + // Redundant operations just update the existing id + if (noteToSelfAvailable) { + val bundle = Bundle() + bundle.putString(KEY_ROOM_TOKEN, noteToSelfToken) + bundle.putBoolean(BundleKeys.KEY_FOCUS_INPUT, true) + val intent = Intent(context, ChatActivity::class.java) + intent.putExtras(bundle) + intent.action = Intent.ACTION_VIEW + val openNotesString = resources.getString(R.string.open_notes) + + val shortcut = ShortcutInfoCompat.Builder(context, NOTE_TO_SELF_SHORTCUT_ID) + .setShortLabel(openNotesString) + .setLongLabel(openNotesString) + .setIcon(IconCompat.createWithResource(context, R.drawable.ic_pencil_grey600_24dp)) + .setIntent(intent) + .build() + + ShortcutManagerCompat.pushDynamicShortcut(context, shortcut) + } else { + ShortcutManagerCompat.removeDynamicShortcuts(context, listOf(NOTE_TO_SELF_SHORTCUT_ID)) + } + } + private fun setConversationList(list: List) { // Update Conversations conversationItems.clear() @@ -640,7 +670,7 @@ class ConversationsListActivity : } } - val archiveFilterOn = filterState[ARCHIVE] ?: false + val archiveFilterOn = filterState[ARCHIVE] == true if (archiveFilterOn && newItems.isEmpty()) { binding.noArchivedConversationLayout.visibility = View.VISIBLE } else { @@ -755,7 +785,7 @@ class ConversationsListActivity : } private fun initSearchView() { - val searchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager? + val searchManager = getSystemService(SEARCH_SERVICE) as SearchManager? if (searchItem != null) { searchView = MenuItemCompat.getActionView(searchItem) as SearchView viewThemeUtils.talk.themeSearchView(searchView!!) @@ -1217,7 +1247,7 @@ class ConversationsListActivity : }) binding.recyclerView.setOnTouchListener { v: View, _: MotionEvent? -> if (!isDestroyed) { - val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager imm.hideSoftInputFromWindow(v.windowToken, 0) } false @@ -1398,7 +1428,7 @@ class ConversationsListActivity : adapter?.updateDataSet(conversationItems) adapter?.setFilter("") adapter?.filterItems() - val archiveFilterOn = filterState[ARCHIVE] ?: false + val archiveFilterOn = filterState[ARCHIVE] == true if (archiveFilterOn && adapter!!.isEmpty) { binding.noArchivedConversationLayout.visibility = View.VISIBLE } else { @@ -1809,7 +1839,7 @@ class ConversationsListActivity : val callsChannelNotEnabled = !NotificationUtils.isCallsNotificationChannelEnabled(this) val serverNotificationAppInstalled = - currentUser?.capabilities?.notificationsCapability?.features?.isNotEmpty() ?: false + currentUser?.capabilities?.notificationsCapability?.features?.isNotEmpty() == true val settingsOfUserAreWrong = notificationPermissionNotGranted || batteryOptimizationNotIgnored || @@ -2183,5 +2213,6 @@ class ConversationsListActivity : const val ROOM_TYPE_ONE_ONE = "1" private const val SIXTEEN_HOURS_IN_SECONDS: Long = 57600 const val LONG_1000: Long = 1000 + private const val NOTE_TO_SELF_SHORTCUT_ID = "NOTE_TO_SELF_SHORTCUT_ID" } } diff --git a/app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenMediaActivity.kt b/app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenMediaActivity.kt index fb252375c..4cb14f4d4 100644 --- a/app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenMediaActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenMediaActivity.kt @@ -190,6 +190,7 @@ class FullScreenMediaActivity : AppCompatActivity() { supportActionBar?.show() } + @OptIn(UnstableApi::class) private fun applyWindowInsets() { val playerView = binding.playerView val exoControls = playerView.findViewById(R.id.exo_bottom_bar) diff --git a/app/src/main/java/com/nextcloud/talk/receivers/DirectReplyReceiver.kt b/app/src/main/java/com/nextcloud/talk/receivers/DirectReplyReceiver.kt index fb8842433..8612dbebc 100644 --- a/app/src/main/java/com/nextcloud/talk/receivers/DirectReplyReceiver.kt +++ b/app/src/main/java/com/nextcloud/talk/receivers/DirectReplyReceiver.kt @@ -6,14 +6,17 @@ */ package com.nextcloud.talk.receivers +import android.Manifest import android.app.Notification import android.app.NotificationManager import android.content.BroadcastReceiver import android.content.Context import android.content.Intent +import android.content.pm.PackageManager import android.text.SpannableStringBuilder import android.text.style.ForegroundColorSpan import android.util.Log +import androidx.core.app.ActivityCompat import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.core.app.Person @@ -162,9 +165,15 @@ class DirectReplyReceiver : BroadcastReceiver() { // Set the updated style previousBuilder.setStyle(previousStyle) - // Check if notification still exists - if (findActiveNotification(systemNotificationId!!) != null) { - NotificationManagerCompat.from(context).notify(systemNotificationId!!, previousBuilder.build()) + if (ActivityCompat.checkSelfPermission( + context, + Manifest.permission.POST_NOTIFICATIONS + ) == PackageManager.PERMISSION_GRANTED + ) { + // Check if notification still exists + if (findActiveNotification(systemNotificationId!!) != null) { + NotificationManagerCompat.from(context).notify(systemNotificationId!!, previousBuilder.build()) + } } } .subscribeOn(Schedulers.io()) diff --git a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt index 4080f315a..1d4d61023 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt @@ -81,4 +81,5 @@ object BundleKeys { const val KEY_FIELD_MAP: String = "KEY_FIELD_MAP" const val KEY_CHAT_URL: String = "KEY_CHAT_URL" const val KEY_SCROLL_TO_NOTIFICATION_CATEGORY: String = "KEY_SCROLL_TO_NOTIFICATION_CATEGORY" + const val KEY_FOCUS_INPUT: String = "KEY_FOCUS_INPUT" } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 887a4849f..77b035605 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -521,6 +521,10 @@ How to translate with transifex: Reply Reply privately Delete + + This conversation will be automatically deleted for everyone in %1$d day of no activity + This conversation will be automatically deleted for everyone in %1$d days of no activity + This conversation will be automatically deleted for everyone in %1$d days of no activity Delete now Keep @@ -860,4 +864,5 @@ How to translate with transifex: Unarchived %1$s Conversation is archived Local time: %1$s + Open Notes