mirror of
https://github.com/oxen-io/session-android.git
synced 2025-04-03 06:05:41 +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-gradle-plugin:$kotlinVersion"
|
||||||
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlinVersion"
|
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlinVersion"
|
||||||
classpath "com.google.gms:google-services:$googleServicesVersion"
|
classpath "com.google.gms:google-services:$googleServicesVersion"
|
||||||
classpath "com.google.dagger:hilt-android-gradle-plugin:$daggerVersion"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id 'kotlin-kapt'
|
id 'com.google.devtools.ksp'
|
||||||
id 'com.google.dagger.hilt.android'
|
id 'com.google.dagger.hilt.android'
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
apply plugin: 'witness'
|
apply plugin: 'witness'
|
||||||
apply plugin: 'kotlin-kapt'
|
|
||||||
apply plugin: 'kotlin-parcelize'
|
apply plugin: 'kotlin-parcelize'
|
||||||
apply plugin: 'kotlinx-serialization'
|
apply plugin: 'kotlinx-serialization'
|
||||||
apply plugin: 'dagger.hilt.android.plugin'
|
|
||||||
|
|
||||||
configurations.all {
|
configurations.all {
|
||||||
exclude module: "commons-logging"
|
exclude module: "commons-logging"
|
||||||
}
|
}
|
||||||
|
|
||||||
def canonicalVersionCode = 374
|
def canonicalVersionCode = 376
|
||||||
def canonicalVersionName = "1.18.5"
|
def canonicalVersionName = "1.18.6"
|
||||||
|
|
||||||
def postFixSize = 10
|
def postFixSize = 10
|
||||||
def abiPostFix = ['armeabi-v7a' : 1,
|
def abiPostFix = ['armeabi-v7a' : 1,
|
||||||
@ -89,7 +86,7 @@ android {
|
|||||||
compose true
|
compose true
|
||||||
}
|
}
|
||||||
composeOptions {
|
composeOptions {
|
||||||
kotlinCompilerExtensionVersion '1.4.7'
|
kotlinCompilerExtensionVersion '1.5.14'
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
@ -238,10 +235,14 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation project(':content-descriptions')
|
||||||
|
|
||||||
implementation("com.google.dagger:hilt-android:2.46.1")
|
ksp("com.google.dagger:hilt-compiler:$daggerHiltVersion")
|
||||||
kapt("com.google.dagger:hilt-android-compiler:2.44")
|
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.appcompat:appcompat:$appcompatVersion"
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
||||||
implementation "com.google.android.material:material:$materialVersion"
|
implementation "com.google.android.material:material:$materialVersion"
|
||||||
@ -263,12 +264,15 @@ dependencies {
|
|||||||
implementation 'androidx.fragment:fragment-ktx:1.5.3'
|
implementation 'androidx.fragment:fragment-ktx:1.5.3'
|
||||||
implementation "androidx.core:core-ktx:$coreVersion"
|
implementation "androidx.core:core-ktx:$coreVersion"
|
||||||
implementation "androidx.work:work-runtime-ktx:2.7.1"
|
implementation "androidx.work:work-runtime-ktx:2.7.1"
|
||||||
|
|
||||||
playImplementation ("com.google.firebase:firebase-messaging:18.0.0") {
|
playImplementation ("com.google.firebase:firebase-messaging:18.0.0") {
|
||||||
exclude group: 'com.google.firebase', module: 'firebase-core'
|
exclude group: 'com.google.firebase', module: 'firebase-core'
|
||||||
exclude group: 'com.google.firebase', module: 'firebase-analytics'
|
exclude group: 'com.google.firebase', module: 'firebase-analytics'
|
||||||
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
|
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (project.hasProperty('huawei')) huaweiImplementation 'com.huawei.hms:push:6.7.0.300'
|
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-core:2.9.1'
|
||||||
implementation 'com.google.android.exoplayer:exoplayer-ui:2.9.1'
|
implementation 'com.google.android.exoplayer:exoplayer-ui:2.9.1'
|
||||||
implementation 'org.conscrypt:conscrypt-android:2.5.2'
|
implementation 'org.conscrypt:conscrypt-android:2.5.2'
|
||||||
@ -281,8 +285,6 @@ dependencies {
|
|||||||
implementation 'commons-net:commons-net:3.7.2'
|
implementation 'commons-net:commons-net:3.7.2'
|
||||||
implementation 'com.github.chrisbanes:PhotoView:2.1.3'
|
implementation 'com.github.chrisbanes:PhotoView:2.1.3'
|
||||||
implementation "com.github.bumptech.glide:glide:$glideVersion"
|
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.makeramen:roundedimageview:2.1.0'
|
||||||
implementation 'com.pnikosis:materialish-progress:1.5'
|
implementation 'com.pnikosis:materialish-progress:1.5'
|
||||||
implementation 'org.greenrobot:eventbus:3.0.0'
|
implementation 'org.greenrobot:eventbus:3.0.0'
|
||||||
@ -290,8 +292,6 @@ dependencies {
|
|||||||
implementation 'com.theartofdev.edmodo:android-image-cropper:2.8.0'
|
implementation 'com.theartofdev.edmodo:android-image-cropper:2.8.0'
|
||||||
implementation 'com.melnykov:floatingactionbutton:1.3.0'
|
implementation 'com.melnykov:floatingactionbutton:1.3.0'
|
||||||
implementation 'com.google.zxing:android-integration:3.1.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 'mobi.upod:time-duration-picker:1.1.3'
|
||||||
implementation 'com.google.zxing:core:3.2.1'
|
implementation 'com.google.zxing:core:3.2.1'
|
||||||
implementation ('com.davemorrissey.labs:subsampling-scale-image-view:3.6.0') {
|
implementation ('com.davemorrissey.labs:subsampling-scale-image-view:3.6.0') {
|
||||||
@ -415,8 +415,3 @@ def autoResConfig() {
|
|||||||
.collect { matcher -> matcher.group(1) }
|
.collect { matcher -> matcher.group(1) }
|
||||||
.sort()
|
.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.By
|
||||||
import androidx.test.uiautomator.UiDevice
|
import androidx.test.uiautomator.UiDevice
|
||||||
import com.adevinta.android.barista.interaction.PermissionGranter
|
import com.adevinta.android.barista.interaction.PermissionGranter
|
||||||
|
import com.squareup.phrase.Phrase
|
||||||
import network.loki.messenger.util.InputBarButtonDrawableMatcher.Companion.inputButtonWithDrawable
|
import network.loki.messenger.util.InputBarButtonDrawableMatcher.Companion.inputButtonWithDrawable
|
||||||
import org.hamcrest.Matcher
|
import org.hamcrest.Matcher
|
||||||
import org.hamcrest.Matchers.allOf
|
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.messaging.sending_receiving.link_preview.LinkPreview
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsignal.utilities.guava.Optional
|
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.ConversationActivityV2
|
||||||
import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBar
|
import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBar
|
||||||
import org.thoughtcrime.securesms.home.HomeActivity
|
import org.thoughtcrime.securesms.home.HomeActivity
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp
|
import com.bumptech.glide.Glide
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@ -71,7 +73,7 @@ class HomeActivityTests {
|
|||||||
onView(allOf(isDescendantOfA(withId(R.id.inputBar)),withId(R.id.inputBarEditText))).perform(ViewActions.replaceText(messageToSend))
|
onView(allOf(isDescendantOfA(withId(R.id.inputBar)),withId(R.id.inputBarEditText))).perform(ViewActions.replaceText(messageToSend))
|
||||||
if (linkPreview != null) {
|
if (linkPreview != null) {
|
||||||
val activity = activityMonitor.waitForActivity() as ConversationActivityV2
|
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)
|
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())
|
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)
|
device.pressKeyCode(67)
|
||||||
|
|
||||||
// Continue with display name
|
// Continue with display name
|
||||||
objectFromDesc(R.string.continue_2).click()
|
objectFromDesc(R.string.theContinue).click()
|
||||||
|
|
||||||
// Continue with default push notification setting
|
// Continue with default push notification setting
|
||||||
objectFromDesc(R.string.continue_2).click()
|
objectFromDesc(R.string.theContinue).click()
|
||||||
|
|
||||||
// PN select
|
// PN select
|
||||||
if (hasViewedSeed) {
|
if (hasViewedSeed) {
|
||||||
@ -127,7 +129,7 @@ class HomeActivityTests {
|
|||||||
@Test
|
@Test
|
||||||
fun testLaunches_dismiss_seedView() {
|
fun testLaunches_dismiss_seedView() {
|
||||||
setupLoggedInState()
|
setupLoggedInState()
|
||||||
objectFromDesc(R.string.continue_2).click()
|
objectFromDesc(R.string.theContinue).click()
|
||||||
objectFromDesc(R.string.copy).click()
|
objectFromDesc(R.string.copy).click()
|
||||||
pressBack()
|
pressBack()
|
||||||
onView(withId(R.id.seedReminderView)).check(matches(not(isDisplayed())))
|
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
|
// then the URL dialog should be displayed with a known punycode url
|
||||||
val amazonPuny = "https://www.xn--mazon-wqa.com/"
|
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(isRoot()).perform(waitFor(1000)) // no other way for this to work apparently
|
||||||
onView(withText(dialogPromptText)).check(matches(isDisplayed()))
|
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.RAISED_THREAD_PRIORITY" />
|
||||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
<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.CHANGE_NETWORK_STATE" tools:node="remove"/>
|
||||||
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT"/>
|
|
||||||
|
|
||||||
<queries>
|
<queries>
|
||||||
<intent>
|
<intent>
|
||||||
@ -130,12 +129,12 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.messagerequests.MessageRequestsActivity"
|
android:name="org.thoughtcrime.securesms.messagerequests.MessageRequestsActivity"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:label="@string/activity_message_requests_title"
|
android:label="@string/sessionMessageRequests"
|
||||||
android:screenOrientation="portrait" />
|
android:screenOrientation="portrait" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.preferences.SettingsActivity"
|
android:name="org.thoughtcrime.securesms.preferences.SettingsActivity"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:label="@string/activity_settings_title" />
|
android:label="@string/sessionSettings" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.home.PathActivity"
|
android:name="org.thoughtcrime.securesms.home.PathActivity"
|
||||||
android:screenOrientation="portrait" />
|
android:screenOrientation="portrait" />
|
||||||
@ -147,11 +146,11 @@
|
|||||||
android:name="org.thoughtcrime.securesms.preferences.BlockedContactsActivity"
|
android:name="org.thoughtcrime.securesms.preferences.BlockedContactsActivity"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:theme="@style/Theme.Session.DayNight.FlatActionBar"
|
android:theme="@style/Theme.Session.DayNight.FlatActionBar"
|
||||||
android:label="@string/blocked_contacts_title"
|
android:label="@string/conversationsBlockedContacts"
|
||||||
/>
|
/>
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.groups.EditClosedGroupActivity"
|
android:name="org.thoughtcrime.securesms.groups.EditClosedGroupActivity"
|
||||||
android:label="@string/activity_edit_closed_group_title"
|
android:label="@string/groupEdit"
|
||||||
android:screenOrientation="portrait" />
|
android:screenOrientation="portrait" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.recoverypassword.RecoveryPasswordActivity"
|
android:name="org.thoughtcrime.securesms.recoverypassword.RecoveryPasswordActivity"
|
||||||
@ -161,7 +160,7 @@
|
|||||||
android:screenOrientation="portrait" />
|
android:screenOrientation="portrait" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.preferences.PrivacySettingsActivity"
|
android:name="org.thoughtcrime.securesms.preferences.PrivacySettingsActivity"
|
||||||
android:label="@string/activity_privacy_settings_title"
|
android:label="@string/sessionPrivacy"
|
||||||
android:screenOrientation="portrait" />
|
android:screenOrientation="portrait" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.preferences.NotificationSettingsActivity"
|
android:name="org.thoughtcrime.securesms.preferences.NotificationSettingsActivity"
|
||||||
@ -171,7 +170,7 @@
|
|||||||
android:screenOrientation="portrait" />
|
android:screenOrientation="portrait" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.preferences.HelpSettingsActivity"
|
android:name="org.thoughtcrime.securesms.preferences.HelpSettingsActivity"
|
||||||
android:label="@string/activity_help_settings_title"
|
android:label="@string/sessionHelp"
|
||||||
android:screenOrientation="portrait" />
|
android:screenOrientation="portrait" />
|
||||||
<activity android:name="org.thoughtcrime.securesms.preferences.appearance.AppearanceSettingsActivity"
|
<activity android:name="org.thoughtcrime.securesms.preferences.appearance.AppearanceSettingsActivity"
|
||||||
android:screenOrientation="portrait"/>
|
android:screenOrientation="portrait"/>
|
||||||
@ -264,7 +263,6 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.MediaPreviewActivity"
|
android:name="org.thoughtcrime.securesms.MediaPreviewActivity"
|
||||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
||||||
android:label="@string/AndroidManifest__media_preview"
|
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:theme="@style/Theme.Session.DayNight.NoActionBar"
|
android:theme="@style/Theme.Session.DayNight.NoActionBar"
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
|
@ -214,6 +214,17 @@ public class ApplicationContext extends Application implements DefaultLifecycleO
|
|||||||
DatabaseModule.init(this);
|
DatabaseModule.init(this);
|
||||||
MessagingModuleConfiguration.configure(this);
|
MessagingModuleConfiguration.configure(this);
|
||||||
super.onCreate();
|
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(
|
messagingModuleConfiguration = new MessagingModuleConfiguration(
|
||||||
this,
|
this,
|
||||||
storage,
|
storage,
|
||||||
|
@ -4,12 +4,8 @@ import android.app.ActivityManager;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
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 androidx.fragment.app.FragmentActivity;
|
||||||
import android.view.KeyEvent;
|
|
||||||
|
|
||||||
import org.session.libsession.utilities.TextSecurePreferences;
|
import org.session.libsession.utilities.TextSecurePreferences;
|
||||||
import org.session.libsession.utilities.dynamiclanguage.DynamicLanguageActivityHelper;
|
import org.session.libsession.utilities.dynamiclanguage.DynamicLanguageActivityHelper;
|
||||||
|
@ -8,19 +8,8 @@ class DeleteMediaDialog {
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun show(context: Context, recordCount: Int, doDelete: Runnable) = context.showSessionDialog {
|
fun show(context: Context, recordCount: Int, doDelete: Runnable) = context.showSessionDialog {
|
||||||
iconAttribute(R.attr.dialog_alert_icon)
|
iconAttribute(R.attr.dialog_alert_icon)
|
||||||
title(
|
title(context.resources.getString(R.string.deleteMessages))
|
||||||
context.resources.getQuantityString(
|
text(context.resources.getString(R.string.deleteMessageDescriptionEveryone))
|
||||||
R.plurals.MediaOverviewActivity_Media_delete_confirm_title,
|
|
||||||
recordCount,
|
|
||||||
recordCount
|
|
||||||
)
|
|
||||||
)
|
|
||||||
text(
|
|
||||||
context.resources.getQuantityString(R.plurals.MediaOverviewActivity_Media_delete_confirm_message,
|
|
||||||
recordCount,
|
|
||||||
recordCount
|
|
||||||
)
|
|
||||||
)
|
|
||||||
button(R.string.delete) { doDelete.run() }
|
button(R.string.delete) { doDelete.run() }
|
||||||
cancelButton()
|
cancelButton()
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,8 @@ class DeleteMediaPreviewDialog {
|
|||||||
fun show(context: Context, doDelete: Runnable) {
|
fun show(context: Context, doDelete: Runnable) {
|
||||||
context.showSessionDialog {
|
context.showSessionDialog {
|
||||||
iconAttribute(R.attr.dialog_alert_icon)
|
iconAttribute(R.attr.dialog_alert_icon)
|
||||||
title(R.string.MediaPreviewActivity_media_delete_confirmation_title)
|
title(R.string.deleteMessage)
|
||||||
text(R.string.MediaPreviewActivity_media_delete_confirmation_message)
|
text(R.string.deleteMessageDescriptionEveryone)
|
||||||
button(R.string.delete) { doDelete.run() }
|
button(R.string.delete) { doDelete.run() }
|
||||||
cancelButton()
|
cancelButton()
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,10 @@ public class MediaDocumentsAdapter extends CursorRecyclerViewAdapter<ViewHolder>
|
|||||||
|
|
||||||
if (slide != null && slide.hasDocument()) {
|
if (slide != null && slide.hasDocument()) {
|
||||||
viewHolder.documentView.setDocument((DocumentSlide)slide, false);
|
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.documentView.setVisibility(View.VISIBLE);
|
||||||
viewHolder.date.setVisibility(View.VISIBLE);
|
viewHolder.date.setVisibility(View.VISIBLE);
|
||||||
viewHolder.documentView.setOnClickListener(view -> {
|
viewHolder.documentView.setOnClickListener(view -> {
|
||||||
@ -71,7 +74,7 @@ public class MediaDocumentsAdapter extends CursorRecyclerViewAdapter<ViewHolder>
|
|||||||
getContext().startActivity(intent);
|
getContext().startActivity(intent);
|
||||||
} catch (ActivityNotFoundException anfe) {
|
} catch (ActivityNotFoundException anfe) {
|
||||||
Log.w(TAG, "No activity existed to view the media.");
|
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 {
|
} else {
|
||||||
@ -104,7 +107,7 @@ public class MediaDocumentsAdapter extends CursorRecyclerViewAdapter<ViewHolder>
|
|||||||
public void onBindHeaderViewHolder(HeaderViewHolder viewHolder, int position) {
|
public void onBindHeaderViewHolder(HeaderViewHolder viewHolder, int position) {
|
||||||
Cursor cursor = getCursorAtPositionOrThrow(position);
|
Cursor cursor = getCursorAtPositionOrThrow(position);
|
||||||
MediaDatabase.MediaRecord mediaRecord = MediaDatabase.MediaRecord.from(getContext(), cursor);
|
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 {
|
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.conversation.v2.utilities.ThumbnailView;
|
||||||
import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord;
|
import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord;
|
||||||
import org.thoughtcrime.securesms.database.loaders.BucketedThreadMediaLoader.BucketedThreadMedia;
|
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.mms.Slide;
|
||||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ class MediaGalleryAdapter extends StickyHeaderGridAdapter {
|
|||||||
private static final String TAG = MediaGalleryAdapter.class.getSimpleName();
|
private static final String TAG = MediaGalleryAdapter.class.getSimpleName();
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final GlideRequests glideRequests;
|
private final RequestManager glideRequests;
|
||||||
private final Locale locale;
|
private final Locale locale;
|
||||||
private final ItemClickListener itemClickListener;
|
private final ItemClickListener itemClickListener;
|
||||||
private final Set<MediaRecord> selected;
|
private final Set<MediaRecord> selected;
|
||||||
@ -74,7 +74,7 @@ class MediaGalleryAdapter extends StickyHeaderGridAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MediaGalleryAdapter(@NonNull Context context,
|
MediaGalleryAdapter(@NonNull Context context,
|
||||||
@NonNull GlideRequests glideRequests,
|
@NonNull RequestManager glideRequests,
|
||||||
BucketedThreadMedia media,
|
BucketedThreadMedia media,
|
||||||
Locale locale,
|
Locale locale,
|
||||||
ItemClickListener clickListener)
|
ItemClickListener clickListener)
|
||||||
|
@ -16,11 +16,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms;
|
package org.thoughtcrime.securesms;
|
||||||
|
|
||||||
|
import static org.session.libsession.utilities.StringSubstitutionConstants.APP_NAME_KEY;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@ -32,10 +33,8 @@ import android.view.ViewGroup;
|
|||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.appcompat.view.ActionMode;
|
import androidx.appcompat.view.ActionMode;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
@ -48,36 +47,34 @@ import androidx.recyclerview.widget.DividerItemDecoration;
|
|||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import androidx.viewpager.widget.ViewPager;
|
import androidx.viewpager.widget.ViewPager;
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
import com.codewaves.stickyheadergrid.StickyHeaderGridLayoutManager;
|
import com.codewaves.stickyheadergrid.StickyHeaderGridLayoutManager;
|
||||||
import com.google.android.material.tabs.TabLayout;
|
import com.google.android.material.tabs.TabLayout;
|
||||||
|
import com.squareup.phrase.Phrase;
|
||||||
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 java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import kotlin.Unit;
|
import kotlin.Unit;
|
||||||
import network.loki.messenger.R;
|
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
|
* Activity for displaying media attachments in-app
|
||||||
@ -117,17 +114,26 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initializeResources() {
|
private void initializeResources() {
|
||||||
Address address = getIntent().getParcelableExtra(ADDRESS_EXTRA);
|
|
||||||
|
|
||||||
this.viewPager = ViewUtil.findById(this, R.id.pager);
|
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);
|
this.tabLayout = ViewUtil.findById(this, R.id.tab_layout);
|
||||||
this.recipient = Recipient.from(this, address, true);
|
|
||||||
|
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() {
|
private void initializeToolbar() {
|
||||||
setSupportActionBar(this.toolbar);
|
setSupportActionBar(this.toolbar);
|
||||||
ActionBar actionBar = getSupportActionBar();
|
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.setTitle(recipient.toShortString());
|
||||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
actionBar.setHomeButtonEnabled(true);
|
actionBar.setHomeButtonEnabled(true);
|
||||||
@ -176,8 +182,8 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CharSequence getPageTitle(int position) {
|
public CharSequence getPageTitle(int position) {
|
||||||
if (position == 0) return getString(R.string.MediaOverviewActivity_Media);
|
if (position == 0) return getString(R.string.media);
|
||||||
else if (position == 1) return getString(R.string.MediaOverviewActivity_Documents);
|
else if (position == 1) return getString(R.string.files);
|
||||||
else throw new AssertionError();
|
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.gridManager = new StickyHeaderGridLayoutManager(getResources().getInteger(R.integer.media_overview_cols));
|
||||||
|
|
||||||
this.recyclerView.setAdapter(new MediaGalleryAdapter(getContext(),
|
this.recyclerView.setAdapter(new MediaGalleryAdapter(getContext(),
|
||||||
GlideApp.with(this),
|
Glide.with(this),
|
||||||
new BucketedThreadMedia(getContext()),
|
new BucketedThreadMedia(getContext()),
|
||||||
locale,
|
locale,
|
||||||
this));
|
this));
|
||||||
@ -325,13 +331,19 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
|
|||||||
Permissions.with(this)
|
Permissions.with(this)
|
||||||
.request(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
.request(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||||
.maxSdkVersion(Build.VERSION_CODES.P)
|
.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(context, R.string.permissionsStorageSaveDenied)
|
||||||
.onAnyDenied(() -> Toast.makeText(getContext(), R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show())
|
.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(() -> {
|
.onAllGranted(() -> {
|
||||||
new ProgressDialogAsyncTask<Void, Void, List<SaveAttachmentTask.Attachment>>(
|
new ProgressDialogAsyncTask<Void, Void, List<SaveAttachmentTask.Attachment>>(
|
||||||
context,
|
context,
|
||||||
R.string.MediaOverviewActivity_collecting_attachments,
|
R.string.attachmentsCollecting,
|
||||||
R.string.please_wait) {
|
R.string.waitOneMoment) {
|
||||||
@Override
|
@Override
|
||||||
protected List<SaveAttachmentTask.Attachment> doInBackground(Void... params) {
|
protected List<SaveAttachmentTask.Attachment> doInBackground(Void... params) {
|
||||||
List<SaveAttachmentTask.Attachment> attachments = new LinkedList<>();
|
List<SaveAttachmentTask.Attachment> attachments = new LinkedList<>();
|
||||||
@ -382,8 +394,8 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
|
|||||||
recordCount,
|
recordCount,
|
||||||
() -> new ProgressDialogAsyncTask<MediaDatabase.MediaRecord, Void, Void>(
|
() -> new ProgressDialogAsyncTask<MediaDatabase.MediaRecord, Void, Void>(
|
||||||
requireContext(),
|
requireContext(),
|
||||||
R.string.MediaOverviewActivity_Media_delete_progress_title,
|
R.string.deleting,
|
||||||
R.string.MediaOverviewActivity_Media_delete_progress_message) {
|
R.string.deleting) {
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground(MediaDatabase.MediaRecord... records) {
|
protected Void doInBackground(MediaDatabase.MediaRecord... records) {
|
||||||
if (records == null || records.length == 0) {
|
if (records == null || records.length == 0) {
|
||||||
|
@ -16,17 +16,17 @@
|
|||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms;
|
package org.thoughtcrime.securesms;
|
||||||
|
|
||||||
|
import static org.session.libsession.utilities.StringSubstitutionConstants.APP_NAME_KEY;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.CursorIndexOutOfBoundsException;
|
import android.database.CursorIndexOutOfBoundsException;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Build.VERSION;
|
import android.os.Build.VERSION;
|
||||||
import android.os.Build.VERSION_CODES;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
@ -44,7 +44,6 @@ import android.view.WindowInsetsController;
|
|||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
@ -56,7 +55,12 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
|||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import androidx.viewpager.widget.PagerAdapter;
|
import androidx.viewpager.widget.PagerAdapter;
|
||||||
import androidx.viewpager.widget.ViewPager;
|
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.messages.control.DataExtractionNotification;
|
||||||
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment;
|
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.database.model.MmsMessageRecord;
|
||||||
import org.thoughtcrime.securesms.mediapreview.MediaPreviewViewModel;
|
import org.thoughtcrime.securesms.mediapreview.MediaPreviewViewModel;
|
||||||
import org.thoughtcrime.securesms.mediapreview.MediaRailAdapter;
|
import org.thoughtcrime.securesms.mediapreview.MediaRailAdapter;
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
import com.bumptech.glide.Glide;
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
import com.bumptech.glide.RequestManager;
|
||||||
import org.thoughtcrime.securesms.mms.Slide;
|
import org.thoughtcrime.securesms.mms.Slide;
|
||||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||||
import org.thoughtcrime.securesms.util.AttachmentUtil;
|
import org.thoughtcrime.securesms.util.AttachmentUtil;
|
||||||
import org.thoughtcrime.securesms.util.DateUtils;
|
import org.thoughtcrime.securesms.util.DateUtils;
|
||||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
|
|
||||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask.Attachment;
|
import org.thoughtcrime.securesms.util.SaveAttachmentTask.Attachment;
|
||||||
|
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.WeakHashMap;
|
|
||||||
|
|
||||||
import kotlin.Unit;
|
|
||||||
import network.loki.messenger.R;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activity for displaying media attachments in-app
|
* Activity for displaying media attachments in-app
|
||||||
@ -242,12 +239,12 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
|||||||
CharSequence relativeTimeSpan;
|
CharSequence relativeTimeSpan;
|
||||||
|
|
||||||
if (mediaItem.date > 0) {
|
if (mediaItem.date > 0) {
|
||||||
relativeTimeSpan = DateUtils.getDisplayFormattedTimeSpanString(this, Locale.getDefault(), mediaItem.date);
|
relativeTimeSpan = DateUtils.INSTANCE.getDisplayFormattedTimeSpanString(this, Locale.getDefault(), mediaItem.date);
|
||||||
} else {
|
} 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 if (mediaItem.recipient != null) getSupportActionBar().setTitle(mediaItem.recipient.toShortString());
|
||||||
else getSupportActionBar().setTitle("");
|
else getSupportActionBar().setTitle("");
|
||||||
|
|
||||||
@ -258,7 +255,6 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
|||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
|
||||||
initializeMedia();
|
initializeMedia();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,7 +277,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
|||||||
mediaPager.setOffscreenPageLimit(1);
|
mediaPager.setOffscreenPageLimit(1);
|
||||||
|
|
||||||
albumRail = findViewById(R.id.media_preview_album_rail);
|
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.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
|
||||||
albumRail.setAdapter(albumRailAdapter);
|
albumRail.setAdapter(albumRailAdapter);
|
||||||
@ -291,7 +287,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
|||||||
captionContainer = findViewById(R.id.media_preview_caption_container);
|
captionContainer = findViewById(R.id.media_preview_caption_container);
|
||||||
playbackControlsContainer = findViewById(R.id.media_preview_playback_controls_container);
|
playbackControlsContainer = findViewById(R.id.media_preview_playback_controls_container);
|
||||||
|
|
||||||
setSupportActionBar(findViewById(R.id.toolbar));
|
setSupportActionBar(findViewById(R.id.search_toolbar));
|
||||||
ActionBar actionBar = getSupportActionBar();
|
ActionBar actionBar = getSupportActionBar();
|
||||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
actionBar.setHomeButtonEnabled(true);
|
actionBar.setHomeButtonEnabled(true);
|
||||||
@ -361,7 +357,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
|||||||
private void initializeMedia() {
|
private void initializeMedia() {
|
||||||
if (!isContentTypeSupported(initialMediaType)) {
|
if (!isContentTypeSupported(initialMediaType)) {
|
||||||
Log.w(TAG, "Unsupported media type sent to MediaPreviewActivity, finishing.");
|
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();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -370,7 +366,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
|||||||
if (conversationRecipient != null) {
|
if (conversationRecipient != null) {
|
||||||
getSupportLoaderManager().restartLoader(0, null, this);
|
getSupportLoaderManager().restartLoader(0, null, this);
|
||||||
} else {
|
} 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);
|
mediaPager.setAdapter(adapter);
|
||||||
|
|
||||||
if (initialCaption != null) {
|
if (initialCaption != null) {
|
||||||
@ -410,6 +406,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
|||||||
@SuppressWarnings("CodeBlock2Expr")
|
@SuppressWarnings("CodeBlock2Expr")
|
||||||
@SuppressLint("InlinedApi")
|
@SuppressLint("InlinedApi")
|
||||||
private void saveToDisk() {
|
private void saveToDisk() {
|
||||||
|
Log.w("ACL", "Asked to save to disk!");
|
||||||
MediaItem mediaItem = getCurrentMediaItem();
|
MediaItem mediaItem = getCurrentMediaItem();
|
||||||
if (mediaItem == null) return;
|
if (mediaItem == null) return;
|
||||||
|
|
||||||
@ -417,8 +414,15 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
|||||||
Permissions.with(this)
|
Permissions.with(this)
|
||||||
.request(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
.request(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||||
.maxSdkVersion(Build.VERSION_CODES.P)
|
.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(getApplicationContext(), R.string.permissionsStorageSaveDenied)
|
||||||
.onAnyDenied(() -> Toast.makeText(this, R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show())
|
.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(() -> {
|
.onAllGranted(() -> {
|
||||||
SaveAttachmentTask saveTask = new SaveAttachmentTask(MediaPreviewActivity.this);
|
SaveAttachmentTask saveTask = new SaveAttachmentTask(MediaPreviewActivity.this);
|
||||||
long saveDate = (mediaItem.date > 0) ? mediaItem.date : SnodeAPI.getNowWithOffset();
|
long saveDate = (mediaItem.date > 0) ? mediaItem.date : SnodeAPI.getNowWithOffset();
|
||||||
@ -518,7 +522,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
|||||||
|
|
||||||
mediaPager.removeOnPageChangeListener(viewPagerListener);
|
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);
|
mediaPager.setAdapter(adapter);
|
||||||
|
|
||||||
viewModel.setCursor(this, data.first, leftIsRecent);
|
viewModel.setCursor(this, data.first, leftIsRecent);
|
||||||
@ -588,7 +592,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
|||||||
|
|
||||||
private static class SingleItemPagerAdapter extends MediaItemAdapter {
|
private static class SingleItemPagerAdapter extends MediaItemAdapter {
|
||||||
|
|
||||||
private final GlideRequests glideRequests;
|
private final RequestManager glideRequests;
|
||||||
private final Window window;
|
private final Window window;
|
||||||
private final Uri uri;
|
private final Uri uri;
|
||||||
private final String mediaType;
|
private final String mediaType;
|
||||||
@ -596,7 +600,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
|||||||
|
|
||||||
private final LayoutInflater inflater;
|
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,
|
@NonNull Window window, @NonNull Uri uri, @NonNull String mediaType,
|
||||||
long size)
|
long size)
|
||||||
{
|
{
|
||||||
@ -663,14 +667,14 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
|||||||
private final WeakHashMap<Integer, MediaView> mediaViews = new WeakHashMap<>();
|
private final WeakHashMap<Integer, MediaView> mediaViews = new WeakHashMap<>();
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final GlideRequests glideRequests;
|
private final RequestManager glideRequests;
|
||||||
private final Window window;
|
private final Window window;
|
||||||
private final Cursor cursor;
|
private final Cursor cursor;
|
||||||
private final boolean leftIsRecent;
|
private final boolean leftIsRecent;
|
||||||
|
|
||||||
private int autoPlayPosition;
|
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,
|
@NonNull Window window, @NonNull Cursor cursor, int autoPlayPosition,
|
||||||
boolean leftIsRecent)
|
boolean leftIsRecent)
|
||||||
{
|
{
|
||||||
|
@ -10,18 +10,18 @@ fun showMuteDialog(
|
|||||||
context: Context,
|
context: Context,
|
||||||
onMuteDuration: (Long) -> Unit
|
onMuteDuration: (Long) -> Unit
|
||||||
): AlertDialog = context.showSessionDialog {
|
): AlertDialog = context.showSessionDialog {
|
||||||
title(R.string.MuteDialog_mute_notifications)
|
title(R.string.notificationsMute)
|
||||||
items(Option.values().map { it.stringRes }.map(context::getString).toTypedArray()) {
|
items(Option.values().map { it.stringRes }.map(context::getString).toTypedArray()) {
|
||||||
onMuteDuration(Option.values()[it].getTime())
|
onMuteDuration(Option.values()[it].getTime())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum class Option(@StringRes val stringRes: Int, val getTime: () -> Long) {
|
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)),
|
ONE_HOUR(R.string.notificationsMute1Hour, duration = TimeUnit.HOURS.toMillis(1)),
|
||||||
TWO_HOURS(R.string.arrays__mute_for_two_hours, duration = TimeUnit.DAYS.toMillis(2)),
|
TWO_HOURS(R.string.notificationsMute2Hours, duration = TimeUnit.DAYS.toMillis(2)),
|
||||||
ONE_DAY(R.string.arrays__mute_for_one_day, duration = TimeUnit.DAYS.toMillis(1)),
|
ONE_DAY(R.string.notificationsMute1Day, duration = TimeUnit.DAYS.toMillis(1)),
|
||||||
SEVEN_DAYS(R.string.arrays__mute_for_seven_days, duration = TimeUnit.DAYS.toMillis(7)),
|
SEVEN_DAYS(R.string.notificationsMute1Week, duration = TimeUnit.DAYS.toMillis(7)),
|
||||||
FOREVER(R.string.arrays__mute_forever, getTime = { Long.MAX_VALUE });
|
FOREVER(R.string.notificationsMute, getTime = { Long.MAX_VALUE });
|
||||||
|
|
||||||
constructor(@StringRes stringRes: Int, duration: Long): this(stringRes, { System.currentTimeMillis() + duration })
|
constructor(@StringRes stringRes: Int, duration: Long): this(stringRes, { System.currentTimeMillis() + duration })
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms;
|
package org.thoughtcrime.securesms;
|
||||||
|
|
||||||
|
import static org.session.libsession.utilities.StringSubstitutionConstants.APP_NAME_KEY;
|
||||||
|
|
||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.app.KeyguardManager;
|
import android.app.KeyguardManager;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
@ -25,20 +27,18 @@ import android.content.ServiceConnection;
|
|||||||
import android.graphics.PorterDuff;
|
import android.graphics.PorterDuff;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
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.View;
|
||||||
import android.view.animation.Animation;
|
import android.view.animation.Animation;
|
||||||
import android.view.animation.BounceInterpolator;
|
import android.view.animation.BounceInterpolator;
|
||||||
import android.view.animation.TranslateAnimation;
|
import android.view.animation.TranslateAnimation;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
|
import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
|
||||||
import androidx.core.os.CancellationSignal;
|
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.libsession.utilities.TextSecurePreferences;
|
||||||
import org.session.libsignal.utilities.Log;
|
import org.session.libsignal.utilities.Log;
|
||||||
import org.thoughtcrime.securesms.components.AnimatingToggle;
|
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.service.KeyCachingService;
|
||||||
import org.thoughtcrime.securesms.util.AnimationCompleteListener;
|
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.
|
//TODO Rename to ScreenLockActivity and refactor to Kotlin.
|
||||||
public class PassphrasePromptActivity extends BaseActionBarActivity {
|
public class PassphrasePromptActivity extends BaseActionBarActivity {
|
||||||
|
|
||||||
@ -158,6 +153,16 @@ public class PassphrasePromptActivity extends BaseActionBarActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initializeResources() {
|
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);
|
visibilityToggle = findViewById(R.id.button_toggle);
|
||||||
fingerprintPrompt = findViewById(R.id.fingerprint_auth_container);
|
fingerprintPrompt = findViewById(R.id.fingerprint_auth_container);
|
||||||
lockScreenButton = findViewById(R.id.lock_screen_auth_container);
|
lockScreenButton = findViewById(R.id.lock_screen_auth_container);
|
||||||
@ -165,10 +170,6 @@ public class PassphrasePromptActivity extends BaseActionBarActivity {
|
|||||||
fingerprintCancellationSignal = new CancellationSignal();
|
fingerprintCancellationSignal = new CancellationSignal();
|
||||||
fingerprintListener = new FingerprintListener();
|
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.setImageResource(R.drawable.ic_fingerprint_white_48dp);
|
||||||
fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.signal_primary), PorterDuff.Mode.SRC_IN);
|
fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.signal_primary), PorterDuff.Mode.SRC_IN);
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA
|
|||||||
private BroadcastReceiver clearKeyReceiver;
|
private BroadcastReceiver clearKeyReceiver;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected final void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
Log.i(TAG, "onCreate(" + savedInstanceState + ")");
|
Log.i(TAG, "onCreate(" + savedInstanceState + ")");
|
||||||
onPreCreate();
|
onPreCreate();
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initializeToolbar() {
|
private void initializeToolbar() {
|
||||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
Toolbar toolbar = findViewById(R.id.search_toolbar);
|
||||||
setSupportActionBar(toolbar);
|
setSupportActionBar(toolbar);
|
||||||
ActionBar actionBar = getSupportActionBar();
|
ActionBar actionBar = getSupportActionBar();
|
||||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
|
@ -37,7 +37,7 @@ public class ShortcutLauncherActivity extends AppCompatActivity {
|
|||||||
String serializedAddress = getIntent().getStringExtra(KEY_SERIALIZED_ADDRESS);
|
String serializedAddress = getIntent().getStringExtra(KEY_SERIALIZED_ADDRESS);
|
||||||
|
|
||||||
if (serializedAddress == null) {
|
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));
|
startActivity(new Intent(this, HomeActivity.class));
|
||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
|
@ -105,7 +105,7 @@ public final class AvatarSelection {
|
|||||||
extraIntents.add(new Intent("network.loki.securesms.action.CLEAR_PROFILE_PHOTO"));
|
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()) {
|
if (!extraIntents.isEmpty()) {
|
||||||
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, extraIntents.toArray(new Intent[0]));
|
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.core.view.isVisible
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
@ -38,7 +39,6 @@ import org.session.libsession.utilities.truncateIdForDisplay
|
|||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp
|
|
||||||
import org.thoughtcrime.securesms.permissions.Permissions
|
import org.thoughtcrime.securesms.permissions.Permissions
|
||||||
import org.thoughtcrime.securesms.service.WebRtcCallService
|
import org.thoughtcrime.securesms.service.WebRtcCallService
|
||||||
import org.thoughtcrime.securesms.util.AvatarPlaceholderGenerator
|
import org.thoughtcrime.securesms.util.AvatarPlaceholderGenerator
|
||||||
@ -70,7 +70,7 @@ class WebRtcCallActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val viewModel by viewModels<CallViewModel>()
|
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 lateinit var binding: ActivityWebrtcBinding
|
||||||
private var uiJob: Job? = null
|
private var uiJob: Job? = null
|
||||||
private var wantsToAnswer = false
|
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.ThemeUtil;
|
||||||
import org.session.libsession.utilities.recipients.Recipient;
|
import org.session.libsession.utilities.recipients.Recipient;
|
||||||
import org.session.libsession.utilities.recipients.RecipientExporter;
|
import org.session.libsession.utilities.recipients.RecipientExporter;
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
import com.bumptech.glide.Glide;
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
import com.bumptech.glide.RequestManager;
|
||||||
import org.thoughtcrime.securesms.util.AvatarPlaceholderGenerator;
|
import org.thoughtcrime.securesms.util.AvatarPlaceholderGenerator;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -117,10 +117,10 @@ public class AvatarImageView extends AppCompatImageView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateAvatar(Recipient recipient) {
|
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 != null) {
|
||||||
if (recipient.isLocalNumber()) {
|
if (recipient.isLocalNumber()) {
|
||||||
setImageDrawable(new ResourceContactPhoto(R.drawable.ic_note_to_self).asDrawable(getContext(), recipient.getColor().toAvatarColor(getContext()), inverted));
|
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);
|
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.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.fileSize.setText(Util.getPrettyFileSize(documentSlide.getFileSize()));
|
||||||
this.document.setText(getFileType(documentSlide.getFileName()));
|
this.document.setText(getFileType(documentSlide.getFileName()));
|
||||||
this.setOnClickListener(new OpenClickedListener(documentSlide));
|
this.setOnClickListener(new OpenClickedListener(documentSlide));
|
||||||
|
@ -54,7 +54,7 @@ public class FromTextView extends EmojiTextView {
|
|||||||
|
|
||||||
|
|
||||||
if (recipient.isLocalNumber()) {
|
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())) {
|
} else if (recipient.getName() == null && !TextUtils.isEmpty(recipient.getProfileName())) {
|
||||||
SpannableString profileName = new SpannableString(" (~" + recipient.getProfileName() + ") ");
|
SpannableString profileName = new SpannableString(" (~" + recipient.getProfileName() + ") ");
|
||||||
profileName.setSpan(new CenterAlignedRelativeSizeSpan(0.75f), 0, profileName.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
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 android.widget.FrameLayout;
|
||||||
|
|
||||||
import network.loki.messenger.R;
|
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.mms.VideoSlide;
|
||||||
import org.thoughtcrime.securesms.video.VideoPlayer;
|
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));
|
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 Window window,
|
||||||
@NonNull Uri source,
|
@NonNull Uri source,
|
||||||
@NonNull String mediaType,
|
@NonNull String mediaType,
|
||||||
|
@ -20,8 +20,8 @@ import org.session.libsession.utilities.GroupUtil
|
|||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp
|
import com.bumptech.glide.Glide
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import com.bumptech.glide.RequestManager
|
||||||
|
|
||||||
class ProfilePictureView @JvmOverloads constructor(
|
class ProfilePictureView @JvmOverloads constructor(
|
||||||
context: Context, attrs: AttributeSet? = null
|
context: Context, attrs: AttributeSet? = null
|
||||||
@ -29,7 +29,7 @@ class ProfilePictureView @JvmOverloads constructor(
|
|||||||
private val TAG = "ProfilePictureView"
|
private val TAG = "ProfilePictureView"
|
||||||
|
|
||||||
private val binding = ViewProfilePictureBinding.inflate(LayoutInflater.from(context), this)
|
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 prefs = AppTextSecurePreferences(context)
|
||||||
private val userPublicKey = prefs.getLocalNumber()
|
private val userPublicKey = prefs.getLocalNumber()
|
||||||
var publicKey: String? = null
|
var publicKey: String? = null
|
||||||
|
@ -28,7 +28,7 @@ import com.bumptech.glide.signature.MediaStoreSignature;
|
|||||||
import network.loki.messenger.R;
|
import network.loki.messenger.R;
|
||||||
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
|
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
|
||||||
import org.thoughtcrime.securesms.database.loaders.RecentPhotosLoader;
|
import org.thoughtcrime.securesms.database.loaders.RecentPhotosLoader;
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
import com.bumptech.glide.Glide;
|
||||||
|
|
||||||
import org.session.libsession.utilities.ViewUtil;
|
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);
|
Key signature = new MediaStoreSignature(mimeType, dateModified, orientation);
|
||||||
|
|
||||||
GlideApp.with(getContext().getApplicationContext())
|
Glide.with(getContext().getApplicationContext())
|
||||||
.load(uri)
|
.load(uri)
|
||||||
.signature(signature)
|
.signature(signature)
|
||||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||||
|
@ -44,10 +44,9 @@ public class SearchToolbar extends LinearLayout {
|
|||||||
inflate(getContext(), R.layout.search_toolbar, this);
|
inflate(getContext(), R.layout.search_toolbar, this);
|
||||||
setOrientation(VERTICAL);
|
setOrientation(VERTICAL);
|
||||||
|
|
||||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
Toolbar toolbar = findViewById(R.id.search_toolbar);
|
||||||
|
|
||||||
toolbar.setNavigationIcon(
|
toolbar.setNavigationIcon(getContext().getResources().getDrawable(R.drawable.ic_baseline_clear_24));
|
||||||
getContext().getResources().getDrawable(R.drawable.ic_baseline_clear_24));
|
|
||||||
toolbar.inflateMenu(R.menu.conversation_list_search);
|
toolbar.inflateMenu(R.menu.conversation_list_search);
|
||||||
|
|
||||||
this.searchItem = toolbar.getMenu().findItem(R.id.action_filter_search);
|
this.searchItem = toolbar.getMenu().findItem(R.id.action_filter_search);
|
||||||
@ -56,8 +55,8 @@ public class SearchToolbar extends LinearLayout {
|
|||||||
|
|
||||||
searchView.setSubmitButtonEnabled(false);
|
searchView.setSubmitButtonEnabled(false);
|
||||||
|
|
||||||
if (searchText != null) searchText.setHint(R.string.SearchToolbar_search);
|
if (searchText != null) searchText.setHint(R.string.search);
|
||||||
else searchView.setQueryHint(getResources().getString(R.string.SearchToolbar_search));
|
else searchView.setQueryHint(getResources().getString(R.string.search));
|
||||||
|
|
||||||
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -8,7 +8,7 @@ import android.view.View;
|
|||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView;
|
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.Slide;
|
||||||
import org.thoughtcrime.securesms.mms.SlideClickListener;
|
import org.thoughtcrime.securesms.mms.SlideClickListener;
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
package org.thoughtcrime.securesms.components;
|
package org.thoughtcrime.securesms.components;
|
||||||
|
|
||||||
|
import static org.session.libsession.utilities.StringSubstitutionConstants.APP_NAME_KEY;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
import androidx.preference.CheckBoxPreference;
|
import androidx.preference.CheckBoxPreference;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
|
import com.squareup.phrase.Phrase;
|
||||||
import network.loki.messenger.R;
|
import network.loki.messenger.R;
|
||||||
|
|
||||||
public class SwitchPreferenceCompat extends CheckBoxPreference {
|
public class SwitchPreferenceCompat extends CheckBoxPreference {
|
||||||
|
|
||||||
|
private static String LOCK_SCREEN_KEY = "pref_android_screen_lock";
|
||||||
|
|
||||||
private Preference.OnPreferenceClickListener listener;
|
private Preference.OnPreferenceClickListener listener;
|
||||||
|
|
||||||
public SwitchPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr) {
|
public SwitchPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
@ -34,6 +37,19 @@ public class SwitchPreferenceCompat extends CheckBoxPreference {
|
|||||||
|
|
||||||
private void setLayoutRes() {
|
private void setLayoutRes() {
|
||||||
setWidgetLayoutResource(R.layout.switch_compat_preference);
|
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
|
@Override
|
||||||
|
@ -1,36 +1,33 @@
|
|||||||
package org.thoughtcrime.securesms.components;
|
package org.thoughtcrime.securesms.components;
|
||||||
|
|
||||||
|
import static org.session.libsession.utilities.StringSubstitutionConstants.COUNT_KEY;
|
||||||
|
|
||||||
import android.animation.LayoutTransition;
|
import android.animation.LayoutTransition;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.core.content.ContextCompat;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
import com.annimon.stream.Stream;
|
import com.annimon.stream.Stream;
|
||||||
import com.pnikosis.materialishprogress.ProgressWheel;
|
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.EventBus;
|
||||||
import org.greenrobot.eventbus.Subscribe;
|
import org.greenrobot.eventbus.Subscribe;
|
||||||
import org.greenrobot.eventbus.ThreadMode;
|
import org.greenrobot.eventbus.ThreadMode;
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
|
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress;
|
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.events.PartProgressEvent;
|
||||||
import org.thoughtcrime.securesms.mms.Slide;
|
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 {
|
public class TransferControlView extends FrameLayout {
|
||||||
|
|
||||||
@Nullable private List<Slide> slides;
|
@Nullable private List<Slide> slides;
|
||||||
@ -191,7 +188,8 @@ public class TransferControlView extends FrameLayout {
|
|||||||
return slides.get(0).getContentDescription();
|
return slides.get(0).getContentDescription();
|
||||||
} else {
|
} else {
|
||||||
int downloadCount = Stream.of(slides).reduce(0, (count, slide) -> slide.getTransferState() != AttachmentTransferProgress.TRANSFER_PROGRESS_DONE ? count + 1 : count);
|
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.AttachmentBitmapDecoder;
|
||||||
import org.thoughtcrime.securesms.components.subsampling.AttachmentRegionDecoder;
|
import org.thoughtcrime.securesms.components.subsampling.AttachmentRegionDecoder;
|
||||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
|
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.mms.PartAuthority;
|
||||||
import org.thoughtcrime.securesms.util.BitmapDecodingException;
|
import org.thoughtcrime.securesms.util.BitmapDecodingException;
|
||||||
import org.thoughtcrime.securesms.util.BitmapUtil;
|
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||||
@ -62,7 +62,7 @@ public class ZoomingImageView extends FrameLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("StaticFieldLeak")
|
@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 Context context = getContext();
|
||||||
final int maxTextureSize = BitmapUtil.getMaxTextureSize();
|
final int maxTextureSize = BitmapUtil.getMaxTextureSize();
|
||||||
@ -97,7 +97,7 @@ public class ZoomingImageView extends FrameLayout {
|
|||||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
}.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);
|
photoView.setVisibility(View.VISIBLE);
|
||||||
subsamplingImageView.setVisibility(View.GONE);
|
subsamplingImageView.setVisibility(View.GONE);
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import android.widget.ImageView;
|
|||||||
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.components.emoji.EmojiPageViewGridAdapter.VariationSelectorListener;
|
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.thoughtcrime.securesms.util.ResUtil;
|
||||||
|
|
||||||
import org.session.libsession.utilities.ThemeUtil;
|
import org.session.libsession.utilities.ThemeUtil;
|
||||||
@ -87,7 +87,7 @@ public class EmojiKeyboardProvider implements MediaKeyboardProvider,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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());
|
Drawable drawable = ResUtil.getDrawable(context, models.get(index).getIconAttr());
|
||||||
imageView.setImageDrawable(drawable);
|
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.InputAwareLayout.InputView;
|
||||||
import org.thoughtcrime.securesms.components.RepeatableImageKey;
|
import org.thoughtcrime.securesms.components.RepeatableImageKey;
|
||||||
import org.session.libsignal.utilities.Log;
|
import org.session.libsignal.utilities.Log;
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
import com.bumptech.glide.Glide;
|
||||||
|
|
||||||
import java.util.Arrays;
|
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.searchButton = view.findViewById(R.id.media_keyboard_search);
|
||||||
this.addButton = view.findViewById(R.id.media_keyboard_add);
|
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.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false));
|
||||||
categoryTabs.setAdapter(categoryTabAdapter);
|
categoryTabs.setAdapter(categoryTabAdapter);
|
||||||
|
@ -9,20 +9,20 @@ import android.widget.ImageView;
|
|||||||
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.components.emoji.MediaKeyboardProvider.TabIconProvider;
|
import org.thoughtcrime.securesms.components.emoji.MediaKeyboardProvider.TabIconProvider;
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
import com.bumptech.glide.RequestManager;
|
||||||
|
|
||||||
import network.loki.messenger.R;
|
import network.loki.messenger.R;
|
||||||
|
|
||||||
public class MediaKeyboardBottomTabAdapter extends RecyclerView.Adapter<MediaKeyboardBottomTabAdapter.MediaKeyboardBottomTabViewHolder> {
|
public class MediaKeyboardBottomTabAdapter extends RecyclerView.Adapter<MediaKeyboardBottomTabAdapter.MediaKeyboardBottomTabViewHolder> {
|
||||||
|
|
||||||
private final GlideRequests glideRequests;
|
private final RequestManager glideRequests;
|
||||||
private final EventListener eventListener;
|
private final EventListener eventListener;
|
||||||
|
|
||||||
private TabIconProvider tabIconProvider;
|
private TabIconProvider tabIconProvider;
|
||||||
private int activePosition;
|
private int activePosition;
|
||||||
private int count;
|
private int count;
|
||||||
|
|
||||||
public MediaKeyboardBottomTabAdapter(@NonNull GlideRequests glideRequests, @NonNull EventListener eventListener) {
|
public MediaKeyboardBottomTabAdapter(@NonNull RequestManager glideRequests, @NonNull EventListener eventListener) {
|
||||||
this.glideRequests = glideRequests;
|
this.glideRequests = glideRequests;
|
||||||
this.eventListener = eventListener;
|
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);
|
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 EventListener eventListener,
|
||||||
@NonNull TabIconProvider tabIconProvider,
|
@NonNull TabIconProvider tabIconProvider,
|
||||||
int index,
|
int index,
|
||||||
|
@ -8,7 +8,7 @@ import android.widget.ImageView;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
import com.bumptech.glide.RequestManager;
|
||||||
|
|
||||||
public interface MediaKeyboardProvider {
|
public interface MediaKeyboardProvider {
|
||||||
@LayoutRes int getProviderIconView(boolean selected);
|
@LayoutRes int getProviderIconView(boolean selected);
|
||||||
@ -48,6 +48,6 @@ public interface MediaKeyboardProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface TabIconProvider {
|
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.contains(TextSecurePreferences.getLocalNumber(context)))
|
||||||
// {
|
// {
|
||||||
// numberList.add(TextSecurePreferences.getLocalNumber(context));
|
// numberList.add(TextSecurePreferences.getLocalNumber(context));
|
||||||
|
@ -6,10 +6,10 @@ import android.view.ViewGroup
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import network.loki.messenger.databinding.ContactSelectionListDividerBinding
|
import network.loki.messenger.databinding.ContactSelectionListDividerBinding
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
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>() {
|
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>()
|
val selectedContacts = mutableSetOf<Recipient>()
|
||||||
var items = listOf<ContactSelectionListItem>()
|
var items = listOf<ContactSelectionListItem>()
|
||||||
set(value) { field = value; notifyDataSetChanged() }
|
set(value) { field = value; notifyDataSetChanged() }
|
||||||
|
@ -11,7 +11,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||||||
import network.loki.messenger.databinding.ContactSelectionListFragmentBinding
|
import network.loki.messenger.databinding.ContactSelectionListFragmentBinding
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.session.libsignal.utilities.Log
|
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 {
|
class ContactSelectionListFragment : Fragment(), LoaderManager.LoaderCallbacks<List<ContactSelectionListItem>>, ContactClickListener {
|
||||||
private lateinit var binding: ContactSelectionListFragmentBinding
|
private lateinit var binding: ContactSelectionListFragmentBinding
|
||||||
@ -27,7 +27,7 @@ class ContactSelectionListFragment : Fragment(), LoaderManager.LoaderCallbacks<L
|
|||||||
|
|
||||||
private val listAdapter by lazy {
|
private val listAdapter by lazy {
|
||||||
val result = ContactSelectionListAdapter(requireActivity(), multiSelect)
|
val result = ContactSelectionListAdapter(requireActivity(), multiSelect)
|
||||||
result.glide = GlideApp.with(this)
|
result.glide = Glide.with(this)
|
||||||
result.contactClickListener = this
|
result.contactClickListener = this
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ class ContactSelectionListLoader(context: Context, val mode: Int, val filter: St
|
|||||||
list.addAll(getClosedGroups(contacts))
|
list.addAll(getClosedGroups(contacts))
|
||||||
}
|
}
|
||||||
if (isFlagSet(DisplayMode.FLAG_OPEN_GROUPS)) {
|
if (isFlagSet(DisplayMode.FLAG_OPEN_GROUPS)) {
|
||||||
list.addAll(getOpenGroups(contacts))
|
list.addAll(getCommunities(contacts))
|
||||||
}
|
}
|
||||||
if (isFlagSet(DisplayMode.FLAG_CONTACTS)) {
|
if (isFlagSet(DisplayMode.FLAG_CONTACTS)) {
|
||||||
list.addAll(getContacts(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> {
|
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
|
!it.isGroupRecipient
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getClosedGroups(contacts: List<Recipient>): List<ContactSelectionListItem> {
|
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
|
it.address.isClosedGroup
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getOpenGroups(contacts: List<Recipient>): List<ContactSelectionListItem> {
|
private fun getCommunities(contacts: List<Recipient>): List<ContactSelectionListItem> {
|
||||||
return getItems(contacts, context.getString(R.string.fragment_contact_selection_open_groups_title)) {
|
return getItems(contacts, context.getString(R.string.conversationsCommunities)) {
|
||||||
it.address.isCommunity
|
it.address.isCommunity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,10 +18,10 @@ public final class ContactUtil {
|
|||||||
String contactName = ContactUtil.getDisplayName(contact);
|
String contactName = ContactUtil.getDisplayName(contact);
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(contactName)) {
|
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) {
|
private static @NonNull String getDisplayName(@Nullable Contact contact) {
|
||||||
|
@ -159,7 +159,7 @@ public class ContactsCursorLoader extends CursorLoader {
|
|||||||
|
|
||||||
private Cursor getGroupsHeaderCursor() {
|
private Cursor getGroupsHeaderCursor() {
|
||||||
MatrixCursor groupHeader = new MatrixCursor(CONTACT_PROJECTION, 1);
|
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,
|
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
|
||||||
"",
|
"",
|
||||||
@ -221,16 +221,6 @@ public class ContactsCursorLoader extends CursorLoader {
|
|||||||
return groupContacts;
|
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) {
|
private static boolean isCursorListEmpty(List<Cursor> list) {
|
||||||
int sum = 0;
|
int sum = 0;
|
||||||
for (Cursor cursor : list) {
|
for (Cursor cursor : list) {
|
||||||
|
@ -12,7 +12,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import network.loki.messenger.databinding.ActivitySelectContactsBinding
|
import network.loki.messenger.databinding.ActivitySelectContactsBinding
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp
|
import com.bumptech.glide.Glide
|
||||||
|
|
||||||
class SelectContactsActivity : PassphraseRequiredActionBarActivity(), LoaderManager.LoaderCallbacks<List<String>> {
|
class SelectContactsActivity : PassphraseRequiredActionBarActivity(), LoaderManager.LoaderCallbacks<List<String>> {
|
||||||
private lateinit var binding: ActivitySelectContactsBinding
|
private lateinit var binding: ActivitySelectContactsBinding
|
||||||
@ -21,7 +21,7 @@ class SelectContactsActivity : PassphraseRequiredActionBarActivity(), LoaderMana
|
|||||||
private lateinit var usersToExclude: Set<String>
|
private lateinit var usersToExclude: Set<String>
|
||||||
|
|
||||||
private val selectContactsAdapter by lazy {
|
private val selectContactsAdapter by lazy {
|
||||||
SelectContactsAdapter(this, GlideApp.with(this))
|
SelectContactsAdapter(this, Glide.with(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -35,7 +35,7 @@ class SelectContactsActivity : PassphraseRequiredActionBarActivity(), LoaderMana
|
|||||||
super.onCreate(savedInstanceState, isReady)
|
super.onCreate(savedInstanceState, isReady)
|
||||||
binding = ActivitySelectContactsBinding.inflate(layoutInflater)
|
binding = ActivitySelectContactsBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
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()
|
usersToExclude = intent.getStringArrayExtra(usersToExcludeKey)?.toSet() ?: setOf()
|
||||||
val emptyStateText = intent.getStringExtra(emptyStateTextKey)
|
val emptyStateText = intent.getStringExtra(emptyStateTextKey)
|
||||||
|
@ -4,10 +4,10 @@ import android.content.Context
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import org.session.libsession.utilities.Address
|
import org.session.libsession.utilities.Address
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import com.bumptech.glide.RequestManager
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
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>()
|
val selectedMembers = mutableSetOf<String>()
|
||||||
var members = listOf<String>()
|
var members = listOf<String>()
|
||||||
set(value) { field = value; notifyDataSetChanged() }
|
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.messaging.contacts.Contact
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import com.bumptech.glide.RequestManager
|
||||||
|
|
||||||
class UserView : LinearLayout {
|
class UserView : LinearLayout {
|
||||||
private lateinit var binding: ViewUserBinding
|
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 {
|
enum class ActionIndicator {
|
||||||
None,
|
None,
|
||||||
@ -45,13 +45,15 @@ class UserView : LinearLayout {
|
|||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Updating
|
// 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
|
val isLocalUser = user.isLocalNumber
|
||||||
|
|
||||||
fun getUserDisplayName(publicKey: String): String {
|
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)
|
val contact = DatabaseComponent.get(context).sessionContactDatabase().getContactWithAccountID(publicKey)
|
||||||
return contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey
|
return contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
val address = user.address.serialize()
|
val address = user.address.serialize()
|
||||||
binding.profilePictureView.update(user)
|
binding.profilePictureView.update(user)
|
||||||
binding.actionIndicatorImageView.setImageResource(R.drawable.ic_baseline_edit_24)
|
binding.actionIndicatorImageView.setImageResource(R.drawable.ic_baseline_edit_24)
|
||||||
@ -84,8 +86,7 @@ class UserView : LinearLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unbind() {
|
fun unbind() { binding.profilePictureView.recycle() }
|
||||||
binding.profilePictureView.recycle()
|
|
||||||
}
|
|
||||||
// endregion
|
// endregion
|
||||||
}
|
}
|
||||||
|
@ -11,21 +11,26 @@ import androidx.recyclerview.widget.ListAdapter
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
|
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
|
||||||
import com.google.android.material.tabs.TabLayoutMediator
|
import com.google.android.material.tabs.TabLayoutMediator
|
||||||
|
import com.squareup.phrase.Phrase
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import network.loki.messenger.databinding.ViewConversationActionBarBinding
|
import network.loki.messenger.databinding.ViewConversationActionBarBinding
|
||||||
import network.loki.messenger.databinding.ViewConversationSettingBinding
|
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.messages.ExpirationConfiguration
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroup
|
import org.session.libsession.messaging.open_groups.OpenGroup
|
||||||
import org.session.libsession.utilities.ExpirationUtil
|
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.modifyLayoutParams
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase
|
import org.thoughtcrime.securesms.database.GroupDatabase
|
||||||
import org.thoughtcrime.securesms.database.LokiAPIDatabase
|
import org.thoughtcrime.securesms.database.LokiAPIDatabase
|
||||||
import org.thoughtcrime.securesms.util.DateUtils
|
|
||||||
import java.util.Locale
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class ConversationActionBarView @JvmOverloads constructor(
|
class ConversationActionBarView @JvmOverloads constructor(
|
||||||
@ -82,7 +87,7 @@ class ConversationActionBarView @JvmOverloads constructor(
|
|||||||
|
|
||||||
fun update(recipient: Recipient, openGroup: OpenGroup? = null, config: ExpirationConfiguration? = null) {
|
fun update(recipient: Recipient, openGroup: OpenGroup? = null, config: ExpirationConfiguration? = null) {
|
||||||
binding.profilePictureView.update(recipient)
|
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)
|
updateSubtitle(recipient, openGroup, config)
|
||||||
|
|
||||||
binding.conversationTitleContainer.modifyLayoutParams<MarginLayoutParams> {
|
binding.conversationTitleContainer.modifyLayoutParams<MarginLayoutParams> {
|
||||||
@ -92,37 +97,58 @@ class ConversationActionBarView @JvmOverloads constructor(
|
|||||||
|
|
||||||
fun updateSubtitle(recipient: Recipient, openGroup: OpenGroup? = null, config: ExpirationConfiguration? = null) {
|
fun updateSubtitle(recipient: Recipient, openGroup: OpenGroup? = null, config: ExpirationConfiguration? = null) {
|
||||||
val settings = mutableListOf<ConversationSetting>()
|
val settings = mutableListOf<ConversationSetting>()
|
||||||
|
|
||||||
|
// Specify the disappearing messages subtitle if we should
|
||||||
if (config?.isEnabled == true) {
|
if (config?.isEnabled == true) {
|
||||||
val prefix = when (config.expiryMode) {
|
// Get the type of disappearing message and the abbreviated duration..
|
||||||
is ExpiryMode.AfterRead -> R.string.expiration_type_disappear_after_read
|
val dmTypeString = when (config.expiryMode) {
|
||||||
else -> R.string.expiration_type_disappear_after_send
|
is AfterRead -> context.getString(R.string.read)
|
||||||
}.let(context::getString)
|
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(
|
settings += ConversationSetting(
|
||||||
"$prefix - ${ExpirationUtil.getExpirationAbbreviatedDisplayValue(context, config.expiryMode.expirySeconds)}",
|
subtitleTxt,
|
||||||
ConversationSettingType.EXPIRATION,
|
ConversationSettingType.EXPIRATION,
|
||||||
R.drawable.ic_timer,
|
R.drawable.ic_timer,
|
||||||
resources.getString(R.string.AccessibilityId_disappearing_messages_type_and_time)
|
resources.getString(R.string.AccessibilityId_disappearing_messages_type_and_time)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recipient.isMuted) {
|
if (recipient.isMuted) {
|
||||||
settings += ConversationSetting(
|
settings += ConversationSetting(
|
||||||
recipient.mutedUntil.takeUnless { it == Long.MAX_VALUE }
|
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())) }
|
?.let {
|
||||||
?: context.getString(R.string.ConversationActivity_muted_forever),
|
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,
|
ConversationSettingType.NOTIFICATION,
|
||||||
R.drawable.ic_outline_notifications_off_24
|
R.drawable.ic_outline_notifications_off_24
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recipient.isGroupRecipient) {
|
if (recipient.isGroupRecipient) {
|
||||||
val title = if (recipient.isCommunityRecipient) {
|
val title = if (recipient.isCommunityRecipient) {
|
||||||
val userCount = openGroup?.let { lokiApiDb.getUserCount(it.room, it.server) } ?: 0
|
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 {
|
} else {
|
||||||
val userCount = groupDb.getGroupMemberAddresses(recipient.address.toGroupString(), true).size
|
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)
|
settings += ConversationSetting(title, ConversationSettingType.MEMBER_COUNT)
|
||||||
}
|
}
|
||||||
|
|
||||||
settingsAdapter.submitList(settings)
|
settingsAdapter.submitList(settings)
|
||||||
binding.settingsTabLayout.isVisible = settings.size > 1
|
binding.settingsTabLayout.isVisible = settings.size > 1
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package org.thoughtcrime.securesms.conversation.disappearingmessages
|
package org.thoughtcrime.securesms.conversation.disappearingmessages
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import com.squareup.phrase.Phrase
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
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.R
|
||||||
import network.loki.messenger.libsession_util.util.ExpiryMode
|
import network.loki.messenger.libsession_util.util.ExpiryMode
|
||||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
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.Address
|
||||||
import org.session.libsession.utilities.ExpirationUtil
|
import org.session.libsession.utilities.ExpirationUtil
|
||||||
import org.session.libsession.utilities.SSKEnvironment.MessageExpirationManagerProtocol
|
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.TextSecurePreferences
|
||||||
import org.session.libsession.utilities.getExpirationTypeDisplayValue
|
import org.session.libsession.utilities.getExpirationTypeDisplayValue
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||||
import org.thoughtcrime.securesms.showSessionDialog
|
import org.thoughtcrime.securesms.showSessionDialog
|
||||||
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
|
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
|
||||||
import javax.inject.Inject
|
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
|
||||||
|
|
||||||
class DisappearingMessages @Inject constructor(
|
class DisappearingMessages @Inject constructor(
|
||||||
@ApplicationContext private val context: Context,
|
@ApplicationContext private val context: Context,
|
||||||
@ -43,21 +46,18 @@ class DisappearingMessages @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun showFollowSettingDialog(context: Context, message: MessageRecord) = context.showSessionDialog {
|
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) {
|
text(if (message.expiresIn == 0L) {
|
||||||
context.getString(R.string.dialog_disappearing_messages_follow_setting_off_body)
|
context.getString(R.string.disappearingMessagesFollowSettingOff)
|
||||||
} else {
|
} else {
|
||||||
context.getString(
|
Phrase.from(context, R.string.disappearingMessagesFollowSettingOn)
|
||||||
R.string.dialog_disappearing_messages_follow_setting_on_body,
|
.put(TIME_LARGE_KEY, ExpirationUtil.getExpirationDisplayValue(context, message.expiresIn.milliseconds))
|
||||||
ExpirationUtil.getExpirationDisplayValue(
|
.put(DISAPPEARING_MESSAGES_TYPE_KEY, context.getExpirationTypeDisplayValue(message.isNotDisappearAfterRead))
|
||||||
context,
|
.format().toString()
|
||||||
message.expiresIn.milliseconds
|
|
||||||
),
|
|
||||||
context.getExpirationTypeDisplayValue(message.isNotDisappearAfterRead)
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
dangerButton(
|
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
|
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)
|
set(message.threadId, message.recipient.address, message.expiryMode, message.recipient.isClosedGroupRecipient)
|
||||||
|
@ -72,7 +72,7 @@ class DisappearingMessagesActivity: PassphraseRequiredActionBarActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setUpToolbar() {
|
private fun setUpToolbar() {
|
||||||
setSupportActionBar(binding.toolbar)
|
setSupportActionBar(binding.searchToolbar)
|
||||||
supportActionBar?.apply {
|
supportActionBar?.apply {
|
||||||
title = getString(R.string.activity_disappearing_messages_title)
|
title = getString(R.string.activity_disappearing_messages_title)
|
||||||
setDisplayHomeAsUpEnabled(true)
|
setDisplayHomeAsUpEnabled(true)
|
||||||
|
@ -51,7 +51,7 @@ enum class ExpiryType(
|
|||||||
) {
|
) {
|
||||||
NONE(
|
NONE(
|
||||||
{ ExpiryMode.NONE },
|
{ ExpiryMode.NONE },
|
||||||
R.string.expiration_off,
|
R.string.off,
|
||||||
contentDescription = R.string.AccessibilityId_disable_disappearing_messages,
|
contentDescription = R.string.AccessibilityId_disable_disappearing_messages,
|
||||||
),
|
),
|
||||||
LEGACY(
|
LEGACY(
|
||||||
@ -61,14 +61,14 @@ enum class ExpiryType(
|
|||||||
),
|
),
|
||||||
AFTER_READ(
|
AFTER_READ(
|
||||||
ExpiryMode::AfterRead,
|
ExpiryMode::AfterRead,
|
||||||
R.string.expiration_type_disappear_after_read,
|
R.string.disappearingMessagesDisappearAfterRead,
|
||||||
R.string.expiration_type_disappear_after_read_description,
|
R.string.disappearingMessagesDisappearAfterReadDescription,
|
||||||
R.string.AccessibilityId_disappear_after_read_option
|
R.string.AccessibilityId_disappear_after_read_option
|
||||||
),
|
),
|
||||||
AFTER_SEND(
|
AFTER_SEND(
|
||||||
ExpiryMode::AfterSend,
|
ExpiryMode::AfterSend,
|
||||||
R.string.expiration_type_disappear_after_send,
|
R.string.disappearingMessagesDisappearAfterSend,
|
||||||
R.string.expiration_type_disappear_after_send_description,
|
R.string.disappearingMessagesDisappearAfterSendDescription,
|
||||||
R.string.AccessibilityId_disappear_after_send_option
|
R.string.AccessibilityId_disappear_after_send_option
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import kotlin.time.Duration.Companion.seconds
|
|||||||
|
|
||||||
fun State.toUiState() = UiState(
|
fun State.toUiState() = UiState(
|
||||||
cards = listOfNotNull(
|
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) }
|
timeOptions()?.let { ExpiryOptionsCardData(GetString(R.string.activity_disappearing_messages_timer), it) }
|
||||||
),
|
),
|
||||||
showGroupFooter = isGroup && isNewConfigEnabled,
|
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 debugTimes(isDebug: Boolean) = if (isDebug) listOf(10.seconds, 30.seconds, 1.minutes) else emptyList()
|
||||||
|
|
||||||
private fun debugModes(isDebug: Boolean, type: ExpiryType) =
|
private fun debugModes(isDebug: Boolean, type: ExpiryType) =
|
||||||
debugTimes(isDebug).map { type.mode(it.inWholeSeconds) }
|
debugTimes(isDebug).map { type.mode(it.inWholeSeconds) }
|
||||||
|
|
||||||
private fun State.debugOptions(): List<ExpiryRadioOption> =
|
private fun State.debugOptions(): List<ExpiryRadioOption> =
|
||||||
debugModes(showDebugOptions, nextType).map { timeOption(it, subtitle = GetString("for testing purposes")) }
|
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 afterSendTimes = listOf(12.hours, 1.days, 7.days, 14.days)
|
||||||
|
|
||||||
private val afterReadTimes = buildList {
|
private val afterReadTimes = buildList {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.thoughtcrime.securesms.conversation.disappearingmessages.ui
|
package org.thoughtcrime.securesms.conversation.disappearingmessages.ui
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
@ -16,21 +15,20 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import network.loki.messenger.libsession_util.util.ExpiryMode
|
import network.loki.messenger.libsession_util.util.ExpiryMode
|
||||||
import org.thoughtcrime.securesms.ui.Callbacks
|
import org.thoughtcrime.securesms.ui.Callbacks
|
||||||
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
|
|
||||||
import org.thoughtcrime.securesms.ui.NoOpCallbacks
|
import org.thoughtcrime.securesms.ui.NoOpCallbacks
|
||||||
import org.thoughtcrime.securesms.ui.OptionsCard
|
import org.thoughtcrime.securesms.ui.OptionsCard
|
||||||
import org.thoughtcrime.securesms.ui.RadioOption
|
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.components.SlimOutlineButton
|
||||||
import org.thoughtcrime.securesms.ui.contentDescription
|
import org.thoughtcrime.securesms.ui.contentDescription
|
||||||
import org.thoughtcrime.securesms.ui.fadingEdges
|
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
|
import org.thoughtcrime.securesms.ui.theme.LocalType
|
||||||
|
|
||||||
typealias ExpiryCallbacks = Callbacks<ExpiryMode>
|
typealias ExpiryCallbacks = Callbacks<ExpiryMode>
|
||||||
typealias ExpiryRadioOption = RadioOption<ExpiryMode>
|
typealias ExpiryRadioOption = RadioOption<ExpiryMode>
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -59,7 +57,9 @@ fun DisappearingMessages(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (state.showGroupFooter) Text(
|
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,
|
style = LocalType.current.extraSmall,
|
||||||
fontWeight = FontWeight(400),
|
fontWeight = FontWeight(400),
|
||||||
color = LocalColors.current.textSecondary,
|
color = LocalColors.current.textSecondary,
|
||||||
@ -72,7 +72,7 @@ fun DisappearingMessages(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (state.showSetButton) SlimOutlineButton(
|
if (state.showSetButton) SlimOutlineButton(
|
||||||
stringResource(R.string.disappearing_messages_set_button_title),
|
stringResource(R.string.set),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.contentDescription(R.string.AccessibilityId_set_button)
|
.contentDescription(R.string.AccessibilityId_set_button)
|
||||||
.align(Alignment.CenterHorizontally)
|
.align(Alignment.CenterHorizontally)
|
||||||
|
@ -41,7 +41,7 @@ internal fun StartConversationScreen(
|
|||||||
LocalColors.current.backgroundSecondary,
|
LocalColors.current.backgroundSecondary,
|
||||||
shape = MaterialTheme.shapes.small
|
shape = MaterialTheme.shapes.small
|
||||||
)) {
|
)) {
|
||||||
AppBar(stringResource(R.string.dialog_start_conversation_title), onClose = delegate::onDialogClosePressed)
|
AppBar(stringResource(R.string.conversationsStart), onClose = delegate::onDialogClosePressed)
|
||||||
Surface(
|
Surface(
|
||||||
modifier = Modifier.nestedScroll(rememberNestedScrollInteropConnection()),
|
modifier = Modifier.nestedScroll(rememberNestedScrollInteropConnection()),
|
||||||
color = LocalColors.current.backgroundSecondary
|
color = LocalColors.current.backgroundSecondary
|
||||||
@ -56,21 +56,21 @@ internal fun StartConversationScreen(
|
|||||||
onClick = delegate::onNewMessageSelected)
|
onClick = delegate::onNewMessageSelected)
|
||||||
Divider(startIndent = LocalDimensions.current.dividerIndent)
|
Divider(startIndent = LocalDimensions.current.dividerIndent)
|
||||||
ItemButton(
|
ItemButton(
|
||||||
textId = R.string.activity_create_group_title,
|
textId = R.string.groupCreate,
|
||||||
icon = R.drawable.ic_group,
|
icon = R.drawable.ic_group,
|
||||||
modifier = Modifier.contentDescription(R.string.AccessibilityId_create_group),
|
modifier = Modifier.contentDescription(R.string.AccessibilityId_create_group),
|
||||||
onClick = delegate::onCreateGroupSelected
|
onClick = delegate::onCreateGroupSelected
|
||||||
)
|
)
|
||||||
Divider(startIndent = LocalDimensions.current.dividerIndent)
|
Divider(startIndent = LocalDimensions.current.dividerIndent)
|
||||||
ItemButton(
|
ItemButton(
|
||||||
textId = R.string.dialog_join_community_title,
|
textId = R.string.communityJoin,
|
||||||
icon = R.drawable.ic_globe,
|
icon = R.drawable.ic_globe,
|
||||||
modifier = Modifier.contentDescription(R.string.AccessibilityId_join_community),
|
modifier = Modifier.contentDescription(R.string.AccessibilityId_join_community),
|
||||||
onClick = delegate::onJoinCommunitySelected
|
onClick = delegate::onJoinCommunitySelected
|
||||||
)
|
)
|
||||||
Divider(startIndent = LocalDimensions.current.dividerIndent)
|
Divider(startIndent = LocalDimensions.current.dividerIndent)
|
||||||
ItemButton(
|
ItemButton(
|
||||||
textId = R.string.activity_settings_invite_button_title,
|
textId = R.string.sessionInviteAFriend,
|
||||||
icon = R.drawable.ic_invite_friend,
|
icon = R.drawable.ic_invite_friend,
|
||||||
Modifier.contentDescription(R.string.AccessibilityId_invite_friend_button),
|
Modifier.contentDescription(R.string.AccessibilityId_invite_friend_button),
|
||||||
onClick = delegate::onInviteFriend
|
onClick = delegate::onInviteFriend
|
||||||
|
@ -16,15 +16,15 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import network.loki.messenger.R
|
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.AppBar
|
||||||
import org.thoughtcrime.securesms.ui.components.SlimOutlineButton
|
import org.thoughtcrime.securesms.ui.components.SlimOutlineButton
|
||||||
import org.thoughtcrime.securesms.ui.components.SlimOutlineCopyButton
|
import org.thoughtcrime.securesms.ui.components.SlimOutlineCopyButton
|
||||||
import org.thoughtcrime.securesms.ui.components.border
|
import org.thoughtcrime.securesms.ui.components.border
|
||||||
import org.thoughtcrime.securesms.ui.contentDescription
|
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.LocalType
|
||||||
|
import org.thoughtcrime.securesms.ui.theme.PreviewTheme
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun InviteFriend(
|
internal fun InviteFriend(
|
||||||
@ -38,7 +38,11 @@ internal fun InviteFriend(
|
|||||||
LocalColors.current.backgroundSecondary,
|
LocalColors.current.backgroundSecondary,
|
||||||
shape = MaterialTheme.shapes.small
|
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(
|
Column(
|
||||||
modifier = Modifier.padding(horizontal = LocalDimensions.current.spacing)
|
modifier = Modifier.padding(horizontal = LocalDimensions.current.spacing)
|
||||||
.padding(top = LocalDimensions.current.spacing),
|
.padding(top = LocalDimensions.current.spacing),
|
||||||
@ -57,7 +61,7 @@ internal fun InviteFriend(
|
|||||||
Spacer(modifier = Modifier.height(LocalDimensions.current.xsSpacing))
|
Spacer(modifier = Modifier.height(LocalDimensions.current.xsSpacing))
|
||||||
|
|
||||||
Text(
|
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,
|
textAlign = TextAlign.Center,
|
||||||
style = LocalType.current.small,
|
style = LocalType.current.small,
|
||||||
color = LocalColors.current.textSecondary,
|
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.contentDescription
|
||||||
import org.thoughtcrime.securesms.ui.theme.LocalType
|
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)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
@ -63,7 +63,7 @@ internal fun NewMessage(
|
|||||||
SessionTabRow(pagerState, TITLES)
|
SessionTabRow(pagerState, TITLES)
|
||||||
HorizontalPager(pagerState) {
|
HorizontalPager(pagerState) {
|
||||||
when (TITLES[it]) {
|
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)
|
R.string.qrScan -> MaybeScanQrCode(qrErrors, onScan = callbacks::onScanQrCode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,7 +101,7 @@ private fun EnterAccountId(
|
|||||||
Spacer(modifier = Modifier.height(LocalDimensions.current.xxxsSpacing))
|
Spacer(modifier = Modifier.height(LocalDimensions.current.xxxsSpacing))
|
||||||
|
|
||||||
BorderlessButtonWithIcon(
|
BorderlessButtonWithIcon(
|
||||||
text = stringResource(R.string.messageNewDescription),
|
text = stringResource(R.string.messageNewDescriptionMobile),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.contentDescription(R.string.AccessibilityId_help_desk_link)
|
.contentDescription(R.string.AccessibilityId_help_desk_link)
|
||||||
.padding(horizontal = LocalDimensions.current.mediumSpacing)
|
.padding(horizontal = LocalDimensions.current.mediumSpacing)
|
||||||
|
@ -4,6 +4,8 @@ import android.app.Application
|
|||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import java.util.concurrent.TimeoutException
|
||||||
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.channels.BufferOverflow
|
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.PublicKeyValidation
|
||||||
import org.session.libsignal.utilities.timeout
|
import org.session.libsignal.utilities.timeout
|
||||||
import org.thoughtcrime.securesms.ui.GetString
|
import org.thoughtcrime.securesms.ui.GetString
|
||||||
import java.util.concurrent.TimeoutException
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
internal class NewMessageViewModel @Inject constructor(
|
internal class NewMessageViewModel @Inject constructor(
|
||||||
@ -59,7 +59,7 @@ internal class NewMessageViewModel @Inject constructor(
|
|||||||
if (PublicKeyValidation.isValid(value, isPrefixRequired = false) && PublicKeyValidation.hasValidPrefix(value)) {
|
if (PublicKeyValidation.isValid(value, isPrefixRequired = false) && PublicKeyValidation.hasValidPrefix(value)) {
|
||||||
onPublicKey(value)
|
onPublicKey(value)
|
||||||
} else {
|
} 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) {
|
private fun Exception.toMessage() = when (this) {
|
||||||
is SnodeAPI.Error.Generic -> application.getString(R.string.onsErrorNotRecognized)
|
is SnodeAPI.Error.Generic -> application.getString(R.string.onsErrorNotRecognized)
|
||||||
is TimeoutException -> application.getString(R.string.onsErrorUnableToSearch)
|
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.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.text.SpannableStringBuilder
|
import android.text.SpannableString
|
||||||
import android.text.SpannedString
|
import android.text.Spanned
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.text.style.StyleSpan
|
import android.text.style.StyleSpan
|
||||||
import android.util.Pair
|
import android.util.Pair
|
||||||
@ -35,9 +35,9 @@ import android.widget.Toast
|
|||||||
import androidx.activity.result.ActivityResult
|
import androidx.activity.result.ActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
|
import androidx.core.view.drawToBitmap
|
||||||
import androidx.core.text.set
|
import androidx.core.text.set
|
||||||
import androidx.core.text.toSpannable
|
import androidx.core.text.toSpannable
|
||||||
import androidx.core.view.drawToBitmap
|
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
@ -52,7 +52,19 @@ import androidx.loader.content.Loader
|
|||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.annimon.stream.Stream
|
import com.annimon.stream.Stream
|
||||||
|
import com.squareup.phrase.Phrase
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
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.Dispatchers
|
||||||
import kotlinx.coroutines.channels.BufferOverflow
|
import kotlinx.coroutines.channels.BufferOverflow
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
@ -65,7 +77,6 @@ import network.loki.messenger.databinding.ActivityConversationV2Binding
|
|||||||
import network.loki.messenger.libsession_util.util.ExpiryMode
|
import network.loki.messenger.libsession_util.util.ExpiryMode
|
||||||
import nl.komponents.kovenant.ui.successUi
|
import nl.komponents.kovenant.ui.successUi
|
||||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
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.ExpirationConfiguration
|
||||||
import org.session.libsession.messaging.messages.applyExpiryMode
|
import org.session.libsession.messaging.messages.applyExpiryMode
|
||||||
import org.session.libsession.messaging.messages.control.DataExtractionNotification
|
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.Address.Companion.fromSerialized
|
||||||
import org.session.libsession.utilities.GroupUtil
|
import org.session.libsession.utilities.GroupUtil
|
||||||
import org.session.libsession.utilities.MediaTypes
|
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.Stub
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsession.utilities.concurrent.SimpleTask
|
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.mediasend.MediaSendActivity
|
||||||
import org.thoughtcrime.securesms.mms.AudioSlide
|
import org.thoughtcrime.securesms.mms.AudioSlide
|
||||||
import org.thoughtcrime.securesms.mms.GifSlide
|
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.ImageSlide
|
||||||
import org.thoughtcrime.securesms.mms.MediaConstraints
|
import org.thoughtcrime.securesms.mms.MediaConstraints
|
||||||
import org.thoughtcrime.securesms.mms.Slide
|
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.MediaUtil
|
||||||
import org.thoughtcrime.securesms.util.NetworkUtils
|
import org.thoughtcrime.securesms.util.NetworkUtils
|
||||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask
|
import org.thoughtcrime.securesms.util.SaveAttachmentTask
|
||||||
|
import org.thoughtcrime.securesms.util.drawToBitmap
|
||||||
import org.thoughtcrime.securesms.util.isScrolledToBottom
|
import org.thoughtcrime.securesms.util.isScrolledToBottom
|
||||||
import org.thoughtcrime.securesms.util.isScrolledToWithin30dpOfBottom
|
import org.thoughtcrime.securesms.util.isScrolledToWithin30dpOfBottom
|
||||||
import org.thoughtcrime.securesms.util.push
|
import org.thoughtcrime.securesms.util.push
|
||||||
import org.thoughtcrime.securesms.util.show
|
import org.thoughtcrime.securesms.util.show
|
||||||
import org.thoughtcrime.securesms.util.start
|
import org.thoughtcrime.securesms.util.start
|
||||||
import org.thoughtcrime.securesms.util.toPx
|
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"
|
private const val TAG = "ConversationActivityV2"
|
||||||
|
|
||||||
@ -349,7 +354,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
adapter
|
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 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 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) }
|
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?) {
|
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
|
return
|
||||||
@ -757,9 +762,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
// called from onCreate
|
// called from onCreate
|
||||||
private fun setUpBlockedBanner() {
|
private fun setUpBlockedBanner() {
|
||||||
val recipient = viewModel.recipient?.takeUnless { it.isGroupRecipient } ?: return
|
val recipient = viewModel.recipient?.takeUnless { it.isGroupRecipient } ?: return
|
||||||
val accountID = recipient.address.toString()
|
binding.blockedBannerTextView.text = applicationContext.getString(R.string.blockBlockedDescription)
|
||||||
val name = sessionContactDb.getContactWithAccountID(accountID)?.displayName(Contact.ContactContext.REGULAR) ?: accountID
|
|
||||||
binding.blockedBannerTextView.text = resources.getString(R.string.activity_conversation_blocked_banner_text, name)
|
|
||||||
binding.blockedBanner.isVisible = recipient.isBlocked
|
binding.blockedBanner.isVisible = recipient.isBlocked
|
||||||
binding.blockedBanner.setOnClickListener { viewModel.unblock() }
|
binding.blockedBanner.setOnClickListener { viewModel.unblock() }
|
||||||
}
|
}
|
||||||
@ -772,8 +775,11 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
|
|
||||||
binding.outdatedBanner.isVisible = shouldShowLegacy
|
binding.outdatedBanner.isVisible = shouldShowLegacy
|
||||||
if (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()
|
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() {
|
private fun updatePlaceholder() {
|
||||||
val recipient = viewModel.recipient ?: return Log.w("Loki", "recipient was null in placeholder update")
|
val recipient = viewModel.recipient ?: return Log.w("Loki", "recipient was null in placeholder update")
|
||||||
val blindedRecipient = viewModel.blindedRecipient
|
val blindedRecipient = viewModel.blindedRecipient
|
||||||
val openGroup = viewModel.openGroup
|
val openGroup = viewModel.openGroup
|
||||||
|
|
||||||
val (textResource, insertParam) = when {
|
// Get the correct placeholder text for this type of empty conversation
|
||||||
recipient.isLocalNumber -> R.string.activity_conversation_empty_state_note_to_self to null
|
val isNoteToSelf = recipient.isLocalNumber
|
||||||
openGroup != null && !openGroup.canWrite -> R.string.activity_conversation_empty_state_read_only to recipient.toShortString()
|
val txtCS: CharSequence = when {
|
||||||
blindedRecipient?.blocksCommunityMessageRequests == true -> R.string.activity_conversation_empty_state_blocks_community_requests to recipient.toShortString()
|
recipient.isLocalNumber -> getString(R.string.noteToSelfEmpty)
|
||||||
else -> R.string.activity_conversation_empty_state_default to recipient.toShortString()
|
|
||||||
|
// 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
|
val showPlaceholder = adapter.itemCount == 0
|
||||||
binding.placeholderText.isVisible = showPlaceholder
|
binding.placeholderText.isVisible = showPlaceholder
|
||||||
if (showPlaceholder) {
|
if (showPlaceholder) {
|
||||||
if (insertParam != null) {
|
if (!isNoteToSelf) {
|
||||||
val span = getText(textResource) as SpannedString
|
binding.placeholderText.text = makeBoldBetweenTags(txtCS)
|
||||||
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
|
|
||||||
} else {
|
} else {
|
||||||
binding.placeholderText.setText(textResource)
|
binding.placeholderText.text = txtCS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1117,11 +1154,21 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun block(deleteThread: Boolean) {
|
override fun block(deleteThread: Boolean) {
|
||||||
|
val recipient = viewModel.recipient ?: return Log.w("Loki", "Recipient was null for block action")
|
||||||
showSessionDialog {
|
showSessionDialog {
|
||||||
title(R.string.RecipientPreferenceActivity_block_this_contact_question)
|
title(R.string.block)
|
||||||
text(R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact)
|
text(
|
||||||
dangerButton(R.string.RecipientPreferenceActivity_block, R.string.AccessibilityId_block_confirm) {
|
Phrase.from(context, R.string.blockDescription)
|
||||||
|
.put(NAME_KEY, recipient.name)
|
||||||
|
.format()
|
||||||
|
)
|
||||||
|
dangerButton(R.string.block, R.string.AccessibilityId_block_confirm) {
|
||||||
viewModel.block()
|
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) {
|
if (deleteThread) {
|
||||||
viewModel.deleteThread()
|
viewModel.deleteThread()
|
||||||
finish()
|
finish()
|
||||||
@ -1135,7 +1182,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
val clip = ClipData.newPlainText("Account ID", accountId)
|
val clip = ClipData.newPlainText("Account ID", accountId)
|
||||||
val manager = getSystemService(PassphraseRequiredActionBarActivity.CLIPBOARD_SERVICE) as ClipboardManager
|
val manager = getSystemService(PassphraseRequiredActionBarActivity.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
manager.setPrimaryClip(clip)
|
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) {
|
override fun copyOpenGroupUrl(thread: Recipient) {
|
||||||
@ -1147,7 +1194,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
val clip = ClipData.newPlainText("Community URL", openGroup.joinURL)
|
val clip = ClipData.newPlainText("Community URL", openGroup.joinURL)
|
||||||
val manager = getSystemService(PassphraseRequiredActionBarActivity.CLIPBOARD_SERVICE) as ClipboardManager
|
val manager = getSystemService(PassphraseRequiredActionBarActivity.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
manager.setPrimaryClip(clip)
|
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) {
|
override fun showDisappearingMessages(thread: Recipient) {
|
||||||
@ -1160,13 +1207,31 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun unblock() {
|
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 {
|
showSessionDialog {
|
||||||
title(R.string.ConversationActivity_unblock_this_contact_question)
|
title(R.string.blockUnblock)
|
||||||
text(R.string.ConversationActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact)
|
text(
|
||||||
|
Phrase.from(context, R.string.blockUnblockName)
|
||||||
|
.put(NAME_KEY, recipient.name)
|
||||||
|
.format()
|
||||||
|
)
|
||||||
dangerButton(
|
dangerButton(
|
||||||
R.string.ConversationActivity_unblock,
|
R.string.blockUnblock,
|
||||||
R.string.AccessibilityId_block_confirm
|
R.string.AccessibilityId_unblock_confirm
|
||||||
) { viewModel.unblock() }
|
) {
|
||||||
|
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()
|
cancelButton()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1666,9 +1731,9 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
val hasSeenGIFMetaDataWarning: Boolean = textSecurePreferences.hasSeenGIFMetaDataWarning()
|
val hasSeenGIFMetaDataWarning: Boolean = textSecurePreferences.hasSeenGIFMetaDataWarning()
|
||||||
if (!hasSeenGIFMetaDataWarning) {
|
if (!hasSeenGIFMetaDataWarning) {
|
||||||
showSessionDialog {
|
showSessionDialog {
|
||||||
title(R.string.giphy_permission_title)
|
title(R.string.giphyWarning)
|
||||||
text(R.string.giphy_permission_message)
|
text(Phrase.from(context, R.string.giphyWarningDescription).put(APP_NAME_KEY, getString(R.string.app_name)).format())
|
||||||
button(R.string.continue_2) {
|
button(R.string.theContinue) {
|
||||||
textSecurePreferences.setHasSeenGIFMetaDataWarning()
|
textSecurePreferences.setHasSeenGIFMetaDataWarning()
|
||||||
selectGif()
|
selectGif()
|
||||||
}
|
}
|
||||||
@ -1715,7 +1780,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(e: ExecutionException?) {
|
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) {
|
when (requestCode) {
|
||||||
@ -1798,8 +1863,10 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
} else {
|
} else {
|
||||||
Permissions.with(this)
|
Permissions.with(this)
|
||||||
.request(Manifest.permission.RECORD_AUDIO)
|
.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)
|
.withRationaleDialog(getString(R.string.permissionsMicrophoneAccessRequired), R.drawable.ic_baseline_mic_48)
|
||||||
.withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_requires_the_microphone_permission_in_order_to_send_audio_messages))
|
.withPermanentDenialDialog(Phrase.from(applicationContext, R.string.permissionsMicrophoneAccessRequiredAndroid)
|
||||||
|
.put(APP_NAME_KEY, getString(R.string.app_name))
|
||||||
|
.format().toString())
|
||||||
.execute()
|
.execute()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1869,7 +1936,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(e: ExecutionException) {
|
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>) {
|
private fun showDeleteLocallyUI(messages: Set<MessageRecord>) {
|
||||||
val messageCount = 1
|
val titleStringId = if (messages.count() == 1) R.string.deleteMessage else R.string.deleteMessages
|
||||||
showSessionDialog {
|
showSessionDialog {
|
||||||
title(resources.getQuantityString(R.plurals.ConversationFragment_delete_selected_messages, messageCount, messageCount))
|
title(resources.getString(titleStringId))
|
||||||
text(resources.getQuantityString(R.plurals.ConversationFragment_this_will_permanently_delete_all_n_selected_messages, messageCount, messageCount))
|
text(resources.getString(R.string.deleteMessagesDescriptionDevice))
|
||||||
button(R.string.delete) { messages.forEach(viewModel::deleteLocally); endActionMode() }
|
button(R.string.delete) { messages.forEach(viewModel::deleteLocally); endActionMode() }
|
||||||
cancelButton(::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 the recipient is a community OR a Note-to-Self then we delete the message for everyone
|
||||||
if (recipient.isCommunityRecipient || recipient.isLocalNumber) {
|
if (recipient.isCommunityRecipient || recipient.isLocalNumber) {
|
||||||
val messageCount = 1 // Only used for plurals string
|
|
||||||
showSessionDialog {
|
showSessionDialog {
|
||||||
title(resources.getQuantityString(R.plurals.ConversationFragment_delete_selected_messages, messageCount, messageCount))
|
title(resources.getString(R.string.deleteMessage))
|
||||||
text(resources.getQuantityString(R.plurals.ConversationFragment_this_will_permanently_delete_all_n_selected_messages, messageCount, messageCount))
|
text(resources.getString(R.string.deleteMessageDescriptionEveryone))
|
||||||
button(R.string.delete) {
|
button(R.string.delete) { messages.forEach(viewModel::deleteForEveryone); endActionMode() }
|
||||||
messages.forEach(viewModel::deleteForEveryone); endActionMode()
|
|
||||||
}
|
|
||||||
cancelButton { endActionMode() }
|
cancelButton { endActionMode() }
|
||||||
}
|
}
|
||||||
// Otherwise if this is a 1-on-1 conversation we may decided to delete just for ourselves or delete for everyone
|
// 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.
|
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 {
|
showSessionDialog {
|
||||||
title(resources.getQuantityString(R.plurals.ConversationFragment_delete_selected_messages, messageCount, messageCount))
|
title(resources.getString(R.string.deleteMessage))
|
||||||
text(resources.getQuantityString(R.plurals.ConversationFragment_this_will_permanently_delete_all_n_selected_messages, messageCount, messageCount))
|
text(resources.getString(R.string.deleteMessageDescriptionEveryone))
|
||||||
button(R.string.delete) {
|
button(R.string.delete) { messages.forEach(viewModel::deleteLocally); endActionMode() }
|
||||||
messages.forEach(viewModel::deleteLocally); endActionMode()
|
|
||||||
}
|
|
||||||
cancelButton(::endActionMode)
|
cancelButton(::endActionMode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1988,18 +2049,20 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
|
|
||||||
override fun banUser(messages: Set<MessageRecord>) {
|
override fun banUser(messages: Set<MessageRecord>) {
|
||||||
showSessionDialog {
|
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.")
|
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)
|
cancelButton(::endActionMode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun banAndDeleteAll(messages: Set<MessageRecord>) {
|
override fun banAndDeleteAll(messages: Set<MessageRecord>) {
|
||||||
showSessionDialog {
|
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.")
|
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)
|
cancelButton(::endActionMode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2035,7 +2098,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
if (TextUtils.isEmpty(result)) { return }
|
if (TextUtils.isEmpty(result)) { return }
|
||||||
val manager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
val manager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
manager.setPrimaryClip(ClipData.newPlainText("Message Content", result))
|
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()
|
endActionMode()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2044,7 +2107,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
val clip = ClipData.newPlainText("Account ID", accountID)
|
val clip = ClipData.newPlainText("Account ID", accountID)
|
||||||
val manager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
val manager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
manager.setPrimaryClip(clip)
|
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()
|
endActionMode()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2096,10 +2159,15 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
Permissions.with(this)
|
Permissions.with(this)
|
||||||
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||||
.maxSdkVersion(Build.VERSION_CODES.P)
|
.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 {
|
.onAnyDenied {
|
||||||
endActionMode()
|
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 {
|
.onAllGranted {
|
||||||
endActionMode()
|
endActionMode()
|
||||||
@ -2116,7 +2184,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
return@onAllGranted
|
return@onAllGranted
|
||||||
}
|
}
|
||||||
Toast.makeText(this,
|
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()
|
Toast.LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
.execute()
|
.execute()
|
||||||
@ -2172,6 +2240,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
searchViewModel.onMissingResult() }
|
searchViewModel.onMissingResult() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.searchBottomBar.setData(result.position, result.getResults().size)
|
binding.searchBottomBar.setData(result.position, result.getResults().size)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -2181,6 +2250,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
binding.searchBottomBar.visibility = View.VISIBLE
|
binding.searchBottomBar.visibility = View.VISIBLE
|
||||||
binding.searchBottomBar.setData(0, 0)
|
binding.searchBottomBar.setData(0, 0)
|
||||||
binding.inputBar.visibility = View.INVISIBLE
|
binding.inputBar.visibility = View.INVISIBLE
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onSearchClosed() {
|
fun onSearchClosed() {
|
||||||
|
@ -12,6 +12,9 @@ import androidx.core.util.getOrDefault
|
|||||||
import androidx.core.util.set
|
import androidx.core.util.set
|
||||||
import androidx.lifecycle.LifecycleCoroutineScope
|
import androidx.lifecycle.LifecycleCoroutineScope
|
||||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
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.Dispatchers.IO
|
||||||
import kotlinx.coroutines.channels.BufferOverflow
|
import kotlinx.coroutines.channels.BufferOverflow
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
@ -20,17 +23,16 @@ import kotlinx.coroutines.launch
|
|||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.session.libsession.messaging.contacts.Contact
|
import org.session.libsession.messaging.contacts.Contact
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
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.ControlMessageView
|
||||||
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageView
|
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageView
|
||||||
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageViewDelegate
|
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageViewDelegate
|
||||||
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter
|
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
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.preferences.PrivacySettingsActivity
|
||||||
import org.thoughtcrime.securesms.showSessionDialog
|
import org.thoughtcrime.securesms.showSessionDialog
|
||||||
import java.util.concurrent.atomic.AtomicLong
|
|
||||||
import kotlin.math.min
|
|
||||||
|
|
||||||
class ConversationAdapter(
|
class ConversationAdapter(
|
||||||
context: Context,
|
context: Context,
|
||||||
@ -42,7 +44,7 @@ class ConversationAdapter(
|
|||||||
private val onItemLongPress: (MessageRecord, Int, VisibleMessageView) -> Unit,
|
private val onItemLongPress: (MessageRecord, Int, VisibleMessageView) -> Unit,
|
||||||
private val onDeselect: (MessageRecord, Int) -> Unit,
|
private val onDeselect: (MessageRecord, Int) -> Unit,
|
||||||
private val onAttachmentNeedsDownload: (DatabaseAttachment) -> Unit,
|
private val onAttachmentNeedsDownload: (DatabaseAttachment) -> Unit,
|
||||||
private val glide: GlideRequests,
|
private val glide: RequestManager,
|
||||||
lifecycleCoroutineScope: LifecycleCoroutineScope
|
lifecycleCoroutineScope: LifecycleCoroutineScope
|
||||||
) : CursorRecyclerViewAdapter<ViewHolder>(context, cursor) {
|
) : CursorRecyclerViewAdapter<ViewHolder>(context, cursor) {
|
||||||
private val messageDB by lazy { DatabaseComponent.get(context).mmsSmsDatabase() }
|
private val messageDB by lazy { DatabaseComponent.get(context).mmsSmsDatabase() }
|
||||||
@ -154,9 +156,15 @@ class ConversationAdapter(
|
|||||||
if (message.isCallLog && message.isFirstMissedCall) {
|
if (message.isCallLog && message.isFirstMissedCall) {
|
||||||
viewHolder.view.setOnClickListener {
|
viewHolder.view.setOnClickListener {
|
||||||
context.showSessionDialog {
|
context.showSessionDialog {
|
||||||
title(R.string.CallNotificationBuilder_first_call_title)
|
val titleTxt = Phrase.from(context, R.string.callsMissedCallFrom)
|
||||||
text(R.string.CallNotificationBuilder_first_call_message)
|
.put(NAME_KEY, message.individualRecipient.name)
|
||||||
button(R.string.activity_settings_title) {
|
.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)
|
Intent(context, PrivacySettingsActivity::class.java)
|
||||||
.let(context::startActivity)
|
.let(context::startActivity)
|
||||||
}
|
}
|
||||||
@ -190,7 +198,7 @@ class ConversationAdapter(
|
|||||||
private fun getMessageBefore(position: Int, cursor: Cursor): MessageRecord? {
|
private fun getMessageBefore(position: Int, cursor: Cursor): MessageRecord? {
|
||||||
// The message that's visually before the current one is actually after the current
|
// The message that's visually before the current one is actually after the current
|
||||||
// one for the cursor because the layout is reversed
|
// one for the cursor because the layout is reversed
|
||||||
if (isReversed && !cursor.moveToPosition(position + 1)) { return null }
|
if (isReversed && !cursor.moveToPosition(position + 1)) { return null }
|
||||||
if (!isReversed && !cursor.moveToPosition(position - 1)) { return null }
|
if (!isReversed && !cursor.moveToPosition(position - 1)) { return null }
|
||||||
|
|
||||||
return messageDB.readerFor(cursor).current
|
return messageDB.readerFor(cursor).current
|
||||||
|
@ -22,7 +22,11 @@ import androidx.constraintlayout.widget.ConstraintLayout
|
|||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.doOnLayout
|
import androidx.core.view.doOnLayout
|
||||||
import androidx.vectordrawable.graphics.drawable.AnimatorInflaterCompat
|
import androidx.vectordrawable.graphics.drawable.AnimatorInflaterCompat
|
||||||
|
import com.squareup.phrase.Phrase
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
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.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
@ -30,7 +34,9 @@ import kotlinx.coroutines.flow.filter
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
|
import org.session.libsession.LocalisedTimeUtil.toShortTwoPartString
|
||||||
import org.session.libsession.snode.SnodeAPI
|
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.TextSecurePreferences.Companion.getLocalNumber
|
||||||
import org.session.libsession.utilities.ThemeUtil
|
import org.session.libsession.utilities.ThemeUtil
|
||||||
import org.thoughtcrime.securesms.components.emoji.EmojiImageView
|
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.repository.ConversationRepository
|
||||||
import org.thoughtcrime.securesms.util.AnimationCompleteListener
|
import org.thoughtcrime.securesms.util.AnimationCompleteListener
|
||||||
import org.thoughtcrime.securesms.util.DateUtils
|
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
|
@AndroidEntryPoint
|
||||||
class ConversationReactionOverlay : FrameLayout {
|
class ConversationReactionOverlay : FrameLayout {
|
||||||
@ -529,11 +527,11 @@ class ConversationReactionOverlay : FrameLayout {
|
|||||||
?: return emptyList()
|
?: return emptyList()
|
||||||
val userPublicKey = getLocalNumber(context)!!
|
val userPublicKey = getLocalNumber(context)!!
|
||||||
// Select message
|
// 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
|
// Reply
|
||||||
val canWrite = openGroup == null || openGroup.canWrite
|
val canWrite = openGroup == null || openGroup.canWrite
|
||||||
if (canWrite && !message.isPending && !message.isFailed && !message.isOpenGroupInvitation) {
|
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
|
// Copy message text
|
||||||
if (!containsControlMessage && hasText) {
|
if (!containsControlMessage && hasText) {
|
||||||
@ -541,7 +539,7 @@ class ConversationReactionOverlay : FrameLayout {
|
|||||||
}
|
}
|
||||||
// Copy Account ID
|
// Copy Account ID
|
||||||
if (recipient.isGroupRecipient && !recipient.isCommunityRecipient && message.recipient.address.toString() != userPublicKey) {
|
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
|
// Delete message
|
||||||
if (userCanDeleteSelectedItems(context, message, openGroup, userPublicKey, blindedPublicKey)) {
|
if (userCanDeleteSelectedItems(context, message, openGroup, userPublicKey, blindedPublicKey)) {
|
||||||
@ -550,17 +548,17 @@ class ConversationReactionOverlay : FrameLayout {
|
|||||||
}
|
}
|
||||||
// Ban user
|
// Ban user
|
||||||
if (userCanBanSelectedUsers(context, message, openGroup, userPublicKey, blindedPublicKey)) {
|
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
|
// Ban and delete all
|
||||||
if (userCanBanSelectedUsers(context, message, openGroup, userPublicKey, blindedPublicKey)) {
|
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
|
// 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
|
// Resend
|
||||||
if (message.isFailed) {
|
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
|
// Resync
|
||||||
if (message.isSyncFailed) {
|
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?)?
|
private val MessageRecord.subtitle: ((Context) -> CharSequence?)?
|
||||||
get() = if (expiresIn <= 0) {
|
get() = if (expiresIn <= 0) {
|
||||||
null
|
null
|
||||||
@ -715,6 +709,10 @@ private val MessageRecord.subtitle: ((Context) -> CharSequence?)?
|
|||||||
(expiresIn - (SnodeAPI.nowWithOffset - (expireStarted.takeIf { it > 0 } ?: timestamp)))
|
(expiresIn - (SnodeAPI.nowWithOffset - (expireStarted.takeIf { it > 0 } ?: timestamp)))
|
||||||
.coerceAtLeast(0L)
|
.coerceAtLeast(0L)
|
||||||
.milliseconds
|
.milliseconds
|
||||||
.to2partString()
|
.toShortTwoPartString()
|
||||||
?.let { context.getString(R.string.auto_deletes_in, it) }
|
.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()) {
|
if (!recipient.isGroupRecipient && !contact.isNullOrEmpty()) {
|
||||||
binding.deleteForEveryoneTextView.text =
|
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.deleteForEveryoneTextView.isVisible = !recipient.isClosedGroupRecipient
|
||||||
binding.deleteForMeTextView.setOnClickListener(this)
|
binding.deleteForMeTextView.setOnClickListener(this)
|
||||||
|
@ -98,7 +98,7 @@ class MessageDetailActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
|
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
|
||||||
super.onCreate(savedInstanceState, ready)
|
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)
|
viewModel.timestamp = intent.getLongExtra(MESSAGE_TIMESTAMP, -1L)
|
||||||
|
|
||||||
@ -319,13 +319,13 @@ fun PreviewMessageDetails(
|
|||||||
state = MessageDetailsState(
|
state = MessageDetailsState(
|
||||||
nonImageAttachmentFileDetails = listOf(
|
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_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.attachmentsFileType, "image/png"),
|
||||||
TitledText(R.string.message_details_header__file_size, "195.6kB"),
|
TitledText(R.string.attachmentsFileSize, "195.6kB"),
|
||||||
TitledText(R.string.message_details_header__resolution, "342x312"),
|
TitledText(R.string.attachmentsResolution, "342x312"),
|
||||||
),
|
),
|
||||||
sent = TitledText(R.string.message_details_header__sent, "6:12 AM Tue, 09/08/2022"),
|
sent = TitledText(R.string.sent, "6:12 AM Tue, 09/08/2022"),
|
||||||
received = TitledText(R.string.message_details_header__received, "6:12 AM Tue, 09/08/2022"),
|
received = TitledText(R.string.received, "6:12 AM Tue, 09/08/2022"),
|
||||||
error = TitledText(R.string.message_details_header__error, "Message failed to send"),
|
error = TitledText(R.string.error, "Message failed to send"),
|
||||||
senderInfo = TitledText("Connor", "d4f1g54sdf5g1d5f4g65ds4564df65f4g65d54"),
|
senderInfo = TitledText("Connor", "d4f1g54sdf5g1d5f4g65ds4564df65f4g65d54"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -78,9 +78,9 @@ class MessageDetailsViewModel @Inject constructor(
|
|||||||
MessageDetailsState(
|
MessageDetailsState(
|
||||||
attachments = slides.map(::Attachment),
|
attachments = slides.map(::Attachment),
|
||||||
record = record,
|
record = record,
|
||||||
sent = dateSent.let(::Date).toString().let { TitledText(R.string.message_details_header__sent, it) },
|
sent = dateSent.let(::Date).toString().let { TitledText(R.string.sent, it) },
|
||||||
received = dateReceived.let(::Date).toString().let { TitledText(R.string.message_details_header__received, it) },
|
received = dateReceived.let(::Date).toString().let { TitledText(R.string.received, it) },
|
||||||
error = lokiMessageDatabase.getErrorMessage(id)?.let { TitledText(R.string.message_details_header__error, it) },
|
error = lokiMessageDatabase.getErrorMessage(id)?.let { TitledText(R.string.theError, it) },
|
||||||
senderInfo = individualRecipient.run { name?.let { TitledText(it, address.serialize()) } },
|
senderInfo = individualRecipient.run { name?.let { TitledText(it, address.serialize()) } },
|
||||||
sender = individualRecipient,
|
sender = individualRecipient,
|
||||||
thread = threadDb.getRecipientForThreadId(threadId)!!,
|
thread = threadDb.getRecipientForThreadId(threadId)!!,
|
||||||
@ -91,13 +91,13 @@ class MessageDetailsViewModel @Inject constructor(
|
|||||||
private val Slide.details: List<TitledText>
|
private val Slide.details: List<TitledText>
|
||||||
get() = listOfNotNull(
|
get() = listOfNotNull(
|
||||||
fileName.orNull()?.let { TitledText(R.string.message_details_header__file_id, it) },
|
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.attachmentsFileType, asAttachment().contentType),
|
||||||
TitledText(R.string.message_details_header__file_size, Util.getPrettyFileSize(fileSize)),
|
TitledText(R.string.attachmentsFileSize, Util.getPrettyFileSize(fileSize)),
|
||||||
takeIf { it is ImageSlide }
|
takeIf { it is ImageSlide }
|
||||||
?.let(Slide::asAttachment)
|
?.let(Slide::asAttachment)
|
||||||
?.run { "${width}x$height" }
|
?.run { "${width}x$height" }
|
||||||
?.let { TitledText(R.string.message_details_header__resolution, it) },
|
?.let { TitledText(R.string.attachmentsResolution, it) },
|
||||||
attachmentDb.duration(this)?.let { TitledText(R.string.message_details_header__duration, it) },
|
attachmentDb.duration(this)?.let { TitledText(R.string.attachmentsDuration, it) },
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun AttachmentDatabase.duration(slide: Slide): String? =
|
private fun AttachmentDatabase.duration(slide: Slide): String? =
|
||||||
@ -157,7 +157,7 @@ data class MessageDetailsState(
|
|||||||
val sender: Recipient? = null,
|
val sender: Recipient? = null,
|
||||||
val thread: 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
|
val canReply = record?.isOpenGroupInvitation != true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,9 +15,10 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
import com.squareup.phrase.Phrase
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import network.loki.messenger.databinding.FragmentModalUrlBottomSheetBinding
|
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 {
|
class ModalUrlBottomSheet(private val url: String): BottomSheetDialogFragment(), View.OnClickListener {
|
||||||
private lateinit var binding: FragmentModalUrlBottomSheetBinding
|
private lateinit var binding: FragmentModalUrlBottomSheetBinding
|
||||||
@ -29,7 +30,9 @@ class ModalUrlBottomSheet(private val url: String): BottomSheetDialogFragment(),
|
|||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
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 spannable = SpannableStringBuilder(explanation)
|
||||||
val startIndex = explanation.indexOf(url)
|
val startIndex = explanation.indexOf(url)
|
||||||
spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + url.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
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))
|
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
||||||
requireContext().startActivity(intent)
|
requireContext().startActivity(intent)
|
||||||
} catch (e: Exception) {
|
} 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()
|
dismiss()
|
||||||
}
|
}
|
||||||
@ -53,7 +56,7 @@ class ModalUrlBottomSheet(private val url: String): BottomSheetDialogFragment(),
|
|||||||
val clip = ClipData.newPlainText("URL", url)
|
val clip = ClipData.newPlainText("URL", url)
|
||||||
val manager = requireContext().getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
val manager = requireContext().getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
manager.setPrimaryClip(clip)
|
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()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,16 +11,18 @@ import android.widget.RelativeLayout
|
|||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.core.view.children
|
import androidx.core.view.children
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import com.squareup.phrase.Phrase
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import network.loki.messenger.databinding.AlbumThumbnailViewBinding
|
import network.loki.messenger.databinding.AlbumThumbnailViewBinding
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress
|
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
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.session.libsession.utilities.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.MediaPreviewActivity
|
import org.thoughtcrime.securesms.MediaPreviewActivity
|
||||||
import org.thoughtcrime.securesms.components.CornerMask
|
import org.thoughtcrime.securesms.components.CornerMask
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView
|
import org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView
|
||||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
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.mms.Slide
|
||||||
import org.thoughtcrime.securesms.util.ActivityDispatcher
|
import org.thoughtcrime.securesms.util.ActivityDispatcher
|
||||||
|
|
||||||
@ -80,7 +82,7 @@ class AlbumThumbnailView : RelativeLayout {
|
|||||||
slideSize = -1
|
slideSize = -1
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bind(glideRequests: GlideRequests, message: MmsMessageRecord,
|
fun bind(glideRequests: RequestManager, message: MmsMessageRecord,
|
||||||
isStart: Boolean, isEnd: Boolean) {
|
isStart: Boolean, isEnd: Boolean) {
|
||||||
slides = message.slideDeck.thumbnailSlides
|
slides = message.slideDeck.thumbnailSlides
|
||||||
if (slides.isEmpty()) {
|
if (slides.isEmpty()) {
|
||||||
@ -97,7 +99,10 @@ class AlbumThumbnailView : RelativeLayout {
|
|||||||
binding.albumCellContainer.findViewById<TextView>(R.id.album_cell_overflow_text)?.let { overflowText ->
|
binding.albumCellContainer.findViewById<TextView>(R.id.album_cell_overflow_text)?.let { overflowText ->
|
||||||
// overflowText will be null if !overflowed
|
// overflowText will be null if !overflowed
|
||||||
overflowText.isVisible = overflowed // more than max album size
|
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
|
this.slideSize = slides.size
|
||||||
}
|
}
|
||||||
@ -110,10 +115,9 @@ class AlbumThumbnailView : RelativeLayout {
|
|||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
|
|
||||||
fun layoutRes(slideCount: Int) = when (slideCount) {
|
fun layoutRes(slideCount: Int) = when (slideCount) {
|
||||||
1 -> R.layout.album_thumbnail_1 // single
|
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
|
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 androidx.core.view.isVisible
|
||||||
import network.loki.messenger.databinding.ViewLinkPreviewDraftBinding
|
import network.loki.messenger.databinding.ViewLinkPreviewDraftBinding
|
||||||
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview
|
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.mms.ImageSlide
|
||||||
import org.thoughtcrime.securesms.util.toPx
|
import org.thoughtcrime.securesms.util.toPx
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ class LinkPreviewDraftView : LinearLayout {
|
|||||||
binding.linkPreviewDraftCancelButton.setOnClickListener { cancel() }
|
binding.linkPreviewDraftCancelButton.setOnClickListener { cancel() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun update(glide: GlideRequests, linkPreview: LinkPreview) {
|
fun update(glide: RequestManager, linkPreview: LinkPreview) {
|
||||||
// Hide the loader and show the content view
|
// Hide the loader and show the content view
|
||||||
binding.linkPreviewDraftContainer.isVisible = true
|
binding.linkPreviewDraftContainer.isVisible = true
|
||||||
binding.linkPreviewDraftLoader.isVisible = false
|
binding.linkPreviewDraftLoader.isVisible = false
|
||||||
|
@ -9,13 +9,13 @@ import android.widget.BaseAdapter
|
|||||||
import android.widget.ListView
|
import android.widget.ListView
|
||||||
import org.session.libsession.messaging.mentions.Mention
|
import org.session.libsession.messaging.mentions.Mention
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import com.bumptech.glide.RequestManager
|
||||||
import org.thoughtcrime.securesms.util.toPx
|
import org.thoughtcrime.securesms.util.toPx
|
||||||
|
|
||||||
class MentionCandidateSelectionView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : ListView(context, attrs, defStyleAttr) {
|
class MentionCandidateSelectionView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : ListView(context, attrs, defStyleAttr) {
|
||||||
private var mentionCandidates = listOf<Mention>()
|
private var mentionCandidates = listOf<Mention>()
|
||||||
set(newValue) { field = newValue; mentionCandidateSelectionViewAdapter.mentionCandidates = newValue }
|
set(newValue) { field = newValue; mentionCandidateSelectionViewAdapter.mentionCandidates = newValue }
|
||||||
var glide: GlideRequests? = null
|
var glide: RequestManager? = null
|
||||||
set(newValue) { field = newValue; mentionCandidateSelectionViewAdapter.glide = newValue }
|
set(newValue) { field = newValue; mentionCandidateSelectionViewAdapter.glide = newValue }
|
||||||
var openGroupServer: String? = null
|
var openGroupServer: String? = null
|
||||||
set(newValue) { field = newValue; mentionCandidateSelectionViewAdapter.openGroupServer = openGroupServer }
|
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() {
|
private class Adapter(private val context: Context) : BaseAdapter() {
|
||||||
var mentionCandidates = listOf<Mention>()
|
var mentionCandidates = listOf<Mention>()
|
||||||
set(newValue) { field = newValue; notifyDataSetChanged() }
|
set(newValue) { field = newValue; notifyDataSetChanged() }
|
||||||
var glide: GlideRequests? = null
|
var glide: RequestManager? = null
|
||||||
var openGroupServer: String? = null
|
var openGroupServer: String? = null
|
||||||
var openGroupRoom: String? = null
|
var openGroupRoom: String? = null
|
||||||
|
|
||||||
|
@ -8,13 +8,13 @@ import android.widget.LinearLayout
|
|||||||
import network.loki.messenger.databinding.ViewMentionCandidateBinding
|
import network.loki.messenger.databinding.ViewMentionCandidateBinding
|
||||||
import org.session.libsession.messaging.mentions.Mention
|
import org.session.libsession.messaging.mentions.Mention
|
||||||
import org.thoughtcrime.securesms.groups.OpenGroupManager
|
import org.thoughtcrime.securesms.groups.OpenGroupManager
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import com.bumptech.glide.RequestManager
|
||||||
|
|
||||||
class MentionCandidateView : LinearLayout {
|
class MentionCandidateView : LinearLayout {
|
||||||
private lateinit var binding: ViewMentionCandidateBinding
|
private lateinit var binding: ViewMentionCandidateBinding
|
||||||
var mentionCandidate = Mention("", "")
|
var mentionCandidate = Mention("", "")
|
||||||
set(newValue) { field = newValue; update() }
|
set(newValue) { field = newValue; update() }
|
||||||
var glide: GlideRequests? = null
|
var glide: RequestManager? = null
|
||||||
var openGroupServer: String? = null
|
var openGroupServer: String? = null
|
||||||
var openGroupRoom: String? = null
|
var openGroupRoom: String? = null
|
||||||
|
|
||||||
|
@ -8,9 +8,11 @@ import android.text.Spannable
|
|||||||
import android.text.SpannableStringBuilder
|
import android.text.SpannableStringBuilder
|
||||||
import android.text.style.StyleSpan
|
import android.text.style.StyleSpan
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import com.squareup.phrase.Phrase
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.messaging.contacts.Contact
|
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.session.libsession.utilities.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.createSessionDialog
|
import org.thoughtcrime.securesms.createSessionDialog
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
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 contact = contactDB.getContactWithAccountID(accountID)
|
||||||
val name = contact?.displayName(Contact.ContactContext.REGULAR) ?: 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 spannable = SpannableStringBuilder(explanation)
|
||||||
val startIndex = explanation.indexOf(name)
|
val startIndex = explanation.indexOf(name)
|
||||||
spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + name.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
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)
|
text(spannable)
|
||||||
button(R.string.ConversationActivity_unblock) { unblock() }
|
button(R.string.blockUnblock) { unblock() }
|
||||||
cancelButton { dismiss() }
|
cancelButton { dismiss() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,12 +7,14 @@ import android.text.Spannable
|
|||||||
import android.text.SpannableStringBuilder
|
import android.text.SpannableStringBuilder
|
||||||
import android.text.style.StyleSpan
|
import android.text.style.StyleSpan
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import com.squareup.phrase.Phrase
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.session.libsession.messaging.contacts.Contact
|
import org.session.libsession.messaging.contacts.Contact
|
||||||
import org.session.libsession.messaging.jobs.AttachmentDownloadJob
|
import org.session.libsession.messaging.jobs.AttachmentDownloadJob
|
||||||
import org.session.libsession.messaging.jobs.JobQueue
|
import org.session.libsession.messaging.jobs.JobQueue
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
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.createSessionDialog
|
||||||
import org.thoughtcrime.securesms.database.SessionContactDatabase
|
import org.thoughtcrime.securesms.database.SessionContactDatabase
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||||
@ -29,15 +31,19 @@ class DownloadDialog(private val recipient: Recipient) : DialogFragment() {
|
|||||||
val accountID = recipient.address.toString()
|
val accountID = recipient.address.toString()
|
||||||
val contact = contactDB.getContactWithAccountID(accountID)
|
val contact = contactDB.getContactWithAccountID(accountID)
|
||||||
val name = contact?.displayName(Contact.ContactContext.REGULAR) ?: 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 spannable = SpannableStringBuilder(explanation)
|
||||||
|
|
||||||
val startIndex = explanation.indexOf(name)
|
val startIndex = explanation.indexOf(name)
|
||||||
spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + name.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + name.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
text(spannable)
|
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() }
|
cancelButton { dismiss() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package org.thoughtcrime.securesms.conversation.v2.dialogs
|
package org.thoughtcrime.securesms.conversation.v2.dialogs
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.createSessionDialog
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@ -8,11 +9,13 @@ import android.text.SpannableStringBuilder
|
|||||||
import android.text.style.StyleSpan
|
import android.text.style.StyleSpan
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import com.squareup.phrase.Phrase
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.utilities.OpenGroupUrlParser
|
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.session.libsignal.utilities.ThreadUtils
|
||||||
import org.thoughtcrime.securesms.createSessionDialog
|
|
||||||
import org.thoughtcrime.securesms.groups.OpenGroupManager
|
import org.thoughtcrime.securesms.groups.OpenGroupManager
|
||||||
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
|
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() {
|
class JoinOpenGroupDialog(private val name: String, private val url: String) : DialogFragment() {
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog {
|
||||||
title(resources.getString(R.string.dialog_join_open_group_title, name))
|
title(resources.getString(R.string.communityJoin))
|
||||||
val explanation = resources.getString(R.string.dialog_join_open_group_explanation, name)
|
val explanation = Phrase.from(context, R.string.communityJoinDescription).put(COMMUNITY_NAME_KEY, name).format()
|
||||||
val spannable = SpannableStringBuilder(explanation)
|
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)
|
spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + name.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
text(spannable)
|
text(spannable)
|
||||||
cancelButton { dismiss() }
|
cancelButton { dismiss() }
|
||||||
button(R.string.open_group_invitation_view__join_accessibility_description) { join() }
|
button(R.string.join) { join() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun 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)
|
MessagingModuleConfiguration.shared.storage.onOpenGroupAdded(openGroup.server, openGroup.room)
|
||||||
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(activity)
|
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(activity)
|
||||||
} catch (e: Exception) {
|
} 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()
|
dismiss()
|
||||||
|
@ -3,7 +3,9 @@ package org.thoughtcrime.securesms.conversation.v2.dialogs
|
|||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import com.squareup.phrase.Phrase
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
|
import org.session.libsession.utilities.StringSubstitutionConstants.APP_NAME_KEY
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.thoughtcrime.securesms.createSessionDialog
|
import org.thoughtcrime.securesms.createSessionDialog
|
||||||
|
|
||||||
@ -12,9 +14,12 @@ import org.thoughtcrime.securesms.createSessionDialog
|
|||||||
class LinkPreviewDialog(private val onEnabled: () -> Unit) : DialogFragment() {
|
class LinkPreviewDialog(private val onEnabled: () -> Unit) : DialogFragment() {
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog {
|
||||||
title(R.string.dialog_link_preview_title)
|
title(R.string.linkPreviewsEnable)
|
||||||
text(R.string.dialog_link_preview_explanation)
|
val txt = Phrase.from(context, R.string.linkPreviewsFirstDescription)
|
||||||
button(R.string.dialog_link_preview_enable_button_title) { enable() }
|
.put(APP_NAME_KEY, context.getString(R.string.app_name))
|
||||||
|
.format()
|
||||||
|
text(txt)
|
||||||
|
button(R.string.enable) { enable() }
|
||||||
cancelButton { dismiss() }
|
cancelButton { dismiss() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,9 +10,9 @@ import org.thoughtcrime.securesms.createSessionDialog
|
|||||||
class SendSeedDialog(private val proceed: (() -> Unit)? = null) : DialogFragment() {
|
class SendSeedDialog(private val proceed: (() -> Unit)? = null) : DialogFragment() {
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog {
|
||||||
title(R.string.dialog_send_seed_title)
|
title(R.string.warning)
|
||||||
text(R.string.dialog_send_seed_explanation)
|
text(R.string.recoveryPasswordWarningSendDescription)
|
||||||
button(R.string.dialog_send_seed_send_button_title) { send() }
|
button(R.string.send) { send() }
|
||||||
cancelButton()
|
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.conversation.v2.messages.QuoteViewDelegate
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
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.addTextChangedListener
|
||||||
import org.thoughtcrime.securesms.util.contains
|
import org.thoughtcrime.securesms.util.contains
|
||||||
|
|
||||||
@ -188,7 +188,7 @@ class InputBar @JvmOverloads constructor(
|
|||||||
|
|
||||||
private fun startRecordingVoiceMessage() { delegate?.startRecordingVoiceMessage() }
|
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)
|
quoteView?.let(binding.inputBarAdditionalContentContainer::removeView)
|
||||||
|
|
||||||
quote = message
|
quote = message
|
||||||
@ -238,7 +238,7 @@ class InputBar @JvmOverloads constructor(
|
|||||||
requestLayout()
|
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)
|
// Update our `linkPreview` property with the new (provided as an argument to this function)
|
||||||
// then update the View from that.
|
// then update the View from that.
|
||||||
linkPreview = updatedLinkPreview.also { linkPreviewDraftView?.update(glide, it) }
|
linkPreview = updatedLinkPreview.also { linkPreviewDraftView?.update(glide, it) }
|
||||||
|
@ -19,7 +19,6 @@ import kotlinx.coroutines.isActive
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import network.loki.messenger.databinding.ViewInputBarRecordingBinding
|
import network.loki.messenger.databinding.ViewInputBarRecordingBinding
|
||||||
import org.thoughtcrime.securesms.util.DateUtils
|
|
||||||
import org.thoughtcrime.securesms.util.animateSizeChange
|
import org.thoughtcrime.securesms.util.animateSizeChange
|
||||||
import org.thoughtcrime.securesms.util.disableClipping
|
import org.thoughtcrime.securesms.util.disableClipping
|
||||||
import org.thoughtcrime.securesms.util.toPx
|
import org.thoughtcrime.securesms.util.toPx
|
||||||
@ -106,8 +105,7 @@ class InputBarRecordingView : RelativeLayout {
|
|||||||
timerJob = scope.launch {
|
timerJob = scope.launch {
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
val duration = (Date().time - startTimestamp) / 1000L
|
val duration = (Date().time - startTimestamp) / 1000L
|
||||||
binding.recordingViewDurationTextView.text = DateUtils.formatElapsedTime(duration)
|
binding.recordingViewDurationTextView.text = android.text.format.DateUtils.formatElapsedTime(duration)
|
||||||
|
|
||||||
delay(500)
|
delay(500)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,10 +16,13 @@ import androidx.appcompat.widget.SearchView.OnQueryTextListener
|
|||||||
import androidx.core.content.pm.ShortcutInfoCompat
|
import androidx.core.content.pm.ShortcutInfoCompat
|
||||||
import androidx.core.content.pm.ShortcutManagerCompat
|
import androidx.core.content.pm.ShortcutManagerCompat
|
||||||
import androidx.core.graphics.drawable.IconCompat
|
import androidx.core.graphics.drawable.IconCompat
|
||||||
|
import com.squareup.phrase.Phrase
|
||||||
|
import java.io.IOException
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.session.libsession.messaging.sending_receiving.MessageSender
|
import org.session.libsession.messaging.sending_receiving.MessageSender
|
||||||
import org.session.libsession.messaging.sending_receiving.leave
|
import org.session.libsession.messaging.sending_receiving.leave
|
||||||
import org.session.libsession.utilities.GroupUtil.doubleDecodeGroupID
|
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.TextSecurePreferences
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.session.libsignal.utilities.guava.Optional
|
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.showMuteDialog
|
||||||
import org.thoughtcrime.securesms.showSessionDialog
|
import org.thoughtcrime.securesms.showSessionDialog
|
||||||
import org.thoughtcrime.securesms.util.BitmapUtil
|
import org.thoughtcrime.securesms.util.BitmapUtil
|
||||||
import java.io.IOException
|
|
||||||
|
|
||||||
object ConversationMenuHelper {
|
object ConversationMenuHelper {
|
||||||
|
|
||||||
@ -164,9 +166,9 @@ object ConversationMenuHelper {
|
|||||||
|
|
||||||
if (!TextSecurePreferences.isCallNotificationsEnabled(context)) {
|
if (!TextSecurePreferences.isCallNotificationsEnabled(context)) {
|
||||||
context.showSessionDialog {
|
context.showSessionDialog {
|
||||||
title(R.string.ConversationActivity_call_title)
|
title(R.string.callsPermissionsRequired)
|
||||||
text(R.string.ConversationActivity_call_prompt)
|
text(R.string.callsPermissionsRequiredDescription)
|
||||||
button(R.string.activity_settings_title, R.string.AccessibilityId_settings) {
|
button(R.string.sessionSettings, R.string.AccessibilityId_settings) {
|
||||||
Intent(context, PrivacySettingsActivity::class.java).let(context::startActivity)
|
Intent(context, PrivacySettingsActivity::class.java).let(context::startActivity)
|
||||||
}
|
}
|
||||||
cancelButton()
|
cancelButton()
|
||||||
@ -217,7 +219,7 @@ object ConversationMenuHelper {
|
|||||||
.setIntent(ShortcutLauncherActivity.createIntent(context, thread.address))
|
.setIntent(ShortcutLauncherActivity.createIntent(context, thread.address))
|
||||||
.build()
|
.build()
|
||||||
if (ShortcutManagerCompat.requestPinShortcut(context, shortcutInfo, null)) {
|
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()
|
}.execute()
|
||||||
@ -274,15 +276,24 @@ object ConversationMenuHelper {
|
|||||||
val accountID = TextSecurePreferences.getLocalNumber(context)
|
val accountID = TextSecurePreferences.getLocalNumber(context)
|
||||||
val isCurrentUserAdmin = admins.any { it.toString() == accountID }
|
val isCurrentUserAdmin = admins.any { it.toString() == accountID }
|
||||||
val message = if (isCurrentUserAdmin) {
|
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 {
|
} 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 {
|
context.showSessionDialog {
|
||||||
title(R.string.ConversationActivity_leave_group)
|
title(R.string.groupLeave)
|
||||||
text(message)
|
text(message)
|
||||||
button(R.string.yes) {
|
button(R.string.yes) {
|
||||||
try {
|
try {
|
||||||
|
@ -8,17 +8,19 @@ import androidx.core.content.res.ResourcesCompat
|
|||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.squareup.phrase.Phrase
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import javax.inject.Inject
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import network.loki.messenger.databinding.ViewControlMessageBinding
|
import network.loki.messenger.databinding.ViewControlMessageBinding
|
||||||
import network.loki.messenger.libsession_util.util.ExpiryMode
|
import network.loki.messenger.libsession_util.util.ExpiryMode
|
||||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.messaging.messages.ExpirationConfiguration
|
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.DisappearingMessages
|
||||||
import org.thoughtcrime.securesms.conversation.disappearingmessages.expiryMode
|
import org.thoughtcrime.securesms.conversation.disappearingmessages.expiryMode
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class ControlMessageView : LinearLayout {
|
class ControlMessageView : LinearLayout {
|
||||||
@ -75,8 +77,10 @@ class ControlMessageView : LinearLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
message.isMessageRequestResponse -> {
|
message.isMessageRequestResponse -> {
|
||||||
binding.textView.text = context.getString(R.string.message_requests_accepted)
|
binding.textView.text = context.getString(R.string.messageRequestsAccepted)
|
||||||
binding.root.contentDescription=context.getString(R.string.AccessibilityId_message_request_config_message)
|
binding.root.contentDescription = Phrase.from(context, R.string.messageRequestYouHaveAccepted)
|
||||||
|
.put(NAME_KEY, message.individualRecipient.name)
|
||||||
|
.format()
|
||||||
}
|
}
|
||||||
message.isCallLog -> {
|
message.isCallLog -> {
|
||||||
val drawable = when {
|
val drawable = when {
|
||||||
|
@ -21,7 +21,7 @@ class DeletedMessageView : LinearLayout {
|
|||||||
// region Updating
|
// region Updating
|
||||||
fun bind(message: MessageRecord, @ColorInt textColor: Int) {
|
fun bind(message: MessageRecord, @ColorInt textColor: Int) {
|
||||||
assert(message.isDeleted)
|
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.deleteTitleTextView.setTextColor(textColor)
|
||||||
binding.deletedMessageViewIconImageView.imageTintList = ColorStateList.valueOf(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.ModalUrlBottomSheet
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.MessageBubbleUtilities
|
import org.thoughtcrime.securesms.conversation.v2.utilities.MessageBubbleUtilities
|
||||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import com.bumptech.glide.RequestManager
|
||||||
import org.thoughtcrime.securesms.mms.ImageSlide
|
import org.thoughtcrime.securesms.mms.ImageSlide
|
||||||
|
|
||||||
class LinkPreviewView : LinearLayout {
|
class LinkPreviewView : LinearLayout {
|
||||||
@ -32,7 +32,7 @@ class LinkPreviewView : LinearLayout {
|
|||||||
// region Updating
|
// region Updating
|
||||||
fun bind(
|
fun bind(
|
||||||
message: MmsMessageRecord,
|
message: MmsMessageRecord,
|
||||||
glide: GlideRequests,
|
glide: RequestManager,
|
||||||
isStartOfMessageCluster: Boolean,
|
isStartOfMessageCluster: Boolean,
|
||||||
isEndOfMessageCluster: Boolean
|
isEndOfMessageCluster: Boolean
|
||||||
) {
|
) {
|
||||||
|
@ -18,7 +18,7 @@ import org.session.libsession.utilities.getColorFromAttr
|
|||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.MentionUtilities
|
import org.thoughtcrime.securesms.conversation.v2.utilities.MentionUtilities
|
||||||
import org.thoughtcrime.securesms.database.SessionContactDatabase
|
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.mms.SlideDeck
|
||||||
import org.thoughtcrime.securesms.util.MediaUtil
|
import org.thoughtcrime.securesms.util.MediaUtil
|
||||||
import org.thoughtcrime.securesms.util.getAccentColor
|
import org.thoughtcrime.securesms.util.getAccentColor
|
||||||
@ -68,20 +68,20 @@ class QuoteView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
|||||||
// region Updating
|
// region Updating
|
||||||
fun bind(authorPublicKey: String, body: String?, attachments: SlideDeck?, thread: Recipient,
|
fun bind(authorPublicKey: String, body: String?, attachments: SlideDeck?, thread: Recipient,
|
||||||
isOutgoingMessage: Boolean, isOpenGroupInvitation: Boolean, threadID: Long,
|
isOutgoingMessage: Boolean, isOpenGroupInvitation: Boolean, threadID: Long,
|
||||||
isOriginalMissing: Boolean, glide: GlideRequests) {
|
isOriginalMissing: Boolean, glide: RequestManager) {
|
||||||
// Author
|
// Author
|
||||||
val author = contactDb.getContactWithAccountID(authorPublicKey)
|
val author = contactDb.getContactWithAccountID(authorPublicKey)
|
||||||
val localNumber = TextSecurePreferences.getLocalNumber(context)
|
val localNumber = TextSecurePreferences.getLocalNumber(context)
|
||||||
val quoteIsLocalUser = localNumber != null && authorPublicKey == localNumber
|
val quoteIsLocalUser = localNumber != null && authorPublicKey == localNumber
|
||||||
|
|
||||||
val authorDisplayName =
|
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)}"
|
else author?.displayName(Contact.contextForRecipient(thread)) ?: "${authorPublicKey.take(4)}...${authorPublicKey.takeLast(4)}"
|
||||||
binding.quoteViewAuthorTextView.text = authorDisplayName
|
binding.quoteViewAuthorTextView.text = authorDisplayName
|
||||||
binding.quoteViewAuthorTextView.setTextColor(getTextColor(isOutgoingMessage))
|
binding.quoteViewAuthorTextView.setTextColor(getTextColor(isOutgoingMessage))
|
||||||
// Body
|
// Body
|
||||||
binding.quoteViewBodyTextView.text = if (isOpenGroupInvitation)
|
binding.quoteViewBodyTextView.text = if (isOpenGroupInvitation)
|
||||||
resources.getString(R.string.open_group_invitation_view__open_group_invitation)
|
resources.getString(R.string.communityInvitation)
|
||||||
else MentionUtilities.highlightMentions(
|
else MentionUtilities.highlightMentions(
|
||||||
text = (body ?: "").toSpannable(),
|
text = (body ?: "").toSpannable(),
|
||||||
isOutgoingMessage = isOutgoingMessage,
|
isOutgoingMessage = isOutgoingMessage,
|
||||||
@ -106,7 +106,7 @@ class QuoteView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
|||||||
attachments.audioSlide != null -> {
|
attachments.audioSlide != null -> {
|
||||||
binding.quoteViewAttachmentPreviewImageView.setImageResource(R.drawable.ic_microphone)
|
binding.quoteViewAttachmentPreviewImageView.setImageResource(R.drawable.ic_microphone)
|
||||||
binding.quoteViewAttachmentPreviewImageView.isVisible = true
|
binding.quoteViewAttachmentPreviewImageView.isVisible = true
|
||||||
binding.quoteViewBodyTextView.text = resources.getString(R.string.Slide_audio)
|
binding.quoteViewBodyTextView.text = resources.getString(R.string.audio)
|
||||||
}
|
}
|
||||||
attachments.documentSlide != null -> {
|
attachments.documentSlide != null -> {
|
||||||
binding.quoteViewAttachmentPreviewImageView.setImageResource(R.drawable.ic_document_large_light)
|
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))
|
.root.setRoundedCorners(toPx(4, resources))
|
||||||
binding.quoteViewAttachmentThumbnailImageView.root.setImageResource(glide, slide, false)
|
binding.quoteViewAttachmentThumbnailImageView.root.setImageResource(glide, slide, false)
|
||||||
binding.quoteViewAttachmentThumbnailImageView.root.isVisible = true
|
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 android.widget.LinearLayout
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import com.squareup.phrase.Phrase
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import network.loki.messenger.databinding.ViewUntrustedAttachmentBinding
|
import network.loki.messenger.databinding.ViewUntrustedAttachmentBinding
|
||||||
|
import org.session.libsession.utilities.StringSubstitutionConstants.FILE_TYPE_KEY
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.conversation.v2.dialogs.DownloadDialog
|
import org.thoughtcrime.securesms.conversation.v2.dialogs.DownloadDialog
|
||||||
import org.thoughtcrime.securesms.util.ActivityDispatcher
|
import org.thoughtcrime.securesms.util.ActivityDispatcher
|
||||||
import java.util.Locale
|
|
||||||
|
|
||||||
class UntrustedAttachmentView: LinearLayout {
|
class UntrustedAttachmentView: LinearLayout {
|
||||||
private val binding: ViewUntrustedAttachmentBinding by lazy { ViewUntrustedAttachmentBinding.bind(this) }
|
private val binding: ViewUntrustedAttachmentBinding by lazy { ViewUntrustedAttachmentBinding.bind(this) }
|
||||||
@ -30,13 +31,17 @@ class UntrustedAttachmentView: LinearLayout {
|
|||||||
// region Updating
|
// region Updating
|
||||||
fun bind(attachmentType: AttachmentType, @ColorInt textColor: Int) {
|
fun bind(attachmentType: AttachmentType, @ColorInt textColor: Int) {
|
||||||
val (iconRes, stringRes) = when (attachmentType) {
|
val (iconRes, stringRes) = when (attachmentType) {
|
||||||
AttachmentType.AUDIO -> R.drawable.ic_microphone to R.string.Slide_audio
|
AttachmentType.AUDIO -> R.drawable.ic_microphone to R.string.audio
|
||||||
AttachmentType.DOCUMENT -> R.drawable.ic_document_large_light to R.string.document
|
AttachmentType.DOCUMENT -> R.drawable.ic_document_large_light to R.string.files
|
||||||
AttachmentType.MEDIA -> R.drawable.ic_image_white_24dp to R.string.media
|
AttachmentType.MEDIA -> R.drawable.ic_image_white_24dp to R.string.media
|
||||||
}
|
}
|
||||||
val iconDrawable = ContextCompat.getDrawable(context,iconRes)!!
|
val iconDrawable = ContextCompat.getDrawable(context,iconRes)!!
|
||||||
iconDrawable.mutate().setTint(textColor)
|
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.untrustedAttachmentIcon.setImageDrawable(iconDrawable)
|
||||||
binding.untrustedAttachmentTitle.text = text
|
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.MessageRecord
|
||||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
||||||
import org.thoughtcrime.securesms.database.model.SmsMessageRecord
|
import org.thoughtcrime.securesms.database.model.SmsMessageRecord
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp
|
import com.bumptech.glide.Glide
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import com.bumptech.glide.RequestManager
|
||||||
import org.thoughtcrime.securesms.util.GlowViewUtilities
|
import org.thoughtcrime.securesms.util.GlowViewUtilities
|
||||||
import org.thoughtcrime.securesms.util.SearchUtil
|
import org.thoughtcrime.securesms.util.SearchUtil
|
||||||
import org.thoughtcrime.securesms.util.getAccentColor
|
import org.thoughtcrime.securesms.util.getAccentColor
|
||||||
@ -63,7 +63,7 @@ class VisibleMessageContentView : ConstraintLayout {
|
|||||||
message: MessageRecord,
|
message: MessageRecord,
|
||||||
isStartOfMessageCluster: Boolean = true,
|
isStartOfMessageCluster: Boolean = true,
|
||||||
isEndOfMessageCluster: Boolean = true,
|
isEndOfMessageCluster: Boolean = true,
|
||||||
glide: GlideRequests = GlideApp.with(this),
|
glide: RequestManager = Glide.with(this),
|
||||||
thread: Recipient,
|
thread: Recipient,
|
||||||
searchQuery: String? = null,
|
searchQuery: String? = null,
|
||||||
contactIsTrusted: Boolean = true,
|
contactIsTrusted: Boolean = true,
|
||||||
@ -117,7 +117,7 @@ class VisibleMessageContentView : ConstraintLayout {
|
|||||||
binding.quoteView.root.isVisible = true
|
binding.quoteView.root.isVisible = true
|
||||||
val quote = message.quote!!
|
val quote = message.quote!!
|
||||||
val quoteText = if (quote.isOriginalMissing) {
|
val quoteText = if (quote.isOriginalMissing) {
|
||||||
context.getString(R.string.QuoteView_original_missing)
|
context.getString(R.string.messageErrorOriginal)
|
||||||
} else {
|
} else {
|
||||||
quote.text
|
quote.text
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ import android.view.MotionEvent
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import android.widget.LinearLayout
|
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
@ -27,6 +26,13 @@ import androidx.core.os.bundleOf
|
|||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.view.marginBottom
|
import androidx.core.view.marginBottom
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
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.R
|
||||||
import network.loki.messenger.databinding.ViewEmojiReactionsBinding
|
import network.loki.messenger.databinding.ViewEmojiReactionsBinding
|
||||||
import network.loki.messenger.databinding.ViewVisibleMessageBinding
|
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.database.model.MessageRecord
|
||||||
import org.thoughtcrime.securesms.groups.OpenGroupManager
|
import org.thoughtcrime.securesms.groups.OpenGroupManager
|
||||||
import org.thoughtcrime.securesms.home.UserDetailsBottomSheet
|
import org.thoughtcrime.securesms.home.UserDetailsBottomSheet
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp
|
import com.bumptech.glide.Glide
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import com.bumptech.glide.RequestManager
|
||||||
import org.thoughtcrime.securesms.util.DateUtils
|
import org.thoughtcrime.securesms.util.DateUtils
|
||||||
import org.thoughtcrime.securesms.util.disableClipping
|
import org.thoughtcrime.securesms.util.disableClipping
|
||||||
import org.thoughtcrime.securesms.util.toDp
|
import org.thoughtcrime.securesms.util.toDp
|
||||||
import org.thoughtcrime.securesms.util.toPx
|
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"
|
private const val TAG = "VisibleMessageView"
|
||||||
|
|
||||||
@ -141,7 +140,7 @@ class VisibleMessageView : FrameLayout {
|
|||||||
message: MessageRecord,
|
message: MessageRecord,
|
||||||
previous: MessageRecord? = null,
|
previous: MessageRecord? = null,
|
||||||
next: MessageRecord? = null,
|
next: MessageRecord? = null,
|
||||||
glide: GlideRequests = GlideApp.with(this),
|
glide: RequestManager = Glide.with(this),
|
||||||
searchQuery: String? = null,
|
searchQuery: String? = null,
|
||||||
contact: Contact? = null,
|
contact: Contact? = null,
|
||||||
senderAccountID: String,
|
senderAccountID: String,
|
||||||
@ -384,37 +383,37 @@ class VisibleMessageView : FrameLayout {
|
|||||||
message.isFailed ->
|
message.isFailed ->
|
||||||
MessageStatusInfo(R.drawable.ic_delivery_status_failed,
|
MessageStatusInfo(R.drawable.ic_delivery_status_failed,
|
||||||
getThemedColor(context, R.attr.danger),
|
getThemedColor(context, R.attr.danger),
|
||||||
R.string.delivery_status_failed
|
R.string.messageStatusFailedToSend
|
||||||
)
|
)
|
||||||
message.isSyncFailed ->
|
message.isSyncFailed ->
|
||||||
MessageStatusInfo(
|
MessageStatusInfo(
|
||||||
R.drawable.ic_delivery_status_failed,
|
R.drawable.ic_delivery_status_failed,
|
||||||
context.getColor(R.color.accent_orange),
|
context.getColor(R.color.accent_orange),
|
||||||
R.string.delivery_status_sync_failed
|
R.string.messageStatusFailedToSync
|
||||||
)
|
)
|
||||||
message.isPending ->
|
message.isPending ->
|
||||||
MessageStatusInfo(
|
MessageStatusInfo(
|
||||||
R.drawable.ic_delivery_status_sending,
|
R.drawable.ic_delivery_status_sending,
|
||||||
context.getColorFromAttr(R.attr.message_status_color),
|
context.getColorFromAttr(R.attr.message_status_color),
|
||||||
R.string.delivery_status_sending
|
R.string.sending
|
||||||
)
|
)
|
||||||
message.isSyncing || message.isResyncing ->
|
message.isSyncing || message.isResyncing ->
|
||||||
MessageStatusInfo(
|
MessageStatusInfo(
|
||||||
R.drawable.ic_delivery_status_sending,
|
R.drawable.ic_delivery_status_sending,
|
||||||
context.getColorFromAttr(R.attr.message_status_color),
|
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 ->
|
message.isRead || message.isIncoming ->
|
||||||
MessageStatusInfo(
|
MessageStatusInfo(
|
||||||
R.drawable.ic_delivery_status_read,
|
R.drawable.ic_delivery_status_read,
|
||||||
context.getColorFromAttr(R.attr.message_status_color),
|
context.getColorFromAttr(R.attr.message_status_color),
|
||||||
R.string.delivery_status_read
|
R.string.read
|
||||||
)
|
)
|
||||||
message.isSent ->
|
message.isSent ->
|
||||||
MessageStatusInfo(
|
MessageStatusInfo(
|
||||||
R.drawable.ic_delivery_status_sent,
|
R.drawable.ic_delivery_status_sent,
|
||||||
context.getColorFromAttr(R.attr.message_status_color),
|
context.getColorFromAttr(R.attr.message_status_color),
|
||||||
R.string.delivery_status_sent
|
R.string.disappearingMessagesSent
|
||||||
)
|
)
|
||||||
else -> {
|
else -> {
|
||||||
// The message isn't one we care about for message statuses we display to the user (i.e.,
|
// 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.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import com.squareup.phrase.Phrase
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import network.loki.messenger.databinding.ViewSearchBottomBarBinding
|
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 {
|
class SearchBottomBar : LinearLayout {
|
||||||
private lateinit var binding: ViewSearchBottomBarBinding
|
private lateinit var binding: ViewSearchBottomBarBinding
|
||||||
@ -35,7 +37,10 @@ class SearchBottomBar : LinearLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (count > 0) {
|
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 {
|
} else {
|
||||||
searchPosition.text = ""
|
searchPosition.text = ""
|
||||||
}
|
}
|
||||||
@ -43,6 +48,44 @@ class SearchBottomBar : LinearLayout {
|
|||||||
setViewEnabled(searchDown, position > 0)
|
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() {
|
fun showLoading() {
|
||||||
binding.searchProgressWheel.visibility = VISIBLE
|
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 {
|
class SearchResult(private val results: CursorList<MessageResult?>, val position: Int) : Closeable {
|
||||||
|
|
||||||
fun getResults(): List<MessageResult?> {
|
fun getResults(): List<MessageResult?> {
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms.conversation.v2.utilities;
|
package org.thoughtcrime.securesms.conversation.v2.utilities;
|
||||||
|
|
||||||
|
import static org.session.libsession.utilities.StringSubstitutionConstants.APP_NAME_KEY;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
@ -30,10 +32,14 @@ import android.provider.OpenableColumns;
|
|||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
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.libsession.utilities.recipients.Recipient;
|
||||||
import org.session.libsignal.utilities.ListenableFuture;
|
import org.session.libsignal.utilities.ListenableFuture;
|
||||||
import org.session.libsignal.utilities.Log;
|
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.AudioSlide;
|
||||||
import org.thoughtcrime.securesms.mms.DocumentSlide;
|
import org.thoughtcrime.securesms.mms.DocumentSlide;
|
||||||
import org.thoughtcrime.securesms.mms.GifSlide;
|
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.ImageSlide;
|
||||||
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
||||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
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.providers.BlobProvider;
|
||||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
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 {
|
public class AttachmentManager {
|
||||||
|
|
||||||
private final static String TAG = AttachmentManager.class.getSimpleName();
|
private final static String TAG = AttachmentManager.class.getSimpleName();
|
||||||
@ -126,7 +125,7 @@ public class AttachmentManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("StaticFieldLeak")
|
@SuppressLint("StaticFieldLeak")
|
||||||
public ListenableFuture<Boolean> setMedia(@NonNull final GlideRequests glideRequests,
|
public ListenableFuture<Boolean> setMedia(@NonNull final RequestManager glideRequests,
|
||||||
@NonNull final Uri uri,
|
@NonNull final Uri uri,
|
||||||
@NonNull final MediaType mediaType,
|
@NonNull final MediaType mediaType,
|
||||||
@NonNull final MediaConstraints constraints,
|
@NonNull final MediaConstraints constraints,
|
||||||
@ -252,13 +251,31 @@ public class AttachmentManager {
|
|||||||
} else {
|
} else {
|
||||||
builder = builder.request(Manifest.permission.READ_EXTERNAL_STORAGE);
|
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.
|
.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();
|
.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void selectGallery(Activity activity, int requestCode, @NonNull Recipient recipient, @NonNull String body) {
|
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);
|
Permissions.PermissionsBuilder builder = Permissions.with(activity);
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
builder = builder.request(Manifest.permission.READ_MEDIA_VIDEO)
|
builder = builder.request(Manifest.permission.READ_MEDIA_VIDEO)
|
||||||
@ -266,8 +283,8 @@ public class AttachmentManager {
|
|||||||
} else {
|
} else {
|
||||||
builder = builder.request(Manifest.permission.READ_EXTERNAL_STORAGE);
|
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))
|
builder.withPermanentDenialDialog(cameraPermissionDeniedTxt)
|
||||||
.withRationaleDialog(activity.getString(R.string.ConversationActivity_to_send_photos_and_video_allow_signal_access_to_storage), R.drawable.ic_baseline_photo_library_24)
|
.withRationaleDialog(needStoragePermissionTxt, R.drawable.ic_baseline_photo_library_24)
|
||||||
.onAllGranted(() -> activity.startActivityForResult(MediaSendActivity.buildGalleryIntent(activity, recipient, body), requestCode))
|
.onAllGranted(() -> activity.startActivityForResult(MediaSendActivity.buildGalleryIntent(activity, recipient, body), requestCode))
|
||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
@ -291,10 +308,19 @@ public class AttachmentManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void capturePhoto(Activity activity, int requestCode, Recipient recipient) {
|
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)
|
Permissions.with(activity)
|
||||||
.request(Manifest.permission.CAMERA)
|
.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))
|
.withPermanentDenialDialog(cameraPermissionDeniedTxt)
|
||||||
.withRationaleDialog(activity.getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera),R.drawable.ic_baseline_photo_camera_24)
|
.withRationaleDialog(requireCameraPermissionTxt, R.drawable.ic_baseline_photo_camera_24)
|
||||||
.onAllGranted(() -> {
|
.onAllGranted(() -> {
|
||||||
Intent captureIntent = MediaSendActivity.buildCameraIntent(activity, recipient);
|
Intent captureIntent = MediaSendActivity.buildCameraIntent(activity, recipient);
|
||||||
if (captureIntent.resolveActivity(activity.getPackageManager()) != null) {
|
if (captureIntent.resolveActivity(activity.getPackageManager()) != null) {
|
||||||
@ -326,7 +352,7 @@ public class AttachmentManager {
|
|||||||
activity.startActivityForResult(intent, requestCode);
|
activity.startActivityForResult(intent, requestCode);
|
||||||
} catch (ActivityNotFoundException anfe) {
|
} catch (ActivityNotFoundException anfe) {
|
||||||
Log.w(TAG, "couldn't complete ACTION_GET_CONTENT intent, no activity found. falling back.");
|
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 userPublicKey = TextSecurePreferences.getLocalNumber(context)!!
|
||||||
val openGroup by lazy { DatabaseComponent.get(context).storage().getOpenGroup(threadID) }
|
val openGroup by lazy { DatabaseComponent.get(context).storage().getOpenGroup(threadID) }
|
||||||
|
|
||||||
// format the mention text
|
// Format the mention text
|
||||||
if (matcher.find(startIndex)) {
|
if (matcher.find(startIndex)) {
|
||||||
while (true) {
|
while (true) {
|
||||||
val publicKey = text.subSequence(matcher.start() + 1, matcher.end()).toString() // +1 to get rid of the @
|
val publicKey = text.subSequence(matcher.start() + 1, matcher.end()).toString() // +1 to get rid of the @
|
||||||
|
|
||||||
val isYou = isYou(publicKey, userPublicKey, openGroup)
|
val isYou = isYou(publicKey, userPublicKey, openGroup)
|
||||||
val userDisplayName: String? = if (isYou) {
|
val userDisplayName: String? = if (isYou) {
|
||||||
context.getString(R.string.MessageRecord_you)
|
context.getString(R.string.you)
|
||||||
} else {
|
} else {
|
||||||
val contact = DatabaseComponent.get(context).sessionContactDatabase().getContactWithAccountID(publicKey)
|
val contact = DatabaseComponent.get(context).sessionContactDatabase().getContactWithAccountID(publicKey)
|
||||||
@Suppress("NAME_SHADOWING") val context = if (openGroup != null) Contact.ContactContext.OPEN_GROUP else Contact.ContactContext.REGULAR
|
@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 {
|
object NotificationUtils {
|
||||||
fun showNotifyDialog(context: Context, thread: Recipient, notifyTypeHandler: (Int)->Unit) {
|
fun showNotifyDialog(context: Context, thread: Recipient, notifyTypeHandler: (Int)->Unit) {
|
||||||
context.showSessionDialog {
|
context.showSessionDialog {
|
||||||
title(R.string.RecipientPreferenceActivity_notification_settings)
|
title(R.string.sessionNotifications)
|
||||||
singleChoiceItems(
|
singleChoiceItems(
|
||||||
context.resources.getStringArray(R.array.notify_types),
|
context.resources.getStringArray(R.array.notify_types),
|
||||||
thread.notifyType
|
thread.notifyType
|
||||||
|
@ -25,8 +25,8 @@ import org.session.libsignal.utilities.SettableFuture
|
|||||||
import org.thoughtcrime.securesms.components.GlideBitmapListeningTarget
|
import org.thoughtcrime.securesms.components.GlideBitmapListeningTarget
|
||||||
import org.thoughtcrime.securesms.components.GlideDrawableListeningTarget
|
import org.thoughtcrime.securesms.components.GlideDrawableListeningTarget
|
||||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri
|
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequest
|
import com.bumptech.glide.RequestBuilder
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import com.bumptech.glide.RequestManager
|
||||||
import org.thoughtcrime.securesms.mms.Slide
|
import org.thoughtcrime.securesms.mms.Slide
|
||||||
|
|
||||||
open class ThumbnailView @JvmOverloads constructor(
|
open class ThumbnailView @JvmOverloads constructor(
|
||||||
@ -104,13 +104,13 @@ open class ThumbnailView @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setImageResource(
|
fun setImageResource(
|
||||||
glide: GlideRequests,
|
glide: RequestManager,
|
||||||
slide: Slide,
|
slide: Slide,
|
||||||
isPreview: Boolean
|
isPreview: Boolean
|
||||||
): ListenableFuture<Boolean> = setImageResource(glide, slide, isPreview, 0, 0)
|
): ListenableFuture<Boolean> = setImageResource(glide, slide, isPreview, 0, 0)
|
||||||
|
|
||||||
fun setImageResource(
|
fun setImageResource(
|
||||||
glide: GlideRequests, slide: Slide,
|
glide: RequestManager, slide: Slide,
|
||||||
isPreview: Boolean, naturalWidth: Int,
|
isPreview: Boolean, naturalWidth: Int,
|
||||||
naturalHeight: Int
|
naturalHeight: Int
|
||||||
): ListenableFuture<Boolean> {
|
): ListenableFuture<Boolean> {
|
||||||
@ -152,9 +152,9 @@ open class ThumbnailView @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun buildThumbnailGlideRequest(
|
private fun buildThumbnailGlideRequest(
|
||||||
glide: GlideRequests,
|
glide: RequestManager,
|
||||||
slide: Slide
|
slide: Slide
|
||||||
): GlideRequest<Drawable> = glide.load(DecryptableUri(slide.thumbnailUri!!))
|
): RequestBuilder<Drawable> = glide.load(DecryptableUri(slide.thumbnailUri!!))
|
||||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||||
.overrideDimensions()
|
.overrideDimensions()
|
||||||
.transition(DrawableTransitionOptions.withCrossFade())
|
.transition(DrawableTransitionOptions.withCrossFade())
|
||||||
@ -162,21 +162,21 @@ open class ThumbnailView @JvmOverloads constructor(
|
|||||||
.missingThumbnailPicture(slide.isInProgress)
|
.missingThumbnailPicture(slide.isInProgress)
|
||||||
|
|
||||||
private fun buildPlaceholderGlideRequest(
|
private fun buildPlaceholderGlideRequest(
|
||||||
glide: GlideRequests,
|
glide: RequestManager,
|
||||||
slide: Slide
|
slide: Slide
|
||||||
): GlideRequest<Bitmap> = glide.asBitmap()
|
): RequestBuilder<Bitmap> = glide.asBitmap()
|
||||||
.load(slide.getPlaceholderRes(context.theme))
|
.load(slide.getPlaceholderRes(context.theme))
|
||||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||||
.overrideDimensions()
|
.overrideDimensions()
|
||||||
.fitCenter()
|
.fitCenter()
|
||||||
|
|
||||||
open fun clear(glideRequests: GlideRequests) {
|
open fun clear(glideRequests: RequestManager) {
|
||||||
glideRequests.clear(binding.thumbnailImage)
|
glideRequests.clear(binding.thumbnailImage)
|
||||||
slide = null
|
slide = null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setImageResource(
|
fun setImageResource(
|
||||||
glideRequests: GlideRequests,
|
glideRequests: RequestManager,
|
||||||
uri: Uri
|
uri: Uri
|
||||||
): ListenableFuture<Boolean> = glideRequests.load(DecryptableUri(uri))
|
): ListenableFuture<Boolean> = glideRequests.load(DecryptableUri(uri))
|
||||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||||
@ -184,19 +184,19 @@ open class ThumbnailView @JvmOverloads constructor(
|
|||||||
.transform(CenterCrop())
|
.transform(CenterCrop())
|
||||||
.intoDrawableTargetAsFuture()
|
.intoDrawableTargetAsFuture()
|
||||||
|
|
||||||
private fun GlideRequest<Drawable>.intoDrawableTargetAsFuture() =
|
private fun RequestBuilder<Drawable>.intoDrawableTargetAsFuture() =
|
||||||
SettableFuture<Boolean>().also {
|
SettableFuture<Boolean>().also {
|
||||||
binding.run {
|
binding.run {
|
||||||
GlideDrawableListeningTarget(thumbnailImage, thumbnailLoadIndicator, it)
|
GlideDrawableListeningTarget(thumbnailImage, thumbnailLoadIndicator, it)
|
||||||
}.let { into(it) }
|
}.let { into(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T> GlideRequest<T>.overrideDimensions() =
|
private fun <T> RequestBuilder<T>.overrideDimensions() =
|
||||||
dimensDelegate.resourceSize().takeIf { 0 !in it }
|
dimensDelegate.resourceSize().takeIf { 0 !in it }
|
||||||
?.let { override(it[WIDTH], it[HEIGHT]) }
|
?.let { override(it[WIDTH], it[HEIGHT]) }
|
||||||
?: override(getDefaultWidth(), getDefaultHeight())
|
?: override(getDefaultWidth(), getDefaultHeight())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T> GlideRequest<T>.missingThumbnailPicture(
|
private fun <T> RequestBuilder<T>.missingThumbnailPicture(
|
||||||
inProgress: Boolean
|
inProgress: Boolean
|
||||||
) = takeIf { inProgress } ?: apply(RequestOptions.errorOf(R.drawable.ic_missing_thumbnail_picture))
|
) = 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.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import net.zetetic.database.sqlcipher.SQLiteDatabase;
|
import net.zetetic.database.sqlcipher.SQLiteDatabase;
|
||||||
|
|
||||||
import network.loki.messenger.R;
|
|
||||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -24,10 +18,10 @@ public class DraftDatabase extends Database {
|
|||||||
public static final String DRAFT_VALUE = "value";
|
public static final String DRAFT_VALUE = "value";
|
||||||
|
|
||||||
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
|
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
|
||||||
THREAD_ID + " INTEGER, " + DRAFT_TYPE + " TEXT, " + DRAFT_VALUE + " TEXT);";
|
THREAD_ID + " INTEGER, " + DRAFT_TYPE + " TEXT, " + DRAFT_VALUE + " TEXT);";
|
||||||
|
|
||||||
public static final String[] CREATE_INDEXS = {
|
public static final String[] CREATE_INDEXS = {
|
||||||
"CREATE INDEX IF NOT EXISTS draft_thread_index ON " + TABLE_NAME + " (" + THREAD_ID + ");",
|
"CREATE INDEX IF NOT EXISTS draft_thread_index ON " + TABLE_NAME + " (" + THREAD_ID + ");",
|
||||||
};
|
};
|
||||||
|
|
||||||
public DraftDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
|
public DraftDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
|
||||||
@ -59,8 +53,8 @@ public class DraftDatabase extends Database {
|
|||||||
|
|
||||||
for (long threadId : threadIds) {
|
for (long threadId : threadIds) {
|
||||||
where.append(" OR ")
|
where.append(" OR ")
|
||||||
.append(THREAD_ID)
|
.append(THREAD_ID)
|
||||||
.append(" = ?");
|
.append(" = ?");
|
||||||
|
|
||||||
arguments.add(String.valueOf(threadId));
|
arguments.add(String.valueOf(threadId));
|
||||||
}
|
}
|
||||||
@ -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 class Draft {
|
||||||
public static final String TEXT = "text";
|
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 type;
|
||||||
private final String value;
|
private final String value;
|
||||||
@ -117,48 +109,10 @@ public class DraftDatabase extends Database {
|
|||||||
public String getValue() {
|
public String getValue() {
|
||||||
return value;
|
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> {
|
public static class Drafts extends LinkedList<Draft> {
|
||||||
private Draft getDraftOfType(String type) {
|
// We don't do anything with drafts of a given type anymore (image, audio etc.) - we store TEXT
|
||||||
for (Draft draft : this) {
|
// drafts, and any files or audio get sent to the recipient when added as a message.
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -166,6 +166,8 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
|||||||
|
|
||||||
const val RESET_SEQ_NO = "UPDATE $lastMessageServerIDTable SET $lastMessageServerID = 0;"
|
const val RESET_SEQ_NO = "UPDATE $lastMessageServerIDTable SET $lastMessageServerID = 0;"
|
||||||
|
|
||||||
|
const val EMPTY_VERSION = "0.0.0"
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +181,8 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
|||||||
val port = components.getOrNull(1)?.toIntOrNull() ?: return@mapNotNull null
|
val port = components.getOrNull(1)?.toIntOrNull() ?: return@mapNotNull null
|
||||||
val ed25519Key = components.getOrNull(2) ?: return@mapNotNull null
|
val ed25519Key = components.getOrNull(2) ?: return@mapNotNull null
|
||||||
val x25519Key = components.getOrNull(3) ?: 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()
|
}?.toSet() ?: setOf()
|
||||||
}
|
}
|
||||||
@ -192,6 +195,7 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
|||||||
if (keySet != null) {
|
if (keySet != null) {
|
||||||
string += "-${keySet.ed25519Key}-${keySet.x25519Key}"
|
string += "-${keySet.ed25519Key}-${keySet.x25519Key}"
|
||||||
}
|
}
|
||||||
|
string += "-${snode.version}"
|
||||||
string
|
string
|
||||||
}
|
}
|
||||||
val row = wrap(mapOf( Companion.dummyKey to "dummy_key", snodePool to snodePoolAsString ))
|
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) {
|
if (keySet != null) {
|
||||||
snodeAsString += "-${keySet.ed25519Key}-${keySet.x25519Key}"
|
snodeAsString += "-${keySet.ed25519Key}-${keySet.x25519Key}"
|
||||||
}
|
}
|
||||||
|
snodeAsString += "-${snode.version}"
|
||||||
val row = wrap(mapOf( Companion.indexPath to indexPath, Companion.snode to snodeAsString ))
|
val row = wrap(mapOf( Companion.indexPath to indexPath, Companion.snode to snodeAsString ))
|
||||||
database.insertOrUpdate(onionRequestPathTable, row, "${Companion.indexPath} = ?", wrap(indexPath))
|
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 port = components.getOrNull(1)?.toIntOrNull()
|
||||||
val ed25519Key = components.getOrNull(2)
|
val ed25519Key = components.getOrNull(2)
|
||||||
val x25519Key = components.getOrNull(3)
|
val x25519Key = components.getOrNull(3)
|
||||||
|
val version = components.getOrNull(4) ?: EMPTY_VERSION
|
||||||
if (port != null && ed25519Key != null && x25519Key != null) {
|
if (port != null && ed25519Key != null && x25519Key != null) {
|
||||||
Snode(address, port, Snode.KeySet(ed25519Key, x25519Key))
|
Snode(address, port, Snode.KeySet(ed25519Key, x25519Key), version)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@ -251,6 +257,11 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun clearSnodePool() {
|
||||||
|
val database = databaseHelper.writableDatabase
|
||||||
|
database.delete(snodePoolTable, null, null)
|
||||||
|
}
|
||||||
|
|
||||||
override fun clearOnionRequestPaths() {
|
override fun clearOnionRequestPaths() {
|
||||||
val database = databaseHelper.writableDatabase
|
val database = databaseHelper.writableDatabase
|
||||||
fun delete(indexPath: String) {
|
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 port = components.getOrNull(1)?.toIntOrNull() ?: return@mapNotNull null
|
||||||
val ed25519Key = components.getOrNull(2) ?: return@mapNotNull null
|
val ed25519Key = components.getOrNull(2) ?: return@mapNotNull null
|
||||||
val x25519Key = components.getOrNull(3) ?: 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()
|
}?.toSet()
|
||||||
}
|
}
|
||||||
|
@ -808,8 +808,8 @@ public class ThreadDatabase extends Database {
|
|||||||
private @NonNull String getFormattedBodyFor(@NonNull MessageRecord messageRecord) {
|
private @NonNull String getFormattedBodyFor(@NonNull MessageRecord messageRecord) {
|
||||||
if (messageRecord.isMms()) {
|
if (messageRecord.isMms()) {
|
||||||
MmsMessageRecord record = (MmsMessageRecord) messageRecord;
|
MmsMessageRecord record = (MmsMessageRecord) messageRecord;
|
||||||
if (record.getSharedContacts().size() > 0) {
|
if (!record.getSharedContacts().isEmpty()) {
|
||||||
Contact contact = ((MmsMessageRecord) messageRecord).getSharedContacts().get(0);
|
Contact contact = ((MmsMessageRecord)messageRecord).getSharedContacts().get(0);
|
||||||
return ContactUtil.getStringSummary(context, contact).toString();
|
return ContactUtil.getStringSummary(context, contact).toString();
|
||||||
}
|
}
|
||||||
String attachmentString = record.getSlideDeck().getBody();
|
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
|
// Notify the user of the issue so they know they can downgrade until the issue is fixed
|
||||||
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
|
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
|
||||||
String channelId = context.getString(R.string.NotificationChannel_failures);
|
String channelId = context.getString(R.string.failures);
|
||||||
|
|
||||||
if (NotificationChannels.supported()) {
|
if (NotificationChannels.supported()) {
|
||||||
NotificationChannel channel = new NotificationChannel(channelId, channelId, NotificationManager.IMPORTANCE_HIGH);
|
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.session.libsession.utilities.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.database.MediaDatabase;
|
import org.thoughtcrime.securesms.database.MediaDatabase;
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent;
|
import org.thoughtcrime.securesms.dependencies.DatabaseComponent;
|
||||||
|
import org.thoughtcrime.securesms.util.DateUtils;
|
||||||
|
import org.thoughtcrime.securesms.util.RelativeDay;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -88,15 +90,17 @@ public class BucketedThreadMediaLoader extends AsyncTaskLoader<BucketedThreadMed
|
|||||||
|
|
||||||
private final TimeBucket[] TIME_SECTIONS;
|
private final TimeBucket[] TIME_SECTIONS;
|
||||||
|
|
||||||
public BucketedThreadMedia(@NonNull Context context) {
|
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));
|
String localisedTodayString = DateUtils.INSTANCE.getLocalisedRelativeDayString(RelativeDay.TODAY);
|
||||||
this.YESTERDAY = new TimeBucket(context.getString(R.string.BucketedThreadMedia_Yesterday), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -2), TimeBucket.addToCalendar(Calendar.DAY_OF_YEAR, -1));
|
String localisedYesterdayString = DateUtils.INSTANCE.getLocalisedRelativeDayString(RelativeDay.YESTERDAY);
|
||||||
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};
|
|
||||||
this.OLDER = new MonthBuckets();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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) {
|
public void add(MediaDatabase.MediaRecord mediaRecord) {
|
||||||
for (TimeBucket timeSection : TIME_SECTIONS) {
|
for (TimeBucket timeSection : TIME_SECTIONS) {
|
||||||
|
@ -77,14 +77,6 @@ public class MediaMmsMessageRecord extends MmsMessageRecord {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpannableString getDisplayBody(@NonNull Context context) {
|
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);
|
return super.getDisplayBody(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,15 +57,7 @@ public class SmsMessageRecord extends MessageRecord {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpannableString getDisplayBody(@NonNull Context context) {
|
public SpannableString getDisplayBody(@NonNull Context context) {
|
||||||
if (SmsDatabase.Types.isFailedDecryptType(type)) {
|
return super.getDisplayBody(context);
|
||||||
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
|
@Override
|
||||||
|
@ -17,21 +17,25 @@
|
|||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms.database.model;
|
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.content.Context;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.text.Spannable;
|
import android.text.Spannable;
|
||||||
import android.text.SpannableString;
|
import android.text.SpannableString;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.style.StyleSpan;
|
import android.text.style.StyleSpan;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import com.squareup.phrase.Phrase;
|
||||||
import org.session.libsession.utilities.ExpirationUtil;
|
import org.session.libsession.utilities.ExpirationUtil;
|
||||||
import org.session.libsession.utilities.recipients.Recipient;
|
import org.session.libsession.utilities.recipients.Recipient;
|
||||||
|
import org.session.libsignal.utilities.Log;
|
||||||
import org.thoughtcrime.securesms.database.MmsSmsColumns;
|
import org.thoughtcrime.securesms.database.MmsSmsColumns;
|
||||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||||
|
|
||||||
import network.loki.messenger.R;
|
import network.loki.messenger.R;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,146 +46,174 @@ import network.loki.messenger.R;
|
|||||||
*/
|
*/
|
||||||
public class ThreadRecord extends DisplayRecord {
|
public class ThreadRecord extends DisplayRecord {
|
||||||
|
|
||||||
private @Nullable final Uri snippetUri;
|
private @Nullable final Uri snippetUri;
|
||||||
public @Nullable final MessageRecord lastMessage;
|
public @Nullable final MessageRecord lastMessage;
|
||||||
private final long count;
|
private final long count;
|
||||||
private final int unreadCount;
|
private final int unreadCount;
|
||||||
private final int unreadMentionCount;
|
private final int unreadMentionCount;
|
||||||
private final int distributionType;
|
private final int distributionType;
|
||||||
private final boolean archived;
|
private final boolean archived;
|
||||||
private final long expiresIn;
|
private final long expiresIn;
|
||||||
private final long lastSeen;
|
private final long lastSeen;
|
||||||
private final boolean pinned;
|
private final boolean pinned;
|
||||||
private final int initialRecipientHash;
|
private final int initialRecipientHash;
|
||||||
|
private final long dateSent;
|
||||||
|
|
||||||
public ThreadRecord(@NonNull String body, @Nullable Uri snippetUri,
|
public ThreadRecord(@NonNull String body, @Nullable Uri snippetUri,
|
||||||
@Nullable MessageRecord lastMessage, @NonNull Recipient recipient, long date, long count, int unreadCount,
|
@Nullable MessageRecord lastMessage, @NonNull Recipient recipient, long date, long count, int unreadCount,
|
||||||
int unreadMentionCount, long threadId, int deliveryReceiptCount, int status,
|
int unreadMentionCount, long threadId, int deliveryReceiptCount, int status,
|
||||||
long snippetType, int distributionType, boolean archived, long expiresIn,
|
long snippetType, int distributionType, boolean archived, long expiresIn,
|
||||||
long lastSeen, int readReceiptCount, boolean pinned)
|
long lastSeen, int readReceiptCount, boolean pinned)
|
||||||
{
|
{
|
||||||
super(body, recipient, date, date, threadId, status, deliveryReceiptCount, snippetType, readReceiptCount);
|
super(body, recipient, date, date, threadId, status, deliveryReceiptCount, snippetType, readReceiptCount);
|
||||||
this.snippetUri = snippetUri;
|
this.snippetUri = snippetUri;
|
||||||
this.lastMessage = lastMessage;
|
this.lastMessage = lastMessage;
|
||||||
this.count = count;
|
this.count = count;
|
||||||
this.unreadCount = unreadCount;
|
this.unreadCount = unreadCount;
|
||||||
this.unreadMentionCount = unreadMentionCount;
|
this.unreadMentionCount = unreadMentionCount;
|
||||||
this.distributionType = distributionType;
|
this.distributionType = distributionType;
|
||||||
this.archived = archived;
|
this.archived = archived;
|
||||||
this.expiresIn = expiresIn;
|
this.expiresIn = expiresIn;
|
||||||
this.lastSeen = lastSeen;
|
this.lastSeen = lastSeen;
|
||||||
this.pinned = pinned;
|
this.pinned = pinned;
|
||||||
this.initialRecipientHash = recipient.hashCode();
|
this.initialRecipientHash = recipient.hashCode();
|
||||||
}
|
this.dateSent = date;
|
||||||
|
|
||||||
public @Nullable Uri getSnippetUri() {
|
|
||||||
return snippetUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SpannableString getDisplayBody(@NonNull Context context) {
|
|
||||||
if (isGroupUpdateMessage()) {
|
|
||||||
return emphasisAdded(context.getString(R.string.ThreadRecord_group_updated));
|
|
||||||
} 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));
|
|
||||||
} 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));
|
|
||||||
} else if (MmsSmsColumns.Types.isDraftMessageType(type)) {
|
|
||||||
String draftText = context.getString(R.string.ThreadRecord_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));
|
|
||||||
} else if (SmsDatabase.Types.isIncomingCall(type)) {
|
|
||||||
return emphasisAdded(context.getString(network.loki.messenger.R.string.ThreadRecord_called_you));
|
|
||||||
} 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()));
|
|
||||||
} 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 time = ExpirationUtil.getExpirationDisplayValue(context, seconds);
|
|
||||||
return emphasisAdded(context.getString(R.string.ThreadRecord_disappearing_message_time_updated_to_s, time));
|
|
||||||
} else if (MmsSmsColumns.Types.isMediaSavedExtraction(type)) {
|
|
||||||
return emphasisAdded(context.getString(R.string.ThreadRecord_media_saved_by_s, getRecipient().toShortString()));
|
|
||||||
} 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));
|
|
||||||
} else if (MmsSmsColumns.Types.isMessageRequestResponse(type)) {
|
|
||||||
return emphasisAdded(context.getString(R.string.message_requests_accepted));
|
|
||||||
} else if (getCount() == 0) {
|
|
||||||
return new SpannableString(context.getString(R.string.ThreadRecord_empty_message));
|
|
||||||
} else {
|
|
||||||
if (TextUtils.isEmpty(getBody())) {
|
|
||||||
return new SpannableString(emphasisAdded(context.getString(R.string.ThreadRecord_media_message)));
|
|
||||||
} else {
|
|
||||||
return new SpannableString(getBody());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private SpannableString emphasisAdded(String sequence) {
|
public @Nullable Uri getSnippetUri() {
|
||||||
return emphasisAdded(sequence, 0, sequence.length());
|
return snippetUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SpannableString emphasisAdded(String sequence, int start, int end) {
|
private String getName() {
|
||||||
SpannableString spannable = new SpannableString(sequence);
|
String name = getRecipient().getName();
|
||||||
spannable.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC),
|
if (name == null) {
|
||||||
start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
Log.w("ThreadRecord", "Got a null name - using: Unknown");
|
||||||
return spannable;
|
name = "Unknown";
|
||||||
}
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
public long getCount() {
|
private String getDisappearingMsgExpiryTypeString(Context context) {
|
||||||
return count;
|
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();
|
||||||
|
|
||||||
public int getUnreadCount() {
|
// Note: This works because expireStarted is 0 for messages which are 'Disappear after read'
|
||||||
return unreadCount;
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
public int getUnreadMentionCount() {
|
@Override
|
||||||
return unreadMentionCount;
|
public SpannableString getDisplayBody(@NonNull Context context) {
|
||||||
}
|
if (isGroupUpdateMessage()) {
|
||||||
|
return emphasisAdded(context.getString(R.string.groupUpdated));
|
||||||
|
} else if (isOpenGroupInvitation()) {
|
||||||
|
return emphasisAdded(context.getString(R.string.communityInvitation));
|
||||||
|
} else if (MmsSmsColumns.Types.isLegacyType(type)) {
|
||||||
|
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.draft);
|
||||||
|
return emphasisAdded(draftText + " " + getBody(), 0, draftText.length());
|
||||||
|
} else if (SmsDatabase.Types.isOutgoingCall(type)) {
|
||||||
|
String txt = Phrase.from(context, R.string.callsYouCalled)
|
||||||
|
.put(NAME_KEY, getName())
|
||||||
|
.format().toString();
|
||||||
|
return emphasisAdded(txt);
|
||||||
|
} else if (SmsDatabase.Types.isIncomingCall(type)) {
|
||||||
|
String txt = Phrase.from(context, R.string.callsCalledYou)
|
||||||
|
.put(NAME_KEY, getName())
|
||||||
|
.format().toString();
|
||||||
|
return emphasisAdded(txt);
|
||||||
|
} else if (SmsDatabase.Types.isMissedCall(type)) {
|
||||||
|
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) {
|
||||||
|
String txt = Phrase.from(context, R.string.disappearingMessagesTurnedOff)
|
||||||
|
.put(NAME_KEY, getName())
|
||||||
|
.format().toString();
|
||||||
|
return emphasisAdded(txt);
|
||||||
|
}
|
||||||
|
|
||||||
public long getDate() {
|
// Implied that disappearing messages is enabled..
|
||||||
return getDateReceived();
|
String time = ExpirationUtil.getExpirationDisplayValue(context, seconds);
|
||||||
}
|
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);
|
||||||
|
|
||||||
public boolean isArchived() {
|
} else if (MmsSmsColumns.Types.isMediaSavedExtraction(type)) {
|
||||||
return archived;
|
String txt = Phrase.from(context, R.string.attachmentsMediaSaved)
|
||||||
}
|
.put(NAME_KEY, getName())
|
||||||
|
.format().toString();
|
||||||
|
return emphasisAdded(txt);
|
||||||
|
|
||||||
public int getDistributionType() {
|
} else if (MmsSmsColumns.Types.isScreenshotExtraction(type)) {
|
||||||
return distributionType;
|
String txt = Phrase.from(context, R.string.screenshotTaken)
|
||||||
}
|
.put(NAME_KEY, getName())
|
||||||
|
.format().toString();
|
||||||
|
return emphasisAdded(txt);
|
||||||
|
|
||||||
public long getExpiresIn() {
|
} else if (MmsSmsColumns.Types.isMessageRequestResponse(type)) {
|
||||||
return expiresIn;
|
return emphasisAdded(context.getString(R.string.messageRequestsAccepted));
|
||||||
}
|
} else if (getCount() == 0) {
|
||||||
|
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.mediaMessage)));
|
||||||
|
} else {
|
||||||
|
return new SpannableString(getBody());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public long getLastSeen() {
|
private SpannableString emphasisAdded(String sequence) {
|
||||||
return lastSeen;
|
return emphasisAdded(sequence, 0, sequence.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPinned() {
|
private SpannableString emphasisAdded(String sequence, int start, int end) {
|
||||||
return pinned;
|
SpannableString spannable = new SpannableString(sequence);
|
||||||
}
|
spannable.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC),
|
||||||
|
start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
return spannable;
|
||||||
|
}
|
||||||
|
|
||||||
public int getInitialRecipientHash() {
|
public long getCount() { return count; }
|
||||||
return initialRecipientHash;
|
|
||||||
}
|
public int getUnreadCount() { return unreadCount; }
|
||||||
|
|
||||||
|
public int getUnreadMentionCount() { return unreadMentionCount; }
|
||||||
|
|
||||||
|
public long getDate() { return getDateReceived(); }
|
||||||
|
|
||||||
|
public boolean isArchived() { return archived; }
|
||||||
|
|
||||||
|
public int getDistributionType() { return distributionType; }
|
||||||
|
|
||||||
|
public long getExpiresIn() { return expiresIn; }
|
||||||
|
|
||||||
|
public long getLastSeen() { return lastSeen; }
|
||||||
|
|
||||||
|
public boolean isPinned() { return pinned; }
|
||||||
|
|
||||||
|
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),
|
PLACES(4, "Places", R.attr.emoji_category_places),
|
||||||
OBJECTS(5, "Objects", R.attr.emoji_category_objects),
|
OBJECTS(5, "Objects", R.attr.emoji_category_objects),
|
||||||
SYMBOLS(6, "Symbols", R.attr.emoji_category_symbol),
|
SYMBOLS(6, "Symbols", R.attr.emoji_category_symbol),
|
||||||
FLAGS(7, "Flags", R.attr.emoji_category_flags),
|
FLAGS(7, "Flags", R.attr.emoji_category_flags);
|
||||||
EMOTICONS(8, "Emoticons", R.attr.emoji_category_emoticons);
|
|
||||||
|
|
||||||
@StringRes
|
@StringRes
|
||||||
fun getCategoryLabel(): Int {
|
fun getCategoryLabel(): Int {
|
||||||
@ -31,15 +30,14 @@ enum class EmojiCategory(val priority: Int, val key: String, @AttrRes val icon:
|
|||||||
@StringRes
|
@StringRes
|
||||||
fun getCategoryLabel(@AttrRes iconAttr: Int): Int {
|
fun getCategoryLabel(@AttrRes iconAttr: Int): Int {
|
||||||
return when (iconAttr) {
|
return when (iconAttr) {
|
||||||
R.attr.emoji_category_people -> R.string.ReactWithAnyEmojiBottomSheetDialogFragment__smileys_and_people
|
R.attr.emoji_category_people -> R.string.emojiCategorySmileys
|
||||||
R.attr.emoji_category_nature -> R.string.ReactWithAnyEmojiBottomSheetDialogFragment__nature
|
R.attr.emoji_category_nature -> R.string.emojiCategoryAnimals
|
||||||
R.attr.emoji_category_foods -> R.string.ReactWithAnyEmojiBottomSheetDialogFragment__food
|
R.attr.emoji_category_foods -> R.string.emojiCategoryFood
|
||||||
R.attr.emoji_category_activity -> R.string.ReactWithAnyEmojiBottomSheetDialogFragment__activities
|
R.attr.emoji_category_activity -> R.string.emojiCategoryActivities
|
||||||
R.attr.emoji_category_places -> R.string.ReactWithAnyEmojiBottomSheetDialogFragment__places
|
R.attr.emoji_category_places -> R.string.emojiCategoryTravel
|
||||||
R.attr.emoji_category_objects -> R.string.ReactWithAnyEmojiBottomSheetDialogFragment__objects
|
R.attr.emoji_category_objects -> R.string.emojiCategoryObjects
|
||||||
R.attr.emoji_category_symbol -> R.string.ReactWithAnyEmojiBottomSheetDialogFragment__symbols
|
R.attr.emoji_category_symbol -> R.string.emojiCategorySymbols
|
||||||
R.attr.emoji_category_flags -> R.string.ReactWithAnyEmojiBottomSheetDialogFragment__flags
|
R.attr.emoji_category_flags -> R.string.emojiCategoryFlags
|
||||||
R.attr.emoji_category_emoticons -> R.string.ReactWithAnyEmojiBottomSheetDialogFragment__emoticons
|
|
||||||
else -> throw AssertionError()
|
else -> throw AssertionError()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,10 +110,12 @@ class EmojiSource(
|
|||||||
val parsedData: ParsedEmojiData = EmojiJsonParser.parse(it, ::getAssetsUri).getOrThrow()
|
val parsedData: ParsedEmojiData = EmojiJsonParser.parse(it, ::getAssetsUri).getOrThrow()
|
||||||
return EmojiSource(
|
return EmojiSource(
|
||||||
ScreenDensity.xhdpiRelativeDensityScaleFactor("xhdpi"),
|
ScreenDensity.xhdpiRelativeDensityScaleFactor("xhdpi"),
|
||||||
|
|
||||||
parsedData.copy(
|
parsedData.copy(
|
||||||
displayPages = parsedData.displayPages + PAGE_EMOTICONS,
|
displayPages = parsedData.displayPages,
|
||||||
dataPages = parsedData.dataPages + PAGE_EMOTICONS
|
dataPages = parsedData.dataPages
|
||||||
)
|
)
|
||||||
|
|
||||||
) { uri: Uri -> EmojiPage.Asset(uri) }
|
) { 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)
|
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 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) {
|
protected void onPostExecute(@Nullable Uri uri) {
|
||||||
if (uri == null) {
|
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) {
|
} else if (viewHolder == finishingImage) {
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
intent.setData(uri);
|
intent.setData(uri);
|
||||||
@ -165,8 +165,8 @@ public class GiphyActivity extends PassphraseRequiredActionBarActivity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CharSequence getPageTitle(int position) {
|
public CharSequence getPageTitle(int position) {
|
||||||
if (position == 0) return context.getString(R.string.GiphyFragmentPagerAdapter_gifs);
|
if (position == 0) return context.getString(R.string.gif);
|
||||||
else return context.getString(R.string.GiphyFragmentPagerAdapter_stickers);
|
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.session.libsignal.utilities.Log;
|
||||||
import org.thoughtcrime.securesms.giph.model.ChunkedImageUrl;
|
import org.thoughtcrime.securesms.giph.model.ChunkedImageUrl;
|
||||||
import org.thoughtcrime.securesms.giph.model.GiphyImage;
|
import org.thoughtcrime.securesms.giph.model.GiphyImage;
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
import com.bumptech.glide.Glide;
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
import com.bumptech.glide.RequestManager;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ExecutionException;
|
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 static final String TAG = GiphyAdapter.class.getSimpleName();
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final GlideRequests glideRequests;
|
private final RequestManager glideRequests;
|
||||||
|
|
||||||
private List<GiphyImage> images;
|
private List<GiphyImage> images;
|
||||||
private OnItemClickListener listener;
|
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.context = context.getApplicationContext();
|
||||||
this.glideRequests = glideRequests;
|
this.glideRequests = glideRequests;
|
||||||
this.images = images;
|
this.images = images;
|
||||||
@ -150,7 +150,7 @@ class GiphyAdapter extends RecyclerView.Adapter<GiphyAdapter.GiphyViewHolder> {
|
|||||||
holder.thumbnail.setAspectRatio(image.getGifAspectRatio());
|
holder.thumbnail.setAspectRatio(image.getGifAspectRatio());
|
||||||
holder.gifProgress.setVisibility(View.GONE);
|
holder.gifProgress.setVisibility(View.GONE);
|
||||||
|
|
||||||
RequestBuilder<Drawable> thumbnailRequest = GlideApp.with(context)
|
RequestBuilder<Drawable> thumbnailRequest = Glide.with(context)
|
||||||
.load(new ChunkedImageUrl(image.getStillUrl(), image.getStillSize()))
|
.load(new ChunkedImageUrl(image.getStillUrl(), image.getStillSize()))
|
||||||
.diskCacheStrategy(DiskCacheStrategy.NONE);
|
.diskCacheStrategy(DiskCacheStrategy.NONE);
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user