Merge branch 'dev' of https://github.com/loki-project/session-android into open-group-avatar

This commit is contained in:
Anton Chekulaev 2020-10-01 21:19:43 +10:00
commit fcba710a32
43 changed files with 342 additions and 141 deletions

View File

@ -144,6 +144,7 @@
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
<activity <activity
android:name="org.thoughtcrime.securesms.loki.activities.EditClosedGroupActivity" android:name="org.thoughtcrime.securesms.loki.activities.EditClosedGroupActivity"
android:label="@string/activity_edit_closed_group_title"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
<activity <activity
android:name="org.thoughtcrime.securesms.loki.activities.JoinPublicChatActivity" android:name="org.thoughtcrime.securesms.loki.activities.JoinPublicChatActivity"

View File

@ -181,7 +181,7 @@ dependencies {
testImplementation 'org.robolectric:shadows-multidex:4.2' testImplementation 'org.robolectric:shadows-multidex:4.2'
} }
def canonicalVersionCode = 100 def canonicalVersionCode = 108
def canonicalVersionName = "1.6.0" def canonicalVersionName = "1.6.0"
def postFixSize = 10 def postFixSize = 10

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?attr/colorControlHighlight">
<!-- Add half of the medium_profile_picture_size padding on the right to better work with the group icons. -->
<item
android:id="@android:id/mask"
android:right="24dp">
<shape>
<corners
android:bottomLeftRadius="@dimen/medium_profile_picture_size"
android:topLeftRadius="@dimen/medium_profile_picture_size" />
<solid android:color="@android:color/white" />
</shape>
</item>
</ripple>

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="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
</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="M11.67,3.87L9.9,2.1 0,12l9.9,9.9 1.77,-1.77L3.54,12z"/>
</vector>

View File

@ -16,4 +16,22 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent" />
<FrameLayout
android:id="@+id/camera_close_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/small_spacing"
android:padding="@dimen/small_spacing"
android:background="@drawable/circle_touch_highlight_background"
android:clickable="true"
android:focusable="true">
<ImageView
android:layout_width="36dp"
android:layout_height="36dp"
android:src="@drawable/ic_baseline_clear_24"
android:tint="@android:color/white"/>
</FrameLayout>
</FrameLayout> </FrameLayout>

View File

@ -11,20 +11,40 @@
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"
app:contentInsetLeft="24dp" android:clipChildren="false"
app:contentInsetRight="20dp"> app:contentInsetStart="4dp">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal"> android:orientation="horizontal"
android:clipChildren="false">
<LinearLayout
android:id="@+id/homeButtonContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:background="@drawable/conversation_home_touch_highlight"
android:clickable="true"
android:focusable="true">
<ImageView
android:layout_width="18dp"
android:layout_height="18dp"
android:src="@drawable/ic_baseline_arrow_back_compact_24"
android:layout_marginRight="-2dp"
android:layout_marginLeft="8dp" />
<org.thoughtcrime.securesms.loki.views.ProfilePictureView <org.thoughtcrime.securesms.loki.views.ProfilePictureView
android:id="@+id/profilePictureView" android:id="@+id/profilePictureView"
android:layout_width="@dimen/medium_profile_picture_size" android:layout_width="@dimen/medium_profile_picture_size"
android:layout_height="@dimen/medium_profile_picture_size" /> android:layout_height="@dimen/medium_profile_picture_size" />
</LinearLayout>
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -153,4 +153,22 @@
</org.thoughtcrime.securesms.components.InputAwareLayout> </org.thoughtcrime.securesms.components.InputAwareLayout>
<FrameLayout
android:id="@+id/mediasend_close_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/small_spacing"
android:padding="@dimen/small_spacing"
android:background="@drawable/circle_touch_highlight_background"
android:clickable="true"
android:focusable="true">
<ImageView
android:layout_width="36dp"
android:layout_height="36dp"
android:src="@drawable/ic_baseline_clear_24"
android:tint="@android:color/white"/>
</FrameLayout>
</FrameLayout> </FrameLayout>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?actionBarSize">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@drawable/session_logo"/>
<FrameLayout
android:id="@+id/back_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_margin="@dimen/small_spacing"
android:padding="@dimen/small_spacing"
android:background="@drawable/circle_touch_highlight_background"
android:clickable="true"
android:focusable="true">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_baseline_arrow_back_24"
android:alpha="0.5"/>
</FrameLayout>
</RelativeLayout>

View File

@ -1386,7 +1386,7 @@ Schlüsselaustausch-Nachricht für eine ungültige Protokollversion empfangen</s
<string name="activity_create_closed_group_empty_state_button_title">Session starten</string> <string name="activity_create_closed_group_empty_state_button_title">Session starten</string>
<string name="activity_create_closed_group_group_name_missing_error">Bitte geben Sie einen Gruppennamen ein.</string> <string name="activity_create_closed_group_group_name_missing_error">Bitte geben Sie einen Gruppennamen ein.</string>
<string name="activity_create_closed_group_group_name_too_long_error">Bitte geben Sie einen kürzeren Gruppennamen ein.</string> <string name="activity_create_closed_group_group_name_too_long_error">Bitte geben Sie einen kürzeren Gruppennamen ein.</string>
<string name="activity_create_closed_group_too_many_group_members_error">Eine geschlossene Gruppe kann maximal zehn Mitglieder haben.</string> <string name="activity_create_closed_group_too_many_group_members_error">Eine geschlossene Gruppe kann maximal 20 Mitglieder haben.</string>
<string name="activity_create_closed_group_invalid_session_id_error">Ein Mitglied Ihrer Gruppe hat eine ungültige Session ID.</string> <string name="activity_create_closed_group_invalid_session_id_error">Ein Mitglied Ihrer Gruppe hat eine ungültige Session ID.</string>
<string name="activity_join_public_chat_title">Offener Gruppe beitreten</string> <string name="activity_join_public_chat_title">Offener Gruppe beitreten</string>

View File

@ -1398,7 +1398,7 @@ Se recibió un mensaje de intercambio de claves para una versión no válida del
<string name="activity_create_closed_group_empty_state_button_title">Empezar una Session</string> <string name="activity_create_closed_group_empty_state_button_title">Empezar una Session</string>
<string name="activity_create_closed_group_group_name_missing_error">Por favor, ingresa un nombre de grupo</string> <string name="activity_create_closed_group_group_name_missing_error">Por favor, ingresa un nombre de grupo</string>
<string name="activity_create_closed_group_group_name_too_long_error">Por favor, ingresa un nombre de grupo más corto</string> <string name="activity_create_closed_group_group_name_too_long_error">Por favor, ingresa un nombre de grupo más corto</string>
<string name="activity_create_closed_group_too_many_group_members_error">Un grupo cerrado no puede tener más de 10 miembros</string> <string name="activity_create_closed_group_too_many_group_members_error">Un grupo cerrado no puede tener más de 20 miembros</string>
<string name="activity_create_closed_group_invalid_session_id_error">Uno de los miembros de tu grupo tiene un ID de Session no válido</string> <string name="activity_create_closed_group_invalid_session_id_error">Uno de los miembros de tu grupo tiene un ID de Session no válido</string>
<string name="activity_join_public_chat_title">Únete al grupo abierto</string> <string name="activity_join_public_chat_title">Únete al grupo abierto</string>

View File

@ -1312,7 +1312,7 @@
<string name="activity_create_closed_group_empty_state_button_title">شروع Session</string> <string name="activity_create_closed_group_empty_state_button_title">شروع Session</string>
<string name="activity_create_closed_group_group_name_missing_error">لطفا یک نام گروه وارد کنید</string> <string name="activity_create_closed_group_group_name_missing_error">لطفا یک نام گروه وارد کنید</string>
<string name="activity_create_closed_group_group_name_too_long_error">لطفا نام گروه کوتاه‌تری وارد کنید</string> <string name="activity_create_closed_group_group_name_too_long_error">لطفا نام گروه کوتاه‌تری وارد کنید</string>
<string name="activity_create_closed_group_too_many_group_members_error">یک گروه خصوصی نمی‌تواند بیش از ۱۰ عضو داشته باشد</string> <string name="activity_create_closed_group_too_many_group_members_error">یک گروه خصوصی نمی‌تواند بیش از بیست عضو داشته باشد</string>
<string name="activity_create_closed_group_invalid_session_id_error">یکی از اعضای گروه شما دارای شناسه نامعتبر است</string> <string name="activity_create_closed_group_invalid_session_id_error">یکی از اعضای گروه شما دارای شناسه نامعتبر است</string>
<string name="activity_join_public_chat_title">به گروه باز بپیوندید</string> <string name="activity_join_public_chat_title">به گروه باز بپیوندید</string>

