Merge branch 'dev' of https://github.com/loki-project/session-android into permission-refactoring

This commit is contained in:
Anton Chekulaev 2020-09-09 17:12:46 +10:00
commit d51901ef98
85 changed files with 814 additions and 388 deletions

View File

@ -342,7 +342,8 @@
<activity <activity
android:name="org.thoughtcrime.securesms.PassphrasePromptActivity" android:name="org.thoughtcrime.securesms.PassphrasePromptActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:launchMode="singleTask" /> android:launchMode="singleTask"
android:theme="@style/Theme.Session.DayNight.NoActionBar"/>
<activity <activity
android:name="org.thoughtcrime.securesms.NewConversationActivity" android:name="org.thoughtcrime.securesms.NewConversationActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 366 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 528 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 472 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 250 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 387 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 356 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 310 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 239 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 458 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 706 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 581 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 250 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 650 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1019 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 919 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 846 B

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,14c1.66,0 2.99,-1.34 2.99,-3L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6c0,1.66 1.34,3 3,3zM17.3,11c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11L5,11c0,3.41 2.72,6.23 6,6.72L11,21h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,7c2.76,0 5,2.24 5,5 0,0.65 -0.13,1.26 -0.36,1.83l2.92,2.92c1.51,-1.26 2.7,-2.89 3.43,-4.75 -1.73,-4.39 -6,-7.5 -11,-7.5 -1.4,0 -2.74,0.25 -3.98,0.7l2.16,2.16C10.74,7.13 11.35,7 12,7zM2,4.27l2.28,2.28 0.46,0.46C3.08,8.3 1.78,10.02 1,12c1.73,4.39 6,7.5 11,7.5 1.55,0 3.03,-0.3 4.38,-0.84l0.42,0.42L19.73,22 21,20.73 3.27,3 2,4.27zM7.53,9.8l1.55,1.55c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.66 1.34,3 3,3 0.22,0 0.44,-0.03 0.65,-0.08l1.55,1.55c-0.67,0.33 -1.41,0.53 -2.2,0.53 -2.76,0 -5,-2.24 -5,-5 0,-0.79 0.2,-1.53 0.53,-2.2zM11.84,9.02l3.15,3.15 0.02,-0.16c0,-1.66 -1.34,-3 -3,-3l-0.17,0.01z"/>
</vector>

View File

@ -3,7 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"> android:shape="rectangle">
<solid android:color="@color/unimportant" /> <solid android:color="@color/profile_picture_background" />
<corners android:radius="38dp" /> <corners android:radius="38dp" />

View File

@ -7,5 +7,5 @@
<corners android:radius="38dp" /> <corners android:radius="38dp" />
<stroke android:width="@dimen/profile_picture_border_thickness" android:color="@color/border" /> <stroke android:width="@dimen/profile_picture_border_thickness" android:color="@color/profile_picture_border" />
</shape> </shape>

View File

@ -3,9 +3,9 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"> android:shape="rectangle">
<solid android:color="@color/unimportant" /> <solid android:color="@color/profile_picture_background" />
<corners android:radius="23dp" /> <corners android:radius="23dp" />
<stroke android:width="@dimen/profile_picture_border_thickness" android:color="@color/border" /> <stroke android:width="@dimen/profile_picture_border_thickness" android:color="@color/profile_picture_border" />
</shape> </shape>

View File

@ -7,5 +7,5 @@
<corners android:radius="23dp" /> <corners android:radius="23dp" />
<stroke android:width="@dimen/profile_picture_border_thickness" android:color="@color/border" /> <stroke android:width="@dimen/profile_picture_border_thickness" android:color="@color/profile_picture_border" />
</shape> </shape>

View File

@ -3,9 +3,9 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"> android:shape="rectangle">
<solid android:color="#353535" /> <solid android:color="@color/profile_picture_background" />
<corners android:radius="23dp" /> <corners android:radius="23dp" />
<stroke android:width="@dimen/profile_picture_border_thickness" android:color="@color/border" /> <stroke android:width="@dimen/profile_picture_border_thickness" android:color="@color/profile_picture_border" />
</shape> </shape>

View File

@ -3,9 +3,9 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"> android:shape="rectangle">
<solid android:color="@color/unimportant" /> <solid android:color="@color/profile_picture_background" />
<corners android:radius="18dp" /> <corners android:radius="18dp" />
<stroke android:width="@dimen/profile_picture_border_thickness" android:color="@color/border" /> <stroke android:width="@dimen/profile_picture_border_thickness" android:color="@color/profile_picture_border" />
</shape> </shape>

View File

@ -7,5 +7,5 @@
<corners android:radius="18dp" /> <corners android:radius="18dp" />
<stroke android:width="@dimen/profile_picture_border_thickness" android:color="@color/border" /> <stroke android:width="@dimen/profile_picture_border_thickness" android:color="@color/profile_picture_border" />
</shape> </shape>

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,5 +9,5 @@
android:visibility="gone" android:visibility="gone"
app:doc_titleColor="?conversation_item_sent_text_primary_color" app:doc_titleColor="?conversation_item_sent_text_primary_color"
app:doc_captionColor="?conversation_item_sent_text_secondary_color" app:doc_captionColor="?conversation_item_sent_text_secondary_color"
app:doc_downloadButtonTint="?conversation_item_sent_download_icon_color" app:doc_downloadButtonTint="?android:colorControlNormal"
tools:visibility="visible"/> tools:visibility="visible"/>

View File

@ -9,13 +9,20 @@
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"
android:drawableStart="?attr/menu_block_icon" android:drawableStart="?attr/menu_block_icon"
android:text="@string/RecipientPreferenceActivity_block" android:text="@string/RecipientPreferenceActivity_block"
android:visibility="gone" android:visibility="gone"
tools:visibility="visible"/> tools:visibility="visible" />
<TextView <TextView
android:id="@+id/unblockTextView" android:id="@+id/unblockTextView"
@ -23,7 +30,7 @@
android:drawableStart="?attr/menu_accept_icon" android:drawableStart="?attr/menu_accept_icon"
android:text="@string/RecipientPreferenceActivity_unblock" android:text="@string/RecipientPreferenceActivity_unblock"
android:visibility="gone" android:visibility="gone"
tools:visibility="visible"/> tools:visibility="visible" />
<TextView <TextView
android:id="@+id/deleteTextView" android:id="@+id/deleteTextView"

View File

@ -9,7 +9,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:gravity="center" android:gravity="center"
android:background="?attr/search_background" android:background="?android:windowBackground"
android:visibility="gone" /> android:visibility="gone" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView

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

@ -13,8 +13,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="match_parent" android:layout_width="match_parent"
android:orientation="vertical" android:orientation="vertical"
android:padding="10dp" android:padding="10dp">
android:background="?attr/invite_background">
<ImageView android:id="@+id/heart" <ImageView android:id="@+id/heart"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@ -17,7 +17,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="6dp" android:layout_marginStart="6dp"
android:padding="6dp" android:padding="6dp"
android:src="@drawable/ic_search_white_24dp" android:src="@drawable/ic_baseline_search_24"
android:tint="?media_keyboard_button_color" android:tint="?media_keyboard_button_color"
android:background="?selectableItemBackgroundBorderless" android:background="?selectableItemBackgroundBorderless"
android:visibility="invisible" android:visibility="invisible"
@ -98,7 +98,7 @@
android:tint="?media_keyboard_button_color" android:tint="?media_keyboard_button_color"
android:visibility="gone" android:visibility="gone"
android:background="?selectableItemBackground" android:background="?selectableItemBackground"
app:srcCompat="@drawable/ic_plus_24" app:srcCompat="@drawable/ic_baseline_add_24"
app:layout_constraintBottom_toBottomOf="@id/media_keyboard_tabs" app:layout_constraintBottom_toBottomOf="@id/media_keyboard_tabs"
app:layout_constraintTop_toTopOf="@id/media_keyboard_tabs" app:layout_constraintTop_toTopOf="@id/media_keyboard_tabs"
app:layout_constraintEnd_toStartOf="@id/media_keyboard_backspace_backup" app:layout_constraintEnd_toStartOf="@id/media_keyboard_backspace_backup"

View File

