Merge pull request #327 from loki-project/ui

UI Cleanup Part 2/3
This commit is contained in:
Niels Andriesse 2020-09-08 16:48:26 +10:00 committed by GitHub
commit 1eb38a1447
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 467 additions and 81 deletions

View File

@ -7,5 +7,5 @@
<corners android:radius="@dimen/medium_button_corner_radius" /> <corners android:radius="@dimen/medium_button_corner_radius" />
<stroke android:width="@dimen/border_thickness" android:color="?colorAccent" /> <stroke android:width="@dimen/border_thickness" android:color="@color/accent" />
</shape> </shape>

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/contentView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
@ -30,7 +31,7 @@
android:textColor="@color/text" android:textColor="@color/text"
android:text="There are two ways Session can notify you of new messages." /> android:text="There are two ways Session can notify you of new messages." />
<LinearLayout <org.thoughtcrime.securesms.loki.views.PNModeView
android:id="@+id/fcmOptionView" android:id="@+id/fcmOptionView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -66,9 +67,9 @@
android:textStyle="bold" android:textStyle="bold"
android:text="@string/activity_pn_mode_recommended_option_tag" /> android:text="@string/activity_pn_mode_recommended_option_tag" />
</LinearLayout> </org.thoughtcrime.securesms.loki.views.PNModeView>
<LinearLayout <org.thoughtcrime.securesms.loki.views.PNModeView
android:id="@+id/backgroundPollingOptionView" android:id="@+id/backgroundPollingOptionView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -95,7 +96,7 @@
android:textColor="@color/text" android:textColor="@color/text"
android:text="Session will occasionally check for new messages in the background. Full metadata protection is guaranteed, but message notifications will be unreliable." /> android:text="Session will occasionally check for new messages in the background. Full metadata protection is guaranteed, but message notifications will be unreliable." />
</LinearLayout> </org.thoughtcrime.securesms.loki.views.PNModeView>
<View <View
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -76,7 +76,8 @@
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:clipChildren="false">
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView" android:id="@+id/recyclerView"

View File

@ -23,7 +23,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" android:layout_weight="1"
android:layout_margin="@dimen/large_spacing"> android:layout_margin="@dimen/large_spacing"
android:clipChildren="false">
<LinearLayout <LinearLayout
android:id="@+id/pathRowsContainer" android:id="@+id/pathRowsContainer"

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/contentView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
@ -30,7 +31,7 @@
android:textColor="@color/text" android:textColor="@color/text"
android:text="There are two ways Session can notify you of new messages." /> android:text="There are two ways Session can notify you of new messages." />
<LinearLayout <org.thoughtcrime.securesms.loki.views.PNModeView
android:id="@+id/fcmOptionView" android:id="@+id/fcmOptionView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -66,9 +67,9 @@
android:textStyle="bold" android:textStyle="bold"
android:text="@string/activity_pn_mode_recommended_option_tag" /> android:text="@string/activity_pn_mode_recommended_option_tag" />
</LinearLayout> </org.thoughtcrime.securesms.loki.views.PNModeView>
<LinearLayout <org.thoughtcrime.securesms.loki.views.PNModeView
android:id="@+id/backgroundPollingOptionView" android:id="@+id/backgroundPollingOptionView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -95,7 +96,7 @@
android:textColor="@color/text" android:textColor="@color/text"
android:text="Session will occasionally check for new messages in the background. Full metadata protection is guaranteed, but message notifications will be unreliable." /> android:text="Session will occasionally check for new messages in the background. Full metadata protection is guaranteed, but message notifications will be unreliable." />
</LinearLayout> </org.thoughtcrime.securesms.loki.views.PNModeView>
<View <View
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -20,9 +20,15 @@
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal"> android:orientation="horizontal">
<org.thoughtcrime.securesms.loki.views.ProfilePictureView
android:id="@+id/profilePictureView"
android:layout_width="@dimen/medium_profile_picture_size"
android:layout_height="@dimen/medium_profile_picture_size" />
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/medium_spacing"
android:orientation="vertical"> android:orientation="vertical">
<TextView <TextView

View File

@ -9,6 +9,13 @@
app:behavior_hideable="true" app:behavior_hideable="true"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"> app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<TextView
android:id="@+id/detailsTextView"
style="@style/BottomSheetActionItem"
android:drawableStart="@drawable/ic_info_outline_white_24dp"
android:drawableTint="?attr/colorControlNormal"
android:text="@string/details" />
<TextView <TextView
android:id="@+id/blockTextView" android:id="@+id/blockTextView"
style="@style/BottomSheetActionItem" style="@style/BottomSheetActionItem"

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center_horizontal"
android:paddingLeft="@dimen/large_spacing"
android:paddingRight="@dimen/large_spacing"
android:paddingBottom="@dimen/large_spacing"
app:behavior_hideable="true"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<org.thoughtcrime.securesms.loki.views.ProfilePictureView
android:id="@+id/profilePictureView"
android:layout_width="@dimen/large_profile_picture_size"
android:layout_height="@dimen/large_profile_picture_size"
android:layout_marginTop="@dimen/large_spacing" />
<TextView
android:id="@+id/nameTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_spacing"
android:textSize="@dimen/massive_font_size"
android:textStyle="bold"
android:textColor="@color/text"
android:text="Spiderman" />
<TextView
style="@style/SessionIDTextView"
android:id="@+id/publicKeyTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/large_font_size"
android:layout_marginTop="@dimen/large_spacing"
android:textIsSelectable="true"
android:text="05987d601943c267879be41830888066c6a024cbdc9a548d06813924bf3372ea78" />
<Button
style="@style/Widget.Session.Button.Common.ProminentOutline"
android:id="@+id/copyButton"
android:layout_width="wrap_content"
android:layout_height="@dimen/medium_button_height"
android:layout_marginTop="@dimen/medium_spacing"
android:paddingLeft="@dimen/large_spacing"
android:paddingRight="@dimen/large_spacing"
android:text="@string/copy" />
</LinearLayout>

View File

@ -5,6 +5,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingLeft="@dimen/very_large_spacing" android:paddingLeft="@dimen/very_large_spacing"
android:paddingRight="@dimen/very_large_spacing" android:paddingRight="@dimen/very_large_spacing"
android:paddingBottom="@dimen/medium_spacing"
android:orientation="vertical"> android:orientation="vertical">
<TextView <TextView

View File

@ -28,7 +28,7 @@
<color name="app_icon_background">#333132</color> <color name="app_icon_background">#333132</color>
<color name="progress_bar_background">#0AFFFFFF</color> <color name="progress_bar_background">#0A000000</color>
<color name="quote_not_found_background">#99FFFFFF</color> <color name="quote_not_found_background">#99FFFFFF</color>
</resources> </resources>