View File

@ -1393,7 +1393,7 @@ Vous avez reçu un message déchange de clés pour une version de protocole i
<string name="activity_create_closed_group_empty_state_button_title">Démarrer une session</string> <string name="activity_create_closed_group_empty_state_button_title">Démarrer une session</string>
<string name="activity_create_closed_group_group_name_missing_error">Veuillez saisir un nom de groupe</string> <string name="activity_create_closed_group_group_name_missing_error">Veuillez saisir un nom de groupe</string>
<string name="activity_create_closed_group_group_name_too_long_error">Veuillez saisir un nom de groupe plus court</string> <string name="activity_create_closed_group_group_name_too_long_error">Veuillez saisir un nom de groupe plus court</string>
<string name="activity_create_closed_group_too_many_group_members_error">Un groupe privé ne peut pas avoir plus de 10 membres</string> <string name="activity_create_closed_group_too_many_group_members_error">Un groupe privé ne peut pas avoir plus de 20 membres</string>
<string name="activity_create_closed_group_invalid_session_id_error">Un des membres de votre groupe a un Session ID non valide</string> <string name="activity_create_closed_group_invalid_session_id_error">Un des membres de votre groupe a un Session ID non valide</string>
<string name="activity_join_public_chat_title">Joindre un groupe public</string> <string name="activity_join_public_chat_title">Joindre un groupe public</string>

View File

@ -1351,7 +1351,7 @@ Diterima pesan pertukaran kunci untuk versi protokol yang tidak valid.
<string name="activity_create_closed_group_group_name_missing_error">Masukkan nama grup</string> <string name="activity_create_closed_group_group_name_missing_error">Masukkan nama grup</string>
<string name="activity_create_closed_group_group_name_too_long_error">Masukkan nama grup yang lebih pendek</string> <string name="activity_create_closed_group_group_name_too_long_error">Masukkan nama grup yang lebih pendek</string>
<string name="activity_create_closed_group_not_enough_group_members_error">Pilih setidaknya 2 anggota grup</string> <string name="activity_create_closed_group_not_enough_group_members_error">Pilih setidaknya 2 anggota grup</string>
<string name="activity_create_closed_group_too_many_group_members_error">Grup tertutup maksimal berisi 10 anggota</string> <string name="activity_create_closed_group_too_many_group_members_error">Grup tertutup maksimal berisi 20 anggota</string>
<string name="activity_create_closed_group_invalid_session_id_error">Salah satu anggota di grup memiliki Session ID yang salah</string> <string name="activity_create_closed_group_invalid_session_id_error">Salah satu anggota di grup memiliki Session ID yang salah</string>
<string name="activity_join_public_chat_title">Gabung ke grup terbuka</string> <string name="activity_join_public_chat_title">Gabung ke grup terbuka</string>

View File

@ -1394,7 +1394,7 @@ Ricevuto un messaggio di scambio chiavi per una versione di protocollo non valid
<string name="activity_create_closed_group_empty_state_button_title">Inizia una sessione</string> <string name="activity_create_closed_group_empty_state_button_title">Inizia una sessione</string>
<string name="activity_create_closed_group_group_name_missing_error">Inserisci un nome per il gruppo</string> <string name="activity_create_closed_group_group_name_missing_error">Inserisci un nome per il gruppo</string>
<string name="activity_create_closed_group_group_name_too_long_error">Inserisci un nome gruppo più breve</string> <string name="activity_create_closed_group_group_name_too_long_error">Inserisci un nome gruppo più breve</string>
<string name="activity_create_closed_group_too_many_group_members_error">Un gruppo chiuso non può avere più di 10 membri</string> <string name="activity_create_closed_group_too_many_group_members_error">Un gruppo chiuso non può avere più di 20 membri</string>
<string name="activity_create_closed_group_invalid_session_id_error">Uno dei membri del tuo gruppo ha una Sessione ID non valido</string> <string name="activity_create_closed_group_invalid_session_id_error">Uno dei membri del tuo gruppo ha una Sessione ID non valido</string>
<string name="activity_join_public_chat_title">Unisciti a un gruppo aperto</string> <string name="activity_join_public_chat_title">Unisciti a un gruppo aperto</string>

View File

@ -1357,7 +1357,7 @@
<string name="activity_create_closed_group_group_name_missing_error">グループ名を入力してください</string> <string name="activity_create_closed_group_group_name_missing_error">グループ名を入力してください</string>
<string name="activity_create_closed_group_group_name_too_long_error">短いグループ名を入力してください</string> <string name="activity_create_closed_group_group_name_too_long_error">短いグループ名を入力してください</string>
<string name="activity_create_closed_group_not_enough_group_members_error">グループメンバーを少なくとも 2 人選択してください</string> <string name="activity_create_closed_group_not_enough_group_members_error">グループメンバーを少なくとも 2 人選択してください</string>
<string name="activity_create_closed_group_too_many_group_members_error">閉じたグループは 10 人を超えるメンバーを抱えることはできません</string> <string name="activity_create_closed_group_too_many_group_members_error">閉じたグループは 20 人を超えるメンバーを抱えることはできません</string>
<string name="activity_create_closed_group_invalid_session_id_error">グループのメンバーの 1 人の Session ID が無効です</string> <string name="activity_create_closed_group_invalid_session_id_error">グループのメンバーの 1 人の Session ID が無効です</string>
<string name="activity_join_public_chat_title">オープングループに参加する</string> <string name="activity_join_public_chat_title">オープングループに参加する</string>

View File

@ -1449,7 +1449,7 @@ Otrzymano wiadomość wymiany klucz dla niepoprawnej wersji protokołu.</string>
<string name="activity_create_closed_group_empty_state_button_title">Rozpocznij sesję</string> <string name="activity_create_closed_group_empty_state_button_title">Rozpocznij sesję</string>
<string name="activity_create_closed_group_group_name_missing_error">Wpisz nazwę grupy</string> <string name="activity_create_closed_group_group_name_missing_error">Wpisz nazwę grupy</string>
<string name="activity_create_closed_group_group_name_too_long_error">Wprowadź krótszą nazwę grupy</string> <string name="activity_create_closed_group_group_name_too_long_error">Wprowadź krótszą nazwę grupy</string>
<string name="activity_create_closed_group_too_many_group_members_error">Grupa zamknięta nie może mieć więcej niż 10 członków</string> <string name="activity_create_closed_group_too_many_group_members_error">Grupa zamknięta nie może mieć więcej niż 20 członków</string>
<string name="activity_create_closed_group_invalid_session_id_error">Jeden z członków Twojej grupy ma nieprawidłowy identyfikator Session</string> <string name="activity_create_closed_group_invalid_session_id_error">Jeden z członków Twojej grupy ma nieprawidłowy identyfikator Session</string>
<string name="activity_join_public_chat_title">Dołącz do Open Group</string> <string name="activity_join_public_chat_title">Dołącz do Open Group</string>

View File

@ -1397,7 +1397,7 @@
<string name="activity_create_closed_group_empty_state_button_title">Iniciar uma sessão</string> <string name="activity_create_closed_group_empty_state_button_title">Iniciar uma sessão</string>
<string name="activity_create_closed_group_group_name_missing_error">Digite um nome de grupo</string> <string name="activity_create_closed_group_group_name_missing_error">Digite um nome de grupo</string>
<string name="activity_create_closed_group_group_name_too_long_error">Digite um nome de grupo mais curto</string> <string name="activity_create_closed_group_group_name_too_long_error">Digite um nome de grupo mais curto</string>
<string name="activity_create_closed_group_too_many_group_members_error">Um grupo fechado não pode ter mais de 10 membros</string> <string name="activity_create_closed_group_too_many_group_members_error">Um grupo fechado não pode ter mais de 20 membros</string>
<string name="activity_create_closed_group_invalid_session_id_error">Um dos membros do seu grupo tem um ID Session inválido</string> <string name="activity_create_closed_group_invalid_session_id_error">Um dos membros do seu grupo tem um ID Session inválido</string>
<string name="activity_join_public_chat_title">Participar em grupo aberto</string> <string name="activity_join_public_chat_title">Participar em grupo aberto</string>