@ -6,7 +6,8 @@
android:id="@+id/prompt_layout" android:id="@+id/prompt_layout"
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"
tools:context="org.thoughtcrime.securesms.PassphrasePromptActivity">
<View android:id="@+id/shim" <View android:id="@+id/shim"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -25,7 +26,7 @@
android:id="@+id/toolbar" android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
android:layout_marginTop="20dp"> android:layout_alignParentTop="true">
<ImageView <ImageView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -78,7 +79,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="60dp" android:layout_marginBottom="60dp"
tools:visibility="gone"> tools:visibility="visible">
<EditText android:id="@+id/passphrase_edit" <EditText android:id="@+id/passphrase_edit"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -88,55 +89,56 @@
android:layout_marginEnd="50dp" android:layout_marginEnd="50dp"
android:singleLine="true" android:singleLine="true"
android:paddingStart="10dp" android:paddingStart="10dp"
android:paddingEnd="40dp"/> android:paddingEnd="40dp"
tools:text="password"/>
<!-- <org.thoughtcrime.securesms.components.AnimatingToggle--> <org.thoughtcrime.securesms.components.AnimatingToggle
<!-- android:id="@+id/button_toggle"--> android:id="@+id/button_toggle"
<!-- android:layout_width="wrap_content"--> android:layout_width="wrap_content"
<!-- android:layout_height="wrap_content"--> android:layout_height="wrap_content"
<!-- android:layout_alignRight="@+id/passphrase_edit"--> android:layout_alignRight="@+id/passphrase_edit"
<!-- android:layout_centerVertical="true"--> android:layout_centerVertical="true"
<!-- android:layout_gravity="center"--> android:layout_gravity="center"
<!-- android:gravity="center">--> android:gravity="center">
<!-- <ImageButton android:id="@+id/passphrase_visibility"--> <ImageButton android:id="@+id/passphrase_visibility"
<!-- android:src="?ic_visibility"--> android:src="?ic_visibility_on"
<!-- android:background="@drawable/touch_highlight_background"--> android:background="@drawable/touch_highlight_background"
<!-- android:layout_width="wrap_content"--> android:layout_width="wrap_content"
<!-- android:layout_height="wrap_content"--> android:layout_height="wrap_content"
<!-- android:paddingStart="8dp"--> android:paddingStart="8dp"
<!-- android:paddingEnd="8dp"--> android:paddingEnd="8dp"
<!-- android:paddingTop="3dp"--> android:paddingTop="3dp"
<!-- android:paddingBottom="3dp"--> android:paddingBottom="3dp"
<!-- android:layout_centerVertical="true" />--> android:layout_centerVertical="true" />
<!-- <ImageButton android:id="@+id/passphrase_visibility_off"--> <ImageButton android:id="@+id/passphrase_visibility_off"
<!-- android:src="?ic_visibility_off"--> android:src="?ic_visibility_off"
<!-- android:background="@drawable/touch_highlight_background"--> android:background="@drawable/touch_highlight_background"
<!-- android:layout_width="wrap_content"--> android:layout_width="wrap_content"
<!-- android:layout_height="wrap_content"--> android:layout_height="wrap_content"
<!-- android:paddingStart="8dp"--> android:paddingStart="8dp"
<!-- android:paddingEnd="8dp"--> android:paddingEnd="8dp"
<!-- android:paddingTop="3dp"--> android:paddingTop="3dp"
<!-- android:paddingBottom="3dp"--> android:paddingBottom="3dp"
<!-- android:layout_centerVertical="true" />--> android:layout_centerVertical="true" />
<!-- </org.thoughtcrime.securesms.components.AnimatingToggle>--> </org.thoughtcrime.securesms.components.AnimatingToggle>
<!-- <ImageButton android:id="@+id/ok_button"--> <ImageButton android:id="@+id/ok_button"
<!-- android:src="?ic_arrow_forward"--> android:src="?ic_arrow_forward"
<!-- android:contentDescription="@string/PassphrasePromptActivity_ok_button_content_description"--> android:contentDescription="@string/PassphrasePromptActivity_ok_button_content_description"
<!-- android:background="@null"--> android:background="@null"
<!-- android:text="@string/prompt_passphrase_activity__unlock"--> android:text="@string/prompt_passphrase_activity__unlock"
<!-- android:layout_width="wrap_content"--> android:layout_width="wrap_content"
<!-- android:layout_height="wrap_content"--> android:layout_height="wrap_content"
<!-- android:layout_alignParentEnd="true"--> android:layout_alignParentEnd="true"
<!-- android:layout_centerVertical="true"--> android:layout_centerVertical="true"
<!-- android:textAppearance="?android:attr/textAppearanceMedium"--> android:textAppearance="?android:attr/textAppearanceMedium"
<!-- android:paddingStart="5dp"--> android:paddingStart="5dp"
<!-- android:paddingTop="5dp"--> android:paddingTop="5dp"
<!-- android:paddingEnd="10dp"--> android:paddingEnd="10dp"
<!-- android:paddingBottom="5dp"/>--> android:paddingBottom="5dp"/>
</RelativeLayout> </RelativeLayout>
</LinearLayout> </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

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto">
<RelativeLayout <RelativeLayout
android:id="@+id/doubleModeImageViewContainer" android:id="@+id/doubleModeImageViewContainer"

View File

@ -7,6 +7,8 @@
<color name="text">#000000</color> <color name="text">#000000</color>
<color name="destructive">#f26f55</color> <color name="destructive">#f26f55</color>
<color name="unimportant">#606060</color> <color name="unimportant">#606060</color>
<color name="profile_picture_border">#23000000</color>
<color name="profile_picture_background">#B0B0B0</color>
<color name="cell_background">#FCFCFC</color> <color name="cell_background">#FCFCFC</color>
<color name="cell_selected">#DFDFDF</color> <color name="cell_selected">#DFDFDF</color>
<color name="navigation_bar_background">#FCFCFC</color> <color name="navigation_bar_background">#FCFCFC</color>
@ -28,7 +30,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

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"> <resources>
<style name="Theme.Session.Light" parent="Base.Theme.Session"> <style name="Theme.Session.Light" parent="Base.Theme.Session">
<item name="android:navigationBarColor">?android:navigationBarColor</item> <item name="android:navigationBarColor">?android:navigationBarColor</item>
@ -23,6 +23,8 @@
<item name="conversation_input_background">@drawable/compose_background_light</item> <item name="conversation_input_background">@drawable/compose_background_light</item>
<item name="quick_camera_icon">@drawable/ic_outline_photo_camera_24</item> <item name="quick_camera_icon">@drawable/ic_outline_photo_camera_24</item>
<item name="media_keyboard_button_color">@color/core_grey_60</item>
</style> </style>
<style name="Theme.Session.DayNight" parent="Theme.Session.Light"> <style name="Theme.Session.DayNight" parent="Theme.Session.Light">
@ -39,8 +41,6 @@
<item name="alertDialogTheme">@style/AppCompatAlertDialogStyleLight</item> <item name="alertDialogTheme">@style/AppCompatAlertDialogStyleLight</item>
<item name="android:alertDialogTheme">@style/AppCompatDialogStyleLight</item> <item name="android:alertDialogTheme">@style/AppCompatDialogStyleLight</item>
<item name="compose_icon_tint">@color/core_grey_60</item>
<item name="conversation_list_item_background">@drawable/conversation_list_item_background</item> <item name="conversation_list_item_background">@drawable/conversation_list_item_background</item>
<item name="conversation_list_item_contact_color">@color/core_grey_90</item> <item name="conversation_list_item_contact_color">@color/core_grey_90</item>
<item name="conversation_list_item_subject_color">@color/core_grey_60</item> <item name="conversation_list_item_subject_color">@color/core_grey_60</item>
@ -74,8 +74,6 @@
<item name="conversation_sticker_footer_icon_color">@color/core_grey_60</item> <item name="conversation_sticker_footer_icon_color">@color/core_grey_60</item>
<item name="conversation_sticker_author_color">@color/core_grey_95</item> <item name="conversation_sticker_author_color">@color/core_grey_95</item>
<item name="contact_list_divider">@drawable/contact_list_divider_light</item>
<item name="verification_background">@color/core_grey_05</item> <item name="verification_background">@color/core_grey_05</item>
<item name="emoji_tab_strip_background">@color/cell_background</item> <item name="emoji_tab_strip_background">@color/cell_background</item>
@ -101,21 +99,13 @@
<item name="conversation_item_sent_text_primary_color">@color/core_grey_90</item> <item name="conversation_item_sent_text_primary_color">@color/core_grey_90</item>
<item name="conversation_item_sent_text_secondary_color">@color/transparent_black_90</item> <item name="conversation_item_sent_text_secondary_color">@color/transparent_black_90</item>
<item name="conversation_item_sent_icon_color">@color/core_grey_60</item> <item name="conversation_item_sent_icon_color">@color/core_grey_60</item>
<item name="conversation_item_sent_download_icon_color">@color/core_grey_60</item>
<item name="conversation_item_sent_text_indicator_tab_color">#99000000</item> <item name="conversation_item_sent_text_indicator_tab_color">#99000000</item>
<item name="conversation_item_received_text_primary_color">@color/text</item> <item name="conversation_item_received_text_primary_color">@color/text</item>
<item name="conversation_item_received_text_secondary_color">@color/text</item> <item name="conversation_item_received_text_secondary_color">@color/text</item>
<item name="conversation_item_update_text_color">@color/core_grey_60</item>
<item name="conversation_item_last_seen_text_color">@color/core_grey_90</item>
<item name="conversation_item_last_seen_line_color">@color/core_grey_60</item>
<item name="conversation_item_date_line_color">@color/core_grey_25</item>
<item name="conversation_item_quote_text_color">@color/core_grey_90</item>
<item name="conversation_item_sticky_date_background">@drawable/sticky_date_header_background_light</item> <item name="conversation_item_sticky_date_background">@drawable/sticky_date_header_background_light</item>
<item name="conversation_item_sticky_date_text_color">@color/core_grey_60</item> <item name="conversation_item_sticky_date_text_color">@color/core_grey_60</item>
<item name="conversation_item_image_outline_color">@color/transparent_black_30</item> <item name="conversation_item_image_outline_color">@color/transparent_black_30</item>
<item name="quick_mic_icon">@drawable/ic_mic_grey600_24dp</item>
<item name="conversation_item_sent_indicator_text_background">@drawable/conversation_item_sent_indicator_text_shape</item> <item name="conversation_item_sent_indicator_text_background">@drawable/conversation_item_sent_indicator_text_shape</item>
<item name="dialog_info_icon">@drawable/ic_info_outline_light</item> <item name="dialog_info_icon">@drawable/ic_info_outline_light</item>
@ -124,34 +114,14 @@
<item name="device_link_item_card_background">@color/device_link_item_background_light</item> <item name="device_link_item_card_background">@color/device_link_item_background_light</item>
<item name="mediapicker_image_outline">@drawable/mediapicker_item_border_light</item>
<item name="import_export_item_background_color">@color/import_export_item_background_light</item>
<item name="import_export_item_background_shadow_color">@color/import_export_item_background_shadow_light</item>
<item name="import_export_item_card_background">@drawable/clickable_card_light</item>
<item name="linkpreview_background_color">@color/core_white</item> <item name="linkpreview_background_color">@color/core_white</item>
<item name="linkpreview_primary_text_color">@color/core_black</item> <item name="linkpreview_primary_text_color">@color/core_black</item>
<item name="linkpreview_secondary_text_color">@color/core_grey_60</item> <item name="linkpreview_secondary_text_color">@color/core_grey_60</item>
<item name="linkpreview_divider_color">@color/core_grey_25</item> <item name="linkpreview_divider_color">@color/core_grey_25</item>
<item name="media_keyboard_button_color">@color/core_grey_60</item>
<item name="menu_new_conversation_icon">@drawable/ic_add_white_24dp</item>
<item name="menu_group_icon">@drawable/ic_group_white_24dp</item>
<item name="menu_search_icon">@drawable/ic_search_white_24dp</item>
<item name="menu_call_icon">@drawable/ic_call_white_24dp</item>
<item name="menu_unlock_icon">@drawable/ic_unlocked_white_24dp</item>
<item name="menu_lock_icon">@drawable/ic_lock_white_24dp</item>
<item name="menu_lock_icon_small">@drawable/ic_lock_white_18dp</item>
<item name="menu_refresh_directory">@drawable/ic_refresh_white_24dp</item>
<item name="menu_info_icon">@drawable/ic_info_outline_white_24dp</item>
<item name="conversation_icon_attach_audio">@drawable/ic_audio_light</item> <item name="conversation_icon_attach_audio">@drawable/ic_audio_light</item>
<item name="conversation_icon_attach_video">@drawable/ic_video_light</item> <item name="conversation_icon_attach_video">@drawable/ic_video_light</item>
<item name="reminder_header_background">#ff1d85d7</item>
<item name="sticker_management_icon">@drawable/sticker_button_light</item> <item name="sticker_management_icon">@drawable/sticker_button_light</item>
<item name="sticker_management_divider_color">@color/core_grey_15</item> <item name="sticker_management_divider_color">@color/core_grey_15</item>
<item name="sticker_management_empty_background_color">@color/core_grey_05</item> <item name="sticker_management_empty_background_color">@color/core_grey_05</item>
@ -165,17 +135,9 @@
<item name="pref_icon_tint">@color/textsecure_primary_dark</item> <item name="pref_icon_tint">@color/textsecure_primary_dark</item>
<item name="pref_divider">@drawable/preference_divider_light</item>
<item name="quote_missing_icon_color">@color/core_grey_60</item>
<item name="group_members_dialog_icon">@drawable/ic_group_grey600_24dp</item> <item name="group_members_dialog_icon">@drawable/ic_group_grey600_24dp</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay.Fix</item> <item name="preferenceTheme">@style/PreferenceThemeOverlay.Fix</item>
<item name="search_toolbar_background">@color/white</item>
<item name="search_background">@color/white</item>
<item name="invite_background">@color/signal_primary</item>
<item name="shared_contact_details_header_background">@color/grey_100</item> <item name="shared_contact_details_header_background">@color/grey_100</item>
<item name="shared_contact_details_titlebar">@color/grey_400</item> <item name="shared_contact_details_titlebar">@color/grey_400</item>
<item name="shared_contact_item_button_color">@color/core_grey_02</item> <item name="shared_contact_item_button_color">@color/core_grey_02</item>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"> <resources>
<style name="Theme.Session.DayNight" parent="Theme.Session.Light"> <style name="Theme.Session.DayNight" parent="Theme.Session.Light">
<item name="android:statusBarColor">@color/transparent</item> <item name="android:statusBarColor">@color/transparent</item>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"> <resources>
<style name="Theme.Session.ForceDark" parent="Base.Theme.Session.ForceDark"> <style name="Theme.Session.ForceDark" parent="Base.Theme.Session.ForceDark">
<item name="android:windowLightStatusBar">false</item> <item name="android:windowLightStatusBar">false</item>