View File

@ -11,4 +11,14 @@
<item name="android:textColor">@android:color/white</item> <item name="android:textColor">@android:color/white</item>
</style> </style>
<style name="FakeChatViewMessageBubble.Incoming">
<item name="android:background">@drawable/fake_chat_view_incoming_message_background</item>
<item name="android:elevation">4dp</item>
</style>
<style name="FakeChatViewMessageBubble.Outgoing">
<item name="android:background">@drawable/fake_chat_view_outgoing_message_background</item>
<item name="android:elevation">4dp</item>
</style>
</resources> </resources>

View File

@ -25,6 +25,7 @@
<color name="compose_text_view_background">#141414</color> <color name="compose_text_view_background">#141414</color>
<color name="quote_not_found_background">#99FFFFFF</color> <color name="quote_not_found_background">#99FFFFFF</color>
<color name="new_conversation_button_collapsed_background">#1F1F1F</color> <color name="new_conversation_button_collapsed_background">#1F1F1F</color>
<color name="new_conversation_button_shadow">#077C44</color>
<color name="pn_option_background">#1B1B1B</color> <color name="pn_option_background">#1B1B1B</color>
<color name="pn_option_border">#212121</color> <color name="pn_option_border">#212121</color>
<color name="paths_building">#FFCE3A</color> <color name="paths_building">#FFCE3A</color>

View File

@ -29,7 +29,7 @@
<dimen name="text_view_corner_radius">8dp</dimen> <dimen name="text_view_corner_radius">8dp</dimen>
<dimen name="fake_chat_view_bubble_width">224dp</dimen> <dimen name="fake_chat_view_bubble_width">224dp</dimen>
<dimen name="fake_chat_view_bubble_corner_radius">10dp</dimen> <dimen name="fake_chat_view_bubble_corner_radius">10dp</dimen>
<dimen name="fake_chat_view_height">234dp</dimen> <dimen name="fake_chat_view_height">250dp</dimen>
<dimen name="setting_button_height">56dp</dimen> <dimen name="setting_button_height">56dp</dimen>
<dimen name="dialog_corner_radius">12dp</dimen> <dimen name="dialog_corner_radius">12dp</dimen>
<dimen name="dialog_button_corner_radius">4dp</dimen> <dimen name="dialog_button_corner_radius">4dp</dimen>

View File

@ -1864,5 +1864,6 @@
<string name="attachment">Attachment</string> <string name="attachment">Attachment</string>
<string name="attachment_type_voice_message">Voice Message</string> <string name="attachment_type_voice_message">Voice Message</string>
<string name="details">Details</string>
</resources> </resources>

View File

@ -184,10 +184,12 @@
<style name="FakeChatViewMessageBubble.Incoming"> <style name="FakeChatViewMessageBubble.Incoming">
<item name="android:background">@drawable/fake_chat_view_incoming_message_background</item> <item name="android:background">@drawable/fake_chat_view_incoming_message_background</item>
<item name="android:elevation">10dp</item>
</style> </style>
<style name="FakeChatViewMessageBubble.Outgoing"> <style name="FakeChatViewMessageBubble.Outgoing">
<item name="android:background">@drawable/fake_chat_view_outgoing_message_background</item> <item name="android:background">@drawable/fake_chat_view_outgoing_message_background</item>
<item name="android:elevation">10dp</item>
</style> </style>
<!-- Session --> <!-- Session -->

View File