View File

@ -1451,7 +1451,7 @@
<string name="activity_create_closed_group_empty_state_button_title">Начать Сессию</string> <string name="activity_create_closed_group_empty_state_button_title">Начать Сессию</string>
<string name="activity_create_closed_group_group_name_missing_error">Пожалуйста, введите название группы</string> <string name="activity_create_closed_group_group_name_missing_error">Пожалуйста, введите название группы</string>
<string name="activity_create_closed_group_group_name_too_long_error">Пожалуйста, введите более короткое имя группы</string> <string name="activity_create_closed_group_group_name_too_long_error">Пожалуйста, введите более короткое имя группы</string>
<string name="activity_create_closed_group_too_many_group_members_error">В закрытой группе не может быть больше 10 участников</string> <string name="activity_create_closed_group_too_many_group_members_error">В закрытой группе не может быть больше 20 участников</string>
<string name="activity_create_closed_group_invalid_session_id_error">Один из участников вашей группы имеет недопустимый Session ID</string> <string name="activity_create_closed_group_invalid_session_id_error">Один из участников вашей группы имеет недопустимый Session ID</string>
<string name="activity_join_public_chat_title">Присоединиться к открытой группе</string> <string name="activity_join_public_chat_title">Присоединиться к открытой группе</string>

View File

@ -845,7 +845,7 @@ Các tin nhắn và cuộc gọi riêng tư miễn phí đến người dùng Si
<string name="activity_create_closed_group_group_name_missing_error">Vui lòng nhập tên nhóm</string> <string name="activity_create_closed_group_group_name_missing_error">Vui lòng nhập tên nhóm</string>
<string name="activity_create_closed_group_group_name_too_long_error">Vui lòng nhập một tên nhóm ngắn hơn </string> <string name="activity_create_closed_group_group_name_too_long_error">Vui lòng nhập một tên nhóm ngắn hơn </string>
<string name="activity_create_closed_group_not_enough_group_members_error">Vui lòng chọn ít nhất 2 thành viên trong nhóm </string> <string name="activity_create_closed_group_not_enough_group_members_error">Vui lòng chọn ít nhất 2 thành viên trong nhóm </string>
<string name="activity_create_closed_group_too_many_group_members_error">Một nhóm kín không thể có nhiều hơn 10 thành viên </string> <string name="activity_create_closed_group_too_many_group_members_error">Một nhóm kín không thể có nhiều hơn 20 thành viên </string>
<string name="activity_create_closed_group_invalid_session_id_error">Một trong các thành viên trong nhóm của bạn có Session ID không hợp lệ </string> <string name="activity_create_closed_group_invalid_session_id_error">Một trong các thành viên trong nhóm của bạn có Session ID không hợp lệ </string>
<string name="activity_join_public_chat_title">Tham gia nhóm mở</string> <string name="activity_join_public_chat_title">Tham gia nhóm mở</string>

View File

@ -1364,7 +1364,7 @@
<string name="activity_create_closed_group_empty_state_button_title">开始对话</string> <string name="activity_create_closed_group_empty_state_button_title">开始对话</string>
<string name="activity_create_closed_group_group_name_missing_error">请输入群组名称</string> <string name="activity_create_closed_group_group_name_missing_error">请输入群组名称</string>
<string name="activity_create_closed_group_group_name_too_long_error">请输入较短的群组名称</string> <string name="activity_create_closed_group_group_name_too_long_error">请输入较短的群组名称</string>
<string name="activity_create_closed_group_too_many_group_members_error">私密群组成员不得超过10个</string> <string name="activity_create_closed_group_too_many_group_members_error">私密群组成员不得超过20个</string>
<string name="activity_create_closed_group_invalid_session_id_error">您群组中的一位成员的Session ID无效</string> <string name="activity_create_closed_group_invalid_session_id_error">您群组中的一位成员的Session ID无效</string>
<string name="activity_join_public_chat_title">加入公开群组</string> <string name="activity_join_public_chat_title">加入公开群组</string>

View File

@ -1748,7 +1748,7 @@
<string name="activity_create_closed_group_group_name_missing_error">Please enter a group name</string> <string name="activity_create_closed_group_group_name_missing_error">Please enter a group name</string>
<string name="activity_create_closed_group_group_name_too_long_error">Please enter a shorter group name</string> <string name="activity_create_closed_group_group_name_too_long_error">Please enter a shorter group name</string>
<string name="activity_create_closed_group_not_enough_group_members_error">Please pick at least 1 group member</string> <string name="activity_create_closed_group_not_enough_group_members_error">Please pick at least 1 group member</string>
<string name="activity_create_closed_group_too_many_group_members_error">A closed group cannot have more than 10 members</string> <string name="activity_create_closed_group_too_many_group_members_error">A closed group cannot have more than 20 members</string>
<string name="activity_create_closed_group_invalid_session_id_error">One of the members of your group has an invalid Session ID</string> <string name="activity_create_closed_group_invalid_session_id_error">One of the members of your group has an invalid Session ID</string>
<string name="activity_join_public_chat_title">Join Open Group</string> <string name="activity_join_public_chat_title">Join Open Group</string>

View File

@ -18,8 +18,8 @@
<item name="android:textSize">@dimen/very_large_font_size</item> <item name="android:textSize">@dimen/very_large_font_size</item>
</style> </style>
<style name="TextSecure.BaseDarkTheme.SearchView" parent="@style/Widget.AppCompat.SearchView"> <style name="Widget.Session.SearchView" parent="@style/Widget.AppCompat.SearchView">
<item name="closeIcon">@drawable/ic_baseline_clear_24</item> <item name="closeIcon">@drawable/ic_clear</item>
</style> </style>
<style name="ThemeOverlay.Session.AlertDialog" parent="ThemeOverlay.AppCompat.Dialog.Alert"> <style name="ThemeOverlay.Session.AlertDialog" parent="ThemeOverlay.AppCompat.Dialog.Alert">

View File

@ -41,6 +41,8 @@
<item name="dividerVertical">@color/separator</item> <item name="dividerVertical">@color/separator</item>
<item name="dividerHorizontal">?dividerVertical</item> <item name="dividerHorizontal">?dividerVertical</item>
<item name="searchViewStyle">@style/Widget.Session.SearchView</item>
<!-- App specific attributes --> <!-- App specific attributes -->
<item name="ic_visibility_on">@drawable/ic_baseline_visibility_24</item> <item name="ic_visibility_on">@drawable/ic_baseline_visibility_24</item>
<item name="ic_visibility_off">@drawable/ic_baseline_visibility_off_24</item> <item name="ic_visibility_off">@drawable/ic_baseline_visibility_off_24</item>
@ -50,8 +52,7 @@
<item name="media_overview_toolbar_background">@color/transparent</item> <item name="media_overview_toolbar_background">@color/transparent</item>
<item name="media_overview_header_foreground">@color/text</item> <item name="media_overview_header_foreground">@color/text</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="attachment_type_selector_background">?android:windowBackground</item> <item name="attachment_type_selector_background">?android:windowBackground</item>
<item name="attachment_type_selector_hide_button_background">@color/gray50</item> <item name="attachment_type_selector_hide_button_background">@color/gray50</item>
@ -161,7 +162,6 @@
<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>
<item name="searchViewStyle">@style/TextSecure.BaseDarkTheme.SearchView</item>
<item name="android:navigationBarColor">@color/compose_view_background</item> <item name="android:navigationBarColor">@color/compose_view_background</item>
<item name="attachment_document_icon_small">@drawable/ic_document_small_dark</item> <item name="attachment_document_icon_small">@drawable/ic_document_small_dark</item>

View File

