mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-23 18:15:22 +00:00
Trim unused files
This commit is contained in:
parent
31350adcf7
commit
27fdfe4ee8
@ -1,5 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
|
||||
<solid android:color="@color/compose_view_background" />
|
||||
|
||||
</shape>
|
@ -1,58 +1,59 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView
|
||||
<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="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/default_session_background"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:orientation="vertical">
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:textSize="@dimen/large_font_size"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/text"
|
||||
android:text="Pick your display name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/titleTextView"
|
||||
style="@style/Signal.Text.Headline.Registration"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="40dp"
|
||||
android:text="@string/activity_display_name_title"
|
||||
android:textAlignment="center" />
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:textSize="@dimen/small_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:text="This will be your name when you use Session." />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtitleTextView"
|
||||
style="@style/Signal.Text.Body"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/activity_display_name_subtitle"
|
||||
android:textAlignment="center" />
|
||||
<EditText
|
||||
style="@style/SmallSessionEditText"
|
||||
android:id="@+id/displayNameEditText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:hint="Enter a display name" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.LabeledEditText
|
||||
android:id="@+id/nameEditText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
app:labeledEditText_background="@color/loki_darkest_gray"
|
||||
app:labeledEditText_label="@string/activity_display_name_name_edit_text_label"/>
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<com.dd.CircularProgressButton
|
||||
android:id="@+id/nextButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginBottom="40dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:background="@color/signal_primary"
|
||||
android:textColor="@color/white"
|
||||
app:cpb_colorIndicator="@color/white"
|
||||
app:cpb_colorProgress="@color/textsecure_primary"
|
||||
app:cpb_cornerRadius="4dp"
|
||||
app:cpb_selectorIdle="@drawable/progress_button_state"
|
||||
app:cpb_textIdle="@string/activity_display_name_button_title" />
|
||||
<Button
|
||||
style="@style/MediumProminentFilledButton"
|
||||
android:id="@+id/registerButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/medium_button_height"
|
||||
android:layout_marginLeft="@dimen/massive_spacing"
|
||||
android:layout_marginRight="@dimen/massive_spacing"
|
||||
android:layout_marginBottom="@dimen/small_spacing"
|
||||
android:text="Continue" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
@ -1,59 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/default_session_background"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:textSize="@dimen/large_font_size"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/text"
|
||||
android:text="Pick your display name" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:textSize="@dimen/small_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:text="This will be your name when you use Session." />
|
||||
|
||||
<EditText
|
||||
style="@style/SmallSessionEditText"
|
||||
android:id="@+id/displayNameEditText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:hint="Enter a display name" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<Button
|
||||
style="@style/MediumProminentFilledButton"
|
||||
android:id="@+id/registerButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/medium_button_height"
|
||||
android:layout_marginLeft="@dimen/massive_spacing"
|
||||
android:layout_marginRight="@dimen/massive_spacing"
|
||||
android:layout_marginBottom="@dimen/small_spacing"
|
||||
android:text="Continue" />
|
||||
|
||||
</LinearLayout>
|
@ -1,158 +1,77 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".loki.SeedActivity">
|
||||
android:background="@drawable/default_session_background"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
<org.thoughtcrime.securesms.loki.redesign.views.SeedReminderView
|
||||
android:id="@+id/seedReminderView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:orientation="vertical"
|
||||
android:animateLayoutChanges="true">
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:textSize="@dimen/large_font_size"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/text"
|
||||
android:text="Meet your recovery phrase" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/titleTextView"
|
||||
style="@style/Signal.Text.Headline.Registration"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="40dp"
|
||||
android:text="@string/activity_key_pair_title"
|
||||
android:textAlignment="center" />
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:text="Your recovery phrase is the master key to your Session ID — you can use it to restore your Session ID if you lose access to your device. Store your recovery phrase in a safe place, and don’t give it to anyone. To restore your Session ID, launch Session and tap Continue your Session." />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/seedExplanationTextView1"
|
||||
style="@style/Signal.Text.Body"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:text="@string/activity_key_pair_seed_explanation_1"
|
||||
android:textStyle="bold"
|
||||
android:textAlignment="center" />
|
||||
<TextView
|
||||
style="@style/SessionIDTextView"
|
||||
android:id="@+id/seedTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="@dimen/medium_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:gravity="center"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textAlignment="center"
|
||||
android:text="nautical novelty populate onion awkward bent etiquette plant submarine itches vipers september axis maximum populate" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/mnemonicTextView"
|
||||
style="@style/Signal.Text.Body"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:alpha="0.8"
|
||||
android:textStyle="italic"
|
||||
android:textAlignment="center"
|
||||
tools:text="quick brown fox jump lazy dog" />
|
||||
<TextView
|
||||
android:id="@+id/revealButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:textAlignment="center"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:alpha="0.6"
|
||||
android:text="Hold to reveal" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/copyButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:background="@color/transparent"
|
||||
android:textColor="@color/signal_primary"
|
||||
android:text="@string/activity_key_pair_copy_button_title"
|
||||
android:elevation="0dp"
|
||||
android:stateListAnimator="@null" />
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/seedExplanationTextView2"
|
||||
style="@style/Signal.Text.Body"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:visibility="gone"
|
||||
android:text="@string/activity_key_pair_seed_explanation_2"
|
||||
android:textAlignment="center" />
|
||||
<Button
|
||||
style="@style/MediumProminentOutlineButton"
|
||||
android:id="@+id/copyButton"
|
||||
android:layout_width="196dp"
|
||||
android:layout_height="@dimen/medium_button_height"
|
||||
android:layout_marginBottom="@dimen/medium_spacing"
|
||||
android:text="Copy" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.LabeledEditText
|
||||
android:id="@+id/mnemonicEditText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:visibility="gone"
|
||||
app:labeledEditText_background="@color/loki_darkest_gray"
|
||||
app:labeledEditText_label="@string/activity_key_pair_mnemonic_edit_text_label"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/linkExplanationTextView"
|
||||
style="@style/Signal.Text.Body"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:visibility="gone"
|
||||
android:text="@string/activity_key_pair_seed_explanation_3"
|
||||
android:textAlignment="center" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.LabeledEditText
|
||||
android:id="@+id/publicKeyEditText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:visibility="gone"
|
||||
app:labeledEditText_background="@color/loki_darkest_gray"
|
||||
app:labeledEditText_label="@string/activity_key_pair_public_key_edit_text_label"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/scanQRButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:background="@color/transparent"
|
||||
android:elevation="0dp"
|
||||
android:stateListAnimator="@null"
|
||||
android:text="@string/fragment_scan_qr_code_title"
|
||||
android:textColor="@color/signal_primary"
|
||||
android:visibility="gone" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/toggleRestoreModeButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:background="@color/transparent"
|
||||
android:textColor="@color/signal_primary"
|
||||
android:text="@string/activity_key_pair_toggle_mode_button_title_1"
|
||||
android:elevation="0dp"
|
||||
android:stateListAnimator="@null" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/toggleRegisterModeButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:background="@color/transparent"
|
||||
android:textColor="@color/signal_primary"
|
||||
android:text="@string/activity_key_pair_toggle_mode_button_title_2"
|
||||
android:visibility="gone"
|
||||
android:elevation="0dp"
|
||||
android:stateListAnimator="@null" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/toggleLinkModeButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:background="@color/transparent"
|
||||
android:textColor="@color/signal_primary"
|
||||
android:text="@string/activity_key_pair_toggle_mode_button_title_3"
|
||||
android:elevation="0dp"
|
||||
android:stateListAnimator="@null" />
|
||||
|
||||
<com.dd.CircularProgressButton
|
||||
android:id="@+id/mainButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginBottom="40dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:background="@color/signal_primary"
|
||||
android:textColor="@color/white"
|
||||
app:cpb_colorIndicator="@color/white"
|
||||
app:cpb_colorProgress="@color/textsecure_primary"
|
||||
app:cpb_cornerRadius="4dp"
|
||||
app:cpb_selectorIdle="@drawable/progress_button_state"
|
||||
app:cpb_textIdle="@string/activity_key_pair_main_button_title_1" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
@ -1,77 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/default_session_background"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical">
|
||||
|
||||
<org.thoughtcrime.securesms.loki.redesign.views.SeedReminderView
|
||||
android:id="@+id/seedReminderView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:textSize="@dimen/large_font_size"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/text"
|
||||
android:text="Meet your recovery phrase" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:text="Your recovery phrase is the master key to your Session ID — you can use it to restore your Session ID if you lose access to your device. Store your recovery phrase in a safe place, and don’t give it to anyone. To restore your Session ID, launch Session and tap Continue your Session." />
|
||||
|
||||
<TextView
|
||||
style="@style/SessionIDTextView"
|
||||
android:id="@+id/seedTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||
android:layout_marginTop="@dimen/medium_spacing"
|
||||
android:layout_marginRight="@dimen/very_large_spacing"
|
||||
android:gravity="center"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textAlignment="center"
|
||||
android:text="nautical novelty populate onion awkward bent etiquette plant submarine itches vipers september axis maximum populate" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/revealButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:textAlignment="center"
|
||||
android:textSize="@dimen/medium_font_size"
|
||||
android:textColor="@color/text"
|
||||
android:alpha="0.6"
|
||||
android:text="Hold to reveal" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<Button
|
||||
style="@style/MediumProminentOutlineButton"
|
||||
android:id="@+id/copyButton"
|
||||
android:layout_width="196dp"
|
||||
android:layout_height="@dimen/medium_button_height"
|
||||
android:layout_marginBottom="@dimen/medium_spacing"
|
||||
android:text="Copy" />
|
||||
|
||||
</LinearLayout>
|
@ -38,7 +38,7 @@
|
||||
android:id="@+id/moderator_icon_image_view"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:src="@drawable/icon_crown"
|
||||
android:src="@drawable/ic_crown"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentBottom="true" />
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
android:id="@+id/moderatorIconImageView"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:src="@drawable/icon_crown"
|
||||
android:src="@drawable/ic_crown"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentBottom="true" />
|
||||
|
||||
|
@ -191,7 +191,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
}
|
||||
// Loki - Set up public chat manager
|
||||
lokiPublicChatManager = new LokiPublicChatManager(this);
|
||||
updatePublicChatProfileAvatarIfNeeded();
|
||||
updatePublicChatProfilePictureIfNeeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -202,6 +202,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
KeyCachingService.onAppForegrounded(this);
|
||||
// Loki - Start long polling if needed
|
||||
startLongPollingIfNeeded();
|
||||
// Loki - Start open group polling if needed
|
||||
lokiPublicChatManager.startPollersIfNeeded();
|
||||
}
|
||||
|
||||
@ -530,7 +531,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
|
||||
public void createRSSFeedsIfNeeded() {
|
||||
ArrayList<LokiRSSFeed> feeds = new ArrayList<>();
|
||||
feeds.add(lokiNewsFeed());
|
||||
// feeds.add(lokiNewsFeed());
|
||||
feeds.add(lokiMessengerUpdatesFeed());
|
||||
for (LokiRSSFeed feed : feeds) {
|
||||
boolean isFeedSetUp = TextSecurePreferences.isChatSetUp(this, feed.getId());
|
||||
@ -586,7 +587,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
if (lokiMessengerUpdatesFeedPoller != null) lokiMessengerUpdatesFeedPoller.startIfNeeded();
|
||||
}
|
||||
|
||||
public void updatePublicChatProfileAvatarIfNeeded() {
|
||||
public void updatePublicChatProfilePictureIfNeeded() {
|
||||
AsyncTask.execute(() -> {
|
||||
LokiPublicChatAPI publicChatAPI = null;
|
||||
try {
|
||||
|
@ -422,7 +422,7 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
|
||||
ProfileKeyUtil.setEncodedProfileKey(context, newProfileKey);
|
||||
|
||||
// Update profile key on the public chat server
|
||||
ApplicationContext.getInstance(context).updatePublicChatProfileAvatarIfNeeded();
|
||||
ApplicationContext.getInstance(context).updatePublicChatProfilePictureIfNeeded();
|
||||
} catch (Exception e) {
|
||||
Log.d("Loki", "Failed to upload profile photo: " + e);
|
||||
return false;
|
||||
|
@ -331,7 +331,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
private MenuItem searchViewItem;
|
||||
private ProgressBar messageStatusProgressBar;
|
||||
private ImageView muteIndicatorImageView;
|
||||
private TextView actionBarSubtitleTextView;
|
||||
private TextView subtitleTextView;
|
||||
|
||||
private AttachmentTypeSelector attachmentTypeSelector;
|
||||
private AttachmentManager attachmentManager;
|
||||
@ -362,6 +362,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
private final DynamicNoActionBarTheme dynamicTheme = new DynamicNoActionBarTheme();
|
||||
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
|
||||
|
||||
// Message Status Bar
|
||||
private ArrayList<BroadcastReceiver> broadcastReceivers = new ArrayList<>();
|
||||
private String messageStatus = null;
|
||||
|
||||
@ -552,7 +553,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
initializeIdentityRecords();
|
||||
composeText.setTransport(sendButton.getSelectedTransport());
|
||||
|
||||
updateTitleTextView(glideRequests, recipient);
|
||||
updateTitleTextView(recipient);
|
||||
updateSubtitleTextView();
|
||||
setActionBarColor(recipient.getColor());
|
||||
setBlockedUserState(recipient, isSecureText, isDefaultSms);
|
||||
@ -645,7 +646,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
case GROUP_EDIT:
|
||||
recipient = Recipient.from(this, data.getParcelableExtra(GroupCreateActivity.GROUP_ADDRESS_EXTRA), true);
|
||||
recipient.addListener(this);
|
||||
updateTitleTextView(glideRequests, recipient);
|
||||
updateTitleTextView(recipient);
|
||||
updateSubtitleTextView();
|
||||
NotificationChannels.updateContactChannelName(this, recipient);
|
||||
setBlockedUserState(recipient, isSecureText, isDefaultSms);
|
||||
@ -749,9 +750,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
MenuInflater inflater = this.getMenuInflater();
|
||||
menu.clear();
|
||||
|
||||
boolean isLokiGroupChat = recipient.getAddress().isPublicChat() || recipient.getAddress().isRSSFeed();
|
||||
boolean isOpenGroupOrRSSFeed = recipient.getAddress().isPublicChat() || recipient.getAddress().isRSSFeed();
|
||||
|
||||
if (isSecureText && !isLokiGroupChat) {
|
||||
if (isSecureText && !isOpenGroupOrRSSFeed) {
|
||||
if (recipient.getExpireMessages() > 0) {
|
||||
inflater.inflate(R.menu.conversation_expiring_on, menu);
|
||||
|
||||
@ -771,7 +772,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
if (isSecureText) inflater.inflate(R.menu.conversation_callable_secure, menu);
|
||||
else inflater.inflate(R.menu.conversation_callable_insecure, menu);
|
||||
*/
|
||||
} else if (isGroupConversation() && !isLokiGroupChat) {
|
||||
} else if (isGroupConversation() && !isOpenGroupOrRSSFeed) {
|
||||
inflater.inflate(R.menu.conversation_group_options, menu);
|
||||
|
||||
if (!isPushGroupConversation()) {
|
||||
@ -1688,7 +1689,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
sessionRestoreBannerView = ViewUtil.findById(this, R.id.sessionRestoreBannerView);
|
||||
messageStatusProgressBar = ViewUtil.findById(this, R.id.messageStatusProgressBar);
|
||||
muteIndicatorImageView = ViewUtil.findById(this, R.id.muteIndicatorImageView);
|
||||
actionBarSubtitleTextView = ViewUtil.findById(this, R.id.subtitleTextView);
|
||||
subtitleTextView = ViewUtil.findById(this, R.id.subtitleTextView);
|
||||
|
||||
ImageButton quickCameraToggle = ViewUtil.findById(this, R.id.quick_camera_toggle);
|
||||
ImageButton inlineAttachmentButton = ViewUtil.findById(this, R.id.inline_attachment_button);
|
||||
@ -1879,7 +1880,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
Log.i(TAG, "onModified(" + recipient.getAddress().serialize() + ")");
|
||||
Util.runOnMain(() -> {
|
||||
Log.i(TAG, "onModifiedRun(): " + recipient.getRegistered());
|
||||
updateTitleTextView(glideRequests, recipient);
|
||||
updateTitleTextView(recipient);
|
||||
updateSubtitleTextView();
|
||||
// titleView.setVerified(identityRecords.isVerified());
|
||||
setBlockedUserState(recipient, isSecureText, isDefaultSms);
|
||||
@ -2322,33 +2323,29 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
private void updateInputPanel() {
|
||||
/*
|
||||
isFriendsWithAnyDevice caches whether we are friends with any of the other users device.
|
||||
isFriendsWithAnyDevice reflects whether we are friends with any of the other user's devices.
|
||||
|
||||
This stops the case where the input panel disables and enables rapidly.
|
||||
- This can occur when we are not friends with the current thread BUT multi-device tells us that we are friends with another one of their devices.
|
||||
This fixes the case where the input panel disables and enables rapidly, which can occur when we are
|
||||
not friends with the current thread BUT multi device tells us that we are friends with another one of their devices.
|
||||
*/
|
||||
if (recipient.isGroupRecipient() || isNoteToSelf() || isFriendsWithAnyDevice) {
|
||||
setInputPanelEnabled(true);
|
||||
return;
|
||||
}
|
||||
if (recipient.isGroupRecipient() || isNoteToSelf() || isFriendsWithAnyDevice) { setInputPanelEnabled(true); return; }
|
||||
|
||||
// It could take a while before our promise resolves, so we assume the best case
|
||||
// Disable the input panel if a friend request is pending
|
||||
LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(this).getFriendRequestStatus(threadId);
|
||||
boolean isPending = friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_SENDING || friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_SENT || friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_RECEIVED;
|
||||
setInputPanelEnabled(!isPending);
|
||||
|
||||
// We should always have the input panel enabled if we are friends with the current user
|
||||
isFriendsWithAnyDevice = friendRequestStatus == LokiThreadFriendRequestStatus.FRIENDS;
|
||||
// Always enable the input panel if we are friends with the current user
|
||||
isFriendsWithAnyDevice = (friendRequestStatus == LokiThreadFriendRequestStatus.FRIENDS);
|
||||
|
||||
// Multi-device input logic
|
||||
if (!isFriendsWithAnyDevice) {
|
||||
// We should enable the input if we don't have any pending friend requests OR we are friends with a linked device
|
||||
MultiDeviceUtilities.hasPendingFriendRequestWithAnyLinkedDevice(this, recipient).success(hasPendingRequests -> {
|
||||
// Enable the input panel if we don't have any pending friend requests OR we are friends with one of the user's linked devices
|
||||
MultiDeviceUtilities.hasPendingFriendRequestWithAnyLinkedDevice(this, recipient).success( hasPendingRequests -> {
|
||||
if (!hasPendingRequests) {
|
||||
setInputPanelEnabled(true);
|
||||
} else {
|
||||
MultiDeviceUtilities.isFriendsWithAnyLinkedDevice(this, recipient).success(isFriends -> {
|
||||
// If we are friend with any of the other devices then we want to make sure the input panel is always enabled for the duration of this conversation
|
||||
MultiDeviceUtilities.isFriendsWithAnyLinkedDevice(this, recipient).success( isFriends -> {
|
||||
// Enable the input panel if we're friends with any of the user's devices
|
||||
isFriendsWithAnyDevice = isFriends;
|
||||
setInputPanelEnabled(isFriends);
|
||||
return Unit.INSTANCE;
|
||||
@ -2574,7 +2571,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
|
||||
private void updateToggleButtonState() {
|
||||
// Don't allow attachments if we're not friends with any device
|
||||
// Don't allow attachments if we're not friends with any of the user's devices
|
||||
if (!isNoteToSelf() && !recipient.isGroupRecipient() && !isFriendsWithAnyDevice) {
|
||||
buttonToggle.display(sendButton);
|
||||
quickAttachmentToggle.hide();
|
||||
@ -3169,7 +3166,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
|
||||
// region Loki
|
||||
private void updateTitleTextView(GlideRequests glide, Recipient recipient) {
|
||||
private void updateTitleTextView(Recipient recipient) {
|
||||
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
|
||||
Set<DeviceLink> deviceLinks = DatabaseFactory.getLokiAPIDatabase(this).getDeviceLinks(userHexEncodedPublicKey);
|
||||
HashSet<String> userLinkedDeviceHexEncodedPublicKeys = new HashSet<>();
|
||||
@ -3183,45 +3180,46 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
} else if (userLinkedDeviceHexEncodedPublicKeys.contains(recipient.getAddress().toString().toLowerCase())) {
|
||||
titleTextView.setText("Note to Self");
|
||||
} else {
|
||||
titleTextView.setText((recipient.getName() == null || recipient.getName().isEmpty()) ? recipient.getAddress().toString() : recipient.getName());
|
||||
boolean hasName = (recipient.getName() != null && !recipient.getName().isEmpty());
|
||||
titleTextView.setText(hasName ? recipient.getName() : recipient.getAddress().toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSubtitleTextView() {
|
||||
muteIndicatorImageView.setVisibility(View.GONE);
|
||||
actionBarSubtitleTextView.setVisibility(View.VISIBLE);
|
||||
subtitleTextView.setVisibility(View.VISIBLE);
|
||||
if (messageStatus != null) {
|
||||
switch (messageStatus) {
|
||||
case "calculatingPoW": actionBarSubtitleTextView.setText("Encrypting message"); break;
|
||||
case "contactingNetwork": actionBarSubtitleTextView.setText("Tracing a path"); break;
|
||||
case "sendingMessage": actionBarSubtitleTextView.setText("Sending message"); break;
|
||||
case "messageSent": actionBarSubtitleTextView.setText("Message sent securely"); break;
|
||||
case "messageFailed": actionBarSubtitleTextView.setText("Message failed to send"); break;
|
||||
case "calculatingPoW": subtitleTextView.setText("Encrypting message"); break;
|
||||
case "contactingNetwork": subtitleTextView.setText("Tracing a path"); break;
|
||||
case "sendingMessage": subtitleTextView.setText("Sending message"); break;
|
||||
case "messageSent": subtitleTextView.setText("Message sent securely"); break;
|
||||
case "messageFailed": subtitleTextView.setText("Message failed to send"); break;
|
||||
}
|
||||
} else if (recipient.isMuted()) {
|
||||
muteIndicatorImageView.setVisibility(View.VISIBLE);
|
||||
actionBarSubtitleTextView.setText("Muted until " + DateUtils.getFormattedDateTime(recipient.mutedUntil, "EEE, MMM d, yyyy HH:mm", Locale.getDefault()));
|
||||
subtitleTextView.setText("Muted until " + DateUtils.getFormattedDateTime(recipient.mutedUntil, "EEE, MMM d, yyyy HH:mm", Locale.getDefault()));
|
||||
} else if (recipient.isGroupRecipient() && recipient.getName() != null && !recipient.getName().equals("Session Updates") && !recipient.getName().equals("Loki News")) {
|
||||
LokiPublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId);
|
||||
if (publicChat != null) {
|
||||
Integer userCount = DatabaseFactory.getLokiAPIDatabase(this).getUserCount(publicChat.getChannel(), publicChat.getServer());
|
||||
if (userCount == null) { userCount = 0; }
|
||||
if (userCount >= 200) {
|
||||
actionBarSubtitleTextView.setText("200+ members");
|
||||
subtitleTextView.setText("200+ members");
|
||||
} else {
|
||||
actionBarSubtitleTextView.setText(userCount + " members");
|
||||
subtitleTextView.setText(userCount + " members");
|
||||
}
|
||||
} else if (PublicKeyValidation.isValid(recipient.getAddress().toString())) {
|
||||
actionBarSubtitleTextView.setText(recipient.getAddress().toString());
|
||||
subtitleTextView.setText(recipient.getAddress().toString());
|
||||
} else {
|
||||
actionBarSubtitleTextView.setVisibility(View.GONE);
|
||||
subtitleTextView.setVisibility(View.GONE);
|
||||
}
|
||||
} else if (PublicKeyValidation.isValid(recipient.getAddress().toString())) {
|
||||
actionBarSubtitleTextView.setText(recipient.getAddress().toString());
|
||||
subtitleTextView.setText(recipient.getAddress().toString());
|
||||
} else {
|
||||
actionBarSubtitleTextView.setVisibility(View.GONE);
|
||||
subtitleTextView.setVisibility(View.GONE);
|
||||
}
|
||||
titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimension((actionBarSubtitleTextView.getVisibility() == View.GONE) ? R.dimen.very_large_font_size : R.dimen.large_font_size));
|
||||
titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimension((subtitleTextView.getVisibility() == View.GONE) ? R.dimen.very_large_font_size : R.dimen.large_font_size));
|
||||
}
|
||||
|
||||
private void setMessageStatusProgressAnimatedIfPossible(int progress) {
|
||||
@ -3293,18 +3291,17 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
@Override
|
||||
public void acceptFriendRequest(@NotNull MessageRecord friendRequest) {
|
||||
// Send the accept to the original friend request thread id
|
||||
// Send the accept to the original friend request thread ID
|
||||
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(this);
|
||||
long originalThreadID = lokiMessageDatabase.getOriginalThreadID(friendRequest.id);
|
||||
long threadId = originalThreadID < 0 ? this.threadId : originalThreadID;
|
||||
|
||||
Recipient contact = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadId);
|
||||
long threadID = originalThreadID < 0 ? this.threadId : originalThreadID;
|
||||
Recipient contact = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadID);
|
||||
Address address = contact.getAddress();
|
||||
String contactPubKey = address.serialize();
|
||||
DatabaseFactory.getLokiThreadDatabase(this).setFriendRequestStatus(threadId, LokiThreadFriendRequestStatus.FRIENDS);
|
||||
String contactHexEncodedPublicKey = address.serialize();
|
||||
DatabaseFactory.getLokiThreadDatabase(this).setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.FRIENDS);
|
||||
lokiMessageDatabase.setFriendRequestStatus(friendRequest.id, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED);
|
||||
DatabaseFactory.getRecipientDatabase(this).setProfileSharing(contact, true);
|
||||
MessageSender.sendBackgroundMessageToAllDevices(this, contactPubKey);
|
||||
MessageSender.sendBackgroundMessageToAllDevices(this, contactHexEncodedPublicKey);
|
||||
MessageSender.syncContact(this, address);
|
||||
updateInputPanel();
|
||||
}
|
||||
@ -3313,10 +3310,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
public void rejectFriendRequest(@NotNull MessageRecord friendRequest) {
|
||||
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(this);
|
||||
long originalThreadID = lokiMessageDatabase.getOriginalThreadID(friendRequest.id);
|
||||
long threadId = originalThreadID < 0 ? this.threadId : originalThreadID;
|
||||
|
||||
DatabaseFactory.getLokiThreadDatabase(this).setFriendRequestStatus(threadId, LokiThreadFriendRequestStatus.NONE);
|
||||
String contactID = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadId).getAddress().toString();
|
||||
long threadID = originalThreadID < 0 ? this.threadId : originalThreadID;
|
||||
DatabaseFactory.getLokiThreadDatabase(this).setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.NONE);
|
||||
String contactID = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadID).getAddress().toString();
|
||||
DatabaseFactory.getLokiPreKeyBundleDatabase(this).removePreKeyBundle(contactID);
|
||||
updateInputPanel();
|
||||
}
|
||||
@ -3324,20 +3320,19 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
public boolean isNoteToSelf() {
|
||||
return TextSecurePreferences.getLocalNumber(this).equals(recipient.getAddress().serialize());
|
||||
}
|
||||
// endregion
|
||||
|
||||
public void restoreSession() {
|
||||
// Loki - User clicked restore session
|
||||
if (recipient.isGroupRecipient()) { return; }
|
||||
LokiThreadDatabase lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(this);
|
||||
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(this);
|
||||
Set<String> devices = lokiThreadDatabase.getSessionRestoreDevices(threadId);
|
||||
for (String device : devices) { MessageSender.sendRestoreSessionMessage(this, device); }
|
||||
long messageId = smsDatabase.insertMessageOutbox(threadId, new OutgoingTextMessage(recipient,"", 0, 0), false, System.currentTimeMillis(), null);
|
||||
if (messageId > -1) {
|
||||
smsDatabase.markAsLokiSessionRestoreSent(messageId);
|
||||
long messageID = smsDatabase.insertMessageOutbox(threadId, new OutgoingTextMessage(recipient,"", 0, 0), false, System.currentTimeMillis(), null);
|
||||
if (messageID > -1) {
|
||||
smsDatabase.markAsLokiSessionRestoreSent(messageID);
|
||||
}
|
||||
lokiThreadDatabase.removeAllSessionRestoreDevices(threadId);
|
||||
updateSessionRestoreBanner();
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
@ -1543,7 +1543,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
|
||||
// Loki - If the recipient is our master device then we need to go and update our avatar mappings on the public chats
|
||||
if (recipient.isOurMasterDevice()) {
|
||||
ApplicationContext.getInstance(context).updatePublicChatProfileAvatarIfNeeded();
|
||||
ApplicationContext.getInstance(context).updatePublicChatProfilePictureIfNeeded();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Toast
|
||||
import kotlinx.android.synthetic.main.activity_display_name_v2.*
|
||||
import kotlinx.android.synthetic.main.activity_display_name.*
|
||||
import network.loki.messenger.R
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.BaseActionBarActivity
|
||||
@ -19,7 +19,7 @@ class DisplayNameActivity : BaseActionBarActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setUpActionBarSessionLogo()
|
||||
setContentView(R.layout.activity_display_name_v2)
|
||||
setContentView(R.layout.activity_display_name)
|
||||
displayNameEditText.imeOptions = displayNameEditText.imeOptions or 16777216 // Always use incognito keyboard
|
||||
registerButton.setOnClickListener { register() }
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import android.text.SpannableString
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.Toast
|
||||
import kotlinx.android.synthetic.main.activity_seed_v2.*
|
||||
import kotlinx.android.synthetic.main.activity_seed.*
|
||||
import network.loki.messenger.R
|
||||
import org.thoughtcrime.securesms.BaseActionBarActivity
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
||||
@ -33,7 +33,7 @@ class SeedActivity : BaseActionBarActivity() {
|
||||
// region Lifecycle
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_seed_v2)
|
||||
setContentView(R.layout.activity_seed)
|
||||
supportActionBar!!.title = "Your Recovery Phrase"
|
||||
val seedReminderViewTitle = SpannableString("You're almost finished! 90%")
|
||||
seedReminderViewTitle.setSpan(ForegroundColorSpan(resources.getColorWithID(R.color.accent, theme)), 24, 27, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
|
@ -178,7 +178,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
||||
AvatarHelper.setAvatar(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)), profilePicture)
|
||||
TextSecurePreferences.setProfileAvatarId(this, SecureRandom().nextInt())
|
||||
ProfileKeyUtil.setEncodedProfileKey(this, encodedProfileKey)
|
||||
ApplicationContext.getInstance(this).updatePublicChatProfileAvatarIfNeeded()
|
||||
ApplicationContext.getInstance(this).updatePublicChatProfilePictureIfNeeded()
|
||||
profilePictureView.update()
|
||||
}
|
||||
profilePictureToBeUploaded = null
|
||||
|
@ -208,7 +208,7 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
|
||||
val database = DatabaseFactory.getRecipientDatabase(context)
|
||||
database.setProfileKey(recipient, profileKey)
|
||||
database.setProfileAvatar(recipient, url)
|
||||
ApplicationContext.getInstance(context).updatePublicChatProfileAvatarIfNeeded()
|
||||
ApplicationContext.getInstance(context).updatePublicChatProfilePictureIfNeeded()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user