Remove a lot of dead code.

Resolves #14672
This commit is contained in:
Jesse Weinstein
2026-03-12 15:52:30 -04:00
committed by Cody Henthorne
parent 98d9b12438
commit ebccc6db30
38 changed files with 9 additions and 1737 deletions

View File

@@ -6,7 +6,6 @@
package org.thoughtcrime.securesms.database
import androidx.test.platform.app.InstrumentationRegistry
import io.mockk.every
import io.mockk.mockkStatic
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
@@ -35,8 +34,6 @@ class ThreadTableTest_active {
fun setUp() {
mockkStatic(RemoteConfig::class)
every { RemoteConfig.showChatFolders } returns true
recipient = Recipient.resolved(SignalDatabase.recipients.getOrInsertFromServiceId(ACI.from(UUID.randomUUID())))
}

View File

@@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.database
import io.mockk.every
import io.mockk.mockkStatic
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
@@ -30,8 +29,6 @@ class ThreadTableTest_pinned {
fun setUp() {
mockkStatic(RemoteConfig::class)
every { RemoteConfig.showChatFolders } returns true
recipient = Recipient.resolved(SignalDatabase.recipients.getOrInsertFromServiceId(ACI.from(UUID.randomUUID())))
}

View File

@@ -1,69 +0,0 @@
package org.thoughtcrime.securesms.color;
import android.content.Context;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class MaterialColors {
public static final MaterialColorList CONVERSATION_PALETTE = new MaterialColorList(new ArrayList<>(Arrays.asList(
MaterialColor.PLUM,
MaterialColor.CRIMSON,
MaterialColor.VERMILLION,
MaterialColor.VIOLET,
MaterialColor.INDIGO,
MaterialColor.TAUPE,
MaterialColor.ULTRAMARINE,
MaterialColor.BLUE,
MaterialColor.TEAL,
MaterialColor.FOREST,
MaterialColor.WINTERGREEN,
MaterialColor.BURLAP,
MaterialColor.STEEL
)));
public static class MaterialColorList {
private final List<MaterialColor> colors;
private MaterialColorList(List<MaterialColor> colors) {
this.colors = colors;
}
public MaterialColor get(int index) {
return colors.get(index);
}
public int size() {
return colors.size();
}
public @Nullable MaterialColor getByColor(Context context, int colorValue) {
for (MaterialColor color : colors) {
if (color.represents(context, colorValue)) {
return color;
}
}
return null;
}
public @ColorInt int[] asConversationColorArray(@NonNull Context context) {
int[] results = new int[colors.size()];
int index = 0;
for (MaterialColor color : colors) {
results[index++] = color.toConversationColor(context);
}
return results;
}
}
}

View File

@@ -1,76 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.compose
import android.app.Activity
import android.graphics.Bitmap
import android.graphics.Canvas
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.view.PixelCopy
import android.view.View
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.layout.LayoutCoordinates
import androidx.compose.ui.layout.boundsInRoot
import androidx.compose.ui.layout.boundsInWindow
/**
* Helper class for screenshotting compose views.
*
* You need to call bind from the compose, passing in the
* LocalView.current view with bounds fetched from when the
* composable is globally positioned.
*
* See QrCodeBadge.kt for an example
*/
class ScreenshotController {
private var screenshotCallback: (() -> Bitmap?)? = null
fun bind(view: View, bounds: Rect?) {
if (bounds == null) {
screenshotCallback = null
return
}
screenshotCallback = {
val bitmap = Bitmap.createBitmap(
bounds.width.toInt(),
bounds.height.toInt(),
Bitmap.Config.ARGB_8888
)
if (Build.VERSION.SDK_INT >= 26) {
PixelCopy.request(
(view.context as Activity).window,
android.graphics.Rect(bounds.left.toInt(), bounds.top.toInt(), bounds.right.toInt(), bounds.bottom.toInt()),
bitmap,
{},
Handler(Looper.getMainLooper())
)
} else {
val canvas = Canvas(bitmap)
.apply {
translate(-bounds.left, -bounds.top)
}
view.draw(canvas)
}
bitmap
}
}
fun screenshot(): Bitmap? {
return screenshotCallback?.invoke()
}
}
fun LayoutCoordinates.getScreenshotBounds(): Rect {
return if (Build.VERSION.SDK_INT >= 26) {
this.boundsInWindow()
} else {
this.boundsInRoot()
}
}

View File

@@ -1,168 +0,0 @@
package org.thoughtcrime.securesms.contacts;
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.database.AbstractCursor;
import android.database.CursorWindow;
import java.util.ArrayList;
/**
* A convenience class that presents a two-dimensional ArrayList
* as a Cursor.
*/
public class ArrayListCursor extends AbstractCursor {
private String[] mColumnNames;
private ArrayList<Object>[] mRows;
@SuppressWarnings({"unchecked"})
public ArrayListCursor(String[] columnNames, ArrayList<ArrayList> rows) {
int colCount = columnNames.length;
boolean foundID = false;
// Add an _id column if not in columnNames
for (int i = 0; i < colCount; ++i) {
if (columnNames[i].compareToIgnoreCase("_id") == 0) {
mColumnNames = columnNames;
foundID = true;
break;
}
}
if (!foundID) {
mColumnNames = new String[colCount + 1];
System.arraycopy(columnNames, 0, mColumnNames, 0, columnNames.length);
mColumnNames[colCount] = "_id";
}
int rowCount = rows.size();
mRows = new ArrayList[rowCount];
for (int i = 0; i < rowCount; ++i) {
mRows[i] = rows.get(i);
if (!foundID) {
mRows[i].add(i);
}
}
}
@Override
public void fillWindow(int position, CursorWindow window) {
if (position < 0 || position > getCount()) {
return;
}
window.acquireReference();
try {
int oldpos = mPos;
mPos = position - 1;
window.clear();
window.setStartPosition(position);
int columnNum = getColumnCount();
window.setNumColumns(columnNum);
while (moveToNext() && window.allocRow()) {
for (int i = 0; i < columnNum; i++) {
final Object data = mRows[mPos].get(i);
if (data != null) {
if (data instanceof byte[]) {
byte[] field = (byte[]) data;
if (!window.putBlob(field, mPos, i)) {
window.freeLastRow();
break;
}
} else {
String field = data.toString();
if (!window.putString(field, mPos, i)) {
window.freeLastRow();
break;
}
}
} else {
if (!window.putNull(mPos, i)) {
window.freeLastRow();
break;
}
}
}
}
mPos = oldpos;
} catch (IllegalStateException e){
// simply ignore it
} finally {
window.releaseReference();
}
}
@Override
public int getCount() {
return mRows.length;
}
public boolean deleteRow() {
return false;
}
@Override
public String[] getColumnNames() {
return mColumnNames;
}
@Override
public byte[] getBlob(int columnIndex) {
return (byte[]) mRows[mPos].get(columnIndex);
}
@Override
public String getString(int columnIndex) {
Object cell = mRows[mPos].get(columnIndex);
return (cell == null) ? null : cell.toString();
}
@Override
public short getShort(int columnIndex) {
Number num = (Number) mRows[mPos].get(columnIndex);
return num.shortValue();
}
@Override
public int getInt(int columnIndex) {
Number num = (Number) mRows[mPos].get(columnIndex);
return num.intValue();
}
@Override
public long getLong(int columnIndex) {
Number num = (Number) mRows[mPos].get(columnIndex);
return num.longValue();
}
@Override
public float getFloat(int columnIndex) {
Number num = (Number) mRows[mPos].get(columnIndex);
return num.floatValue();
}
@Override
public double getDouble(int columnIndex) {
Number num = (Number) mRows[mPos].get(columnIndex);
return num.doubleValue();
}
@Override
public boolean isNull(int columnIndex) {
return mRows[mPos].get(columnIndex) == null;
}
}

View File

@@ -1,168 +0,0 @@
/**
* Copyright (C) 2011 Whisper Systems
* <p>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* <p>
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.contacts;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Contacts;
import java.util.LinkedList;
import java.util.List;
/**
* This class was originally a layer of indirection between
* ContactAccessorNewApi and ContactAccessorOldApi, which corresponded
* to the API changes between 1.x and 2.x.
*
* Now that we no longer support 1.x, this class mostly serves as a place
* to encapsulate Contact-related logic. It's still a singleton, mostly
* just because that's how it's currently called from everywhere.
*
* @author Moxie Marlinspike
*/
public class ContactAccessor {
private static final ContactAccessor instance = new ContactAccessor();
public static ContactAccessor getInstance() {
return instance;
}
public ContactData getContactData(Context context, Uri uri) {
String displayName = getNameFromContact(context, uri);
long id = Long.parseLong(uri.getLastPathSegment());
ContactData contactData = new ContactData(id, displayName);
try (Cursor numberCursor = context.getContentResolver().query(Phone.CONTENT_URI,
null,
Phone.CONTACT_ID + " = ?",
new String[] { contactData.id + "" },
null))
{
while (numberCursor != null && numberCursor.moveToNext()) {
int type = numberCursor.getInt(numberCursor.getColumnIndexOrThrow(Phone.TYPE));
String label = numberCursor.getString(numberCursor.getColumnIndexOrThrow(Phone.LABEL));
String number = numberCursor.getString(numberCursor.getColumnIndexOrThrow(Phone.NUMBER));
String typeLabel = Phone.getTypeLabel(context.getResources(), type, label).toString();
contactData.numbers.add(new NumberData(typeLabel, number));
}
}
return contactData;
}
private String getNameFromContact(Context context, Uri uri) {
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(uri, new String[] { Contacts.DISPLAY_NAME }, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
return cursor.getString(0);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
public static class NumberData implements Parcelable {
public static final Parcelable.Creator<NumberData> CREATOR = new Parcelable.Creator<NumberData>() {
public NumberData createFromParcel(Parcel in) {
return new NumberData(in);
}
public NumberData[] newArray(int size) {
return new NumberData[size];
}
};
public final String number;
public final String type;
public NumberData(String type, String number) {
this.type = type;
this.number = number;
}
public NumberData(Parcel in) {
number = in.readString();
type = in.readString();
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(number);
dest.writeString(type);
}
}
public static class ContactData implements Parcelable {
public static final Parcelable.Creator<ContactData> CREATOR = new Parcelable.Creator<ContactData>() {
public ContactData createFromParcel(Parcel in) {
return new ContactData(in);
}
public ContactData[] newArray(int size) {
return new ContactData[size];
}
};
public final long id;
public final String name;
public final List<NumberData> numbers;
public ContactData(long id, String name) {
this.id = id;
this.name = name;
this.numbers = new LinkedList<NumberData>();
}
public ContactData(Parcel in) {
id = in.readLong();
name = in.readString();
numbers = new LinkedList<NumberData>();
in.readTypedList(numbers, NumberData.CREATOR);
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(id);
dest.writeString(name);
dest.writeTypedList(numbers);
}
}
}

View File

@@ -1,35 +0,0 @@
/**
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.contacts;
/**
* Name and number tuple.
*
* @author Moxie Marlinspike
*
*/
public class NameAndNumber {
public String name;
public String number;
public NameAndNumber(String name, String number) {
this.name = name;
this.number = number;
}
public NameAndNumber() {}
}

View File

@@ -46,7 +46,6 @@ import org.thoughtcrime.securesms.storage.StorageSyncHelper
import org.thoughtcrime.securesms.util.FileUtils
import org.thoughtcrime.securesms.util.ServiceUtil
import org.thoughtcrime.securesms.util.SignalE164Util
import org.thoughtcrime.securesms.util.Triple
import org.whispersystems.signalservice.api.push.DistributionId
import java.io.File
import java.io.IOException

View File

@@ -1,28 +0,0 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.media
import android.media.MediaExtractor
import org.thoughtcrime.securesms.video.interfaces.MediaInput
import java.io.File
import java.io.IOException
/**
* A media input source that the system reads directly from the file.
*/
class FileMediaInput(private val file: File) : MediaInput {
@Throws(IOException::class)
override fun createExtractor(): MediaExtractor {
val extractor = MediaExtractor()
extractor.setDataSource(file.absolutePath)
return extractor
}
override fun hasSameInput(other: MediaInput): Boolean {
return other is FileMediaInput && other.file == this.file
}
override fun close() {}
}

View File

@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.messagerequests
import android.content.Context
import android.content.res.ColorStateList
import android.text.Html
import android.util.AttributeSet
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
@@ -12,7 +13,6 @@ import org.thoughtcrime.securesms.messagerequests.MessageRequestBarColorTheme.Co
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.CommunicationActions
import org.thoughtcrime.securesms.util.Debouncer
import org.thoughtcrime.securesms.util.HtmlUtil
import org.thoughtcrime.securesms.util.views.LearnMoreTextView
import org.thoughtcrime.securesms.util.visible
@@ -64,7 +64,7 @@ class MessageRequestsBottomView @JvmOverloads constructor(context: Context, attr
question.text = HtmlCompat.fromHtml(
context.getString(
message,
HtmlUtil.bold(recipient.getShortDisplayName(context))
bold(recipient.getShortDisplayName(context))
),
0
)
@@ -95,7 +95,7 @@ class MessageRequestsBottomView @JvmOverloads constructor(context: Context, attr
question.text = HtmlCompat.fromHtml(
context.getString(
R.string.MessageRequestBottomView_do_you_want_to_let_s_message_you_they_wont_know_youve_seen_their_messages_until_you_accept,
HtmlUtil.bold(recipient.getShortDisplayName(context))
bold(recipient.getShortDisplayName(context))
),
0
)
@@ -106,7 +106,7 @@ class MessageRequestsBottomView @JvmOverloads constructor(context: Context, attr
question.text = HtmlCompat.fromHtml(
context.getString(
R.string.MessageRequestBottomView_do_you_want_to_let_s_message_you_you_removed_them_before,
HtmlUtil.bold(recipient.getShortDisplayName(context))
bold(recipient.getShortDisplayName(context))
),
0
)
@@ -165,4 +165,8 @@ class MessageRequestsBottomView @JvmOverloads constructor(context: Context, attr
fun setReportOnClickListener(reportOnClickListener: OnClickListener?) {
report.setOnClickListener(reportOnClickListener)
}
fun bold(target: String): String {
return "<b>" + Html.escapeHtml(target) + "</b>"
}
}

View File

@@ -16,7 +16,6 @@
*/
package org.thoughtcrime.securesms.phonenumbers;
import android.telephony.PhoneNumberUtils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -38,15 +37,11 @@ public class NumberUtil {
/**
* Whether or not a number entered by the user is a valid phone or email address. Differs from
* {@link #isValidSmsOrEmail(String)} in that it only returns true for numbers that a user would
* PhoneNumberUtils.isWellFormedSmsAddress(String) in that it only returns true for numbers that a user would
* enter themselves, as opposed to the crazy network prefixes that could theoretically be in an
* SMS address.
*/
public static boolean isVisuallyValidNumberOrEmail(String number) {
return isVisuallyValidNumber(number) || isValidEmail(number);
}
public static boolean isValidSmsOrEmail(String number) {
return PhoneNumberUtils.isWellFormedSmsAddress(number) || isValidEmail(number);
}
}

View File

@@ -1,35 +0,0 @@
/**
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.recipients;
public class RecipientFormattingException extends Exception {
public RecipientFormattingException() {
super();
}
public RecipientFormattingException(String message) {
super(message);
}
public RecipientFormattingException(String message, Throwable nested) {
super(message, nested);
}
public RecipientFormattingException(Throwable nested) {
super(nested);
}
}

View File

@@ -1,76 +0,0 @@
/**
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.recipients;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
public class RecipientsFormatter {
private static String parseBracketedNumber(String recipient) throws RecipientFormattingException {
int begin = recipient.indexOf('<');
int end = recipient.indexOf('>');
String value = recipient.substring(begin + 1, end);
if (PhoneNumberUtils.isWellFormedSmsAddress(value))
return value;
else
throw new RecipientFormattingException("Bracketed value: " + value + " is not valid.");
}
private static String parseRecipient(String recipient) throws RecipientFormattingException {
recipient = recipient.trim();
if ((recipient.indexOf('<') != -1) && (recipient.indexOf('>') != -1))
return parseBracketedNumber(recipient);
if (PhoneNumberUtils.isWellFormedSmsAddress(recipient))
return recipient;
throw new RecipientFormattingException("Recipient: " + recipient + " is badly formatted.");
}
public static List<String> getRecipients(String rawText) throws RecipientFormattingException {
ArrayList<String> results = new ArrayList<String>();
StringTokenizer tokenizer = new StringTokenizer(rawText, ",");
while (tokenizer.hasMoreTokens()) {
results.add(parseRecipient(tokenizer.nextToken()));
}
return results;
}
public static String formatNameAndNumber(String name, String number) {
// Format like this: Mike Cleron <(650) 555-1234>
// Erick Tseng <(650) 555-1212>
// Tutankhamun <tutank1341@gmail.com>
// (408) 555-1289
String formattedNumber = PhoneNumberUtils.formatNumber(number);
if (!TextUtils.isEmpty(name) && !name.equals(number)) {
return name + " <" + formattedNumber + ">";
} else {
return formattedNumber;
}
}
}

View File

@@ -1,92 +0,0 @@
package org.thoughtcrime.securesms.sms;
import android.content.Context;
import android.os.Looper;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
public class TelephonyServiceState {
public boolean isConnected(Context context) {
ListenThread listenThread = new ListenThread(context);
listenThread.start();
return listenThread.get();
}
private static class ListenThread extends Thread {
private final Context context;
private boolean complete;
private boolean result;
public ListenThread(Context context) {
this.context = context.getApplicationContext();
}
@Override
public void run() {
Looper looper = initializeLooper();
ListenCallback callback = new ListenCallback(looper);
TelephonyManager telephonyManager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
telephonyManager.listen(callback, PhoneStateListener.LISTEN_SERVICE_STATE);
Looper.loop();
telephonyManager.listen(callback, PhoneStateListener.LISTEN_NONE);
set(callback.isConnected());
}
private Looper initializeLooper() {
Looper looper = Looper.myLooper();
if (looper == null) {
Looper.prepare();
}
return Looper.myLooper();
}
public synchronized boolean get() {
while (!complete) {
try {
wait();
} catch (InterruptedException e) {
throw new AssertionError(e);
}
}
return result;
}
private synchronized void set(boolean result) {
this.result = result;
this.complete = true;
notifyAll();
}
}
private static class ListenCallback extends PhoneStateListener {
private final Looper looper;
private volatile boolean connected;
public ListenCallback(Looper looper) {
this.looper = looper;
}
@Override
public void onServiceStateChanged(ServiceState serviceState) {
this.connected = (serviceState.getState() == ServiceState.STATE_IN_SERVICE);
looper.quit();
}
public boolean isConnected() {
return connected;
}
}
}

View File

@@ -1,40 +0,0 @@
/**
* Copyright (C) 2015 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.util;
import android.os.Parcelable;
public abstract class CharacterCalculator implements Parcelable {
public abstract CharacterState calculateCharacters(String messageBody);
public static class CharacterState {
public final int charactersRemaining;
public final int messagesSpent;
public final int maxTotalMessageSize;
public final int maxPrimaryMessageSize;
public CharacterState(int messagesSpent, int charactersRemaining, int maxTotalMessageSize, int maxPrimaryMessageSize) {
this.messagesSpent = messagesSpent;
this.charactersRemaining = charactersRemaining;
this.maxTotalMessageSize = maxTotalMessageSize;
this.maxPrimaryMessageSize = maxPrimaryMessageSize;
}
}
}

View File

@@ -1,32 +0,0 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.util
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.recipients.RecipientId
/**
* This exists as a temporary shim to improve the callsites where we'll be setting the expiration timer.
*
* Until the versions that don't understand expiration timers expire, we'll have to check capabilities before incrementing the version.
*
* After those old clients expire, we can remove this shim entirely and call the RecipientTable methods directly.
*/
object ExpirationTimerUtil {
@JvmStatic
fun setExpirationTimer(recipientId: RecipientId, expirationTimeSeconds: Int): Int {
val selfCapable = true
val recipientCapable = true
return if (selfCapable && recipientCapable) {
SignalDatabase.recipients.setExpireMessagesAndIncrementVersion(recipientId, expirationTimeSeconds)
} else {
SignalDatabase.recipients.setExpireMessagesWithoutIncrementingVersion(recipientId, expirationTimeSeconds)
1
}
}
}

View File

@@ -5,10 +5,7 @@ import android.content.DialogInterface
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.LayoutRes
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
@@ -17,24 +14,6 @@ import androidx.fragment.app.Fragment
*/
object FragmentDialogs {
fun Fragment.displayInDialogAboveAnchor(
anchorView: View,
@LayoutRes contentLayoutId: Int,
windowDim: Float = -1f,
onShow: (DialogInterface, View) -> Unit = { _, _ -> }
): DialogInterface {
val contentView = LayoutInflater.from(anchorView.context).inflate(contentLayoutId, requireView() as ViewGroup, false)
contentView.measure(
View.MeasureSpec.makeMeasureSpec(contentView.layoutParams.width, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(contentView.layoutParams.height, View.MeasureSpec.EXACTLY)
)
contentView.layout(0, 0, contentView.measuredWidth, contentView.measuredHeight)
return displayInDialogAboveAnchor(anchorView, contentView, windowDim, onShow)
}
@SuppressLint("AlertDialogBuilderUsage")
fun Fragment.displayInDialogAboveAnchor(
anchorView: View,

View File

@@ -1,8 +0,0 @@
package org.thoughtcrime.securesms.util;
/**
* A function which takes 3 inputs and returns 1 output.
*/
public interface Function3<A, B, C, D> {
D apply(A a, B b, C c);
}

View File

@@ -1,11 +0,0 @@
package org.thoughtcrime.securesms.util;
import android.text.Html;
import androidx.annotation.NonNull;
public class HtmlUtil {
public static @NonNull String bold(@NonNull String target) {
return "<b>" + Html.escapeHtml(target) + "</b>";
}
}

View File

@@ -1,33 +0,0 @@
package org.thoughtcrime.securesms.util;
import android.content.Context;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.push.IasTrustStore;
import org.whispersystems.signalservice.api.push.TrustStore;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
public final class IasKeyStore {
private IasKeyStore() {
}
public static KeyStore getIasKeyStore(@NonNull Context context) {
try {
TrustStore contactTrustStore = new IasTrustStore(context);
KeyStore keyStore = KeyStore.getInstance("BKS");
keyStore.load(contactTrustStore.getKeyStoreInputStream(), contactTrustStore.getKeyStorePassword().toCharArray());
return keyStore;
} catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
}
}

View File

@@ -1,55 +0,0 @@
package org.thoughtcrime.securesms.util;
import android.graphics.PointF;
import androidx.annotation.NonNull;
public class MathUtils {
/**
* For more info:
* <a href="http://math.stackexchange.com/questions/190111/how-to-check-if-a-point-is-inside-a-rectangle">StackOverflow: How to check point is in rectangle</a>
*
* @param pt point to check
* @param v1 vertex 1 of the triangle
* @param v2 vertex 2 of the triangle
* @param v3 vertex 3 of the triangle
* @return true if point (x, y) is inside the triangle
*/
public static boolean pointInTriangle(@NonNull PointF pt, @NonNull PointF v1,
@NonNull PointF v2, @NonNull PointF v3) {
boolean b1 = crossProduct(pt, v1, v2) < 0.0f;
boolean b2 = crossProduct(pt, v2, v3) < 0.0f;
boolean b3 = crossProduct(pt, v3, v1) < 0.0f;
return (b1 == b2) && (b2 == b3);
}
/**
* calculates cross product of vectors AB and AC
*
* @param a beginning of 2 vectors
* @param b end of vector 1
* @param c end of vector 2
* @return cross product AB * AC
*/
private static float crossProduct(@NonNull PointF a, @NonNull PointF b, @NonNull PointF c) {
return crossProduct(a.x, a.y, b.x, b.y, c.x, c.y);
}
/**
* calculates cross product of vectors AB and AC
*
* @param ax X coordinate of point A
* @param ay Y coordinate of point A
* @param bx X coordinate of point B
* @param by Y coordinate of point B
* @param cx X coordinate of point C
* @param cy Y coordinate of point C
* @return cross product AB * AC
*/
private static float crossProduct(float ax, float ay, float bx, float by, float cx, float cy) {
return (ax - cx) * (by - cy) - (bx - cx) * (ay - cy);
}
}

View File

@@ -1,34 +0,0 @@
package org.thoughtcrime.securesms.util;
import android.os.Parcel;
public class MmsCharacterCalculator extends CharacterCalculator {
private static final int MAX_SIZE = 5000;
@Override
public CharacterState calculateCharacters(String messageBody) {
return new CharacterState(1, MAX_SIZE - messageBody.length(), MAX_SIZE, MAX_SIZE);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
}
public static final Creator<MmsCharacterCalculator> CREATOR = new Creator<MmsCharacterCalculator>() {
@Override
public MmsCharacterCalculator createFromParcel(Parcel in) {
return new MmsCharacterCalculator();
}
@Override
public MmsCharacterCalculator[] newArray(int size) {
return new MmsCharacterCalculator[size];
}
};
}

View File

@@ -1,50 +0,0 @@
/**
* Copyright (C) 2015 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.util;
import android.os.Parcel;
public class PushCharacterCalculator extends CharacterCalculator {
private static final int MAX_TOTAL_SIZE = 64 * 1024;
private static final int MAX_PRIMARY_SIZE = 2000;
@Override
public CharacterState calculateCharacters(String messageBody) {
return new CharacterState(1, MAX_TOTAL_SIZE - messageBody.length(), MAX_TOTAL_SIZE, MAX_PRIMARY_SIZE);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
}
public static final Creator<PushCharacterCalculator> CREATOR = new Creator<PushCharacterCalculator>() {
@Override
public PushCharacterCalculator createFromParcel(Parcel in) {
return new PushCharacterCalculator();
}
@Override
public PushCharacterCalculator[] newArray(int size) {
return new PushCharacterCalculator[size];
}
};
}

View File

@@ -1086,14 +1086,6 @@ object RemoteConfig {
value.asLong(8.kibiBytes.inWholeBytes).bytes
}
@JvmStatic
@get:JvmName("libsignalEnforceMinTlsVersion")
val libsignalEnforceMinTlsVersion by remoteBoolean(
key = "android.libsignalEnforceMinTlsVersion",
defaultValue = false,
hotSwappable = false
)
@JvmStatic
val backgroundMessageProcessInterval: Long by remoteValue(
key = "android.messageProcessor.alarmIntervalMins",
@@ -1131,14 +1123,6 @@ object RemoteConfig {
hotSwappable = true
)
/** Whether or not to show chat folders. */
@JvmStatic
val showChatFolders: Boolean by remoteBoolean(
key = "android.showChatFolders.2",
defaultValue = false,
hotSwappable = true
)
/** Whether or not to use the new pinned chat UI. */
@JvmStatic
val inlinePinnedChats: Boolean by remoteBoolean(

View File

@@ -1,8 +0,0 @@
package org.thoughtcrime.securesms.util;
public final class RequestCodes {
public static final int NOT_SET = -1;
private RequestCodes() { }
}

View File

@@ -1,70 +0,0 @@
/**
* Copyright (C) 2015 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.util;
import android.content.Context;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.TypedValue;
import androidx.annotation.ArrayRes;
import androidx.annotation.AttrRes;
import androidx.annotation.DimenRes;
import androidx.annotation.NonNull;
import androidx.appcompat.content.res.AppCompatResources;
public class ResUtil {
public static int getColor(Context context, @AttrRes int attr) {
final TypedArray styledAttributes = context.obtainStyledAttributes(new int[]{attr});
final int result = styledAttributes.getColor(0, -1);
styledAttributes.recycle();
return result;
}
public static int getDrawableRes(Context c, @AttrRes int attr) {
return getDrawableRes(c.getTheme(), attr);
}
public static int getDrawableRes(Theme theme, @AttrRes int attr) {
final TypedValue out = new TypedValue();
theme.resolveAttribute(attr, out, true);
return out.resourceId;
}
public static Drawable getDrawable(Context c, @AttrRes int attr) {
return AppCompatResources.getDrawable(c, getDrawableRes(c, attr));
}
public static int[] getResourceIds(Context c, @ArrayRes int array) {
final TypedArray typedArray = c.getResources().obtainTypedArray(array);
final int[] resourceIds = new int[typedArray.length()];
for (int i = 0; i < typedArray.length(); i++) {
resourceIds[i] = typedArray.getResourceId(i, 0);
}
typedArray.recycle();
return resourceIds;
}
public static float getFloat(@NonNull Context context, @DimenRes int resId) {
TypedValue value = new TypedValue();
context.getResources().getValue(resId, value, true);
return value.getFloat();
}
}

View File

@@ -1,31 +0,0 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.util
import android.content.Context
import android.content.SharedPreferences
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
/**
* A lifecycle-aware observer that will let the changes to the [TextSecurePreferences] be observed.
*
* @param keysToListeners a map of [TextSecurePreferences] string keys to listeners that should be invoked when the values change.
*/
class SharedPreferencesLifecycleObserver(private val context: Context, keysToListeners: Map<String, () -> Unit>) : DefaultLifecycleObserver {
private val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
keysToListeners[key]?.invoke()
}
override fun onResume(owner: LifecycleOwner) {
TextSecurePreferences.registerListener(context, listener)
}
override fun onPause(owner: LifecycleOwner) {
TextSecurePreferences.unregisterListener(context, listener)
}
}

View File

@@ -1,43 +0,0 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.util;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
/**
* FutureTask with a reference identifier tag.
*
* @author Jake McGinty
*/
public class TaggedFutureTask<V> extends FutureTask<V> {
private final Object tag;
public TaggedFutureTask(Runnable runnable, V result, Object tag) {
super(runnable, result);
this.tag = tag;
}
public TaggedFutureTask(Callable<V> callable, Object tag) {
super(callable);
this.tag = tag;
}
public Object getTag() {
return tag;
}
}

View File

@@ -1,43 +0,0 @@
package org.thoughtcrime.securesms.util;
import androidx.annotation.Nullable;
import androidx.core.util.ObjectsCompat;
public class Triple<A, B, C> {
private final A a;
private final B b;
private final C c;
public Triple(@Nullable A a, @Nullable B b, @Nullable C c) {
this.a = a;
this.b = b;
this.c = c;
}
public @Nullable A first() {
return a;
}
public @Nullable B second() {
return b;
}
public @Nullable C third() {
return c;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Triple)) {
return false;
}
Triple<?, ?, ?> t = (Triple<?, ?, ?>) o;
return ObjectsCompat.equals(t.a, a) && ObjectsCompat.equals(t.b, b) && ObjectsCompat.equals(t.c, c);
}
@Override
public int hashCode() {
return (a == null ? 0 : a.hashCode()) ^ (b == null ? 0 : b.hashCode()) ^ (c == null ? 0 : c.hashCode());
}
}

View File

@@ -1,27 +0,0 @@
package org.thoughtcrime.securesms.util
import android.view.View
import android.view.animation.AnimationUtils
import androidx.annotation.AnimRes
/**
* Runs the given animation on this view, assuming that the view is in an INVISIBLE or HIDDEN state.
*/
fun View.runRevealAnimation(@AnimRes anim: Int) {
animation = AnimationUtils.loadAnimation(context, anim)
visible = true
}
/**
* Runs the given animation on this view, assuming that the view is in a VISIBLE state and will
* hide on completion
*/
fun View.runHideAnimation(@AnimRes anim: Int) {
startAnimation(
AnimationUtils.loadAnimation(context, anim).apply {
setListeners(onAnimationEnd = {
visible = false
})
}
)
}

View File

@@ -1,48 +0,0 @@
/**
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.util;
import java.util.List;
public class WorkerThread extends Thread {
private final List<Runnable> workQueue;
public WorkerThread(List<Runnable> workQueue, String name) {
super(name);
this.workQueue = workQueue;
}
private Runnable getWork() {
synchronized (workQueue) {
try {
while (workQueue.isEmpty())
workQueue.wait();
return workQueue.remove(0);
} catch (InterruptedException ie) {
throw new AssertionError(ie);
}
}
}
@Override
public void run() {
for (;;)
getWork().run();
}
}

View File

@@ -1,135 +0,0 @@
package org.thoughtcrime.securesms.util.livedata;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MediatorLiveData;
import org.thoughtcrime.securesms.util.Triple;
public final class LiveDataTriple<A, B, C> extends MediatorLiveData<Triple<A, B, C>> {
private A a;
private B b;
private C c;
public LiveDataTriple(@NonNull LiveData<A> liveDataA,
@NonNull LiveData<B> liveDataB,
@NonNull LiveData<C> liveDataC)
{
this(liveDataA, liveDataB, liveDataC, null, null, null);
}
public LiveDataTriple(@NonNull LiveData<A> liveDataA,
@NonNull LiveData<B> liveDataB,
@NonNull LiveData<C> liveDataC,
@Nullable A initialA,
@Nullable B initialB,
@Nullable C initialC)
{
a = initialA;
b = initialB;
c = initialC;
setValue(new Triple<>(a, b, c));
if (liveDataA == liveDataB && liveDataA == liveDataC) {
addSource(liveDataA, a -> {
if (a != null) {
this.a = a;
//noinspection unchecked: A is B if live datas are same instance
this.b = (B) a;
//noinspection unchecked: A is C if live datas are same instance
this.c = (C) a;
}
setValue(new Triple<>(a, b, c));
});
} else if (liveDataA == liveDataB) {
addSource(liveDataA, a -> {
if (a != null) {
this.a = a;
//noinspection unchecked: A is B if live datas are same instance
this.b = (B) a;
}
setValue(new Triple<>(a, b, c));
});
addSource(liveDataC, c -> {
if (c != null) {
this.c = c;
}
setValue(new Triple<>(a, b, c));
});
} else if (liveDataA == liveDataC) {
addSource(liveDataA, a -> {
if (a != null) {
this.a = a;
//noinspection unchecked: A is C if live datas are same instance
this.c = (C) a;
}
setValue(new Triple<>(a, b, c));
});
addSource(liveDataB, b -> {
if (b != null) {
this.b = b;
}
setValue(new Triple<>(a, b, c));
});
} else if (liveDataB == liveDataC) {
addSource(liveDataB, b -> {
if (b != null) {
this.b = b;
//noinspection unchecked: A is C if live datas are same instance
this.c = (C) b;
}
setValue(new Triple<>(a, b, c));
});
addSource(liveDataA, a -> {
if (a != null) {
this.a = a;
}
setValue(new Triple<>(a, b, c));
});
} else {
addSource(liveDataA, a -> {
if (a != null) {
this.a = a;
}
setValue(new Triple<>(a, b, c));
});
addSource(liveDataB, b -> {
if (b != null) {
this.b = b;
}
setValue(new Triple<>(a, b, c));
});
addSource(liveDataC, c -> {
if (c != null) {
this.c = c;
}
setValue(new Triple<>(a, b, c));
});
}
}
}