@ -12,6 +12,7 @@ import android.os.Build;
import android.os.Build.VERSION_CODES; import android.os.Build.VERSION_CODES;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.core.app.ActivityOptionsCompat; import androidx.core.app.ActivityOptionsCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.localbroadcastmanager.content.LocalBroadcastManager;
@ -42,6 +43,13 @@ public abstract class BaseActionBarActivity extends AppCompatActivity {
if (BaseActivity.isMenuWorkaroundRequired()) { if (BaseActivity.isMenuWorkaroundRequired()) {
forceOverflowMenu(); forceOverflowMenu();
} }
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeButtonEnabled(true);
}
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
} }
@ -69,6 +77,14 @@ public abstract class BaseActionBarActivity extends AppCompatActivity {
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, new IntentFilter("unexpectedDeviceLinkRequestReceived")); LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, new IntentFilter("unexpectedDeviceLinkRequestReceived"));
} }
@Override
public boolean onSupportNavigateUp() {
if (super.onSupportNavigateUp()) return true;
onBackPressed();
return true;
}
@Override @Override
protected void onDestroy() { protected void onDestroy() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver); LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);

View File

@ -34,6 +34,7 @@ import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.ActionMode; import androidx.appcompat.view.ActionMode;
@ -136,9 +137,12 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
private void initializeToolbar() { private void initializeToolbar() {
setSupportActionBar(this.toolbar); setSupportActionBar(this.toolbar);
getSupportActionBar().setTitle(recipient.toShortString()); ActionBar actionBar = getSupportActionBar();
actionBar.setTitle(recipient.toShortString());
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeButtonEnabled(true);
this.recipient.addListener(recipient -> { this.recipient.addListener(recipient -> {
Util.runOnMain(() -> getSupportActionBar().setTitle(recipient.toShortString())); Util.runOnMain(() -> actionBar.setTitle(recipient.toShortString()));
}); });
} }

View File

