Merge pull request #5032 from nextcloud/issue-5016-app-shortcuts

Adding a short cut to note-to-self
This commit is contained in:
Marcel Hibbe 2025-06-13 17:24:48 +00:00 committed by GitHub
commit 20b70c2728
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 76 additions and 19 deletions

View File

@ -286,6 +286,7 @@ class WebViewLoginActivity : BaseActivity() {
}
}
@SuppressLint("DiscouragedPrivateApi")
@Suppress("Detekt.TooGenericExceptionCaught")
override fun onReceivedSslError(view: WebView, handler: SslErrorHandler, error: SslError) {
try {

View File

@ -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<IT : ListItemWithImage>(
}
}
@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(

View File

@ -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<View?>(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

View File

@ -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,29 @@ class ConversationsListActivity :
}
}
private fun handleNoteToSelfShortcut(noteToSelfAvailable: Boolean, noteToSelfToken: String) {
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<ConversationModel>) {
// Update Conversations
conversationItems.clear()
@ -640,7 +669,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 +784,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 +1246,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 +1427,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 +1838,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 +2212,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"
}
}

View File

@ -190,6 +190,7 @@ class FullScreenMediaActivity : AppCompatActivity() {
supportActionBar?.show()
}
@OptIn(UnstableApi::class)
private fun applyWindowInsets() {
val playerView = binding.playerView
val exoControls = playerView.findViewById<FrameLayout>(R.id.exo_bottom_bar)

View File

@ -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())

View File

@ -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"
}

View File

@ -521,6 +521,10 @@ How to translate with transifex:
<string name="nc_reply">Reply</string>
<string name="nc_reply_privately">Reply privately</string>
<string name="nc_delete_message">Delete</string>
<plurals name="nc_conversation_auto_delete_notice">
<item quantity="one">This conversation will be automatically deleted for everyone in %1$d day of no activity</item>
<item quantity="other">This conversation will be automatically deleted for everyone in %1$d days of no activity</item>
</plurals>
<string name="nc_conversation_auto_delete_notice">This conversation will be automatically deleted for everyone in %1$d days of no activity</string>
<string name="nc_delete_now">Delete now</string>
<string name="nc_keep">Keep</string>
@ -860,4 +864,5 @@ How to translate with transifex:
<string name="unarchived_conversation">Unarchived %1$s</string>
<string name="conversation_archived">Conversation is archived</string>
<string name="local_time">Local time: %1$s</string>
<string name="open_notes">Open Notes</string>
</resources>