View File

@ -19,16 +19,13 @@
<attr name="conversation_list_toolbar_background" format="reference"/> <attr name="conversation_list_toolbar_background" format="reference"/>
<attr name="conversation_list_typing_tint" format="color"/> <attr name="conversation_list_typing_tint" format="color"/>
<attr name="conversation_sent_card_background" format="reference|color"/>
<attr name="conversation_group_member_name" format="reference|color"/> <attr name="conversation_group_member_name" format="reference|color"/>
<attr name="conversation_received_card_background" format="reference|color"/>
<attr name="fab_color" format="reference|color" /> <attr name="fab_color" format="reference|color" />
<attr name="lower_right_divet" format="reference" /> <attr name="lower_right_divet" format="reference" />
<attr name="centered_app_title_color" format="reference|color" />
<attr name="ic_arrow_forward" format="reference" /> <attr name="ic_arrow_forward" format="reference" />
<attr name="ic_visibility" format="reference" /> <attr name="ic_visibility_on" format="reference" />
<attr name="ic_visibility_off" format="reference" /> <attr name="ic_visibility_off" format="reference" />
<attr name="conversation_background" format="reference|color"/> <attr name="conversation_background" format="reference|color"/>
@ -76,9 +73,6 @@
<attr name="quick_camera_icon" format="reference"/> <attr name="quick_camera_icon" format="reference"/>
<attr name="quick_mic_icon" format="reference"/> <attr name="quick_mic_icon" format="reference"/>
<attr name="compose_icon_tint" />
<attr name="conversation_item_background" format="reference"/>
<attr name="conversation_item_bubble_background" format="reference|color"/> <attr name="conversation_item_bubble_background" format="reference|color"/>
<attr name="conversation_item_sent_text_primary_color" format="reference|color"/> <attr name="conversation_item_sent_text_primary_color" format="reference|color"/>
<attr name="conversation_item_sent_text_secondary_color" format="reference|color"/> <attr name="conversation_item_sent_text_secondary_color" format="reference|color"/>
@ -87,12 +81,6 @@
<attr name="conversation_item_sent_text_indicator_tab_color" format="reference|color"/> <attr name="conversation_item_sent_text_indicator_tab_color" format="reference|color"/>
<attr name="conversation_item_sent_indicator_text_background" format="reference" /> <attr name="conversation_item_sent_indicator_text_background" format="reference" />
<attr name="conversation_item_sent_icon_color" format="color" /> <attr name="conversation_item_sent_icon_color" format="color" />
<attr name="conversation_item_sent_download_icon_color" format="reference|color"/>
<attr name="conversation_item_update_text_color" format="reference"/>
<attr name="conversation_item_last_seen_text_color" format="reference"/>
<attr name="conversation_item_last_seen_line_color" format="reference"/>
<attr name="conversation_item_date_line_color" format="reference"/>
<attr name="conversation_item_quote_text_color" format="reference"/>
<attr name="conversation_item_sticky_date_background" format="reference" /> <attr name="conversation_item_sticky_date_background" format="reference" />
<attr name="conversation_item_sticky_date_text_color" format="color" /> <attr name="conversation_item_sticky_date_text_color" format="color" />
<attr name="conversation_item_image_outline_color" format="color" /> <attr name="conversation_item_image_outline_color" format="color" />
@ -104,45 +92,21 @@
<attr name="conversation_icon_attach_audio" format="reference"/> <attr name="conversation_icon_attach_audio" format="reference"/>
<attr name="conversation_icon_attach_video" format="reference" /> <attr name="conversation_icon_attach_video" format="reference" />
<attr name="contact_selection_push_user" format="reference|color" />
<attr name="contact_selection_lay_user" format="reference|color" />
<attr name="contact_selection_label_text" format="reference|color" />
<attr name="contact_selection_header_text" format="reference|color" />
<attr name="device_link_item_card_background" format="reference|color" /> <attr name="device_link_item_card_background" format="reference|color" />
<attr name="mediapicker_image_outline" format="reference" />
<attr name="import_export_item_background_color" format="reference|color" />
<attr name="import_export_item_background_shadow_color" format="reference|color" />
<attr name="import_export_item_card_background" format="reference" />
<attr name="invite_background" format="color"/>
<attr name="linkpreview_background_color" format="color" /> <attr name="linkpreview_background_color" format="color" />
<attr name="linkpreview_primary_text_color" format="color" /> <attr name="linkpreview_primary_text_color" format="color" />
<attr name="linkpreview_secondary_text_color" format="color" /> <attr name="linkpreview_secondary_text_color" format="color" />
<attr name="linkpreview_divider_color" format="color" /> <attr name="linkpreview_divider_color" format="color" />
<attr name="reminder_header_background" format="color"/>
<attr name="media_keyboard_button_color" format="color" /> <attr name="media_keyboard_button_color" format="color" />
<attr name="menu_new_conversation_icon" format="reference" />
<attr name="menu_search_icon" format="reference" />
<attr name="menu_call_icon" format="reference" />
<attr name="menu_popup_expand" format="reference"/> <attr name="menu_popup_expand" format="reference"/>
<attr name="menu_unlock_icon" format="reference" />
<attr name="menu_lock_icon" format="reference" />
<attr name="menu_lock_icon_small" format="reference" />
<attr name="menu_trash_icon" format="reference" /> <attr name="menu_trash_icon" format="reference" />
<attr name="menu_selectall_icon" format="reference" /> <attr name="menu_selectall_icon" format="reference" />
<attr name="menu_group_icon" format="reference" />
<attr name="menu_split_icon" format="reference" /> <attr name="menu_split_icon" format="reference" />
<attr name="menu_accept_icon" format="reference" /> <attr name="menu_accept_icon" format="reference" />
<attr name="menu_refresh_directory" format="reference" />
<attr name="menu_copy_icon" format="reference" /> <attr name="menu_copy_icon" format="reference" />
<attr name="menu_info_icon" format="reference" />
<attr name="menu_forward_icon" format="reference" /> <attr name="menu_forward_icon" format="reference" />
<attr name="menu_save_icon" format="reference" /> <attr name="menu_save_icon" format="reference" />
<attr name="menu_reply_icon" format="reference" /> <attr name="menu_reply_icon" format="reference" />
@ -152,40 +116,23 @@
<attr name="pref_icon_tint" format="color"/> <attr name="pref_icon_tint" format="color"/>
<attr name="pref_divider" format="reference" />
<attr name="quote_missing_icon_color" format="color" />
<attr name="home_gradient_start" format="color|reference" /> <attr name="home_gradient_start" format="color|reference" />
<attr name="home_gradient_end" format="color|reference" /> <attr name="home_gradient_end" format="color|reference" />
<attr name="message_received_background_color" format="color|reference" /> <attr name="message_received_background_color" format="color|reference" />
<attr name="message_sent_background_color" format="color|reference" /> <attr name="message_sent_background_color" format="color|reference" />
<declare-styleable name="CustomDefaultPreference">
<attr name="custom_pref_toggle" format="string"/>
</declare-styleable>
<attr name="group_members_dialog_icon" format="reference"/> <attr name="group_members_dialog_icon" format="reference"/>
<attr name="lockscreen_watermark" format="reference" />
<attr name="recipient_preference_blocked" format="color"/>
<attr name="verification_background" format="color"/> <attr name="verification_background" format="color"/>
<attr name="media_overview_toolbar_background" format="color"/> <attr name="media_overview_toolbar_background" format="color"/>
<attr name="media_overview_toolbar_foreground" format="color"/>
<attr name="media_overview_header_foreground" format="color"/> <attr name="media_overview_header_foreground" format="color"/>
<attr name="media_overview_document_primary" format="color"/>
<attr name="media_overview_document_secondary" format="color"/>
<attr name="shared_contact_details_header_background" format="color"/> <attr name="shared_contact_details_header_background" format="color"/>
<attr name="shared_contact_details_titlebar" format="color"/> <attr name="shared_contact_details_titlebar" format="color"/>
<attr name="shared_contact_item_button_color" format="color"/> <attr name="shared_contact_item_button_color" format="color"/>
<attr name="search_toolbar_background" format="color"/>
<attr name="search_background" format="color"/>
<attr name="sticker_management_icon" format="reference" /> <attr name="sticker_management_icon" format="reference" />
<attr name="sticker_management_divider_color" format="color" /> <attr name="sticker_management_divider_color" format="color" />
<attr name="sticker_management_empty_background_color" format="color" /> <attr name="sticker_management_empty_background_color" format="color" />
@ -197,15 +144,8 @@
<attr name="tooltip_default_color" format="reference|color" /> <attr name="tooltip_default_color" format="reference|color" />
<attr name="contact_list_divider" format="reference"/> <declare-styleable name="CustomDefaultPreference">
<attr name="custom_pref_toggle" format="string"/>
<attr name="login_top_background" format="color"/>
<attr name="login_floating_background" format="reference"/>
<declare-styleable name="ColorPreference">
<attr name="itemLayout" format="reference" />
<attr name="choices" format="reference" />
<attr name="numColumns" format="integer" />
</declare-styleable> </declare-styleable>
<declare-styleable name="AvatarImageView"> <declare-styleable name="AvatarImageView">
@ -270,22 +210,12 @@
<attr name="emoji_maxLength" format="integer" /> <attr name="emoji_maxLength" format="integer" />
</declare-styleable> </declare-styleable>
<declare-styleable name="RingtonePreference">
<attr name="showAdd" format="boolean" />
<attr name="summaryHasRingtone" format="string|reference" />
</declare-styleable>
<declare-styleable name="ColorPickerPreference"> <declare-styleable name="ColorPickerPreference">
<attr name="currentColor" format="reference" /> <attr name="currentColor" format="reference" />
<attr name="colors" format="reference" /> <attr name="colors" format="reference" />
<attr name="sortColors" format="boolean|reference" /> <attr name="sortColors" format="boolean|reference" />
<attr name="colorDescriptions" format="reference" /> <attr name="colorDescriptions" format="reference" />
<attr name="columns" format="integer|reference" /> <attr name="columns" format="integer|reference" />
<attr name="colorSize" format="enum|reference"> <attr name="colorSize" format="enum|reference">
<enum name="large" value="1" /> <enum name="large" value="1" />
<enum name="small" value="2" /> <enum name="small" value="2" />

