Add support for persisting wallpaper selection.
This commit is contained in:
parent
80651d2425
commit
6bcb0de43d
13 changed files with 447 additions and 37 deletions
|
@ -31,6 +31,7 @@ import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||||
import org.thoughtcrime.securesms.database.model.ThreadRecord;
|
import org.thoughtcrime.securesms.database.model.ThreadRecord;
|
||||||
import org.thoughtcrime.securesms.database.model.databaseprotos.DeviceLastResetTime;
|
import org.thoughtcrime.securesms.database.model.databaseprotos.DeviceLastResetTime;
|
||||||
import org.thoughtcrime.securesms.database.model.databaseprotos.ProfileKeyCredentialColumnData;
|
import org.thoughtcrime.securesms.database.model.databaseprotos.ProfileKeyCredentialColumnData;
|
||||||
|
import org.thoughtcrime.securesms.database.model.databaseprotos.Wallpaper;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.groups.GroupId;
|
import org.thoughtcrime.securesms.groups.GroupId;
|
||||||
import org.thoughtcrime.securesms.groups.v2.ProfileKeySet;
|
import org.thoughtcrime.securesms.groups.v2.ProfileKeySet;
|
||||||
|
@ -38,6 +39,7 @@ import org.thoughtcrime.securesms.groups.v2.processing.GroupsV2StateProcessor;
|
||||||
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
|
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RequestGroupV2InfoJob;
|
import org.thoughtcrime.securesms.jobs.RequestGroupV2InfoJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
||||||
|
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||||
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
||||||
import org.thoughtcrime.securesms.profiles.ProfileName;
|
import org.thoughtcrime.securesms.profiles.ProfileName;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
@ -53,6 +55,9 @@ import org.thoughtcrime.securesms.util.IdentityUtil;
|
||||||
import org.thoughtcrime.securesms.util.SqlUtil;
|
import org.thoughtcrime.securesms.util.SqlUtil;
|
||||||
import org.thoughtcrime.securesms.util.StringUtil;
|
import org.thoughtcrime.securesms.util.StringUtil;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
|
import org.thoughtcrime.securesms.wallpaper.ChatWallpaper;
|
||||||
|
import org.thoughtcrime.securesms.wallpaper.ChatWallpaperFactory;
|
||||||
|
import org.thoughtcrime.securesms.wallpaper.WallpaperStorage;
|
||||||
import org.whispersystems.libsignal.IdentityKey;
|
import org.whispersystems.libsignal.IdentityKey;
|
||||||
import org.whispersystems.libsignal.InvalidKeyException;
|
import org.whispersystems.libsignal.InvalidKeyException;
|
||||||
import org.whispersystems.libsignal.util.Pair;
|
import org.whispersystems.libsignal.util.Pair;
|
||||||
|
@ -131,6 +136,8 @@ public class RecipientDatabase extends Database {
|
||||||
private static final String STORAGE_PROTO = "storage_proto";
|
private static final String STORAGE_PROTO = "storage_proto";
|
||||||
private static final String LAST_GV1_MIGRATE_REMINDER = "last_gv1_migrate_reminder";
|
private static final String LAST_GV1_MIGRATE_REMINDER = "last_gv1_migrate_reminder";
|
||||||
private static final String LAST_SESSION_RESET = "last_session_reset";
|
private static final String LAST_SESSION_RESET = "last_session_reset";
|
||||||
|
private static final String WALLPAPER = "wallpaper";
|
||||||
|
private static final String WALLPAPER_URI = "wallpaper_file";
|
||||||
|
|
||||||
public static final String SEARCH_PROFILE_NAME = "search_signal_profile";
|
public static final String SEARCH_PROFILE_NAME = "search_signal_profile";
|
||||||
private static final String SORT_NAME = "sort_name";
|
private static final String SORT_NAME = "sort_name";
|
||||||
|
@ -155,7 +162,7 @@ public class RecipientDatabase extends Database {
|
||||||
FORCE_SMS_SELECTION,
|
FORCE_SMS_SELECTION,
|
||||||
CAPABILITIES,
|
CAPABILITIES,
|
||||||
STORAGE_SERVICE_ID, DIRTY,
|
STORAGE_SERVICE_ID, DIRTY,
|
||||||
MENTION_SETTING
|
MENTION_SETTING, WALLPAPER, WALLPAPER_URI
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final String[] ID_PROJECTION = new String[]{ID};
|
private static final String[] ID_PROJECTION = new String[]{ID};
|
||||||
|
@ -350,7 +357,9 @@ public class RecipientDatabase extends Database {
|
||||||
STORAGE_PROTO + " TEXT DEFAULT NULL, " +
|
STORAGE_PROTO + " TEXT DEFAULT NULL, " +
|
||||||
CAPABILITIES + " INTEGER DEFAULT 0, " +
|
CAPABILITIES + " INTEGER DEFAULT 0, " +
|
||||||
LAST_GV1_MIGRATE_REMINDER + " INTEGER DEFAULT 0, " +
|
LAST_GV1_MIGRATE_REMINDER + " INTEGER DEFAULT 0, " +
|
||||||
LAST_SESSION_RESET + " BLOB DEFAULT NULL);";
|
LAST_SESSION_RESET + " BLOB DEFAULT NULL, " +
|
||||||
|
WALLPAPER + " BLOB DEFAULT NULL, " +
|
||||||
|
WALLPAPER_URI + " TEXT DEFAULT NULL);";
|
||||||
|
|
||||||
private static final String INSIGHTS_INVITEE_LIST = "SELECT " + TABLE_NAME + "." + ID +
|
private static final String INSIGHTS_INVITEE_LIST = "SELECT " + TABLE_NAME + "." + ID +
|
||||||
" FROM " + TABLE_NAME +
|
" FROM " + TABLE_NAME +
|
||||||
|
@ -1264,6 +1273,7 @@ public class RecipientDatabase extends Database {
|
||||||
long capabilities = CursorUtil.requireLong(cursor, CAPABILITIES);
|
long capabilities = CursorUtil.requireLong(cursor, CAPABILITIES);
|
||||||
String storageKeyRaw = CursorUtil.requireString(cursor, STORAGE_SERVICE_ID);
|
String storageKeyRaw = CursorUtil.requireString(cursor, STORAGE_SERVICE_ID);
|
||||||
int mentionSettingId = CursorUtil.requireInt(cursor, MENTION_SETTING);
|
int mentionSettingId = CursorUtil.requireInt(cursor, MENTION_SETTING);
|
||||||
|
byte[] wallpaper = CursorUtil.requireBlob(cursor, WALLPAPER);
|
||||||
|
|
||||||
MaterialColor color;
|
MaterialColor color;
|
||||||
byte[] profileKey = null;
|
byte[] profileKey = null;
|
||||||
|
@ -1303,6 +1313,16 @@ public class RecipientDatabase extends Database {
|
||||||
|
|
||||||
byte[] storageKey = storageKeyRaw != null ? Base64.decodeOrThrow(storageKeyRaw) : null;
|
byte[] storageKey = storageKeyRaw != null ? Base64.decodeOrThrow(storageKeyRaw) : null;
|
||||||
|
|
||||||
|
ChatWallpaper chatWallpaper = null;
|
||||||
|
|
||||||
|
if (wallpaper != null) {
|
||||||
|
try {
|
||||||
|
chatWallpaper = ChatWallpaperFactory.create(Wallpaper.parseFrom(wallpaper));
|
||||||
|
} catch (InvalidProtocolBufferException e) {
|
||||||
|
Log.w(TAG, "Failed to parse wallpaper.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return new RecipientSettings(RecipientId.from(id),
|
return new RecipientSettings(RecipientId.from(id),
|
||||||
uuid,
|
uuid,
|
||||||
username,
|
username,
|
||||||
|
@ -1338,6 +1358,7 @@ public class RecipientDatabase extends Database {
|
||||||
InsightsBannerTier.fromId(insightsBannerTier),
|
InsightsBannerTier.fromId(insightsBannerTier),
|
||||||
storageKey,
|
storageKey,
|
||||||
MentionSetting.fromId(mentionSettingId),
|
MentionSetting.fromId(mentionSettingId),
|
||||||
|
chatWallpaper,
|
||||||
getSyncExtras(cursor));
|
getSyncExtras(cursor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1778,6 +1799,60 @@ public class RecipientDatabase extends Database {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setWallpaper(@NonNull RecipientId id, @NonNull ChatWallpaper chatWallpaper) {
|
||||||
|
Wallpaper wallpaper = chatWallpaper.serialize();
|
||||||
|
Uri existingWallpaperUri = getWallpaperUri(id);
|
||||||
|
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(WALLPAPER, wallpaper.toByteArray());
|
||||||
|
|
||||||
|
if (wallpaper.hasFile()) {
|
||||||
|
values.put(WALLPAPER_URI, wallpaper.getFile().getUri());
|
||||||
|
} else {
|
||||||
|
values.putNull(WALLPAPER_URI);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (update(id, values)) {
|
||||||
|
Recipient.live(id).refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existingWallpaperUri != null) {
|
||||||
|
WallpaperStorage.onWallpaperDeselected(context, existingWallpaperUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable Uri getWallpaperUri(@NonNull RecipientId id) {
|
||||||
|
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||||
|
|
||||||
|
try (Cursor cursor = db.query(TABLE_NAME, new String[] {WALLPAPER_URI}, ID_WHERE, SqlUtil.buildArgs(id), null, null, null)) {
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
String raw = CursorUtil.requireString(cursor, WALLPAPER_URI);
|
||||||
|
|
||||||
|
if (raw != null) {
|
||||||
|
return Uri.parse(raw);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWallpaperUriUsageCount(@NonNull Uri uri) {
|
||||||
|
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||||
|
String query = WALLPAPER_URI + " = ?";
|
||||||
|
String[] args = SqlUtil.buildArgs(uri);
|
||||||
|
|
||||||
|
try (Cursor cursor = db.query(TABLE_NAME, new String[] { "COUNT(*)"}, query, args, null, null, null)) {
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
return cursor.getInt(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return True if setting the phone number resulted in changed recipientId, otherwise false.
|
* @return True if setting the phone number resulted in changed recipientId, otherwise false.
|
||||||
*/
|
*/
|
||||||
|
@ -2788,6 +2863,7 @@ public class RecipientDatabase extends Database {
|
||||||
private final InsightsBannerTier insightsBannerTier;
|
private final InsightsBannerTier insightsBannerTier;
|
||||||
private final byte[] storageId;
|
private final byte[] storageId;
|
||||||
private final MentionSetting mentionSetting;
|
private final MentionSetting mentionSetting;
|
||||||
|
private final ChatWallpaper wallpaper;
|
||||||
private final SyncExtras syncExtras;
|
private final SyncExtras syncExtras;
|
||||||
|
|
||||||
RecipientSettings(@NonNull RecipientId id,
|
RecipientSettings(@NonNull RecipientId id,
|
||||||
|
@ -2825,6 +2901,7 @@ public class RecipientDatabase extends Database {
|
||||||
@NonNull InsightsBannerTier insightsBannerTier,
|
@NonNull InsightsBannerTier insightsBannerTier,
|
||||||
@Nullable byte[] storageId,
|
@Nullable byte[] storageId,
|
||||||
@NonNull MentionSetting mentionSetting,
|
@NonNull MentionSetting mentionSetting,
|
||||||
|
@Nullable ChatWallpaper wallpaper,
|
||||||
@NonNull SyncExtras syncExtras)
|
@NonNull SyncExtras syncExtras)
|
||||||
{
|
{
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
@ -2864,6 +2941,7 @@ public class RecipientDatabase extends Database {
|
||||||
this.insightsBannerTier = insightsBannerTier;
|
this.insightsBannerTier = insightsBannerTier;
|
||||||
this.storageId = storageId;
|
this.storageId = storageId;
|
||||||
this.mentionSetting = mentionSetting;
|
this.mentionSetting = mentionSetting;
|
||||||
|
this.wallpaper = wallpaper;
|
||||||
this.syncExtras = syncExtras;
|
this.syncExtras = syncExtras;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3011,6 +3089,10 @@ public class RecipientDatabase extends Database {
|
||||||
return mentionSetting;
|
return mentionSetting;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public @Nullable ChatWallpaper getWallpaper() {
|
||||||
|
return wallpaper;
|
||||||
|
}
|
||||||
|
|
||||||
public @NonNull SyncExtras getSyncExtras() {
|
public @NonNull SyncExtras getSyncExtras() {
|
||||||
return syncExtras;
|
return syncExtras;
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,8 +169,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
|
||||||
private static final int GV1_MIGRATION_REFACTOR = 85;
|
private static final int GV1_MIGRATION_REFACTOR = 85;
|
||||||
private static final int CLEAR_PROFILE_KEY_CREDENTIALS = 86;
|
private static final int CLEAR_PROFILE_KEY_CREDENTIALS = 86;
|
||||||
private static final int LAST_RESET_SESSION_TIME = 87;
|
private static final int LAST_RESET_SESSION_TIME = 87;
|
||||||
|
private static final int WALLPAPER = 88;
|
||||||
|
|
||||||
private static final int DATABASE_VERSION = 87;
|
private static final int DATABASE_VERSION = 88;
|
||||||
private static final String DATABASE_NAME = "signal.db";
|
private static final String DATABASE_NAME = "signal.db";
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
@ -1246,6 +1247,11 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
|
||||||
db.execSQL("ALTER TABLE recipient ADD COLUMN last_session_reset BLOB DEFAULT NULL");
|
db.execSQL("ALTER TABLE recipient ADD COLUMN last_session_reset BLOB DEFAULT NULL");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldVersion < WALLPAPER) {
|
||||||
|
db.execSQL("ALTER TABLE recipient ADD COLUMN wallpaper BLOB DEFAULT NULL");
|
||||||
|
db.execSQL("ALTER TABLE recipient ADD COLUMN wallpaper_file TEXT DEFAULT NULL");
|
||||||
|
}
|
||||||
|
|
||||||
db.setTransactionSuccessful();
|
db.setTransactionSuccessful();
|
||||||
} finally {
|
} finally {
|
||||||
db.endTransaction();
|
db.endTransaction();
|
||||||
|
|
|
@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.keyvalue;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.preference.PreferenceDataStore;
|
import androidx.preference.PreferenceDataStore;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.database.model.databaseprotos.Wallpaper;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.util.SignalUncaughtExceptionHandler;
|
import org.thoughtcrime.securesms.util.SignalUncaughtExceptionHandler;
|
||||||
|
|
||||||
|
@ -28,6 +29,7 @@ public final class SignalStore {
|
||||||
private final CertificateValues certificateValues;
|
private final CertificateValues certificateValues;
|
||||||
private final PhoneNumberPrivacyValues phoneNumberPrivacyValues;
|
private final PhoneNumberPrivacyValues phoneNumberPrivacyValues;
|
||||||
private final OnboardingValues onboardingValues;
|
private final OnboardingValues onboardingValues;
|
||||||
|
private final WallpaperValues wallpaperValues;
|
||||||
|
|
||||||
private SignalStore() {
|
private SignalStore() {
|
||||||
this.store = new KeyValueStore(ApplicationDependencies.getApplication());
|
this.store = new KeyValueStore(ApplicationDependencies.getApplication());
|
||||||
|
@ -45,6 +47,7 @@ public final class SignalStore {
|
||||||
this.certificateValues = new CertificateValues(store);
|
this.certificateValues = new CertificateValues(store);
|
||||||
this.phoneNumberPrivacyValues = new PhoneNumberPrivacyValues(store);
|
this.phoneNumberPrivacyValues = new PhoneNumberPrivacyValues(store);
|
||||||
this.onboardingValues = new OnboardingValues(store);
|
this.onboardingValues = new OnboardingValues(store);
|
||||||
|
this.wallpaperValues = new WallpaperValues(store);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void onFirstEverAppLaunch() {
|
public static void onFirstEverAppLaunch() {
|
||||||
|
@ -61,6 +64,7 @@ public final class SignalStore {
|
||||||
certificateValues().onFirstEverAppLaunch();
|
certificateValues().onFirstEverAppLaunch();
|
||||||
phoneNumberPrivacy().onFirstEverAppLaunch();
|
phoneNumberPrivacy().onFirstEverAppLaunch();
|
||||||
onboarding().onFirstEverAppLaunch();
|
onboarding().onFirstEverAppLaunch();
|
||||||
|
wallpaper().onFirstEverAppLaunch();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NonNull KbsValues kbsValues() {
|
public static @NonNull KbsValues kbsValues() {
|
||||||
|
@ -119,6 +123,10 @@ public final class SignalStore {
|
||||||
return INSTANCE.onboardingValues;
|
return INSTANCE.onboardingValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static @NonNull WallpaperValues wallpaper() {
|
||||||
|
return INSTANCE.wallpaperValues;
|
||||||
|
}
|
||||||
|
|
||||||
public static @NonNull GroupsV2AuthorizationSignalStoreCache groupsV2AuthorizationCache() {
|
public static @NonNull GroupsV2AuthorizationSignalStoreCache groupsV2AuthorizationCache() {
|
||||||
return new GroupsV2AuthorizationSignalStoreCache(getStore());
|
return new GroupsV2AuthorizationSignalStoreCache(getStore());
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
package org.thoughtcrime.securesms.keyvalue;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
|
|
||||||
|
import org.signal.core.util.logging.Log;
|
||||||
|
import org.thoughtcrime.securesms.database.model.databaseprotos.Wallpaper;
|
||||||
|
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||||
|
import org.thoughtcrime.securesms.wallpaper.ChatWallpaper;
|
||||||
|
import org.thoughtcrime.securesms.wallpaper.ChatWallpaperFactory;
|
||||||
|
import org.thoughtcrime.securesms.wallpaper.WallpaperStorage;
|
||||||
|
|
||||||
|
public final class WallpaperValues extends SignalStoreValues {
|
||||||
|
|
||||||
|
private static final String TAG = Log.tag(WallpaperValues.class);
|
||||||
|
|
||||||
|
private static final String KEY_WALLPAPER = "wallpaper.wallpaper";
|
||||||
|
|
||||||
|
WallpaperValues(@NonNull KeyValueStore store) {
|
||||||
|
super(store);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void onFirstEverAppLaunch() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWallpaper(@NonNull Context context, @Nullable ChatWallpaper wallpaper) {
|
||||||
|
Wallpaper currentWallpaper = getCurrentWallpaper();
|
||||||
|
Uri currentUri = null;
|
||||||
|
|
||||||
|
if (currentWallpaper != null && currentWallpaper.hasFile()) {
|
||||||
|
currentUri = Uri.parse(currentWallpaper.getFile().getUri());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wallpaper != null) {
|
||||||
|
putBlob(KEY_WALLPAPER, wallpaper.serialize().toByteArray());
|
||||||
|
} else {
|
||||||
|
getStore().beginWrite().remove(KEY_WALLPAPER).apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
WallpaperStorage.onWallpaperDeselected(context, currentUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable ChatWallpaper getWallpaper() {
|
||||||
|
Wallpaper currentWallpaper = getCurrentWallpaper();
|
||||||
|
|
||||||
|
if (currentWallpaper != null) {
|
||||||
|
return ChatWallpaperFactory.create(currentWallpaper);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable Uri getCurrentWallpaperUri() {
|
||||||
|
Wallpaper currentWallpaper = getCurrentWallpaper();
|
||||||
|
|
||||||
|
if (currentWallpaper != null && currentWallpaper.hasFile()) {
|
||||||
|
return Uri.parse(currentWallpaper.getFile().getUri());
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable Wallpaper getCurrentWallpaper() {
|
||||||
|
byte[] serialized = getBlob(KEY_WALLPAPER, null);
|
||||||
|
|
||||||
|
if (serialized != null) {
|
||||||
|
try {
|
||||||
|
return Wallpaper.parseFrom(serialized);
|
||||||
|
} catch (InvalidProtocolBufferException e) {
|
||||||
|
Log.w(TAG, "Invalid proto stored for wallpaper!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,22 +15,26 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.providers.BlobProvider;
|
import org.thoughtcrime.securesms.providers.BlobProvider;
|
||||||
import org.thoughtcrime.securesms.providers.DeprecatedPersistentBlobProvider;
|
import org.thoughtcrime.securesms.providers.DeprecatedPersistentBlobProvider;
|
||||||
import org.thoughtcrime.securesms.providers.PartProvider;
|
import org.thoughtcrime.securesms.providers.PartProvider;
|
||||||
|
import org.thoughtcrime.securesms.wallpaper.WallpaperStorage;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
public class PartAuthority {
|
public class PartAuthority {
|
||||||
|
|
||||||
private static final String AUTHORITY = BuildConfig.APPLICATION_ID;
|
private static final String AUTHORITY = BuildConfig.APPLICATION_ID;
|
||||||
private static final String PART_URI_STRING = "content://" + AUTHORITY + "/part";
|
private static final String PART_URI_STRING = "content://" + AUTHORITY + "/part";
|
||||||
private static final String STICKER_URI_STRING = "content://" + AUTHORITY + "/sticker";
|
private static final String STICKER_URI_STRING = "content://" + AUTHORITY + "/sticker";
|
||||||
private static final Uri PART_CONTENT_URI = Uri.parse(PART_URI_STRING);
|
private static final String WALLPAPER_URI_STRING = "content://" + AUTHORITY + "/wallpaper";
|
||||||
private static final Uri STICKER_CONTENT_URI = Uri.parse(STICKER_URI_STRING);
|
private static final Uri PART_CONTENT_URI = Uri.parse(PART_URI_STRING);
|
||||||
|
private static final Uri STICKER_CONTENT_URI = Uri.parse(STICKER_URI_STRING);
|
||||||
|
private static final Uri WALLPAPER_CONTENT_URI = Uri.parse(WALLPAPER_URI_STRING);
|
||||||
|
|
||||||
private static final int PART_ROW = 1;
|
private static final int PART_ROW = 1;
|
||||||
private static final int PERSISTENT_ROW = 2;
|
private static final int PERSISTENT_ROW = 2;
|
||||||
private static final int BLOB_ROW = 3;
|
private static final int BLOB_ROW = 3;
|
||||||
private static final int STICKER_ROW = 4;
|
private static final int STICKER_ROW = 4;
|
||||||
|
private static final int WALLPAPER_ROW = 5;
|
||||||
|
|
||||||
private static final UriMatcher uriMatcher;
|
private static final UriMatcher uriMatcher;
|
||||||
|
|
||||||
|
@ -38,6 +42,7 @@ public class PartAuthority {
|
||||||
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||||
uriMatcher.addURI(AUTHORITY, "part/*/#", PART_ROW);
|
uriMatcher.addURI(AUTHORITY, "part/*/#", PART_ROW);
|
||||||
uriMatcher.addURI(AUTHORITY, "sticker/#", STICKER_ROW);
|
uriMatcher.addURI(AUTHORITY, "sticker/#", STICKER_ROW);
|
||||||
|
uriMatcher.addURI(AUTHORITY, "wallpaper/*", WALLPAPER_ROW);
|
||||||
uriMatcher.addURI(DeprecatedPersistentBlobProvider.AUTHORITY, DeprecatedPersistentBlobProvider.EXPECTED_PATH_OLD, PERSISTENT_ROW);
|
uriMatcher.addURI(DeprecatedPersistentBlobProvider.AUTHORITY, DeprecatedPersistentBlobProvider.EXPECTED_PATH_OLD, PERSISTENT_ROW);
|
||||||
uriMatcher.addURI(DeprecatedPersistentBlobProvider.AUTHORITY, DeprecatedPersistentBlobProvider.EXPECTED_PATH_NEW, PERSISTENT_ROW);
|
uriMatcher.addURI(DeprecatedPersistentBlobProvider.AUTHORITY, DeprecatedPersistentBlobProvider.EXPECTED_PATH_NEW, PERSISTENT_ROW);
|
||||||
uriMatcher.addURI(BlobProvider.AUTHORITY, BlobProvider.PATH, BLOB_ROW);
|
uriMatcher.addURI(BlobProvider.AUTHORITY, BlobProvider.PATH, BLOB_ROW);
|
||||||
|
@ -59,6 +64,7 @@ public class PartAuthority {
|
||||||
case STICKER_ROW: return DatabaseFactory.getStickerDatabase(context).getStickerStream(ContentUris.parseId(uri));
|
case STICKER_ROW: return DatabaseFactory.getStickerDatabase(context).getStickerStream(ContentUris.parseId(uri));
|
||||||
case PERSISTENT_ROW: return DeprecatedPersistentBlobProvider.getInstance(context).getStream(context, ContentUris.parseId(uri));
|
case PERSISTENT_ROW: return DeprecatedPersistentBlobProvider.getInstance(context).getStream(context, ContentUris.parseId(uri));
|
||||||
case BLOB_ROW: return BlobProvider.getInstance().getStream(context, uri);
|
case BLOB_ROW: return BlobProvider.getInstance().getStream(context, uri);
|
||||||
|
case WALLPAPER_ROW: return WallpaperStorage.read(context, getWallpaperFilename(uri));
|
||||||
default: return context.getContentResolver().openInputStream(uri);
|
default: return context.getContentResolver().openInputStream(uri);
|
||||||
}
|
}
|
||||||
} catch (SecurityException se) {
|
} catch (SecurityException se) {
|
||||||
|
@ -138,6 +144,14 @@ public class PartAuthority {
|
||||||
return ContentUris.withAppendedId(STICKER_CONTENT_URI, id);
|
return ContentUris.withAppendedId(STICKER_CONTENT_URI, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Uri getWallpaperUri(String filename) {
|
||||||
|
return Uri.withAppendedPath(WALLPAPER_CONTENT_URI, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getWallpaperFilename(Uri uri) {
|
||||||
|
return uri.getPathSegments().get(1);
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isLocalUri(final @NonNull Uri uri) {
|
public static boolean isLocalUri(final @NonNull Uri uri) {
|
||||||
int match = uriMatcher.match(uri);
|
int match = uriMatcher.match(uri);
|
||||||
switch (match) {
|
switch (match) {
|
||||||
|
|
|
@ -43,6 +43,7 @@ import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||||
import org.thoughtcrime.securesms.util.StringUtil;
|
import org.thoughtcrime.securesms.util.StringUtil;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
|
import org.thoughtcrime.securesms.wallpaper.ChatWallpaper;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.libsignal.util.guava.Preconditions;
|
import org.whispersystems.libsignal.util.guava.Preconditions;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
|
@ -106,6 +107,7 @@ public class Recipient {
|
||||||
private final InsightsBannerTier insightsBannerTier;
|
private final InsightsBannerTier insightsBannerTier;
|
||||||
private final byte[] storageId;
|
private final byte[] storageId;
|
||||||
private final MentionSetting mentionSetting;
|
private final MentionSetting mentionSetting;
|
||||||
|
private final ChatWallpaper wallpaper;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -339,6 +341,7 @@ public class Recipient {
|
||||||
this.groupsV1MigrationCapability = Capability.UNKNOWN;
|
this.groupsV1MigrationCapability = Capability.UNKNOWN;
|
||||||
this.storageId = null;
|
this.storageId = null;
|
||||||
this.mentionSetting = MentionSetting.ALWAYS_NOTIFY;
|
this.mentionSetting = MentionSetting.ALWAYS_NOTIFY;
|
||||||
|
this.wallpaper = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Recipient(@NonNull RecipientId id, @NonNull RecipientDetails details, boolean resolved) {
|
public Recipient(@NonNull RecipientId id, @NonNull RecipientDetails details, boolean resolved) {
|
||||||
|
@ -381,6 +384,7 @@ public class Recipient {
|
||||||
this.groupsV1MigrationCapability = details.groupsV1MigrationCapability;
|
this.groupsV1MigrationCapability = details.groupsV1MigrationCapability;
|
||||||
this.storageId = details.storageId;
|
this.storageId = details.storageId;
|
||||||
this.mentionSetting = details.mentionSetting;
|
this.mentionSetting = details.mentionSetting;
|
||||||
|
this.wallpaper = details.wallpaper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull RecipientId getId() {
|
public @NonNull RecipientId getId() {
|
||||||
|
@ -843,6 +847,10 @@ public class Recipient {
|
||||||
return unidentifiedAccessMode;
|
return unidentifiedAccessMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public @Nullable ChatWallpaper getWallpaper() {
|
||||||
|
return wallpaper;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isSystemContact() {
|
public boolean isSystemContact() {
|
||||||
return contactUri != null;
|
return contactUri != null;
|
||||||
}
|
}
|
||||||
|
@ -961,7 +969,8 @@ public class Recipient {
|
||||||
groupsV1MigrationCapability == other.groupsV1MigrationCapability &&
|
groupsV1MigrationCapability == other.groupsV1MigrationCapability &&
|
||||||
insightsBannerTier == other.insightsBannerTier &&
|
insightsBannerTier == other.insightsBannerTier &&
|
||||||
Arrays.equals(storageId, other.storageId) &&
|
Arrays.equals(storageId, other.storageId) &&
|
||||||
mentionSetting == other.mentionSetting;
|
mentionSetting == other.mentionSetting &&
|
||||||
|
Objects.equals(wallpaper, other.wallpaper);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean allContentsAreTheSame(@NonNull List<Recipient> a, @NonNull List<Recipient> b) {
|
private static boolean allContentsAreTheSame(@NonNull List<Recipient> a, @NonNull List<Recipient> b) {
|
||||||
|
@ -999,7 +1008,6 @@ public class Recipient {
|
||||||
public @NonNull FallbackContactPhoto getPhotoForRecipientWithoutName() {
|
public @NonNull FallbackContactPhoto getPhotoForRecipientWithoutName() {
|
||||||
return new ResourceContactPhoto(R.drawable.ic_profile_outline_40, R.drawable.ic_profile_outline_20, R.drawable.ic_profile_outline_48);
|
return new ResourceContactPhoto(R.drawable.ic_profile_outline_40, R.drawable.ic_profile_outline_20, R.drawable.ic_profile_outline_48);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class MissingAddressError extends AssertionError {
|
private static class MissingAddressError extends AssertionError {
|
||||||
|
|
|
@ -19,6 +19,7 @@ import org.thoughtcrime.securesms.groups.GroupId;
|
||||||
import org.thoughtcrime.securesms.profiles.ProfileName;
|
import org.thoughtcrime.securesms.profiles.ProfileName;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
|
import org.thoughtcrime.securesms.wallpaper.ChatWallpaper;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
@ -65,6 +66,7 @@ public class RecipientDetails {
|
||||||
final InsightsBannerTier insightsBannerTier;
|
final InsightsBannerTier insightsBannerTier;
|
||||||
final byte[] storageId;
|
final byte[] storageId;
|
||||||
final MentionSetting mentionSetting;
|
final MentionSetting mentionSetting;
|
||||||
|
final ChatWallpaper wallpaper;
|
||||||
|
|
||||||
public RecipientDetails(@Nullable String name,
|
public RecipientDetails(@Nullable String name,
|
||||||
@NonNull Optional<Long> groupAvatarId,
|
@NonNull Optional<Long> groupAvatarId,
|
||||||
|
@ -110,6 +112,7 @@ public class RecipientDetails {
|
||||||
this.insightsBannerTier = settings.getInsightsBannerTier();
|
this.insightsBannerTier = settings.getInsightsBannerTier();
|
||||||
this.storageId = settings.getStorageId();
|
this.storageId = settings.getStorageId();
|
||||||
this.mentionSetting = settings.getMentionSetting();
|
this.mentionSetting = settings.getMentionSetting();
|
||||||
|
this.wallpaper = settings.getWallpaper();
|
||||||
|
|
||||||
if (name == null) this.name = settings.getSystemDisplayName();
|
if (name == null) this.name = settings.getSystemDisplayName();
|
||||||
else this.name = name;
|
else this.name = name;
|
||||||
|
@ -157,6 +160,7 @@ public class RecipientDetails {
|
||||||
this.groupsV1MigrationCapability = Recipient.Capability.UNKNOWN;
|
this.groupsV1MigrationCapability = Recipient.Capability.UNKNOWN;
|
||||||
this.storageId = null;
|
this.storageId = null;
|
||||||
this.mentionSetting = MentionSetting.ALWAYS_NOTIFY;
|
this.mentionSetting = MentionSetting.ALWAYS_NOTIFY;
|
||||||
|
this.wallpaper = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NonNull RecipientDetails forIndividual(@NonNull Context context, @NonNull RecipientSettings settings) {
|
public static @NonNull RecipientDetails forIndividual(@NonNull Context context, @NonNull RecipientSettings settings) {
|
||||||
|
|
|
@ -5,6 +5,8 @@ import android.widget.ImageView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.database.model.databaseprotos.Wallpaper;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -26,4 +28,6 @@ public interface ChatWallpaper extends Parcelable {
|
||||||
GradientChatWallpaper.GRADIENT_2);
|
GradientChatWallpaper.GRADIENT_2);
|
||||||
|
|
||||||
void loadInto(@NonNull ImageView imageView);
|
void loadInto(@NonNull ImageView imageView);
|
||||||
|
|
||||||
|
@NonNull Wallpaper serialize();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
package org.thoughtcrime.securesms.wallpaper;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.database.model.databaseprotos.Wallpaper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts persisted models of wallpaper into usable {@link ChatWallpaper} instances.
|
||||||
|
*/
|
||||||
|
public class ChatWallpaperFactory {
|
||||||
|
|
||||||
|
public static @NonNull ChatWallpaper create(@NonNull Wallpaper model) {
|
||||||
|
if (model.hasSingleColor()) {
|
||||||
|
return new GradientChatWallpaper(model.getSingleColor().getColor());
|
||||||
|
} else if (model.hasLinearGradient()) {
|
||||||
|
return buildForLinearGradinent(model.getLinearGradient());
|
||||||
|
} else if (model.hasFile()) {
|
||||||
|
return buildForFile(model.getFile());
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @NonNull ChatWallpaper create(@NonNull Uri uri) {
|
||||||
|
return new UriChatWallpaper(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NonNull ChatWallpaper buildForLinearGradinent(@NonNull Wallpaper.LinearGradient gradient) {
|
||||||
|
int[] colors = new int[gradient.getColorsCount()];
|
||||||
|
for (int i = 0; i < colors.length; i++) {
|
||||||
|
colors[i] = gradient.getColors(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
float[] positions = new float[gradient.getPositionsCount()];
|
||||||
|
for (int i = 0; i < positions.length; i++) {
|
||||||
|
positions[i] = gradient.getPositions(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GradientChatWallpaper(gradient.getRotation(), colors, positions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NonNull ChatWallpaper buildForFile(@NonNull Wallpaper.File file) {
|
||||||
|
Uri uri = Uri.parse(file.getUri());
|
||||||
|
return new UriChatWallpaper(uri);
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,8 @@ import android.widget.ImageView;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.database.model.databaseprotos.Wallpaper;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@ -81,6 +83,25 @@ final class GradientChatWallpaper implements ChatWallpaper, Parcelable {
|
||||||
imageView.setImageDrawable(buildDrawable());
|
imageView.setImageDrawable(buildDrawable());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull Wallpaper serialize() {
|
||||||
|
Wallpaper.LinearGradient.Builder builder = Wallpaper.LinearGradient.newBuilder();
|
||||||
|
|
||||||
|
builder.setRotation(degrees);
|
||||||
|
|
||||||
|
for (int color : colors) {
|
||||||
|
builder.addColors(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (float position : positions) {
|
||||||
|
builder.addPositions(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Wallpaper.newBuilder()
|
||||||
|
.setLinearGradient(builder)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
|
|
|
@ -7,46 +7,50 @@ import android.widget.ImageView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.database.model.databaseprotos.Wallpaper;
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||||
|
|
||||||
final class UriChatWallpaper implements ChatWallpaper, Parcelable {
|
final class UriChatWallpaper implements ChatWallpaper, Parcelable {
|
||||||
|
|
||||||
private final Uri uri;
|
private final Uri uri;
|
||||||
|
|
||||||
UriChatWallpaper(@NonNull Uri uri) {
|
public UriChatWallpaper(@NonNull Uri uri) {
|
||||||
this.uri = uri;
|
this.uri = uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected UriChatWallpaper(Parcel in) {
|
|
||||||
uri = in.readParcelable(Uri.class.getClassLoader());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
|
||||||
dest.writeParcelable(uri, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int describeContents() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final Creator<UriChatWallpaper> CREATOR = new Creator<UriChatWallpaper>() {
|
|
||||||
@Override
|
|
||||||
public UriChatWallpaper createFromParcel(Parcel in) {
|
|
||||||
return new UriChatWallpaper(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UriChatWallpaper[] newArray(int size) {
|
|
||||||
return new UriChatWallpaper[size];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadInto(@NonNull ImageView imageView) {
|
public void loadInto(@NonNull ImageView imageView) {
|
||||||
GlideApp.with(imageView)
|
GlideApp.with(imageView)
|
||||||
.load(uri)
|
.load(uri)
|
||||||
.into(imageView);
|
.into(imageView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull Wallpaper serialize() {
|
||||||
|
return Wallpaper.newBuilder()
|
||||||
|
.setFile(Wallpaper.File.newBuilder().setUri(uri.toString()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
dest.writeString(uri.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Creator<UriChatWallpaper> CREATOR = new Creator<UriChatWallpaper>() {
|
||||||
|
@Override
|
||||||
|
public UriChatWallpaper createFromParcel(Parcel in) {
|
||||||
|
return new UriChatWallpaper(Uri.parse(in.readString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UriChatWallpaper[] newArray(int size) {
|
||||||
|
return new UriChatWallpaper[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
package org.thoughtcrime.securesms.wallpaper;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.WorkerThread;
|
||||||
|
|
||||||
|
import com.annimon.stream.Stream;
|
||||||
|
|
||||||
|
import org.signal.core.util.StreamUtil;
|
||||||
|
import org.signal.core.util.logging.Log;
|
||||||
|
import org.thoughtcrime.securesms.crypto.AttachmentSecret;
|
||||||
|
import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider;
|
||||||
|
import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream;
|
||||||
|
import org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream;
|
||||||
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||||
|
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages the storage of custom wallpaper files.
|
||||||
|
*/
|
||||||
|
public final class WallpaperStorage {
|
||||||
|
|
||||||
|
private static final String TAG = Log.tag(WallpaperStorage.class);
|
||||||
|
|
||||||
|
private static final String DIRECTORY = "wallpapers";
|
||||||
|
private static final String FILENAME_BASE = "wallpaper";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the provided input stream as a new wallpaper file.
|
||||||
|
*/
|
||||||
|
@WorkerThread
|
||||||
|
public static @NonNull ChatWallpaper save(@NonNull Context context, @NonNull InputStream wallpaperStream) throws IOException {
|
||||||
|
File directory = context.getDir(DIRECTORY, Context.MODE_PRIVATE);
|
||||||
|
File file = File.createTempFile(FILENAME_BASE, "", directory);
|
||||||
|
|
||||||
|
StreamUtil.copy(wallpaperStream, getOutputStream(context, file));
|
||||||
|
|
||||||
|
return ChatWallpaperFactory.create(PartAuthority.getWallpaperUri(file.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
public static @NonNull InputStream read(@NonNull Context context, String filename) throws IOException {
|
||||||
|
File directory = context.getDir(DIRECTORY, Context.MODE_PRIVATE);
|
||||||
|
File wallpaperFile = new File(directory, filename);
|
||||||
|
|
||||||
|
return getInputStream(context, wallpaperFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
public static @NonNull List<ChatWallpaper> getAll(@NonNull Context context) {
|
||||||
|
File directory = context.getDir(DIRECTORY, Context.MODE_PRIVATE);
|
||||||
|
File[] allFiles = directory.listFiles(pathname -> pathname.getName().contains(FILENAME_BASE));
|
||||||
|
|
||||||
|
return Stream.of(allFiles)
|
||||||
|
.map(File::getName)
|
||||||
|
.map(PartAuthority::getWallpaperUri)
|
||||||
|
.map(ChatWallpaperFactory::create)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when wallpaper is deselected. This will check anywhere the wallpaper could be used, and
|
||||||
|
* if we discover it's unused, we'll delete the file.
|
||||||
|
*/
|
||||||
|
@WorkerThread
|
||||||
|
public static void onWallpaperDeselected(@NonNull Context context, @NonNull Uri uri) {
|
||||||
|
Uri globalUri = SignalStore.wallpaper().getCurrentWallpaperUri();
|
||||||
|
if (Objects.equals(uri, globalUri)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int recipientCount = DatabaseFactory.getRecipientDatabase(context).getWallpaperUriUsageCount(uri);
|
||||||
|
if (recipientCount > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String filename = PartAuthority.getWallpaperFilename(uri);
|
||||||
|
File directory = context.getDir(DIRECTORY, Context.MODE_PRIVATE);
|
||||||
|
File wallpaperFile = new File(directory, filename);
|
||||||
|
|
||||||
|
if (!wallpaperFile.delete()) {
|
||||||
|
Log.w(TAG, "Failed to delete " + filename + "!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NonNull OutputStream getOutputStream(@NonNull Context context, File outputFile) throws IOException {
|
||||||
|
AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret();
|
||||||
|
return ModernEncryptingPartOutputStream.createFor(attachmentSecret, outputFile, true).second;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NonNull InputStream getInputStream(@NonNull Context context, File inputFile) throws IOException {
|
||||||
|
AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret();
|
||||||
|
return ModernDecryptingPartInputStream.createFor(attachmentSecret, inputFile, 0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -91,3 +91,25 @@ message DeviceLastResetTime {
|
||||||
|
|
||||||
repeated Pair resetTime = 1;
|
repeated Pair resetTime = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message Wallpaper {
|
||||||
|
message SingleColor {
|
||||||
|
int32 color = 1;
|
||||||
|
}
|
||||||
|
message LinearGradient {
|
||||||
|
float rotation = 1;
|
||||||
|
repeated int32 colors = 2;
|
||||||
|
repeated float positions = 3;
|
||||||
|
}
|
||||||
|
message File {
|
||||||
|
string uri = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
oneof wallpaper {
|
||||||
|
SingleColor singleColor = 1;
|
||||||
|
LinearGradient linearGradient = 2;
|
||||||
|
File file = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
float dimLevelInDarkMode = 4;
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue