Fix conversation deletion & public chat joining

This commit is contained in:
Niels Andriesse 2020-01-08 10:50:11 +11:00
parent fb3bd26538
commit 7da4f1f6ae
7 changed files with 155 additions and 51 deletions

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/contentView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"

View File

@ -1,14 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.view.ViewPager
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/viewPager"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent" >
android:layout_height="match_parent">
<android.support.design.widget.TabLayout
style="@style/Session.DarkTabLayout"
android:id="@+id/tabLayout"
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="@dimen/tab_bar_height" />
android:layout_height="match_parent" >
</android.support.v4.view.ViewPager>
<android.support.design.widget.TabLayout
style="@style/Session.DarkTabLayout"
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="@dimen/tab_bar_height" />
</android.support.v4.view.ViewPager>
<RelativeLayout
android:id="@+id/loader"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#A4000000"
android:visibility="gone"
android:alpha="0">
<com.github.ybq.android.spinkit.SpinKitView
style="@style/SpinKitView.Large.ThreeBounce"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_centerInParent="true"
app:SpinKit_Color="@color/text" />
</RelativeLayout>
</RelativeLayout>

View File

@ -37,7 +37,7 @@
<dimen name="large_spacing">24dp</dimen>
<dimen name="very_large_spacing">35dp</dimen>
<dimen name="massive_spacing">64dp</dimen>
<dimen name="new_conversation_button_bottom_offset">40dp</dimen>
<dimen name="new_conversation_button_bottom_offset">56dp</dimen>
<dimen name="onboarding_button_bottom_offset">40dp</dimen>
<!-- Session -->

View File

@ -16,6 +16,19 @@
<item name="android:textSize">@dimen/very_large_font_size</item>
</style>
<style name="Session.AlertDialog" parent="ThemeOverlay.AppCompat.Dialog.Alert">
<item name="buttonBarNegativeButtonStyle">@style/Session.AlertDialog.NegativeButtonStyle</item>
<item name="buttonBarPositiveButtonStyle">@style/Session.AlertDialog.PositiveButtonStyle</item>
</style>
<style name="Session.AlertDialog.NegativeButtonStyle" parent="Widget.AppCompat.Button.ButtonBar.AlertDialog">
<item name="android:textColor">@color/accent</item>
</style>
<style name="Session.AlertDialog.PositiveButtonStyle" parent="Widget.AppCompat.Button.ButtonBar.AlertDialog">
<item name="android:textColor">@color/accent</item>
</style>
<style name="Session.DarkTabLayout" parent="Widget.Design.TabLayout">
<item name="tabIndicatorColor">@color/accent</item>
<item name="tabIndicatorHeight">@dimen/accent_line_thickness</item>

View File

@ -8,6 +8,7 @@
<item name="colorPrimary">@color/action_bar_background</item>
<item name="colorPrimaryDark">@color/action_bar_background</item>
<item name="android:navigationBarColor">@color/navigation_bar_background</item>
<item name="alertDialogTheme">@style/Session.AlertDialog</item>
</style>
<style name="Session.DarkTheme.NoActionBar" parent="@style/Theme.AppCompat.NoActionBar">
@ -15,6 +16,7 @@
<item name="colorPrimary">@color/action_bar_background</item>
<item name="colorPrimaryDark">@color/action_bar_background</item>
<item name="android:navigationBarColor">@color/navigation_bar_background</item>
<item name="alertDialogTheme">@style/Session.AlertDialog</item>
</style>
<!-- Session -->

View File