View File

@ -7,6 +7,8 @@
<color name="text">#FFFFFF</color> <color name="text">#FFFFFF</color>
<color name="destructive">#FF453A</color> <color name="destructive">#FF453A</color>
<color name="unimportant">#D8D8D8</color> <color name="unimportant">#D8D8D8</color>
<color name="profile_picture_border">#23FFFFFF</color>
<color name="profile_picture_background">#353535</color>
<color name="border">#979797</color> <color name="border">#979797</color>
<color name="cell_background">#1B1B1B</color> <color name="cell_background">#1B1B1B</color>
<color name="cell_selected">#0C0C0C</color> <color name="cell_selected">#0C0C0C</color>
@ -25,9 +27,17 @@
<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>
<array name="profile_picture_placeholder_colors">
<item>#5ff8b0</item>
<item>#26cdb9</item>
<item>#f3c615</item>
<item>#fcac5a</item>
</array>
<!-- Session --> <!-- Session -->
<!-- Loki --> <!-- Loki -->

View File

@ -29,9 +29,9 @@
<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">8dp</dimen>
<dimen name="dialog_button_corner_radius">4dp</dimen> <dimen name="dialog_button_corner_radius">4dp</dimen>
<dimen name="pn_option_corner_radius">8dp</dimen> <dimen name="pn_option_corner_radius">8dp</dimen>
<dimen name="path_status_view_size">8dp</dimen> <dimen name="path_status_view_size">8dp</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