@ -19,7 +19,8 @@ package org.thoughtcrime.securesms;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import androidx.lifecycle.ViewModelProviders; import androidx.appcompat.app.ActionBar;
import androidx.lifecycle.ViewModelProvider;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.database.Cursor; import android.database.Cursor;
@ -122,7 +123,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
protected void onCreate(Bundle bundle, boolean ready) { protected void onCreate(Bundle bundle, boolean ready) {
dynamicLanguage.onCreate(this); dynamicLanguage.onCreate(this);
viewModel = ViewModelProviders.of(this).get(MediaPreviewViewModel.class); viewModel = new ViewModelProvider(this).get(MediaPreviewViewModel.class);
setContentView(R.layout.media_preview_activity); setContentView(R.layout.media_preview_activity);
@ -224,8 +225,10 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
captionContainer = findViewById(R.id.media_preview_caption_container); captionContainer = findViewById(R.id.media_preview_caption_container);
playbackControlsContainer = findViewById(R.id.media_preview_playback_controls_container); playbackControlsContainer = findViewById(R.id.media_preview_playback_controls_container);
setSupportActionBar(findViewById(R.id.toolbar)); setSupportActionBar(findViewById(R.id.toolbar));
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeButtonEnabled(true);
} }
private void initializeResources() { private void initializeResources() {

View File

@ -32,6 +32,7 @@ import android.widget.ImageView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import org.thoughtcrime.securesms.components.SearchToolbar; import org.thoughtcrime.securesms.components.SearchToolbar;
@ -151,6 +152,9 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
private void initializeToolbar() { private void initializeToolbar() {
Toolbar toolbar = findViewById(R.id.toolbar); Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeButtonEnabled(true);
} }
private void initializeResources() { private void initializeResources() {

View File

@ -3,8 +3,6 @@ package org.thoughtcrime.securesms.components;
import android.animation.Animator; import android.animation.Animator;
import android.content.Context; import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.os.Build; import android.os.Build;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.MenuItem; import android.view.MenuItem;
@ -49,11 +47,8 @@ public class SearchToolbar extends LinearLayout {
Toolbar toolbar = findViewById(R.id.toolbar); Toolbar toolbar = findViewById(R.id.toolbar);
Drawable drawable = getContext().getResources().getDrawable(R.drawable.ic_arrow_back_white_24dp); toolbar.setNavigationIcon(
drawable.mutate(); getContext().getResources().getDrawable(R.drawable.ic_baseline_clear_24));
drawable.setColorFilter(getContext().getResources().getColor(R.color.grey_700), PorterDuff.Mode.SRC_IN);
toolbar.setNavigationIcon(drawable);
toolbar.inflateMenu(R.menu.conversation_list_search); toolbar.inflateMenu(R.menu.conversation_list_search);
this.searchItem = toolbar.getMenu().findItem(R.id.action_filter_search); this.searchItem = toolbar.getMenu().findItem(R.id.action_filter_search);
@ -122,7 +117,6 @@ public class SearchToolbar extends LinearLayout {
private void hide() { private void hide() {
if (getVisibility() == View.VISIBLE) { if (getVisibility() == View.VISIBLE) {
if (listener != null) listener.onSearchClosed(); if (listener != null) listener.onSearchClosed();
if (Build.VERSION.SDK_INT >= 21) { if (Build.VERSION.SDK_INT >= 21) {

View File

@ -325,6 +325,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private ProgressBar messageStatusProgressBar; private ProgressBar messageStatusProgressBar;
private ImageView muteIndicatorImageView; private ImageView muteIndicatorImageView;
private TextView subtitleTextView; private TextView subtitleTextView;
private View homeButtonContainer;
private AttachmentTypeSelector attachmentTypeSelector; private AttachmentTypeSelector attachmentTypeSelector;
private AttachmentManager attachmentManager; private AttachmentManager attachmentManager;
@ -487,6 +488,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
collapsedKeyboardHeight = Math.min(collapsedKeyboardHeight, height); collapsedKeyboardHeight = Math.min(collapsedKeyboardHeight, height);
keyboardHeight = expandedKeyboardHeight - collapsedKeyboardHeight; keyboardHeight = expandedKeyboardHeight - collapsedKeyboardHeight;
// Use 300dp if the keyboard wasn't opened yet.
if (keyboardHeight == 0) {
keyboardHeight = (int)(300f * getResources().getDisplayMetrics().density);
}
}); });
} }
@ -1704,6 +1710,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
messageStatusProgressBar = ViewUtil.findById(this, R.id.messageStatusProgressBar); messageStatusProgressBar = ViewUtil.findById(this, R.id.messageStatusProgressBar);
muteIndicatorImageView = ViewUtil.findById(this, R.id.muteIndicatorImageView); muteIndicatorImageView = ViewUtil.findById(this, R.id.muteIndicatorImageView);
subtitleTextView = ViewUtil.findById(this, R.id.subtitleTextView); subtitleTextView = ViewUtil.findById(this, R.id.subtitleTextView);
homeButtonContainer = ViewUtil.findById(this, R.id.homeButtonContainer);
ImageButton quickCameraToggle = ViewUtil.findById(this, R.id.quick_camera_toggle); ImageButton quickCameraToggle = ViewUtil.findById(this, R.id.quick_camera_toggle);
ImageButton inlineAttachmentButton = ViewUtil.findById(this, R.id.inline_attachment_button); ImageButton inlineAttachmentButton = ViewUtil.findById(this, R.id.inline_attachment_button);
@ -1758,6 +1765,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
searchNav.setEventListener(this); searchNav.setEventListener(this);
inlineAttachmentButton.setOnClickListener(v -> handleAddAttachment()); inlineAttachmentButton.setOnClickListener(v -> handleAddAttachment());
homeButtonContainer.setOnClickListener(v -> onSupportNavigateUp());
} }
protected void initializeActionBar() { protected void initializeActionBar() {
@ -2291,7 +2300,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} else { } else {
MarkReadReceiver.process(context, messageIds); MarkReadReceiver.process(context, messageIds);
} }
ApplicationContext.getInstance(context).messageNotifier.updateNotification(context);
return null; return null;
} }
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, threadId); }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, threadId);
@ -2444,9 +2453,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
final long id = fragment.stageOutgoingMessage(outgoingMessage); final long id = fragment.stageOutgoingMessage(outgoingMessage);
new AsyncTask<Void, Void, Long>() {
@Override
protected Long doInBackground(Void... param) {
if (initiating) { if (initiating) {
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true); DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
} }
@ -2457,15 +2463,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(recipient.getAddress().serialize()); ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(recipient.getAddress().serialize());
} }
return result;
}
@Override
protected void onPostExecute(Long result) {
sendComplete(result); sendComplete(result);
future.set(null); future.set(null);
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
return future; return future;
} }
@ -2493,28 +2492,18 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
silentlySetComposeText(""); silentlySetComposeText("");
final long id = fragment.stageOutgoingMessage(message); final long id = fragment.stageOutgoingMessage(message);
new AsyncTask<OutgoingTextMessage, Void, Long>() {
@Override
protected Long doInBackground(OutgoingTextMessage... messages) {
if (initiatingConversation) { if (initiatingConversation) {
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true); DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
} }
long result = MessageSender.send(context, messages[0], threadId, forceSms, () -> fragment.releaseOutgoingMessage(id)); long result = MessageSender.send(context, message, threadId, forceSms, () -> fragment.releaseOutgoingMessage(id));
if (!recipient.isGroupRecipient()) { if (!recipient.isGroupRecipient()) {
ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(recipient.getAddress().serialize()); ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(recipient.getAddress().serialize());
} }
return result;
}
@Override
protected void onPostExecute(Long result) {
sendComplete(result); sendComplete(result);
} }
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, message);
}
private void showDefaultSmsPrompt() { private void showDefaultSmsPrompt() {
new AlertDialog.Builder(this) new AlertDialog.Builder(this)

View File

@ -71,8 +71,8 @@ public class GiphyActivity extends PassphraseRequiredActionBarActivity
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(false); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowTitleEnabled(false); getSupportActionBar().setHomeButtonEnabled(true);
} }
private void initializeResources() { private void initializeResources() {

View File

@ -12,6 +12,7 @@ import android.view.View
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.content.res.AppCompatResources
import kotlinx.android.synthetic.main.activity_create_closed_group.emptyStateContainer import kotlinx.android.synthetic.main.activity_create_closed_group.emptyStateContainer
import kotlinx.android.synthetic.main.activity_create_closed_group.mainContentContainer import kotlinx.android.synthetic.main.activity_create_closed_group.mainContentContainer
import kotlinx.android.synthetic.main.activity_edit_closed_group.* import kotlinx.android.synthetic.main.activity_edit_closed_group.*
@ -27,6 +28,7 @@ import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.GroupUtil import org.thoughtcrime.securesms.util.GroupUtil
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.ThemeUtil
import org.whispersystems.signalservice.loki.utilities.toHexString import org.whispersystems.signalservice.loki.utilities.toHexString
import java.io.IOException import java.io.IOException
@ -60,11 +62,12 @@ class EditClosedGroupActivity : 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_edit_closed_group) setContentView(R.layout.activity_edit_closed_group)
supportActionBar!!.title = resources.getString(R.string.activity_edit_closed_group_title)
groupID = intent.getStringExtra(Companion.groupIDKey) supportActionBar!!.setHomeAsUpIndicator(
ThemeUtil.getThemedDrawableResId(this, R.attr.actionModeCloseDrawable))
groupID = intent.getStringExtra(groupIDKey)!!
originalName = DatabaseFactory.getGroupDatabase(this).getGroup(groupID).get().title originalName = DatabaseFactory.getGroupDatabase(this).getGroup(groupID).get().title
name = originalName name = originalName
@ -88,7 +91,7 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
} }
} }
LoaderManager.getInstance(this).initLoader(Companion.loaderID, null, object : LoaderManager.LoaderCallbacks<List<String>> { LoaderManager.getInstance(this).initLoader(loaderID, null, object : LoaderManager.LoaderCallbacks<List<String>> {
override fun onCreateLoader(id: Int, bundle: Bundle?): Loader<List<String>> { override fun onCreateLoader(id: Int, bundle: Bundle?): Loader<List<String>> {
return EditClosedGroupLoader(this@EditClosedGroupActivity, groupID) return EditClosedGroupLoader(this@EditClosedGroupActivity, groupID)
@ -97,7 +100,7 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
override fun onLoadFinished(loader: Loader<List<String>>, members: List<String>) { override fun onLoadFinished(loader: Loader<List<String>>, members: List<String>) {
// We no longer need any subsequent loading events // We no longer need any subsequent loading events
// (they will occur on every activity resume). // (they will occur on every activity resume).
LoaderManager.getInstance(this@EditClosedGroupActivity).destroyLoader(Companion.loaderID) LoaderManager.getInstance(this@EditClosedGroupActivity).destroyLoader(loaderID)
originalMembers.clear() originalMembers.clear()
originalMembers.addAll(members.toHashSet()) originalMembers.addAll(members.toHashSet())
@ -120,7 +123,7 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
when (requestCode) { when (requestCode) {
Companion.addUsersRequestCode -> { addUsersRequestCode -> {
if (resultCode != RESULT_OK) return if (resultCode != RESULT_OK) return
if (data == null || data.extras == null || !data.hasExtra(SelectContactsActivity.selectedContactsKey)) return if (data == null || data.extras == null || !data.hasExtra(SelectContactsActivity.selectedContactsKey)) return
@ -180,8 +183,8 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
private fun onAddMembersClick() { private fun onAddMembersClick() {
val intent = Intent(this@EditClosedGroupActivity, SelectContactsActivity::class.java) val intent = Intent(this@EditClosedGroupActivity, SelectContactsActivity::class.java)
intent.putExtra(SelectContactsActivity.Companion.usersToExcludeKey, members.toTypedArray()) intent.putExtra(SelectContactsActivity.usersToExcludeKey, members.toTypedArray())
startActivityForResult(intent, Companion.addUsersRequestCode) startActivityForResult(intent, addUsersRequestCode)
} }
private fun saveName() { private fun saveName() {
@ -227,7 +230,7 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
return Toast.makeText(this, R.string.activity_edit_closed_group_not_enough_group_members_error, Toast.LENGTH_LONG).show() return Toast.makeText(this, R.string.activity_edit_closed_group_not_enough_group_members_error, Toast.LENGTH_LONG).show()
} }
val maxGroupMembers = if (isSSKBasedClosedGroup) ClosedGroupsProtocol.groupSizeLimit else Companion.legacyGroupSizeLimit val maxGroupMembers = if (isSSKBasedClosedGroup) ClosedGroupsProtocol.groupSizeLimit else legacyGroupSizeLimit
if (members.size >= maxGroupMembers) { if (members.size >= maxGroupMembers) {
// TODO: Update copy for SSK based closed groups // TODO: Update copy for SSK based closed groups
return Toast.makeText(this, R.string.activity_edit_closed_group_too_many_group_members_error, Toast.LENGTH_LONG).show() return Toast.makeText(this, R.string.activity_edit_closed_group_too_many_group_members_error, Toast.LENGTH_LONG).show()

View File

@ -40,7 +40,7 @@ class LandingActivity : BaseActionBarActivity(), LinkDeviceSlaveModeDialogDelega
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_landing) setContentView(R.layout.activity_landing)
setUpActionBarSessionLogo() setUpActionBarSessionLogo(true)
fakeChatView.startAnimating() fakeChatView.startAnimating()
registerButton.setOnClickListener { register() } registerButton.setOnClickListener { register() }
restoreButton.setOnClickListener { restore() } restoreButton.setOnClickListener { restore() }

View File

@ -112,6 +112,7 @@ object ClosedGroupsProtocol {
return return
} }
val oldMembers = group.members.map { it.serialize() }.toSet() val oldMembers = group.members.map { it.serialize() }.toSet()
val newMembers = members.minus(oldMembers)
val membersAsData = members.map { Hex.fromStringCondensed(it) } val membersAsData = members.map { Hex.fromStringCondensed(it) }
val admins = group.admins.map { it.serialize() } val admins = group.admins.map { it.serialize() }
val adminsAsData = admins.map { Hex.fromStringCondensed(it) } val adminsAsData = admins.map { Hex.fromStringCondensed(it) }
@ -123,6 +124,7 @@ object ClosedGroupsProtocol {
val wasAnyUserRemoved = members.toSet().intersect(oldMembers) != oldMembers.toSet() val wasAnyUserRemoved = members.toSet().intersect(oldMembers) != oldMembers.toSet()
val removedMembers = oldMembers.minus(members) val removedMembers = oldMembers.minus(members)
val isUserLeaving = removedMembers.contains(userPublicKey) val isUserLeaving = removedMembers.contains(userPublicKey)
var newSenderKeys = listOf<ClosedGroupSenderKey>()
if (wasAnyUserRemoved) { if (wasAnyUserRemoved) {
if (isUserLeaving && removedMembers.count() != 1) { if (isUserLeaving && removedMembers.count() != 1) {
Log.d("Loki", "Can't remove self and others simultaneously.") Log.d("Loki", "Can't remove self and others simultaneously.")
@ -147,6 +149,15 @@ object ClosedGroupsProtocol {
} else { } else {
// Establish sessions if needed // Establish sessions if needed
establishSessionsWithMembersIfNeeded(context, members) establishSessionsWithMembersIfNeeded(context, members)
// Send closed group update messages to any new members using established channels
for (member in newMembers) {
@Suppress("NAME_SHADOWING")
val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.New(Hex.fromStringCondensed(groupPublicKey), name,
Hex.fromStringCondensed(groupPrivateKey), listOf(), membersAsData, adminsAsData)
@Suppress("NAME_SHADOWING")
val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind)
ApplicationContext.getInstance(context).jobManager.add(job)
}
// Send out the user's new ratchet to all members (minus the removed ones) using established channels // Send out the user's new ratchet to all members (minus the removed ones) using established channels
val userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, userPublicKey) val userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, userPublicKey)
val userSenderKey = ClosedGroupSenderKey(Hex.fromStringCondensed(userRatchet.chainKey), userRatchet.keyIndex, Hex.fromStringCondensed(userPublicKey)) val userSenderKey = ClosedGroupSenderKey(Hex.fromStringCondensed(userRatchet.chainKey), userRatchet.keyIndex, Hex.fromStringCondensed(userPublicKey))
@ -159,10 +170,9 @@ object ClosedGroupsProtocol {
ApplicationContext.getInstance(context).jobManager.add(job) ApplicationContext.getInstance(context).jobManager.add(job)
} }
} }
} else { } else if (newMembers.isNotEmpty()) {
// Generate ratchets for any new members // Generate ratchets for any new members
val newMembers = members.minus(oldMembers) newSenderKeys = newMembers.map { publicKey ->
val newSenderKeys: List<ClosedGroupSenderKey> = newMembers.map { publicKey ->
val ratchet = SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, publicKey) val ratchet = SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, publicKey)
ClosedGroupSenderKey(Hex.fromStringCondensed(ratchet.chainKey), ratchet.keyIndex, Hex.fromStringCondensed(publicKey)) ClosedGroupSenderKey(Hex.fromStringCondensed(ratchet.chainKey), ratchet.keyIndex, Hex.fromStringCondensed(publicKey))
} }
@ -174,7 +184,8 @@ object ClosedGroupsProtocol {
// Establish sessions if needed // Establish sessions if needed
establishSessionsWithMembersIfNeeded(context, newMembers) establishSessionsWithMembersIfNeeded(context, newMembers)
// Send closed group update messages to the new members using established channels // Send closed group update messages to the new members using established channels
val allSenderKeys = sskDatabase.getAllClosedGroupSenderKeys(groupPublicKey) + newSenderKeys var allSenderKeys = sskDatabase.getAllClosedGroupSenderKeys(groupPublicKey);
allSenderKeys = allSenderKeys.union(newSenderKeys)
for (member in newMembers) { for (member in newMembers) {
@Suppress("NAME_SHADOWING") @Suppress("NAME_SHADOWING")
val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.New(Hex.fromStringCondensed(groupPublicKey), name, val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.New(Hex.fromStringCondensed(groupPublicKey), name,
@ -183,6 +194,12 @@ object ClosedGroupsProtocol {
val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind) val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind)
ApplicationContext.getInstance(context).jobManager.add(job) ApplicationContext.getInstance(context).jobManager.add(job)
} }
} else {
val allSenderKeys = sskDatabase.getAllClosedGroupSenderKeys(groupPublicKey);
val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.Info(Hex.fromStringCondensed(groupPublicKey), name,
allSenderKeys, membersAsData, adminsAsData)
val job = ClosedGroupUpdateMessageSendJob(groupPublicKey, closedGroupUpdateKind)
ApplicationContext.getInstance(context).jobManager.add(job)
} }
// Update the group // Update the group
groupDB.updateTitle(groupID, name) groupDB.updateTitle(groupID, name)
@ -225,7 +242,7 @@ object ClosedGroupsProtocol {
when (closedGroupUpdate.type) { when (closedGroupUpdate.type) {
SignalServiceProtos.ClosedGroupUpdate.Type.NEW -> { SignalServiceProtos.ClosedGroupUpdate.Type.NEW -> {
return !closedGroupUpdate.name.isNullOrEmpty() && !(closedGroupUpdate.groupPrivateKey ?: ByteString.copyFrom(ByteArray(0))).isEmpty return !closedGroupUpdate.name.isNullOrEmpty() && !(closedGroupUpdate.groupPrivateKey ?: ByteString.copyFrom(ByteArray(0))).isEmpty
&& closedGroupUpdate.senderKeysCount > 0 && closedGroupUpdate.membersCount > 0 && closedGroupUpdate.adminsCount > 0 && closedGroupUpdate.membersCount > 0 && closedGroupUpdate.adminsCount > 0 // senderKeys may be empty
} }
SignalServiceProtos.ClosedGroupUpdate.Type.INFO -> { SignalServiceProtos.ClosedGroupUpdate.Type.INFO -> {
return !closedGroupUpdate.name.isNullOrEmpty() && closedGroupUpdate.membersCount > 0 && closedGroupUpdate.adminsCount > 0 // senderKeys may be empty return !closedGroupUpdate.name.isNullOrEmpty() && closedGroupUpdate.membersCount > 0 && closedGroupUpdate.adminsCount > 0 // senderKeys may be empty
@ -255,10 +272,35 @@ object ClosedGroupsProtocol {
val ratchet = ClosedGroupRatchet(senderKey.chainKey.toHexString(), senderKey.keyIndex, listOf()) val ratchet = ClosedGroupRatchet(senderKey.chainKey.toHexString(), senderKey.keyIndex, listOf())
sskDatabase.setClosedGroupRatchet(groupPublicKey, senderKey.publicKey.toHexString(), ratchet) sskDatabase.setClosedGroupRatchet(groupPublicKey, senderKey.publicKey.toHexString(), ratchet)
} }
// Sort out any discrepancies between the provided sender keys and what's required
val missingSenderKeys = members.toSet().subtract(senderKeys.map { Hex.toStringCondensed(it.publicKey) })
if (missingSenderKeys.contains(userPublicKey)) {
establishSessionsWithMembersIfNeeded(context, members)
val userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, userPublicKey)
val userSenderKey = ClosedGroupSenderKey(Hex.fromStringCondensed(userRatchet.chainKey), userRatchet.keyIndex, Hex.fromStringCondensed(userPublicKey))
for (member in members) {
if (member == userPublicKey) { continue }
@Suppress("NAME_SHADOWING")
val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.SenderKey(Hex.fromStringCondensed(groupPublicKey), userSenderKey)
@Suppress("NAME_SHADOWING")
val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind)
ApplicationContext.getInstance(context).jobManager.add(job)
}
}
for (publicKey in missingSenderKeys.minus(userPublicKey)) {
requestSenderKey(context, groupPublicKey, publicKey)
}
// Create the group // Create the group
val groupID = doubleEncodeGroupID(groupPublicKey) val groupID = doubleEncodeGroupID(groupPublicKey)
DatabaseFactory.getGroupDatabase(context).create(groupID, name, LinkedList<Address>(members.map { Address.fromSerialized(it) }), val groupDB = DatabaseFactory.getGroupDatabase(context)
if (groupDB.getGroup(groupID).orNull() != null) {
// Update the group
groupDB.updateTitle(groupID, name)
groupDB.updateMembers(groupID, members.map { Address.fromSerialized(it) })
} else {
groupDB.create(groupID, name, LinkedList<Address>(members.map { Address.fromSerialized(it) }),
null, null, LinkedList<Address>(admins.map { Address.fromSerialized(it) })) null, null, LinkedList<Address>(admins.map { Address.fromSerialized(it) }))
}
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(Recipient.from(context, Address.fromSerialized(groupID), false), true) DatabaseFactory.getRecipientDatabase(context).setProfileSharing(Recipient.from(context, Address.fromSerialized(groupID), false), true)
// Add the group to the user's set of public keys to poll for // Add the group to the user's set of public keys to poll for
sskDatabase.setClosedGroupPrivateKey(groupPublicKey, groupPrivateKey.toHexString()) sskDatabase.setClosedGroupPrivateKey(groupPublicKey, groupPrivateKey.toHexString())
@ -297,7 +339,6 @@ object ClosedGroupsProtocol {
} }
// Store the ratchets for any new members (it's important that this happens before the code below) // Store the ratchets for any new members (it's important that this happens before the code below)
senderKeys.forEach { senderKey -> senderKeys.forEach { senderKey ->
if (!members.contains(senderKey.publicKey.toHexString())) { return@forEach }
val ratchet = ClosedGroupRatchet(senderKey.chainKey.toHexString(), senderKey.keyIndex, listOf()) val ratchet = ClosedGroupRatchet(senderKey.chainKey.toHexString(), senderKey.keyIndex, listOf())
sskDatabase.setClosedGroupRatchet(groupPublicKey, senderKey.publicKey.toHexString(), ratchet) sskDatabase.setClosedGroupRatchet(groupPublicKey, senderKey.publicKey.toHexString(), ratchet)
} }
@ -358,7 +399,8 @@ object ClosedGroupsProtocol {
// Respond to the request // Respond to the request
Log.d("Loki", "Responding to sender key request from: $senderPublicKey.") Log.d("Loki", "Responding to sender key request from: $senderPublicKey.")
ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(senderPublicKey) ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(senderPublicKey)
val userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, userPublicKey) val userRatchet = DatabaseFactory.getSSKDatabase(context).getClosedGroupRatchet(groupPublicKey, userPublicKey)
?: SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, userPublicKey)
val userSenderKey = ClosedGroupSenderKey(Hex.fromStringCondensed(userRatchet.chainKey), userRatchet.keyIndex, Hex.fromStringCondensed(userPublicKey)) val userSenderKey = ClosedGroupSenderKey(Hex.fromStringCondensed(userRatchet.chainKey), userRatchet.keyIndex, Hex.fromStringCondensed(userPublicKey))
val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.SenderKey(Hex.fromStringCondensed(groupPublicKey), userSenderKey) val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.SenderKey(Hex.fromStringCondensed(groupPublicKey), userSenderKey)
val job = ClosedGroupUpdateMessageSendJob(senderPublicKey, closedGroupUpdateKind) val job = ClosedGroupUpdateMessageSendJob(senderPublicKey, closedGroupUpdateKind)
@ -369,24 +411,12 @@ object ClosedGroupsProtocol {
// Prepare // Prepare
val sskDatabase = DatabaseFactory.getSSKDatabase(context) val sskDatabase = DatabaseFactory.getSSKDatabase(context)
val groupPublicKey = closedGroupUpdate.groupPublicKey.toByteArray().toHexString() val groupPublicKey = closedGroupUpdate.groupPublicKey.toByteArray().toHexString()
val groupDB = DatabaseFactory.getGroupDatabase(context)
val groupID = doubleEncodeGroupID(groupPublicKey)
val group = groupDB.getGroup(groupID).orNull()
if (group == null) {
Log.d("Loki", "Ignoring closed group sender key for nonexistent group.")
return
}
val senderKeyProto = closedGroupUpdate.senderKeysList.firstOrNull() val senderKeyProto = closedGroupUpdate.senderKeysList.firstOrNull()
if (senderKeyProto == null) { if (senderKeyProto == null) {
Log.d("Loki", "Ignoring invalid closed group sender key.") Log.d("Loki", "Ignoring invalid closed group sender key.")
return return
} }
val senderKey = ClosedGroupSenderKey(senderKeyProto.chainKey.toByteArray(), senderKeyProto.keyIndex, senderKeyProto.publicKey.toByteArray()) val senderKey = ClosedGroupSenderKey(senderKeyProto.chainKey.toByteArray(), senderKeyProto.keyIndex, senderKeyProto.publicKey.toByteArray())
// Check that the sending user is a member of the group
if (!group.members.map { it.serialize() }.contains(senderPublicKey)) {
Log.d("Loki", "Ignoring closed group sender key from non-member.")
return
}
if (senderKeyProto.publicKey.toByteArray().toHexString() != senderPublicKey) { if (senderKeyProto.publicKey.toByteArray().toHexString() != senderPublicKey) {
Log.d("Loki", "Ignoring invalid closed group sender key.") Log.d("Loki", "Ignoring invalid closed group sender key.")
return return

View File

@ -21,7 +21,7 @@ import java.util.concurrent.TimeUnit
class NullMessageSendJob private constructor(parameters: Parameters, private val publicKey: String) : BaseJob(parameters) { class NullMessageSendJob private constructor(parameters: Parameters, private val publicKey: String) : BaseJob(parameters) {
companion object { companion object {
const val KEY = "PushNullMessageSendJob" const val KEY = "NullMessageSendJob"
} }
constructor(publicKey: String) : this(Parameters.Builder() constructor(publicKey: String) : this(Parameters.Builder()

View File

@ -22,7 +22,7 @@ import java.util.concurrent.TimeUnit
class SessionRequestMessageSendJob private constructor(parameters: Parameters, private val publicKey: String, private val timestamp: Long) : BaseJob(parameters) { class SessionRequestMessageSendJob private constructor(parameters: Parameters, private val publicKey: String, private val timestamp: Long) : BaseJob(parameters) {
companion object { companion object {
const val KEY = "PushSessionRequestMessageSendJob" const val KEY = "SessionRequestMessageSendJob"
} }
constructor(publicKey: String, timestamp: Long) : this(Parameters.Builder() constructor(publicKey: String, timestamp: Long) : this(Parameters.Builder()

View File

@ -1,24 +1,36 @@
package org.thoughtcrime.securesms.loki.utilities package org.thoughtcrime.securesms.loki.utilities
import android.content.Intent import android.content.Intent
import androidx.appcompat.app.ActionBar import android.view.View
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import android.view.Gravity import androidx.appcompat.widget.Toolbar
import android.widget.ImageView
import android.widget.RelativeLayout
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.BaseActionBarActivity
fun AppCompatActivity.setUpActionBarSessionLogo() { fun BaseActionBarActivity.setUpActionBarSessionLogo(hideBackButton: Boolean = false) {
supportActionBar!!.setDisplayShowHomeEnabled(false) val actionbar = supportActionBar!!
supportActionBar!!.setDisplayShowTitleEnabled(false)
val logoImageView = ImageView(this) actionbar.setDisplayShowHomeEnabled(false)
logoImageView.setImageResource(R.drawable.session_logo) actionbar.setDisplayShowTitleEnabled(false)
val logoImageViewContainer = RelativeLayout(this) actionbar.setDisplayHomeAsUpEnabled(false)
logoImageViewContainer.addView(logoImageView) actionbar.setHomeButtonEnabled(false)
logoImageViewContainer.gravity = Gravity.CENTER
val logoImageViewContainerLayoutParams = ActionBar.LayoutParams(ActionBar.LayoutParams.MATCH_PARENT, ActionBar.LayoutParams.WRAP_CONTENT) actionbar.setCustomView(R.layout.session_logo_action_bar_content)
supportActionBar!!.setCustomView(logoImageViewContainer, logoImageViewContainerLayoutParams) actionbar.setDisplayShowCustomEnabled(true)
supportActionBar!!.setDisplayShowCustomEnabled(true)
val rootView: Toolbar = actionbar.customView!!.parent as Toolbar
rootView.setPadding(0,0,0,0)
rootView.setContentInsetsAbsolute(0,0);
val backButton = actionbar.customView!!.findViewById<View>(R.id.back_button)
if (hideBackButton) {
backButton.visibility = View.GONE
} else {
backButton.visibility = View.VISIBLE
backButton.setOnClickListener {
onSupportNavigateUp()
}
}
} }
val AppCompatActivity.defaultSessionRequestCode: Int val AppCompatActivity.defaultSessionRequestCode: Int

View File

@ -27,6 +27,7 @@ import android.view.animation.DecelerateInterpolator;
import android.view.animation.RotateAnimation; import android.view.animation.RotateAnimation;
import android.widget.Button; import android.widget.Button;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.ImageView;
import com.bumptech.glide.load.MultiTransformation; import com.bumptech.glide.load.MultiTransformation;
import com.bumptech.glide.load.Transformation; import com.bumptech.glide.load.Transformation;
@ -51,6 +52,7 @@ public class Camera1Fragment extends Fragment implements TextureView.SurfaceText
private TextureView cameraPreview; private TextureView cameraPreview;
private ViewGroup controlsContainer; private ViewGroup controlsContainer;
private View cameraCloseButton;
private ImageButton flipButton; private ImageButton flipButton;
private Button captureButton; private Button captureButton;
private Camera1Controller camera; private Camera1Controller camera;
@ -95,6 +97,7 @@ public class Camera1Fragment extends Fragment implements TextureView.SurfaceText
cameraPreview = view.findViewById(R.id.camera_preview); cameraPreview = view.findViewById(R.id.camera_preview);
controlsContainer = view.findViewById(R.id.camera_controls_container); controlsContainer = view.findViewById(R.id.camera_controls_container);
cameraCloseButton = view.findViewById(R.id.camera_close_button);
onOrientationChanged(getResources().getConfiguration().orientation); onOrientationChanged(getResources().getConfiguration().orientation);
@ -102,6 +105,8 @@ public class Camera1Fragment extends Fragment implements TextureView.SurfaceText
GestureDetector gestureDetector = new GestureDetector(flipGestureListener); GestureDetector gestureDetector = new GestureDetector(flipGestureListener);
cameraPreview.setOnTouchListener((v, event) -> gestureDetector.onTouchEvent(event)); cameraPreview.setOnTouchListener((v, event) -> gestureDetector.onTouchEvent(event));
cameraCloseButton.setOnClickListener(v -> requireActivity().onBackPressed());
} }
@Override @Override

View File

@ -1,5 +1,7 @@
package org.thoughtcrime.securesms.mediasend; package org.thoughtcrime.securesms.mediasend;
import androidx.appcompat.app.ActionBar;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelProviders; import androidx.lifecycle.ViewModelProviders;
import android.content.Context; import android.content.Context;
import android.content.res.Configuration; import android.content.res.Configuration;
@ -53,7 +55,7 @@ public class MediaPickerFolderFragment extends Fragment implements MediaPickerFo
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
recipientName = getArguments().getString(KEY_RECIPIENT_NAME); recipientName = getArguments().getString(KEY_RECIPIENT_NAME);
viewModel = ViewModelProviders.of(requireActivity(), new MediaSendViewModel.Factory(requireActivity().getApplication(), new MediaRepository())).get(MediaSendViewModel.class); viewModel = new ViewModelProvider(requireActivity(), new MediaSendViewModel.Factory(requireActivity().getApplication(), new MediaRepository())).get(MediaSendViewModel.class);
} }
@Override @Override
@ -85,7 +87,7 @@ public class MediaPickerFolderFragment extends Fragment implements MediaPickerFo
list.setLayoutManager(layoutManager); list.setLayoutManager(layoutManager);
list.setAdapter(adapter); list.setAdapter(adapter);
viewModel.getFolders(requireContext()).observe(this, adapter::setFolders); viewModel.getFolders(requireContext()).observe(getViewLifecycleOwner(), adapter::setFolders);
initToolbar(view.findViewById(R.id.mediapicker_toolbar)); initToolbar(view.findViewById(R.id.mediapicker_toolbar));
} }
@ -107,7 +109,10 @@ public class MediaPickerFolderFragment extends Fragment implements MediaPickerFo
private void initToolbar(Toolbar toolbar) { private void initToolbar(Toolbar toolbar) {
((AppCompatActivity) requireActivity()).setSupportActionBar(toolbar); ((AppCompatActivity) requireActivity()).setSupportActionBar(toolbar);
((AppCompatActivity) requireActivity()).getSupportActionBar().setTitle(getString(R.string.MediaPickerActivity_send_to, recipientName)); ActionBar actionBar = ((AppCompatActivity) requireActivity()).getSupportActionBar();
actionBar.setTitle(getString(R.string.MediaPickerActivity_send_to, recipientName));
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeButtonEnabled(true);
toolbar.setNavigationOnClickListener(v -> requireActivity().onBackPressed()); toolbar.setNavigationOnClickListener(v -> requireActivity().onBackPressed());
} }

View File

@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.mediasend; package org.thoughtcrime.securesms.mediasend;
import androidx.appcompat.app.ActionBar;
import androidx.lifecycle.ViewModelProviders; import androidx.lifecycle.ViewModelProviders;
import android.content.Context; import android.content.Context;
import android.content.res.Configuration; import android.content.res.Configuration;
@ -166,8 +167,12 @@ public class MediaPickerItemFragment extends Fragment implements MediaPickerItem
} }
private void initToolbar(Toolbar toolbar) { private void initToolbar(Toolbar toolbar) {
((AppCompatActivity) requireActivity()).setSupportActionBar(toolbar); AppCompatActivity activity = (AppCompatActivity) requireActivity();
((AppCompatActivity) requireActivity()).getSupportActionBar().setTitle(folderTitle); activity.setSupportActionBar(toolbar);
ActionBar actionBar = activity.getSupportActionBar();
actionBar.setTitle(folderTitle);
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeButtonEnabled(true);
toolbar.setNavigationOnClickListener(v -> requireActivity().onBackPressed()); toolbar.setNavigationOnClickListener(v -> requireActivity().onBackPressed());
} }

View File

@ -1,16 +1,10 @@
package org.thoughtcrime.securesms.mediasend; package org.thoughtcrime.securesms.mediasend;
import android.Manifest; import android.Manifest;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelProviders;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import android.view.View; import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator; import android.view.animation.AccelerateInterpolator;
@ -21,8 +15,13 @@ import android.view.animation.ScaleAnimation;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.ViewModelProvider;
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity; import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
import network.loki.messenger.R;
import org.thoughtcrime.securesms.TransportOption; import org.thoughtcrime.securesms.TransportOption;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
@ -41,6 +40,8 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import network.loki.messenger.R;
/** /**
* Encompasses the entire flow of sending media, starting from the selection process to the actual * Encompasses the entire flow of sending media, starting from the selection process to the actual
* captioning and editing of the content. * captioning and editing of the content.
@ -124,6 +125,8 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
@Override @Override
protected void onCreate(Bundle savedInstanceState, boolean ready) { protected void onCreate(Bundle savedInstanceState, boolean ready) {
super.onCreate(savedInstanceState, ready);
setContentView(R.layout.mediasend_activity); setContentView(R.layout.mediasend_activity);
setResult(RESULT_CANCELED); setResult(RESULT_CANCELED);

View File

@ -91,6 +91,7 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
private Stub<MediaKeyboard> emojiDrawer; private Stub<MediaKeyboard> emojiDrawer;
private ViewGroup playbackControlsContainer; private ViewGroup playbackControlsContainer;
private TextView charactersLeft; private TextView charactersLeft;
private View closeButton;
private ControllableViewPager fragmentPager; private ControllableViewPager fragmentPager;
private MediaSendFragmentPagerAdapter fragmentPagerAdapter; private MediaSendFragmentPagerAdapter fragmentPagerAdapter;
@ -154,6 +155,7 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
mediaRail = view.findViewById(R.id.mediasend_media_rail); mediaRail = view.findViewById(R.id.mediasend_media_rail);
playbackControlsContainer = view.findViewById(R.id.mediasend_playback_controls_container); playbackControlsContainer = view.findViewById(R.id.mediasend_playback_controls_container);
charactersLeft = view.findViewById(R.id.mediasend_characters_left); charactersLeft = view.findViewById(R.id.mediasend_characters_left);
closeButton = view.findViewById(R.id.mediasend_close_button);
View sendButtonBkg = view.findViewById(R.id.mediasend_send_button_bkg); View sendButtonBkg = view.findViewById(R.id.mediasend_send_button_bkg);
@ -227,6 +229,8 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
} else { } else {
emojiToggle.setOnClickListener(this::onEmojiToggleClicked); emojiToggle.setOnClickListener(this::onEmojiToggleClicked);
} }
closeButton.setOnClickListener(v -> requireActivity().onBackPressed());
} }
@Override @Override