That fuck shit the fascists are using
at master 181 lines 5.7 kB view raw
1/* 2 * Copyright 2023 Signal Messenger, LLC 3 * SPDX-License-Identifier: AGPL-3.0-only 4 */ 5 6package org.tm.archive.providers 7 8import android.content.ContentUris 9import android.content.ContentValues 10import android.content.Intent 11import android.content.UriMatcher 12import android.database.Cursor 13import android.graphics.Bitmap 14import android.net.Uri 15import android.os.ParcelFileDescriptor 16import org.signal.core.util.concurrent.SignalExecutors 17import org.signal.core.util.logging.Log 18import org.tm.archive.BuildConfig 19import org.tm.archive.database.SignalDatabase 20import org.tm.archive.profiles.AvatarHelper 21import org.tm.archive.recipients.Recipient 22import org.tm.archive.recipients.RecipientId 23import org.tm.archive.service.KeyCachingService 24import org.tm.archive.util.AvatarUtil 25import org.tm.archive.util.DrawableUtil 26import org.tm.archive.util.MediaUtil 27import java.io.File 28import java.io.FileNotFoundException 29import java.io.IOException 30 31/** 32 * Provides user avatar bitmaps to the android system service for use in notifications and shortcuts. 33 * 34 * This file heavily borrows from [PartProvider] 35 */ 36class AvatarProvider : BaseContentProvider() { 37 38 companion object { 39 private val TAG = Log.tag(AvatarProvider::class.java) 40 private const val CONTENT_AUTHORITY = "${BuildConfig.APPLICATION_ID}.avatar" 41 private const val CONTENT_URI_STRING = "content://$CONTENT_AUTHORITY/avatar" 42 private const val AVATAR = 1 43 private val CONTENT_URI = Uri.parse(CONTENT_URI_STRING) 44 private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply { 45 addURI(CONTENT_AUTHORITY, "avatar/#", AVATAR) 46 } 47 48 private const val VERBOSE = false 49 50 @JvmStatic 51 fun getContentUri(recipientId: RecipientId): Uri { 52 if (VERBOSE) Log.d(TAG, "getContentUri: $recipientId") 53 return ContentUris.withAppendedId(CONTENT_URI, recipientId.toLong()) 54 } 55 } 56 57 override fun onCreate(): Boolean { 58 if (VERBOSE) Log.i(TAG, "onCreate called") 59 return true 60 } 61 62 @Throws(FileNotFoundException::class) 63 override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor? { 64 if (VERBOSE) Log.i(TAG, "openFile() called!") 65 66 if (KeyCachingService.isLocked(context)) { 67 Log.w(TAG, "masterSecret was null, abandoning.") 68 return null 69 } 70 71 if (SignalDatabase.instance == null) { 72 Log.w(TAG, "SignalDatabase unavailable") 73 return null 74 } 75 76 if (uriMatcher.match(uri) == AVATAR) { 77 if (VERBOSE) Log.i(TAG, "Loading avatar.") 78 try { 79 val recipient = getRecipientId(uri)?.let { Recipient.resolved(it) } ?: return null 80 return getParcelFileDescriptorForAvatar(recipient) 81 } catch (ioe: IOException) { 82 Log.w(TAG, ioe) 83 throw FileNotFoundException("Error opening file: " + ioe.message) 84 } 85 } 86 87 Log.w(TAG, "Bad request.") 88 throw FileNotFoundException("Request for bad avatar.") 89 } 90 91 override fun query(uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String?): Cursor? { 92 if (VERBOSE) Log.i(TAG, "query() called: $uri") 93 94 if (SignalDatabase.instance == null) { 95 Log.w(TAG, "SignalDatabase unavailable") 96 return null 97 } 98 99 if (uriMatcher.match(uri) == AVATAR) { 100 val recipientId = getRecipientId(uri) ?: return null 101 102 if (AvatarHelper.hasAvatar(context!!, recipientId)) { 103 val file: File = AvatarHelper.getAvatarFile(context!!, recipientId) 104 if (file.exists()) { 105 return createCursor(projection, file.name, file.length()) 106 } 107 } 108 109 return createCursor(projection, "fallback-$recipientId.jpg", 0) 110 } else { 111 return null 112 } 113 } 114 115 override fun getType(uri: Uri): String? { 116 if (VERBOSE) Log.i(TAG, "getType() called: $uri") 117 118 if (SignalDatabase.instance == null) { 119 Log.w(TAG, "SignalDatabase unavailable") 120 return null 121 } 122 123 if (uriMatcher.match(uri) == AVATAR) { 124 getRecipientId(uri) ?: return null 125 126 return MediaUtil.IMAGE_PNG 127 } 128 129 return null 130 } 131 132 override fun insert(uri: Uri, values: ContentValues?): Uri? { 133 if (VERBOSE) Log.i(TAG, "insert() called") 134 return null 135 } 136 137 override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int { 138 if (VERBOSE) Log.i(TAG, "delete() called") 139 context?.applicationContext?.revokeUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION) 140 return 0 141 } 142 143 override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<out String>?): Int { 144 if (VERBOSE) Log.i(TAG, "update() called") 145 return 0 146 } 147 148 private fun getRecipientId(uri: Uri): RecipientId? { 149 val rawRecipientId = ContentUris.parseId(uri) 150 if (rawRecipientId <= 0) { 151 Log.w(TAG, "Invalid recipient id.") 152 return null 153 } 154 155 val recipientId = RecipientId.from(rawRecipientId) 156 if (!SignalDatabase.recipients.containsId(recipientId)) { 157 Log.w(TAG, "Recipient does not exist.") 158 return null 159 } 160 161 return recipientId 162 } 163 164 private fun getParcelFileDescriptorForAvatar(recipient: Recipient): ParcelFileDescriptor { 165 val pipe: Array<ParcelFileDescriptor> = ParcelFileDescriptor.createPipe() 166 167 SignalExecutors.UNBOUNDED.execute { 168 ParcelFileDescriptor.AutoCloseOutputStream(pipe[1]).use { output -> 169 if (VERBOSE) Log.i(TAG, "Writing to pipe:${recipient.id}") 170 171 AvatarUtil.getBitmapForNotification(context!!, recipient, DrawableUtil.SHORTCUT_INFO_WRAPPED_SIZE).apply { 172 compress(Bitmap.CompressFormat.PNG, 100, output) 173 } 174 output.flush() 175 if (VERBOSE) Log.i(TAG, "Writing to pipe done:${recipient.id}") 176 } 177 } 178 179 return pipe[0] 180 } 181}