@ -6,7 +6,6 @@
<item name="android:background">?colorPrimary</item> <item name="android:background">?colorPrimary</item>
<item name="elevation">1dp</item> <item name="elevation">1dp</item>
<item name="titleTextStyle">@style/TextAppearance.Session.DarkActionBar.TitleTextStyle</item> <item name="titleTextStyle">@style/TextAppearance.Session.DarkActionBar.TitleTextStyle</item>
<item name="popupTheme">@style/Widget.Session.ActionBar</item>
</style> </style>
<style name="Widget.Session.ActionBar.Flat"> <style name="Widget.Session.ActionBar.Flat">
@ -184,10 +183,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

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<!-- Session --> <!-- Session -->
@ -43,16 +42,14 @@
<item name="dividerHorizontal">?dividerVertical</item> <item name="dividerHorizontal">?dividerVertical</item>
<!-- App specific attributes --> <!-- App specific attributes -->
<item name="search_toolbar_background">@color/black</item> <item name="ic_visibility_on">@drawable/ic_baseline_visibility_24</item>
<item name="search_background">@color/black</item> <item name="ic_visibility_off">@drawable/ic_baseline_visibility_off_24</item>
<item name="ic_arrow_forward">@drawable/ic_baseline_arrow_forward_24</item>
<item name="dialog_background_color">@color/dialog_background</item> <item name="dialog_background_color">@color/dialog_background</item>
<item name="media_overview_toolbar_background">@color/transparent</item> <item name="media_overview_toolbar_background">@color/transparent</item>
<item name="media_overview_toolbar_foreground">@color/white</item>
<item name="media_overview_header_foreground">@color/text</item> <item name="media_overview_header_foreground">@color/text</item>
<item name="media_overview_document_primary">@color/core_grey_05</item>
<item name="media_overview_document_secondary">#99FFFFFF</item>
<item name="media_keyboard_button_color">@color/core_grey_25</item> <item name="media_keyboard_button_color">@color/core_grey_25</item>
<item name="searchViewStyle">@style/TextSecure.BaseDarkTheme.SearchView</item> <item name="searchViewStyle">@style/TextSecure.BaseDarkTheme.SearchView</item>
@ -87,6 +84,7 @@
<item name="conversation_input_background">@drawable/compose_background_dark</item> <item name="conversation_input_background">@drawable/compose_background_dark</item>
<item name="quick_camera_icon">@drawable/ic_baseline_photo_camera_24</item> <item name="quick_camera_icon">@drawable/ic_baseline_photo_camera_24</item>
<item name="quick_mic_icon">@drawable/ic_baseline_mic_24</item>
</style> </style>
<!-- This should be the default theme for the application. --> <!-- This should be the default theme for the application. -->
@ -158,14 +156,7 @@
<item name="colorPrimary">@color/action_bar_background</item> <item name="colorPrimary">@color/action_bar_background</item>
<item name="colorPrimaryDark">@color/action_bar_background</item> <item name="colorPrimaryDark">@color/action_bar_background</item>
<item name="recipient_preference_blocked">#d00000</item>
<item name="contact_selection_label_text">#66eeeeee</item>
<item name="contact_selection_push_user">#ffeeeeee</item>
<item name="contact_selection_lay_user">#afeeeeee</item>
<item name="contact_selection_header_text">#66eeeeee</item>
<item name="media_overview_toolbar_background">@color/transparent</item> <item name="media_overview_toolbar_background">@color/transparent</item>
<item name="media_overview_toolbar_foreground">@color/white</item>
<item name="media_overview_header_foreground">@color/text</item> <item name="media_overview_header_foreground">@color/text</item>
<item name="theme_type">dark</item> <item name="theme_type">dark</item>
@ -175,8 +166,6 @@
<item name="attachment_document_icon_small">@drawable/ic_document_small_dark</item> <item name="attachment_document_icon_small">@drawable/ic_document_small_dark</item>
<item name="attachment_document_icon_large">@drawable/ic_document_large_dark</item> <item name="attachment_document_icon_large">@drawable/ic_document_large_dark</item>
<item name="compose_icon_tint">@color/core_grey_25</item>
<item name="conversation_list_item_background">@drawable/conversation_list_item_background_dark</item> <item name="conversation_list_item_background">@drawable/conversation_list_item_background_dark</item>
<item name="conversation_list_item_contact_color">#ffdddddd</item> <item name="conversation_list_item_contact_color">#ffdddddd</item>
<item name="conversation_list_item_subject_color">#ffdddddd</item> <item name="conversation_list_item_subject_color">#ffdddddd</item>
@ -194,22 +183,14 @@
<item name="conversation_item_sent_text_primary_color">@color/core_grey_05</item> <item name="conversation_item_sent_text_primary_color">@color/core_grey_05</item>
<item name="conversation_item_sent_text_secondary_color">@color/core_grey_25</item> <item name="conversation_item_sent_text_secondary_color">@color/core_grey_25</item>
<item name="conversation_item_sent_icon_color">@color/core_grey_25</item> <item name="conversation_item_sent_icon_color">@color/core_grey_25</item>
<item name="conversation_item_sent_download_icon_color">@color/core_white</item>
<item name="conversation_item_sent_text_indicator_tab_color">#99ffffff</item> <item name="conversation_item_sent_text_indicator_tab_color">#99ffffff</item>
<item name="conversation_item_received_text_primary_color">@color/text</item> <item name="conversation_item_received_text_primary_color">@color/text</item>
<item name="conversation_item_received_text_secondary_color">@color/text</item> <item name="conversation_item_received_text_secondary_color">@color/text</item>
<item name="conversation_item_sent_indicator_text_background">@drawable/conversation_item_sent_indicator_text_shape_dark</item> <item name="conversation_item_sent_indicator_text_background">@drawable/conversation_item_sent_indicator_text_shape_dark</item>
<item name="conversation_item_update_text_color">@color/core_grey_45</item>
<item name="conversation_item_last_seen_text_color">@color/core_grey_25</item>
<item name="conversation_item_last_seen_line_color">@color/core_grey_25</item>
<item name="conversation_item_date_line_color">@color/core_grey_45</item>
<item name="conversation_item_quote_text_color">@color/core_grey_05</item>
<item name="conversation_item_sticky_date_background">@drawable/sticky_date_header_background_dark</item> <item name="conversation_item_sticky_date_background">@drawable/sticky_date_header_background_dark</item>
<item name="conversation_item_sticky_date_text_color">@color/core_grey_45</item> <item name="conversation_item_sticky_date_text_color">@color/core_grey_45</item>
<item name="conversation_item_image_outline_color">@color/transparent_white_30</item> <item name="conversation_item_image_outline_color">@color/transparent_white_30</item>
<item name="contact_list_divider">@drawable/contact_list_divider_dark</item>
<item name="verification_background">@color/core_grey_95</item> <item name="verification_background">@color/core_grey_95</item>
<item name="dialog_info_icon">@drawable/ic_info_outline_dark</item> <item name="dialog_info_icon">@drawable/ic_info_outline_dark</item>
@ -217,12 +198,6 @@
<item name="device_link_item_card_background">@color/device_link_item_background_dark</item> <item name="device_link_item_card_background">@color/device_link_item_background_dark</item>
<item name="mediapicker_image_outline">@drawable/mediapicker_item_border_dark</item>
<item name="import_export_item_background_color">@color/import_export_item_background_dark</item>
<item name="import_export_item_background_shadow_color">@color/import_export_item_background_shadow_dark</item>
<item name="import_export_item_card_background">@drawable/clickable_card_dark</item>
<item name="fab_color">@color/textsecure_primary_dark</item> <item name="fab_color">@color/textsecure_primary_dark</item>
<item name="lower_right_divet">@drawable/divet_lower_right_light</item> <item name="lower_right_divet">@drawable/divet_lower_right_light</item>
@ -269,27 +244,9 @@
<item name="linkpreview_secondary_text_color">@color/text</item> <item name="linkpreview_secondary_text_color">@color/text</item>
<item name="linkpreview_divider_color">@color/transparent</item> <item name="linkpreview_divider_color">@color/transparent</item>
<!-- <item name="quick_camera_icon">@drawable/ic_baseline_photo_camera_24</item>-->
<item name="quick_mic_icon">@drawable/ic_mic_white_24dp</item>
<item name="media_keyboard_button_color">@color/core_grey_25</item>
<item name="menu_new_conversation_icon">@drawable/ic_add_white_24dp</item>
<item name="menu_group_icon">@drawable/ic_group_white_24dp</item>
<item name="menu_search_icon">@drawable/ic_search_white_24dp</item>
<item name="menu_call_icon">@drawable/ic_call_white_24dp</item>
<item name="menu_unlock_icon">@drawable/ic_unlocked_white_24dp</item>
<item name="menu_lock_icon">@drawable/ic_lock_white_24dp</item>
<item name="menu_lock_icon_small">@drawable/ic_lock_white_18dp</item>
<item name="menu_accept_icon">@drawable/ic_check_white_24dp</item>
<item name="menu_refresh_directory">@drawable/ic_refresh_white_24dp</item>
<item name="menu_info_icon">@drawable/ic_info_outline_white_24dp</item>
<item name="conversation_icon_attach_audio">@drawable/ic_audio_dark</item> <item name="conversation_icon_attach_audio">@drawable/ic_audio_dark</item>
<item name="conversation_icon_attach_video">@drawable/ic_video_dark</item> <item name="conversation_icon_attach_video">@drawable/ic_video_dark</item>
<item name="reminder_header_background">@color/textsecure_primary_dark</item>
<item name="sticker_management_icon">@drawable/sticker_button_dark</item> <item name="sticker_management_icon">@drawable/sticker_button_dark</item>
<item name="sticker_management_divider_color">@color/core_grey_75</item> <item name="sticker_management_divider_color">@color/core_grey_75</item>
<item name="sticker_management_empty_background_color">@color/core_grey_85</item> <item name="sticker_management_empty_background_color">@color/core_grey_85</item>
@ -303,15 +260,8 @@
<item name="pref_icon_tint">#FFFFFF</item> <item name="pref_icon_tint">#FFFFFF</item>
<item name="pref_divider">@drawable/preference_divider_dark</item>
<item name="quote_missing_icon_color">@color/core_grey_05</item>
<item name="group_members_dialog_icon">@drawable/ic_group_white_24dp</item> <item name="group_members_dialog_icon">@drawable/ic_group_white_24dp</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay.Fix</item> <item name="preferenceTheme">@style/PreferenceThemeOverlay.Fix</item>
<item name="search_toolbar_background">@color/black</item>
<item name="search_background">@color/black</item>
<item name="invite_background">@color/black</item>
<item name="shared_contact_details_header_background">@color/grey_800</item> <item name="shared_contact_details_header_background">@color/grey_800</item>
<item name="shared_contact_details_titlebar">@color/grey_900</item> <item name="shared_contact_details_titlebar">@color/grey_900</item>

View File

@ -44,7 +44,7 @@
android:negativeButtonText="@null" android:negativeButtonText="@null"
android:positiveButtonText="@null" android:positiveButtonText="@null"
android:persistent="false" android:persistent="false"
app:numColumns="5" /> app:columns="5" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:layout="@layout/preference_divider"/> <PreferenceCategory android:layout="@layout/preference_divider"/>

View File

@ -23,9 +23,6 @@ import android.content.Intent;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
import androidx.core.os.CancellationSignal;
import androidx.appcompat.widget.Toolbar;
import android.text.Editable; import android.text.Editable;
import android.text.InputType; import android.text.InputType;
import android.text.SpannableString; import android.text.SpannableString;
@ -49,6 +46,9 @@ import android.widget.ImageButton;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
import androidx.core.os.CancellationSignal;
import org.thoughtcrime.securesms.animation.AnimationCompleteListener; import org.thoughtcrime.securesms.animation.AnimationCompleteListener;
import org.thoughtcrime.securesms.components.AnimatingToggle; import org.thoughtcrime.securesms.components.AnimatingToggle;
import org.thoughtcrime.securesms.crypto.InvalidPassphraseException; import org.thoughtcrime.securesms.crypto.InvalidPassphraseException;
@ -76,8 +76,8 @@ public class PassphrasePromptActivity extends PassphraseActivity {
private Button lockScreenButton; private Button lockScreenButton;
private EditText passphraseText; private EditText passphraseText;
// private ImageButton showButton; private ImageButton showButton;
// private ImageButton hideButton; private ImageButton hideButton;
private AnimatingToggle visibilityToggle; private AnimatingToggle visibilityToggle;
private FingerprintManagerCompat fingerprintManager; private FingerprintManagerCompat fingerprintManager;
@ -91,8 +91,6 @@ public class PassphrasePromptActivity extends PassphraseActivity {
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "onCreate()"); Log.i(TAG, "onCreate()");
dynamicLanguage.onCreate(this); dynamicLanguage.onCreate(this);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.prompt_passphrase_activity); setContentView(R.layout.prompt_passphrase_activity);
@ -203,11 +201,10 @@ public class PassphrasePromptActivity extends PassphraseActivity {
private void initializeResources() { private void initializeResources() {
// ImageButton okButton = findViewById(R.id.ok_button); ImageButton okButton = findViewById(R.id.ok_button);
Toolbar toolbar = findViewById(R.id.toolbar);
// showButton = findViewById(R.id.passphrase_visibility); showButton = findViewById(R.id.passphrase_visibility);
// hideButton = findViewById(R.id.passphrase_visibility_off); hideButton = findViewById(R.id.passphrase_visibility_off);
visibilityToggle = findViewById(R.id.button_toggle); visibilityToggle = findViewById(R.id.button_toggle);
passphraseText = findViewById(R.id.passphrase_edit); passphraseText = findViewById(R.id.passphrase_edit);
passphraseAuthContainer = findViewById(R.id.password_auth_container); passphraseAuthContainer = findViewById(R.id.password_auth_container);
@ -217,20 +214,16 @@ public class PassphrasePromptActivity extends PassphraseActivity {
fingerprintCancellationSignal = new CancellationSignal(); fingerprintCancellationSignal = new CancellationSignal();
fingerprintListener = new FingerprintListener(); fingerprintListener = new FingerprintListener();
// setSupportActionBar(toolbar);
// getSupportActionBar().setTitle("");
SpannableString hint = new SpannableString(" " + getString(R.string.PassphrasePromptActivity_enter_passphrase)); SpannableString hint = new SpannableString(" " + getString(R.string.PassphrasePromptActivity_enter_passphrase));
hint.setSpan(new RelativeSizeSpan(0.9f), 0, hint.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); hint.setSpan(new RelativeSizeSpan(0.9f), 0, hint.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
hint.setSpan(new TypefaceSpan("sans-serif"), 0, hint.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); hint.setSpan(new TypefaceSpan("sans-serif"), 0, hint.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
passphraseText.setHint(hint); passphraseText.setHint(hint);
// okButton.setOnClickListener(new OkButtonClickListener()); okButton.setOnClickListener(new OkButtonClickListener());
// showButton.setOnClickListener(new ShowButtonOnClickListener()); showButton.setOnClickListener(new ShowButtonOnClickListener());
// hideButton.setOnClickListener(new HideButtonOnClickListener()); hideButton.setOnClickListener(new HideButtonOnClickListener());
passphraseText.setOnEditorActionListener(new PassphraseActionListener()); passphraseText.setOnEditorActionListener(new PassphraseActionListener());
passphraseText.setImeActionLabel(getString(R.string.prompt_passphrase_activity__unlock), passphraseText.setImeActionLabel(getString(R.string.prompt_passphrase_activity__unlock), EditorInfo.IME_ACTION_DONE);
EditorInfo.IME_ACTION_DONE);
fingerprintPrompt.setImageResource(R.drawable.ic_fingerprint_white_48dp); fingerprintPrompt.setImageResource(R.drawable.ic_fingerprint_white_48dp);
fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.signal_primary), PorterDuff.Mode.SRC_IN); fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.signal_primary), PorterDuff.Mode.SRC_IN);
@ -316,7 +309,7 @@ public class PassphrasePromptActivity extends PassphraseActivity {
private class ShowButtonOnClickListener implements OnClickListener { private class ShowButtonOnClickListener implements OnClickListener {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
// visibilityToggle.display(hideButton); visibilityToggle.display(hideButton);
setPassphraseVisibility(true); setPassphraseVisibility(true);
} }
} }
@ -324,7 +317,7 @@ public class PassphrasePromptActivity extends PassphraseActivity {
private class HideButtonOnClickListener implements OnClickListener { private class HideButtonOnClickListener implements OnClickListener {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
// visibilityToggle.display(showButton); visibilityToggle.display(showButton);
setPassphraseVisibility(false); setPassphraseVisibility(false);
} }
} }

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);
@ -3098,6 +3105,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

@ -804,6 +804,7 @@ public class ConversationItem extends LinearLayout
bodyBubble.setLayoutParams(layoutParams); bodyBubble.setLayoutParams(layoutParams);
if (profilePictureView == null) return; if (profilePictureView == null) return;
profilePictureView.setPublicKey(recipient.getAddress().toString()); profilePictureView.setPublicKey(recipient.getAddress().toString());
profilePictureView.setDisplayName(recipient.getName());
profilePictureView.setAdditionalPublicKey(null); profilePictureView.setAdditionalPublicKey(null);
profilePictureView.setRSSFeed(false); profilePictureView.setRSSFeed(false);
profilePictureView.setGlide(glideRequests); profilePictureView.setGlide(glideRequests);

View File

@ -528,6 +528,7 @@ public class ThreadDatabase extends Database {
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {
return cursor.getLong(cursor.getColumnIndexOrThrow(ID)); return cursor.getLong(cursor.getColumnIndexOrThrow(ID));
} else { } else {
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
return createThreadForRecipient(recipient.getAddress(), recipient.isGroupRecipient(), distributionType); return createThreadForRecipient(recipient.getAddress(), recipient.isGroupRecipient(), distributionType);
} }
} finally { } finally {

View File

@ -1,7 +1,6 @@
package org.thoughtcrime.securesms.loki.activities package org.thoughtcrime.securesms.loki.activities
import android.app.AlertDialog import android.app.AlertDialog
import androidx.lifecycle.Observer
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
@ -11,10 +10,6 @@ import android.net.Uri
import android.os.AsyncTask import android.os.AsyncTask
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import androidx.loader.app.LoaderManager
import androidx.loader.content.Loader
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.recyclerview.widget.LinearLayoutManager
import android.text.Spannable import android.text.Spannable
import android.text.SpannableString import android.text.SpannableString
import android.text.style.ForegroundColorSpan import android.text.style.ForegroundColorSpan
@ -22,6 +17,11 @@ import android.util.DisplayMetrics
import android.view.View import android.view.View
import android.widget.RelativeLayout import android.widget.RelativeLayout
import android.widget.Toast import android.widget.Toast
import androidx.lifecycle.Observer
import androidx.loader.app.LoaderManager
import androidx.loader.content.Loader
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.android.synthetic.main.activity_home.* import kotlinx.android.synthetic.main.activity_home.*
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.ApplicationContext
@ -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.*
@ -42,14 +43,13 @@ import org.thoughtcrime.securesms.loki.views.NewConversationButtonSetViewDelegat
import org.thoughtcrime.securesms.loki.views.SeedReminderViewDelegate import org.thoughtcrime.securesms.loki.views.SeedReminderViewDelegate
import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.util.GroupUtil
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.Util import org.thoughtcrime.securesms.util.Util
import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI import org.whispersystems.signalservice.loki.api.fileserver.FileServerAPI
import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager
import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol
import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol
import org.whispersystems.signalservice.loki.protocol.sessionmanagement.SessionManagementProtocol import org.whispersystems.signalservice.loki.protocol.sessionmanagement.SessionManagementProtocol
import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol
import org.whispersystems.signalservice.loki.protocol.shelved.syncmessages.SyncMessagesProtocol import org.whispersystems.signalservice.loki.protocol.shelved.syncmessages.SyncMessagesProtocol
import org.whispersystems.signalservice.loki.utilities.toHexString import org.whispersystems.signalservice.loki.utilities.toHexString
@ -98,8 +98,10 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
// Set up toolbar buttons // Set up toolbar buttons
profileButton.glide = glide profileButton.glide = glide
profileButton.publicKey = publicKey profileButton.publicKey = publicKey
profileButton.displayName = TextSecurePreferences.getProfileName(this)
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

@ -54,10 +54,8 @@ import java.security.SecureRandom
import java.util.* import java.util.*
class SettingsActivity : PassphraseRequiredActionBarActivity() { class SettingsActivity : PassphraseRequiredActionBarActivity() {
private var displayNameEditActionMode: ActionMode? = null private var displayNameEditActionMode: ActionMode? = null
set(value) { field = value; handleDisplayNameEditActionModeChanged() } set(value) { field = value; handleDisplayNameEditActionModeChanged() }
private lateinit var glide: GlideRequests private lateinit var glide: GlideRequests
private var displayNameToBeUploaded: String? = null private var displayNameToBeUploaded: String? = null
private var profilePictureToBeUploaded: ByteArray? = null private var profilePictureToBeUploaded: ByteArray? = null
@ -73,17 +71,17 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
// region Lifecycle // region Lifecycle
override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) {
super.onCreate(savedInstanceState, isReady) super.onCreate(savedInstanceState, isReady)
setContentView(R.layout.activity_settings) setContentView(R.layout.activity_settings)
val displayName = DatabaseFactory.getLokiUserDatabase(this).getDisplayName(hexEncodedPublicKey)
glide = GlideApp.with(this) glide = GlideApp.with(this)
profilePictureView.glide = glide profilePictureView.glide = glide
profilePictureView.publicKey = hexEncodedPublicKey profilePictureView.publicKey = hexEncodedPublicKey
profilePictureView.displayName = displayName
profilePictureView.isLarge = true profilePictureView.isLarge = true
profilePictureView.update() profilePictureView.update()
profilePictureView.setOnClickListener { showEditProfilePictureUI() } profilePictureView.setOnClickListener { showEditProfilePictureUI() }
ctnGroupNameSection.setOnClickListener { startActionMode(DisplayNameEditActionModeCallback()) } ctnGroupNameSection.setOnClickListener { startActionMode(DisplayNameEditActionModeCallback()) }
btnGroupNameDisplay.text = DatabaseFactory.getLokiUserDatabase(this).getDisplayName(hexEncodedPublicKey) btnGroupNameDisplay.text = displayName
publicKeyTextView.text = hexEncodedPublicKey publicKeyTextView.text = hexEncodedPublicKey
copyButton.setOnClickListener { copyPublicKey() } copyButton.setOnClickListener { copyPublicKey() }
shareButton.setOnClickListener { sharePublicKey() } shareButton.setOnClickListener { sharePublicKey() }
@ -105,12 +103,9 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.settings_general, menu) menuInflater.inflate(R.menu.settings_general, menu)
// Update UI mode menu icon
// Update UI mode menu icon.
// It uses three-level selector where each level corresponds to the related UiMode ordinal value.
val uiMode = UiModeUtilities.getUserSelectedUiMode(this) val uiMode = UiModeUtilities.getUserSelectedUiMode(this)
menu.findItem(R.id.action_change_theme).icon!!.level = uiMode.ordinal menu.findItem(R.id.action_change_theme).icon!!.level = uiMode.ordinal
return true return true
} }

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

