Skip to content
Snippets Groups Projects
Verified Commit 3fca452b authored by Janne Mareike Koschinski's avatar Janne Mareike Koschinski
Browse files

Implement attachments

parent 2eca6ee2
Branches attachments
No related tags found
No related merge requests found
Pipeline #411 canceled
Showing
with 1038 additions and 100 deletions
......@@ -137,6 +137,7 @@ dependencies {
implementation("commons-codec", "commons-codec", "1.11")
implementation("com.squareup.retrofit2", "retrofit", "2.5.0")
implementation("com.squareup.retrofit2", "converter-gson", "2.5.0")
implementation("com.squareup.retrofit2", "adapter-rxjava2", "2.5.0")
withVersion("10.0.0") {
implementation("com.jakewharton", "butterknife", version)
kapt("com.jakewharton", "butterknife-compiler", version)
......
......@@ -39,7 +39,8 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.SplashTheme">
android:theme="@style/Theme.SplashTheme"
android:usesCleartextTraffic="true">
<meta-data
android:name="WindowManagerPreference:FreeformWindowSize"
......@@ -112,6 +113,18 @@
android:exported="false"
android:label="@string/label_info_certificate"
android:windowSoftInputMode="adjustResize" />
<activity
android:name="de.kuschku.quasseldroid.ui.info.message.MessageInfoActivity"
android:exported="true"
android:label="@string/label_info_message"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Core Settings -->
<activity
......
......@@ -77,6 +77,8 @@ import de.kuschku.quasseldroid.ui.info.channellist.ChannelListActivity
import de.kuschku.quasseldroid.ui.info.channellist.ChannelListFragmentProvider
import de.kuschku.quasseldroid.ui.info.core.CoreInfoActivity
import de.kuschku.quasseldroid.ui.info.core.CoreInfoFragmentProvider
import de.kuschku.quasseldroid.ui.info.message.MessageInfoActivity
import de.kuschku.quasseldroid.ui.info.message.MessageInfoFragmentProvider
import de.kuschku.quasseldroid.ui.info.topic.TopicActivity
import de.kuschku.quasseldroid.ui.info.user.UserInfoActivity
import de.kuschku.quasseldroid.ui.info.user.UserInfoFragmentProvider
......@@ -123,6 +125,10 @@ abstract class ActivityModule {
@ContributesAndroidInjector(modules = [CertificateInfoFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class])
abstract fun bindCertificateInfoActivity(): CertificateInfoActivity
@ActivityScope
@ContributesAndroidInjector(modules = [MessageInfoFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class])
abstract fun bindMessageInfoActivity(): MessageInfoActivity
// Client Settings
@ActivityScope
......
......@@ -31,9 +31,11 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import butterknife.BindView
import butterknife.ButterKnife
import com.bumptech.glide.load.engine.DiskCacheStrategy
import de.kuschku.libquassel.protocol.Message_Flag
import de.kuschku.libquassel.protocol.Message_Type
import de.kuschku.libquassel.util.flag.hasFlag
import de.kuschku.quasseldroid.GlideApp
import de.kuschku.quasseldroid.R
import de.kuschku.quasseldroid.persistence.models.MessageData
import de.kuschku.quasseldroid.settings.MessageSettings
......@@ -42,6 +44,7 @@ import de.kuschku.quasseldroid.util.helper.loadAvatars
import de.kuschku.quasseldroid.util.helper.visibleIf
import de.kuschku.quasseldroid.util.ui.BetterLinkMovementMethod
import de.kuschku.quasseldroid.util.ui.DoubleClickHelper
import de.kuschku.quasseldroid.util.ui.view.MessageAttachmentView
import de.kuschku.quasseldroid.viewmodel.data.FormattedMessage
import javax.inject.Inject
......@@ -225,6 +228,10 @@ class MessageAdapter @Inject constructor(
@JvmField
var combined: TextView? = null
@BindView(R.id.attachment)
@JvmField
var attachment: MessageAttachmentView? = null
private var message: FormattedMessage? = null
private var original: MessageData? = null
......@@ -278,6 +285,46 @@ class MessageAdapter @Inject constructor(
content?.text = message.content
combined?.text = message.combined
attachment?.visibleIf(message.attachment != null)
attachment?.post {
attachment?.apply {
val attachment = message.attachment
reinitViews()
if (attachment != null) {
setLink(attachment.fromUrl)
setColor(attachment.color)
GlideApp.with(itemView)
.load(attachment.authorIcon)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(authorIconTarget)
setAuthor(attachment.authorName)
setTitle(attachment.title)
setDescription(attachment.text)
if (false) {
GlideApp.with(itemView)
.clear(thumbnailTarget)
GlideApp.with(itemView)
.load(attachment.imageUrl)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(previewTarget)
} else {
GlideApp.with(itemView)
.clear(previewTarget)
GlideApp.with(itemView)
.load(attachment.imageUrl)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(thumbnailTarget)
}
GlideApp.with(itemView)
.load(attachment.serviceIcon)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(serviceIconTarget)
setService(attachment.serviceName)
}
}
}
this.messageContainer?.isSelected = message.isSelected
if (hasDayChange) daychange?.text = message.dayChange
......
......@@ -65,6 +65,7 @@ import de.kuschku.quasseldroid.settings.AutoCompleteSettings
import de.kuschku.quasseldroid.settings.BacklogSettings
import de.kuschku.quasseldroid.settings.MessageSettings
import de.kuschku.quasseldroid.ui.chat.ChatActivity
import de.kuschku.quasseldroid.ui.info.message.MessageInfoActivity
import de.kuschku.quasseldroid.ui.info.user.UserInfoActivity
import de.kuschku.quasseldroid.util.Patterns
import de.kuschku.quasseldroid.util.avatars.AvatarHelper
......@@ -122,6 +123,15 @@ class MessageListFragment : ServiceBoundFragment() {
private val actionModeCallback = object : ActionMode.Callback {
override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?) = when (item?.itemId) {
R.id.action_message_info -> {
viewModel.selectedMessages.value.values.firstOrNull()?.let { msg ->
MessageInfoActivity.launch(
requireContext(),
messageId = msg.original.messageId
)
true
} ?: false
}
R.id.action_user_info -> {
viewModel.selectedMessages.value.values.firstOrNull()?.let { msg ->
viewModel.session.value?.orNull()?.bufferSyncer?.let { bufferSyncer ->
......@@ -250,8 +260,14 @@ class MessageListFragment : ServiceBoundFragment() {
if (actionMode != null) {
when (viewModel.selectedMessagesToggle(msg.original.messageId, msg)) {
0 -> actionMode?.finish()
1 -> actionMode?.menu?.findItem(R.id.action_user_info)?.isVisible = true
else -> actionMode?.menu?.findItem(R.id.action_user_info)?.isVisible = false
1 -> {
actionMode?.menu?.findItem(R.id.action_user_info)?.isVisible = true
//actionMode?.menu?.findItem(R.id.action_message_info)?.isVisible = true
}
else -> {
actionMode?.menu?.findItem(R.id.action_user_info)?.isVisible = false
//actionMode?.menu?.findItem(R.id.action_message_info)?.isVisible = false
}
}
} else if (msg.hasSpoilers) {
val value = viewModel.expandedMessages.value
......@@ -267,8 +283,14 @@ class MessageListFragment : ServiceBoundFragment() {
}
when (viewModel.selectedMessagesToggle(msg.original.messageId, msg)) {
0 -> actionMode?.finish()
1 -> actionMode?.menu?.findItem(R.id.action_user_info)?.isVisible = true
else -> actionMode?.menu?.findItem(R.id.action_user_info)?.isVisible = false
1 -> {
actionMode?.menu?.findItem(R.id.action_user_info)?.isVisible = true
actionMode?.menu?.findItem(R.id.action_message_info)?.isVisible = true
}
else -> {
actionMode?.menu?.findItem(R.id.action_user_info)?.isVisible = false
actionMode?.menu?.findItem(R.id.action_message_info)?.isVisible = false
}
}
}
if (autoCompleteSettings.senderDoubleClick)
......
......@@ -30,6 +30,8 @@ import android.view.Gravity
import android.view.View
import android.widget.FrameLayout
import android.widget.LinearLayout
import com.google.gson.GsonBuilder
import com.google.gson.JsonSyntaxException
import de.kuschku.libquassel.protocol.Message.MessageType.*
import de.kuschku.libquassel.protocol.Message_Flag
import de.kuschku.libquassel.protocol.Message_Type
......@@ -40,7 +42,9 @@ import de.kuschku.quasseldroid.R
import de.kuschku.quasseldroid.persistence.models.MessageData
import de.kuschku.quasseldroid.settings.MessageSettings
import de.kuschku.quasseldroid.util.ColorContext
import de.kuschku.quasseldroid.util.attachment.AttachmentData
import de.kuschku.quasseldroid.util.avatars.AvatarHelper
import de.kuschku.quasseldroid.util.helper.fromJson
import de.kuschku.quasseldroid.util.helper.styledAttributes
import de.kuschku.quasseldroid.util.helper.visibleIf
import de.kuschku.quasseldroid.util.irc.format.ContentFormatter
......@@ -103,6 +107,8 @@ class QuasselMessageRenderer @Inject constructor(
private val zoneId = ZoneId.systemDefault()
private val gson = GsonBuilder().setLenient().create()
override fun layout(type: Message_Type?,
hasHighlight: Boolean,
isFollowUp: Boolean,
......@@ -244,6 +250,13 @@ class QuasselMessageRenderer @Inject constructor(
val self = message.content.flag.hasFlag(Message_Flag.Self)
val highlight = message.content.flag.hasFlag(Message_Flag.Highlight)
val monochromeForeground = highlight && monochromeHighlights
val parsedAttachment: AttachmentData? = try {
gson.fromJson<AttachmentData>(message.content.attachments)
} catch (ignored: JsonSyntaxException) {
null
}
return when (message.content.type.enabledValues().firstOrNull()) {
Message_Type.Plain -> {
val realName = ircFormatDeserializer.formatString(message.content.realName,
......@@ -292,7 +305,8 @@ class QuasselMessageRenderer @Inject constructor(
isMarkerLine = message.isMarkerLine,
isExpanded = message.isExpanded,
isSelected = message.isSelected,
hasSpoilers = hasSpoilers
hasSpoilers = hasSpoilers,
attachment = parsedAttachment
)
}
Message_Type.Action -> {
......@@ -330,7 +344,8 @@ class QuasselMessageRenderer @Inject constructor(
isMarkerLine = message.isMarkerLine,
isExpanded = message.isExpanded,
isSelected = message.isSelected,
hasSpoilers = hasSpoilers
hasSpoilers = hasSpoilers,
attachment = parsedAttachment
)
}
Message_Type.Notice -> {
......@@ -352,7 +367,8 @@ class QuasselMessageRenderer @Inject constructor(
isMarkerLine = message.isMarkerLine,
isExpanded = message.isExpanded,
isSelected = message.isSelected,
hasSpoilers = hasSpoilers
hasSpoilers = hasSpoilers,
attachment = parsedAttachment
)
}
Message_Type.Nick -> {
......@@ -395,7 +411,8 @@ class QuasselMessageRenderer @Inject constructor(
isMarkerLine = message.isMarkerLine,
isExpanded = message.isExpanded,
isSelected = message.isSelected,
hasSpoilers = false
hasSpoilers = false,
attachment = parsedAttachment
)
}
Message_Type.Mode -> FormattedMessage(
......@@ -412,7 +429,8 @@ class QuasselMessageRenderer @Inject constructor(
isMarkerLine = message.isMarkerLine,
isExpanded = message.isExpanded,
isSelected = message.isSelected,
hasSpoilers = false
hasSpoilers = false,
attachment = parsedAttachment
)
Message_Type.Join -> FormattedMessage(
original = message.content,
......@@ -433,7 +451,8 @@ class QuasselMessageRenderer @Inject constructor(
isMarkerLine = message.isMarkerLine,
isExpanded = message.isExpanded,
isSelected = message.isSelected,
hasSpoilers = false
hasSpoilers = false,
attachment = parsedAttachment
)
Message_Type.Part -> {
val (content, hasSpoilers) = if (message.content.content.isBlank()) {
......@@ -480,7 +499,8 @@ class QuasselMessageRenderer @Inject constructor(
isMarkerLine = message.isMarkerLine,
isExpanded = message.isExpanded,
isSelected = message.isSelected,
hasSpoilers = hasSpoilers
hasSpoilers = hasSpoilers,
attachment = parsedAttachment
)
}
Message_Type.Quit -> {
......@@ -528,7 +548,8 @@ class QuasselMessageRenderer @Inject constructor(
isMarkerLine = message.isMarkerLine,
isExpanded = message.isExpanded,
isSelected = message.isSelected,
hasSpoilers = hasSpoilers
hasSpoilers = hasSpoilers,
attachment = parsedAttachment
)
}
Message_Type.Kick -> {
......@@ -575,7 +596,8 @@ class QuasselMessageRenderer @Inject constructor(
isMarkerLine = message.isMarkerLine,
isExpanded = message.isExpanded,
isSelected = message.isSelected,
hasSpoilers = hasSpoilers
hasSpoilers = hasSpoilers,
attachment = parsedAttachment
)
}
Message_Type.Kill -> {
......@@ -622,7 +644,8 @@ class QuasselMessageRenderer @Inject constructor(
isMarkerLine = message.isMarkerLine,
isExpanded = message.isExpanded,
isSelected = message.isSelected,
hasSpoilers = hasSpoilers
hasSpoilers = hasSpoilers,
attachment = parsedAttachment
)
}
Message_Type.NetsplitJoin -> {
......@@ -649,7 +672,8 @@ class QuasselMessageRenderer @Inject constructor(
isMarkerLine = message.isMarkerLine,
isExpanded = message.isExpanded,
isSelected = message.isSelected,
hasSpoilers = false
hasSpoilers = false,
attachment = parsedAttachment
)
}
Message_Type.NetsplitQuit -> {
......@@ -676,7 +700,8 @@ class QuasselMessageRenderer @Inject constructor(
isMarkerLine = message.isMarkerLine,
isExpanded = message.isExpanded,
isSelected = message.isSelected,
hasSpoilers = false
hasSpoilers = false,
attachment = parsedAttachment
)
}
Message_Type.Server,
......@@ -695,7 +720,8 @@ class QuasselMessageRenderer @Inject constructor(
isMarkerLine = message.isMarkerLine,
isExpanded = message.isExpanded,
isSelected = message.isSelected,
hasSpoilers = hasSpoilers
hasSpoilers = hasSpoilers,
attachment = parsedAttachment
)
}
Message_Type.Topic -> {
......@@ -712,7 +738,8 @@ class QuasselMessageRenderer @Inject constructor(
isMarkerLine = message.isMarkerLine,
isExpanded = message.isExpanded,
isSelected = message.isSelected,
hasSpoilers = hasSpoilers
hasSpoilers = hasSpoilers,
attachment = parsedAttachment
)
}
Message_Type.DayChange -> FormattedMessage(
......@@ -724,7 +751,8 @@ class QuasselMessageRenderer @Inject constructor(
isMarkerLine = false,
isExpanded = false,
isSelected = false,
hasSpoilers = false
hasSpoilers = false,
attachment = parsedAttachment
)
Message_Type.Invite -> {
val (content, hasSpoilers) = contentFormatter.formatContent(message.content.content,
......@@ -740,7 +768,8 @@ class QuasselMessageRenderer @Inject constructor(
isMarkerLine = message.isMarkerLine,
isExpanded = message.isExpanded,
isSelected = message.isSelected,
hasSpoilers = hasSpoilers
hasSpoilers = hasSpoilers,
attachment = parsedAttachment
)
}
else -> FormattedMessage(
......@@ -763,13 +792,15 @@ class QuasselMessageRenderer @Inject constructor(
isMarkerLine = message.isMarkerLine,
isExpanded = message.isExpanded,
isSelected = message.isSelected,
hasSpoilers = false
hasSpoilers = false,
attachment = parsedAttachment
)
}
}
private fun formatDayChange(
message: DisplayMessage) =
if (message.hasDayChange) dateFormatter.format(message.content.time.atZone(zoneId).truncatedTo(
ChronoUnit.DAYS)) else null
private fun formatDayChange(message: DisplayMessage) =
if (message.hasDayChange)
dateFormatter.format(message.content.time.atZone(zoneId).truncatedTo(ChronoUnit.DAYS))
else
null
}
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2019 Janne Koschinski
* Copyright (c) 2019 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.ui.info.message
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import butterknife.BindView
import butterknife.ButterKnife
import com.bumptech.glide.load.engine.DiskCacheStrategy
import de.kuschku.quasseldroid.GlideApp
import de.kuschku.quasseldroid.R
import de.kuschku.quasseldroid.util.attachment.AttachmentData
import de.kuschku.quasseldroid.util.ui.view.MessageAttachmentView
class MessageAttachmentAdapter(private val showLarge: Boolean) :
ListAdapter<AttachmentData, MessageAttachmentAdapter.MessageAttachmentViewHolder>(
object : DiffUtil.ItemCallback<AttachmentData>() {
override fun areItemsTheSame(oldItem: AttachmentData, newItem: AttachmentData) =
oldItem.fromUrl == newItem.fromUrl
override fun areContentsTheSame(oldItem: AttachmentData, newItem: AttachmentData) =
oldItem == newItem
}
) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MessageAttachmentViewHolder {
return MessageAttachmentViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.widget_message_attachment_item, parent, false),
showLarge
)
}
override fun onBindViewHolder(holder: MessageAttachmentViewHolder, position: Int) {
holder.bind(getItem(position))
}
class MessageAttachmentViewHolder(itemView: View, private val showLarge: Boolean) :
RecyclerView.ViewHolder(itemView) {
@BindView(R.id.view)
lateinit var attachmentView: MessageAttachmentView
init {
ButterKnife.bind(this, itemView)
}
fun bind(attachment: AttachmentData) {
attachmentView.reinitViews()
attachmentView.setLink(attachment.fromUrl)
attachmentView.setColor(attachment.color)
GlideApp.with(itemView)
.load(attachment.authorIcon)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(attachmentView.authorIconTarget)
attachmentView.setAuthor(attachment.authorName)
//attachmentView.setAuthorLink(attachment.author_link)
attachmentView.setTitle(attachment.title)
attachmentView.setDescription(attachment.text)
if (showLarge) {
GlideApp.with(itemView)
.clear(attachmentView.thumbnailTarget)
GlideApp.with(itemView)
.load(attachment.imageUrl)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(attachmentView.previewTarget)
} else {
GlideApp.with(itemView)
.clear(attachmentView.previewTarget)
GlideApp.with(itemView)
.load(attachment.imageUrl)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(attachmentView.thumbnailTarget)
}
GlideApp.with(itemView)
.load(attachment.serviceIcon)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(attachmentView.serviceIconTarget)
attachmentView.setService(attachment.serviceName)
}
}
}
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2019 Janne Koschinski
* Copyright (c) 2019 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.ui.info.message
import android.content.Context
import android.content.Intent
import de.kuschku.libquassel.protocol.MsgId
import de.kuschku.quasseldroid.util.ui.settings.ServiceBoundSettingsActivity
class MessageInfoActivity : ServiceBoundSettingsActivity(MessageInfoFragment()) {
companion object {
fun launch(
context: Context,
messageId: MsgId
) = context.startActivity(intent(context, messageId))
fun intent(
context: Context,
messageId: MsgId
) = Intent(context, MessageInfoActivity::class.java).apply {
putExtra("messageId", messageId.id)
}
}
}
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2019 Janne Koschinski
* Copyright (c) 2019 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.ui.info.message
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import butterknife.BindView
import butterknife.ButterKnife
import com.google.gson.Gson
import de.kuschku.libquassel.protocol.MsgId
import de.kuschku.libquassel.util.Optional
import de.kuschku.quasseldroid.R
import de.kuschku.quasseldroid.persistence.dao.find
import de.kuschku.quasseldroid.persistence.db.QuasselDatabase
import de.kuschku.quasseldroid.util.attachment.AttachmentData
import de.kuschku.quasseldroid.util.attachments.AttachmentApi
import de.kuschku.quasseldroid.util.helper.combineLatest
import de.kuschku.quasseldroid.util.helper.toLiveData
import de.kuschku.quasseldroid.util.service.ServiceBoundFragment
import de.kuschku.quasseldroid.util.ui.BetterLinkMovementMethod
import de.kuschku.quasseldroid.util.ui.LinkLongClickMenuHelper
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import javax.inject.Inject
class MessageInfoFragment : ServiceBoundFragment() {
@BindView(R.id.list)
lateinit var list: RecyclerView
@Inject
lateinit var database: QuasselDatabase
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.info_message, container, false)
ButterKnife.bind(this, view)
val adapter = MessageAttachmentAdapter(true)
list.adapter = adapter
list.layoutManager = LinearLayoutManager(list.context)
list.itemAnimator = DefaultItemAnimator()
viewModel.session.toLiveData().observe(this, Observer {
runInBackground {
val movementMethod = BetterLinkMovementMethod.newInstance()
movementMethod.setOnLinkLongClickListener(LinkLongClickMenuHelper())
val messageId = MsgId(arguments?.getLong("messageId") ?: -1)
val message = database.message().find(messageId)
val retrofit = Retrofit.Builder()
.baseUrl("http://192.168.178.29:8080/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
val api = retrofit.create(AttachmentApi::class.java)
val gson = Gson()
val ircCloudEmbeds = listOf(
"{\"ts\":1448400805,\"title_link\":\"https://medium.com/slack-developer-blog/everything-you-ever-wanted-to-know-about-unfurling-but-were-afraid-to-ask-or-how-to-make-your-e64b4bb9254\",\"title\":\"Everything you ever wanted to know about unfurling but were afraid to ask /or/ How to make your…\",\"text\":\"Let’s start with the most obvious question first. This is what an “unfurl” is:\",\"service_name\":\"Medium\",\"service_icon\":\"https://cdn-images-1.medium.com/fit/c/304/304/1*a1O3xhOq8KWSibZF6Ze5xQ.png\",\"image_width\":170,\"image_url\":\"https://cdn-images-1.medium.com/max/1200/1*QOMaDLcO8rExD0ctBV3BWg.png\",\"image_height\":250,\"image_bytes\":695475,\"from_url\":\"https://medium.com/slack-developer-blog/everything-you-ever-wanted-to-know-about-unfurling-but-were-afraid-to-ask-or-how-to-make-your-e64b4bb9254\",\"fields\":[{\"value\":\"10 min read\",\"title\":\"Reading time\",\"short\":true}]}",
"{\"title_link\":\"https://www.youtube.com/watch?v=_Cd9FYO9Rh4\",\"title\":\"Scorpions Berlin Philharmonic Orchestra Rock You Like a Hurricane\",\"service_url\":\"https://www.youtube.com/\",\"service_name\":\"YouTube\",\"service_icon\":\"https://a.slack-edge.com/2089/img/unfurl_icons/youtube.png\",\"from_url\":\"https://www.youtube.com/watch?v=_Cd9FYO9Rh4\",\"author_name\":\"Nathan Allen\",\"author_link\":\"https://www.youtube.com/user/27caboose\"}",
"{\"title_link\":\"http://www.kn-online.de/Nachrichten/Panorama/Lehrerin-fuehrt-Netflix-Experiment-durch-das-Ergebnis-erschreckt\",\"title\":\"Lehrerin führt Netflix-Experiment durch – das Ergebnis erschreckt\",\"text\":\"Rebecca Schiller aus Potsdam unterrichtet am Marie-Curie-Gymnasium im Havelland. Als „Frau Lehrerin“ ist sie auf Twitter eine kleine Berühmtheit – vor allem, seit sie dort eine unkonventionelle Unterrichtsmethode veröffentlicht hat.\",\"service_name\":\"KN - Kieler Nachrichten\",\"service_icon\":\"http://www.kn-online.de/bundles/molasset/images/sites/desktop/kn/apple-touch-icon.png\",\"image_width\":500,\"image_url\":\"http://www.kn-online.de/var/storage/images/rnd/nachrichten/panorama/uebersicht/lehrerin-fuehrt-netflix-experiment-durch-das-ergebnis-erschreckt/712106427-8-ger-DE/Lehrerin-fuehrt-Netflix-Experiment-durch-das-Ergebnis-erschreckt_reference_2_1.jpg\",\"image_height\":250,\"image_bytes\":65450,\"from_url\":\"http://www.kn-online.de/Nachrichten/Panorama/Lehrerin-fuehrt-Netflix-Experiment-durch-das-Ergebnis-erschreckt\"}"
).map {
gson.fromJson(it, AttachmentData::class.java)
}
val urls = listOf(
"https://quasseldroid.info/",
"https://www.youtube.com/watch?v=IfXMN3VhikA",
"https://twitter.com/dw_politik/status/1092872739445104640",
"https://soundcloud.com/kevin-manthei/sto-ds9-main-title",
"https://twitter.com/raketenlurch/status/1093991675209416704",
"https://arxius.io/i/25287151",
"https://i.imgur.com/W8DjyWk.jpg",
"https://imgur.com/W8DjyWk",
"https://rp-online.de/panorama/deutschland/buchenbach-transporter-hindert-krankenwagen-mit-kind-an-bord-minutenlang-am-ueberholen_aid-35758071",
"http://m.kn-online.de/Nachrichten/Panorama/Lehrerin-fuehrt-Netflix-Experiment-durch-das-Ergebnis-erschreckt",
"https://media.ccc.de/v/35c3-9904-the_social_credit_system",
"https://medium.com/slack-developer-blog/everything-you-ever-wanted-to-know-about-unfurling-but-were-afraid-to-ask-or-how-to-make-your-e64b4bb9254"
)
activity?.runOnUiThread {
combineLatest(urls.map {
api.retrieve(it)
.map { Optional.of(it) }
.onErrorReturnItem(Optional.empty())
}).map {
it.mapNotNull(Optional<AttachmentData>::orNull)
}.toLiveData().observe(this, Observer {
adapter.submitList(ircCloudEmbeds + it)
})
}
}
})
return view
}
}
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2019 Janne Koschinski
* Copyright (c) 2019 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.ui.info.message
import androidx.fragment.app.FragmentActivity
import dagger.Binds
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module
abstract class MessageInfoFragmentProvider {
@Binds
abstract fun bindFragmentActivity(activity: MessageInfoActivity): FragmentActivity
@ContributesAndroidInjector
abstract fun bindMessageInfoFragment(): MessageInfoFragment
}
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2019 Janne Koschinski
* Copyright (c) 2019 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.util.attachments
import de.kuschku.quasseldroid.util.attachment.AttachmentData
import io.reactivex.Observable
import retrofit2.http.GET
import retrofit2.http.Query
interface AttachmentApi {
@GET("/")
fun retrieve(@Query("url") url: String): Observable<AttachmentData>
}
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2019 Janne Koschinski
* Copyright (c) 2019 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.util.ui.view
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.net.Uri
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.ColorInt
import butterknife.BindView
import butterknife.ButterKnife
import com.bumptech.glide.request.target.CustomViewTarget
import com.bumptech.glide.request.transition.Transition
import de.kuschku.quasseldroid.R
import de.kuschku.quasseldroid.util.helper.styledAttributes
import de.kuschku.quasseldroid.util.helper.visibleIf
class MessageAttachmentView : FrameLayout {
@BindView(R.id.attachment_color_bar)
lateinit var colorBar: View
@BindView(R.id.attachment_author_icon)
lateinit var authorIcon: ImageView
@BindView(R.id.attachment_author)
lateinit var author: TextView
@BindView(R.id.attachment_title)
lateinit var title: TextView
@BindView(R.id.attachment_description)
lateinit var description: TextView
@BindView(R.id.attachment_thumbnail)
lateinit var thumbnail: ImageView
@BindView(R.id.attachment_preview)
lateinit var preview: ImageView
@BindView(R.id.attachment_service_icon)
lateinit var serviceIcon: ImageView
@BindView(R.id.attachment_service)
lateinit var service: TextView
val authorIconTarget: VisibilitySettingDrawableImageViewTarget
val thumbnailTarget: VisibilitySettingDrawableImageViewTarget
val previewTarget: VisibilitySettingDrawableImageViewTarget
val serviceIconTarget: VisibilitySettingDrawableImageViewTarget
private var url: String? = null
private var authorUrl: String? = null
constructor(context: Context) :
this(context, null)
constructor(context: Context, attrs: AttributeSet?) :
this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) :
super(context, attrs, defStyleAttr) {
LayoutInflater.from(context).inflate(R.layout.widget_message_attachment, this, true)
ButterKnife.bind(this)
authorIconTarget = VisibilitySettingDrawableImageViewTarget(authorIcon)
thumbnailTarget = VisibilitySettingDrawableImageViewTarget(thumbnail)
previewTarget = VisibilitySettingDrawableImageViewTarget(preview)
serviceIconTarget = VisibilitySettingDrawableImageViewTarget(serviceIcon)
reinitViews()
}
fun reinitViews() {
setColor(0)
setAuthorIcon(null)
authorIcon.visibility = View.VISIBLE
setAuthor("")
setTitle("")
setDescription("")
setThumbnail(null)
thumbnail.visibility = View.VISIBLE
setPreview(null)
preview.visibility = View.VISIBLE
setServiceIcon(null)
serviceIcon.visibility = View.VISIBLE
setService("")
}
class VisibilitySettingDrawableImageViewTarget(view: ImageView) :
CustomViewTarget<ImageView, Drawable>(view) {
override fun onLoadFailed(errorDrawable: Drawable?) {
view.setImageDrawable(errorDrawable)
view.visibility = View.GONE
}
override fun onResourceCleared(placeholder: Drawable?) {
view.setImageDrawable(placeholder)
view.visibility = View.GONE
}
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
view.setImageDrawable(resource)
view.visibility = View.VISIBLE
}
}
private val colorForegroundSecondary = context.theme.styledAttributes(R.attr.colorForegroundSecondary) {
getColor(0, 0)
}
fun setColor(color: String?) {
setColor(try {
Color.parseColor(color)
} catch (ignored: Throwable) {
0
})
}
fun setColor(@ColorInt color: Int) {
if (color != 0) {
colorBar.setBackgroundColor(color)
} else {
colorBar.setBackgroundColor(colorForegroundSecondary)
}
}
fun setAuthorIcon(drawable: Drawable?) {
authorIcon.setImageDrawable(drawable)
}
fun setAuthor(text: String?) {
author.text = text
author.visibleIf(!text.isNullOrBlank())
}
fun setAuthorLink(url: String) {
if (url.isNotBlank()) {
author.setOnClickListener {
context?.startActivity(Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse(url)
})
}
} else {
author.setOnClickListener(null)
}
}
fun setTitle(text: String?) {
title.text = text
title.visibleIf(!text.isNullOrBlank())
}
fun setLink(url: String?) {
if (url.isNullOrBlank()) {
this.setOnClickListener(null)
} else {
this.setOnClickListener {
context?.startActivity(Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse(url)
})
}
}
}
fun setDescription(text: String?) {
description.text = text
description.visibleIf(!text.isNullOrBlank())
}
fun setThumbnail(drawable: Drawable?) {
thumbnail.setImageDrawable(drawable)
}
fun setPreview(drawable: Drawable?) {
preview.setImageDrawable(drawable)
}
fun setServiceIcon(drawable: Drawable?) {
serviceIcon.setImageDrawable(drawable)
}
fun setService(text: String?) {
service.text = text
service.visibleIf(!text.isNullOrBlank())
}
}
<?xml version="1.0" encoding="utf-8"?><!--
Quasseldroid - Quassel client for Android
Copyright (c) 2019 Janne Koschinski
Copyright (c) 2019 The Quassel Project
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License version 3 as published
by the Free Software Foundation.
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 <http://www.gnu.org/licenses/>.
-->
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
......@@ -70,6 +70,11 @@
tools:src="@tools:sample/avatars" />
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
......@@ -108,6 +113,12 @@
tools:textSize="11.9sp"
tools:visibility="visible" />
</LinearLayout>
<de.kuschku.quasseldroid.util.ui.view.MessageAttachmentView
android:id="@+id/attachment"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
......@@ -59,6 +59,16 @@
android:layout_marginRight="@dimen/message_horizontal"
android:visibility="gone" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<de.kuschku.quasseldroid.util.ui.view.RipplePassthroughTextView
android:id="@+id/combined"
style="@style/Widget.RtlConformTextView"
......@@ -83,5 +93,13 @@
tools:text="@sample/messages.json/data/time"
tools:textSize="11.9sp"
tools:visibility="visible" />
</LinearLayout>
<de.kuschku.quasseldroid.util.ui.view.MessageAttachmentView
android:id="@+id/attachment"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
......@@ -59,6 +59,16 @@
android:layout_marginRight="@dimen/message_horizontal"
android:visibility="gone" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<de.kuschku.quasseldroid.util.ui.view.RipplePassthroughTextView
android:id="@+id/combined"
style="@style/Widget.RtlConformTextView"
......@@ -82,5 +92,13 @@
tools:text="@sample/messages.json/data/time"
tools:textSize="11.9sp"
tools:visibility="visible" />
</LinearLayout>
<de.kuschku.quasseldroid.util.ui.view.MessageAttachmentView
android:id="@+id/attachment"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
......@@ -138,7 +138,6 @@
android:textColor="?attr/colorForeground"
tools:text="@sample/messages.json/data/message"
tools:visibility="gone" />
</LinearLayout>
<TextView
......@@ -156,6 +155,11 @@
tools:textSize="11.9sp"
tools:visibility="visible" />
</LinearLayout>
<de.kuschku.quasseldroid.util.ui.view.MessageAttachmentView
android:id="@+id/attachment"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
......
<?xml version="1.0" encoding="utf-8"?><!--
Quasseldroid - Quassel client for Android
Copyright (c) 2019 Janne Koschinski
Copyright (c) 2019 The Quassel Project
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License version 3 as published
by the Free Software Foundation.
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 <http://www.gnu.org/licenses/>.
-->
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:cardBackgroundColor="?colorBackgroundCard">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/attachment_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:orientation="horizontal">
<View
android:id="@+id/attachment_color_bar"
android:layout_width="4dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:background="@android:color/holo_red_dark" />
<ImageView
android:id="@+id/attachment_author_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:adjustViewBounds="true"
android:maxWidth="16dp"
android:maxHeight="16dp"
app:layout_constraintBottom_toBottomOf="@+id/attachment_author"
app:layout_constraintStart_toEndOf="@+id/attachment_color_bar"
app:layout_constraintTop_toTopOf="@+id/attachment_author"
tools:srcCompat="@tools:sample/avatars" />
<TextView
android:id="@+id/attachment_author"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:textColor="?colorTextSecondary"
app:layout_constraintEnd_toStartOf="@+id/attachment_thumbnail"
app:layout_constraintStart_toEndOf="@id/attachment_author_icon"
app:layout_constraintTop_toTopOf="parent"
tools:text="MrDomigruber" />
<TextView
android:id="@+id/attachment_title"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:textColor="?colorTextPrimary"
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@+id/attachment_thumbnail"
app:layout_constraintStart_toEndOf="@id/attachment_color_bar"
app:layout_constraintTop_toBottomOf="@+id/attachment_author"
tools:text="Ein sprechender Elch will meine Kreditkartennummer" />
<TextView
android:id="@+id/attachment_description"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:textColor="?colorTextPrimary"
app:layout_constraintEnd_toStartOf="@+id/attachment_thumbnail"
app:layout_constraintStart_toEndOf="@id/attachment_color_bar"
app:layout_constraintTop_toBottomOf="@id/attachment_title"
tools:text="Homer Simpson gibt einen Elch seine Kreditkarten nummer" />
<ImageView
android:id="@+id/attachment_thumbnail"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
android:adjustViewBounds="true"
android:maxWidth="80dp"
android:scaleType="fitStart"
app:layout_constraintBottom_toTopOf="@+id/attachment_preview"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
tools:background="#ff0000"
tools:minHeight="60dp"
tools:srcCompat="@tools:sample/avatars" />
<ImageView
android:id="@+id/attachment_preview"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:adjustViewBounds="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/attachment_color_bar"
app:layout_constraintTop_toBottomOf="@+id/attachment_description"
tools:background="#00ff00"
tools:minHeight="160dp" />
<ImageView
android:id="@+id/attachment_service_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:adjustViewBounds="true"
android:maxWidth="16dp"
android:maxHeight="16dp"
app:layout_constraintBottom_toBottomOf="@+id/attachment_service"
app:layout_constraintStart_toEndOf="@id/attachment_color_bar"
app:layout_constraintTop_toTopOf="@+id/attachment_service"
tools:srcCompat="@tools:sample/avatars" />
<TextView
android:id="@+id/attachment_service"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="4dp"
android:textColor="?colorTextSecondary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/attachment_service_icon"
app:layout_constraintTop_toBottomOf="@+id/attachment_preview"
app:layout_constraintVertical_bias="0.0"
tools:text="YouTube" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
<?xml version="1.0" encoding="utf-8"?><!--
Quasseldroid - Quassel client for Android
Copyright (c) 2019 Janne Koschinski
Copyright (c) 2019 The Quassel Project
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License version 3 as published
by the Free Software Foundation.
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 <http://www.gnu.org/licenses/>.
-->
<de.kuschku.quasseldroid.util.ui.view.MessageAttachmentView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
......@@ -18,6 +18,11 @@
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_message_info"
android:icon="@drawable/ic_info"
android:title="@string/label_info_message"
android:visible="false" />
<item
android:id="@+id/action_user_info"
android:icon="@drawable/ic_account"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment