mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-24 10:35:19 +00:00
Initial squash merge for strings
This commit is contained in:
parent
bc968dcdae
commit
aa1db13e3a
@ -10,29 +10,26 @@ buildscript {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
||||
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlinVersion"
|
||||
classpath "com.google.gms:google-services:$googleServicesVersion"
|
||||
classpath "com.google.dagger:hilt-android-gradle-plugin:$daggerVersion"
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'kotlin-kapt'
|
||||
id 'com.google.devtools.ksp'
|
||||
id 'com.google.dagger.hilt.android'
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'witness'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply plugin: 'kotlin-parcelize'
|
||||
apply plugin: 'kotlinx-serialization'
|
||||
apply plugin: 'dagger.hilt.android.plugin'
|
||||
|
||||
configurations.all {
|
||||
exclude module: "commons-logging"
|
||||
}
|
||||
|
||||
def canonicalVersionCode = 374
|
||||
def canonicalVersionName = "1.18.5"
|
||||
def canonicalVersionCode = 376
|
||||
def canonicalVersionName = "1.18.6"
|
||||
|
||||
def postFixSize = 10
|
||||
def abiPostFix = ['armeabi-v7a' : 1,
|
||||
@ -89,7 +86,7 @@ android {
|
||||
compose true
|
||||
}
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion '1.4.7'
|
||||
kotlinCompilerExtensionVersion '1.5.14'
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
@ -238,10 +235,14 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':content-descriptions')
|
||||
|
||||
implementation("com.google.dagger:hilt-android:2.46.1")
|
||||
kapt("com.google.dagger:hilt-android-compiler:2.44")
|
||||
ksp("com.google.dagger:hilt-compiler:$daggerHiltVersion")
|
||||
ksp("androidx.hilt:hilt-compiler:$jetpackHiltVersion")
|
||||
ksp "com.github.bumptech.glide:ksp:$glideVersion"
|
||||
|
||||
implementation 'androidx.compose.material3:material3-android:1.2.1'
|
||||
implementation("com.google.dagger:hilt-android:$daggerHiltVersion")
|
||||
implementation "androidx.appcompat:appcompat:$appcompatVersion"
|
||||
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
||||
implementation "com.google.android.material:material:$materialVersion"
|
||||
@ -263,12 +264,15 @@ dependencies {
|
||||
implementation 'androidx.fragment:fragment-ktx:1.5.3'
|
||||
implementation "androidx.core:core-ktx:$coreVersion"
|
||||
implementation "androidx.work:work-runtime-ktx:2.7.1"
|
||||
|
||||
playImplementation ("com.google.firebase:firebase-messaging:18.0.0") {
|
||||
exclude group: 'com.google.firebase', module: 'firebase-core'
|
||||
exclude group: 'com.google.firebase', module: 'firebase-analytics'
|
||||
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
|
||||
}
|
||||
|
||||
if (project.hasProperty('huawei')) huaweiImplementation 'com.huawei.hms:push:6.7.0.300'
|
||||
|
||||
implementation 'com.google.android.exoplayer:exoplayer-core:2.9.1'
|
||||
implementation 'com.google.android.exoplayer:exoplayer-ui:2.9.1'
|
||||
implementation 'org.conscrypt:conscrypt-android:2.5.2'
|
||||
@ -281,8 +285,6 @@ dependencies {
|
||||
implementation 'commons-net:commons-net:3.7.2'
|
||||
implementation 'com.github.chrisbanes:PhotoView:2.1.3'
|
||||
implementation "com.github.bumptech.glide:glide:$glideVersion"
|
||||
annotationProcessor "com.github.bumptech.glide:compiler:$glideVersion"
|
||||
kapt "com.github.bumptech.glide:compiler:$glideVersion"
|
||||
implementation 'com.makeramen:roundedimageview:2.1.0'
|
||||
implementation 'com.pnikosis:materialish-progress:1.5'
|
||||
implementation 'org.greenrobot:eventbus:3.0.0'
|
||||
@ -290,8 +292,6 @@ dependencies {
|
||||
implementation 'com.theartofdev.edmodo:android-image-cropper:2.8.0'
|
||||
implementation 'com.melnykov:floatingactionbutton:1.3.0'
|
||||
implementation 'com.google.zxing:android-integration:3.1.0'
|
||||
implementation "com.google.dagger:hilt-android:$daggerVersion"
|
||||
kapt "com.google.dagger:hilt-compiler:$daggerVersion"
|
||||
implementation 'mobi.upod:time-duration-picker:1.1.3'
|
||||
implementation 'com.google.zxing:core:3.2.1'
|
||||
implementation ('com.davemorrissey.labs:subsampling-scale-image-view:3.6.0') {
|
||||
@ -415,8 +415,3 @@ def autoResConfig() {
|
||||
.collect { matcher -> matcher.group(1) }
|
||||
.sort()
|
||||
}
|
||||
|
||||
// Allow references to generated code
|
||||
kapt {
|
||||
correctErrorTypes = true
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.uiautomator.By
|
||||
import androidx.test.uiautomator.UiDevice
|
||||
import com.adevinta.android.barista.interaction.PermissionGranter
|
||||
import com.squareup.phrase.Phrase
|
||||
import network.loki.messenger.util.InputBarButtonDrawableMatcher.Companion.inputButtonWithDrawable
|
||||
import org.hamcrest.Matcher
|
||||
import org.hamcrest.Matchers.allOf
|
||||
@ -37,10 +38,11 @@ import org.junit.runner.RunWith
|
||||
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsignal.utilities.guava.Optional
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.URL_KEY
|
||||
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
||||
import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBar
|
||||
import org.thoughtcrime.securesms.home.HomeActivity
|
||||
import org.thoughtcrime.securesms.mms.GlideApp
|
||||
import com.bumptech.glide.Glide
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@LargeTest
|
||||
@ -71,7 +73,7 @@ class HomeActivityTests {
|
||||
onView(allOf(isDescendantOfA(withId(R.id.inputBar)),withId(R.id.inputBarEditText))).perform(ViewActions.replaceText(messageToSend))
|
||||
if (linkPreview != null) {
|
||||
val activity = activityMonitor.waitForActivity() as ConversationActivityV2
|
||||
val glide = GlideApp.with(activity)
|
||||
val glide = Glide.with(activity)
|
||||
activity.findViewById<InputBar>(R.id.inputBar).updateLinkPreviewDraft(glide, linkPreview)
|
||||
}
|
||||
onView(allOf(isDescendantOfA(withId(R.id.inputBar)),inputButtonWithDrawable(R.drawable.ic_arrow_up))).perform(ViewActions.click())
|
||||
@ -92,10 +94,10 @@ class HomeActivityTests {
|
||||
device.pressKeyCode(67)
|
||||
|
||||
// Continue with display name
|
||||
objectFromDesc(R.string.continue_2).click()
|
||||
objectFromDesc(R.string.theContinue).click()
|
||||
|
||||
// Continue with default push notification setting
|
||||
objectFromDesc(R.string.continue_2).click()
|
||||
objectFromDesc(R.string.theContinue).click()
|
||||
|
||||
// PN select
|
||||
if (hasViewedSeed) {
|
||||
@ -127,7 +129,7 @@ class HomeActivityTests {
|
||||
@Test
|
||||
fun testLaunches_dismiss_seedView() {
|
||||
setupLoggedInState()
|
||||
objectFromDesc(R.string.continue_2).click()
|
||||
objectFromDesc(R.string.theContinue).click()
|
||||
objectFromDesc(R.string.copy).click()
|
||||
pressBack()
|
||||
onView(withId(R.id.seedReminderView)).check(matches(not(isDisplayed())))
|
||||
@ -172,7 +174,11 @@ class HomeActivityTests {
|
||||
// then the URL dialog should be displayed with a known punycode url
|
||||
val amazonPuny = "https://www.xn--mazon-wqa.com/"
|
||||
|
||||
val dialogPromptText = InstrumentationRegistry.getInstrumentation().targetContext.getString(R.string.dialog_open_url_explanation, amazonPuny)
|
||||
// Substitute the URL into our string
|
||||
val c = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
val dialogPromptText = Phrase.from(c, R.string.urlOpenDescription)
|
||||
.put(URL_KEY, amazonPuny)
|
||||
.format().toString()
|
||||
|
||||
onView(isRoot()).perform(waitFor(1000)) // no other way for this to work apparently
|
||||
onView(withText(dialogPromptText)).check(matches(isDisplayed()))
|
||||
|
@ -60,7 +60,6 @@
|
||||
<uses-permission android:name="android.permission.RAISED_THREAD_PRIORITY" />
|
||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" tools:node="remove"/>
|
||||
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT"/>
|
||||
|
||||
<queries>
|
||||
<intent>
|
||||
@ -130,12 +129,12 @@
|
||||
<activity
|
||||
android:name="org.thoughtcrime.securesms.messagerequests.MessageRequestsActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/activity_message_requests_title"
|
||||
android:label="@string/sessionMessageRequests"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name="org.thoughtcrime.securesms.preferences.SettingsActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:label="@string/activity_settings_title" />
|
||||
android:label="@string/sessionSettings" />
|
||||
<activity
|
||||
android:name="org.thoughtcrime.securesms.home.PathActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
@ -147,11 +146,11 @@
|
||||
android:name="org.thoughtcrime.securesms.preferences.BlockedContactsActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/Theme.Session.DayNight.FlatActionBar"
|
||||
android:label="@string/blocked_contacts_title"
|
||||
android:label="@string/conversationsBlockedContacts"
|
||||
/>
|
||||
<activity
|
||||
android:name="org.thoughtcrime.securesms.groups.EditClosedGroupActivity"
|
||||
android:label="@string/activity_edit_closed_group_title"
|
||||
android:label="@string/groupEdit"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name="org.thoughtcrime.securesms.recoverypassword.RecoveryPasswordActivity"
|
||||
@ -161,7 +160,7 @@
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name="org.thoughtcrime.securesms.preferences.PrivacySettingsActivity"
|
||||
android:label="@string/activity_privacy_settings_title"
|
||||
android:label="@string/sessionPrivacy"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name="org.thoughtcrime.securesms.preferences.NotificationSettingsActivity"
|
||||
@ -171,7 +170,7 @@
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name="org.thoughtcrime.securesms.preferences.HelpSettingsActivity"
|
||||
android:label="@string/activity_help_settings_title"
|
||||
android:label="@string/sessionHelp"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity android:name="org.thoughtcrime.securesms.preferences.appearance.AppearanceSettingsActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
@ -264,7 +263,6 @@
|
||||
<activity
|
||||
android:name="org.thoughtcrime.securesms.MediaPreviewActivity"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
||||
android:label="@string/AndroidManifest__media_preview"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/Theme.Session.DayNight.NoActionBar"
|
||||
android:launchMode="singleTask"
|
||||
|
@ -214,6 +214,17 @@ public class ApplicationContext extends Application implements DefaultLifecycleO
|
||||
DatabaseModule.init(this);
|
||||
MessagingModuleConfiguration.configure(this);
|
||||
super.onCreate();
|
||||
|
||||
// we need to clear the snode and onionrequest databases once on first launch
|
||||
// in order to apply a patch that adds a version number to the Snode objects.
|
||||
if(!TextSecurePreferences.hasAppliedPatchSnodeVersion(this)) {
|
||||
ThreadUtils.queue(() -> {
|
||||
lokiAPIDatabase.clearSnodePool();
|
||||
lokiAPIDatabase.clearOnionRequestPaths();
|
||||
TextSecurePreferences.setHasAppliedPatchSnodeVersion(this, true);
|
||||
});
|
||||
}
|
||||
|
||||
messagingModuleConfiguration = new MessagingModuleConfiguration(
|
||||
this,
|
||||
storage,
|
||||
|
@ -4,12 +4,8 @@ import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.Build;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import org.session.libsession.utilities.TextSecurePreferences;
|
||||
import org.session.libsession.utilities.dynamiclanguage.DynamicLanguageActivityHelper;
|
||||
|
@ -8,19 +8,8 @@ class DeleteMediaDialog {
|
||||
@JvmStatic
|
||||
fun show(context: Context, recordCount: Int, doDelete: Runnable) = context.showSessionDialog {
|
||||
iconAttribute(R.attr.dialog_alert_icon)
|
||||
title(
|
||||
context.resources.getQuantityString(
|
||||
R.plurals.MediaOverviewActivity_Media_delete_confirm_title,
|
||||
recordCount,
|
||||
recordCount
|
||||
)
|
||||
)
|
||||
text(
|
||||
context.resources.getQuantityString(R.plurals.MediaOverviewActivity_Media_delete_confirm_message,
|
||||
recordCount,
|
||||
recordCount
|
||||
)
|
||||
)
|
||||
title(context.resources.getString(R.string.deleteMessages))
|
||||
text(context.resources.getString(R.string.deleteMessageDescriptionEveryone))
|
||||
button(R.string.delete) { doDelete.run() }
|
||||
cancelButton()
|
||||
}
|
||||
|
@ -9,8 +9,8 @@ class DeleteMediaPreviewDialog {
|
||||
fun show(context: Context, doDelete: Runnable) {
|
||||
context.showSessionDialog {
|
||||
iconAttribute(R.attr.dialog_alert_icon)
|
||||
title(R.string.MediaPreviewActivity_media_delete_confirmation_title)
|
||||
text(R.string.MediaPreviewActivity_media_delete_confirmation_message)
|
||||
title(R.string.deleteMessage)
|
||||
text(R.string.deleteMessageDescriptionEveryone)
|
||||
button(R.string.delete) { doDelete.run() }
|
||||
cancelButton()
|
||||
}
|
||||
|
@ -60,7 +60,10 @@ public class MediaDocumentsAdapter extends CursorRecyclerViewAdapter<ViewHolder>
|
||||
|
||||
if (slide != null && slide.hasDocument()) {
|
||||
viewHolder.documentView.setDocument((DocumentSlide)slide, false);
|
||||
viewHolder.date.setText(DateUtils.getRelativeDate(getContext(), locale, mediaRecord.getDate()));
|
||||
|
||||
String relativeDate = DateUtils.INSTANCE.getRelativeDate(getContext(), locale, mediaRecord.getDate());
|
||||
viewHolder.date.setText(relativeDate);
|
||||
|
||||
viewHolder.documentView.setVisibility(View.VISIBLE);
|
||||
viewHolder.date.setVisibility(View.VISIBLE);
|
||||
viewHolder.documentView.setOnClickListener(view -> {
|
||||
@ -71,7 +74,7 @@ public class MediaDocumentsAdapter extends CursorRecyclerViewAdapter<ViewHolder>
|
||||
getContext().startActivity(intent);
|
||||
} catch (ActivityNotFoundException anfe) {
|
||||
Log.w(TAG, "No activity existed to view the media.");
|
||||
Toast.makeText(getContext(), R.string.ConversationItem_unable_to_open_media, Toast.LENGTH_LONG).show();
|
||||
Toast.makeText(getContext(), R.string.attachmentsErrorOpen, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@ -104,7 +107,7 @@ public class MediaDocumentsAdapter extends CursorRecyclerViewAdapter<ViewHolder>
|
||||
public void onBindHeaderViewHolder(HeaderViewHolder viewHolder, int position) {
|
||||
Cursor cursor = getCursorAtPositionOrThrow(position);
|
||||
MediaDatabase.MediaRecord mediaRecord = MediaDatabase.MediaRecord.from(getContext(), cursor);
|
||||
viewHolder.textView.setText(DateUtils.getRelativeDate(getContext(), locale, mediaRecord.getDate()));
|
||||
viewHolder.textView.setText(DateUtils.INSTANCE.getRelativeDate(getContext(), locale, mediaRecord.getDate()));
|
||||
}
|
||||
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
|
@ -29,7 +29,7 @@ import com.codewaves.stickyheadergrid.StickyHeaderGridAdapter;
|
||||
import org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView;
|
||||
import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord;
|
||||
import org.thoughtcrime.securesms.database.loaders.BucketedThreadMediaLoader.BucketedThreadMedia;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import com.bumptech.glide.RequestManager;
|
||||
import org.thoughtcrime.securesms.mms.Slide;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
|
||||
@ -46,7 +46,7 @@ class MediaGalleryAdapter extends StickyHeaderGridAdapter {
|
||||
private static final String TAG = MediaGalleryAdapter.class.getSimpleName();
|
||||
|
||||
private final Context context;
|
||||
private final GlideRequests glideRequests;
|
||||
private final RequestManager glideRequests;
|
||||
private final Locale locale;
|
||||
private final ItemClickListener itemClickListener;
|
||||
private final Set<MediaRecord> selected;
|
||||
@ -74,7 +74,7 @@ class MediaGalleryAdapter extends StickyHeaderGridAdapter {
|
||||
}
|
||||
|
||||
MediaGalleryAdapter(@NonNull Context context,
|
||||
@NonNull GlideRequests glideRequests,
|
||||
@NonNull RequestManager glideRequests,
|
||||
BucketedThreadMedia media,
|
||||
Locale locale,
|
||||
ItemClickListener clickListener)
|
||||
|
@ -16,11 +16,12 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import static org.session.libsession.utilities.StringSubstitutionConstants.APP_NAME_KEY;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
@ -32,10 +33,8 @@ import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.view.ActionMode;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
@ -48,36 +47,34 @@ import androidx.recyclerview.widget.DividerItemDecoration;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.codewaves.stickyheadergrid.StickyHeaderGridLayoutManager;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
|
||||
import org.session.libsession.messaging.messages.control.DataExtractionNotification;
|
||||
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
||||
import org.session.libsession.snode.SnodeAPI;
|
||||
import org.session.libsession.utilities.Address;
|
||||
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
|
||||
import org.thoughtcrime.securesms.database.MediaDatabase;
|
||||
import org.thoughtcrime.securesms.database.loaders.BucketedThreadMediaLoader;
|
||||
import org.thoughtcrime.securesms.database.loaders.BucketedThreadMediaLoader.BucketedThreadMedia;
|
||||
import org.thoughtcrime.securesms.database.loaders.ThreadMediaLoader;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.session.libsession.utilities.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.AttachmentUtil;
|
||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
|
||||
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
|
||||
import org.session.libsession.utilities.Util;
|
||||
import org.session.libsession.utilities.ViewUtil;
|
||||
import org.session.libsession.utilities.task.ProgressDialogAsyncTask;
|
||||
|
||||
import com.squareup.phrase.Phrase;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import kotlin.Unit;
|
||||
import network.loki.messenger.R;
|
||||
import org.session.libsession.messaging.messages.control.DataExtractionNotification;
|
||||
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
||||
import org.session.libsession.snode.SnodeAPI;
|
||||
import org.session.libsession.utilities.Address;
|
||||
import org.session.libsession.utilities.Util;
|
||||
import org.session.libsession.utilities.ViewUtil;
|
||||
import org.session.libsession.utilities.recipients.Recipient;
|
||||
import org.session.libsession.utilities.task.ProgressDialogAsyncTask;
|
||||
import org.session.libsignal.utilities.Log;
|
||||
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
|
||||
import org.thoughtcrime.securesms.database.MediaDatabase;
|
||||
import org.thoughtcrime.securesms.database.loaders.BucketedThreadMediaLoader.BucketedThreadMedia;
|
||||
import org.thoughtcrime.securesms.database.loaders.BucketedThreadMediaLoader;
|
||||
import org.thoughtcrime.securesms.database.loaders.ThreadMediaLoader;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.util.AttachmentUtil;
|
||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
|
||||
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
|
||||
|
||||
/**
|
||||
* Activity for displaying media attachments in-app
|
||||
@ -117,17 +114,26 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
Address address = getIntent().getParcelableExtra(ADDRESS_EXTRA);
|
||||
|
||||
this.viewPager = ViewUtil.findById(this, R.id.pager);
|
||||
this.toolbar = ViewUtil.findById(this, R.id.toolbar);
|
||||
this.toolbar = ViewUtil.findById(this, R.id.search_toolbar);
|
||||
this.tabLayout = ViewUtil.findById(this, R.id.tab_layout);
|
||||
|
||||
Address address = getIntent().getParcelableExtra(ADDRESS_EXTRA);
|
||||
if (address == null) {
|
||||
Log.w(TAG, "Got null address in initializeResources.");
|
||||
} else {
|
||||
this.recipient = Recipient.from(this, address, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeToolbar() {
|
||||
setSupportActionBar(this.toolbar);
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if (actionBar == null) {
|
||||
Log.w(TAG, "Could not get support actionbar");
|
||||
return;
|
||||
}
|
||||
// Implied else that the actionbar is fine to work with...
|
||||
actionBar.setTitle(recipient.toShortString());
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
actionBar.setHomeButtonEnabled(true);
|
||||
@ -176,8 +182,8 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
|
||||
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
if (position == 0) return getString(R.string.MediaOverviewActivity_Media);
|
||||
else if (position == 1) return getString(R.string.MediaOverviewActivity_Documents);
|
||||
if (position == 0) return getString(R.string.media);
|
||||
else if (position == 1) return getString(R.string.files);
|
||||
else throw new AssertionError();
|
||||
}
|
||||
}
|
||||
@ -227,7 +233,7 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
|
||||
this.gridManager = new StickyHeaderGridLayoutManager(getResources().getInteger(R.integer.media_overview_cols));
|
||||
|
||||
this.recyclerView.setAdapter(new MediaGalleryAdapter(getContext(),
|
||||
GlideApp.with(this),
|
||||
Glide.with(this),
|
||||
new BucketedThreadMedia(getContext()),
|
||||
locale,
|
||||
this));
|
||||
@ -325,13 +331,19 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
|
||||
Permissions.with(this)
|
||||
.request(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
.maxSdkVersion(Build.VERSION_CODES.P)
|
||||
.withPermanentDenialDialog(getString(R.string.MediaPreviewActivity_signal_needs_the_storage_permission_in_order_to_write_to_external_storage_but_it_has_been_permanently_denied))
|
||||
.onAnyDenied(() -> Toast.makeText(getContext(), R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show())
|
||||
.withPermanentDenialDialog(Phrase.from(context, R.string.permissionsStorageSaveDenied)
|
||||
.put(APP_NAME_KEY, getString(R.string.app_name))
|
||||
.format().toString())
|
||||
.onAnyDenied(() -> Toast.makeText(getContext(),
|
||||
Phrase.from(context, R.string.permissionsStorageSaveDenied)
|
||||
.put(APP_NAME_KEY, getString(R.string.app_name))
|
||||
.format().toString(),
|
||||
Toast.LENGTH_LONG).show())
|
||||
.onAllGranted(() -> {
|
||||
new ProgressDialogAsyncTask<Void, Void, List<SaveAttachmentTask.Attachment>>(
|
||||
context,
|
||||
R.string.MediaOverviewActivity_collecting_attachments,
|
||||
R.string.please_wait) {
|
||||
R.string.attachmentsCollecting,
|
||||
R.string.waitOneMoment) {
|
||||
@Override
|
||||
protected List<SaveAttachmentTask.Attachment> doInBackground(Void... params) {
|
||||
List<SaveAttachmentTask.Attachment> attachments = new LinkedList<>();
|
||||
@ -382,8 +394,8 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
|
||||
recordCount,
|
||||
() -> new ProgressDialogAsyncTask<MediaDatabase.MediaRecord, Void, Void>(
|
||||
requireContext(),
|
||||
R.string.MediaOverviewActivity_Media_delete_progress_title,
|
||||
R.string.MediaOverviewActivity_Media_delete_progress_message) {
|
||||
R.string.deleting,
|
||||
R.string.deleting) {
|
||||
@Override
|
||||
protected Void doInBackground(MediaDatabase.MediaRecord... records) {
|
||||
if (records == null || records.length == 0) {
|
||||
|
@ -16,17 +16,17 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import static org.session.libsession.utilities.StringSubstitutionConstants.APP_NAME_KEY;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.database.CursorIndexOutOfBoundsException;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
@ -44,7 +44,6 @@ import android.view.WindowInsetsController;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
@ -56,7 +55,12 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import com.squareup.phrase.Phrase;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.WeakHashMap;
|
||||
import kotlin.Unit;
|
||||
import network.loki.messenger.R;
|
||||
import org.session.libsession.messaging.messages.control.DataExtractionNotification;
|
||||
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment;
|
||||
@ -72,21 +76,14 @@ import org.thoughtcrime.securesms.database.loaders.PagingMediaLoader;
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.mediapreview.MediaPreviewViewModel;
|
||||
import org.thoughtcrime.securesms.mediapreview.MediaRailAdapter;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.RequestManager;
|
||||
import org.thoughtcrime.securesms.mms.Slide;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.util.AttachmentUtil;
|
||||
import org.thoughtcrime.securesms.util.DateUtils;
|
||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
|
||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask.Attachment;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import kotlin.Unit;
|
||||
import network.loki.messenger.R;
|
||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
|
||||
|
||||
/**
|
||||
* Activity for displaying media attachments in-app
|
||||
@ -242,12 +239,12 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
CharSequence relativeTimeSpan;
|
||||
|
||||
if (mediaItem.date > 0) {
|
||||
relativeTimeSpan = DateUtils.getDisplayFormattedTimeSpanString(this, Locale.getDefault(), mediaItem.date);
|
||||
relativeTimeSpan = DateUtils.INSTANCE.getDisplayFormattedTimeSpanString(this, Locale.getDefault(), mediaItem.date);
|
||||
} else {
|
||||
relativeTimeSpan = getString(R.string.MediaPreviewActivity_draft);
|
||||
relativeTimeSpan = getString(R.string.draft);
|
||||
}
|
||||
|
||||
if (mediaItem.outgoing) getSupportActionBar().setTitle(getString(R.string.MediaPreviewActivity_you));
|
||||
if (mediaItem.outgoing) getSupportActionBar().setTitle(getString(R.string.you));
|
||||
else if (mediaItem.recipient != null) getSupportActionBar().setTitle(mediaItem.recipient.toShortString());
|
||||
else getSupportActionBar().setTitle("");
|
||||
|
||||
@ -258,7 +255,6 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
initializeMedia();
|
||||
}
|
||||
|
||||
@ -281,7 +277,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
mediaPager.setOffscreenPageLimit(1);
|
||||
|
||||
albumRail = findViewById(R.id.media_preview_album_rail);
|
||||
albumRailAdapter = new MediaRailAdapter(GlideApp.with(this), this, false);
|
||||
albumRailAdapter = new MediaRailAdapter(Glide.with(this), this, false);
|
||||
|
||||
albumRail.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
|
||||
albumRail.setAdapter(albumRailAdapter);
|
||||
@ -291,7 +287,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
captionContainer = findViewById(R.id.media_preview_caption_container);
|
||||
playbackControlsContainer = findViewById(R.id.media_preview_playback_controls_container);
|
||||
|
||||
setSupportActionBar(findViewById(R.id.toolbar));
|
||||
setSupportActionBar(findViewById(R.id.search_toolbar));
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
actionBar.setHomeButtonEnabled(true);
|
||||
@ -361,7 +357,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
private void initializeMedia() {
|
||||
if (!isContentTypeSupported(initialMediaType)) {
|
||||
Log.w(TAG, "Unsupported media type sent to MediaPreviewActivity, finishing.");
|
||||
Toast.makeText(getApplicationContext(), R.string.MediaPreviewActivity_unssuported_media_type, Toast.LENGTH_LONG).show();
|
||||
Toast.makeText(getApplicationContext(), R.string.attachmentsErrorNotSupported, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
}
|
||||
|
||||
@ -370,7 +366,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
if (conversationRecipient != null) {
|
||||
getSupportLoaderManager().restartLoader(0, null, this);
|
||||
} else {
|
||||
adapter = new SingleItemPagerAdapter(this, GlideApp.with(this), getWindow(), initialMediaUri, initialMediaType, initialMediaSize);
|
||||
adapter = new SingleItemPagerAdapter(this, Glide.with(this), getWindow(), initialMediaUri, initialMediaType, initialMediaSize);
|
||||
mediaPager.setAdapter(adapter);
|
||||
|
||||
if (initialCaption != null) {
|
||||
@ -410,6 +406,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
@SuppressWarnings("CodeBlock2Expr")
|
||||
@SuppressLint("InlinedApi")
|
||||
private void saveToDisk() {
|
||||
Log.w("ACL", "Asked to save to disk!");
|
||||
MediaItem mediaItem = getCurrentMediaItem();
|
||||
if (mediaItem == null) return;
|
||||
|
||||
@ -417,8 +414,15 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
Permissions.with(this)
|
||||
.request(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
.maxSdkVersion(Build.VERSION_CODES.P)
|
||||
.withPermanentDenialDialog(getString(R.string.MediaPreviewActivity_signal_needs_the_storage_permission_in_order_to_write_to_external_storage_but_it_has_been_permanently_denied))
|
||||
.onAnyDenied(() -> Toast.makeText(this, R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show())
|
||||
.withPermanentDenialDialog(Phrase.from(getApplicationContext(), R.string.permissionsStorageSaveDenied)
|
||||
.put(APP_NAME_KEY, getString(R.string.app_name))
|
||||
.format().toString())
|
||||
.onAnyDenied(() -> {
|
||||
String txt = Phrase.from(getApplicationContext(), R.string.permissionsStorageSaveDenied)
|
||||
.put(APP_NAME_KEY, getString(R.string.app_name))
|
||||
.format().toString();
|
||||
Toast.makeText(this, txt, Toast.LENGTH_LONG).show();
|
||||
})
|
||||
.onAllGranted(() -> {
|
||||
SaveAttachmentTask saveTask = new SaveAttachmentTask(MediaPreviewActivity.this);
|
||||
long saveDate = (mediaItem.date > 0) ? mediaItem.date : SnodeAPI.getNowWithOffset();
|
||||
@ -518,7 +522,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
|
||||
mediaPager.removeOnPageChangeListener(viewPagerListener);
|
||||
|
||||
adapter = new CursorPagerAdapter(this, GlideApp.with(this), getWindow(), data.first, data.second, leftIsRecent);
|
||||
adapter = new CursorPagerAdapter(this, Glide.with(this), getWindow(), data.first, data.second, leftIsRecent);
|
||||
mediaPager.setAdapter(adapter);
|
||||
|
||||
viewModel.setCursor(this, data.first, leftIsRecent);
|
||||
@ -588,7 +592,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
|
||||
private static class SingleItemPagerAdapter extends MediaItemAdapter {
|
||||
|
||||
private final GlideRequests glideRequests;
|
||||
private final RequestManager glideRequests;
|
||||
private final Window window;
|
||||
private final Uri uri;
|
||||
private final String mediaType;
|
||||
@ -596,7 +600,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
|
||||
private final LayoutInflater inflater;
|
||||
|
||||
SingleItemPagerAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests,
|
||||
SingleItemPagerAdapter(@NonNull Context context, @NonNull RequestManager glideRequests,
|
||||
@NonNull Window window, @NonNull Uri uri, @NonNull String mediaType,
|
||||
long size)
|
||||
{
|
||||
@ -663,14 +667,14 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
private final WeakHashMap<Integer, MediaView> mediaViews = new WeakHashMap<>();
|
||||
|
||||
private final Context context;
|
||||
private final GlideRequests glideRequests;
|
||||
private final RequestManager glideRequests;
|
||||
private final Window window;
|
||||
private final Cursor cursor;
|
||||
private final boolean leftIsRecent;
|
||||
|
||||
private int autoPlayPosition;
|
||||
|
||||
CursorPagerAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests,
|
||||
CursorPagerAdapter(@NonNull Context context, @NonNull RequestManager glideRequests,
|
||||
@NonNull Window window, @NonNull Cursor cursor, int autoPlayPosition,
|
||||
boolean leftIsRecent)
|
||||
{
|
||||
|
@ -10,18 +10,18 @@ fun showMuteDialog(
|
||||
context: Context,
|
||||
onMuteDuration: (Long) -> Unit
|
||||
): AlertDialog = context.showSessionDialog {
|
||||
title(R.string.MuteDialog_mute_notifications)
|
||||
title(R.string.notificationsMute)
|
||||
items(Option.values().map { it.stringRes }.map(context::getString).toTypedArray()) {
|
||||
onMuteDuration(Option.values()[it].getTime())
|
||||
}
|
||||
}
|
||||
|
||||
private enum class Option(@StringRes val stringRes: Int, val getTime: () -> Long) {
|
||||
ONE_HOUR(R.string.arrays__mute_for_one_hour, duration = TimeUnit.HOURS.toMillis(1)),
|
||||
TWO_HOURS(R.string.arrays__mute_for_two_hours, duration = TimeUnit.DAYS.toMillis(2)),
|
||||
ONE_DAY(R.string.arrays__mute_for_one_day, duration = TimeUnit.DAYS.toMillis(1)),
|
||||
SEVEN_DAYS(R.string.arrays__mute_for_seven_days, duration = TimeUnit.DAYS.toMillis(7)),
|
||||
FOREVER(R.string.arrays__mute_forever, getTime = { Long.MAX_VALUE });
|
||||
ONE_HOUR(R.string.notificationsMute1Hour, duration = TimeUnit.HOURS.toMillis(1)),
|
||||
TWO_HOURS(R.string.notificationsMute2Hours, duration = TimeUnit.DAYS.toMillis(2)),
|
||||
ONE_DAY(R.string.notificationsMute1Day, duration = TimeUnit.DAYS.toMillis(1)),
|
||||
SEVEN_DAYS(R.string.notificationsMute1Week, duration = TimeUnit.DAYS.toMillis(7)),
|
||||
FOREVER(R.string.notificationsMute, getTime = { Long.MAX_VALUE });
|
||||
|
||||
constructor(@StringRes stringRes: Int, duration: Long): this(stringRes, { System.currentTimeMillis() + duration })
|
||||
}
|
||||
|
@ -16,6 +16,8 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import static org.session.libsession.utilities.StringSubstitutionConstants.APP_NAME_KEY;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.app.KeyguardManager;
|
||||
import android.content.ComponentName;
|
||||
@ -25,20 +27,18 @@ import android.content.ServiceConnection;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.RelativeSizeSpan;
|
||||
import android.text.style.TypefaceSpan;
|
||||
import android.view.View;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.BounceInterpolator;
|
||||
import android.view.animation.TranslateAnimation;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import android.widget.TextView;
|
||||
import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
|
||||
import androidx.core.os.CancellationSignal;
|
||||
|
||||
import com.squareup.phrase.Phrase;
|
||||
import java.security.Signature;
|
||||
import network.loki.messenger.R;
|
||||
import org.session.libsession.utilities.TextSecurePreferences;
|
||||
import org.session.libsignal.utilities.Log;
|
||||
import org.thoughtcrime.securesms.components.AnimatingToggle;
|
||||
@ -46,11 +46,6 @@ import org.thoughtcrime.securesms.crypto.BiometricSecretProvider;
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||
import org.thoughtcrime.securesms.util.AnimationCompleteListener;
|
||||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Signature;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
|
||||
//TODO Rename to ScreenLockActivity and refactor to Kotlin.
|
||||
public class PassphrasePromptActivity extends BaseActionBarActivity {
|
||||
|
||||
@ -158,6 +153,16 @@ public class PassphrasePromptActivity extends BaseActionBarActivity {
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
|
||||
TextView statusTitle = findViewById(R.id.app_lock_status_title);
|
||||
if (statusTitle != null) {
|
||||
Context c = getApplicationContext();
|
||||
String lockedTxt = Phrase.from(c, R.string.lockAppLocked)
|
||||
.put(APP_NAME_KEY, c.getString(R.string.app_name))
|
||||
.format().toString();
|
||||
statusTitle.setText(lockedTxt);
|
||||
}
|
||||
|
||||
visibilityToggle = findViewById(R.id.button_toggle);
|
||||
fingerprintPrompt = findViewById(R.id.fingerprint_auth_container);
|
||||
lockScreenButton = findViewById(R.id.lock_screen_auth_container);
|
||||
@ -165,10 +170,6 @@ public class PassphrasePromptActivity extends BaseActionBarActivity {
|
||||
fingerprintCancellationSignal = new CancellationSignal();
|
||||
fingerprintListener = new FingerprintListener();
|
||||
|
||||
SpannableString hint = new SpannableString(" " + getString(R.string.PassphrasePromptActivity_enter_passphrase));
|
||||
hint.setSpan(new RelativeSizeSpan(0.9f), 0, hint.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
hint.setSpan(new TypefaceSpan("sans-serif"), 0, hint.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
|
||||
fingerprintPrompt.setImageResource(R.drawable.ic_fingerprint_white_48dp);
|
||||
fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.signal_primary), PorterDuff.Mode.SRC_IN);
|
||||
|
||||
|
@ -34,7 +34,7 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA
|
||||
private BroadcastReceiver clearKeyReceiver;
|
||||
|
||||
@Override
|
||||
protected final void onCreate(Bundle savedInstanceState) {
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
Log.i(TAG, "onCreate(" + savedInstanceState + ")");
|
||||
onPreCreate();
|
||||
|
||||
|
@ -132,7 +132,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
|
||||
private void initializeToolbar() {
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
Toolbar toolbar = findViewById(R.id.search_toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
|
@ -37,7 +37,7 @@ public class ShortcutLauncherActivity extends AppCompatActivity {
|
||||
String serializedAddress = getIntent().getStringExtra(KEY_SERIALIZED_ADDRESS);
|
||||
|
||||
if (serializedAddress == null) {
|
||||
Toast.makeText(this, R.string.ShortcutLauncherActivity_invalid_shortcut, Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(this, R.string.invalidShortcut, Toast.LENGTH_SHORT).show();
|
||||
startActivity(new Intent(this, HomeActivity.class));
|
||||
finish();
|
||||
return;
|
||||
|
@ -105,7 +105,7 @@ public final class AvatarSelection {
|
||||
extraIntents.add(new Intent("network.loki.securesms.action.CLEAR_PROFILE_PHOTO"));
|
||||
}
|
||||
|
||||
Intent chooserIntent = Intent.createChooser(galleryIntent, context.getString(R.string.CreateProfileActivity_profile_photo));
|
||||
Intent chooserIntent = Intent.createChooser(galleryIntent, context.getString(R.string.profileDisplayPicture));
|
||||
|
||||
if (!extraIntents.isEmpty()) {
|
||||
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, extraIntents.toArray(new Intent[0]));
|
||||
|
@ -22,6 +22,7 @@ import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.Job
|
||||
@ -38,7 +39,6 @@ import org.session.libsession.utilities.truncateIdForDisplay
|
||||
import org.session.libsignal.utilities.Log
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||
import org.thoughtcrime.securesms.mms.GlideApp
|
||||
import org.thoughtcrime.securesms.permissions.Permissions
|
||||
import org.thoughtcrime.securesms.service.WebRtcCallService
|
||||
import org.thoughtcrime.securesms.util.AvatarPlaceholderGenerator
|
||||
@ -70,7 +70,7 @@ class WebRtcCallActivity : PassphraseRequiredActionBarActivity() {
|
||||
}
|
||||
|
||||
private val viewModel by viewModels<CallViewModel>()
|
||||
private val glide by lazy { GlideApp.with(this) }
|
||||
private val glide by lazy { Glide.with(this) }
|
||||
private lateinit var binding: ActivityWebrtcBinding
|
||||
private var uiJob: Job? = null
|
||||
private var wantsToAnswer = false
|
||||
|
@ -1,312 +0,0 @@
|
||||
package org.thoughtcrime.securesms.components;
|
||||
|
||||
import android.Manifest;
|
||||
import android.animation.Animator;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import android.util.Pair;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewAnimationUtils;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationSet;
|
||||
import android.view.animation.OvershootInterpolator;
|
||||
import android.view.animation.ScaleAnimation;
|
||||
import android.view.animation.TranslateAnimation;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.PopupWindow;
|
||||
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.session.libsession.utilities.ViewUtil;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
|
||||
public class AttachmentTypeSelector extends PopupWindow {
|
||||
|
||||
public static final int ADD_GALLERY = 1;
|
||||
public static final int ADD_DOCUMENT = 2;
|
||||
public static final int ADD_SOUND = 3;
|
||||
public static final int ADD_CONTACT_INFO = 4;
|
||||
public static final int TAKE_PHOTO = 5;
|
||||
public static final int ADD_LOCATION = 6;
|
||||
public static final int ADD_GIF = 7;
|
||||
|
||||
private static final int ANIMATION_DURATION = 300;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = AttachmentTypeSelector.class.getSimpleName();
|
||||
|
||||
private final @NonNull Context context;
|
||||
public int keyboardHeight;
|
||||
private final @NonNull LoaderManager loaderManager;
|
||||
private final @NonNull RecentPhotoViewRail recentRail;
|
||||
private final @NonNull ImageView imageButton;
|
||||
private final @NonNull ImageView audioButton;
|
||||
private final @NonNull ImageView documentButton;
|
||||
private final @NonNull ImageView contactButton;
|
||||
private final @NonNull ImageView cameraButton;
|
||||
private final @NonNull ImageView locationButton;
|
||||
private final @NonNull ImageView gifButton;
|
||||
private final @NonNull ImageView closeButton;
|
||||
|
||||
private @Nullable View currentAnchor;
|
||||
private @Nullable AttachmentClickedListener listener;
|
||||
|
||||
public AttachmentTypeSelector(@NonNull Context context, @NonNull LoaderManager loaderManager, @Nullable AttachmentClickedListener listener, int keyboardHeight) {
|
||||
super(context);
|
||||
|
||||
this.context = context;
|
||||
this.keyboardHeight = keyboardHeight;
|
||||
|
||||
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
LinearLayout layout = (LinearLayout) inflater.inflate(R.layout.attachment_type_selector, null, true);
|
||||
|
||||
this.listener = listener;
|
||||
this.loaderManager = loaderManager;
|
||||
this.recentRail = ViewUtil.findById(layout, R.id.recent_photos);
|
||||
this.imageButton = ViewUtil.findById(layout, R.id.gallery_button);
|
||||
this.audioButton = ViewUtil.findById(layout, R.id.audio_button);
|
||||
this.documentButton = ViewUtil.findById(layout, R.id.document_button);
|
||||
this.contactButton = ViewUtil.findById(layout, R.id.contact_button);
|
||||
this.cameraButton = ViewUtil.findById(layout, R.id.camera_button);
|
||||
this.locationButton = ViewUtil.findById(layout, R.id.location_button);
|
||||
this.gifButton = ViewUtil.findById(layout, R.id.giphy_button);
|
||||
this.closeButton = ViewUtil.findById(layout, R.id.close_button);
|
||||
|
||||
this.imageButton.setOnClickListener(new PropagatingClickListener(ADD_GALLERY));
|
||||
this.audioButton.setOnClickListener(new PropagatingClickListener(ADD_SOUND));
|
||||
this.documentButton.setOnClickListener(new PropagatingClickListener(ADD_DOCUMENT));
|
||||
this.contactButton.setOnClickListener(new PropagatingClickListener(ADD_CONTACT_INFO));
|
||||
this.cameraButton.setOnClickListener(new PropagatingClickListener(TAKE_PHOTO));
|
||||
this.locationButton.setOnClickListener(new PropagatingClickListener(ADD_LOCATION));
|
||||
this.gifButton.setOnClickListener(new PropagatingClickListener(ADD_GIF));
|
||||
this.closeButton.setOnClickListener(new CloseClickListener());
|
||||
this.recentRail.setListener(new RecentPhotoSelectedListener());
|
||||
|
||||
setContentView(layout);
|
||||
setWidth(LinearLayout.LayoutParams.MATCH_PARENT);
|
||||
setHeight(LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
setBackgroundDrawable(new BitmapDrawable());
|
||||
setAnimationStyle(0);
|
||||
setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
|
||||
setFocusable(true);
|
||||
setTouchable(true);
|
||||
|
||||
updateHeight();
|
||||
|
||||
loaderManager.initLoader(1, null, recentRail);
|
||||
}
|
||||
|
||||
public void show(@NonNull Activity activity, final @NonNull View anchor) {
|
||||
updateHeight();
|
||||
|
||||
if (Permissions.hasAll(activity, Manifest.permission.READ_EXTERNAL_STORAGE)) {
|
||||
recentRail.setVisibility(View.VISIBLE);
|
||||
loaderManager.restartLoader(1, null, recentRail);
|
||||
} else {
|
||||
recentRail.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
this.currentAnchor = anchor;
|
||||
|
||||
showAtLocation(anchor, Gravity.BOTTOM, 0, 0);
|
||||
|
||||
getContentView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
getContentView().getViewTreeObserver().removeGlobalOnLayoutListener(this);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
animateWindowInCircular(anchor, getContentView());
|
||||
} else {
|
||||
animateWindowInTranslate(getContentView());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
animateButtonIn(imageButton, ANIMATION_DURATION / 2);
|
||||
animateButtonIn(cameraButton, ANIMATION_DURATION / 2);
|
||||
|
||||
animateButtonIn(audioButton, ANIMATION_DURATION / 3);
|
||||
animateButtonIn(locationButton, ANIMATION_DURATION / 3);
|
||||
animateButtonIn(documentButton, ANIMATION_DURATION / 4);
|
||||
animateButtonIn(gifButton, ANIMATION_DURATION / 4);
|
||||
animateButtonIn(contactButton, 0);
|
||||
animateButtonIn(closeButton, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateHeight() {
|
||||
int thresholdInDP = 120;
|
||||
float scale = context.getResources().getDisplayMetrics().density;
|
||||
int thresholdInPX = (int)(thresholdInDP * scale);
|
||||
View contentView = ViewUtil.findById(getContentView(), R.id.contentView);
|
||||
LinearLayout.LayoutParams contentViewLayoutParams = (LinearLayout.LayoutParams)contentView.getLayoutParams();
|
||||
contentViewLayoutParams.height = keyboardHeight > thresholdInPX ? keyboardHeight : LinearLayout.LayoutParams.WRAP_CONTENT;
|
||||
contentView.setLayoutParams(contentViewLayoutParams);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dismiss() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
animateWindowOutCircular(currentAnchor, getContentView());
|
||||
} else {
|
||||
animateWindowOutTranslate(getContentView());
|
||||
}
|
||||
}
|
||||
|
||||
public void setListener(@Nullable AttachmentClickedListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
private void animateButtonIn(View button, int delay) {
|
||||
AnimationSet animation = new AnimationSet(true);
|
||||
Animation scale = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f,
|
||||
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.0f);
|
||||
|
||||
animation.addAnimation(scale);
|
||||
animation.setInterpolator(new OvershootInterpolator(1));
|
||||
animation.setDuration(ANIMATION_DURATION);
|
||||
animation.setStartOffset(delay);
|
||||
button.startAnimation(animation);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private void animateWindowInCircular(@Nullable View anchor, @NonNull View contentView) {
|
||||
Pair<Integer, Integer> coordinates = getClickOrigin(anchor, contentView);
|
||||
Animator animator = ViewAnimationUtils.createCircularReveal(contentView,
|
||||
coordinates.first,
|
||||
coordinates.second,
|
||||
0,
|
||||
Math.max(contentView.getWidth(), contentView.getHeight()));
|
||||
animator.setDuration(ANIMATION_DURATION);
|
||||
animator.start();
|
||||
}
|
||||
|
||||
private void animateWindowInTranslate(@NonNull View contentView) {
|
||||
Animation animation = new TranslateAnimation(0, 0, contentView.getHeight(), 0);
|
||||
animation.setDuration(ANIMATION_DURATION);
|
||||
|
||||
getContentView().startAnimation(animation);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private void animateWindowOutCircular(@Nullable View anchor, @NonNull View contentView) {
|
||||
Pair<Integer, Integer> coordinates = getClickOrigin(anchor, contentView);
|
||||
Animator animator = ViewAnimationUtils.createCircularReveal(getContentView(),
|
||||
coordinates.first,
|
||||
coordinates.second,
|
||||
Math.max(getContentView().getWidth(), getContentView().getHeight()),
|
||||
0);
|
||||
|
||||
animator.setDuration(ANIMATION_DURATION);
|
||||
animator.addListener(new Animator.AnimatorListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
AttachmentTypeSelector.super.dismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
}
|
||||
});
|
||||
|
||||
animator.start();
|
||||
}
|
||||
|
||||
private void animateWindowOutTranslate(@NonNull View contentView) {
|
||||
Animation animation = new TranslateAnimation(0, 0, 0, contentView.getTop() + contentView.getHeight());
|
||||
animation.setDuration(ANIMATION_DURATION);
|
||||
animation.setAnimationListener(new Animation.AnimationListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
AttachmentTypeSelector.super.dismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
}
|
||||
});
|
||||
|
||||
getContentView().startAnimation(animation);
|
||||
}
|
||||
|
||||
private Pair<Integer, Integer> getClickOrigin(@Nullable View anchor, @NonNull View contentView) {
|
||||
if (anchor == null) return new Pair<>(0, 0);
|
||||
|
||||
final int[] anchorCoordinates = new int[2];
|
||||
anchor.getLocationOnScreen(anchorCoordinates);
|
||||
anchorCoordinates[0] += anchor.getWidth() / 2;
|
||||
anchorCoordinates[1] += anchor.getHeight() / 2;
|
||||
|
||||
final int[] contentCoordinates = new int[2];
|
||||
contentView.getLocationOnScreen(contentCoordinates);
|
||||
|
||||
int x = anchorCoordinates[0] - contentCoordinates[0];
|
||||
int y = anchorCoordinates[1] - contentCoordinates[1];
|
||||
|
||||
return new Pair<>(x, y);
|
||||
}
|
||||
|
||||
private class RecentPhotoSelectedListener implements RecentPhotoViewRail.OnItemClickedListener {
|
||||
@Override
|
||||
public void onItemClicked(Uri uri, String mimeType, String bucketId, long dateTaken, int width, int height, long size) {
|
||||
animateWindowOutTranslate(getContentView());
|
||||
|
||||
if (listener != null) listener.onQuickAttachment(uri, mimeType, bucketId, dateTaken, width, height, size);
|
||||
}
|
||||
}
|
||||
|
||||
private class PropagatingClickListener implements View.OnClickListener {
|
||||
|
||||
private final int type;
|
||||
|
||||
private PropagatingClickListener(int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
animateWindowOutTranslate(getContentView());
|
||||
|
||||
if (listener != null) listener.onClick(type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class CloseClickListener implements View.OnClickListener {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
public interface AttachmentClickedListener {
|
||||
void onClick(int type);
|
||||
void onQuickAttachment(Uri uri, String mimeType, String bucketId, long dateTaken, int width, int height, long size);
|
||||
}
|
||||
|
||||
}
|
@ -25,8 +25,8 @@ import org.session.libsession.utilities.Address;
|
||||
import org.session.libsession.utilities.ThemeUtil;
|
||||
import org.session.libsession.utilities.recipients.Recipient;
|
||||
import org.session.libsession.utilities.recipients.RecipientExporter;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.RequestManager;
|
||||
import org.thoughtcrime.securesms.util.AvatarPlaceholderGenerator;
|
||||
|
||||
import java.util.Objects;
|
||||
@ -117,10 +117,10 @@ public class AvatarImageView extends AppCompatImageView {
|
||||
}
|
||||
|
||||
private void updateAvatar(Recipient recipient) {
|
||||
setAvatar(GlideApp.with(getContext()), recipient, false);
|
||||
setAvatar(Glide.with(getContext()), recipient, false);
|
||||
}
|
||||
|
||||
public void setAvatar(@NonNull GlideRequests requestManager, @Nullable Recipient recipient, boolean quickContactEnabled) {
|
||||
public void setAvatar(@NonNull RequestManager requestManager, @Nullable Recipient recipient, boolean quickContactEnabled) {
|
||||
if (recipient != null) {
|
||||
if (recipient.isLocalNumber()) {
|
||||
setImageDrawable(new ResourceContactPhoto(R.drawable.ic_note_to_self).asDrawable(getContext(), recipient.getColor().toAvatarColor(getContext()), inverted));
|
||||
@ -156,7 +156,7 @@ public class AvatarImageView extends AppCompatImageView {
|
||||
}
|
||||
}
|
||||
|
||||
public void clear(@NonNull GlideRequests glideRequests) {
|
||||
public void clear(@NonNull RequestManager glideRequests) {
|
||||
glideRequests.clear(this);
|
||||
}
|
||||
|
||||
|
@ -1,259 +0,0 @@
|
||||
package org.thoughtcrime.securesms.components;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.preference.DialogPreference;
|
||||
import androidx.preference.PreferenceDialogFragmentCompat;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import org.session.libsignal.utilities.Log;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
import org.thoughtcrime.securesms.components.CustomDefaultPreference.CustomDefaultPreferenceDialogFragmentCompat.CustomPreferenceValidator;
|
||||
import org.session.libsession.utilities.TextSecurePreferences;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
|
||||
public class CustomDefaultPreference extends DialogPreference {
|
||||
|
||||
private static final String TAG = CustomDefaultPreference.class.getSimpleName();
|
||||
|
||||
private final int inputType;
|
||||
private final String customPreference;
|
||||
private final String customToggle;
|
||||
|
||||
private CustomPreferenceValidator validator;
|
||||
private String defaultValue;
|
||||
|
||||
public CustomDefaultPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
int[] attributeNames = new int[]{android.R.attr.inputType, R.attr.custom_pref_toggle};
|
||||
TypedArray attributes = context.obtainStyledAttributes(attrs, attributeNames);
|
||||
|
||||
this.inputType = attributes.getInt(0, 0);
|
||||
this.customPreference = getKey();
|
||||
this.customToggle = attributes.getString(1);
|
||||
this.validator = new CustomDefaultPreferenceDialogFragmentCompat.NullValidator();
|
||||
|
||||
attributes.recycle();
|
||||
|
||||
setPersistent(false);
|
||||
setDialogLayoutResource(R.layout.custom_default_preference_dialog);
|
||||
}
|
||||
|
||||
public CustomDefaultPreference setValidator(CustomPreferenceValidator validator) {
|
||||
this.validator = validator;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CustomDefaultPreference setDefaultValue(String defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
this.setSummary(getSummary());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSummary() {
|
||||
if (isCustom()) {
|
||||
return getContext().getString(R.string.CustomDefaultPreference_using_custom,
|
||||
getPrettyPrintValue(getCustomValue()));
|
||||
} else {
|
||||
return getContext().getString(R.string.CustomDefaultPreference_using_default,
|
||||
getPrettyPrintValue(getDefaultValue()));
|
||||
}
|
||||
}
|
||||
|
||||
private String getPrettyPrintValue(String value) {
|
||||
if (TextUtils.isEmpty(value)) return getContext().getString(R.string.CustomDefaultPreference_none);
|
||||
else return value;
|
||||
}
|
||||
|
||||
private boolean isCustom() {
|
||||
return TextSecurePreferences.getBooleanPreference(getContext(), customToggle, false);
|
||||
}
|
||||
|
||||
private void setCustom(boolean custom) {
|
||||
TextSecurePreferences.setBooleanPreference(getContext(), customToggle, custom);
|
||||
}
|
||||
|
||||
private String getCustomValue() {
|
||||
return TextSecurePreferences.getStringPreference(getContext(), customPreference, "");
|
||||
}
|
||||
|
||||
private void setCustomValue(String value) {
|
||||
TextSecurePreferences.setStringPreference(getContext(), customPreference, value);
|
||||
}
|
||||
|
||||
private String getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
|
||||
public static class CustomDefaultPreferenceDialogFragmentCompat extends PreferenceDialogFragmentCompat {
|
||||
|
||||
private static final String INPUT_TYPE = "input_type";
|
||||
|
||||
private Spinner spinner;
|
||||
private EditText customText;
|
||||
private TextView defaultLabel;
|
||||
|
||||
public static CustomDefaultPreferenceDialogFragmentCompat newInstance(String key) {
|
||||
CustomDefaultPreferenceDialogFragmentCompat fragment = new CustomDefaultPreferenceDialogFragmentCompat();
|
||||
Bundle b = new Bundle(1);
|
||||
b.putString(PreferenceDialogFragmentCompat.ARG_KEY, key);
|
||||
fragment.setArguments(b);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBindDialogView(@NonNull View view) {
|
||||
Log.i(TAG, "onBindDialogView");
|
||||
super.onBindDialogView(view);
|
||||
|
||||
CustomDefaultPreference preference = (CustomDefaultPreference)getPreference();
|
||||
|
||||
this.spinner = (Spinner) view.findViewById(R.id.default_or_custom);
|
||||
this.defaultLabel = (TextView) view.findViewById(R.id.default_label);
|
||||
this.customText = (EditText) view.findViewById(R.id.custom_edit);
|
||||
|
||||
this.customText.setInputType(preference.inputType);
|
||||
this.customText.addTextChangedListener(new TextValidator());
|
||||
this.customText.setText(preference.getCustomValue());
|
||||
this.spinner.setOnItemSelectedListener(new SelectionLister());
|
||||
this.defaultLabel.setText(preference.getPrettyPrintValue(preference.defaultValue));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public @NonNull Dialog onCreateDialog(Bundle instanceState) {
|
||||
Dialog dialog = super.onCreateDialog(instanceState);
|
||||
|
||||
CustomDefaultPreference preference = (CustomDefaultPreference)getPreference();
|
||||
|
||||
if (preference.isCustom()) spinner.setSelection(1, true);
|
||||
else spinner.setSelection(0, true);
|
||||
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDialogClosed(boolean positiveResult) {
|
||||
CustomDefaultPreference preference = (CustomDefaultPreference)getPreference();
|
||||
|
||||
if (positiveResult) {
|
||||
if (spinner != null) preference.setCustom(spinner.getSelectedItemPosition() == 1);
|
||||
if (customText != null) preference.setCustomValue(customText.getText().toString());
|
||||
|
||||
preference.setSummary(preference.getSummary());
|
||||
}
|
||||
}
|
||||
|
||||
interface CustomPreferenceValidator {
|
||||
public boolean isValid(String value);
|
||||
}
|
||||
|
||||
private static class NullValidator implements CustomPreferenceValidator {
|
||||
@Override
|
||||
public boolean isValid(String value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private class TextValidator implements TextWatcher {
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
CustomDefaultPreference preference = (CustomDefaultPreference)getPreference();
|
||||
|
||||
if (spinner.getSelectedItemPosition() == 1) {
|
||||
Button positiveButton = ((AlertDialog)getDialog()).getButton(AlertDialog.BUTTON_POSITIVE);
|
||||
positiveButton.setEnabled(preference.validator.isValid(s.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class UriValidator implements CustomPreferenceValidator {
|
||||
@Override
|
||||
public boolean isValid(String value) {
|
||||
if (TextUtils.isEmpty(value)) return true;
|
||||
|
||||
try {
|
||||
new URI(value);
|
||||
return true;
|
||||
} catch (URISyntaxException mue) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class HostnameValidator implements CustomPreferenceValidator {
|
||||
@Override
|
||||
public boolean isValid(String value) {
|
||||
if (TextUtils.isEmpty(value)) return true;
|
||||
|
||||
try {
|
||||
URI uri = new URI(null, value, null, null);
|
||||
return true;
|
||||
} catch (URISyntaxException mue) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class PortValidator implements CustomPreferenceValidator {
|
||||
@Override
|
||||
public boolean isValid(String value) {
|
||||
try {
|
||||
Integer.parseInt(value);
|
||||
return true;
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SelectionLister implements AdapterView.OnItemSelectedListener {
|
||||
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
CustomDefaultPreference preference = (CustomDefaultPreference)getPreference();
|
||||
Button positiveButton = ((AlertDialog)getDialog()).getButton(AlertDialog.BUTTON_POSITIVE);
|
||||
|
||||
defaultLabel.setVisibility(position == 0 ? View.VISIBLE : View.GONE);
|
||||
customText.setVisibility(position == 0 ? View.GONE : View.VISIBLE);
|
||||
positiveButton.setEnabled(position == 0 || preference.validator.isValid(customText.getText().toString()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
defaultLabel.setVisibility(View.VISIBLE);
|
||||
customText.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -105,7 +105,7 @@ public class DocumentView extends FrameLayout {
|
||||
|
||||
this.documentSlide = documentSlide;
|
||||
|
||||
this.fileName.setText(documentSlide.getFileName().or(getContext().getString(R.string.DocumentView_unknown_file)));
|
||||
this.fileName.setText(documentSlide.getFileName().or(getContext().getString(R.string.attachmentsErrorNotSupported)));
|
||||
this.fileSize.setText(Util.getPrettyFileSize(documentSlide.getFileSize()));
|
||||
this.document.setText(getFileType(documentSlide.getFileName()));
|
||||
this.setOnClickListener(new OpenClickedListener(documentSlide));
|
||||
|
@ -54,7 +54,7 @@ public class FromTextView extends EmojiTextView {
|
||||
|
||||
|
||||
if (recipient.isLocalNumber()) {
|
||||
builder.append(getContext().getString(R.string.note_to_self));
|
||||
builder.append(getContext().getString(R.string.noteToSelf));
|
||||
} else if (recipient.getName() == null && !TextUtils.isEmpty(recipient.getProfileName())) {
|
||||
SpannableString profileName = new SpannableString(" (~" + recipient.getProfileName() + ") ");
|
||||
profileName.setSpan(new CenterAlignedRelativeSizeSpan(0.75f), 0, profileName.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
@ -13,7 +13,7 @@ import android.view.Window;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import com.bumptech.glide.RequestManager;
|
||||
import org.thoughtcrime.securesms.mms.VideoSlide;
|
||||
import org.thoughtcrime.securesms.video.VideoPlayer;
|
||||
|
||||
@ -54,7 +54,7 @@ public class MediaView extends FrameLayout {
|
||||
this.videoView = new Stub<>(findViewById(R.id.video_player_stub));
|
||||
}
|
||||
|
||||
public void set(@NonNull GlideRequests glideRequests,
|
||||
public void set(@NonNull RequestManager glideRequests,
|
||||
@NonNull Window window,
|
||||
@NonNull Uri source,
|
||||
@NonNull String mediaType,
|
||||
|
@ -20,8 +20,8 @@ import org.session.libsession.utilities.GroupUtil
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.session.libsignal.utilities.Log
|
||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||
import org.thoughtcrime.securesms.mms.GlideApp
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.RequestManager
|
||||
|
||||
class ProfilePictureView @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null
|
||||
@ -29,7 +29,7 @@ class ProfilePictureView @JvmOverloads constructor(
|
||||
private val TAG = "ProfilePictureView"
|
||||
|
||||
private val binding = ViewProfilePictureBinding.inflate(LayoutInflater.from(context), this)
|
||||
private val glide: GlideRequests = GlideApp.with(this)
|
||||
private val glide: RequestManager = Glide.with(this)
|
||||
private val prefs = AppTextSecurePreferences(context)
|
||||
private val userPublicKey = prefs.getLocalNumber()
|
||||
var publicKey: String? = null
|
||||
|
@ -28,7 +28,7 @@ import com.bumptech.glide.signature.MediaStoreSignature;
|
||||
import network.loki.messenger.R;
|
||||
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
|
||||
import org.thoughtcrime.securesms.database.loaders.RecentPhotosLoader;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import com.bumptech.glide.Glide;
|
||||
|
||||
import org.session.libsession.utilities.ViewUtil;
|
||||
|
||||
@ -118,7 +118,7 @@ public class RecentPhotoViewRail extends FrameLayout implements LoaderManager.Lo
|
||||
|
||||
Key signature = new MediaStoreSignature(mimeType, dateModified, orientation);
|
||||
|
||||
GlideApp.with(getContext().getApplicationContext())
|
||||
Glide.with(getContext().getApplicationContext())
|
||||
.load(uri)
|
||||
.signature(signature)
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
|
@ -44,10 +44,9 @@ public class SearchToolbar extends LinearLayout {
|
||||
inflate(getContext(), R.layout.search_toolbar, this);
|
||||
setOrientation(VERTICAL);
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
Toolbar toolbar = findViewById(R.id.search_toolbar);
|
||||
|
||||
toolbar.setNavigationIcon(
|
||||
getContext().getResources().getDrawable(R.drawable.ic_baseline_clear_24));
|
||||
toolbar.setNavigationIcon(getContext().getResources().getDrawable(R.drawable.ic_baseline_clear_24));
|
||||
toolbar.inflateMenu(R.menu.conversation_list_search);
|
||||
|
||||
this.searchItem = toolbar.getMenu().findItem(R.id.action_filter_search);
|
||||
@ -56,8 +55,8 @@ public class SearchToolbar extends LinearLayout {
|
||||
|
||||
searchView.setSubmitButtonEnabled(false);
|
||||
|
||||
if (searchText != null) searchText.setHint(R.string.SearchToolbar_search);
|
||||
else searchView.setQueryHint(getResources().getString(R.string.SearchToolbar_search));
|
||||
if (searchText != null) searchText.setHint(R.string.search);
|
||||
else searchView.setQueryHint(getResources().getString(R.string.search));
|
||||
|
||||
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||
@Override
|
||||
|
@ -8,7 +8,7 @@ import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import com.bumptech.glide.RequestManager;
|
||||
|
||||
import org.thoughtcrime.securesms.mms.Slide;
|
||||
import org.thoughtcrime.securesms.mms.SlideClickListener;
|
||||
|
@ -1,15 +1,18 @@
|
||||
package org.thoughtcrime.securesms.components;
|
||||
|
||||
import static org.session.libsession.utilities.StringSubstitutionConstants.APP_NAME_KEY;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.preference.CheckBoxPreference;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.squareup.phrase.Phrase;
|
||||
import network.loki.messenger.R;
|
||||
|
||||
public class SwitchPreferenceCompat extends CheckBoxPreference {
|
||||
|
||||
private static String LOCK_SCREEN_KEY = "pref_android_screen_lock";
|
||||
|
||||
private Preference.OnPreferenceClickListener listener;
|
||||
|
||||
public SwitchPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
@ -34,6 +37,19 @@ public class SwitchPreferenceCompat extends CheckBoxPreference {
|
||||
|
||||
private void setLayoutRes() {
|
||||
setWidgetLayoutResource(R.layout.switch_compat_preference);
|
||||
|
||||
if (this.hasKey()) {
|
||||
String key = this.getKey();
|
||||
|
||||
// Substitute app name into lockscreen preference summary
|
||||
if (key.equalsIgnoreCase(LOCK_SCREEN_KEY)) {
|
||||
Context c = getContext();
|
||||
CharSequence substitutedSummaryCS = Phrase.from(c, R.string.lockAppDescriptionAndroid)
|
||||
.put(APP_NAME_KEY, c.getString(R.string.app_name))
|
||||
.format();
|
||||
this.setSummary(substitutedSummaryCS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,36 +1,33 @@
|
||||
package org.thoughtcrime.securesms.components;
|
||||
|
||||
import static org.session.libsession.utilities.StringSubstitutionConstants.COUNT_KEY;
|
||||
|
||||
import android.animation.LayoutTransition;
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import com.annimon.stream.Stream;
|
||||
import com.pnikosis.materialishprogress.ProgressWheel;
|
||||
|
||||
import com.squareup.phrase.Phrase;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import network.loki.messenger.R;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress;
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||
import org.session.libsession.utilities.ViewUtil;
|
||||
import org.thoughtcrime.securesms.events.PartProgressEvent;
|
||||
import org.thoughtcrime.securesms.mms.Slide;
|
||||
|
||||
import org.session.libsession.utilities.ViewUtil;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
|
||||
public class TransferControlView extends FrameLayout {
|
||||
|
||||
@Nullable private List<Slide> slides;
|
||||
@ -191,7 +188,8 @@ public class TransferControlView extends FrameLayout {
|
||||
return slides.get(0).getContentDescription();
|
||||
} else {
|
||||
int downloadCount = Stream.of(slides).reduce(0, (count, slide) -> slide.getTransferState() != AttachmentTransferProgress.TRANSFER_PROGRESS_DONE ? count + 1 : count);
|
||||
return getContext().getResources().getQuantityString(R.plurals.TransferControlView_n_items, downloadCount, downloadCount);
|
||||
|
||||
return Phrase.from(getContext(), R.string.andMore).put(COUNT_KEY, downloadCount).format().toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ import network.loki.messenger.R;
|
||||
import org.thoughtcrime.securesms.components.subsampling.AttachmentBitmapDecoder;
|
||||
import org.thoughtcrime.securesms.components.subsampling.AttachmentRegionDecoder;
|
||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import com.bumptech.glide.RequestManager;
|
||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||
import org.thoughtcrime.securesms.util.BitmapDecodingException;
|
||||
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||
@ -62,7 +62,7 @@ public class ZoomingImageView extends FrameLayout {
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
public void setImageUri(@NonNull GlideRequests glideRequests, @NonNull Uri uri, @NonNull String contentType)
|
||||
public void setImageUri(@NonNull RequestManager glideRequests, @NonNull Uri uri, @NonNull String contentType)
|
||||
{
|
||||
final Context context = getContext();
|
||||
final int maxTextureSize = BitmapUtil.getMaxTextureSize();
|
||||
@ -97,7 +97,7 @@ public class ZoomingImageView extends FrameLayout {
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
private void setImageViewUri(@NonNull GlideRequests glideRequests, @NonNull Uri uri) {
|
||||
private void setImageViewUri(@NonNull RequestManager glideRequests, @NonNull Uri uri) {
|
||||
photoView.setVisibility(View.VISIBLE);
|
||||
subsamplingImageView.setVisibility(View.GONE);
|
||||
|
||||
|
@ -12,7 +12,7 @@ import android.widget.ImageView;
|
||||
|
||||
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiPageViewGridAdapter.VariationSelectorListener;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import com.bumptech.glide.RequestManager;
|
||||
import org.thoughtcrime.securesms.util.ResUtil;
|
||||
|
||||
import org.session.libsession.utilities.ThemeUtil;
|
||||
@ -87,7 +87,7 @@ public class EmojiKeyboardProvider implements MediaKeyboardProvider,
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadCategoryTabIcon(@NonNull GlideRequests glideRequests, @NonNull ImageView imageView, int index) {
|
||||
public void loadCategoryTabIcon(@NonNull RequestManager glideRequests, @NonNull ImageView imageView, int index) {
|
||||
Drawable drawable = ResUtil.getDrawable(context, models.get(index).getIconAttr());
|
||||
imageView.setImageDrawable(drawable);
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -17,7 +17,7 @@ import android.widget.FrameLayout;
|
||||
import org.thoughtcrime.securesms.components.InputAwareLayout.InputView;
|
||||
import org.thoughtcrime.securesms.components.RepeatableImageKey;
|
||||
import org.session.libsignal.utilities.Log;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import com.bumptech.glide.Glide;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@ -158,7 +158,7 @@ public class MediaKeyboard extends FrameLayout implements InputView,
|
||||
this.searchButton = view.findViewById(R.id.media_keyboard_search);
|
||||
this.addButton = view.findViewById(R.id.media_keyboard_add);
|
||||
|
||||
this.categoryTabAdapter = new MediaKeyboardBottomTabAdapter(GlideApp.with(this), this);
|
||||
this.categoryTabAdapter = new MediaKeyboardBottomTabAdapter(Glide.with(this), this);
|
||||
|
||||
categoryTabs.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false));
|
||||
categoryTabs.setAdapter(categoryTabAdapter);
|
||||
|
@ -9,20 +9,20 @@ import android.widget.ImageView;
|
||||
|
||||
|
||||
import org.thoughtcrime.securesms.components.emoji.MediaKeyboardProvider.TabIconProvider;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import com.bumptech.glide.RequestManager;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
|
||||
public class MediaKeyboardBottomTabAdapter extends RecyclerView.Adapter<MediaKeyboardBottomTabAdapter.MediaKeyboardBottomTabViewHolder> {
|
||||
|
||||
private final GlideRequests glideRequests;
|
||||
private final RequestManager glideRequests;
|
||||
private final EventListener eventListener;
|
||||
|
||||
private TabIconProvider tabIconProvider;
|
||||
private int activePosition;
|
||||
private int count;
|
||||
|
||||
public MediaKeyboardBottomTabAdapter(@NonNull GlideRequests glideRequests, @NonNull EventListener eventListener) {
|
||||
public MediaKeyboardBottomTabAdapter(@NonNull RequestManager glideRequests, @NonNull EventListener eventListener) {
|
||||
this.glideRequests = glideRequests;
|
||||
this.eventListener = eventListener;
|
||||
}
|
||||
@ -71,7 +71,7 @@ public class MediaKeyboardBottomTabAdapter extends RecyclerView.Adapter<MediaKey
|
||||
this.indicator = itemView.findViewById(R.id.media_keyboard_bottom_tab_indicator);
|
||||
}
|
||||
|
||||
void bind(@NonNull GlideRequests glideRequests,
|
||||
void bind(@NonNull RequestManager glideRequests,
|
||||
@NonNull EventListener eventListener,
|
||||
@NonNull TabIconProvider tabIconProvider,
|
||||
int index,
|
||||
|
@ -8,7 +8,7 @@ import android.widget.ImageView;
|
||||
|
||||
|
||||
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import com.bumptech.glide.RequestManager;
|
||||
|
||||
public interface MediaKeyboardProvider {
|
||||
@LayoutRes int getProviderIconView(boolean selected);
|
||||
@ -48,6 +48,6 @@ public interface MediaKeyboardProvider {
|
||||
}
|
||||
|
||||
interface TabIconProvider {
|
||||
void loadCategoryTabIcon(@NonNull GlideRequests glideRequests, @NonNull ImageView imageView, int index);
|
||||
void loadCategoryTabIcon(@NonNull RequestManager glideRequests, @NonNull ImageView imageView, int index);
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ public class ContactAccessor {
|
||||
}
|
||||
}
|
||||
|
||||
// if (context.getString(R.string.note_to_self).toLowerCase().contains(constraint.toLowerCase()) &&
|
||||
// if (context.getString(R.string.noteToSelf).toLowerCase().contains(constraint.toLowerCase()) &&
|
||||
// !numberList.contains(TextSecurePreferences.getLocalNumber(context)))
|
||||
// {
|
||||
// numberList.add(TextSecurePreferences.getLocalNumber(context));
|
||||
|
@ -6,10 +6,10 @@ import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import network.loki.messenger.databinding.ContactSelectionListDividerBinding
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||
import com.bumptech.glide.RequestManager
|
||||
|
||||
class ContactSelectionListAdapter(private val context: Context, private val multiSelect: Boolean) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
lateinit var glide: GlideRequests
|
||||
lateinit var glide: RequestManager
|
||||
val selectedContacts = mutableSetOf<Recipient>()
|
||||
var items = listOf<ContactSelectionListItem>()
|
||||
set(value) { field = value; notifyDataSetChanged() }
|
||||
|
@ -11,7 +11,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import network.loki.messenger.databinding.ContactSelectionListFragmentBinding
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.session.libsignal.utilities.Log
|
||||
import org.thoughtcrime.securesms.mms.GlideApp
|
||||
import com.bumptech.glide.Glide
|
||||
|
||||
class ContactSelectionListFragment : Fragment(), LoaderManager.LoaderCallbacks<List<ContactSelectionListItem>>, ContactClickListener {
|
||||
private lateinit var binding: ContactSelectionListFragmentBinding
|
||||
@ -27,7 +27,7 @@ class ContactSelectionListFragment : Fragment(), LoaderManager.LoaderCallbacks<L
|
||||
|
||||
private val listAdapter by lazy {
|
||||
val result = ContactSelectionListAdapter(requireActivity(), multiSelect)
|
||||
result.glide = GlideApp.with(this)
|
||||
result.glide = Glide.with(this)
|
||||
result.contactClickListener = this
|
||||
result
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ class ContactSelectionListLoader(context: Context, val mode: Int, val filter: St
|
||||
list.addAll(getClosedGroups(contacts))
|
||||
}
|
||||
if (isFlagSet(DisplayMode.FLAG_OPEN_GROUPS)) {
|
||||
list.addAll(getOpenGroups(contacts))
|
||||
list.addAll(getCommunities(contacts))
|
||||
}
|
||||
if (isFlagSet(DisplayMode.FLAG_CONTACTS)) {
|
||||
list.addAll(getContacts(contacts))
|
||||
@ -45,19 +45,19 @@ class ContactSelectionListLoader(context: Context, val mode: Int, val filter: St
|
||||
}
|
||||
|
||||
private fun getContacts(contacts: List<Recipient>): List<ContactSelectionListItem> {
|
||||
return getItems(contacts, context.getString(R.string.fragment_contact_selection_contacts_title)) {
|
||||
return getItems(contacts, context.getString(R.string.contactsContacts)) {
|
||||
!it.isGroupRecipient
|
||||
}
|
||||
}
|
||||
|
||||
private fun getClosedGroups(contacts: List<Recipient>): List<ContactSelectionListItem> {
|
||||
return getItems(contacts, context.getString(R.string.fragment_contact_selection_closed_groups_title)) {
|
||||
return getItems(contacts, context.getString(R.string.conversationsGroups)) {
|
||||
it.address.isClosedGroup
|
||||
}
|
||||
}
|
||||
|
||||
private fun getOpenGroups(contacts: List<Recipient>): List<ContactSelectionListItem> {
|
||||
return getItems(contacts, context.getString(R.string.fragment_contact_selection_open_groups_title)) {
|
||||
private fun getCommunities(contacts: List<Recipient>): List<ContactSelectionListItem> {
|
||||
return getItems(contacts, context.getString(R.string.conversationsCommunities)) {
|
||||
it.address.isCommunity
|
||||
}
|
||||
}
|
||||
|
@ -18,10 +18,10 @@ public final class ContactUtil {
|
||||
String contactName = ContactUtil.getDisplayName(contact);
|
||||
|
||||
if (!TextUtils.isEmpty(contactName)) {
|
||||
return context.getString(R.string.MessageNotifier_contact_message, EmojiStrings.BUST_IN_SILHOUETTE, contactName);
|
||||
return EmojiStrings.BUST_IN_SILHOUETTE + " " + contactName;
|
||||
}
|
||||
|
||||
return SpanUtil.italic(context.getString(R.string.MessageNotifier_unknown_contact_message));
|
||||
return SpanUtil.italic(context.getString(R.string.unknown));
|
||||
}
|
||||
|
||||
private static @NonNull String getDisplayName(@Nullable Contact contact) {
|
||||
|
@ -159,7 +159,7 @@ public class ContactsCursorLoader extends CursorLoader {
|
||||
|
||||
private Cursor getGroupsHeaderCursor() {
|
||||
MatrixCursor groupHeader = new MatrixCursor(CONTACT_PROJECTION, 1);
|
||||
groupHeader.addRow(new Object[]{ getContext().getString(R.string.ContactsCursorLoader_groups),
|
||||
groupHeader.addRow(new Object[]{ getContext().getString(R.string.conversationsGroups),
|
||||
"",
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
|
||||
"",
|
||||
@ -221,16 +221,6 @@ public class ContactsCursorLoader extends CursorLoader {
|
||||
return groupContacts;
|
||||
}
|
||||
|
||||
private Cursor getNewNumberCursor() {
|
||||
MatrixCursor newNumberCursor = new MatrixCursor(CONTACT_PROJECTION, 1);
|
||||
newNumberCursor.addRow(new Object[] { getContext().getString(R.string.contact_selection_list__unknown_contact),
|
||||
filter,
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM,
|
||||
"\u21e2",
|
||||
NEW_TYPE });
|
||||
return newNumberCursor;
|
||||
}
|
||||
|
||||
private static boolean isCursorListEmpty(List<Cursor> list) {
|
||||
int sum = 0;
|
||||
for (Cursor cursor : list) {
|
||||
|
@ -12,7 +12,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import network.loki.messenger.R
|
||||
import network.loki.messenger.databinding.ActivitySelectContactsBinding
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||
import org.thoughtcrime.securesms.mms.GlideApp
|
||||
import com.bumptech.glide.Glide
|
||||
|
||||
class SelectContactsActivity : PassphraseRequiredActionBarActivity(), LoaderManager.LoaderCallbacks<List<String>> {
|
||||
private lateinit var binding: ActivitySelectContactsBinding
|
||||
@ -21,7 +21,7 @@ class SelectContactsActivity : PassphraseRequiredActionBarActivity(), LoaderMana
|
||||
private lateinit var usersToExclude: Set<String>
|
||||
|
||||
private val selectContactsAdapter by lazy {
|
||||
SelectContactsAdapter(this, GlideApp.with(this))
|
||||
SelectContactsAdapter(this, Glide.with(this))
|
||||
}
|
||||
|
||||
companion object {
|
||||
@ -35,7 +35,7 @@ class SelectContactsActivity : PassphraseRequiredActionBarActivity(), LoaderMana
|
||||
super.onCreate(savedInstanceState, isReady)
|
||||
binding = ActivitySelectContactsBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
supportActionBar!!.title = resources.getString(R.string.activity_select_contacts_title)
|
||||
supportActionBar!!.title = resources.getString(R.string.contactSelect)
|
||||
|
||||
usersToExclude = intent.getStringArrayExtra(usersToExcludeKey)?.toSet() ?: setOf()
|
||||
val emptyStateText = intent.getStringExtra(emptyStateTextKey)
|
||||
|
@ -4,10 +4,10 @@ import android.content.Context
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.view.ViewGroup
|
||||
import org.session.libsession.utilities.Address
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||
import com.bumptech.glide.RequestManager
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
|
||||
class SelectContactsAdapter(private val context: Context, private val glide: GlideRequests) : RecyclerView.Adapter<SelectContactsAdapter.ViewHolder>() {
|
||||
class SelectContactsAdapter(private val context: Context, private val glide: RequestManager) : RecyclerView.Adapter<SelectContactsAdapter.ViewHolder>() {
|
||||
val selectedMembers = mutableSetOf<String>()
|
||||
var members = listOf<String>()
|
||||
set(value) { field = value; notifyDataSetChanged() }
|
||||
|
@ -10,11 +10,11 @@ import network.loki.messenger.databinding.ViewUserBinding
|
||||
import org.session.libsession.messaging.contacts.Contact
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||
import com.bumptech.glide.RequestManager
|
||||
|
||||
class UserView : LinearLayout {
|
||||
private lateinit var binding: ViewUserBinding
|
||||
var openGroupThreadID: Long = -1 // FIXME: This is a bit ugly
|
||||
var openGroupThreadID: Long = -1L // FIXME: This is a bit ugly
|
||||
|
||||
enum class ActionIndicator {
|
||||
None,
|
||||
@ -45,13 +45,15 @@ class UserView : LinearLayout {
|
||||
// endregion
|
||||
|
||||
// region Updating
|
||||
fun bind(user: Recipient, glide: GlideRequests, actionIndicator: ActionIndicator, isSelected: Boolean = false) {
|
||||
fun bind(user: Recipient, glide: RequestManager, actionIndicator: ActionIndicator, isSelected: Boolean = false) {
|
||||
val isLocalUser = user.isLocalNumber
|
||||
|
||||
fun getUserDisplayName(publicKey: String): String {
|
||||
if (isLocalUser) return context.getString(R.string.MessageRecord_you)
|
||||
if (isLocalUser) return context.getString(R.string.you)
|
||||
val contact = DatabaseComponent.get(context).sessionContactDatabase().getContactWithAccountID(publicKey)
|
||||
return contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey
|
||||
}
|
||||
|
||||
val address = user.address.serialize()
|
||||
binding.profilePictureView.update(user)
|
||||
binding.actionIndicatorImageView.setImageResource(R.drawable.ic_baseline_edit_24)
|
||||
@ -84,8 +86,7 @@ class UserView : LinearLayout {
|
||||
}
|
||||
}
|
||||
|
||||
fun unbind() {
|
||||
binding.profilePictureView.recycle()
|
||||
}
|
||||
fun unbind() { binding.profilePictureView.recycle() }
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
@ -11,21 +11,26 @@ import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import com.squareup.phrase.Phrase
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import network.loki.messenger.R
|
||||
import network.loki.messenger.databinding.ViewConversationActionBarBinding
|
||||
import network.loki.messenger.databinding.ViewConversationSettingBinding
|
||||
import network.loki.messenger.libsession_util.util.ExpiryMode
|
||||
import network.loki.messenger.libsession_util.util.ExpiryMode.AfterRead
|
||||
import org.session.libsession.LocalisedTimeUtil
|
||||
import org.session.libsession.messaging.messages.ExpirationConfiguration
|
||||
import org.session.libsession.messaging.open_groups.OpenGroup
|
||||
import org.session.libsession.utilities.ExpirationUtil
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.COUNT_KEY
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.DISAPPEARING_MESSAGES_TYPE_KEY
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.TIME_KEY
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.TIME_LARGE_KEY
|
||||
import org.session.libsession.utilities.modifyLayoutParams
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase
|
||||
import org.thoughtcrime.securesms.database.LokiAPIDatabase
|
||||
import org.thoughtcrime.securesms.util.DateUtils
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ConversationActionBarView @JvmOverloads constructor(
|
||||
@ -82,7 +87,7 @@ class ConversationActionBarView @JvmOverloads constructor(
|
||||
|
||||
fun update(recipient: Recipient, openGroup: OpenGroup? = null, config: ExpirationConfiguration? = null) {
|
||||
binding.profilePictureView.update(recipient)
|
||||
binding.conversationTitleView.text = recipient.takeUnless { it.isLocalNumber }?.toShortString() ?: context.getString(R.string.note_to_self)
|
||||
binding.conversationTitleView.text = recipient.takeUnless { it.isLocalNumber }?.toShortString() ?: context.getString(R.string.noteToSelf)
|
||||
updateSubtitle(recipient, openGroup, config)
|
||||
|
||||
binding.conversationTitleContainer.modifyLayoutParams<MarginLayoutParams> {
|
||||
@ -92,37 +97,58 @@ class ConversationActionBarView @JvmOverloads constructor(
|
||||
|
||||
fun updateSubtitle(recipient: Recipient, openGroup: OpenGroup? = null, config: ExpirationConfiguration? = null) {
|
||||
val settings = mutableListOf<ConversationSetting>()
|
||||
|
||||
// Specify the disappearing messages subtitle if we should
|
||||
if (config?.isEnabled == true) {
|
||||
val prefix = when (config.expiryMode) {
|
||||
is ExpiryMode.AfterRead -> R.string.expiration_type_disappear_after_read
|
||||
else -> R.string.expiration_type_disappear_after_send
|
||||
}.let(context::getString)
|
||||
// Get the type of disappearing message and the abbreviated duration..
|
||||
val dmTypeString = when (config.expiryMode) {
|
||||
is AfterRead -> context.getString(R.string.read)
|
||||
else -> context.getString(R.string.send)
|
||||
}
|
||||
val durationAbbreviated = ExpirationUtil.getExpirationAbbreviatedDisplayValue(config.expiryMode.expirySeconds)
|
||||
|
||||
// ..then substitute into the string..
|
||||
val subtitleTxt = Phrase.from(context, R.string.disappearingMessagesDisappear)
|
||||
.put(DISAPPEARING_MESSAGES_TYPE_KEY, dmTypeString)
|
||||
.put(TIME_KEY, durationAbbreviated)
|
||||
.format().toString()
|
||||
|
||||
// .. and apply to the subtitle.
|
||||
settings += ConversationSetting(
|
||||
"$prefix - ${ExpirationUtil.getExpirationAbbreviatedDisplayValue(context, config.expiryMode.expirySeconds)}",
|
||||
subtitleTxt,
|
||||
ConversationSettingType.EXPIRATION,
|
||||
R.drawable.ic_timer,
|
||||
resources.getString(R.string.AccessibilityId_disappearing_messages_type_and_time)
|
||||
)
|
||||
}
|
||||
|
||||
if (recipient.isMuted) {
|
||||
settings += ConversationSetting(
|
||||
recipient.mutedUntil.takeUnless { it == Long.MAX_VALUE }
|
||||
?.let { context.getString(R.string.ConversationActivity_muted_until_date, DateUtils.getFormattedDateTime(it, "EEE, MMM d, yyyy HH:mm", Locale.getDefault())) }
|
||||
?: context.getString(R.string.ConversationActivity_muted_forever),
|
||||
?.let {
|
||||
val mutedDuration = it.milliseconds
|
||||
val durationString = LocalisedTimeUtil.getDurationWithSingleLargestTimeUnit(context, mutedDuration)
|
||||
Phrase.from(context, R.string.notificationsMuteFor)
|
||||
.put(TIME_LARGE_KEY, durationString)
|
||||
.format().toString()
|
||||
}
|
||||
?: context.getString(R.string.notificationsMuted),
|
||||
ConversationSettingType.NOTIFICATION,
|
||||
R.drawable.ic_outline_notifications_off_24
|
||||
)
|
||||
}
|
||||
|
||||
if (recipient.isGroupRecipient) {
|
||||
val title = if (recipient.isCommunityRecipient) {
|
||||
val userCount = openGroup?.let { lokiApiDb.getUserCount(it.room, it.server) } ?: 0
|
||||
context.getString(R.string.ConversationActivity_active_member_count, userCount)
|
||||
resources.getQuantityString(R.plurals.membersActive, userCount, userCount)
|
||||
} else {
|
||||
val userCount = groupDb.getGroupMemberAddresses(recipient.address.toGroupString(), true).size
|
||||
context.getString(R.string.ConversationActivity_member_count, userCount)
|
||||
resources.getQuantityString(R.plurals.members, userCount, userCount)
|
||||
}
|
||||
settings += ConversationSetting(title, ConversationSettingType.MEMBER_COUNT)
|
||||
}
|
||||
|
||||
settingsAdapter.submitList(settings)
|
||||
binding.settingsTabLayout.isVisible = settings.size > 1
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
package org.thoughtcrime.securesms.conversation.disappearingmessages
|
||||
|
||||
import android.content.Context
|
||||
import com.squareup.phrase.Phrase
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import javax.inject.Inject
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import network.loki.messenger.R
|
||||
import network.loki.messenger.libsession_util.util.ExpiryMode
|
||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||
@ -12,13 +15,13 @@ import org.session.libsession.snode.SnodeAPI
|
||||
import org.session.libsession.utilities.Address
|
||||
import org.session.libsession.utilities.ExpirationUtil
|
||||
import org.session.libsession.utilities.SSKEnvironment.MessageExpirationManagerProtocol
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.DISAPPEARING_MESSAGES_TYPE_KEY
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.TIME_LARGE_KEY
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsession.utilities.getExpirationTypeDisplayValue
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||
import org.thoughtcrime.securesms.showSessionDialog
|
||||
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
|
||||
import javax.inject.Inject
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
class DisappearingMessages @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
@ -43,21 +46,18 @@ class DisappearingMessages @Inject constructor(
|
||||
}
|
||||
|
||||
fun showFollowSettingDialog(context: Context, message: MessageRecord) = context.showSessionDialog {
|
||||
title(R.string.dialog_disappearing_messages_follow_setting_title)
|
||||
title(R.string.disappearingMessagesFollowSetting)
|
||||
text(if (message.expiresIn == 0L) {
|
||||
context.getString(R.string.dialog_disappearing_messages_follow_setting_off_body)
|
||||
context.getString(R.string.disappearingMessagesFollowSettingOff)
|
||||
} else {
|
||||
context.getString(
|
||||
R.string.dialog_disappearing_messages_follow_setting_on_body,
|
||||
ExpirationUtil.getExpirationDisplayValue(
|
||||
context,
|
||||
message.expiresIn.milliseconds
|
||||
),
|
||||
context.getExpirationTypeDisplayValue(message.isNotDisappearAfterRead)
|
||||
)
|
||||
Phrase.from(context, R.string.disappearingMessagesFollowSettingOn)
|
||||
.put(TIME_LARGE_KEY, ExpirationUtil.getExpirationDisplayValue(context, message.expiresIn.milliseconds))
|
||||
.put(DISAPPEARING_MESSAGES_TYPE_KEY, context.getExpirationTypeDisplayValue(message.isNotDisappearAfterRead))
|
||||
.format().toString()
|
||||
})
|
||||
|
||||
dangerButton(
|
||||
text = if (message.expiresIn == 0L) R.string.dialog_disappearing_messages_follow_setting_confirm else R.string.dialog_disappearing_messages_follow_setting_set,
|
||||
text = if (message.expiresIn == 0L) R.string.dialog_disappearing_messages_follow_setting_confirm else R.string.set,
|
||||
contentDescription = if (message.expiresIn == 0L) R.string.AccessibilityId_confirm else R.string.AccessibilityId_set_button
|
||||
) {
|
||||
set(message.threadId, message.recipient.address, message.expiryMode, message.recipient.isClosedGroupRecipient)
|
||||
|
@ -72,7 +72,7 @@ class DisappearingMessagesActivity: PassphraseRequiredActionBarActivity() {
|
||||
}
|
||||
|
||||
private fun setUpToolbar() {
|
||||
setSupportActionBar(binding.toolbar)
|
||||
setSupportActionBar(binding.searchToolbar)
|
||||
supportActionBar?.apply {
|
||||
title = getString(R.string.activity_disappearing_messages_title)
|
||||
setDisplayHomeAsUpEnabled(true)
|
||||
|
@ -51,7 +51,7 @@ enum class ExpiryType(
|
||||
) {
|
||||
NONE(
|
||||
{ ExpiryMode.NONE },
|
||||
R.string.expiration_off,
|
||||
R.string.off,
|
||||
contentDescription = R.string.AccessibilityId_disable_disappearing_messages,
|
||||
),
|
||||
LEGACY(
|
||||
@ -61,14 +61,14 @@ enum class ExpiryType(
|
||||
),
|
||||
AFTER_READ(
|
||||
ExpiryMode::AfterRead,
|
||||
R.string.expiration_type_disappear_after_read,
|
||||
R.string.expiration_type_disappear_after_read_description,
|
||||
R.string.disappearingMessagesDisappearAfterRead,
|
||||
R.string.disappearingMessagesDisappearAfterReadDescription,
|
||||
R.string.AccessibilityId_disappear_after_read_option
|
||||
),
|
||||
AFTER_SEND(
|
||||
ExpiryMode::AfterSend,
|
||||
R.string.expiration_type_disappear_after_send,
|
||||
R.string.expiration_type_disappear_after_send_description,
|
||||
R.string.disappearingMessagesDisappearAfterSend,
|
||||
R.string.disappearingMessagesDisappearAfterSendDescription,
|
||||
R.string.AccessibilityId_disappear_after_send_option
|
||||
);
|
||||
|
||||
|
@ -13,7 +13,7 @@ import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
fun State.toUiState() = UiState(
|
||||
cards = listOfNotNull(
|
||||
typeOptions()?.let { ExpiryOptionsCardData(GetString(R.string.activity_disappearing_messages_delete_type), it) },
|
||||
typeOptions()?.let { ExpiryOptionsCardData(GetString(R.string.disappearingMessagesDeleteType), it) },
|
||||
timeOptions()?.let { ExpiryOptionsCardData(GetString(R.string.activity_disappearing_messages_timer), it) }
|
||||
),
|
||||
showGroupFooter = isGroup && isNewConfigEnabled,
|
||||
@ -66,11 +66,14 @@ private fun State.typeOption(
|
||||
)
|
||||
|
||||
private fun debugTimes(isDebug: Boolean) = if (isDebug) listOf(10.seconds, 30.seconds, 1.minutes) else emptyList()
|
||||
|
||||
private fun debugModes(isDebug: Boolean, type: ExpiryType) =
|
||||
debugTimes(isDebug).map { type.mode(it.inWholeSeconds) }
|
||||
|
||||
private fun State.debugOptions(): List<ExpiryRadioOption> =
|
||||
debugModes(showDebugOptions, nextType).map { timeOption(it, subtitle = GetString("for testing purposes")) }
|
||||
|
||||
// Standard list of available disappearing message times
|
||||
private val afterSendTimes = listOf(12.hours, 1.days, 7.days, 14.days)
|
||||
|
||||
private val afterReadTimes = buildList {
|
||||
|
@ -1,6 +1,5 @@
|
||||
package org.thoughtcrime.securesms.conversation.disappearingmessages.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
@ -16,18 +15,17 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import network.loki.messenger.R
|
||||
import network.loki.messenger.libsession_util.util.ExpiryMode
|
||||
import org.thoughtcrime.securesms.ui.Callbacks
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.NoOpCallbacks
|
||||
import org.thoughtcrime.securesms.ui.OptionsCard
|
||||
import org.thoughtcrime.securesms.ui.RadioOption
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.components.SlimOutlineButton
|
||||
import org.thoughtcrime.securesms.ui.contentDescription
|
||||
import org.thoughtcrime.securesms.ui.fadingEdges
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalType
|
||||
|
||||
typealias ExpiryCallbacks = Callbacks<ExpiryMode>
|
||||
@ -59,7 +57,9 @@ fun DisappearingMessages(
|
||||
}
|
||||
|
||||
if (state.showGroupFooter) Text(
|
||||
text = stringResource(R.string.activity_disappearing_messages_group_footer),
|
||||
text = stringResource(R.string.disappearingMessagesDescription) +
|
||||
"\n" +
|
||||
stringResource(R.string.disappearingMessagesOnlyAdmins),
|
||||
style = LocalType.current.extraSmall,
|
||||
fontWeight = FontWeight(400),
|
||||
color = LocalColors.current.textSecondary,
|
||||
@ -72,7 +72,7 @@ fun DisappearingMessages(
|
||||
}
|
||||
|
||||
if (state.showSetButton) SlimOutlineButton(
|
||||
stringResource(R.string.disappearing_messages_set_button_title),
|
||||
stringResource(R.string.set),
|
||||
modifier = Modifier
|
||||
.contentDescription(R.string.AccessibilityId_set_button)
|
||||
.align(Alignment.CenterHorizontally)
|
||||
|
@ -41,7 +41,7 @@ internal fun StartConversationScreen(
|
||||
LocalColors.current.backgroundSecondary,
|
||||
shape = MaterialTheme.shapes.small
|
||||
)) {
|
||||
AppBar(stringResource(R.string.dialog_start_conversation_title), onClose = delegate::onDialogClosePressed)
|
||||
AppBar(stringResource(R.string.conversationsStart), onClose = delegate::onDialogClosePressed)
|
||||
Surface(
|
||||
modifier = Modifier.nestedScroll(rememberNestedScrollInteropConnection()),
|
||||
color = LocalColors.current.backgroundSecondary
|
||||
@ -56,21 +56,21 @@ internal fun StartConversationScreen(
|
||||
onClick = delegate::onNewMessageSelected)
|
||||
Divider(startIndent = LocalDimensions.current.dividerIndent)
|
||||
ItemButton(
|
||||
textId = R.string.activity_create_group_title,
|
||||
textId = R.string.groupCreate,
|
||||
icon = R.drawable.ic_group,
|
||||
modifier = Modifier.contentDescription(R.string.AccessibilityId_create_group),
|
||||
onClick = delegate::onCreateGroupSelected
|
||||
)
|
||||
Divider(startIndent = LocalDimensions.current.dividerIndent)
|
||||
ItemButton(
|
||||
textId = R.string.dialog_join_community_title,
|
||||
textId = R.string.communityJoin,
|
||||
icon = R.drawable.ic_globe,
|
||||
modifier = Modifier.contentDescription(R.string.AccessibilityId_join_community),
|
||||
onClick = delegate::onJoinCommunitySelected
|
||||
)
|
||||
Divider(startIndent = LocalDimensions.current.dividerIndent)
|
||||
ItemButton(
|
||||
textId = R.string.activity_settings_invite_button_title,
|
||||
textId = R.string.sessionInviteAFriend,
|
||||
icon = R.drawable.ic_invite_friend,
|
||||
Modifier.contentDescription(R.string.AccessibilityId_invite_friend_button),
|
||||
onClick = delegate::onInviteFriend
|
||||
|
@ -16,15 +16,15 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import network.loki.messenger.R
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.theme.PreviewTheme
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.components.AppBar
|
||||
import org.thoughtcrime.securesms.ui.components.SlimOutlineButton
|
||||
import org.thoughtcrime.securesms.ui.components.SlimOutlineCopyButton
|
||||
import org.thoughtcrime.securesms.ui.components.border
|
||||
import org.thoughtcrime.securesms.ui.contentDescription
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalColors
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalType
|
||||
import org.thoughtcrime.securesms.ui.theme.PreviewTheme
|
||||
|
||||
@Composable
|
||||
internal fun InviteFriend(
|
||||
@ -38,7 +38,11 @@ internal fun InviteFriend(
|
||||
LocalColors.current.backgroundSecondary,
|
||||
shape = MaterialTheme.shapes.small
|
||||
)) {
|
||||
AppBar(stringResource(R.string.invite_a_friend), onBack = onBack, onClose = onClose)
|
||||
AppBar(
|
||||
stringResource(R.string.sessionInviteAFriend),
|
||||
onBack = onBack,
|
||||
onClose = onClose
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.padding(horizontal = LocalDimensions.current.spacing)
|
||||
.padding(top = LocalDimensions.current.spacing),
|
||||
@ -57,7 +61,7 @@ internal fun InviteFriend(
|
||||
Spacer(modifier = Modifier.height(LocalDimensions.current.xsSpacing))
|
||||
|
||||
Text(
|
||||
stringResource(R.string.invite_your_friend_to_chat_with_you_on_session_by_sharing_your_account_id_with_them),
|
||||
stringResource(R.string.shareAccountIdDescription),
|
||||
textAlign = TextAlign.Center,
|
||||
style = LocalType.current.small,
|
||||
color = LocalColors.current.textSecondary,
|
||||
|
@ -41,7 +41,7 @@ import org.thoughtcrime.securesms.ui.components.SessionTabRow
|
||||
import org.thoughtcrime.securesms.ui.contentDescription
|
||||
import org.thoughtcrime.securesms.ui.theme.LocalType
|
||||
|
||||
private val TITLES = listOf(R.string.enter_account_id, R.string.qrScan)
|
||||
private val TITLES = listOf(R.string.accountIdEnter, R.string.qrScan)
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
@ -63,7 +63,7 @@ internal fun NewMessage(
|
||||
SessionTabRow(pagerState, TITLES)
|
||||
HorizontalPager(pagerState) {
|
||||
when (TITLES[it]) {
|
||||
R.string.enter_account_id -> EnterAccountId(state, callbacks, onHelp)
|
||||
R.string.accountIdEnter -> EnterAccountId(state, callbacks, onHelp)
|
||||
R.string.qrScan -> MaybeScanQrCode(qrErrors, onScan = callbacks::onScanQrCode)
|
||||
}
|
||||
}
|
||||
@ -101,7 +101,7 @@ private fun EnterAccountId(
|
||||
Spacer(modifier = Modifier.height(LocalDimensions.current.xxxsSpacing))
|
||||
|
||||
BorderlessButtonWithIcon(
|
||||
text = stringResource(R.string.messageNewDescription),
|
||||
text = stringResource(R.string.messageNewDescriptionMobile),
|
||||
modifier = Modifier
|
||||
.contentDescription(R.string.AccessibilityId_help_desk_link)
|
||||
.padding(horizontal = LocalDimensions.current.mediumSpacing)
|
||||
|
@ -4,6 +4,8 @@ import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import java.util.concurrent.TimeoutException
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
@ -19,8 +21,6 @@ import org.session.libsession.snode.SnodeAPI
|
||||
import org.session.libsignal.utilities.PublicKeyValidation
|
||||
import org.session.libsignal.utilities.timeout
|
||||
import org.thoughtcrime.securesms.ui.GetString
|
||||
import java.util.concurrent.TimeoutException
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
internal class NewMessageViewModel @Inject constructor(
|
||||
@ -59,7 +59,7 @@ internal class NewMessageViewModel @Inject constructor(
|
||||
if (PublicKeyValidation.isValid(value, isPrefixRequired = false) && PublicKeyValidation.hasValidPrefix(value)) {
|
||||
onPublicKey(value)
|
||||
} else {
|
||||
_qrErrors.tryEmit(application.getString(R.string.this_qr_code_does_not_contain_an_account_id))
|
||||
_qrErrors.tryEmit(application.getString(R.string.qrNotAccountId))
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,7 +99,7 @@ internal class NewMessageViewModel @Inject constructor(
|
||||
private fun Exception.toMessage() = when (this) {
|
||||
is SnodeAPI.Error.Generic -> application.getString(R.string.onsErrorNotRecognized)
|
||||
is TimeoutException -> application.getString(R.string.onsErrorUnableToSearch)
|
||||
else -> application.getString(R.string.fragment_enter_public_key_error_message)
|
||||
else -> application.getString(R.string.accountIdErrorInvalid)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,8 +18,8 @@ import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.provider.MediaStore
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.SpannedString
|
||||
import android.text.SpannableString
|
||||
import android.text.Spanned
|
||||
import android.text.TextUtils
|
||||
import android.text.style.StyleSpan
|
||||
import android.util.Pair
|
||||
@ -35,9 +35,9 @@ import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.view.drawToBitmap
|
||||
import androidx.core.text.set
|
||||
import androidx.core.text.toSpannable
|
||||
import androidx.core.view.drawToBitmap
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.DialogFragment
|
||||
@ -52,7 +52,19 @@ import androidx.loader.content.Loader
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.annimon.stream.Stream
|
||||
import com.squareup.phrase.Phrase
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.ExecutionException
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.math.sqrt
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
@ -65,7 +77,6 @@ import network.loki.messenger.databinding.ActivityConversationV2Binding
|
||||
import network.loki.messenger.libsession_util.util.ExpiryMode
|
||||
import nl.komponents.kovenant.ui.successUi
|
||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||
import org.session.libsession.messaging.contacts.Contact
|
||||
import org.session.libsession.messaging.messages.ExpirationConfiguration
|
||||
import org.session.libsession.messaging.messages.applyExpiryMode
|
||||
import org.session.libsession.messaging.messages.control.DataExtractionNotification
|
||||
@ -85,6 +96,10 @@ import org.session.libsession.utilities.Address
|
||||
import org.session.libsession.utilities.Address.Companion.fromSerialized
|
||||
import org.session.libsession.utilities.GroupUtil
|
||||
import org.session.libsession.utilities.MediaTypes
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.APP_NAME_KEY
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.CONVERSATION_NAME_KEY
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.GROUP_NAME_KEY
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.NAME_KEY
|
||||
import org.session.libsession.utilities.Stub
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsession.utilities.concurrent.SimpleTask
|
||||
@ -156,7 +171,7 @@ import org.thoughtcrime.securesms.mediasend.Media
|
||||
import org.thoughtcrime.securesms.mediasend.MediaSendActivity
|
||||
import org.thoughtcrime.securesms.mms.AudioSlide
|
||||
import org.thoughtcrime.securesms.mms.GifSlide
|
||||
import org.thoughtcrime.securesms.mms.GlideApp
|
||||
import com.bumptech.glide.Glide
|
||||
import org.thoughtcrime.securesms.mms.ImageSlide
|
||||
import org.thoughtcrime.securesms.mms.MediaConstraints
|
||||
import org.thoughtcrime.securesms.mms.Slide
|
||||
@ -173,23 +188,13 @@ import org.thoughtcrime.securesms.util.DateUtils
|
||||
import org.thoughtcrime.securesms.util.MediaUtil
|
||||
import org.thoughtcrime.securesms.util.NetworkUtils
|
||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask
|
||||
import org.thoughtcrime.securesms.util.drawToBitmap
|
||||
import org.thoughtcrime.securesms.util.isScrolledToBottom
|
||||
import org.thoughtcrime.securesms.util.isScrolledToWithin30dpOfBottom
|
||||
import org.thoughtcrime.securesms.util.push
|
||||
import org.thoughtcrime.securesms.util.show
|
||||
import org.thoughtcrime.securesms.util.start
|
||||
import org.thoughtcrime.securesms.util.toPx
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.ExecutionException
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.math.sqrt
|
||||
|
||||
private const val TAG = "ConversationActivityV2"
|
||||
|
||||
@ -349,7 +354,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
adapter
|
||||
}
|
||||
|
||||
private val glide by lazy { GlideApp.with(this) }
|
||||
private val glide by lazy { Glide.with(this) }
|
||||
private val lockViewHitMargin by lazy { toPx(40, resources) }
|
||||
private val gifButton by lazy { InputBarButton(this, R.drawable.ic_gif_white_24dp, hasOpaqueBackground = true, isGIFButton = true) }
|
||||
private val documentButton by lazy { InputBarButton(this, R.drawable.ic_document_small_dark, hasOpaqueBackground = true) }
|
||||
@ -706,7 +711,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
}
|
||||
|
||||
override fun onFailure(e: ExecutionException?) {
|
||||
Toast.makeText(this@ConversationActivityV2, R.string.activity_conversation_attachment_prep_failed, Toast.LENGTH_LONG).show()
|
||||
Toast.makeText(this@ConversationActivityV2, R.string.attachmentsErrorLoad, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
})
|
||||
return
|
||||
@ -757,9 +762,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
// called from onCreate
|
||||
private fun setUpBlockedBanner() {
|
||||
val recipient = viewModel.recipient?.takeUnless { it.isGroupRecipient } ?: return
|
||||
val accountID = recipient.address.toString()
|
||||
val name = sessionContactDb.getContactWithAccountID(accountID)?.displayName(Contact.ContactContext.REGULAR) ?: accountID
|
||||
binding.blockedBannerTextView.text = resources.getString(R.string.activity_conversation_blocked_banner_text, name)
|
||||
binding.blockedBannerTextView.text = applicationContext.getString(R.string.blockBlockedDescription)
|
||||
binding.blockedBanner.isVisible = recipient.isBlocked
|
||||
binding.blockedBanner.setOnClickListener { viewModel.unblock() }
|
||||
}
|
||||
@ -772,8 +775,11 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
|
||||
binding.outdatedBanner.isVisible = shouldShowLegacy
|
||||
if (shouldShowLegacy) {
|
||||
binding.outdatedBannerTextView.text =
|
||||
resources.getString(R.string.activity_conversation_outdated_client_banner_text, legacyRecipient!!.name)
|
||||
|
||||
val txt = Phrase.from(applicationContext, R.string.disappearingMessagesLegacy)
|
||||
.put(NAME_KEY, legacyRecipient!!.name)
|
||||
.format()
|
||||
binding?.outdatedBannerTextView?.text = txt
|
||||
}
|
||||
}
|
||||
|
||||
@ -1056,33 +1062,64 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
updateUnreadCountIndicator()
|
||||
}
|
||||
|
||||
// Method that takes a char sequence that contains one or more elements surrounded in bold tags
|
||||
// like "Hello <b>world</b>" and returns a SpannableString that will display the appropriate
|
||||
// elements in bold. If there are no such <b> or </b> elements then the original string is returned
|
||||
// as a SpannableString without any bold highlighting.
|
||||
private fun makeBoldBetweenTags(input: CharSequence): SpannableString {
|
||||
val spannable = SpannableString(input)
|
||||
var startIndex = 0
|
||||
while (true) {
|
||||
startIndex = input.indexOf("<b>", startIndex)
|
||||
if (startIndex == -1) break
|
||||
val endIndex = input.indexOf("</b>", startIndex + 3)
|
||||
if (endIndex == -1) break
|
||||
spannable.setSpan(StyleSpan(Typeface.BOLD),startIndex + 3, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
startIndex = endIndex + 4
|
||||
}
|
||||
return spannable
|
||||
}
|
||||
|
||||
// Update placeholder / control messages in a conversation
|
||||
private fun updatePlaceholder() {
|
||||
val recipient = viewModel.recipient ?: return Log.w("Loki", "recipient was null in placeholder update")
|
||||
val blindedRecipient = viewModel.blindedRecipient
|
||||
val openGroup = viewModel.openGroup
|
||||
|
||||
val (textResource, insertParam) = when {
|
||||
recipient.isLocalNumber -> R.string.activity_conversation_empty_state_note_to_self to null
|
||||
openGroup != null && !openGroup.canWrite -> R.string.activity_conversation_empty_state_read_only to recipient.toShortString()
|
||||
blindedRecipient?.blocksCommunityMessageRequests == true -> R.string.activity_conversation_empty_state_blocks_community_requests to recipient.toShortString()
|
||||
else -> R.string.activity_conversation_empty_state_default to recipient.toShortString()
|
||||
// Get the correct placeholder text for this type of empty conversation
|
||||
val isNoteToSelf = recipient.isLocalNumber
|
||||
val txtCS: CharSequence = when {
|
||||
recipient.isLocalNumber -> getString(R.string.noteToSelfEmpty)
|
||||
|
||||
// If this is a community which we cannot write to
|
||||
openGroup != null && !openGroup.canWrite -> {
|
||||
Phrase.from(applicationContext, R.string.conversationsEmpty)
|
||||
.put(CONVERSATION_NAME_KEY, openGroup.name)
|
||||
.format()
|
||||
}
|
||||
|
||||
// If we're trying to message someone who has blocked community message requests
|
||||
blindedRecipient?.blocksCommunityMessageRequests == true -> {
|
||||
Phrase.from(applicationContext, R.string.messageRequestsTurnedOff)
|
||||
.put(NAME_KEY, recipient.toShortString())
|
||||
.format()
|
||||
}
|
||||
|
||||
else -> {
|
||||
// If this is a group or community that we CAN send messages to
|
||||
Phrase.from(applicationContext, R.string.groupNoMessages)
|
||||
.put(GROUP_NAME_KEY, recipient.toShortString())
|
||||
.format()
|
||||
}
|
||||
}
|
||||
|
||||
val showPlaceholder = adapter.itemCount == 0
|
||||
binding.placeholderText.isVisible = showPlaceholder
|
||||
if (showPlaceholder) {
|
||||
if (insertParam != null) {
|
||||
val span = getText(textResource) as SpannedString
|
||||
val annotations = span.getSpans(0, span.length, StyleSpan::class.java)
|
||||
val boldSpan = annotations.first()
|
||||
val spannedParam = insertParam.toSpannable()
|
||||
spannedParam[0 until spannedParam.length] = StyleSpan(boldSpan.style)
|
||||
val originalStart = span.getSpanStart(boldSpan)
|
||||
val originalEnd = span.getSpanEnd(boldSpan)
|
||||
val newString = SpannableStringBuilder(span)
|
||||
.replace(originalStart, originalEnd, spannedParam)
|
||||
binding.placeholderText.text = newString
|
||||
if (!isNoteToSelf) {
|
||||
binding.placeholderText.text = makeBoldBetweenTags(txtCS)
|
||||
} else {
|
||||
binding.placeholderText.setText(textResource)
|
||||
binding.placeholderText.text = txtCS
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1117,11 +1154,21 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
}
|
||||
|
||||
override fun block(deleteThread: Boolean) {
|
||||
val recipient = viewModel.recipient ?: return Log.w("Loki", "Recipient was null for block action")
|
||||
showSessionDialog {
|
||||
title(R.string.RecipientPreferenceActivity_block_this_contact_question)
|
||||
text(R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact)
|
||||
dangerButton(R.string.RecipientPreferenceActivity_block, R.string.AccessibilityId_block_confirm) {
|
||||
title(R.string.block)
|
||||
text(
|
||||
Phrase.from(context, R.string.blockDescription)
|
||||
.put(NAME_KEY, recipient.name)
|
||||
.format()
|
||||
)
|
||||
dangerButton(R.string.block, R.string.AccessibilityId_block_confirm) {
|
||||
viewModel.block()
|
||||
|
||||
// Block confirmation toast added as per SS-64
|
||||
val txt = Phrase.from(context, R.string.blockBlockedUser).put(NAME_KEY, recipient.name).format().toString()
|
||||
Toast.makeText(context, txt, Toast.LENGTH_LONG).show()
|
||||
|
||||
if (deleteThread) {
|
||||
viewModel.deleteThread()
|
||||
finish()
|
||||
@ -1135,7 +1182,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
val clip = ClipData.newPlainText("Account ID", accountId)
|
||||
val manager = getSystemService(PassphraseRequiredActionBarActivity.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
manager.setPrimaryClip(clip)
|
||||
Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, R.string.copied, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
override fun copyOpenGroupUrl(thread: Recipient) {
|
||||
@ -1147,7 +1194,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
val clip = ClipData.newPlainText("Community URL", openGroup.joinURL)
|
||||
val manager = getSystemService(PassphraseRequiredActionBarActivity.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
manager.setPrimaryClip(clip)
|
||||
Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, R.string.copied, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
override fun showDisappearingMessages(thread: Recipient) {
|
||||
@ -1160,13 +1207,31 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
}
|
||||
|
||||
override fun unblock() {
|
||||
val recipient = viewModel.recipient ?: return Log.w("Loki", "Recipient was null for unblock action")
|
||||
|
||||
if (!recipient.isContactRecipient) {
|
||||
return Log.w("Loki", "Cannot unblock a user who is not a contact recipient - aborting unblock attempt.")
|
||||
}
|
||||
|
||||
showSessionDialog {
|
||||
title(R.string.ConversationActivity_unblock_this_contact_question)
|
||||
text(R.string.ConversationActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact)
|
||||
title(R.string.blockUnblock)
|
||||
text(
|
||||
Phrase.from(context, R.string.blockUnblockName)
|
||||
.put(NAME_KEY, recipient.name)
|
||||
.format()
|
||||
)
|
||||
dangerButton(
|
||||
R.string.ConversationActivity_unblock,
|
||||
R.string.AccessibilityId_block_confirm
|
||||
) { viewModel.unblock() }
|
||||
R.string.blockUnblock,
|
||||
R.string.AccessibilityId_unblock_confirm
|
||||
) {
|
||||
viewModel.unblock()
|
||||
|
||||
// Unblock confirmation toast added as per SS-64
|
||||
val txt = Phrase.from(context, R.string.blockUnblockedUser)
|
||||
.put(NAME_KEY, recipient.name).
|
||||
format().toString()
|
||||
Toast.makeText(context, txt, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
cancelButton()
|
||||
}
|
||||
}
|
||||
@ -1666,9 +1731,9 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
val hasSeenGIFMetaDataWarning: Boolean = textSecurePreferences.hasSeenGIFMetaDataWarning()
|
||||
if (!hasSeenGIFMetaDataWarning) {
|
||||
showSessionDialog {
|
||||
title(R.string.giphy_permission_title)
|
||||
text(R.string.giphy_permission_message)
|
||||
button(R.string.continue_2) {
|
||||
title(R.string.giphyWarning)
|
||||
text(Phrase.from(context, R.string.giphyWarningDescription).put(APP_NAME_KEY, getString(R.string.app_name)).format())
|
||||
button(R.string.theContinue) {
|
||||
textSecurePreferences.setHasSeenGIFMetaDataWarning()
|
||||
selectGif()
|
||||
}
|
||||
@ -1715,7 +1780,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
}
|
||||
|
||||
override fun onFailure(e: ExecutionException?) {
|
||||
Toast.makeText(this@ConversationActivityV2, R.string.activity_conversation_attachment_prep_failed, Toast.LENGTH_LONG).show()
|
||||
Toast.makeText(this@ConversationActivityV2, R.string.attachmentsErrorLoad, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
when (requestCode) {
|
||||
@ -1798,8 +1863,10 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
} else {
|
||||
Permissions.with(this)
|
||||
.request(Manifest.permission.RECORD_AUDIO)
|
||||
.withRationaleDialog(getString(R.string.ConversationActivity_to_send_audio_messages_allow_signal_access_to_your_microphone), R.drawable.ic_baseline_mic_48)
|
||||
.withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_requires_the_microphone_permission_in_order_to_send_audio_messages))
|
||||
.withRationaleDialog(getString(R.string.permissionsMicrophoneAccessRequired), R.drawable.ic_baseline_mic_48)
|
||||
.withPermanentDenialDialog(Phrase.from(applicationContext, R.string.permissionsMicrophoneAccessRequiredAndroid)
|
||||
.put(APP_NAME_KEY, getString(R.string.app_name))
|
||||
.format().toString())
|
||||
.execute()
|
||||
}
|
||||
}
|
||||
@ -1869,7 +1936,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
}
|
||||
|
||||
override fun onFailure(e: ExecutionException) {
|
||||
Toast.makeText(this@ConversationActivityV2, R.string.ConversationActivity_unable_to_record_audio, Toast.LENGTH_LONG).show()
|
||||
Toast.makeText(this@ConversationActivityV2, R.string.audioUnableToRecord, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -1920,10 +1987,10 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
}
|
||||
|
||||
private fun showDeleteLocallyUI(messages: Set<MessageRecord>) {
|
||||
val messageCount = 1
|
||||
val titleStringId = if (messages.count() == 1) R.string.deleteMessage else R.string.deleteMessages
|
||||
showSessionDialog {
|
||||
title(resources.getQuantityString(R.plurals.ConversationFragment_delete_selected_messages, messageCount, messageCount))
|
||||
text(resources.getQuantityString(R.plurals.ConversationFragment_this_will_permanently_delete_all_n_selected_messages, messageCount, messageCount))
|
||||
title(resources.getString(titleStringId))
|
||||
text(resources.getString(R.string.deleteMessagesDescriptionDevice))
|
||||
button(R.string.delete) { messages.forEach(viewModel::deleteLocally); endActionMode() }
|
||||
cancelButton(::endActionMode)
|
||||
}
|
||||
@ -1943,13 +2010,10 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
|
||||
// If the recipient is a community OR a Note-to-Self then we delete the message for everyone
|
||||
if (recipient.isCommunityRecipient || recipient.isLocalNumber) {
|
||||
val messageCount = 1 // Only used for plurals string
|
||||
showSessionDialog {
|
||||
title(resources.getQuantityString(R.plurals.ConversationFragment_delete_selected_messages, messageCount, messageCount))
|
||||
text(resources.getQuantityString(R.plurals.ConversationFragment_this_will_permanently_delete_all_n_selected_messages, messageCount, messageCount))
|
||||
button(R.string.delete) {
|
||||
messages.forEach(viewModel::deleteForEveryone); endActionMode()
|
||||
}
|
||||
title(resources.getString(R.string.deleteMessage))
|
||||
text(resources.getString(R.string.deleteMessageDescriptionEveryone))
|
||||
button(R.string.delete) { messages.forEach(viewModel::deleteForEveryone); endActionMode() }
|
||||
cancelButton { endActionMode() }
|
||||
}
|
||||
// Otherwise if this is a 1-on-1 conversation we may decided to delete just for ourselves or delete for everyone
|
||||
@ -1974,13 +2038,10 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
}
|
||||
else // Finally, if this is a closed group and you are deleting someone else's message(s) then we can only delete locally.
|
||||
{
|
||||
val messageCount = 1
|
||||
showSessionDialog {
|
||||
title(resources.getQuantityString(R.plurals.ConversationFragment_delete_selected_messages, messageCount, messageCount))
|
||||
text(resources.getQuantityString(R.plurals.ConversationFragment_this_will_permanently_delete_all_n_selected_messages, messageCount, messageCount))
|
||||
button(R.string.delete) {
|
||||
messages.forEach(viewModel::deleteLocally); endActionMode()
|
||||
}
|
||||
title(resources.getString(R.string.deleteMessage))
|
||||
text(resources.getString(R.string.deleteMessageDescriptionEveryone))
|
||||
button(R.string.delete) { messages.forEach(viewModel::deleteLocally); endActionMode() }
|
||||
cancelButton(::endActionMode)
|
||||
}
|
||||
}
|
||||
@ -1988,18 +2049,20 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
|
||||
override fun banUser(messages: Set<MessageRecord>) {
|
||||
showSessionDialog {
|
||||
title(R.string.ConversationFragment_ban_selected_user)
|
||||
title(R.string.banUser)
|
||||
// ACL TODO - We need a string for the below `text` element
|
||||
text("This will ban the selected user from this room. It won't ban them from other rooms.")
|
||||
button(R.string.ban) { viewModel.banUser(messages.first().individualRecipient); endActionMode() }
|
||||
button(R.string.banUser) { viewModel.banUser(messages.first().individualRecipient); endActionMode() }
|
||||
cancelButton(::endActionMode)
|
||||
}
|
||||
}
|
||||
|
||||
override fun banAndDeleteAll(messages: Set<MessageRecord>) {
|
||||
showSessionDialog {
|
||||
title(R.string.ConversationFragment_ban_selected_user)
|
||||
title(R.string.banUser)
|
||||
// ACL TODO - We need a string for the below `text` element
|
||||
text("This will ban the selected user from this room and delete all messages sent by them. It won't ban them from other rooms or delete the messages they sent there.")
|
||||
button(R.string.ban) { viewModel.banAndDeleteAll(messages.first()); endActionMode() }
|
||||
button(R.string.banUser) { viewModel.banAndDeleteAll(messages.first()); endActionMode() }
|
||||
cancelButton(::endActionMode)
|
||||
}
|
||||
}
|
||||
@ -2035,7 +2098,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
if (TextUtils.isEmpty(result)) { return }
|
||||
val manager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
||||
manager.setPrimaryClip(ClipData.newPlainText("Message Content", result))
|
||||
Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, R.string.copied, Toast.LENGTH_SHORT).show()
|
||||
endActionMode()
|
||||
}
|
||||
|
||||
@ -2044,7 +2107,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
val clip = ClipData.newPlainText("Account ID", accountID)
|
||||
val manager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
||||
manager.setPrimaryClip(clip)
|
||||
Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, R.string.copied, Toast.LENGTH_SHORT).show()
|
||||
endActionMode()
|
||||
}
|
||||
|
||||
@ -2096,10 +2159,15 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
Permissions.with(this)
|
||||
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
.maxSdkVersion(Build.VERSION_CODES.P)
|
||||
.withPermanentDenialDialog(getString(R.string.MediaPreviewActivity_signal_needs_the_storage_permission_in_order_to_write_to_external_storage_but_it_has_been_permanently_denied))
|
||||
.withPermanentDenialDialog(Phrase.from(applicationContext, R.string.permissionsStorageSaveDenied)
|
||||
.put(APP_NAME_KEY, getString(R.string.app_name))
|
||||
.format().toString())
|
||||
.onAnyDenied {
|
||||
endActionMode()
|
||||
Toast.makeText(this@ConversationActivityV2, R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show()
|
||||
val txt = Phrase.from(applicationContext, R.string.permissionsStorageSaveDenied)
|
||||
.put(APP_NAME_KEY, getString(R.string.app_name))
|
||||
.format().toString()
|
||||
Toast.makeText(this@ConversationActivityV2, txt, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
.onAllGranted {
|
||||
endActionMode()
|
||||
@ -2116,7 +2184,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
return@onAllGranted
|
||||
}
|
||||
Toast.makeText(this,
|
||||
resources.getQuantityString(R.plurals.ConversationFragment_error_while_saving_attachments_to_sd_card, 1),
|
||||
resources.getString(R.string.attachmentsSaveError),
|
||||
Toast.LENGTH_LONG).show()
|
||||
}
|
||||
.execute()
|
||||
@ -2172,6 +2240,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
searchViewModel.onMissingResult() }
|
||||
}
|
||||
}
|
||||
|
||||
binding.searchBottomBar.setData(result.position, result.getResults().size)
|
||||
})
|
||||
}
|
||||
@ -2181,6 +2250,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
binding.searchBottomBar.visibility = View.VISIBLE
|
||||
binding.searchBottomBar.setData(0, 0)
|
||||
binding.inputBar.visibility = View.INVISIBLE
|
||||
|
||||
}
|
||||
|
||||
fun onSearchClosed() {
|
||||
|
@ -12,6 +12,9 @@ import androidx.core.util.getOrDefault
|
||||
import androidx.core.util.set
|
||||
import androidx.lifecycle.LifecycleCoroutineScope
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.squareup.phrase.Phrase
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
import kotlin.math.min
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
@ -20,17 +23,16 @@ import kotlinx.coroutines.launch
|
||||
import network.loki.messenger.R
|
||||
import org.session.libsession.messaging.contacts.Contact
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.NAME_KEY
|
||||
import org.thoughtcrime.securesms.conversation.v2.messages.ControlMessageView
|
||||
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageView
|
||||
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageViewDelegate
|
||||
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||
import com.bumptech.glide.RequestManager
|
||||
import org.thoughtcrime.securesms.preferences.PrivacySettingsActivity
|
||||
import org.thoughtcrime.securesms.showSessionDialog
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
import kotlin.math.min
|
||||
|
||||
class ConversationAdapter(
|
||||
context: Context,
|
||||
@ -42,7 +44,7 @@ class ConversationAdapter(
|
||||
private val onItemLongPress: (MessageRecord, Int, VisibleMessageView) -> Unit,
|
||||
private val onDeselect: (MessageRecord, Int) -> Unit,
|
||||
private val onAttachmentNeedsDownload: (DatabaseAttachment) -> Unit,
|
||||
private val glide: GlideRequests,
|
||||
private val glide: RequestManager,
|
||||
lifecycleCoroutineScope: LifecycleCoroutineScope
|
||||
) : CursorRecyclerViewAdapter<ViewHolder>(context, cursor) {
|
||||
private val messageDB by lazy { DatabaseComponent.get(context).mmsSmsDatabase() }
|
||||
@ -154,9 +156,15 @@ class ConversationAdapter(
|
||||
if (message.isCallLog && message.isFirstMissedCall) {
|
||||
viewHolder.view.setOnClickListener {
|
||||
context.showSessionDialog {
|
||||
title(R.string.CallNotificationBuilder_first_call_title)
|
||||
text(R.string.CallNotificationBuilder_first_call_message)
|
||||
button(R.string.activity_settings_title) {
|
||||
val titleTxt = Phrase.from(context, R.string.callsMissedCallFrom)
|
||||
.put(NAME_KEY, message.individualRecipient.name)
|
||||
.format().toString()
|
||||
title(titleTxt)
|
||||
val bodyTxt = Phrase.from(context, R.string.callsYouMissedCallPermissions)
|
||||
.put(NAME_KEY, message.individualRecipient.name)
|
||||
.format().toString()
|
||||
text(bodyTxt)
|
||||
button(R.string.sessionSettings) {
|
||||
Intent(context, PrivacySettingsActivity::class.java)
|
||||
.let(context::startActivity)
|
||||
}
|
||||
|
@ -22,7 +22,11 @@ import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.doOnLayout
|
||||
import androidx.vectordrawable.graphics.drawable.AnimatorInflaterCompat
|
||||
import com.squareup.phrase.Phrase
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
@ -30,7 +34,9 @@ import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import network.loki.messenger.R
|
||||
import org.session.libsession.LocalisedTimeUtil.toShortTwoPartString
|
||||
import org.session.libsession.snode.SnodeAPI
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.TIME_LARGE_KEY
|
||||
import org.session.libsession.utilities.TextSecurePreferences.Companion.getLocalNumber
|
||||
import org.session.libsession.utilities.ThemeUtil
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiImageView
|
||||
@ -46,14 +52,6 @@ import org.thoughtcrime.securesms.dependencies.DatabaseComponent.Companion.get
|
||||
import org.thoughtcrime.securesms.repository.ConversationRepository
|
||||
import org.thoughtcrime.securesms.util.AnimationCompleteListener
|
||||
import org.thoughtcrime.securesms.util.DateUtils
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.days
|
||||
import kotlin.time.Duration.Companion.hours
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ConversationReactionOverlay : FrameLayout {
|
||||
@ -529,11 +527,11 @@ class ConversationReactionOverlay : FrameLayout {
|
||||
?: return emptyList()
|
||||
val userPublicKey = getLocalNumber(context)!!
|
||||
// Select message
|
||||
items += ActionItem(R.attr.menu_select_icon, R.string.conversation_context__menu_select, { handleActionItemClicked(Action.SELECT) }, R.string.AccessibilityId_select)
|
||||
items += ActionItem(R.attr.menu_select_icon, R.string.select, { handleActionItemClicked(Action.SELECT) }, R.string.AccessibilityId_select)
|
||||
// Reply
|
||||
val canWrite = openGroup == null || openGroup.canWrite
|
||||
if (canWrite && !message.isPending && !message.isFailed && !message.isOpenGroupInvitation) {
|
||||
items += ActionItem(R.attr.menu_reply_icon, R.string.conversation_context__menu_reply, { handleActionItemClicked(Action.REPLY) }, R.string.AccessibilityId_reply_message)
|
||||
items += ActionItem(R.attr.menu_reply_icon, R.string.reply, { handleActionItemClicked(Action.REPLY) }, R.string.AccessibilityId_reply_message)
|
||||
}
|
||||
// Copy message text
|
||||
if (!containsControlMessage && hasText) {
|
||||
@ -541,7 +539,7 @@ class ConversationReactionOverlay : FrameLayout {
|
||||
}
|
||||
// Copy Account ID
|
||||
if (recipient.isGroupRecipient && !recipient.isCommunityRecipient && message.recipient.address.toString() != userPublicKey) {
|
||||
items += ActionItem(R.attr.menu_copy_icon, R.string.activity_conversation_menu_copy_account_id, { handleActionItemClicked(Action.COPY_ACCOUNT_ID) })
|
||||
items += ActionItem(R.attr.menu_copy_icon, R.string.accountIdCopy, { handleActionItemClicked(Action.COPY_ACCOUNT_ID) })
|
||||
}
|
||||
// Delete message
|
||||
if (userCanDeleteSelectedItems(context, message, openGroup, userPublicKey, blindedPublicKey)) {
|
||||
@ -550,17 +548,17 @@ class ConversationReactionOverlay : FrameLayout {
|
||||
}
|
||||
// Ban user
|
||||
if (userCanBanSelectedUsers(context, message, openGroup, userPublicKey, blindedPublicKey)) {
|
||||
items += ActionItem(R.attr.menu_block_icon, R.string.conversation_context__menu_ban_user, { handleActionItemClicked(Action.BAN_USER) })
|
||||
items += ActionItem(R.attr.menu_block_icon, R.string.banUser, { handleActionItemClicked(Action.BAN_USER) })
|
||||
}
|
||||
// Ban and delete all
|
||||
if (userCanBanSelectedUsers(context, message, openGroup, userPublicKey, blindedPublicKey)) {
|
||||
items += ActionItem(R.attr.menu_trash_icon, R.string.conversation_context__menu_ban_and_delete_all, { handleActionItemClicked(Action.BAN_AND_DELETE_ALL) })
|
||||
items += ActionItem(R.attr.menu_trash_icon, R.string.banDeleteAll, { handleActionItemClicked(Action.BAN_AND_DELETE_ALL) })
|
||||
}
|
||||
// Message detail
|
||||
items += ActionItem(R.attr.menu_info_icon, R.string.conversation_context__menu_message_details, { handleActionItemClicked(Action.VIEW_INFO) })
|
||||
items += ActionItem(R.attr.menu_info_icon, R.string.messageInfo, { handleActionItemClicked(Action.VIEW_INFO) })
|
||||
// Resend
|
||||
if (message.isFailed) {
|
||||
items += ActionItem(R.attr.menu_reply_icon, R.string.conversation_context__menu_resend_message, { handleActionItemClicked(Action.RESEND) })
|
||||
items += ActionItem(R.attr.menu_reply_icon, R.string.resend, { handleActionItemClicked(Action.RESEND) })
|
||||
}
|
||||
// Resync
|
||||
if (message.isSyncFailed) {
|
||||
@ -704,10 +702,6 @@ class ConversationReactionOverlay : FrameLayout {
|
||||
}
|
||||
}
|
||||
|
||||
private fun Duration.to2partString(): String? =
|
||||
toComponents { days, hours, minutes, seconds, nanoseconds -> listOf(days.days, hours.hours, minutes.minutes, seconds.seconds) }
|
||||
.filter { it.inWholeSeconds > 0L }.take(2).takeIf { it.isNotEmpty() }?.joinToString(" ")
|
||||
|
||||
private val MessageRecord.subtitle: ((Context) -> CharSequence?)?
|
||||
get() = if (expiresIn <= 0) {
|
||||
null
|
||||
@ -715,6 +709,10 @@ private val MessageRecord.subtitle: ((Context) -> CharSequence?)?
|
||||
(expiresIn - (SnodeAPI.nowWithOffset - (expireStarted.takeIf { it > 0 } ?: timestamp)))
|
||||
.coerceAtLeast(0L)
|
||||
.milliseconds
|
||||
.to2partString()
|
||||
?.let { context.getString(R.string.auto_deletes_in, it) }
|
||||
.toShortTwoPartString()
|
||||
.let {
|
||||
Phrase.from(context, R.string.disappearingMessagesCountdownBigMobile)
|
||||
.put(TIME_LARGE_KEY, it)
|
||||
.format().toString()
|
||||
}
|
||||
}
|
@ -58,7 +58,7 @@ class DeleteOptionsBottomSheet : BottomSheetDialogFragment(), View.OnClickListen
|
||||
}
|
||||
if (!recipient.isGroupRecipient && !contact.isNullOrEmpty()) {
|
||||
binding.deleteForEveryoneTextView.text =
|
||||
resources.getString(R.string.delete_message_for_me_and_recipient, contact)
|
||||
resources.getString(R.string.clearMessagesForEveryone, contact)
|
||||
}
|
||||
binding.deleteForEveryoneTextView.isVisible = !recipient.isClosedGroupRecipient
|
||||
binding.deleteForMeTextView.setOnClickListener(this)
|
||||
|
@ -98,7 +98,7 @@ class MessageDetailActivity : PassphraseRequiredActionBarActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
|
||||
super.onCreate(savedInstanceState, ready)
|
||||
|
||||
title = resources.getString(R.string.conversation_context__menu_message_details)
|
||||
title = resources.getString(R.string.messageInfo)
|
||||
|
||||
viewModel.timestamp = intent.getLongExtra(MESSAGE_TIMESTAMP, -1L)
|
||||
|
||||
@ -319,13 +319,13 @@ fun PreviewMessageDetails(
|
||||
state = MessageDetailsState(
|
||||
nonImageAttachmentFileDetails = listOf(
|
||||
TitledText(R.string.message_details_header__file_id, "Screen Shot 2023-07-06 at 11.35.50 am.png"),
|
||||
TitledText(R.string.message_details_header__file_type, "image/png"),
|
||||
TitledText(R.string.message_details_header__file_size, "195.6kB"),
|
||||
TitledText(R.string.message_details_header__resolution, "342x312"),
|
||||
TitledText(R.string.attachmentsFileType, "image/png"),
|
||||
TitledText(R.string.attachmentsFileSize, "195.6kB"),
|
||||
TitledText(R.string.attachmentsResolution, "342x312"),
|
||||
),
|
||||
sent = TitledText(R.string.message_details_header__sent, "6:12 AM Tue, 09/08/2022"),
|
||||
received = TitledText(R.string.message_details_header__received, "6:12 AM Tue, 09/08/2022"),
|
||||
error = TitledText(R.string.message_details_header__error, "Message failed to send"),
|
||||
sent = TitledText(R.string.sent, "6:12 AM Tue, 09/08/2022"),
|
||||
received = TitledText(R.string.received, "6:12 AM Tue, 09/08/2022"),
|
||||
error = TitledText(R.string.error, "Message failed to send"),
|
||||
senderInfo = TitledText("Connor", "d4f1g54sdf5g1d5f4g65ds4564df65f4g65d54"),
|
||||
)
|
||||
)
|
||||
|
@ -78,9 +78,9 @@ class MessageDetailsViewModel @Inject constructor(
|
||||
MessageDetailsState(
|
||||
attachments = slides.map(::Attachment),
|
||||
record = record,
|
||||
sent = dateSent.let(::Date).toString().let { TitledText(R.string.message_details_header__sent, it) },
|
||||
received = dateReceived.let(::Date).toString().let { TitledText(R.string.message_details_header__received, it) },
|
||||
error = lokiMessageDatabase.getErrorMessage(id)?.let { TitledText(R.string.message_details_header__error, it) },
|
||||
sent = dateSent.let(::Date).toString().let { TitledText(R.string.sent, it) },
|
||||
received = dateReceived.let(::Date).toString().let { TitledText(R.string.received, it) },
|
||||
error = lokiMessageDatabase.getErrorMessage(id)?.let { TitledText(R.string.theError, it) },
|
||||
senderInfo = individualRecipient.run { name?.let { TitledText(it, address.serialize()) } },
|
||||
sender = individualRecipient,
|
||||
thread = threadDb.getRecipientForThreadId(threadId)!!,
|
||||
@ -91,13 +91,13 @@ class MessageDetailsViewModel @Inject constructor(
|
||||
private val Slide.details: List<TitledText>
|
||||
get() = listOfNotNull(
|
||||
fileName.orNull()?.let { TitledText(R.string.message_details_header__file_id, it) },
|
||||
TitledText(R.string.message_details_header__file_type, asAttachment().contentType),
|
||||
TitledText(R.string.message_details_header__file_size, Util.getPrettyFileSize(fileSize)),
|
||||
TitledText(R.string.attachmentsFileType, asAttachment().contentType),
|
||||
TitledText(R.string.attachmentsFileSize, Util.getPrettyFileSize(fileSize)),
|
||||
takeIf { it is ImageSlide }
|
||||
?.let(Slide::asAttachment)
|
||||
?.run { "${width}x$height" }
|
||||
?.let { TitledText(R.string.message_details_header__resolution, it) },
|
||||
attachmentDb.duration(this)?.let { TitledText(R.string.message_details_header__duration, it) },
|
||||
?.let { TitledText(R.string.attachmentsResolution, it) },
|
||||
attachmentDb.duration(this)?.let { TitledText(R.string.attachmentsDuration, it) },
|
||||
)
|
||||
|
||||
private fun AttachmentDatabase.duration(slide: Slide): String? =
|
||||
@ -157,7 +157,7 @@ data class MessageDetailsState(
|
||||
val sender: Recipient? = null,
|
||||
val thread: Recipient? = null,
|
||||
) {
|
||||
val fromTitle = GetString(R.string.message_details_header__from)
|
||||
val fromTitle = GetString(R.string.from)
|
||||
val canReply = record?.isOpenGroupInvitation != true
|
||||
}
|
||||
|
||||
|
@ -15,9 +15,10 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import com.squareup.phrase.Phrase
|
||||
import network.loki.messenger.R
|
||||
import network.loki.messenger.databinding.FragmentModalUrlBottomSheetBinding
|
||||
import org.thoughtcrime.securesms.util.UiModeUtilities
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.URL_KEY
|
||||
|
||||
class ModalUrlBottomSheet(private val url: String): BottomSheetDialogFragment(), View.OnClickListener {
|
||||
private lateinit var binding: FragmentModalUrlBottomSheetBinding
|
||||
@ -29,7 +30,9 @@ class ModalUrlBottomSheet(private val url: String): BottomSheetDialogFragment(),
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val explanation = resources.getString(R.string.dialog_open_url_explanation, url)
|
||||
val explanation = Phrase.from(context, R.string.urlOpenDescription)
|
||||
.put(URL_KEY, url)
|
||||
.format()
|
||||
val spannable = SpannableStringBuilder(explanation)
|
||||
val startIndex = explanation.indexOf(url)
|
||||
spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + url.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
@ -44,7 +47,7 @@ class ModalUrlBottomSheet(private val url: String): BottomSheetDialogFragment(),
|
||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
||||
requireContext().startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(context, R.string.invalid_url, Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(context, R.string.communityEnterUrlErrorInvalid, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
@ -53,7 +56,7 @@ class ModalUrlBottomSheet(private val url: String): BottomSheetDialogFragment(),
|
||||
val clip = ClipData.newPlainText("URL", url)
|
||||
val manager = requireContext().getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
||||
manager.setPrimaryClip(clip)
|
||||
Toast.makeText(requireContext(), R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(requireContext(), R.string.copied, Toast.LENGTH_SHORT).show()
|
||||
dismiss()
|
||||
}
|
||||
|
||||
|
@ -11,16 +11,18 @@ import android.widget.RelativeLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.children
|
||||
import androidx.core.view.isVisible
|
||||
import com.squareup.phrase.Phrase
|
||||
import network.loki.messenger.R
|
||||
import network.loki.messenger.databinding.AlbumThumbnailViewBinding
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.COUNT_KEY
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.MediaPreviewActivity
|
||||
import org.thoughtcrime.securesms.components.CornerMask
|
||||
import org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||
import com.bumptech.glide.RequestManager
|
||||
import org.thoughtcrime.securesms.mms.Slide
|
||||
import org.thoughtcrime.securesms.util.ActivityDispatcher
|
||||
|
||||
@ -80,7 +82,7 @@ class AlbumThumbnailView : RelativeLayout {
|
||||
slideSize = -1
|
||||
}
|
||||
|
||||
fun bind(glideRequests: GlideRequests, message: MmsMessageRecord,
|
||||
fun bind(glideRequests: RequestManager, message: MmsMessageRecord,
|
||||
isStart: Boolean, isEnd: Boolean) {
|
||||
slides = message.slideDeck.thumbnailSlides
|
||||
if (slides.isEmpty()) {
|
||||
@ -97,7 +99,10 @@ class AlbumThumbnailView : RelativeLayout {
|
||||
binding.albumCellContainer.findViewById<TextView>(R.id.album_cell_overflow_text)?.let { overflowText ->
|
||||
// overflowText will be null if !overflowed
|
||||
overflowText.isVisible = overflowed // more than max album size
|
||||
overflowText.text = context.getString(R.string.AlbumThumbnailView_plus, slides.size - MAX_ALBUM_DISPLAY_SIZE)
|
||||
val txt = Phrase.from(context, R.string.andMore)
|
||||
.put(COUNT_KEY, slides.size - MAX_ALBUM_DISPLAY_SIZE)
|
||||
.format()
|
||||
overflowText.text = txt
|
||||
}
|
||||
this.slideSize = slides.size
|
||||
}
|
||||
@ -110,10 +115,9 @@ class AlbumThumbnailView : RelativeLayout {
|
||||
|
||||
// endregion
|
||||
|
||||
|
||||
fun layoutRes(slideCount: Int) = when (slideCount) {
|
||||
1 -> R.layout.album_thumbnail_1 // single
|
||||
2 -> R.layout.album_thumbnail_2// two sidebyside
|
||||
2 -> R.layout.album_thumbnail_2 // two side-by-side
|
||||
else -> R.layout.album_thumbnail_3 // three stacked with additional text
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@ import android.widget.LinearLayout
|
||||
import androidx.core.view.isVisible
|
||||
import network.loki.messenger.databinding.ViewLinkPreviewDraftBinding
|
||||
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||
import com.bumptech.glide.RequestManager
|
||||
import org.thoughtcrime.securesms.mms.ImageSlide
|
||||
import org.thoughtcrime.securesms.util.toPx
|
||||
|
||||
@ -27,7 +27,7 @@ class LinkPreviewDraftView : LinearLayout {
|
||||
binding.linkPreviewDraftCancelButton.setOnClickListener { cancel() }
|
||||
}
|
||||
|
||||
fun update(glide: GlideRequests, linkPreview: LinkPreview) {
|
||||
fun update(glide: RequestManager, linkPreview: LinkPreview) {
|
||||
// Hide the loader and show the content view
|
||||
binding.linkPreviewDraftContainer.isVisible = true
|
||||
binding.linkPreviewDraftLoader.isVisible = false
|
||||
|
@ -9,13 +9,13 @@ import android.widget.BaseAdapter
|
||||
import android.widget.ListView
|
||||
import org.session.libsession.messaging.mentions.Mention
|
||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||
import com.bumptech.glide.RequestManager
|
||||
import org.thoughtcrime.securesms.util.toPx
|
||||
|
||||
class MentionCandidateSelectionView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : ListView(context, attrs, defStyleAttr) {
|
||||
private var mentionCandidates = listOf<Mention>()
|
||||
set(newValue) { field = newValue; mentionCandidateSelectionViewAdapter.mentionCandidates = newValue }
|
||||
var glide: GlideRequests? = null
|
||||
var glide: RequestManager? = null
|
||||
set(newValue) { field = newValue; mentionCandidateSelectionViewAdapter.glide = newValue }
|
||||
var openGroupServer: String? = null
|
||||
set(newValue) { field = newValue; mentionCandidateSelectionViewAdapter.openGroupServer = openGroupServer }
|
||||
@ -28,7 +28,7 @@ class MentionCandidateSelectionView(context: Context, attrs: AttributeSet?, defS
|
||||
private class Adapter(private val context: Context) : BaseAdapter() {
|
||||
var mentionCandidates = listOf<Mention>()
|
||||
set(newValue) { field = newValue; notifyDataSetChanged() }
|
||||
var glide: GlideRequests? = null
|
||||
var glide: RequestManager? = null
|
||||
var openGroupServer: String? = null
|
||||
var openGroupRoom: String? = null
|
||||
|
||||
|
@ -8,13 +8,13 @@ import android.widget.LinearLayout
|
||||
import network.loki.messenger.databinding.ViewMentionCandidateBinding
|
||||
import org.session.libsession.messaging.mentions.Mention
|
||||
import org.thoughtcrime.securesms.groups.OpenGroupManager
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||
import com.bumptech.glide.RequestManager
|
||||
|
||||
class MentionCandidateView : LinearLayout {
|
||||
private lateinit var binding: ViewMentionCandidateBinding
|
||||
var mentionCandidate = Mention("", "")
|
||||
set(newValue) { field = newValue; update() }
|
||||
var glide: GlideRequests? = null
|
||||
var glide: RequestManager? = null
|
||||
var openGroupServer: String? = null
|
||||
var openGroupRoom: String? = null
|
||||
|
||||
|
@ -8,9 +8,11 @@ import android.text.Spannable
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.StyleSpan
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.squareup.phrase.Phrase
|
||||
import network.loki.messenger.R
|
||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||
import org.session.libsession.messaging.contacts.Contact
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.COMMUNITY_NAME_KEY
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.createSessionDialog
|
||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||
@ -24,14 +26,14 @@ class BlockedDialog(private val recipient: Recipient, private val context: Conte
|
||||
val contact = contactDB.getContactWithAccountID(accountID)
|
||||
val name = contact?.displayName(Contact.ContactContext.REGULAR) ?: accountID
|
||||
|
||||
val explanation = resources.getString(R.string.dialog_blocked_explanation, name)
|
||||
val explanation = Phrase.from(context, R.string.communityJoinDescription).put(COMMUNITY_NAME_KEY, name).format()
|
||||
val spannable = SpannableStringBuilder(explanation)
|
||||
val startIndex = explanation.indexOf(name)
|
||||
spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + name.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
|
||||
title(resources.getString(R.string.dialog_blocked_title, name))
|
||||
title(resources.getString(R.string.blockUnblock))
|
||||
text(spannable)
|
||||
button(R.string.ConversationActivity_unblock) { unblock() }
|
||||
button(R.string.blockUnblock) { unblock() }
|
||||
cancelButton { dismiss() }
|
||||
}
|
||||
|
||||
|
@ -7,12 +7,14 @@ import android.text.Spannable
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.StyleSpan
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.squareup.phrase.Phrase
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import network.loki.messenger.R
|
||||
import org.session.libsession.messaging.contacts.Contact
|
||||
import org.session.libsession.messaging.jobs.AttachmentDownloadJob
|
||||
import org.session.libsession.messaging.jobs.JobQueue
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.CONVERSATION_NAME_KEY
|
||||
import org.thoughtcrime.securesms.createSessionDialog
|
||||
import org.thoughtcrime.securesms.database.SessionContactDatabase
|
||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||
@ -29,15 +31,19 @@ class DownloadDialog(private val recipient: Recipient) : DialogFragment() {
|
||||
val accountID = recipient.address.toString()
|
||||
val contact = contactDB.getContactWithAccountID(accountID)
|
||||
val name = contact?.displayName(Contact.ContactContext.REGULAR) ?: accountID
|
||||
title(resources.getString(R.string.dialog_download_title, name))
|
||||
|
||||
val explanation = resources.getString(R.string.dialog_download_explanation, name)
|
||||
title(getString(R.string.attachmentsAutoDownloadModalTitle))
|
||||
|
||||
val explanation = Phrase.from(context, R.string.attachmentsAutoDownloadModalDescription)
|
||||
.put(CONVERSATION_NAME_KEY, recipient.name)
|
||||
.format()
|
||||
val spannable = SpannableStringBuilder(explanation)
|
||||
|
||||
val startIndex = explanation.indexOf(name)
|
||||
spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + name.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
text(spannable)
|
||||
|
||||
button(R.string.dialog_download_button_title, R.string.AccessibilityId_download_media) { trust() }
|
||||
button(R.string.download, R.string.AccessibilityId_download_media) { trust() }
|
||||
cancelButton { dismiss() }
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.thoughtcrime.securesms.conversation.v2.dialogs
|
||||
|
||||
import org.thoughtcrime.securesms.createSessionDialog
|
||||
import android.app.Dialog
|
||||
import android.graphics.Typeface
|
||||
import android.os.Bundle
|
||||
@ -8,11 +9,13 @@ import android.text.SpannableStringBuilder
|
||||
import android.text.style.StyleSpan
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.squareup.phrase.Phrase
|
||||
import network.loki.messenger.R
|
||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||
import org.session.libsession.utilities.OpenGroupUrlParser
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.COMMUNITY_NAME_KEY
|
||||
import org.session.libsignal.utilities.Log
|
||||
import org.session.libsignal.utilities.ThreadUtils
|
||||
import org.thoughtcrime.securesms.createSessionDialog
|
||||
import org.thoughtcrime.securesms.groups.OpenGroupManager
|
||||
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
|
||||
|
||||
@ -20,14 +23,18 @@ import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
|
||||
class JoinOpenGroupDialog(private val name: String, private val url: String) : DialogFragment() {
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog {
|
||||
title(resources.getString(R.string.dialog_join_open_group_title, name))
|
||||
val explanation = resources.getString(R.string.dialog_join_open_group_explanation, name)
|
||||
title(resources.getString(R.string.communityJoin))
|
||||
val explanation = Phrase.from(context, R.string.communityJoinDescription).put(COMMUNITY_NAME_KEY, name).format()
|
||||
val spannable = SpannableStringBuilder(explanation)
|
||||
val startIndex = explanation.indexOf(name)
|
||||
var startIndex = explanation.indexOf(name)
|
||||
if (startIndex < 0) {
|
||||
Log.w("JoinOpenGroupDialog", "Could not find $name in explanation dialog: $explanation")
|
||||
startIndex = 0 // Limit the startIndex to zero if not found (will be -1) to prevent a crash
|
||||
}
|
||||
spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + name.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
text(spannable)
|
||||
cancelButton { dismiss() }
|
||||
button(R.string.open_group_invitation_view__join_accessibility_description) { join() }
|
||||
button(R.string.join) { join() }
|
||||
}
|
||||
|
||||
private fun join() {
|
||||
@ -39,7 +46,7 @@ class JoinOpenGroupDialog(private val name: String, private val url: String) : D
|
||||
MessagingModuleConfiguration.shared.storage.onOpenGroupAdded(openGroup.server, openGroup.room)
|
||||
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(activity)
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(activity, R.string.activity_join_public_chat_error, Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(activity, R.string.communityErrorDescription, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
dismiss()
|
||||
|
@ -3,7 +3,9 @@ package org.thoughtcrime.securesms.conversation.v2.dialogs
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.squareup.phrase.Phrase
|
||||
import network.loki.messenger.R
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.APP_NAME_KEY
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.thoughtcrime.securesms.createSessionDialog
|
||||
|
||||
@ -12,9 +14,12 @@ import org.thoughtcrime.securesms.createSessionDialog
|
||||
class LinkPreviewDialog(private val onEnabled: () -> Unit) : DialogFragment() {
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog {
|
||||
title(R.string.dialog_link_preview_title)
|
||||
text(R.string.dialog_link_preview_explanation)
|
||||
button(R.string.dialog_link_preview_enable_button_title) { enable() }
|
||||
title(R.string.linkPreviewsEnable)
|
||||
val txt = Phrase.from(context, R.string.linkPreviewsFirstDescription)
|
||||
.put(APP_NAME_KEY, context.getString(R.string.app_name))
|
||||
.format()
|
||||
text(txt)
|
||||
button(R.string.enable) { enable() }
|
||||
cancelButton { dismiss() }
|
||||
}
|
||||
|
||||
|
@ -10,9 +10,9 @@ import org.thoughtcrime.securesms.createSessionDialog
|
||||
class SendSeedDialog(private val proceed: (() -> Unit)? = null) : DialogFragment() {
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog {
|
||||
title(R.string.dialog_send_seed_title)
|
||||
text(R.string.dialog_send_seed_explanation)
|
||||
button(R.string.dialog_send_seed_send_button_title) { send() }
|
||||
title(R.string.warning)
|
||||
text(R.string.recoveryPasswordWarningSendDescription)
|
||||
button(R.string.send) { send() }
|
||||
cancelButton()
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ import org.thoughtcrime.securesms.conversation.v2.messages.QuoteView
|
||||
import org.thoughtcrime.securesms.conversation.v2.messages.QuoteViewDelegate
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||
import com.bumptech.glide.RequestManager
|
||||
import org.thoughtcrime.securesms.util.addTextChangedListener
|
||||
import org.thoughtcrime.securesms.util.contains
|
||||
|
||||
@ -188,7 +188,7 @@ class InputBar @JvmOverloads constructor(
|
||||
|
||||
private fun startRecordingVoiceMessage() { delegate?.startRecordingVoiceMessage() }
|
||||
|
||||
fun draftQuote(thread: Recipient, message: MessageRecord, glide: GlideRequests) {
|
||||
fun draftQuote(thread: Recipient, message: MessageRecord, glide: RequestManager) {
|
||||
quoteView?.let(binding.inputBarAdditionalContentContainer::removeView)
|
||||
|
||||
quote = message
|
||||
@ -238,7 +238,7 @@ class InputBar @JvmOverloads constructor(
|
||||
requestLayout()
|
||||
}
|
||||
|
||||
fun updateLinkPreviewDraft(glide: GlideRequests, updatedLinkPreview: LinkPreview) {
|
||||
fun updateLinkPreviewDraft(glide: RequestManager, updatedLinkPreview: LinkPreview) {
|
||||
// Update our `linkPreview` property with the new (provided as an argument to this function)
|
||||
// then update the View from that.
|
||||
linkPreview = updatedLinkPreview.also { linkPreviewDraftView?.update(glide, it) }
|
||||
|
@ -19,7 +19,6 @@ import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import network.loki.messenger.R
|
||||
import network.loki.messenger.databinding.ViewInputBarRecordingBinding
|
||||
import org.thoughtcrime.securesms.util.DateUtils
|
||||
import org.thoughtcrime.securesms.util.animateSizeChange
|
||||
import org.thoughtcrime.securesms.util.disableClipping
|
||||
import org.thoughtcrime.securesms.util.toPx
|
||||
@ -106,8 +105,7 @@ class InputBarRecordingView : RelativeLayout {
|
||||
timerJob = scope.launch {
|
||||
while (isActive) {
|
||||
val duration = (Date().time - startTimestamp) / 1000L
|
||||
binding.recordingViewDurationTextView.text = DateUtils.formatElapsedTime(duration)
|
||||
|
||||
binding.recordingViewDurationTextView.text = android.text.format.DateUtils.formatElapsedTime(duration)
|
||||
delay(500)
|
||||
}
|
||||
}
|
||||
|
@ -16,10 +16,13 @@ import androidx.appcompat.widget.SearchView.OnQueryTextListener
|
||||
import androidx.core.content.pm.ShortcutInfoCompat
|
||||
import androidx.core.content.pm.ShortcutManagerCompat
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import com.squareup.phrase.Phrase
|
||||
import java.io.IOException
|
||||
import network.loki.messenger.R
|
||||
import org.session.libsession.messaging.sending_receiving.MessageSender
|
||||
import org.session.libsession.messaging.sending_receiving.leave
|
||||
import org.session.libsession.utilities.GroupUtil.doubleDecodeGroupID
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.GROUP_NAME_KEY
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.session.libsignal.utilities.guava.Optional
|
||||
@ -38,7 +41,6 @@ import org.thoughtcrime.securesms.service.WebRtcCallService
|
||||
import org.thoughtcrime.securesms.showMuteDialog
|
||||
import org.thoughtcrime.securesms.showSessionDialog
|
||||
import org.thoughtcrime.securesms.util.BitmapUtil
|
||||
import java.io.IOException
|
||||
|
||||
object ConversationMenuHelper {
|
||||
|
||||
@ -164,9 +166,9 @@ object ConversationMenuHelper {
|
||||
|
||||
if (!TextSecurePreferences.isCallNotificationsEnabled(context)) {
|
||||
context.showSessionDialog {
|
||||
title(R.string.ConversationActivity_call_title)
|
||||
text(R.string.ConversationActivity_call_prompt)
|
||||
button(R.string.activity_settings_title, R.string.AccessibilityId_settings) {
|
||||
title(R.string.callsPermissionsRequired)
|
||||
text(R.string.callsPermissionsRequiredDescription)
|
||||
button(R.string.sessionSettings, R.string.AccessibilityId_settings) {
|
||||
Intent(context, PrivacySettingsActivity::class.java).let(context::startActivity)
|
||||
}
|
||||
cancelButton()
|
||||
@ -217,7 +219,7 @@ object ConversationMenuHelper {
|
||||
.setIntent(ShortcutLauncherActivity.createIntent(context, thread.address))
|
||||
.build()
|
||||
if (ShortcutManagerCompat.requestPinShortcut(context, shortcutInfo, null)) {
|
||||
Toast.makeText(context, context.resources.getString(R.string.ConversationActivity_added_to_home_screen), Toast.LENGTH_LONG).show()
|
||||
Toast.makeText(context, context.resources.getString(R.string.conversationsAddedToHome), Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}.execute()
|
||||
@ -274,15 +276,24 @@ object ConversationMenuHelper {
|
||||
val accountID = TextSecurePreferences.getLocalNumber(context)
|
||||
val isCurrentUserAdmin = admins.any { it.toString() == accountID }
|
||||
val message = if (isCurrentUserAdmin) {
|
||||
"Because you are the creator of this group it will be deleted for everyone. This cannot be undone."
|
||||
Phrase.from(context, R.string.groupLeaveDescriptionAdmin)
|
||||
.put(GROUP_NAME_KEY, group.title)
|
||||
.format().toString()
|
||||
} else {
|
||||
context.resources.getString(R.string.ConversationActivity_are_you_sure_you_want_to_leave_this_group)
|
||||
Phrase.from(context, R.string.groupLeaveDescription)
|
||||
.put(GROUP_NAME_KEY, group.title)
|
||||
.format().toString()
|
||||
}
|
||||
|
||||
fun onLeaveFailed() = Toast.makeText(context, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show()
|
||||
fun onLeaveFailed() {
|
||||
val txt = Phrase.from(context, R.string.groupLeaveErrorFailed)
|
||||
.put(GROUP_NAME_KEY, group.title)
|
||||
.format().toString()
|
||||
Toast.makeText(context, txt, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
context.showSessionDialog {
|
||||
title(R.string.ConversationActivity_leave_group)
|
||||
title(R.string.groupLeave)
|
||||
text(message)
|
||||
button(R.string.yes) {
|
||||
try {
|
||||
|
@ -8,17 +8,19 @@ import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.squareup.phrase.Phrase
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
import network.loki.messenger.R
|
||||
import network.loki.messenger.databinding.ViewControlMessageBinding
|
||||
import network.loki.messenger.libsession_util.util.ExpiryMode
|
||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||
import org.session.libsession.messaging.messages.ExpirationConfiguration
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.NAME_KEY
|
||||
import org.thoughtcrime.securesms.conversation.disappearingmessages.DisappearingMessages
|
||||
import org.thoughtcrime.securesms.conversation.disappearingmessages.expiryMode
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ControlMessageView : LinearLayout {
|
||||
@ -75,8 +77,10 @@ class ControlMessageView : LinearLayout {
|
||||
}
|
||||
}
|
||||
message.isMessageRequestResponse -> {
|
||||
binding.textView.text = context.getString(R.string.message_requests_accepted)
|
||||
binding.root.contentDescription=context.getString(R.string.AccessibilityId_message_request_config_message)
|
||||
binding.textView.text = context.getString(R.string.messageRequestsAccepted)
|
||||
binding.root.contentDescription = Phrase.from(context, R.string.messageRequestYouHaveAccepted)
|
||||
.put(NAME_KEY, message.individualRecipient.name)
|
||||
.format()
|
||||
}
|
||||
message.isCallLog -> {
|
||||
val drawable = when {
|
||||
|
@ -21,7 +21,7 @@ class DeletedMessageView : LinearLayout {
|
||||
// region Updating
|
||||
fun bind(message: MessageRecord, @ColorInt textColor: Int) {
|
||||
assert(message.isDeleted)
|
||||
binding.deleteTitleTextView.text = context.getString(R.string.deleted_message)
|
||||
binding.deleteTitleTextView.text = context.getString(R.string.deleteMessageDeleted)
|
||||
binding.deleteTitleTextView.setTextColor(textColor)
|
||||
binding.deletedMessageViewIconImageView.imageTintList = ColorStateList.valueOf(textColor)
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ import org.thoughtcrime.securesms.components.CornerMask
|
||||
import org.thoughtcrime.securesms.conversation.v2.ModalUrlBottomSheet
|
||||
import org.thoughtcrime.securesms.conversation.v2.utilities.MessageBubbleUtilities
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||
import com.bumptech.glide.RequestManager
|
||||
import org.thoughtcrime.securesms.mms.ImageSlide
|
||||
|
||||
class LinkPreviewView : LinearLayout {
|
||||
@ -32,7 +32,7 @@ class LinkPreviewView : LinearLayout {
|
||||
// region Updating
|
||||
fun bind(
|
||||
message: MmsMessageRecord,
|
||||
glide: GlideRequests,
|
||||
glide: RequestManager,
|
||||
isStartOfMessageCluster: Boolean,
|
||||
isEndOfMessageCluster: Boolean
|
||||
) {
|
||||
|
@ -18,7 +18,7 @@ import org.session.libsession.utilities.getColorFromAttr
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.conversation.v2.utilities.MentionUtilities
|
||||
import org.thoughtcrime.securesms.database.SessionContactDatabase
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||
import com.bumptech.glide.RequestManager
|
||||
import org.thoughtcrime.securesms.mms.SlideDeck
|
||||
import org.thoughtcrime.securesms.util.MediaUtil
|
||||
import org.thoughtcrime.securesms.util.getAccentColor
|
||||
@ -68,20 +68,20 @@ class QuoteView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
||||
// region Updating
|
||||
fun bind(authorPublicKey: String, body: String?, attachments: SlideDeck?, thread: Recipient,
|
||||
isOutgoingMessage: Boolean, isOpenGroupInvitation: Boolean, threadID: Long,
|
||||
isOriginalMissing: Boolean, glide: GlideRequests) {
|
||||
isOriginalMissing: Boolean, glide: RequestManager) {
|
||||
// Author
|
||||
val author = contactDb.getContactWithAccountID(authorPublicKey)
|
||||
val localNumber = TextSecurePreferences.getLocalNumber(context)
|
||||
val quoteIsLocalUser = localNumber != null && authorPublicKey == localNumber
|
||||
|
||||
val authorDisplayName =
|
||||
if (quoteIsLocalUser) context.getString(R.string.QuoteView_you)
|
||||
if (quoteIsLocalUser) context.getString(R.string.you)
|
||||
else author?.displayName(Contact.contextForRecipient(thread)) ?: "${authorPublicKey.take(4)}...${authorPublicKey.takeLast(4)}"
|
||||
binding.quoteViewAuthorTextView.text = authorDisplayName
|
||||
binding.quoteViewAuthorTextView.setTextColor(getTextColor(isOutgoingMessage))
|
||||
// Body
|
||||
binding.quoteViewBodyTextView.text = if (isOpenGroupInvitation)
|
||||
resources.getString(R.string.open_group_invitation_view__open_group_invitation)
|
||||
resources.getString(R.string.communityInvitation)
|
||||
else MentionUtilities.highlightMentions(
|
||||
text = (body ?: "").toSpannable(),
|
||||
isOutgoingMessage = isOutgoingMessage,
|
||||
@ -106,7 +106,7 @@ class QuoteView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
||||
attachments.audioSlide != null -> {
|
||||
binding.quoteViewAttachmentPreviewImageView.setImageResource(R.drawable.ic_microphone)
|
||||
binding.quoteViewAttachmentPreviewImageView.isVisible = true
|
||||
binding.quoteViewBodyTextView.text = resources.getString(R.string.Slide_audio)
|
||||
binding.quoteViewBodyTextView.text = resources.getString(R.string.audio)
|
||||
}
|
||||
attachments.documentSlide != null -> {
|
||||
binding.quoteViewAttachmentPreviewImageView.setImageResource(R.drawable.ic_document_large_light)
|
||||
@ -120,7 +120,7 @@ class QuoteView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
||||
.root.setRoundedCorners(toPx(4, resources))
|
||||
binding.quoteViewAttachmentThumbnailImageView.root.setImageResource(glide, slide, false)
|
||||
binding.quoteViewAttachmentThumbnailImageView.root.isVisible = true
|
||||
binding.quoteViewBodyTextView.text = if (MediaUtil.isVideo(slide.asAttachment())) resources.getString(R.string.Slide_video) else resources.getString(R.string.Slide_image)
|
||||
binding.quoteViewBodyTextView.text = if (MediaUtil.isVideo(slide.asAttachment())) resources.getString(R.string.video) else resources.getString(R.string.image)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,12 +5,13 @@ import android.util.AttributeSet
|
||||
import android.widget.LinearLayout
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.squareup.phrase.Phrase
|
||||
import network.loki.messenger.R
|
||||
import network.loki.messenger.databinding.ViewUntrustedAttachmentBinding
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.FILE_TYPE_KEY
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.conversation.v2.dialogs.DownloadDialog
|
||||
import org.thoughtcrime.securesms.util.ActivityDispatcher
|
||||
import java.util.Locale
|
||||
|
||||
class UntrustedAttachmentView: LinearLayout {
|
||||
private val binding: ViewUntrustedAttachmentBinding by lazy { ViewUntrustedAttachmentBinding.bind(this) }
|
||||
@ -30,13 +31,17 @@ class UntrustedAttachmentView: LinearLayout {
|
||||
// region Updating
|
||||
fun bind(attachmentType: AttachmentType, @ColorInt textColor: Int) {
|
||||
val (iconRes, stringRes) = when (attachmentType) {
|
||||
AttachmentType.AUDIO -> R.drawable.ic_microphone to R.string.Slide_audio
|
||||
AttachmentType.DOCUMENT -> R.drawable.ic_document_large_light to R.string.document
|
||||
AttachmentType.AUDIO -> R.drawable.ic_microphone to R.string.audio
|
||||
AttachmentType.DOCUMENT -> R.drawable.ic_document_large_light to R.string.files
|
||||
AttachmentType.MEDIA -> R.drawable.ic_image_white_24dp to R.string.media
|
||||
}
|
||||
val iconDrawable = ContextCompat.getDrawable(context,iconRes)!!
|
||||
iconDrawable.mutate().setTint(textColor)
|
||||
val text = context.getString(R.string.UntrustedAttachmentView_download_attachment, context.getString(stringRes).toLowerCase(Locale.ROOT))
|
||||
|
||||
val text = Phrase.from(context, R.string.attachmentsTapToDownload)
|
||||
.put(FILE_TYPE_KEY, stringRes)
|
||||
.format()
|
||||
binding.untrustedAttachmentTitle.text = text
|
||||
|
||||
binding.untrustedAttachmentIcon.setImageDrawable(iconDrawable)
|
||||
binding.untrustedAttachmentTitle.text = text
|
||||
|
@ -38,8 +38,8 @@ import org.thoughtcrime.securesms.conversation.v2.utilities.TextUtilities.getInt
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
||||
import org.thoughtcrime.securesms.database.model.SmsMessageRecord
|
||||
import org.thoughtcrime.securesms.mms.GlideApp
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.RequestManager
|
||||
import org.thoughtcrime.securesms.util.GlowViewUtilities
|
||||
import org.thoughtcrime.securesms.util.SearchUtil
|
||||
import org.thoughtcrime.securesms.util.getAccentColor
|
||||
@ -63,7 +63,7 @@ class VisibleMessageContentView : ConstraintLayout {
|
||||
message: MessageRecord,
|
||||
isStartOfMessageCluster: Boolean = true,
|
||||
isEndOfMessageCluster: Boolean = true,
|
||||
glide: GlideRequests = GlideApp.with(this),
|
||||
glide: RequestManager = Glide.with(this),
|
||||
thread: Recipient,
|
||||
searchQuery: String? = null,
|
||||
contactIsTrusted: Boolean = true,
|
||||
@ -117,7 +117,7 @@ class VisibleMessageContentView : ConstraintLayout {
|
||||
binding.quoteView.root.isVisible = true
|
||||
val quote = message.quote!!
|
||||
val quoteText = if (quote.isOriginalMissing) {
|
||||
context.getString(R.string.QuoteView_original_missing)
|
||||
context.getString(R.string.messageErrorOriginal)
|
||||
} else {
|
||||
quote.text
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.LinearLayout
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
@ -27,6 +26,13 @@ import androidx.core.os.bundleOf
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.marginBottom
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.math.sqrt
|
||||
import network.loki.messenger.R
|
||||
import network.loki.messenger.databinding.ViewEmojiReactionsBinding
|
||||
import network.loki.messenger.databinding.ViewVisibleMessageBinding
|
||||
@ -52,19 +58,12 @@ import org.thoughtcrime.securesms.database.ThreadDatabase
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||
import org.thoughtcrime.securesms.groups.OpenGroupManager
|
||||
import org.thoughtcrime.securesms.home.UserDetailsBottomSheet
|
||||
import org.thoughtcrime.securesms.mms.GlideApp
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.RequestManager
|
||||
import org.thoughtcrime.securesms.util.DateUtils
|
||||
import org.thoughtcrime.securesms.util.disableClipping
|
||||
import org.thoughtcrime.securesms.util.toDp
|
||||
import org.thoughtcrime.securesms.util.toPx
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.math.sqrt
|
||||
|
||||
private const val TAG = "VisibleMessageView"
|
||||
|
||||
@ -141,7 +140,7 @@ class VisibleMessageView : FrameLayout {
|
||||
message: MessageRecord,
|
||||
previous: MessageRecord? = null,
|
||||
next: MessageRecord? = null,
|
||||
glide: GlideRequests = GlideApp.with(this),
|
||||
glide: RequestManager = Glide.with(this),
|
||||
searchQuery: String? = null,
|
||||
contact: Contact? = null,
|
||||
senderAccountID: String,
|
||||
@ -384,37 +383,37 @@ class VisibleMessageView : FrameLayout {
|
||||
message.isFailed ->
|
||||
MessageStatusInfo(R.drawable.ic_delivery_status_failed,
|
||||
getThemedColor(context, R.attr.danger),
|
||||
R.string.delivery_status_failed
|
||||
R.string.messageStatusFailedToSend
|
||||
)
|
||||
message.isSyncFailed ->
|
||||
MessageStatusInfo(
|
||||
R.drawable.ic_delivery_status_failed,
|
||||
context.getColor(R.color.accent_orange),
|
||||
R.string.delivery_status_sync_failed
|
||||
R.string.messageStatusFailedToSync
|
||||
)
|
||||
message.isPending ->
|
||||
MessageStatusInfo(
|
||||
R.drawable.ic_delivery_status_sending,
|
||||
context.getColorFromAttr(R.attr.message_status_color),
|
||||
R.string.delivery_status_sending
|
||||
R.string.sending
|
||||
)
|
||||
message.isSyncing || message.isResyncing ->
|
||||
MessageStatusInfo(
|
||||
R.drawable.ic_delivery_status_sending,
|
||||
context.getColorFromAttr(R.attr.message_status_color),
|
||||
R.string.delivery_status_sending // We COULD tell the user that we're `syncing` (R.string.delivery_status_syncing) but it will likely make more sense to them if we say "Sending"
|
||||
R.string.messageStatusSyncing
|
||||
)
|
||||
message.isRead || message.isIncoming ->
|
||||
MessageStatusInfo(
|
||||
R.drawable.ic_delivery_status_read,
|
||||
context.getColorFromAttr(R.attr.message_status_color),
|
||||
R.string.delivery_status_read
|
||||
R.string.read
|
||||
)
|
||||
message.isSent ->
|
||||
MessageStatusInfo(
|
||||
R.drawable.ic_delivery_status_sent,
|
||||
context.getColorFromAttr(R.attr.message_status_color),
|
||||
R.string.delivery_status_sent
|
||||
R.string.disappearingMessagesSent
|
||||
)
|
||||
else -> {
|
||||
// The message isn't one we care about for message statuses we display to the user (i.e.,
|
||||
|
@ -5,9 +5,11 @@ import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import com.squareup.phrase.Phrase
|
||||
import network.loki.messenger.R
|
||||
import network.loki.messenger.databinding.ViewSearchBottomBarBinding
|
||||
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.COUNT_KEY
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.TOTAL_COUNT_KEY
|
||||
|
||||
class SearchBottomBar : LinearLayout {
|
||||
private lateinit var binding: ViewSearchBottomBarBinding
|
||||
@ -35,7 +37,10 @@ class SearchBottomBar : LinearLayout {
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
searchPosition.text = resources.getString(R.string.ConversationActivity_search_position, position + 1, count)
|
||||
searchPosition.text = Phrase.from(context, R.string.searchMatches)
|
||||
.put(COUNT_KEY, position + 1)
|
||||
.put(TOTAL_COUNT_KEY, count)
|
||||
.format()
|
||||
} else {
|
||||
searchPosition.text = ""
|
||||
}
|
||||
@ -43,6 +48,44 @@ class SearchBottomBar : LinearLayout {
|
||||
setViewEnabled(searchDown, position > 0)
|
||||
}
|
||||
|
||||
/*
|
||||
fun setData(position: Int, count: Int, query: String?) = with(binding) {
|
||||
searchProgressWheel.visibility = GONE
|
||||
searchUp.setOnClickListener { v: View? ->
|
||||
if (eventListener != null) {
|
||||
eventListener!!.onSearchMoveUpPressed()
|
||||
}
|
||||
}
|
||||
searchDown.setOnClickListener { v: View? ->
|
||||
if (eventListener != null) {
|
||||
eventListener!!.onSearchMoveDownPressed()
|
||||
}
|
||||
}
|
||||
|
||||
// If we found search results list how many we found
|
||||
if (count > 0) {
|
||||
searchPosition.text = Phrase.from(context, R.string.searchMatches)
|
||||
.put(COUNT_KEY, position + 1)
|
||||
.put(TOTAL_COUNT_KEY, count)
|
||||
.format()
|
||||
} else {
|
||||
// If there are no results we don't display anything if the query is
|
||||
// empty, but we'll substitute "No results found for <query>" otherwise.
|
||||
var txt = ""
|
||||
if (query != null) {
|
||||
if (query.isNotEmpty()) {
|
||||
txt = Phrase.from(context, R.string.searchMatchesNoneSpecific)
|
||||
.put(QUERY_KEY, query)
|
||||
.format().toString()
|
||||
}
|
||||
}
|
||||
searchPosition.text = txt
|
||||
}
|
||||
setViewEnabled(searchUp, position < count - 1)
|
||||
setViewEnabled(searchDown, position > 0)
|
||||
}
|
||||
*/
|
||||
|
||||
fun showLoading() {
|
||||
binding.searchProgressWheel.visibility = VISIBLE
|
||||
}
|
||||
|
@ -94,6 +94,8 @@ class SearchViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
public fun getActiveQuery() = activeQuery
|
||||
|
||||
class SearchResult(private val results: CursorList<MessageResult?>, val position: Int) : Closeable {
|
||||
|
||||
fun getResults(): List<MessageResult?> {
|
||||
|
@ -16,6 +16,8 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms.conversation.v2.utilities;
|
||||
|
||||
import static org.session.libsession.utilities.StringSubstitutionConstants.APP_NAME_KEY;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
@ -30,10 +32,14 @@ import android.provider.OpenableColumns;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Pair;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.squareup.phrase.Phrase;
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import network.loki.messenger.R;
|
||||
import org.session.libsession.utilities.recipients.Recipient;
|
||||
import org.session.libsignal.utilities.ListenableFuture;
|
||||
import org.session.libsignal.utilities.Log;
|
||||
@ -44,7 +50,7 @@ import org.thoughtcrime.securesms.mediasend.MediaSendActivity;
|
||||
import org.thoughtcrime.securesms.mms.AudioSlide;
|
||||
import org.thoughtcrime.securesms.mms.DocumentSlide;
|
||||
import org.thoughtcrime.securesms.mms.GifSlide;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import com.bumptech.glide.RequestManager;
|
||||
import org.thoughtcrime.securesms.mms.ImageSlide;
|
||||
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||
@ -55,13 +61,6 @@ import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.providers.BlobProvider;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
|
||||
public class AttachmentManager {
|
||||
|
||||
private final static String TAG = AttachmentManager.class.getSimpleName();
|
||||
@ -126,7 +125,7 @@ public class AttachmentManager {
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
public ListenableFuture<Boolean> setMedia(@NonNull final GlideRequests glideRequests,
|
||||
public ListenableFuture<Boolean> setMedia(@NonNull final RequestManager glideRequests,
|
||||
@NonNull final Uri uri,
|
||||
@NonNull final MediaType mediaType,
|
||||
@NonNull final MediaConstraints constraints,
|
||||
@ -252,13 +251,31 @@ public class AttachmentManager {
|
||||
} else {
|
||||
builder = builder.request(Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||
}
|
||||
builder.withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_the_external_storage_permission_in_order_to_attach_photos_videos_or_audio))
|
||||
.withRationaleDialog(activity.getString(R.string.ConversationActivity_to_send_photos_and_video_allow_signal_access_to_storage), R.drawable.ic_baseline_photo_library_24)
|
||||
|
||||
Context c = activity.getApplicationContext();
|
||||
String needStoragePermissionTxt = Phrase.from(c, R.string.permissionsStorageSend)
|
||||
.put(APP_NAME_KEY, c.getString(R.string.app_name))
|
||||
.format().toString();
|
||||
String storagePermissionDeniedTxt = Phrase.from(c, R.string.cameraGrantAccessStorageDenied)
|
||||
.put(APP_NAME_KEY, c.getString(R.string.app_name))
|
||||
.format().toString();
|
||||
|
||||
builder.withPermanentDenialDialog(needStoragePermissionTxt)
|
||||
.withRationaleDialog(storagePermissionDeniedTxt, R.drawable.ic_baseline_photo_library_24)
|
||||
.onAllGranted(() -> selectMediaType(activity, "*/*", null, requestCode)) // Note: We can use startActivityForResult w/ the ACTION_OPEN_DOCUMENT or ACTION_OPEN_DOCUMENT_TREE intent if we need to modernise this.
|
||||
.execute();
|
||||
}
|
||||
|
||||
public static void selectGallery(Activity activity, int requestCode, @NonNull Recipient recipient, @NonNull String body) {
|
||||
|
||||
Context c = activity.getApplicationContext();
|
||||
String needStoragePermissionTxt = Phrase.from(c, R.string.permissionsStorageSend)
|
||||
.put(APP_NAME_KEY, c.getString(R.string.app_name))
|
||||
.format().toString();
|
||||
String cameraPermissionDeniedTxt = Phrase.from(c, R.string.cameraGrantAccessDenied)
|
||||
.put(APP_NAME_KEY, c.getString(R.string.app_name))
|
||||
.format().toString();
|
||||
|
||||
Permissions.PermissionsBuilder builder = Permissions.with(activity);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
builder = builder.request(Manifest.permission.READ_MEDIA_VIDEO)
|
||||
@ -266,8 +283,8 @@ public class AttachmentManager {
|
||||
} else {
|
||||
builder = builder.request(Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||
}
|
||||
builder.withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_the_external_storage_permission_in_order_to_attach_photos_videos_or_audio))
|
||||
.withRationaleDialog(activity.getString(R.string.ConversationActivity_to_send_photos_and_video_allow_signal_access_to_storage), R.drawable.ic_baseline_photo_library_24)
|
||||
builder.withPermanentDenialDialog(cameraPermissionDeniedTxt)
|
||||
.withRationaleDialog(needStoragePermissionTxt, R.drawable.ic_baseline_photo_library_24)
|
||||
.onAllGranted(() -> activity.startActivityForResult(MediaSendActivity.buildGalleryIntent(activity, recipient, body), requestCode))
|
||||
.execute();
|
||||
}
|
||||
@ -291,10 +308,19 @@ public class AttachmentManager {
|
||||
}
|
||||
|
||||
public void capturePhoto(Activity activity, int requestCode, Recipient recipient) {
|
||||
|
||||
String cameraPermissionDeniedTxt = Phrase.from(context, R.string.cameraGrantAccessDenied)
|
||||
.put(APP_NAME_KEY, context.getString(R.string.app_name))
|
||||
.format().toString();
|
||||
|
||||
String requireCameraPermissionTxt = Phrase.from(context, R.string.cameraGrantAccessDescription)
|
||||
.put(APP_NAME_KEY, context.getString(R.string.app_name))
|
||||
.format().toString();
|
||||
|
||||
Permissions.with(activity)
|
||||
.request(Manifest.permission.CAMERA)
|
||||
.withPermanentDenialDialog(activity.getString(R.string.AttachmentManager_signal_requires_the_camera_permission_in_order_to_take_photos_but_it_has_been_permanently_denied))
|
||||
.withRationaleDialog(activity.getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera),R.drawable.ic_baseline_photo_camera_24)
|
||||
.withPermanentDenialDialog(cameraPermissionDeniedTxt)
|
||||
.withRationaleDialog(requireCameraPermissionTxt, R.drawable.ic_baseline_photo_camera_24)
|
||||
.onAllGranted(() -> {
|
||||
Intent captureIntent = MediaSendActivity.buildCameraIntent(activity, recipient);
|
||||
if (captureIntent.resolveActivity(activity.getPackageManager()) != null) {
|
||||
@ -326,7 +352,7 @@ public class AttachmentManager {
|
||||
activity.startActivityForResult(intent, requestCode);
|
||||
} catch (ActivityNotFoundException anfe) {
|
||||
Log.w(TAG, "couldn't complete ACTION_GET_CONTENT intent, no activity found. falling back.");
|
||||
Toast.makeText(activity, R.string.AttachmentManager_cant_open_media_selection, Toast.LENGTH_LONG).show();
|
||||
Toast.makeText(activity, R.string.attachmentsErrorNoApp, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,13 +54,14 @@ object MentionUtilities {
|
||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)!!
|
||||
val openGroup by lazy { DatabaseComponent.get(context).storage().getOpenGroup(threadID) }
|
||||
|
||||
// format the mention text
|
||||
// Format the mention text
|
||||
if (matcher.find(startIndex)) {
|
||||
while (true) {
|
||||
val publicKey = text.subSequence(matcher.start() + 1, matcher.end()).toString() // +1 to get rid of the @
|
||||
|
||||
val isYou = isYou(publicKey, userPublicKey, openGroup)
|
||||
val userDisplayName: String? = if (isYou) {
|
||||
context.getString(R.string.MessageRecord_you)
|
||||
context.getString(R.string.you)
|
||||
} else {
|
||||
val contact = DatabaseComponent.get(context).sessionContactDatabase().getContactWithAccountID(publicKey)
|
||||
@Suppress("NAME_SHADOWING") val context = if (openGroup != null) Contact.ContactContext.OPEN_GROUP else Contact.ContactContext.REGULAR
|
||||
|
@ -8,7 +8,7 @@ import org.thoughtcrime.securesms.showSessionDialog
|
||||
object NotificationUtils {
|
||||
fun showNotifyDialog(context: Context, thread: Recipient, notifyTypeHandler: (Int)->Unit) {
|
||||
context.showSessionDialog {
|
||||
title(R.string.RecipientPreferenceActivity_notification_settings)
|
||||
title(R.string.sessionNotifications)
|
||||
singleChoiceItems(
|
||||
context.resources.getStringArray(R.array.notify_types),
|
||||
thread.notifyType
|
||||
|
@ -25,8 +25,8 @@ import org.session.libsignal.utilities.SettableFuture
|
||||
import org.thoughtcrime.securesms.components.GlideBitmapListeningTarget
|
||||
import org.thoughtcrime.securesms.components.GlideDrawableListeningTarget
|
||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri
|
||||
import org.thoughtcrime.securesms.mms.GlideRequest
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||
import com.bumptech.glide.RequestBuilder
|
||||
import com.bumptech.glide.RequestManager
|
||||
import org.thoughtcrime.securesms.mms.Slide
|
||||
|
||||
open class ThumbnailView @JvmOverloads constructor(
|
||||
@ -104,13 +104,13 @@ open class ThumbnailView @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
fun setImageResource(
|
||||
glide: GlideRequests,
|
||||
glide: RequestManager,
|
||||
slide: Slide,
|
||||
isPreview: Boolean
|
||||
): ListenableFuture<Boolean> = setImageResource(glide, slide, isPreview, 0, 0)
|
||||
|
||||
fun setImageResource(
|
||||
glide: GlideRequests, slide: Slide,
|
||||
glide: RequestManager, slide: Slide,
|
||||
isPreview: Boolean, naturalWidth: Int,
|
||||
naturalHeight: Int
|
||||
): ListenableFuture<Boolean> {
|
||||
@ -152,9 +152,9 @@ open class ThumbnailView @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
private fun buildThumbnailGlideRequest(
|
||||
glide: GlideRequests,
|
||||
glide: RequestManager,
|
||||
slide: Slide
|
||||
): GlideRequest<Drawable> = glide.load(DecryptableUri(slide.thumbnailUri!!))
|
||||
): RequestBuilder<Drawable> = glide.load(DecryptableUri(slide.thumbnailUri!!))
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.overrideDimensions()
|
||||
.transition(DrawableTransitionOptions.withCrossFade())
|
||||
@ -162,21 +162,21 @@ open class ThumbnailView @JvmOverloads constructor(
|
||||
.missingThumbnailPicture(slide.isInProgress)
|
||||
|
||||
private fun buildPlaceholderGlideRequest(
|
||||
glide: GlideRequests,
|
||||
glide: RequestManager,
|
||||
slide: Slide
|
||||
): GlideRequest<Bitmap> = glide.asBitmap()
|
||||
): RequestBuilder<Bitmap> = glide.asBitmap()
|
||||
.load(slide.getPlaceholderRes(context.theme))
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.overrideDimensions()
|
||||
.fitCenter()
|
||||
|
||||
open fun clear(glideRequests: GlideRequests) {
|
||||
open fun clear(glideRequests: RequestManager) {
|
||||
glideRequests.clear(binding.thumbnailImage)
|
||||
slide = null
|
||||
}
|
||||
|
||||
fun setImageResource(
|
||||
glideRequests: GlideRequests,
|
||||
glideRequests: RequestManager,
|
||||
uri: Uri
|
||||
): ListenableFuture<Boolean> = glideRequests.load(DecryptableUri(uri))
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
@ -184,19 +184,19 @@ open class ThumbnailView @JvmOverloads constructor(
|
||||
.transform(CenterCrop())
|
||||
.intoDrawableTargetAsFuture()
|
||||
|
||||
private fun GlideRequest<Drawable>.intoDrawableTargetAsFuture() =
|
||||
private fun RequestBuilder<Drawable>.intoDrawableTargetAsFuture() =
|
||||
SettableFuture<Boolean>().also {
|
||||
binding.run {
|
||||
GlideDrawableListeningTarget(thumbnailImage, thumbnailLoadIndicator, it)
|
||||
}.let { into(it) }
|
||||
}
|
||||
|
||||
private fun <T> GlideRequest<T>.overrideDimensions() =
|
||||
private fun <T> RequestBuilder<T>.overrideDimensions() =
|
||||
dimensDelegate.resourceSize().takeIf { 0 !in it }
|
||||
?.let { override(it[WIDTH], it[HEIGHT]) }
|
||||
?: override(getDefaultWidth(), getDefaultHeight())
|
||||
}
|
||||
|
||||
private fun <T> GlideRequest<T>.missingThumbnailPicture(
|
||||
private fun <T> RequestBuilder<T>.missingThumbnailPicture(
|
||||
inProgress: Boolean
|
||||
) = takeIf { inProgress } ?: apply(RequestOptions.errorOf(R.drawable.ic_missing_thumbnail_picture))
|
||||
|
@ -3,14 +3,8 @@ package org.thoughtcrime.securesms.database;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@ -95,12 +89,10 @@ public class DraftDatabase extends Database {
|
||||
}
|
||||
}
|
||||
|
||||
// Class to save drafts of text (only) messages if the user is in the middle of writing a message
|
||||
// and then the app loses focus or is closed.
|
||||
public static class Draft {
|
||||
public static final String TEXT = "text";
|
||||
public static final String IMAGE = "image";
|
||||
public static final String VIDEO = "video";
|
||||
public static final String AUDIO = "audio";
|
||||
public static final String QUOTE = "quote";
|
||||
|
||||
private final String type;
|
||||
private final String value;
|
||||
@ -117,48 +109,10 @@ public class DraftDatabase extends Database {
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
String getSnippet(Context context) {
|
||||
switch (type) {
|
||||
case TEXT: return value;
|
||||
case IMAGE: return context.getString(R.string.DraftDatabase_Draft_image_snippet);
|
||||
case VIDEO: return context.getString(R.string.DraftDatabase_Draft_video_snippet);
|
||||
case AUDIO: return context.getString(R.string.DraftDatabase_Draft_audio_snippet);
|
||||
case QUOTE: return context.getString(R.string.DraftDatabase_Draft_quote_snippet);
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Drafts extends LinkedList<Draft> {
|
||||
private Draft getDraftOfType(String type) {
|
||||
for (Draft draft : this) {
|
||||
if (type.equals(draft.getType())) {
|
||||
return draft;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getSnippet(Context context) {
|
||||
Draft textDraft = getDraftOfType(Draft.TEXT);
|
||||
if (textDraft != null) {
|
||||
return textDraft.getSnippet(context);
|
||||
} else if (size() > 0) {
|
||||
return get(0).getSnippet(context);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable Uri getUriSnippet() {
|
||||
Draft imageDraft = getDraftOfType(Draft.IMAGE);
|
||||
|
||||
if (imageDraft != null && imageDraft.getValue() != null) {
|
||||
return Uri.parse(imageDraft.getValue());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
// We don't do anything with drafts of a given type anymore (image, audio etc.) - we store TEXT
|
||||
// drafts, and any files or audio get sent to the recipient when added as a message.
|
||||
}
|
||||
}
|
@ -166,6 +166,8 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
|
||||
const val RESET_SEQ_NO = "UPDATE $lastMessageServerIDTable SET $lastMessageServerID = 0;"
|
||||
|
||||
const val EMPTY_VERSION = "0.0.0"
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
||||
@ -179,7 +181,8 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
val port = components.getOrNull(1)?.toIntOrNull() ?: return@mapNotNull null
|
||||
val ed25519Key = components.getOrNull(2) ?: return@mapNotNull null
|
||||
val x25519Key = components.getOrNull(3) ?: return@mapNotNull null
|
||||
Snode(address, port, Snode.KeySet(ed25519Key, x25519Key))
|
||||
val version = components.getOrNull(4) ?: EMPTY_VERSION
|
||||
Snode(address, port, Snode.KeySet(ed25519Key, x25519Key), version)
|
||||
}
|
||||
}?.toSet() ?: setOf()
|
||||
}
|
||||
@ -192,6 +195,7 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
if (keySet != null) {
|
||||
string += "-${keySet.ed25519Key}-${keySet.x25519Key}"
|
||||
}
|
||||
string += "-${snode.version}"
|
||||
string
|
||||
}
|
||||
val row = wrap(mapOf( Companion.dummyKey to "dummy_key", snodePool to snodePoolAsString ))
|
||||
@ -207,6 +211,7 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
if (keySet != null) {
|
||||
snodeAsString += "-${keySet.ed25519Key}-${keySet.x25519Key}"
|
||||
}
|
||||
snodeAsString += "-${snode.version}"
|
||||
val row = wrap(mapOf( Companion.indexPath to indexPath, Companion.snode to snodeAsString ))
|
||||
database.insertOrUpdate(onionRequestPathTable, row, "${Companion.indexPath} = ?", wrap(indexPath))
|
||||
}
|
||||
@ -232,8 +237,9 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
val port = components.getOrNull(1)?.toIntOrNull()
|
||||
val ed25519Key = components.getOrNull(2)
|
||||
val x25519Key = components.getOrNull(3)
|
||||
val version = components.getOrNull(4) ?: EMPTY_VERSION
|
||||
if (port != null && ed25519Key != null && x25519Key != null) {
|
||||
Snode(address, port, Snode.KeySet(ed25519Key, x25519Key))
|
||||
Snode(address, port, Snode.KeySet(ed25519Key, x25519Key), version)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
@ -251,6 +257,11 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
return result
|
||||
}
|
||||
|
||||
override fun clearSnodePool() {
|
||||
val database = databaseHelper.writableDatabase
|
||||
database.delete(snodePoolTable, null, null)
|
||||
}
|
||||
|
||||
override fun clearOnionRequestPaths() {
|
||||
val database = databaseHelper.writableDatabase
|
||||
fun delete(indexPath: String) {
|
||||
@ -271,7 +282,8 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
val port = components.getOrNull(1)?.toIntOrNull() ?: return@mapNotNull null
|
||||
val ed25519Key = components.getOrNull(2) ?: return@mapNotNull null
|
||||
val x25519Key = components.getOrNull(3) ?: return@mapNotNull null
|
||||
Snode(address, port, Snode.KeySet(ed25519Key, x25519Key))
|
||||
val version = components.getOrNull(4) ?: EMPTY_VERSION
|
||||
Snode(address, port, Snode.KeySet(ed25519Key, x25519Key), version)
|
||||
}
|
||||
}?.toSet()
|
||||
}
|
||||
|
@ -808,8 +808,8 @@ public class ThreadDatabase extends Database {
|
||||
private @NonNull String getFormattedBodyFor(@NonNull MessageRecord messageRecord) {
|
||||
if (messageRecord.isMms()) {
|
||||
MmsMessageRecord record = (MmsMessageRecord) messageRecord;
|
||||
if (record.getSharedContacts().size() > 0) {
|
||||
Contact contact = ((MmsMessageRecord) messageRecord).getSharedContacts().get(0);
|
||||
if (!record.getSharedContacts().isEmpty()) {
|
||||
Contact contact = ((MmsMessageRecord)messageRecord).getSharedContacts().get(0);
|
||||
return ContactUtil.getStringSummary(context, contact).toString();
|
||||
}
|
||||
String attachmentString = record.getSlideDeck().getBody();
|
||||
|
@ -250,7 +250,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||
|
||||
// Notify the user of the issue so they know they can downgrade until the issue is fixed
|
||||
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
|
||||
String channelId = context.getString(R.string.NotificationChannel_failures);
|
||||
String channelId = context.getString(R.string.failures);
|
||||
|
||||
if (NotificationChannels.supported()) {
|
||||
NotificationChannel channel = new NotificationChannel(channelId, channelId, NotificationManager.IMPORTANCE_HIGH);
|
||||
|
@ -14,6 +14,8 @@ import org.session.libsession.utilities.Address;
|
||||
import org.session.libsession.utilities.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.database.MediaDatabase;
|
||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent;
|
||||
import org.thoughtcrime.securesms.util.DateUtils;
|
||||
import org.thoughtcrime.securesms.util.RelativeDay;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
@ -89,15 +91,17 @@ public class BucketedThreadMediaLoader extends AsyncTaskLoader<BucketedThreadMed
|
||||
private final TimeBucket[] TIME_SECTIONS;
|
||||
|
||||
public BucketedThreadMedia(@NonNull Context context) {
|
||||
this.TODAY = new TimeBucket(context.getString(R.string.BucketedThreadMedia_Today), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -1), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, 1000));
|
||||
this.YESTERDAY = new TimeBucket(context.getString(R.string.BucketedThreadMedia_Yesterday), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -2), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -1));
|
||||
this.THIS_WEEK = new TimeBucket(context.getString(R.string.BucketedThreadMedia_This_week), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -7), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -2));
|
||||
this.THIS_MONTH = new TimeBucket(context.getString(R.string.BucketedThreadMedia_This_month), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -30), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -7));
|
||||
this.TIME_SECTIONS = new TimeBucket[]{TODAY, YESTERDAY, THIS_WEEK, THIS_MONTH};
|
||||
String localisedTodayString = DateUtils.INSTANCE.getLocalisedRelativeDayString(RelativeDay.TODAY);
|
||||
String localisedYesterdayString = DateUtils.INSTANCE.getLocalisedRelativeDayString(RelativeDay.YESTERDAY);
|
||||
|
||||
this.TODAY = new TimeBucket(localisedTodayString, TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -1), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, 1000));
|
||||
this.YESTERDAY = new TimeBucket(localisedYesterdayString, TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -2), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -1));
|
||||
this.THIS_WEEK = new TimeBucket(context.getString(R.string.attachmentsThisWeek), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -7), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -2));
|
||||
this.THIS_MONTH = new TimeBucket(context.getString(R.string.attachmentsThisMonth), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -30), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -7));
|
||||
this.TIME_SECTIONS = new TimeBucket[] { TODAY, YESTERDAY, THIS_WEEK, THIS_MONTH };
|
||||
this.OLDER = new MonthBuckets();
|
||||
}
|
||||
|
||||
|
||||
public void add(MediaDatabase.MediaRecord mediaRecord) {
|
||||
for (TimeBucket timeSection : TIME_SECTIONS) {
|
||||
if (timeSection.inRange(mediaRecord.getDate())) {
|
||||
|
@ -77,14 +77,6 @@ public class MediaMmsMessageRecord extends MmsMessageRecord {
|
||||
|
||||
@Override
|
||||
public SpannableString getDisplayBody(@NonNull Context context) {
|
||||
if (MmsDatabase.Types.isFailedDecryptType(type)) {
|
||||
return emphasisAdded(context.getString(R.string.MmsMessageRecord_bad_encrypted_mms_message));
|
||||
} else if (MmsDatabase.Types.isDuplicateMessageType(type)) {
|
||||
return emphasisAdded(context.getString(R.string.SmsMessageRecord_duplicate_message));
|
||||
} else if (MmsDatabase.Types.isNoRemoteSessionType(type)) {
|
||||
return emphasisAdded(context.getString(R.string.MmsMessageRecord_mms_message_encrypted_for_non_existing_session));
|
||||
}
|
||||
|
||||
return super.getDisplayBody(context);
|
||||
}
|
||||
}
|
||||
|
@ -57,16 +57,8 @@ public class SmsMessageRecord extends MessageRecord {
|
||||
|
||||
@Override
|
||||
public SpannableString getDisplayBody(@NonNull Context context) {
|
||||
if (SmsDatabase.Types.isFailedDecryptType(type)) {
|
||||
return emphasisAdded(context.getString(R.string.MessageDisplayHelper_bad_encrypted_message));
|
||||
} else if (SmsDatabase.Types.isDuplicateMessageType(type)) {
|
||||
return emphasisAdded(context.getString(R.string.SmsMessageRecord_duplicate_message));
|
||||
} else if (SmsDatabase.Types.isNoRemoteSessionType(type)) {
|
||||
return emphasisAdded(context.getString(R.string.MessageDisplayHelper_message_encrypted_for_non_existing_session));
|
||||
} else {
|
||||
return super.getDisplayBody(context);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMms() {
|
||||
|
@ -17,21 +17,25 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms.database.model;
|
||||
|
||||
import static org.session.libsession.utilities.StringSubstitutionConstants.APP_NAME_KEY;
|
||||
import static org.session.libsession.utilities.StringSubstitutionConstants.DISAPPEARING_MESSAGES_TYPE_KEY;
|
||||
import static org.session.libsession.utilities.StringSubstitutionConstants.NAME_KEY;
|
||||
import static org.session.libsession.utilities.StringSubstitutionConstants.TIME_KEY;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.StyleSpan;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.squareup.phrase.Phrase;
|
||||
import org.session.libsession.utilities.ExpirationUtil;
|
||||
import org.session.libsession.utilities.recipients.Recipient;
|
||||
import org.session.libsignal.utilities.Log;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsColumns;
|
||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
|
||||
/**
|
||||
@ -53,6 +57,7 @@ public class ThreadRecord extends DisplayRecord {
|
||||
private final long lastSeen;
|
||||
private final boolean pinned;
|
||||
private final int initialRecipientHash;
|
||||
private final long dateSent;
|
||||
|
||||
public ThreadRecord(@NonNull String body, @Nullable Uri snippetUri,
|
||||
@Nullable MessageRecord lastMessage, @NonNull Recipient recipient, long date, long count, int unreadCount,
|
||||
@ -72,62 +77,109 @@ public class ThreadRecord extends DisplayRecord {
|
||||
this.lastSeen = lastSeen;
|
||||
this.pinned = pinned;
|
||||
this.initialRecipientHash = recipient.hashCode();
|
||||
this.dateSent = date;
|
||||
}
|
||||
|
||||
public @Nullable Uri getSnippetUri() {
|
||||
return snippetUri;
|
||||
}
|
||||
|
||||
private String getName() {
|
||||
String name = getRecipient().getName();
|
||||
if (name == null) {
|
||||
Log.w("ThreadRecord", "Got a null name - using: Unknown");
|
||||
name = "Unknown";
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
private String getDisappearingMsgExpiryTypeString(Context context) {
|
||||
MessageRecord lm = this.lastMessage;
|
||||
if (lm == null) {
|
||||
Log.w("ThreadRecord", "Could not get last message to determine disappearing msg type.");
|
||||
return "Unknown";
|
||||
}
|
||||
long expireStarted = lm.getExpireStarted();
|
||||
|
||||
// Note: This works because expireStarted is 0 for messages which are 'Disappear after read'
|
||||
// while it's a touch higher than the sent timestamp for "Disappear after send". We could then
|
||||
// use `expireStarted == 0`, but that's not how it's done in UpdateMessageBuilder so to keep
|
||||
// things the same I'll assume there's a reason for this and follow suit.
|
||||
// Also: `this.lastMessage.getExpiresIn()` is available.
|
||||
if (expireStarted >= dateSent) {
|
||||
return context.getString(R.string.disappearingMessagesSent);
|
||||
}
|
||||
return context.getString(R.string.disappearingMessagesRead);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpannableString getDisplayBody(@NonNull Context context) {
|
||||
if (isGroupUpdateMessage()) {
|
||||
return emphasisAdded(context.getString(R.string.ThreadRecord_group_updated));
|
||||
return emphasisAdded(context.getString(R.string.groupUpdated));
|
||||
} else if (isOpenGroupInvitation()) {
|
||||
return emphasisAdded(context.getString(R.string.ThreadRecord_open_group_invitation));
|
||||
} else if (SmsDatabase.Types.isFailedDecryptType(type)) {
|
||||
return emphasisAdded(context.getString(R.string.MessageDisplayHelper_bad_encrypted_message));
|
||||
} else if (SmsDatabase.Types.isNoRemoteSessionType(type)) {
|
||||
return emphasisAdded(context.getString(R.string.MessageDisplayHelper_message_encrypted_for_non_existing_session));
|
||||
} else if (SmsDatabase.Types.isEndSessionType(type)) {
|
||||
return emphasisAdded(context.getString(R.string.ThreadRecord_secure_session_reset));
|
||||
return emphasisAdded(context.getString(R.string.communityInvitation));
|
||||
} else if (MmsSmsColumns.Types.isLegacyType(type)) {
|
||||
return emphasisAdded(context.getString(R.string.MessageRecord_message_encrypted_with_a_legacy_protocol_version_that_is_no_longer_supported));
|
||||
String txt = Phrase.from(context, R.string.messageErrorOld)
|
||||
.put(APP_NAME_KEY, context.getString(R.string.app_name))
|
||||
.format().toString();
|
||||
return emphasisAdded(txt);
|
||||
} else if (MmsSmsColumns.Types.isDraftMessageType(type)) {
|
||||
String draftText = context.getString(R.string.ThreadRecord_draft);
|
||||
String draftText = context.getString(R.string.draft);
|
||||
return emphasisAdded(draftText + " " + getBody(), 0, draftText.length());
|
||||
} else if (SmsDatabase.Types.isOutgoingCall(type)) {
|
||||
return emphasisAdded(context.getString(network.loki.messenger.R.string.ThreadRecord_called));
|
||||
String txt = Phrase.from(context, R.string.callsYouCalled)
|
||||
.put(NAME_KEY, getName())
|
||||
.format().toString();
|
||||
return emphasisAdded(txt);
|
||||
} else if (SmsDatabase.Types.isIncomingCall(type)) {
|
||||
return emphasisAdded(context.getString(network.loki.messenger.R.string.ThreadRecord_called_you));
|
||||
String txt = Phrase.from(context, R.string.callsCalledYou)
|
||||
.put(NAME_KEY, getName())
|
||||
.format().toString();
|
||||
return emphasisAdded(txt);
|
||||
} else if (SmsDatabase.Types.isMissedCall(type)) {
|
||||
return emphasisAdded(context.getString(network.loki.messenger.R.string.ThreadRecord_missed_call));
|
||||
} else if (SmsDatabase.Types.isJoinedType(type)) {
|
||||
return emphasisAdded(context.getString(R.string.ThreadRecord_s_is_on_signal, getRecipient().toShortString()));
|
||||
String txt = Phrase.from(context, R.string.callsMissedCallFrom)
|
||||
.put(NAME_KEY, getName())
|
||||
.format().toString();
|
||||
return emphasisAdded(txt);
|
||||
} else if (SmsDatabase.Types.isExpirationTimerUpdate(type)) {
|
||||
int seconds = (int) (getExpiresIn() / 1000);
|
||||
if (seconds <= 0) {
|
||||
return emphasisAdded(context.getString(R.string.ThreadRecord_disappearing_messages_disabled));
|
||||
String txt = Phrase.from(context, R.string.disappearingMessagesTurnedOff)
|
||||
.put(NAME_KEY, getName())
|
||||
.format().toString();
|
||||
return emphasisAdded(txt);
|
||||
}
|
||||
|
||||
// Implied that disappearing messages is enabled..
|
||||
String time = ExpirationUtil.getExpirationDisplayValue(context, seconds);
|
||||
return emphasisAdded(context.getString(R.string.ThreadRecord_disappearing_message_time_updated_to_s, time));
|
||||
String disappearAfterWhat = getDisappearingMsgExpiryTypeString(context); // Disappear after send or read?
|
||||
String txt = Phrase.from(context, R.string.disappearingMessagesSet)
|
||||
.put(NAME_KEY, getName())
|
||||
.put(TIME_KEY, time)
|
||||
.put(DISAPPEARING_MESSAGES_TYPE_KEY, disappearAfterWhat)
|
||||
.format().toString();
|
||||
return emphasisAdded(txt);
|
||||
|
||||
} else if (MmsSmsColumns.Types.isMediaSavedExtraction(type)) {
|
||||
return emphasisAdded(context.getString(R.string.ThreadRecord_media_saved_by_s, getRecipient().toShortString()));
|
||||
String txt = Phrase.from(context, R.string.attachmentsMediaSaved)
|
||||
.put(NAME_KEY, getName())
|
||||
.format().toString();
|
||||
return emphasisAdded(txt);
|
||||
|
||||
} else if (MmsSmsColumns.Types.isScreenshotExtraction(type)) {
|
||||
return emphasisAdded(context.getString(R.string.ThreadRecord_s_took_a_screenshot, getRecipient().toShortString()));
|
||||
} else if (SmsDatabase.Types.isIdentityUpdate(type)) {
|
||||
if (getRecipient().isGroupRecipient()) return emphasisAdded(context.getString(R.string.ThreadRecord_safety_number_changed));
|
||||
else return emphasisAdded(context.getString(R.string.ThreadRecord_your_safety_number_with_s_has_changed, getRecipient().toShortString()));
|
||||
} else if (SmsDatabase.Types.isIdentityVerified(type)) {
|
||||
return emphasisAdded(context.getString(R.string.ThreadRecord_you_marked_verified));
|
||||
} else if (SmsDatabase.Types.isIdentityDefault(type)) {
|
||||
return emphasisAdded(context.getString(R.string.ThreadRecord_you_marked_unverified));
|
||||
String txt = Phrase.from(context, R.string.screenshotTaken)
|
||||
.put(NAME_KEY, getName())
|
||||
.format().toString();
|
||||
return emphasisAdded(txt);
|
||||
|
||||
} else if (MmsSmsColumns.Types.isMessageRequestResponse(type)) {
|
||||
return emphasisAdded(context.getString(R.string.message_requests_accepted));
|
||||
return emphasisAdded(context.getString(R.string.messageRequestsAccepted));
|
||||
} else if (getCount() == 0) {
|
||||
return new SpannableString(context.getString(R.string.ThreadRecord_empty_message));
|
||||
return new SpannableString(context.getString(R.string.messageEmpty));
|
||||
} else {
|
||||
// This is shown when we receive a media message from an un-accepted contact
|
||||
if (TextUtils.isEmpty(getBody())) {
|
||||
return new SpannableString(emphasisAdded(context.getString(R.string.ThreadRecord_media_message)));
|
||||
return new SpannableString(emphasisAdded(context.getString(R.string.mediaMessage)));
|
||||
} else {
|
||||
return new SpannableString(getBody());
|
||||
}
|
||||
@ -145,43 +197,23 @@ public class ThreadRecord extends DisplayRecord {
|
||||
return spannable;
|
||||
}
|
||||
|
||||
public long getCount() {
|
||||
return count;
|
||||
}
|
||||
public long getCount() { return count; }
|
||||
|
||||
public int getUnreadCount() {
|
||||
return unreadCount;
|
||||
}
|
||||
public int getUnreadCount() { return unreadCount; }
|
||||
|
||||
public int getUnreadMentionCount() {
|
||||
return unreadMentionCount;
|
||||
}
|
||||
public int getUnreadMentionCount() { return unreadMentionCount; }
|
||||
|
||||
public long getDate() {
|
||||
return getDateReceived();
|
||||
}
|
||||
public long getDate() { return getDateReceived(); }
|
||||
|
||||
public boolean isArchived() {
|
||||
return archived;
|
||||
}
|
||||
public boolean isArchived() { return archived; }
|
||||
|
||||
public int getDistributionType() {
|
||||
return distributionType;
|
||||
}
|
||||
public int getDistributionType() { return distributionType; }
|
||||
|
||||
public long getExpiresIn() {
|
||||
return expiresIn;
|
||||
}
|
||||
public long getExpiresIn() { return expiresIn; }
|
||||
|
||||
public long getLastSeen() {
|
||||
return lastSeen;
|
||||
}
|
||||
public long getLastSeen() { return lastSeen; }
|
||||
|
||||
public boolean isPinned() {
|
||||
return pinned;
|
||||
}
|
||||
public boolean isPinned() { return pinned; }
|
||||
|
||||
public int getInitialRecipientHash() {
|
||||
return initialRecipientHash;
|
||||
}
|
||||
public int getInitialRecipientHash() { return initialRecipientHash; }
|
||||
}
|
||||
|
@ -0,0 +1,101 @@
|
||||
package org.thoughtcrime.securesms.dms
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.text.InputType
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.widget.Toast
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.fragment.app.Fragment
|
||||
import network.loki.messenger.R
|
||||
import network.loki.messenger.databinding.FragmentEnterPublicKeyBinding
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.thoughtcrime.securesms.util.QRCodeUtilities
|
||||
import org.thoughtcrime.securesms.util.hideKeyboard
|
||||
import org.thoughtcrime.securesms.util.toPx
|
||||
|
||||
class EnterPublicKeyFragment : Fragment() {
|
||||
private lateinit var binding: FragmentEnterPublicKeyBinding
|
||||
|
||||
var delegate: EnterPublicKeyDelegate? = null
|
||||
|
||||
private val hexEncodedPublicKey: String
|
||||
get() {
|
||||
return TextSecurePreferences.getLocalNumber(requireContext())!!
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
binding = FragmentEnterPublicKeyBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
with(binding) {
|
||||
publicKeyEditText.imeOptions = EditorInfo.IME_ACTION_DONE or 16777216 // Always use incognito keyboard
|
||||
publicKeyEditText.setRawInputType(InputType.TYPE_CLASS_TEXT)
|
||||
publicKeyEditText.setOnEditorActionListener { v, actionID, _ ->
|
||||
if (actionID == EditorInfo.IME_ACTION_DONE) {
|
||||
v.hideKeyboard()
|
||||
handlePublicKeyEntered()
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
publicKeyEditText.addTextChangedListener { text -> createPrivateChatButton.isVisible = !text.isNullOrBlank() }
|
||||
publicKeyEditText.setOnFocusChangeListener { _, hasFocus -> optionalContentContainer.isVisible = !hasFocus }
|
||||
mainContainer.setOnTouchListener { _, _ ->
|
||||
binding.optionalContentContainer.isVisible = true
|
||||
publicKeyEditText.clearFocus()
|
||||
publicKeyEditText.hideKeyboard()
|
||||
true
|
||||
}
|
||||
val size = toPx(228, resources)
|
||||
val qrCode = QRCodeUtilities.encode(hexEncodedPublicKey, size, isInverted = false, hasTransparentBackground = false)
|
||||
qrCodeImageView.setImageBitmap(qrCode)
|
||||
publicKeyTextView.text = hexEncodedPublicKey
|
||||
publicKeyTextView.setOnCreateContextMenuListener { contextMenu, view, _ ->
|
||||
contextMenu.add(0, view.id, 0, R.string.copy).setOnMenuItemClickListener {
|
||||
copyPublicKey()
|
||||
true
|
||||
}
|
||||
}
|
||||
copyButton.setOnClickListener { copyPublicKey() }
|
||||
shareButton.setOnClickListener { sharePublicKey() }
|
||||
createPrivateChatButton.setOnClickListener { handlePublicKeyEntered(); publicKeyEditText.hideKeyboard() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyPublicKey() {
|
||||
val clipboard = requireActivity().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clip = ClipData.newPlainText("Session ID", hexEncodedPublicKey)
|
||||
clipboard.setPrimaryClip(clip)
|
||||
Toast.makeText(requireContext(), R.string.copied, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
private fun sharePublicKey() {
|
||||
val intent = Intent()
|
||||
intent.action = Intent.ACTION_SEND
|
||||
intent.putExtra(Intent.EXTRA_TEXT, hexEncodedPublicKey)
|
||||
intent.type = "text/plain"
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
private fun handlePublicKeyEntered() {
|
||||
val hexEncodedPublicKey = binding.publicKeyEditText.text?.trim()?.toString()
|
||||
if (hexEncodedPublicKey.isNullOrEmpty()) return
|
||||
delegate?.handlePublicKeyEntered(hexEncodedPublicKey)
|
||||
}
|
||||
}
|
||||
|
||||
fun interface EnterPublicKeyDelegate {
|
||||
fun handlePublicKeyEntered(publicKey: String)
|
||||
}
|
@ -15,8 +15,7 @@ enum class EmojiCategory(val priority: Int, val key: String, @AttrRes val icon:
|
||||
PLACES(4, "Places", R.attr.emoji_category_places),
|
||||
OBJECTS(5, "Objects", R.attr.emoji_category_objects),
|
||||
SYMBOLS(6, "Symbols", R.attr.emoji_category_symbol),
|
||||
FLAGS(7, "Flags", R.attr.emoji_category_flags),
|
||||
EMOTICONS(8, "Emoticons", R.attr.emoji_category_emoticons);
|
||||
FLAGS(7, "Flags", R.attr.emoji_category_flags);
|
||||
|
||||
@StringRes
|
||||
fun getCategoryLabel(): Int {
|
||||
@ -31,15 +30,14 @@ enum class EmojiCategory(val priority: Int, val key: String, @AttrRes val icon:
|
||||
@StringRes
|
||||
fun getCategoryLabel(@AttrRes iconAttr: Int): Int {
|
||||
return when (iconAttr) {
|
||||
R.attr.emoji_category_people -> R.string.ReactWithAnyEmojiBottomSheetDialogFragment__smileys_and_people
|
||||
R.attr.emoji_category_nature -> R.string.ReactWithAnyEmojiBottomSheetDialogFragment__nature
|
||||
R.attr.emoji_category_foods -> R.string.ReactWithAnyEmojiBottomSheetDialogFragment__food
|
||||
R.attr.emoji_category_activity -> R.string.ReactWithAnyEmojiBottomSheetDialogFragment__activities
|
||||
R.attr.emoji_category_places -> R.string.ReactWithAnyEmojiBottomSheetDialogFragment__places
|
||||
R.attr.emoji_category_objects -> R.string.ReactWithAnyEmojiBottomSheetDialogFragment__objects
|
||||
R.attr.emoji_category_symbol -> R.string.ReactWithAnyEmojiBottomSheetDialogFragment__symbols
|
||||
R.attr.emoji_category_flags -> R.string.ReactWithAnyEmojiBottomSheetDialogFragment__flags
|
||||
R.attr.emoji_category_emoticons -> R.string.ReactWithAnyEmojiBottomSheetDialogFragment__emoticons
|
||||
R.attr.emoji_category_people -> R.string.emojiCategorySmileys
|
||||
R.attr.emoji_category_nature -> R.string.emojiCategoryAnimals
|
||||
R.attr.emoji_category_foods -> R.string.emojiCategoryFood
|
||||
R.attr.emoji_category_activity -> R.string.emojiCategoryActivities
|
||||
R.attr.emoji_category_places -> R.string.emojiCategoryTravel
|
||||
R.attr.emoji_category_objects -> R.string.emojiCategoryObjects
|
||||
R.attr.emoji_category_symbol -> R.string.emojiCategorySymbols
|
||||
R.attr.emoji_category_flags -> R.string.emojiCategoryFlags
|
||||
else -> throw AssertionError()
|
||||
}
|
||||
}
|
||||
|
@ -110,10 +110,12 @@ class EmojiSource(
|
||||
val parsedData: ParsedEmojiData = EmojiJsonParser.parse(it, ::getAssetsUri).getOrThrow()
|
||||
return EmojiSource(
|
||||
ScreenDensity.xhdpiRelativeDensityScaleFactor("xhdpi"),
|
||||
|
||||
parsedData.copy(
|
||||
displayPages = parsedData.displayPages + PAGE_EMOTICONS,
|
||||
dataPages = parsedData.dataPages + PAGE_EMOTICONS
|
||||
displayPages = parsedData.displayPages,
|
||||
dataPages = parsedData.dataPages
|
||||
)
|
||||
|
||||
) { uri: Uri -> EmojiPage.Asset(uri) }
|
||||
}
|
||||
}
|
||||
@ -137,25 +139,3 @@ data class ObsoleteEmoji(val obsolete: String, val replaceWith: String)
|
||||
data class EmojiMetrics(val rawHeight: Int, val rawWidth: Int, val perRow: Int)
|
||||
|
||||
private fun getAssetsUri(name: String, format: String): Uri = Uri.parse("file:///android_asset/emoji/$name.$format")
|
||||
|
||||
private val PAGE_EMOTICONS: EmojiPageModel = StaticEmojiPageModel(
|
||||
EmojiCategory.EMOTICONS,
|
||||
arrayOf(
|
||||
":-)", ";-)", "(-:", ":->", ":-D", "\\o/",
|
||||
":-P", "B-)", ":-$", ":-*", "O:-)", "=-O",
|
||||
"O_O", "O_o", "o_O", ":O", ":-!", ":-x",
|
||||
":-|", ":-\\", ":-(", ":'(", ":-[", ">:-(",
|
||||
"^.^", "^_^", "\\(\u02c6\u02da\u02c6)/",
|
||||
"\u30fd(\u00b0\u25c7\u00b0 )\u30ce", "\u00af\\(\u00b0_o)/\u00af",
|
||||
"\u00af\\_(\u30c4)_/\u00af", "(\u00ac_\u00ac)",
|
||||
"(>_<)", "(\u2565\ufe4f\u2565)", "(\u261e\uff9f\u30ee\uff9f)\u261e",
|
||||
"\u261c(\uff9f\u30ee\uff9f\u261c)", "\u261c(\u2312\u25bd\u2312)\u261e",
|
||||
"(\u256f\u00b0\u25a1\u00b0)\u256f\ufe35", "\u253b\u2501\u253b",
|
||||
"\u252c\u2500\u252c", "\u30ce(\u00b0\u2013\u00b0\u30ce)",
|
||||
"(^._.^)\uff89", "\u0e05^\u2022\ufecc\u2022^\u0e05",
|
||||
"\u0295\u2022\u1d25\u2022\u0294", "(\u2022_\u2022)",
|
||||
" \u25a0-\u25a0\u00ac <(\u2022_\u2022) ", "(\u25a0_\u25a0\u00ac)",
|
||||
"\u01aa(\u0693\u05f2)\u200e\u01aa\u200b\u200b"
|
||||
),
|
||||
null
|
||||
)
|
||||
|
@ -120,7 +120,7 @@ public class GiphyActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
protected void onPostExecute(@Nullable Uri uri) {
|
||||
if (uri == null) {
|
||||
Toast.makeText(GiphyActivity.this, R.string.GiphyActivity_error_while_retrieving_full_resolution_gif, Toast.LENGTH_LONG).show();
|
||||
Toast.makeText(GiphyActivity.this, R.string.errorUnknown, Toast.LENGTH_LONG).show();
|
||||
} else if (viewHolder == finishingImage) {
|
||||
Intent intent = new Intent();
|
||||
intent.setData(uri);
|
||||
@ -165,8 +165,8 @@ public class GiphyActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
if (position == 0) return context.getString(R.string.GiphyFragmentPagerAdapter_gifs);
|
||||
else return context.getString(R.string.GiphyFragmentPagerAdapter_stickers);
|
||||
if (position == 0) return context.getString(R.string.gif);
|
||||
else return context.getString(R.string.stickers);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,8 +29,8 @@ import org.session.libsession.utilities.ViewUtil;
|
||||
import org.session.libsignal.utilities.Log;
|
||||
import org.thoughtcrime.securesms.giph.model.ChunkedImageUrl;
|
||||
import org.thoughtcrime.securesms.giph.model.GiphyImage;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.RequestManager;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
@ -43,7 +43,7 @@ class GiphyAdapter extends RecyclerView.Adapter<GiphyAdapter.GiphyViewHolder> {
|
||||
private static final String TAG = GiphyAdapter.class.getSimpleName();
|
||||
|
||||
private final Context context;
|
||||
private final GlideRequests glideRequests;
|
||||
private final RequestManager glideRequests;
|
||||
|
||||
private List<GiphyImage> images;
|
||||
private OnItemClickListener listener;
|
||||
@ -117,7 +117,7 @@ class GiphyAdapter extends RecyclerView.Adapter<GiphyAdapter.GiphyViewHolder> {
|
||||
}
|
||||
}
|
||||
|
||||
GiphyAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests, @NonNull List<GiphyImage> images) {
|
||||
GiphyAdapter(@NonNull Context context, @NonNull RequestManager glideRequests, @NonNull List<GiphyImage> images) {
|
||||
this.context = context.getApplicationContext();
|
||||
this.glideRequests = glideRequests;
|
||||
this.images = images;
|
||||
@ -150,7 +150,7 @@ class GiphyAdapter extends RecyclerView.Adapter<GiphyAdapter.GiphyViewHolder> {
|
||||
holder.thumbnail.setAspectRatio(image.getGifAspectRatio());
|
||||
holder.gifProgress.setVisibility(View.GONE);
|
||||
|
||||
RequestBuilder<Drawable> thumbnailRequest = GlideApp.with(context)
|
||||
RequestBuilder<Drawable> thumbnailRequest = Glide.with(context)
|
||||
.load(new ChunkedImageUrl(image.getStillUrl(), image.getStillSize()))
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE);
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user