@ -0,0 +1,79 @@
package org.thoughtcrime.securesms.loki.todo
import android.content.Context
import android.graphics.*
import android.graphics.drawable.BitmapDrawable
import android.text.TextPaint
import android.text.TextUtils
import androidx.annotation.ColorInt
import androidx.core.graphics.ColorUtils
import network.loki.messenger.R
import java.util.*
object AvatarPlaceholderGenerator {
private val tmpFloatArray = FloatArray(3)
private const val EMPTY_LABEL = "0";
fun generate(context: Context, pixelSize: Int, hashString: String, displayName: String?): BitmapDrawable {
//TODO That should be replaced with a proper hash extraction code.
val hash: Long
if (hashString.length >= 12 && hashString.matches(Regex("^[0-9A-Fa-f]+\$"))) {
hash = hashString.substring(0 until 12).toLong(16)
} else {
hash = 0
}
// Do not cache color array, it may be different depends on the current theme.
val colorArray = context.resources.getIntArray(R.array.profile_picture_placeholder_colors)
val colorPrimary = colorArray[(hash % colorArray.size).toInt()]
val colorSecondary = changeColorHueBy(colorPrimary, 12f)
val labelText = when {
!TextUtils.isEmpty(displayName) -> extractLabel(displayName!!)
!TextUtils.isEmpty(hashString) -> extractLabel(hashString)
else -> EMPTY_LABEL
}
val bitmap = Bitmap.createBitmap(pixelSize, pixelSize, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
// Draw background/frame
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint.shader = LinearGradient(0f, 0f, 0f, pixelSize.toFloat(),
colorPrimary,
colorPrimary,
Shader.TileMode.REPEAT)
canvas.drawCircle(pixelSize.toFloat() / 2, pixelSize.toFloat() / 2, pixelSize.toFloat() / 2, paint)
// Draw text
val textPaint = TextPaint(Paint.ANTI_ALIAS_FLAG)
textPaint.typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL)
textPaint.textSize = pixelSize * 0.5f
textPaint.color = Color.WHITE
val areaRect = Rect(0, 0, pixelSize, pixelSize)
val textBounds = RectF(areaRect)
textBounds.right = textPaint.measureText(labelText)
textBounds.bottom = textPaint.descent() - textPaint.ascent()
textBounds.left += (areaRect.width() - textBounds.right) * 0.5f
textBounds.top += (areaRect.height() - textBounds.bottom) * 0.5f
canvas.drawText(labelText, textBounds.left, textBounds.top - textPaint.ascent(), textPaint)
return BitmapDrawable(context.resources, bitmap)
}
@ColorInt
private fun changeColorHueBy(@ColorInt color: Int, hueDelta: Float): Int {
val hslColor = tmpFloatArray
ColorUtils.colorToHSL(color, hslColor)
hslColor[0] = (hslColor[0] + hueDelta) % 360f
return ColorUtils.HSLToColor(hslColor)
}
private fun extractLabel(content: String): String {
var content = content.trim()
if (content.isEmpty()) return EMPTY_LABEL
return content.first().toString().toUpperCase(Locale.ROOT)
}
}

View File

@ -2,12 +2,14 @@ package org.thoughtcrime.securesms.loki.views
import android.content.Context import android.content.Context
import android.graphics.Typeface import android.graphics.Typeface
import android.text.TextUtils
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.LinearLayout
import kotlinx.android.synthetic.main.view_conversation.view.* import kotlinx.android.synthetic.main.view_conversation.view.*
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.model.ThreadRecord import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities.populateUserPublicKeyCacheIfNeeded import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities.populateUserPublicKeyCacheIfNeeded
import org.thoughtcrime.securesms.loki.utilities.MentionUtilities.highlightMentions import org.thoughtcrime.securesms.loki.utilities.MentionUtilities.highlightMentions
@ -55,30 +57,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)
@ -103,5 +83,10 @@ class ConversationView : LinearLayout {
else -> statusIndicatorImageView.setImageResource(R.drawable.ic_circle_check) else -> statusIndicatorImageView.setImageResource(R.drawable.ic_circle_check)
} }
} }
private fun getUserDisplayName(publicKey: String?): String? {
if (TextUtils.isEmpty(publicKey)) return null
return DatabaseFactory.getLokiUserDatabase(context).getDisplayName(publicKey!!)
}
// endregion // endregion
} }

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