@ -1,9 +1,7 @@
package org.thoughtcrime.securesms.loki.redesign.activities
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.arch.lifecycle.Observer
import android.content.Context
import android.content.Intent
import android.database.Cursor
import android.graphics.BitmapFactory
@ -11,9 +9,10 @@ import android.graphics.Canvas
import android.graphics.Paint
import android.os.AsyncTask
import android.os.Bundle
import android.os.Handler
import android.support.design.widget.Snackbar
import android.support.v4.app.LoaderManager
import android.support.v4.content.Loader
import android.support.v7.app.AlertDialog
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.support.v7.widget.helper.ItemTouchHelper
@ -23,6 +22,7 @@ import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.conversation.ConversationActivity
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.ThreadDatabase
import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.loki.getColorWithID
import org.thoughtcrime.securesms.loki.redesign.utilities.push
@ -48,6 +48,24 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) {
super.onCreate(savedInstanceState, isReady)
// Process any outstanding deletes
val threadDatabase = DatabaseFactory.getThreadDatabase(this)
val archivedConversationCount = threadDatabase.archivedConversationListCount
if (archivedConversationCount > 0) {
val archivedConversations = threadDatabase.archivedConversationList
archivedConversations.moveToFirst()
fun deleteThreadAtCurrentPosition() {
val threadID = archivedConversations.getLong(archivedConversations.getColumnIndex(ThreadDatabase.ID))
AsyncTask.execute {
threadDatabase.deleteConversation(threadID)
MessageNotifier.updateNotification(this)
}
}
deleteThreadAtCurrentPosition()
while (archivedConversations.moveToNext()) {
deleteThreadAtCurrentPosition()
}
}
// Set content view
setContentView(R.layout.activity_home)
// Set custom toolbar
@ -106,7 +124,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
}
override fun onLongConversationClick(view: ConversationView) {
// TODO: Implement
// Do nothing
}
private fun openConversation(thread: ThreadRecord) {
@ -135,7 +153,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
startActivity(intent)
}
private class SwipeCallback(val context: Context) : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
private class SwipeCallback(val activity: HomeActivity) : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
return false
@ -143,51 +161,48 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
@SuppressLint("StaticFieldLeak")
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val builder = AlertDialog.Builder(context)
builder.setIconAttribute(R.attr.dialog_alert_icon)
builder.setTitle("Delete Selected Conversation?")
builder.setMessage("This will permanently delete the selected conversation.")
builder.setCancelable(true)
builder.setPositiveButton("Delete") { dialog, _ ->
val threadID = (viewHolder as HomeAdapter.ViewHolder).view.thread!!.threadId
AsyncTask.execute {
DatabaseFactory.getThreadDatabase(context).deleteConversation(threadID)
MessageNotifier.updateNotification(context)
val threadID = (viewHolder as HomeAdapter.ViewHolder).view.thread!!.threadId
val threadDatabase = DatabaseFactory.getThreadDatabase(activity)
threadDatabase.archiveConversation(threadID)
val deleteThread = object : Runnable {
override fun run() {
AsyncTask.execute {
threadDatabase.deleteConversation(threadID)
MessageNotifier.updateNotification(activity)
}
}
dialog.dismiss()
}
builder.setNegativeButton(android.R.string.cancel) { dialog, _ ->
val animator = ValueAnimator.ofFloat(viewHolder.itemView.translationX, 0.0f)
animator.duration = 150
animator.addUpdateListener {
update(viewHolder, animator.animatedValue as Float)
}
animator.start()
dialog.dismiss()
val handler = Handler()
handler.postDelayed(deleteThread, 5000)
val snackbar = Snackbar.make(activity.contentView, "Conversation Deleted", Snackbar.LENGTH_LONG)
snackbar.setAction("Undo") {
threadDatabase.unarchiveConversation(threadID)
handler.removeCallbacks(deleteThread)
animate(viewHolder, 0.0f)
}
builder.create().show()
snackbar.setActionTextColor(activity.resources.getColorWithID(R.color.accent, activity.theme))
snackbar.show()
}
override fun onChildDraw(c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, dx: Float, dy: Float, actionState: Int, isCurrentlyActive: Boolean) {
if (actionState != ItemTouchHelper.ACTION_STATE_SWIPE) {
super.onChildDraw(c, recyclerView, viewHolder, dx, dy, actionState, isCurrentlyActive)
} else {
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE && dx < 0) {
val itemView = viewHolder.itemView
if (dx < 0) {
val backgroundPaint = Paint()
backgroundPaint.color = context.resources.getColorWithID(R.color.destructive, context.theme)
c.drawRect(itemView.right.toFloat() - abs(dx), itemView.top.toFloat(), itemView.right.toFloat(), itemView.bottom.toFloat(), backgroundPaint)
val icon = BitmapFactory.decodeResource(context.resources, R.drawable.ic_trash_filled_32)
val iconPaint = Paint()
val left = itemView.right.toFloat() - abs(dx) + context.resources.getDimension(R.dimen.medium_spacing)
val top = itemView.top.toFloat() + (itemView.bottom.toFloat() - itemView.top.toFloat() - icon.height) / 2
c.drawBitmap(icon, left, top, iconPaint)
}
update(viewHolder, dx)
animate(viewHolder, dx)
val backgroundPaint = Paint()
backgroundPaint.color = activity.resources.getColorWithID(R.color.destructive, activity.theme)
c.drawRect(itemView.right.toFloat() - abs(dx), itemView.top.toFloat(), itemView.right.toFloat(), itemView.bottom.toFloat(), backgroundPaint)
val icon = BitmapFactory.decodeResource(activity.resources, R.drawable.ic_trash_filled_32)
val iconPaint = Paint()
val left = itemView.right.toFloat() - abs(dx) + activity.resources.getDimension(R.dimen.medium_spacing)
val top = itemView.top.toFloat() + (itemView.bottom.toFloat() - itemView.top.toFloat() - icon.height) / 2
c.drawBitmap(icon, left, top, iconPaint)
} else {
super.onChildDraw(c, recyclerView, viewHolder, dx, dy, actionState, isCurrentlyActive)
}
}
private fun update(viewHolder: RecyclerView.ViewHolder, dx: Float) {
private fun animate(viewHolder: RecyclerView.ViewHolder, dx: Float) {
val alpha = 1.0f - abs(dx) / viewHolder.itemView.width.toFloat()
viewHolder.itemView.alpha = alpha
viewHolder.itemView.translationX = dx

View File

@ -1,17 +1,28 @@
package org.thoughtcrime.securesms.loki.redesign.activities
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentPagerAdapter
import android.util.Patterns
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_join_public_chat.*
import kotlinx.android.synthetic.main.fragment_enter_chat_url.*
import network.loki.messenger.R
import nl.komponents.kovenant.ui.failUi
import nl.komponents.kovenant.ui.successUi
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil
import org.thoughtcrime.securesms.loki.redesign.fragments.ScanQRCodeWrapperFragment
import org.thoughtcrime.securesms.loki.redesign.fragments.ScanQRCodeWrapperFragmentDelegate
import org.thoughtcrime.securesms.util.TextSecurePreferences
class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCodeWrapperFragmentDelegate {
private val adapter = JoinPublicChatActivityAdapter(this)
@ -29,13 +40,48 @@ class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCode
}
// endregion
// region Updating
private fun showLoader() {
loader.visibility = View.VISIBLE
loader.animate().setDuration(150).alpha(1.0f).start()
}
private fun hideLoader() {
loader.animate().setDuration(150).alpha(0.0f).setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
super.onAnimationEnd(animation)
loader.visibility = View.GONE
}
})
}
// endregion
// region Interaction
override fun handleQRCodeScanned(url: String) {
joinPublicChatIfPossible(url)
}
fun joinPublicChatIfPossible(url: String) {
// TODO: Implement
if (!Patterns.WEB_URL.matcher(url).matches() || !url.startsWith("https://")) {
return Toast.makeText(this, "Invalid URL", Toast.LENGTH_SHORT).show()
}
showLoader()
val application = ApplicationContext.getInstance(this)
val channel: Long = 1
val displayName = TextSecurePreferences.getProfileName(this)
val lokiPublicChatAPI = application.lokiPublicChatAPI!!
application.lokiPublicChatManager.addChat(url, channel).successUi {
lokiPublicChatAPI.getMessages(channel, url)
lokiPublicChatAPI.setDisplayName(displayName, url)
val profileKey: ByteArray = ProfileKeyUtil.getProfileKey(this)
val profileUrl: String? = TextSecurePreferences.getProfileAvatarUrl(this)
lokiPublicChatAPI.setProfilePicture(url, profileKey, profileUrl)
finish()
}.failUi {
hideLoader()
Toast.makeText(this, "Couldn't Join Public Chat", Toast.LENGTH_SHORT).show()
}
}
// endregion
}
@ -83,7 +129,9 @@ class EnterChatURLFragment : Fragment() {
}
private fun joinPublicChatIfPossible() {
val chatURL = chatURLEditText.text.trim().toString()
val inputMethodManager = context!!.getSystemService(BaseActionBarActivity.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(chatURLEditText.windowToken, 0)
val chatURL = chatURLEditText.text.trim().toString().toLowerCase().replace("http://", "https://")
(activity!! as JoinPublicChatActivity).joinPublicChatIfPossible(chatURL)
}
}