From 7da4f1f6ae4d0d32ac88460610f6b46d016a89bd Mon Sep 17 00:00:00 2001 From: Niels Andriesse <andriesseniels@gmail.com> Date: Wed, 8 Jan 2020 10:50:11 +1100 Subject: [PATCH] Fix conversation deletion & public chat joining --- res/layout/activity_home.xml | 1 + res/layout/activity_join_public_chat.xml | 41 ++++++-- res/values/dimens.xml | 2 +- res/values/styles.xml | 13 +++ res/values/themes.xml | 2 + .../loki/redesign/activities/HomeActivity.kt | 95 +++++++++++-------- .../activities/JoinPublicChatActivity.kt | 52 +++++++++- 7 files changed, 155 insertions(+), 51 deletions(-) diff --git a/res/layout/activity_home.xml b/res/layout/activity_home.xml index 498738cf9d..b6239f1348 100644 --- a/res/layout/activity_home.xml +++ b/res/layout/activity_home.xml @@ -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" diff --git a/res/layout/activity_join_public_chat.xml b/res/layout/activity_join_public_chat.xml index d6bf8f66d7..3ad2c9465d 100644 --- a/res/layout/activity_join_public_chat.xml +++ b/res/layout/activity_join_public_chat.xml @@ -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> \ No newline at end of file + <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> \ No newline at end of file diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 83960a07fa..cc41589e8a 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -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 --> diff --git a/res/values/styles.xml b/res/values/styles.xml index 8af14e202a..88a1725dff 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -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> diff --git a/res/values/themes.xml b/res/values/themes.xml index 175217d7a3..bad8e588e7 100644 --- a/res/values/themes.xml +++ b/res/values/themes.xml @@ -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 --> diff --git a/src/org/thoughtcrime/securesms/loki/redesign/activities/HomeActivity.kt b/src/org/thoughtcrime/securesms/loki/redesign/activities/HomeActivity.kt index 3ae25ca036..a2d292b43a 100644 --- a/src/org/thoughtcrime/securesms/loki/redesign/activities/HomeActivity.kt +++ b/src/org/thoughtcrime/securesms/loki/redesign/activities/HomeActivity.kt @@ -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 diff --git a/src/org/thoughtcrime/securesms/loki/redesign/activities/JoinPublicChatActivity.kt b/src/org/thoughtcrime/securesms/loki/redesign/activities/JoinPublicChatActivity.kt index 771e5aae4b..b190e75bf7 100644 --- a/src/org/thoughtcrime/securesms/loki/redesign/activities/JoinPublicChatActivity.kt +++ b/src/org/thoughtcrime/securesms/loki/redesign/activities/JoinPublicChatActivity.kt @@ -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) } }