@ -160,6 +160,7 @@ import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol; import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol;
import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities; import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities;
import org.thoughtcrime.securesms.loki.views.MentionCandidateSelectionView; import org.thoughtcrime.securesms.loki.views.MentionCandidateSelectionView;
import org.thoughtcrime.securesms.loki.views.ProfilePictureView;
import org.thoughtcrime.securesms.loki.views.SessionRestoreBannerView; import org.thoughtcrime.securesms.loki.views.SessionRestoreBannerView;
import org.thoughtcrime.securesms.mediasend.Media; import org.thoughtcrime.securesms.mediasend.Media;
import org.thoughtcrime.securesms.mediasend.MediaSendActivity; import org.thoughtcrime.securesms.mediasend.MediaSendActivity;
@ -234,6 +235,7 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -300,6 +302,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private AnimatingToggle buttonToggle; private AnimatingToggle buttonToggle;
private SendButton sendButton; private SendButton sendButton;
private ImageButton attachButton; private ImageButton attachButton;
private ProfilePictureView profilePictureView;
private TextView titleTextView; private TextView titleTextView;
private TextView charactersLeft; private TextView charactersLeft;
private ConversationFragment fragment; private ConversationFragment fragment;
@ -527,6 +530,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
composeText.setTransport(sendButton.getSelectedTransport()); composeText.setTransport(sendButton.getSelectedTransport());
updateTitleTextView(recipient); updateTitleTextView(recipient);
updateProfilePicture();
updateSubtitleTextView(); updateSubtitleTextView();
setActionBarColor(recipient.getColor()); setActionBarColor(recipient.getColor());
updateInputUI(recipient, isSecureText, isDefaultSms); updateInputUI(recipient, isSecureText, isDefaultSms);
@ -621,6 +625,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
recipient = Recipient.from(this, data.getParcelableExtra(GroupCreateActivity.GROUP_ADDRESS_EXTRA), true); recipient = Recipient.from(this, data.getParcelableExtra(GroupCreateActivity.GROUP_ADDRESS_EXTRA), true);
recipient.addListener(this); recipient.addListener(this);
updateTitleTextView(recipient); updateTitleTextView(recipient);
updateProfilePicture();
updateSubtitleTextView(); updateSubtitleTextView();
NotificationChannels.updateContactChannelName(this, recipient); NotificationChannels.updateContactChannelName(this, recipient);
updateInputUI(recipient, isSecureText, isDefaultSms); updateInputUI(recipient, isSecureText, isDefaultSms);
@ -1654,6 +1659,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
private void initializeViews() { private void initializeViews() {
profilePictureView = findViewById(R.id.profilePictureView);
titleTextView = findViewById(R.id.titleTextView); titleTextView = findViewById(R.id.titleTextView);
buttonToggle = ViewUtil.findById(this, R.id.button_toggle); buttonToggle = ViewUtil.findById(this, R.id.button_toggle);
sendButton = ViewUtil.findById(this, R.id.send_button); sendButton = ViewUtil.findById(this, R.id.send_button);
@ -1872,6 +1878,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
Util.runOnMain(() -> { Util.runOnMain(() -> {
Log.i(TAG, "onModifiedRun(): " + recipient.getRegistered()); Log.i(TAG, "onModifiedRun(): " + recipient.getRegistered());
updateTitleTextView(recipient); updateTitleTextView(recipient);
updateProfilePicture();
updateSubtitleTextView(); updateSubtitleTextView();
// titleView.setVerified(identityRecords.isVerified()); // titleView.setVerified(identityRecords.isVerified());
updateInputUI(recipient, isSecureText, isDefaultSms); updateInputUI(recipient, isSecureText, isDefaultSms);
@ -3100,6 +3107,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
} }
private void updateProfilePicture() {
profilePictureView.glide = GlideApp.with(this);
profilePictureView.update(recipient, threadId);
}
private void updateSubtitleTextView() { private void updateSubtitleTextView() {
muteIndicatorImageView.setVisibility(View.GONE); muteIndicatorImageView.setVisibility(View.GONE);
subtitleTextView.setVisibility(View.VISIBLE); subtitleTextView.setVisibility(View.VISIBLE);

View File

@ -34,6 +34,7 @@ import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob
import org.thoughtcrime.securesms.loki.dialogs.ConversationOptionsBottomSheet import org.thoughtcrime.securesms.loki.dialogs.ConversationOptionsBottomSheet
import org.thoughtcrime.securesms.loki.dialogs.LightThemeFeatureIntroBottomSheet import org.thoughtcrime.securesms.loki.dialogs.LightThemeFeatureIntroBottomSheet
import org.thoughtcrime.securesms.loki.dialogs.MultiDeviceRemovalBottomSheet import org.thoughtcrime.securesms.loki.dialogs.MultiDeviceRemovalBottomSheet
import org.thoughtcrime.securesms.loki.dialogs.UserDetailsBottomSheet
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol
import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation
import org.thoughtcrime.securesms.loki.utilities.* import org.thoughtcrime.securesms.loki.utilities.*
@ -100,6 +101,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
profileButton.publicKey = publicKey profileButton.publicKey = publicKey
profileButton.update() profileButton.update()
profileButton.setOnClickListener { openSettings() } profileButton.setOnClickListener { openSettings() }
pathStatusViewContainer.disableClipping()
pathStatusViewContainer.setOnClickListener { showPath() } pathStatusViewContainer.setOnClickListener { showPath() }
// Set up seed reminder view // Set up seed reminder view
val isMasterDevice = (TextSecurePreferences.getMasterHexEncodedPublicKey(this) == null) val isMasterDevice = (TextSecurePreferences.getMasterHexEncodedPublicKey(this) == null)
@ -271,6 +273,14 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
val thread = view.thread ?: return val thread = view.thread ?: return
val bottomSheet = ConversationOptionsBottomSheet() val bottomSheet = ConversationOptionsBottomSheet()
bottomSheet.recipient = thread.recipient bottomSheet.recipient = thread.recipient
bottomSheet.onViewDetailsTapped = {
bottomSheet.dismiss()
val userDetailsBottomSheet = UserDetailsBottomSheet()
val bundle = Bundle()
bundle.putString("publicKey", thread.recipient.address.toPhoneString())
userDetailsBottomSheet.arguments = bundle
userDetailsBottomSheet.show(supportFragmentManager, userDetailsBottomSheet.tag)
}
bottomSheet.onBlockTapped = { bottomSheet.onBlockTapped = {
bottomSheet.dismiss() bottomSheet.dismiss()
if (!thread.recipient.isBlocked) { if (!thread.recipient.isBlocked) {

View File

@ -1,5 +1,7 @@
package org.thoughtcrime.securesms.loki.activities package org.thoughtcrime.securesms.loki.activities
import android.animation.ArgbEvaluator
import android.animation.ValueAnimator
import android.app.AlertDialog import android.app.AlertDialog
import android.content.Intent import android.content.Intent
import android.graphics.drawable.TransitionDrawable import android.graphics.drawable.TransitionDrawable
@ -9,27 +11,36 @@ import androidx.annotation.DrawableRes
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.widget.LinearLayout
import android.widget.Toast import android.widget.Toast
import androidx.annotation.ColorRes
import kotlinx.android.synthetic.main.activity_display_name.registerButton import kotlinx.android.synthetic.main.activity_display_name.registerButton
import kotlinx.android.synthetic.main.activity_pn_mode.* import kotlinx.android.synthetic.main.activity_pn_mode.*
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.BaseActionBarActivity import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.loki.utilities.disableClipping
import org.thoughtcrime.securesms.loki.utilities.getColorWithID
import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo
import org.thoughtcrime.securesms.loki.utilities.show import org.thoughtcrime.securesms.loki.utilities.show
import org.thoughtcrime.securesms.loki.views.GlowViewUtilities
import org.thoughtcrime.securesms.loki.views.PNModeView
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
class PNModeActivity : BaseActionBarActivity() { class PNModeActivity : BaseActionBarActivity() {
private var selectedOptionView: LinearLayout? = null private var selectedOptionView: PNModeView? = null
// region Lifecycle // region Lifecycle
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setUpActionBarSessionLogo() setUpActionBarSessionLogo()
setContentView(R.layout.activity_pn_mode) setContentView(R.layout.activity_pn_mode)
contentView.disableClipping()
fcmOptionView.setOnClickListener { toggleFCM() } fcmOptionView.setOnClickListener { toggleFCM() }
fcmOptionView.mainColor = resources.getColorWithID(R.color.pn_option_background, theme)
fcmOptionView.strokeColor = resources.getColorWithID(R.color.pn_option_border, theme)
backgroundPollingOptionView.setOnClickListener { toggleBackgroundPolling() } backgroundPollingOptionView.setOnClickListener { toggleBackgroundPolling() }
backgroundPollingOptionView.mainColor = resources.getColorWithID(R.color.pn_option_background, theme)
backgroundPollingOptionView.strokeColor = resources.getColorWithID(R.color.pn_option_border, theme)
registerButton.setOnClickListener { register() } registerButton.setOnClickListener { register() }
} }
@ -71,15 +82,23 @@ class PNModeActivity : BaseActionBarActivity() {
when (selectedOptionView) { when (selectedOptionView) {
null -> { null -> {
performTransition(R.drawable.pn_option_background_select_transition, fcmOptionView) performTransition(R.drawable.pn_option_background_select_transition, fcmOptionView)
GlowViewUtilities.animateShadowColorChange(this, fcmOptionView, R.color.transparent, R.color.accent)
animateStrokeColorChange(fcmOptionView, R.color.pn_option_border, R.color.accent)
selectedOptionView = fcmOptionView selectedOptionView = fcmOptionView
} }
fcmOptionView -> { fcmOptionView -> {
performTransition(R.drawable.pn_option_background_deselect_transition, fcmOptionView) performTransition(R.drawable.pn_option_background_deselect_transition, fcmOptionView)
GlowViewUtilities.animateShadowColorChange(this, fcmOptionView, R.color.accent, R.color.transparent)
animateStrokeColorChange(fcmOptionView, R.color.accent, R.color.pn_option_border)
selectedOptionView = null selectedOptionView = null
} }
backgroundPollingOptionView -> { backgroundPollingOptionView -> {
performTransition(R.drawable.pn_option_background_select_transition, fcmOptionView) performTransition(R.drawable.pn_option_background_select_transition, fcmOptionView)
GlowViewUtilities.animateShadowColorChange(this, fcmOptionView, R.color.transparent, R.color.accent)
animateStrokeColorChange(fcmOptionView, R.color.pn_option_border, R.color.accent)
performTransition(R.drawable.pn_option_background_deselect_transition, backgroundPollingOptionView) performTransition(R.drawable.pn_option_background_deselect_transition, backgroundPollingOptionView)
GlowViewUtilities.animateShadowColorChange(this, backgroundPollingOptionView, R.color.accent, R.color.transparent)
animateStrokeColorChange(backgroundPollingOptionView, R.color.accent, R.color.pn_option_border)
selectedOptionView = fcmOptionView selectedOptionView = fcmOptionView
} }
} }
@ -89,20 +108,40 @@ class PNModeActivity : BaseActionBarActivity() {
when (selectedOptionView) { when (selectedOptionView) {
null -> { null -> {
performTransition(R.drawable.pn_option_background_select_transition, backgroundPollingOptionView) performTransition(R.drawable.pn_option_background_select_transition, backgroundPollingOptionView)
GlowViewUtilities.animateShadowColorChange(this, backgroundPollingOptionView, R.color.transparent, R.color.accent)
animateStrokeColorChange(backgroundPollingOptionView, R.color.pn_option_border, R.color.accent)
selectedOptionView = backgroundPollingOptionView selectedOptionView = backgroundPollingOptionView
} }
backgroundPollingOptionView -> { backgroundPollingOptionView -> {
performTransition(R.drawable.pn_option_background_deselect_transition, backgroundPollingOptionView) performTransition(R.drawable.pn_option_background_deselect_transition, backgroundPollingOptionView)
GlowViewUtilities.animateShadowColorChange(this, backgroundPollingOptionView, R.color.accent, R.color.transparent)
animateStrokeColorChange(backgroundPollingOptionView, R.color.accent, R.color.pn_option_border)
selectedOptionView = null selectedOptionView = null
} }
fcmOptionView -> { fcmOptionView -> {
performTransition(R.drawable.pn_option_background_select_transition, backgroundPollingOptionView) performTransition(R.drawable.pn_option_background_select_transition, backgroundPollingOptionView)
GlowViewUtilities.animateShadowColorChange(this, backgroundPollingOptionView, R.color.transparent, R.color.accent)
animateStrokeColorChange(backgroundPollingOptionView, R.color.pn_option_border, R.color.accent)
performTransition(R.drawable.pn_option_background_deselect_transition, fcmOptionView) performTransition(R.drawable.pn_option_background_deselect_transition, fcmOptionView)
GlowViewUtilities.animateShadowColorChange(this, fcmOptionView, R.color.accent, R.color.transparent)
animateStrokeColorChange(fcmOptionView, R.color.accent, R.color.pn_option_border)
selectedOptionView = backgroundPollingOptionView selectedOptionView = backgroundPollingOptionView
} }
} }
} }
private fun animateStrokeColorChange(bubble: PNModeView, @ColorRes startColorID: Int, @ColorRes endColorID: Int) {
val startColor = resources.getColorWithID(startColorID, theme)
val endColor = resources.getColorWithID(endColorID, theme)
val animation = ValueAnimator.ofObject(ArgbEvaluator(), startColor, endColor)
animation.duration = 250
animation.addUpdateListener { animator ->
val color = animator.animatedValue as Int
bubble.strokeColor = color
}
animation.start()
}
private fun register() { private fun register() {
if (selectedOptionView == null) { if (selectedOptionView == null) {
val dialog = AlertDialog.Builder(this) val dialog = AlertDialog.Builder(this)
@ -111,7 +150,6 @@ class PNModeActivity : BaseActionBarActivity() {
dialog.create().show() dialog.create().show()
return return
} }
val displayName = TextSecurePreferences.getProfileName(this)
TextSecurePreferences.setHasSeenWelcomeScreen(this, true) TextSecurePreferences.setHasSeenWelcomeScreen(this, true)
TextSecurePreferences.setPromptedPushRegistration(this, true) TextSecurePreferences.setPromptedPushRegistration(this, true)
TextSecurePreferences.setIsUsingFCM(this, (selectedOptionView == fcmOptionView)) TextSecurePreferences.setIsUsingFCM(this, (selectedOptionView == fcmOptionView))

View File

@ -16,10 +16,13 @@ import android.widget.LinearLayout
import android.widget.RelativeLayout import android.widget.RelativeLayout
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.annotation.ColorRes
import kotlinx.android.synthetic.main.activity_path.* import kotlinx.android.synthetic.main.activity_path.*
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.loki.utilities.* import org.thoughtcrime.securesms.loki.utilities.*
import org.thoughtcrime.securesms.loki.views.GlowViewUtilities
import org.thoughtcrime.securesms.loki.views.PathDotView
import org.whispersystems.signalservice.loki.api.Snode import org.whispersystems.signalservice.loki.api.Snode
import org.whispersystems.signalservice.loki.api.onionrequests.OnionRequestAPI import org.whispersystems.signalservice.loki.api.onionrequests.OnionRequestAPI
@ -31,6 +34,7 @@ class PathActivity : PassphraseRequiredActionBarActivity() {
super.onCreate(savedInstanceState, isReady) super.onCreate(savedInstanceState, isReady)
setContentView(R.layout.activity_path) setContentView(R.layout.activity_path)
supportActionBar!!.title = resources.getString(R.string.activity_path_title) supportActionBar!!.title = resources.getString(R.string.activity_path_title)
pathRowsContainer.disableClipping()
learnMoreButton.setOnClickListener { learnMore() } learnMoreButton.setOnClickListener { learnMore() }
update(false) update(false)
registerObservers() registerObservers()
@ -111,6 +115,7 @@ class PathActivity : PassphraseRequiredActionBarActivity() {
val mainContainer = LinearLayout(this) val mainContainer = LinearLayout(this)
mainContainer.orientation = LinearLayout.HORIZONTAL mainContainer.orientation = LinearLayout.HORIZONTAL
mainContainer.gravity = Gravity.CENTER_VERTICAL mainContainer.gravity = Gravity.CENTER_VERTICAL
mainContainer.disableClipping()
val mainContainerLayoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT) val mainContainerLayoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)
mainContainer.layoutParams = mainContainerLayoutParams mainContainer.layoutParams = mainContainerLayoutParams
val lineView = LineView(this, location, dotAnimationStartDelay, dotAnimationRepeatInterval) val lineView = LineView(this, location, dotAnimationStartDelay, dotAnimationRepeatInterval)
@ -170,8 +175,9 @@ class PathActivity : PassphraseRequiredActionBarActivity() {
private var dotAnimationRepeatInterval: Long = 0 private var dotAnimationRepeatInterval: Long = 0
private val dotView by lazy { private val dotView by lazy {
val result = View(context) val result = PathDotView(context)
result.setBackgroundResource(R.drawable.accent_dot) result.setBackgroundResource(R.drawable.accent_dot)
result.mainColor = resources.getColorWithID(R.color.accent, context.theme)
result result
} }
@ -203,6 +209,7 @@ class PathActivity : PassphraseRequiredActionBarActivity() {
} }
private fun setUpViewHierarchy() { private fun setUpViewHierarchy() {
disableClipping()
val lineView = View(context) val lineView = View(context)
lineView.setBackgroundColor(resources.getColorWithID(R.color.text, context.theme)) lineView.setBackgroundColor(resources.getColorWithID(R.color.text, context.theme))
val lineViewHeight = when (location) { val lineViewHeight = when (location) {
@ -239,10 +246,14 @@ class PathActivity : PassphraseRequiredActionBarActivity() {
private fun expand() { private fun expand() {
dotView.animateSizeChange(R.dimen.path_row_dot_size, R.dimen.path_row_expanded_dot_size) dotView.animateSizeChange(R.dimen.path_row_dot_size, R.dimen.path_row_expanded_dot_size)
@ColorRes val startColorID = if (UiModeUtilities.isDayUiMode(context)) R.color.transparent_black_30 else R.color.black
GlowViewUtilities.animateShadowColorChange(context, dotView, startColorID, R.color.accent)
} }
private fun collapse() { private fun collapse() {
dotView.animateSizeChange(R.dimen.path_row_expanded_dot_size, R.dimen.path_row_dot_size) dotView.animateSizeChange(R.dimen.path_row_expanded_dot_size, R.dimen.path_row_dot_size)
@ColorRes val endColorID = if (UiModeUtilities.isDayUiMode(context)) R.color.transparent_black_30 else R.color.black
GlowViewUtilities.animateShadowColorChange(context, dotView, R.color.accent, endColorID)
} }
} }
// endregion // endregion

View File

@ -20,6 +20,7 @@ public class ConversationOptionsBottomSheet : BottomSheetDialogFragment() {
// if we want to use dialog fragments properly. // if we want to use dialog fragments properly.
lateinit var recipient: Recipient lateinit var recipient: Recipient
var onViewDetailsTapped: (() -> Unit?)? = null
var onBlockTapped: (() -> Unit)? = null var onBlockTapped: (() -> Unit)? = null
var onUnblockTapped: (() -> Unit)? = null var onUnblockTapped: (() -> Unit)? = null
var onDeleteTapped: (() -> Unit)? = null var onDeleteTapped: (() -> Unit)? = null
@ -30,18 +31,16 @@ public class ConversationOptionsBottomSheet : BottomSheetDialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
if (!this::recipient.isInitialized) { return dismiss() }
if (!this::recipient.isInitialized) {
dismiss()
return
}
if (!recipient.isGroupRecipient && !recipient.isLocalNumber) { if (!recipient.isGroupRecipient && !recipient.isLocalNumber) {
detailsTextView.visibility = View.VISIBLE
unblockTextView.visibility = if (recipient.isBlocked) View.VISIBLE else View.GONE unblockTextView.visibility = if (recipient.isBlocked) View.VISIBLE else View.GONE
blockTextView.visibility = if (recipient.isBlocked) View.GONE else View.VISIBLE blockTextView.visibility = if (recipient.isBlocked) View.GONE else View.VISIBLE
detailsTextView.setOnClickListener { onViewDetailsTapped?.invoke() }
blockTextView.setOnClickListener { onBlockTapped?.invoke() } blockTextView.setOnClickListener { onBlockTapped?.invoke() }
unblockTextView.setOnClickListener { onUnblockTapped?.invoke() } unblockTextView.setOnClickListener { onUnblockTapped?.invoke() }
} else {
detailsTextView.visibility = View.GONE
} }
deleteTextView.setOnClickListener { onDeleteTapped?.invoke() } deleteTextView.setOnClickListener { onDeleteTapped?.invoke() }
} }

View File

@ -0,0 +1,40 @@
package org.thoughtcrime.securesms.loki.dialogs
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.os.Bundle
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import kotlinx.android.synthetic.main.fragment_user_details_bottom_sheet.*
import kotlinx.android.synthetic.main.view_conversation.view.*
import network.loki.messenger.R
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.mms.GlideApp
public class UserDetailsBottomSheet : BottomSheetDialogFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_user_details_bottom_sheet, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val publicKey = arguments?.getString("publicKey") ?: return dismiss()
profilePictureView.publicKey = publicKey
profilePictureView.glide = GlideApp.with(this)
profilePictureView.isLarge = true
profilePictureView.update()
nameTextView.text = DatabaseFactory.getLokiUserDatabase(requireContext()).getDisplayName(publicKey) ?: "Anonymous"
publicKeyTextView.text = publicKey
copyButton.setOnClickListener {
val clipboard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("Session ID", publicKey)
clipboard.setPrimaryClip(clip)
Toast.makeText(requireContext(), R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show()
}
}
}

View File

@ -55,30 +55,8 @@ class ConversationView : LinearLayout {
accentView.setBackgroundResource(R.color.accent) accentView.setBackgroundResource(R.color.accent)
accentView.visibility = if (thread.unreadCount > 0) View.VISIBLE else View.INVISIBLE accentView.visibility = if (thread.unreadCount > 0) View.VISIBLE else View.INVISIBLE
} }
if (thread.recipient.isGroupRecipient) {
if ("Session Public Chat" == thread.recipient.name) {
profilePictureView.publicKey = ""
profilePictureView.additionalPublicKey = null
profilePictureView.isRSSFeed = true
} else {
val users = MentionsManager.shared.userPublicKeyCache[thread.threadId]?.toMutableList() ?: mutableListOf()
users.remove(TextSecurePreferences.getLocalNumber(context))
val masterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context)
if (masterPublicKey != null) {
users.remove(masterPublicKey)
}
val randomUsers = users.sorted() // Sort to provide a level of stability
profilePictureView.publicKey = randomUsers.getOrNull(0) ?: ""
profilePictureView.additionalPublicKey = randomUsers.getOrNull(1) ?: ""
profilePictureView.isRSSFeed = thread.recipient.name == "Loki News" || thread.recipient.name == "Session Updates"
}
} else {
profilePictureView.publicKey = thread.recipient.address.toString()
profilePictureView.additionalPublicKey = null
profilePictureView.isRSSFeed = false
}
profilePictureView.glide = glide profilePictureView.glide = glide
profilePictureView.update() profilePictureView.update(thread.recipient, thread.threadId)
val senderDisplayName = if (thread.recipient.isLocalNumber) context.getString(R.string.note_to_self) else if (!thread.recipient.name.isNullOrEmpty()) thread.recipient.name else thread.recipient.address.toString() val senderDisplayName = if (thread.recipient.isLocalNumber) context.getString(R.string.note_to_self) else if (!thread.recipient.name.isNullOrEmpty()) thread.recipient.name else thread.recipient.address.toString()
btnGroupNameDisplay.text = senderDisplayName btnGroupNameDisplay.text = senderDisplayName
timestampTextView.text = DateUtils.getBriefRelativeTimeSpanString(context, Locale.getDefault(), thread.date) timestampTextView.text = DateUtils.getBriefRelativeTimeSpanString(context, Locale.getDefault(), thread.date)

View File

@ -1,14 +1,18 @@
package org.thoughtcrime.securesms.loki.views package org.thoughtcrime.securesms.loki.views
import android.animation.FloatEvaluator
import android.animation.ValueAnimator
import android.content.Context import android.content.Context
import android.content.Context.LAYOUT_INFLATER_SERVICE import android.content.Context.LAYOUT_INFLATER_SERVICE
import android.os.Handler import android.os.Handler
import android.util.AttributeSet import android.util.AttributeSet
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.widget.LinearLayout
import android.widget.ScrollView import android.widget.ScrollView
import kotlinx.android.synthetic.main.view_fake_chat.view.* import kotlinx.android.synthetic.main.view_fake_chat.view.*
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.loki.utilities.disableClipping
class FakeChatView : ScrollView { class FakeChatView : ScrollView {
@ -38,7 +42,8 @@ class FakeChatView : ScrollView {
private fun setUpViewHierarchy() { private fun setUpViewHierarchy() {
val inflater = context.getSystemService(LAYOUT_INFLATER_SERVICE) as LayoutInflater val inflater = context.getSystemService(LAYOUT_INFLATER_SERVICE) as LayoutInflater
val contentView = inflater.inflate(R.layout.view_fake_chat, null) val contentView = inflater.inflate(R.layout.view_fake_chat, null) as LinearLayout
contentView.disableClipping()
addView(contentView) addView(contentView)
isVerticalScrollBarEnabled = false isVerticalScrollBarEnabled = false
} }
@ -47,8 +52,13 @@ class FakeChatView : ScrollView {
// region Animation // region Animation
fun startAnimating() { fun startAnimating() {
listOf( bubble1, bubble2, bubble3, bubble4, bubble5 ).forEach { it.alpha = 0.0f } listOf( bubble1, bubble2, bubble3, bubble4, bubble5 ).forEach { it.alpha = 0.0f }
fun show(view: View) { fun show(bubble: View) {
view.animate().alpha(1.0f).setDuration(animationDuration).start() val animation = ValueAnimator.ofObject(FloatEvaluator(), 0.0f, 1.0f)
animation.duration = animationDuration
animation.addUpdateListener { animator ->
bubble.alpha = animator.animatedValue as Float
}
animation.start()
} }
Handler().postDelayed({ Handler().postDelayed({
show(bubble1) show(bubble1)

View File

@ -0,0 +1,158 @@
package org.thoughtcrime.securesms.loki.views
import android.animation.ArgbEvaluator
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import android.view.ViewOutlineProvider
import android.widget.LinearLayout
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import network.loki.messenger.R
import org.thoughtcrime.securesms.loki.utilities.getColorWithID
import org.thoughtcrime.securesms.loki.utilities.toPx
interface GlowView {
var mainColor: Int
var sessionShadowColor: Int
}
object GlowViewUtilities {
fun animateColorChange(context: Context, view: GlowView, @ColorRes startColorID: Int, @ColorRes endColorID: Int) {
val startColor = context.resources.getColorWithID(startColorID, context.theme)
val endColor = context.resources.getColorWithID(endColorID, context.theme)
val animation = ValueAnimator.ofObject(ArgbEvaluator(), startColor, endColor)
animation.duration = 250
animation.addUpdateListener { animator ->
val color = animator.animatedValue as Int
view.mainColor = color
}
animation.start()
}
fun animateShadowColorChange(context: Context, view: GlowView, @ColorRes startColorID: Int, @ColorRes endColorID: Int) {
val startColor = context.resources.getColorWithID(startColorID, context.theme)
val endColor = context.resources.getColorWithID(endColorID, context.theme)
val animation = ValueAnimator.ofObject(ArgbEvaluator(), startColor, endColor)
animation.duration = 250
animation.addUpdateListener { animator ->
val color = animator.animatedValue as Int
view.sessionShadowColor = color
}
animation.start()
}
}
class PNModeView : LinearLayout, GlowView {
@ColorInt override var mainColor: Int = 0
set(newValue) { field = newValue; paint.color = newValue }
@ColorInt var strokeColor: Int = 0
set(newValue) { field = newValue; strokePaint.color = newValue }
@ColorInt override var sessionShadowColor: Int = 0
set(newValue) { field = newValue; paint.setShadowLayer(toPx(4, resources).toFloat(), 0.0f, 0.0f, newValue) }
private val paint: Paint by lazy {
val result = Paint()
result.style = Paint.Style.FILL
result.isAntiAlias = true
result
}
private val strokePaint: Paint by lazy {
val result = Paint()
result.style = Paint.Style.STROKE
result.isAntiAlias = true
result.strokeWidth = toPx(1, resources).toFloat()
result
}
// region Lifecycle
constructor(context: Context) : super(context) { }
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { }
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { }
init {
setWillNotDraw(false)
}
// endregion
// region Updating
override fun onDraw(c: Canvas) {
val w = width.toFloat()
val h = height.toFloat()
val r = resources.getDimension(R.dimen.pn_option_corner_radius)
c.drawRoundRect(0.0f, 0.0f, w, h, r, r, paint)
c.drawRoundRect(0.0f, 0.0f, w, h, r, r, strokePaint)
super.onDraw(c)
}
// endregion
}
class NewConversationButtonImageView : androidx.appcompat.widget.AppCompatImageView, GlowView {
@ColorInt override var mainColor: Int = 0
set(newValue) { field = newValue; paint.color = newValue }
@ColorInt override var sessionShadowColor: Int = 0
set(newValue) { field = newValue; paint.setShadowLayer(toPx(6, resources).toFloat(), 0.0f, 0.0f, newValue) }
private val paint: Paint by lazy {
val result = Paint()
result.style = Paint.Style.FILL
result.isAntiAlias = true
result
}
// region Lifecycle
constructor(context: Context) : super(context) { }
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { }
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { }
init {
setWillNotDraw(false)
}
// endregion
// region Updating
override fun onDraw(c: Canvas) {
val w = width.toFloat()
val h = height.toFloat()
c.drawCircle(w / 2, h / 2, w / 2, paint)
super.onDraw(c)
}
// endregion
}
class PathDotView : View, GlowView {
@ColorInt override var mainColor: Int = 0
set(newValue) { field = newValue; paint.color = newValue }
@ColorInt override var sessionShadowColor: Int = 0
set(newValue) { field = newValue; paint.setShadowLayer(toPx(4, resources).toFloat(), 0.0f, 0.0f, newValue) }
private val paint: Paint by lazy {
val result = Paint()
result.style = Paint.Style.FILL
result.isAntiAlias = true
result
}
// region Lifecycle
constructor(context: Context) : super(context) { }
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { }
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { }
init {
setWillNotDraw(false)
}
// endregion
// region Updating
override fun onDraw(c: Canvas) {
val w = width.toFloat()
val h = height.toFloat()
c.drawCircle(w / 2, h / 2, w / 2, paint)
super.onDraw(c)
}
// endregion
}

View File

@ -7,9 +7,7 @@ import android.animation.ValueAnimator
import android.content.Context import android.content.Context
import android.content.Context.VIBRATOR_SERVICE import android.content.Context.VIBRATOR_SERVICE
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Color
import android.graphics.PointF import android.graphics.PointF
import android.graphics.drawable.GradientDrawable
import android.os.Build import android.os.Build
import android.os.VibrationEffect import android.os.VibrationEffect
import android.os.VibrationEffect.DEFAULT_AMPLITUDE import android.os.VibrationEffect.DEFAULT_AMPLITUDE
@ -66,24 +64,25 @@ class NewConversationButtonSetView : RelativeLayout {
private val collapsedImageViewPosition by lazy { PointF((expandedSize - collapsedSize) / 2, (expandedSize - collapsedSize) / 2) } private val collapsedImageViewPosition by lazy { PointF((expandedSize - collapsedSize) / 2, (expandedSize - collapsedSize) / 2) }
private val imageView by lazy { private val imageView by lazy {
val result = ImageView(context) val result = NewConversationButtonImageView(context)
val size = collapsedSize.toInt() val size = collapsedSize.toInt()
result.layoutParams = LayoutParams(size, size) result.layoutParams = LayoutParams(size, size)
result.setBackgroundResource(R.drawable.new_conversation_button_background) result.setBackgroundResource(R.drawable.new_conversation_button_background)
val background = result.background as GradientDrawable @ColorRes val backgroundColorID = if (isMain) R.color.accent else R.color.new_conversation_button_collapsed_background
@ColorRes val backgroundColorID = if (isMain) @ColorRes val shadowColorID = if (isMain) {
R.color.accent else R.color.new_conversation_button_shadow
R.color.new_conversation_button_collapsed_background } else {
background.color = ColorStateList.valueOf(resources.getColorWithID(backgroundColorID, context.theme)) if (UiModeUtilities.isDayUiMode(context)) R.color.transparent_black_30 else R.color.black
}
result.mainColor = resources.getColorWithID(backgroundColorID, context.theme)
result.sessionShadowColor = resources.getColorWithID(shadowColorID, context.theme)
result.scaleType = ImageView.ScaleType.CENTER result.scaleType = ImageView.ScaleType.CENTER
result.setImageResource(iconID) result.setImageResource(iconID)
result.imageTintList = if (isMain) {
result.imageTintList = if (isMain)
// Always use white icon for the main button.
ColorStateList.valueOf(resources.getColorWithID(android.R.color.white, context.theme)) ColorStateList.valueOf(resources.getColorWithID(android.R.color.white, context.theme))
else } else {
ColorStateList.valueOf(resources.getColorWithID(R.color.text, context.theme)) ColorStateList.valueOf(resources.getColorWithID(R.color.text, context.theme))
}
result result
} }
@ -106,30 +105,21 @@ class NewConversationButtonSetView : RelativeLayout {
} }
fun expand() { fun expand() {
animateImageViewColorChange(R.color.new_conversation_button_collapsed_background, R.color.accent) GlowViewUtilities.animateColorChange(context, imageView, R.color.new_conversation_button_collapsed_background, R.color.accent)
@ColorRes val startShadowColorID = if (UiModeUtilities.isDayUiMode(context)) R.color.transparent_black_30 else R.color.black
GlowViewUtilities.animateShadowColorChange(context, imageView, startShadowColorID, R.color.new_conversation_button_shadow)
imageView.animateSizeChange(R.dimen.new_conversation_button_collapsed_size, R.dimen.new_conversation_button_expanded_size, animationDuration) imageView.animateSizeChange(R.dimen.new_conversation_button_collapsed_size, R.dimen.new_conversation_button_expanded_size, animationDuration)
animateImageViewPositionChange(collapsedImageViewPosition, expandedImageViewPosition) animateImageViewPositionChange(collapsedImageViewPosition, expandedImageViewPosition)
} }
fun collapse() { fun collapse() {
animateImageViewColorChange(R.color.accent, R.color.new_conversation_button_collapsed_background) GlowViewUtilities.animateColorChange(context, imageView, R.color.accent, R.color.new_conversation_button_collapsed_background)
@ColorRes val endShadowColorID = if (UiModeUtilities.isDayUiMode(context)) R.color.transparent_black_30 else R.color.black
GlowViewUtilities.animateShadowColorChange(context, imageView, R.color.new_conversation_button_shadow, endShadowColorID)
imageView.animateSizeChange(R.dimen.new_conversation_button_expanded_size, R.dimen.new_conversation_button_collapsed_size, animationDuration) imageView.animateSizeChange(R.dimen.new_conversation_button_expanded_size, R.dimen.new_conversation_button_collapsed_size, animationDuration)
animateImageViewPositionChange(expandedImageViewPosition, collapsedImageViewPosition) animateImageViewPositionChange(expandedImageViewPosition, collapsedImageViewPosition)
} }
private fun animateImageViewColorChange(@ColorRes startColorID: Int, @ColorRes endColorID: Int) {
val drawable = imageView.background as GradientDrawable
val startColor = resources.getColorWithID(startColorID, context.theme)
val endColor = resources.getColorWithID(endColorID, context.theme)
val animation = ValueAnimator.ofObject(ArgbEvaluator(), startColor, endColor)
animation.duration = animationDuration
animation.addUpdateListener { animator ->
val color = animator.animatedValue as Int
drawable.color = ColorStateList.valueOf(color)
}
animation.start()
}
private fun animateImageViewPositionChange(startPosition: PointF, endPosition: PointF) { private fun animateImageViewPositionChange(startPosition: PointF, endPosition: PointF) {
val animation = ValueAnimator.ofObject(PointFEvaluator(), startPosition, endPosition) val animation = ValueAnimator.ofObject(PointFEvaluator(), startPosition, endPosition)
animation.duration = animationDuration animation.duration = animationDuration
@ -170,6 +160,7 @@ class NewConversationButtonSetView : RelativeLayout {
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) { setUpViewHierarchy() } constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) { setUpViewHierarchy() }
private fun setUpViewHierarchy() { private fun setUpViewHierarchy() {
disableClipping()
// Set up session button // Set up session button
addView(sessionButton) addView(sessionButton)
sessionButton.alpha = 0.0f sessionButton.alpha = 0.0f

View File

@ -4,14 +4,30 @@ import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.graphics.Canvas
import android.graphics.Paint
import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.localbroadcastmanager.content.LocalBroadcastManager
import android.util.AttributeSet import android.util.AttributeSet
import android.view.View import android.view.View
import androidx.annotation.ColorInt
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.loki.utilities.getColorWithID
import org.thoughtcrime.securesms.loki.utilities.toPx
import org.whispersystems.signalservice.loki.api.onionrequests.OnionRequestAPI import org.whispersystems.signalservice.loki.api.onionrequests.OnionRequestAPI
class PathStatusView : View { class PathStatusView : View {
private val broadcastReceivers = mutableListOf<BroadcastReceiver>() private val broadcastReceivers = mutableListOf<BroadcastReceiver>()
@ColorInt var mainColor: Int = 0
set(newValue) { field = newValue; paint.color = newValue }
@ColorInt var sessionShadowColor: Int = 0
set(newValue) { field = newValue; paint.setShadowLayer(toPx(8, resources).toFloat(), 0.0f, 0.0f, newValue) }
private val paint: Paint by lazy {
val result = Paint()
result.style = Paint.Style.FILL
result.isAntiAlias = true
result
}
constructor(context: Context) : super(context) { constructor(context: Context) : super(context) {
initialize() initialize()
@ -31,6 +47,7 @@ class PathStatusView : View {
private fun initialize() { private fun initialize() {
update() update()
setWillNotDraw(false)
} }
override fun onAttachedToWindow() { override fun onAttachedToWindow() {
@ -70,8 +87,19 @@ class PathStatusView : View {
private fun update() { private fun update() {
if (OnionRequestAPI.paths.count() >= OnionRequestAPI.pathCount) { if (OnionRequestAPI.paths.count() >= OnionRequestAPI.pathCount) {
setBackgroundResource(R.drawable.accent_dot) setBackgroundResource(R.drawable.accent_dot)
mainColor = resources.getColorWithID(R.color.accent, context.theme)
sessionShadowColor = resources.getColorWithID(R.color.accent, context.theme)
} else { } else {
setBackgroundResource(R.drawable.paths_building_dot) setBackgroundResource(R.drawable.paths_building_dot)
mainColor = resources.getColorWithID(R.color.paths_building, context.theme)
sessionShadowColor = resources.getColorWithID(R.color.paths_building, context.theme)
} }
} }
override fun onDraw(c: Canvas) {
val w = width.toFloat()
val h = height.toFloat()
c.drawCircle(w / 2, h / 2, w / 2, paint)
super.onDraw(c)
}
} }

View File

@ -8,6 +8,7 @@ import android.view.View
import android.widget.ImageView import android.widget.ImageView
import android.widget.RelativeLayout import android.widget.RelativeLayout
import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.DiskCacheStrategy
import kotlinx.android.synthetic.main.view_conversation.view.*
import kotlinx.android.synthetic.main.view_profile_picture.view.* import kotlinx.android.synthetic.main.view_profile_picture.view.*
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto
@ -16,6 +17,7 @@ import org.thoughtcrime.securesms.loki.todo.JazzIdenticonDrawable
import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager
// TODO: Look into a better way of handling different sizes. Maybe an enum (with associated values) encapsulating the different modes? // TODO: Look into a better way of handling different sizes. Maybe an enum (with associated values) encapsulating the different modes?
@ -51,6 +53,32 @@ class ProfilePictureView : RelativeLayout {
// endregion // endregion
// region Updating // region Updating
fun update(recipient: Recipient, threadID: Long) {
if (recipient.isGroupRecipient) {
if ("Session Public Chat" == recipient.name) {
publicKey = ""
additionalPublicKey = null
isRSSFeed = true
} else {
val users = MentionsManager.shared.userPublicKeyCache[threadID]?.toMutableList() ?: mutableListOf()
users.remove(TextSecurePreferences.getLocalNumber(context))
val masterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context)
if (masterPublicKey != null) {
users.remove(masterPublicKey)
}
val randomUsers = users.sorted() // Sort to provide a level of stability
publicKey = randomUsers.getOrNull(0) ?: ""
additionalPublicKey = randomUsers.getOrNull(1) ?: ""
isRSSFeed = recipient.name == "Loki News" || recipient.name == "Session Updates"
}
} else {
publicKey = recipient.address.toString()
additionalPublicKey = null
isRSSFeed = false
}
update()
}
fun update() { fun update() {
val publicKey = publicKey ?: return val publicKey = publicKey ?: return
val additionalPublicKey = additionalPublicKey val additionalPublicKey = additionalPublicKey