@ -16,7 +16,7 @@ class LabeledSeparatorView : RelativeLayout {
private val path = Path() private val path = Path()
private val paint: Paint by lazy{ private val paint: Paint by lazy {
val result = Paint() val result = Paint()
result.style = Paint.Style.STROKE result.style = Paint.Style.STROKE
result.color = ThemeUtil.getThemedColor(context, R.attr.dividerHorizontal) result.color = ThemeUtil.getThemedColor(context, R.attr.dividerHorizontal)

View File

@ -32,6 +32,7 @@ class MentionCandidateView(context: Context, attrs: AttributeSet?, defStyleAttr:
private fun update() { private fun update() {
btnGroupNameDisplay.text = mentionCandidate.displayName btnGroupNameDisplay.text = mentionCandidate.displayName
profilePictureView.publicKey = mentionCandidate.publicKey profilePictureView.publicKey = mentionCandidate.publicKey
profilePictureView.displayName = mentionCandidate.displayName
profilePictureView.additionalPublicKey = null profilePictureView.additionalPublicKey = null
profilePictureView.isRSSFeed = false profilePictureView.isRSSFeed = false
profilePictureView.glide = glide!! profilePictureView.glide = glide!!

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

@ -1,28 +1,32 @@
package org.thoughtcrime.securesms.loki.views package org.thoughtcrime.securesms.loki.views
import android.content.Context import android.content.Context
import androidx.annotation.DimenRes
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.ImageView import android.widget.ImageView
import android.widget.RelativeLayout import android.widget.RelativeLayout
import androidx.annotation.DimenRes
import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.DiskCacheStrategy
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
import org.thoughtcrime.securesms.database.Address import org.thoughtcrime.securesms.database.Address
import org.thoughtcrime.securesms.loki.todo.JazzIdenticonDrawable import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.loki.todo.AvatarPlaceholderGenerator
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?
class ProfilePictureView : RelativeLayout { class ProfilePictureView : RelativeLayout {
lateinit var glide: GlideRequests lateinit var glide: GlideRequests
var publicKey: String? = null var publicKey: String? = null
var displayName: String? = null
var additionalPublicKey: String? = null var additionalPublicKey: String? = null
var additionalDisplayName: String? = null
var isRSSFeed = false var isRSSFeed = false
var isLarge = false var isLarge = false
@ -51,6 +55,45 @@ class ProfilePictureView : RelativeLayout {
// endregion // endregion
// region Updating // region Updating
fun update(recipient: Recipient, threadID: Long) {
fun getUserDisplayName(publicKey: String?): String? {
if (publicKey == null || publicKey.isBlank()) {
return null
} else {
return DatabaseFactory.getLokiUserDatabase(context).getDisplayName(publicKey!!)
}
}
if (recipient.isGroupRecipient) {
if ("Session Public Chat" == recipient.name) {
publicKey = ""
displayName = ""
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
val pk = randomUsers.getOrNull(0) ?: ""
publicKey = pk
displayName = getUserDisplayName(pk)
val apk = randomUsers.getOrNull(1) ?: ""
additionalPublicKey = apk
additionalDisplayName = getUserDisplayName(apk)
isRSSFeed = recipient.name == "Loki News" || recipient.name == "Session Updates"
}
} else {
publicKey = recipient.address.toString()
displayName = recipient.name
additionalPublicKey = null
isRSSFeed = false
}
update()
}
fun update() { fun update() {
val publicKey = publicKey ?: return val publicKey = publicKey ?: return
val additionalPublicKey = additionalPublicKey val additionalPublicKey = additionalPublicKey
@ -58,28 +101,50 @@ class ProfilePictureView : RelativeLayout {
singleModeImageViewContainer.visibility = if (additionalPublicKey == null && !isRSSFeed && !isLarge) View.VISIBLE else View.INVISIBLE singleModeImageViewContainer.visibility = if (additionalPublicKey == null && !isRSSFeed && !isLarge) View.VISIBLE else View.INVISIBLE
largeSingleModeImageViewContainer.visibility = if (additionalPublicKey == null && !isRSSFeed && isLarge) View.VISIBLE else View.INVISIBLE largeSingleModeImageViewContainer.visibility = if (additionalPublicKey == null && !isRSSFeed && isLarge) View.VISIBLE else View.INVISIBLE
rssImageView.visibility = if (isRSSFeed) View.VISIBLE else View.INVISIBLE rssImageView.visibility = if (isRSSFeed) View.VISIBLE else View.INVISIBLE
fun setProfilePictureIfNeeded(imageView: ImageView, hexEncodedPublicKey: String, @DimenRes sizeID: Int) { setProfilePictureIfNeeded(
glide.clear(imageView) doubleModeImageView1,
if (hexEncodedPublicKey.isNotEmpty()) { publicKey,
val recipient = Recipient.from(context, Address.fromSerialized(hexEncodedPublicKey), false); displayName,
val signalProfilePicture = recipient.contactPhoto R.dimen.small_profile_picture_size)
if (signalProfilePicture != null && (signalProfilePicture as? ProfileContactPhoto)?.avatarObject != "0" && (signalProfilePicture as? ProfileContactPhoto)?.avatarObject != "") { setProfilePictureIfNeeded(
glide.load(signalProfilePicture).diskCacheStrategy(DiskCacheStrategy.ALL).circleCrop().into(imageView) doubleModeImageView2,
} else { additionalPublicKey ?: "",
val size = resources.getDimensionPixelSize(sizeID) additionalDisplayName,
val masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context) R.dimen.small_profile_picture_size)
val hepk = if (recipient.isLocalNumber && masterHexEncodedPublicKey != null) masterHexEncodedPublicKey else hexEncodedPublicKey setProfilePictureIfNeeded(
val jazzIcon = JazzIdenticonDrawable(size, size, hepk) singleModeImageView,
glide.load(jazzIcon).diskCacheStrategy(DiskCacheStrategy.ALL).circleCrop().into(imageView) publicKey,
} displayName,
R.dimen.medium_profile_picture_size)
setProfilePictureIfNeeded(
largeSingleModeImageView,
publicKey,
displayName,
R.dimen.large_profile_picture_size)
}
private fun setProfilePictureIfNeeded(imageView: ImageView, publicKey: String, displayName: String?, @DimenRes sizeResId: Int) {
glide.clear(imageView)
if (publicKey.isNotEmpty()) {
val recipient = Recipient.from(context, Address.fromSerialized(publicKey), false);
val signalProfilePicture = recipient.contactPhoto
if (signalProfilePicture != null && (signalProfilePicture as? ProfileContactPhoto)?.avatarObject != "0"
&& (signalProfilePicture as? ProfileContactPhoto)?.avatarObject != "") {
glide.load(signalProfilePicture).diskCacheStrategy(DiskCacheStrategy.ALL).circleCrop().into(imageView)
} else { } else {
imageView.setImageDrawable(null) val sizeInPX = resources.getDimensionPixelSize(sizeResId)
val masterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context)
val hepk = if (recipient.isLocalNumber && masterPublicKey != null) masterPublicKey else publicKey
glide.load(AvatarPlaceholderGenerator.generate(
context,
sizeInPX,
hepk,
displayName
)).diskCacheStrategy(DiskCacheStrategy.ALL).circleCrop().into(imageView)
} }
} else {
imageView.setImageDrawable(null)
} }
setProfilePictureIfNeeded(doubleModeImageView1, publicKey, R.dimen.small_profile_picture_size)
setProfilePictureIfNeeded(doubleModeImageView2, additionalPublicKey ?: "", R.dimen.small_profile_picture_size)
setProfilePictureIfNeeded(singleModeImageView, publicKey, R.dimen.medium_profile_picture_size)
setProfilePictureIfNeeded(largeSingleModeImageView, publicKey, R.dimen.large_profile_picture_size)
} }
// endregion // endregion
} }

View File

@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.loki.views package org.thoughtcrime.securesms.loki.views
import android.content.Context import android.content.Context
import android.text.TextUtils
import android.util.AttributeSet import android.util.AttributeSet
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -8,6 +9,7 @@ import android.widget.LinearLayout
import kotlinx.android.synthetic.main.view_conversation.view.profilePictureView import kotlinx.android.synthetic.main.view_conversation.view.profilePictureView
import kotlinx.android.synthetic.main.view_user.view.* import kotlinx.android.synthetic.main.view_user.view.*
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.groups.GroupManager import org.thoughtcrime.securesms.groups.GroupManager
import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.Recipient
@ -47,22 +49,35 @@ class UserView : LinearLayout {
// region Updating // region Updating
fun bind(user: Recipient, glide: GlideRequests, actionIndicator: ActionIndicator, isSelected: Boolean = false) { fun bind(user: Recipient, glide: GlideRequests, actionIndicator: ActionIndicator, isSelected: Boolean = false) {
fun getUserDisplayName(publicKey: String?): String? {
if (publicKey == null || publicKey.isBlank()) {
return null
} else {
return DatabaseFactory.getLokiUserDatabase(context).getDisplayName(publicKey!!)
}
}
val address = user.address.serialize() val address = user.address.serialize()
if (user.isGroupRecipient) { if (user.isGroupRecipient) {
if ("Session Public Chat" == user.name || user.address.isRSSFeed) { if ("Session Public Chat" == user.name || user.address.isRSSFeed) {
profilePictureView.publicKey = "" profilePictureView.publicKey = ""
profilePictureView.displayName = null
profilePictureView.additionalPublicKey = null profilePictureView.additionalPublicKey = null
profilePictureView.isRSSFeed = true profilePictureView.isRSSFeed = true
} else { } else {
val threadID = GroupManager.getThreadIDFromGroupID(address, context) val threadID = GroupManager.getThreadIDFromGroupID(address, context)
val users = MentionsManager.shared.userPublicKeyCache[threadID]?.toList() ?: listOf() val users = MentionsManager.shared.userPublicKeyCache[threadID]?.toList() ?: listOf()
val randomUsers = users.sorted() // Sort to provide a level of stability val randomUsers = users.sorted() // Sort to provide a level of stability
profilePictureView.publicKey = randomUsers.getOrNull(0) ?: "" val pk = randomUsers.getOrNull(0) ?: ""
profilePictureView.additionalPublicKey = randomUsers.getOrNull(1) ?: "" profilePictureView.publicKey = pk
profilePictureView.displayName = getUserDisplayName(pk)
val apk = randomUsers.getOrNull(1) ?: ""
profilePictureView.additionalPublicKey = apk
profilePictureView.additionalDisplayName = getUserDisplayName(apk)
profilePictureView.isRSSFeed = false profilePictureView.isRSSFeed = false
} }
} else { } else {
profilePictureView.publicKey = address profilePictureView.publicKey = address
profilePictureView.displayName = getUserDisplayName(address)
profilePictureView.additionalPublicKey = null profilePictureView.additionalPublicKey = null
profilePictureView.isRSSFeed = false profilePictureView.isRSSFeed = false
} }

View File

@ -14,21 +14,22 @@ import android.graphics.RectF;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.text.SpannableStringBuilder;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationCompat.Action; import androidx.core.app.NotificationCompat.Action;
import androidx.core.app.RemoteInput; import androidx.core.app.RemoteInput;
import android.text.SpannableStringBuilder;
import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.load.engine.DiskCacheStrategy;
import org.thoughtcrime.securesms.contacts.avatars.ContactColors; import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.todo.AvatarPlaceholderGenerator;
import org.thoughtcrime.securesms.loki.utilities.NotificationUtilities; import org.thoughtcrime.securesms.loki.utilities.NotificationUtilities;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader;
import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.GlideApp;
@ -80,9 +81,7 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
addPerson(recipient.getContactUri().toString()); addPerson(recipient.getContactUri().toString());
} }
ContactPhoto contactPhoto = recipient.getContactPhoto(); ContactPhoto contactPhoto = recipient.getContactPhoto();
FallbackContactPhoto fallbackContactPhoto = recipient.getFallbackContactPhoto();
if (contactPhoto != null) { if (contactPhoto != null) {
try { try {
setLargeIcon(GlideApp.with(context.getApplicationContext()) setLargeIcon(GlideApp.with(context.getApplicationContext())
@ -94,10 +93,10 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
.get()); .get());
} catch (InterruptedException | ExecutionException e) { } catch (InterruptedException | ExecutionException e) {
Log.w(TAG, e); Log.w(TAG, e);
setLargeIcon(fallbackContactPhoto.asDrawable(context, recipient.getColor().toConversationColor(context))); setLargeIcon(getPlaceholderDrawable(context, recipient));
} }
} else { } else {
setLargeIcon(fallbackContactPhoto.asDrawable(context, recipient.getColor().toConversationColor(context))); setLargeIcon(getPlaceholderDrawable(context, recipient));
} }
} else { } else {
@ -321,4 +320,12 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
return content; return content;
} }
private static Drawable getPlaceholderDrawable(Context context, Recipient recipient) {
String publicKey = recipient.getAddress().serialize();
String hepk = (recipient.isLocalNumber() && publicKey != null)
? TextSecurePreferences.getMasterHexEncodedPublicKey(context)
: publicKey;
String displayName = recipient.getName();
return AvatarPlaceholderGenerator.INSTANCE.generate(context, 128, hepk, displayName);
}
} }