| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  | /* | 
					
						
							|  |  |  |  * Copyright 2016 Christian Basler | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package ch.dissem.apps.abit | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import android.content.Context | 
					
						
							|  |  |  | import android.content.Intent | 
					
						
							|  |  |  | import android.os.Bundle | 
					
						
							|  |  |  | import android.support.annotation.IdRes | 
					
						
							|  |  |  | import android.support.v4.app.Fragment | 
					
						
							|  |  |  | import android.support.v7.widget.GridLayoutManager | 
					
						
							|  |  |  | import android.support.v7.widget.LinearLayoutManager | 
					
						
							|  |  |  | import android.support.v7.widget.RecyclerView | 
					
						
							|  |  |  | import android.text.util.Linkify | 
					
						
							| 
									
										
										
										
											2017-10-31 07:50:57 +01:00
										 |  |  | import android.text.util.Linkify.WEB_URLS | 
					
						
							|  |  |  | import android.view.* | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  | import android.widget.ImageView | 
					
						
							|  |  |  | import android.widget.TextView | 
					
						
							| 
									
										
										
										
											2018-03-12 21:18:10 +01:00
										 |  |  | import ch.dissem.apps.abit.adapter.LabelAdapter | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  | import ch.dissem.apps.abit.service.Singleton | 
					
						
							|  |  |  | import ch.dissem.apps.abit.util.Assets | 
					
						
							| 
									
										
										
										
											2017-10-31 07:50:57 +01:00
										 |  |  | import ch.dissem.apps.abit.util.Constants.BITMESSAGE_ADDRESS_PATTERN | 
					
						
							|  |  |  | import ch.dissem.apps.abit.util.Constants.BITMESSAGE_URL_SCHEMA | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  | import ch.dissem.apps.abit.util.Drawables | 
					
						
							|  |  |  | import ch.dissem.apps.abit.util.Labels | 
					
						
							| 
									
										
										
										
											2017-10-31 07:50:57 +01:00
										 |  |  | import ch.dissem.apps.abit.util.Strings.prepareMessageExtract | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  | import ch.dissem.bitmessage.entity.Plaintext | 
					
						
							|  |  |  | import ch.dissem.bitmessage.entity.valueobject.Label | 
					
						
							| 
									
										
										
										
											2017-10-31 07:50:57 +01:00
										 |  |  | import com.mikepenz.google_material_typeface_library.GoogleMaterial | 
					
						
							|  |  |  | import com.mikepenz.iconics.view.IconicsImageView | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  | import kotlinx.android.synthetic.main.fragment_message_detail.* | 
					
						
							| 
									
										
										
										
											2017-10-31 07:50:57 +01:00
										 |  |  | import java.util.* | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * A fragment representing a single Message detail screen. | 
					
						
							|  |  |  |  * This fragment is either contained in a [MainActivity] | 
					
						
							|  |  |  |  * in two-pane mode (on tablets) or a [MessageDetailActivity] | 
					
						
							|  |  |  |  * on handsets. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | class MessageDetailFragment : Fragment() { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * The content this fragment is presenting. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     private var item: Plaintext? = null | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     override fun onCreate(savedInstanceState: Bundle?) { | 
					
						
							|  |  |  |         super.onCreate(savedInstanceState) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-31 07:50:57 +01:00
										 |  |  |         arguments?.let { arguments -> | 
					
						
							|  |  |  |             if (arguments.containsKey(ARG_ITEM)) { | 
					
						
							|  |  |  |                 // Load the dummy content specified by the fragment
 | 
					
						
							|  |  |  |                 // arguments. In a real-world scenario, use a Loader
 | 
					
						
							|  |  |  |                 // to load content from a content provider.
 | 
					
						
							|  |  |  |                 item = arguments.getSerializable(ARG_ITEM) as Plaintext | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |         setHasOptionsMenu(true) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-19 23:55:48 +01:00
										 |  |  |     override fun onCreateView( | 
					
						
							|  |  |  |         inflater: LayoutInflater, | 
					
						
							|  |  |  |         container: ViewGroup?, | 
					
						
							|  |  |  |         savedInstanceState: Bundle? | 
					
						
							|  |  |  |     ): View = | 
					
						
							| 
									
										
										
										
											2017-10-31 07:50:57 +01:00
										 |  |  |         inflater.inflate(R.layout.fragment_message_detail, container, false) | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | 
					
						
							|  |  |  |         super.onViewCreated(view, savedInstanceState) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-31 07:50:57 +01:00
										 |  |  |         val ctx = activity ?: throw IllegalStateException("Fragment is not attached to an activity") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  |         // Show the dummy content as text in a TextView.
 | 
					
						
							|  |  |  |         item?.let { item -> | 
					
						
							|  |  |  |             subject.text = item.subject | 
					
						
							|  |  |  |             status.setImageResource(Assets.getStatusDrawable(item.status)) | 
					
						
							|  |  |  |             status.contentDescription = getString(Assets.getStatusString(item.status)) | 
					
						
							|  |  |  |             avatar.setImageDrawable(Identicon(item.from)) | 
					
						
							| 
									
										
										
										
											2018-02-19 23:55:48 +01:00
										 |  |  |             val senderClickListener: (View) -> Unit = { | 
					
						
							|  |  |  |                 MainActivity.apply { | 
					
						
							|  |  |  |                     onItemSelected(item.from) | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             avatar.setOnClickListener(senderClickListener) | 
					
						
							|  |  |  |             sender.setOnClickListener(senderClickListener) | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  |             sender.text = item.from.toString() | 
					
						
							|  |  |  |             item.to?.let { to -> | 
					
						
							|  |  |  |                 recipient.text = to.toString() | 
					
						
							|  |  |  |             } ?: { | 
					
						
							|  |  |  |                 if (item.type == Plaintext.Type.BROADCAST) { | 
					
						
							|  |  |  |                     recipient.setText(R.string.broadcast) | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             }.invoke() | 
					
						
							| 
									
										
										
										
											2017-10-31 07:50:57 +01:00
										 |  |  |             val labelAdapter = LabelAdapter(ctx, item.labels) | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  |             labels.adapter = labelAdapter | 
					
						
							|  |  |  |             labels.layoutManager = GridLayoutManager(activity, 2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             text.text = item.text | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             Linkify.addLinks(text, WEB_URLS) | 
					
						
							|  |  |  |             Linkify.addLinks(text, BITMESSAGE_ADDRESS_PATTERN, BITMESSAGE_URL_SCHEMA, null, | 
					
						
							| 
									
										
										
										
											2017-10-31 07:50:57 +01:00
										 |  |  |                 Linkify.TransformFilter { match, _ -> match.group() } | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  |             ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             text.linksClickable = true | 
					
						
							|  |  |  |             text.setTextIsSelectable(true) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-31 07:50:57 +01:00
										 |  |  |             val messageRepo = Singleton.getMessageRepository(ctx) | 
					
						
							| 
									
										
										
										
											2017-11-21 20:12:31 +01:00
										 |  |  |             if (item.isUnread()) { | 
					
						
							|  |  |  |                 Singleton.labeler.markAsRead(item) | 
					
						
							| 
									
										
										
										
											2017-10-31 07:50:57 +01:00
										 |  |  |                 (activity as? MainActivity)?.updateUnread() | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  |                 messageRepo.save(item) | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             val parents = ArrayList<Plaintext>(item.parents.size) | 
					
						
							|  |  |  |             for (parentIV in item.parents) { | 
					
						
							|  |  |  |                 val parent = messageRepo.getMessage(parentIV) | 
					
						
							|  |  |  |                 if (parent != null) { | 
					
						
							|  |  |  |                     parents.add(parent) | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2017-10-31 07:50:57 +01:00
										 |  |  |             showRelatedMessages(ctx, view, R.id.parents, parents) | 
					
						
							|  |  |  |             showRelatedMessages(ctx, view, R.id.responses, messageRepo.findResponses(item)) | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-19 23:55:48 +01:00
										 |  |  |     private fun showRelatedMessages( | 
					
						
							|  |  |  |         ctx: Context, | 
					
						
							|  |  |  |         rootView: View, @IdRes id: Int, | 
					
						
							|  |  |  |         messages: List<Plaintext> | 
					
						
							|  |  |  |     ) { | 
					
						
							| 
									
										
										
										
											2017-10-31 07:50:57 +01:00
										 |  |  |         val recyclerView = rootView.findViewById<RecyclerView>(id) | 
					
						
							|  |  |  |         val adapter = RelatedMessageAdapter(ctx, messages) | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  |         recyclerView.adapter = adapter | 
					
						
							|  |  |  |         recyclerView.layoutManager = LinearLayoutManager(activity) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-01 07:29:44 +02:00
										 |  |  |     override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { | 
					
						
							|  |  |  |         inflater.inflate(R.menu.message, menu) | 
					
						
							| 
									
										
										
										
											2017-10-31 07:50:57 +01:00
										 |  |  |         activity?.let { activity -> | 
					
						
							|  |  |  |             Drawables.addIcon(activity, menu, R.id.reply, GoogleMaterial.Icon.gmd_reply) | 
					
						
							|  |  |  |             Drawables.addIcon(activity, menu, R.id.delete, GoogleMaterial.Icon.gmd_delete) | 
					
						
							| 
									
										
										
										
											2018-02-19 23:55:48 +01:00
										 |  |  |             Drawables.addIcon( | 
					
						
							|  |  |  |                 activity, menu, R.id.mark_unread, GoogleMaterial.Icon | 
					
						
							|  |  |  |                     .gmd_markunread | 
					
						
							|  |  |  |             ) | 
					
						
							| 
									
										
										
										
											2017-10-31 07:50:57 +01:00
										 |  |  |             Drawables.addIcon(activity, menu, R.id.archive, GoogleMaterial.Icon.gmd_archive) | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         super.onCreateOptionsMenu(menu, inflater) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     override fun onOptionsItemSelected(menuItem: MenuItem): Boolean { | 
					
						
							| 
									
										
										
										
											2017-10-31 07:50:57 +01:00
										 |  |  |         val messageRepo = Singleton.getMessageRepository( | 
					
						
							|  |  |  |             context ?: throw IllegalStateException("No context available") | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  |         item?.let { item -> | 
					
						
							|  |  |  |             when (menuItem.itemId) { | 
					
						
							|  |  |  |                 R.id.reply -> { | 
					
						
							|  |  |  |                     ComposeMessageActivity.launchReplyTo(this, item) | 
					
						
							|  |  |  |                     return true | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 R.id.delete -> { | 
					
						
							|  |  |  |                     if (isInTrash(item)) { | 
					
						
							| 
									
										
										
										
											2017-09-15 15:25:32 +02:00
										 |  |  |                         Singleton.labeler.delete(item) | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  |                         messageRepo.remove(item) | 
					
						
							|  |  |  |                     } else { | 
					
						
							| 
									
										
										
										
											2017-09-15 15:25:32 +02:00
										 |  |  |                         Singleton.labeler.delete(item) | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  |                         messageRepo.save(item) | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2017-09-22 20:29:23 +02:00
										 |  |  |                     (activity as? MainActivity)?.updateUnread() | 
					
						
							| 
									
										
										
										
											2017-10-31 07:50:57 +01:00
										 |  |  |                     activity?.onBackPressed() | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  |                     return true | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 R.id.mark_unread -> { | 
					
						
							| 
									
										
										
										
											2017-09-12 21:28:56 +02:00
										 |  |  |                     Singleton.labeler.markAsUnread(item) | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  |                     messageRepo.save(item) | 
					
						
							| 
									
										
										
										
											2017-09-22 20:29:23 +02:00
										 |  |  |                     (activity as? MainActivity)?.updateUnread() | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  |                     return true | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 R.id.archive -> { | 
					
						
							|  |  |  |                     if (item.isUnread() && activity is MainActivity) { | 
					
						
							|  |  |  |                         (activity as MainActivity).updateUnread() | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2017-09-12 21:28:56 +02:00
										 |  |  |                     Singleton.labeler.archive(item) | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  |                     messageRepo.save(item) | 
					
						
							| 
									
										
										
										
											2017-09-22 20:29:23 +02:00
										 |  |  |                     (activity as? MainActivity)?.updateUnread() | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  |                     return true | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 else -> return false | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return false | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-19 23:55:48 +01:00
										 |  |  |     private class RelatedMessageAdapter internal constructor( | 
					
						
							|  |  |  |         private val ctx: Context, | 
					
						
							|  |  |  |         private val messages: List<Plaintext> | 
					
						
							|  |  |  |     ) : RecyclerView.Adapter<RelatedMessageAdapter.ViewHolder>() { | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-19 23:55:48 +01:00
										 |  |  |         override fun onCreateViewHolder( | 
					
						
							|  |  |  |             parent: ViewGroup, | 
					
						
							|  |  |  |             viewType: Int | 
					
						
							|  |  |  |         ): RelatedMessageAdapter.ViewHolder { | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  |             val context = parent.context | 
					
						
							|  |  |  |             val inflater = LayoutInflater.from(context) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Inflate the custom layout
 | 
					
						
							|  |  |  |             val contactView = inflater.inflate(R.layout.item_message_minimized, parent, false) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Return a new holder instance
 | 
					
						
							|  |  |  |             return ViewHolder(contactView) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Involves populating data into the item through holder
 | 
					
						
							|  |  |  |         override fun onBindViewHolder(viewHolder: RelatedMessageAdapter.ViewHolder, position: Int) { | 
					
						
							|  |  |  |             // Get the data model based on position
 | 
					
						
							|  |  |  |             val message = messages[position] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             viewHolder.avatar.setImageDrawable(Identicon(message.from)) | 
					
						
							|  |  |  |             viewHolder.status.setImageResource(Assets.getStatusDrawable(message.status)) | 
					
						
							|  |  |  |             viewHolder.sender.text = message.from.toString() | 
					
						
							|  |  |  |             viewHolder.extract.text = prepareMessageExtract(message.text) | 
					
						
							|  |  |  |             viewHolder.item = message | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Returns the total count of items in the list
 | 
					
						
							|  |  |  |         override fun getItemCount() = messages.size | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         internal inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { | 
					
						
							| 
									
										
										
										
											2017-10-31 07:50:57 +01:00
										 |  |  |             internal val avatar = itemView.findViewById<ImageView>(R.id.avatar) | 
					
						
							|  |  |  |             internal val status = itemView.findViewById<ImageView>(R.id.status) | 
					
						
							|  |  |  |             internal val sender = itemView.findViewById<TextView>(R.id.sender) | 
					
						
							|  |  |  |             internal val extract = itemView.findViewById<TextView>(R.id.text) | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  |             internal var item: Plaintext? = null | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             init { | 
					
						
							|  |  |  |                 itemView.setOnClickListener { | 
					
						
							|  |  |  |                     if (ctx is MainActivity) { | 
					
						
							|  |  |  |                         item?.let { ctx.onItemSelected(it) } | 
					
						
							|  |  |  |                     } else { | 
					
						
							|  |  |  |                         val detailIntent = Intent(ctx, MessageDetailActivity::class.java) | 
					
						
							|  |  |  |                         detailIntent.putExtra(MessageDetailFragment.ARG_ITEM, item) | 
					
						
							|  |  |  |                         ctx.startActivity(detailIntent) | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     companion object { | 
					
						
							|  |  |  |         /** | 
					
						
							|  |  |  |          * The fragment argument representing the item ID that this fragment | 
					
						
							|  |  |  |          * represents. | 
					
						
							|  |  |  |          */ | 
					
						
							| 
									
										
										
										
											2018-02-20 16:40:03 +01:00
										 |  |  |         const val ARG_ITEM = "item" | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-31 07:50:57 +01:00
										 |  |  |         fun isInTrash(item: Plaintext?) = item?.labels?.any { it.type == Label.Type.TRASH } == true | 
					
						
							| 
									
										
										
										
											2017-08-25 20:24:25 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | } |