diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..436c3c41702b29f9df62b0504b5aaff2ee4ea70a --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,21 @@ +image: "registry.gitlab.e.foundation:5000/e/apps/docker-android-apps-cicd:latest" + +stages: + - build + +before_script: + - export GRADLE_USER_HOME=$(pwd)/.gradle + - chmod +x ./gradlew + +cache: + key: ${CI_PROJECT_ID} + paths: + - .gradle/ + +build: + stage: build + script: + - ./gradlew build --stacktrace + artifacts: + paths: + - app/build/outputs/ diff --git a/.travis.yml b/.travis.yml index 93c0ff54ff969290f17d75816ed24e9a689b8cae..22ce005512d445f078888a17977f4db67c62d8af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -75,7 +75,7 @@ before_script: # Build APK script: - | - ./gradlew assembleWithAnalyticsRelease assembleAndroidTest -PtestCoverageEnabled='true' + ./gradlew :presentation:assembleWithAnalyticsRelease assembleAndroidTest -PtestCoverageEnabled='true' retval=$? if [$retval -ne 0]; then echo "error on assembling, exit code: "$retval diff --git a/build.gradle b/build.gradle index 886609e5131ce1cecb16ff00bebde58ae593dc4c..dd44e1e26e960a58dd3b043a3139063159739ce9 100644 --- a/build.gradle +++ b/build.gradle @@ -7,6 +7,7 @@ buildscript { ext.androidx_emoji_version = '1.0.0' ext.androidx_exifinterface_version = '1.0.0' ext.androidx_testrunner_version = '1.1.0-alpha3' + ext.androidx_viewpager_version = '1.0.0-alpha01' ext.autodispose_version = '0.7.0' ext.conductor_version = '2.1.5' ext.dagger_version = "2.16" @@ -37,7 +38,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:3.3.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "io.realm:realm-gradle-plugin:$realm_version" } diff --git a/common/src/main/AndroidManifest.xml b/common/src/main/AndroidManifest.xml index 2a15365710f378e1e109075bef279269a9e5fcec..b9d4a22b9ff7828d0d2b5d595251d1b793944d21 100644 --- a/common/src/main/AndroidManifest.xml +++ b/common/src/main/AndroidManifest.xml @@ -16,5 +16,4 @@ ~ You should have received a copy of the GNU General Public License ~ along with QKSMS. If not, see . --> - + diff --git a/common/src/main/java/com/bumptech/glide/gifencoder/AnimatedGifEncoder.java b/common/src/main/java/com/bumptech/glide/gifencoder/AnimatedGifEncoder.java index e7d40ee1114970e50fcb487d2493bb1fd1192a7b..9d4f32be1e47f3e6471ad19eb4ea87c2185e11b0 100644 --- a/common/src/main/java/com/bumptech/glide/gifencoder/AnimatedGifEncoder.java +++ b/common/src/main/java/com/bumptech/glide/gifencoder/AnimatedGifEncoder.java @@ -70,8 +70,7 @@ public class AnimatedGifEncoder { * Sets the delay time between each frame, or changes it for subsequent frames * (applies to last frame added). * - * @param ms - * int delay time in milliseconds + * @param ms int delay time in milliseconds */ public void setDelay(int ms) { delay = Math.round(ms / 10.0f); @@ -82,8 +81,7 @@ public class AnimatedGifEncoder { * subsequent frames. Default is 0 if no transparent color has been set, * otherwise 2. * - * @param code - * int disposal code. + * @param code int disposal code. */ public void setDispose(int code) { if (code >= 0) { @@ -96,8 +94,7 @@ public class AnimatedGifEncoder { * 1; 0 means play indefinitely. Must be invoked before the first image is * added. * - * @param iter - * int number of iterations. + * @param iter int number of iterations. */ public void setRepeat(int iter) { if (iter >= 0) { @@ -112,8 +109,7 @@ public class AnimatedGifEncoder { * color becomes the transparent color for that frame. May be set to null to * indicate no transparent color. * - * @param color - * Color to be treated as transparent on display. + * @param color Color to be treated as transparent on display. */ public void setTransparent(int color) { transparent = color; @@ -126,8 +122,7 @@ public class AnimatedGifEncoder { * setSize was invoked, the size is used for all subsequent frames. * Otherwise, the actual size of the image is used for each frames. * - * @param im - * BufferedImage containing frame to write. + * @param im BufferedImage containing frame to write. * @return true if successful. */ public boolean addFrame(@Nullable Bitmap im) { @@ -140,17 +135,14 @@ public class AnimatedGifEncoder { * Invoking finish() flushes all frames. If setSize was invoked, the * size is used for all subsequent frames. Otherwise, the actual size of the image is used for * each frame. - * + *

* See page 11 of http://giflib.sourceforge.net/gif89.txt for the position of the frame * - * @param im - * BufferedImage containing frame to write. - * @param x - * Column number, in pixels, of the left edge of the image, with respect to the left - * edge of the Logical Screen. - * @param y - * Row number, in pixels, of the top edge of the image with respect to the top edge of - * the Logical Screen. + * @param im BufferedImage containing frame to write. + * @param x Column number, in pixels, of the left edge of the image, with respect to the left + * edge of the Logical Screen. + * @param y Row number, in pixels, of the top edge of the image with respect to the top edge of + * the Logical Screen. * @return true if successful. */ public boolean addFrame(@Nullable Bitmap im, int x, int y) { @@ -225,8 +217,7 @@ public class AnimatedGifEncoder { * Sets frame rate in frames per second. Equivalent to * setDelay(1000/fps). * - * @param fps - * float frame rate (frames per second) + * @param fps float frame rate (frames per second) */ public void setFrameRate(float fps) { if (fps != 0f) { @@ -253,10 +244,8 @@ public class AnimatedGifEncoder { * Sets the fixed GIF frame size for all the frames. * This should be called before start. * - * @param w - * int frame width. - * @param h - * int frame width. + * @param w int frame width. + * @param h int frame width. */ public void setSize(int w, int h) { if (started) { @@ -278,10 +267,8 @@ public class AnimatedGifEncoder { /** * Sets current GIF frame size. * - * @param w - * int frame width. - * @param h - * int frame width. + * @param w int frame width. + * @param h int frame width. */ private void setFrameSize(int w, int h) { width = w; @@ -292,8 +279,7 @@ public class AnimatedGifEncoder { * Initiates GIF file creation on the given stream. The stream is not closed * automatically. * - * @param os - * OutputStream on which GIF images are written. + * @param os OutputStream on which GIF images are written. * @return false if initial write failed. */ public boolean start(@Nullable OutputStream os) { @@ -313,8 +299,7 @@ public class AnimatedGifEncoder { /** * Initiates writing of a GIF file with the specified name. * - * @param file - * String containing output file name. + * @param file String containing output file name. * @return false if open or initial write failed. */ public boolean start(@NonNull String file) { @@ -366,7 +351,6 @@ public class AnimatedGifEncoder { /** * Returns index of palette color closest to c - * */ private int findClosest(int color) { if (colorTab == null) @@ -377,7 +361,7 @@ public class AnimatedGifEncoder { int minpos = 0; int dmin = 256 * 256 * 256; int len = colorTab.length; - for (int i = 0; i < len;) { + for (int i = 0; i < len; ) { int dr = r - (colorTab[i++] & 0xff); int dg = g - (colorTab[i++] & 0xff); int db = b - (colorTab[i] & 0xff); @@ -430,7 +414,7 @@ public class AnimatedGifEncoder { hasTransparentPixels = transparentPercentage > MIN_TRANSPARENT_PERCENTAGE; if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "got pixels for frame with " + transparentPercentage - + "% transparent pixels"); + + "% transparent pixels"); } } diff --git a/common/src/main/java/com/bumptech/glide/gifencoder/NeuQuant.java b/common/src/main/java/com/bumptech/glide/gifencoder/NeuQuant.java index 368a930b86bac675cdfbe34488814b10027d7dc2..48412a9173be032e237cbc3452f0a7cc98706a46 100644 --- a/common/src/main/java/com/bumptech/glide/gifencoder/NeuQuant.java +++ b/common/src/main/java/com/bumptech/glide/gifencoder/NeuQuant.java @@ -6,7 +6,7 @@ class NeuQuant { protected static final int netsize = 256; /* number of colours used */ /* four primes near 500 - assume no image has a length so large */ - /* that it is divisible by all four primes */ + /* that it is divisible by all four primes */ protected static final int prime1 = 499; protected static final int prime2 = 491; @@ -17,19 +17,19 @@ class NeuQuant { protected static final int minpicturebytes = (3 * prime4); - /* minimum size for input image */ + /* minimum size for input image */ - /* - * Program Skeleton ---------------- [select samplefac in range 1..30] [read - * image from input file] pic = (unsigned char*) malloc(3*width*height); - * initnet(pic,3*width*height,samplefac); learn(); unbiasnet(); [write output - * image header, using writecolourmap(f)] inxbuild(); write output image using - * inxsearch(b,g,r) - */ + /* + * Program Skeleton ---------------- [select samplefac in range 1..30] [read + * image from input file] pic = (unsigned char*) malloc(3*width*height); + * initnet(pic,3*width*height,samplefac); learn(); unbiasnet(); [write output + * image header, using writecolourmap(f)] inxbuild(); write output image using + * inxsearch(b,g,r) + */ - /* - * Network Definitions ------------------- - */ + /* + * Network Definitions ------------------- + */ protected static final int maxnetpos = (netsize - 1); @@ -54,19 +54,19 @@ class NeuQuant { /* defs for decreasing radius factor */ protected static final int initrad = (netsize >> 3); /* - * for 256 cols, radius - * starts - */ + * for 256 cols, radius + * starts + */ protected static final int radiusbiasshift = 6; /* at 32.0 biased by 6 bits */ protected static final int radiusbias = (((int) 1) << radiusbiasshift); protected static final int initradius = (initrad * radiusbias); /* - * and - * decreases - * by a - */ + * and + * decreases + * by a + */ protected static final int radiusdec = 30; /* factor of 1/30 each cycle */ @@ -86,9 +86,9 @@ class NeuQuant { protected static final int alpharadbias = (((int) 1) << alpharadbshift); - /* - * Types and Global Variables -------------------------- - */ + /* + * Types and Global Variables -------------------------- + */ protected byte[] thepicture; /* the input image itself */ @@ -101,7 +101,7 @@ class NeuQuant { protected int[] netindex = new int[256]; - /* for network lookup - really 256 */ + /* for network lookup - really 256 */ protected int[] bias = new int[netsize]; @@ -110,7 +110,7 @@ class NeuQuant { protected int[] radpower = new int[initrad]; - /* radpower for precomputation */ + /* radpower for precomputation */ /* * Initialise network in range (0,0,0) to (255,255,255) and set parameters @@ -168,7 +168,7 @@ class NeuQuant { p = network[i]; smallpos = i; smallval = p[1]; /* index on g */ - /* find smallest in i..netsize-1 */ + /* find smallest in i..netsize-1 */ for (j = i + 1; j < netsize; j++) { q = network[j]; if (q[1] < smallval) { /* index on g */ @@ -177,7 +177,7 @@ class NeuQuant { } } q = network[smallpos]; - /* swap p (i) and q (smallpos) entries */ + /* swap p (i) and q (smallpos) entries */ if (i != smallpos) { j = q[0]; q[0] = p[0]; @@ -192,7 +192,7 @@ class NeuQuant { q[3] = p[3]; p[3] = j; } - /* smallval entry is now in position i */ + /* smallval entry is now in position i */ if (smallval != previouscol) { netindex[previouscol] = (startpos + i) >> 1; for (j = previouscol + 1; j < smallval; j++) @@ -426,7 +426,7 @@ class NeuQuant { */ protected void altersingle(int alpha, int i, int b, int g, int r) { - /* alter hit neuron */ + /* alter hit neuron */ int[] n = network[i]; n[0] -= (alpha * (n[0] - b)) / initalpha; n[1] -= (alpha * (n[1] - g)) / initalpha; @@ -438,10 +438,10 @@ class NeuQuant { */ protected int contest(int b, int g, int r) { - /* finds closest neuron (min dist) and updates freq */ - /* finds best neuron (min dist-bias) and returns position */ - /* for frequently chosen neurons, freq[i] is high and bias[i] is negative */ - /* bias[i] = gamma*((1/netsize)-freq[i]) */ + /* finds closest neuron (min dist) and updates freq */ + /* finds best neuron (min dist-bias) and returns position */ + /* for frequently chosen neurons, freq[i] is high and bias[i] is negative */ + /* bias[i] = gamma*((1/netsize)-freq[i]) */ int i, dist, a, biasdist, betafreq; int bestpos, bestbiaspos, bestd, bestbiasd; diff --git a/data/src/main/java/com/moez/QKSMS/manager/ExternalBlockingManagerImpl.kt b/data/src/main/java/com/moez/QKSMS/manager/ExternalBlockingManagerImpl.kt index a8c7c936542570c6e27a6b59efac4443ae721414..ebf56d1c7687548937b402b7ac689c1d83bb3569 100644 --- a/data/src/main/java/com/moez/QKSMS/manager/ExternalBlockingManagerImpl.kt +++ b/data/src/main/java/com/moez/QKSMS/manager/ExternalBlockingManagerImpl.kt @@ -74,7 +74,8 @@ class ExternalBlockingManagerImpl @Inject constructor( if (prefs.sia.get()) { intent = tryOrNull(false) { context.packageManager.getApplicationInfo("org.mistergroup.shouldianswerpersonal", 0).enabled - Intent("org.mistergroup.shouldianswerpersonal.PublicService").setPackage("org.mistergroup.shouldianswerpersonal") + Intent("org.mistergroup.shouldianswerpersonal.PublicService") + .setPackage("org.mistergroup.shouldianswerpersonal") } ?: tryOrNull(false) { context.packageManager.getApplicationInfo("org.mistergroup.muzutozvednout", 0).enabled Intent("org.mistergroup.muzutozvednout.PublicService").setPackage("org.mistergroup.muzutozvednout") diff --git a/data/src/main/java/com/moez/QKSMS/manager/PermissionManagerImpl.kt b/data/src/main/java/com/moez/QKSMS/manager/PermissionManagerImpl.kt index df98b3b56a805687c817827e7cb60091c237102d..5ee266c4d7d286063bbd15281e3dfea320e105f4 100644 --- a/data/src/main/java/com/moez/QKSMS/manager/PermissionManagerImpl.kt +++ b/data/src/main/java/com/moez/QKSMS/manager/PermissionManagerImpl.kt @@ -31,16 +31,29 @@ class PermissionManagerImpl @Inject constructor(private val context: Context) : return Telephony.Sms.getDefaultSmsPackage(context) == context.packageName } - override fun hasReadSms(): Boolean = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_SMS) == PERMISSION_GRANTED + override fun hasReadSms(): Boolean { + return ContextCompat.checkSelfPermission(context, Manifest.permission.READ_SMS) == PERMISSION_GRANTED + } - override fun hasSendSms(): Boolean = ContextCompat.checkSelfPermission(context, Manifest.permission.SEND_SMS) == PERMISSION_GRANTED + override fun hasSendSms(): Boolean { + return ContextCompat.checkSelfPermission(context, Manifest.permission.SEND_SMS) == PERMISSION_GRANTED + } - override fun hasContacts(): Boolean = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) == PERMISSION_GRANTED + override fun hasContacts(): Boolean { + return ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) == PERMISSION_GRANTED + } - override fun hasPhone(): Boolean = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == PERMISSION_GRANTED + override fun hasPhone(): Boolean { + return ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == PERMISSION_GRANTED + } - override fun hasCalling(): Boolean = ContextCompat.checkSelfPermission(context, Manifest.permission.CALL_PHONE) == PERMISSION_GRANTED + override fun hasCalling(): Boolean { + return ContextCompat.checkSelfPermission(context, Manifest.permission.CALL_PHONE) == PERMISSION_GRANTED + } - override fun hasStorage(): Boolean = ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PERMISSION_GRANTED + override fun hasStorage(): Boolean { + return ContextCompat.checkSelfPermission(context, + Manifest.permission.WRITE_EXTERNAL_STORAGE) == PERMISSION_GRANTED + } } \ No newline at end of file diff --git a/data/src/main/java/com/moez/QKSMS/mapper/CursorToConversationImpl.kt b/data/src/main/java/com/moez/QKSMS/mapper/CursorToConversationImpl.kt index 3f8ac93be1e4ec910fef19157247eea48396304b..9ee32c36bcc940db14728e822e46565d7979032d 100644 --- a/data/src/main/java/com/moez/QKSMS/mapper/CursorToConversationImpl.kt +++ b/data/src/main/java/com/moez/QKSMS/mapper/CursorToConversationImpl.kt @@ -40,7 +40,8 @@ class CursorToConversationImpl @Inject constructor( Threads.RECIPIENT_IDS, Threads.MESSAGE_COUNT, Threads.READ, - Threads.SNIPPET) + Threads.SNIPPET + ) const val ID = 0 const val DATE = 1 @@ -67,10 +68,7 @@ class CursorToConversationImpl @Inject constructor( override fun getConversationsCursor(lastSync: Long): Cursor? { return when (permissionManager.hasReadSms()) { - true -> context.contentResolver.query(URI, PROJECTION, - "date > $lastSync", null, - "date desc") - + true -> context.contentResolver.query(URI, PROJECTION, "date > $lastSync", null, "date desc") false -> null } } diff --git a/data/src/main/java/com/moez/QKSMS/mapper/CursorToMessageImpl.kt b/data/src/main/java/com/moez/QKSMS/mapper/CursorToMessageImpl.kt index b0f268a31f2fb673cd347ab8406f73b834a993df..08bfb8f37837844120c57bcb01efa55bba950279 100644 --- a/data/src/main/java/com/moez/QKSMS/mapper/CursorToMessageImpl.kt +++ b/data/src/main/java/com/moez/QKSMS/mapper/CursorToMessageImpl.kt @@ -67,7 +67,8 @@ class CursorToMessageImpl @Inject constructor( Mms.DELIVERY_REPORT, Mms.READ_REPORT, MmsSms.PendingMessages.ERROR_TYPE, - Mms.STATUS) + Mms.STATUS + ) override fun map(from: Pair): Message { val cursor = from.first diff --git a/data/src/main/java/com/moez/QKSMS/mapper/RatingManagerImpl.kt b/data/src/main/java/com/moez/QKSMS/mapper/RatingManagerImpl.kt index ba85267c6b2fec2abf7e6db9721fc6f57ec7ddc0..09c8d60fc692d917f4a4cd79d160bb804cfa4740 100644 --- a/data/src/main/java/com/moez/QKSMS/mapper/RatingManagerImpl.kt +++ b/data/src/main/java/com/moez/QKSMS/mapper/RatingManagerImpl.kt @@ -40,8 +40,8 @@ class RatingManagerImpl @Inject constructor( override val shouldShowRating = Observables.combineLatest( sessions.asObservable(), rated.asObservable(), - dismissed.asObservable()) { sessions, rated, dismissed -> - + dismissed.asObservable() + ) { sessions, rated, dismissed -> sessions > RATING_THRESHOLD && !rated && !dismissed } diff --git a/data/src/main/java/com/moez/QKSMS/migration/QkRealmMigration.kt b/data/src/main/java/com/moez/QKSMS/migration/QkRealmMigration.kt index a8d5a38f324aec269640043593205ef2acdf9ae7..d9e87acf50f831bbfeaf659b54aca49cf8c8e2d2 100644 --- a/data/src/main/java/com/moez/QKSMS/migration/QkRealmMigration.kt +++ b/data/src/main/java/com/moez/QKSMS/migration/QkRealmMigration.kt @@ -22,7 +22,6 @@ import io.realm.DynamicRealm import io.realm.FieldAttribute import io.realm.RealmMigration - class QkRealmMigration : RealmMigration { companion object { diff --git a/data/src/main/java/com/moez/QKSMS/receiver/MmsSentReceiver.kt b/data/src/main/java/com/moez/QKSMS/receiver/MmsSentReceiver.kt index a7b2c136945389ab73885e56358aa3328ddba685..1aaba0e36b19686faaf1427ff3b2eb1cc710974b 100644 --- a/data/src/main/java/com/moez/QKSMS/receiver/MmsSentReceiver.kt +++ b/data/src/main/java/com/moez/QKSMS/receiver/MmsSentReceiver.kt @@ -68,9 +68,11 @@ class MmsSentReceiver : BroadcastReceiver() { // Need to figure out why the message isn't appearing in the PendingMessages Uri, // so that we can properly assign the error type val errorTypeValues = ContentValues(1) - errorTypeValues.put(Telephony.MmsSms.PendingMessages.ERROR_TYPE, Telephony.MmsSms.ERR_TYPE_GENERIC_PERMANENT) + errorTypeValues.put(Telephony.MmsSms.PendingMessages.ERROR_TYPE, + Telephony.MmsSms.ERR_TYPE_GENERIC_PERMANENT) SqliteWrapper.update(context, context.contentResolver, Telephony.MmsSms.PendingMessages.CONTENT_URI, - errorTypeValues, "${Telephony.MmsSms.PendingMessages.MSG_ID} = ?", arrayOf(messageId.toString())) + errorTypeValues, "${Telephony.MmsSms.PendingMessages.MSG_ID} = ?", + arrayOf(messageId.toString())) } catch (e: MmsException) { e.printStackTrace() diff --git a/data/src/main/java/com/moez/QKSMS/repository/BackupRepositoryImpl.kt b/data/src/main/java/com/moez/QKSMS/repository/BackupRepositoryImpl.kt index 11de53661a49e035179313b1566f9fe36f3ffada..62cf7b56370e30f9e0f9c1a9de125f1fc9b2de4f 100644 --- a/data/src/main/java/com/moez/QKSMS/repository/BackupRepositoryImpl.kt +++ b/data/src/main/java/com/moez/QKSMS/repository/BackupRepositoryImpl.kt @@ -42,7 +42,6 @@ import javax.inject.Inject import javax.inject.Singleton import kotlin.concurrent.schedule - @Singleton class BackupRepositoryImpl @Inject constructor( private val context: Context, @@ -78,11 +77,14 @@ class BackupRepositoryImpl @Inject constructor( val protocol: Int, val serviceCenter: String?, val locked: Boolean, - val subId: Int) + val subId: Int + ) // Subjects to emit our progress events to - private val backupProgress: Subject = BehaviorSubject.createDefault(BackupRepository.Progress.Idle()) - private val restoreProgress: Subject = BehaviorSubject.createDefault(BackupRepository.Progress.Idle()) + private val backupProgress: Subject = + BehaviorSubject.createDefault(BackupRepository.Progress.Idle()) + private val restoreProgress: Subject = + BehaviorSubject.createDefault(BackupRepository.Progress.Idle()) @Volatile private var stopFlag: Boolean = false @@ -116,7 +118,8 @@ class BackupRepositoryImpl @Inject constructor( try { // Create the directory and file val dir = File(BACKUP_DIRECTORY).apply { mkdirs() } - val file = File(dir, "backup-${SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).format(System.currentTimeMillis())}.json") + val timestamp = SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).format(System.currentTimeMillis()) + val file = File(dir, "backup-$timestamp.json") // Write the log to the file FileOutputStream(file, true).use { fileOutputStream -> fileOutputStream.write(json) } @@ -140,7 +143,8 @@ class BackupRepositoryImpl @Inject constructor( protocol = 0, serviceCenter = null, locked = message.locked, - subId = message.subId) + subId = message.subId + ) override fun getBackupProgress(): Observable = backupProgress @@ -176,6 +180,7 @@ class BackupRepositoryImpl @Inject constructor( } val messageCount = backup?.messages?.size ?: 0 + var errorCount = 0 backup?.messages?.forEachIndexed { index, message -> if (stopFlag) { @@ -187,19 +192,29 @@ class BackupRepositoryImpl @Inject constructor( // Update the progress restoreProgress.onNext(BackupRepository.Progress.Running(messageCount, index)) - context.contentResolver.insert(Telephony.Sms.CONTENT_URI, contentValuesOf( - Telephony.Sms.TYPE to message.type, - Telephony.Sms.ADDRESS to message.address, - Telephony.Sms.DATE to message.date, - Telephony.Sms.DATE_SENT to message.dateSent, - Telephony.Sms.READ to message.read, - Telephony.Sms.SEEN to 1, - Telephony.Sms.STATUS to message.status, - Telephony.Sms.BODY to message.body, - Telephony.Sms.PROTOCOL to message.protocol, - Telephony.Sms.SERVICE_CENTER to message.serviceCenter, - Telephony.Sms.LOCKED to message.locked, - Telephony.Sms.SUBSCRIPTION_ID to message.subId)) + try { + context.contentResolver.insert(Telephony.Sms.CONTENT_URI, contentValuesOf( + Telephony.Sms.TYPE to message.type, + Telephony.Sms.ADDRESS to message.address, + Telephony.Sms.DATE to message.date, + Telephony.Sms.DATE_SENT to message.dateSent, + Telephony.Sms.READ to message.read, + Telephony.Sms.SEEN to 1, + Telephony.Sms.STATUS to message.status, + Telephony.Sms.BODY to message.body, + Telephony.Sms.PROTOCOL to message.protocol, + Telephony.Sms.SERVICE_CENTER to message.serviceCenter, + Telephony.Sms.LOCKED to message.locked, + Telephony.Sms.SUBSCRIPTION_ID to message.subId) + ) + } catch (e: Exception) { + Timber.w(e) + errorCount++ + } + } + + if (errorCount > 0) { + Timber.w(Exception("Failed to backup $errorCount/$messageCount messages")) } // Sync the messages diff --git a/data/src/main/java/com/moez/QKSMS/repository/ContactRepositoryImpl.kt b/data/src/main/java/com/moez/QKSMS/repository/ContactRepositoryImpl.kt index c1205f4e770c786cb0e6e33cbc8cad6c1a04ec07..848e7d38a9f44b0f942f08b6d98a6401c163ea25 100644 --- a/data/src/main/java/com/moez/QKSMS/repository/ContactRepositoryImpl.kt +++ b/data/src/main/java/com/moez/QKSMS/repository/ContactRepositoryImpl.kt @@ -47,8 +47,13 @@ class ContactRepositoryImpl @Inject constructor( return Flowable.just(address) .map { when { - address.contains('@') -> Uri.withAppendedPath(Email.CONTENT_FILTER_URI, Uri.encode(address)) - else -> Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(address)) + address.contains('@') -> { + Uri.withAppendedPath(Email.CONTENT_FILTER_URI, Uri.encode(address)) + } + + else -> { + Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(address)) + } } } .mapNotNull { uri -> context.contentResolver.query(uri, arrayOf(BaseColumns._ID), null, null, null) } diff --git a/data/src/main/java/com/moez/QKSMS/repository/ConversationRepositoryImpl.kt b/data/src/main/java/com/moez/QKSMS/repository/ConversationRepositoryImpl.kt index 9995435681d1f9de4c17db44dc4d65b4a6b57f01..f36e001c3181b7a86d4c8277053d9992c67f447b 100644 --- a/data/src/main/java/com/moez/QKSMS/repository/ConversationRepositoryImpl.kt +++ b/data/src/main/java/com/moez/QKSMS/repository/ConversationRepositoryImpl.kt @@ -61,27 +61,31 @@ class ConversationRepositoryImpl @Inject constructor( override fun getConversationsSnapshot(): List { val realm = Realm.getDefaultInstance() - return realm.copyFromRealm(realm.where(Conversation::class.java) - .notEqualTo("id", 0L) - .greaterThan("count", 0) - .equalTo("archived", false) - .equalTo("blocked", false) - .isNotEmpty("recipients") - .sort("pinned", Sort.DESCENDING, "date", Sort.DESCENDING) - .findAll()) + return realm.copyFromRealm( + realm.where(Conversation::class.java) + .notEqualTo("id", 0L) + .greaterThan("count", 0) + .equalTo("archived", false) + .equalTo("blocked", false) + .isNotEmpty("recipients") + .sort("pinned", Sort.DESCENDING, "date", Sort.DESCENDING) + .findAll() + ) } override fun getTopConversations(): List { val realm = Realm.getDefaultInstance() - return realm.copyFromRealm(realm.where(Conversation::class.java) - .notEqualTo("id", 0L) - .greaterThan("count", 0) - .greaterThan("date", System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7)) - .equalTo("archived", false) - .equalTo("blocked", false) - .isNotEmpty("recipients") - .sort("pinned", Sort.DESCENDING, "count", Sort.DESCENDING) - .findAll()) + return realm.copyFromRealm( + realm.where(Conversation::class.java) + .notEqualTo("id", 0L) + .greaterThan("count", 0) + .greaterThan("date", System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7)) + .equalTo("archived", false) + .equalTo("blocked", false) + .isNotEmpty("recipients") + .sort("pinned", Sort.DESCENDING, "count", Sort.DESCENDING) + .findAll() + ) } override fun setConversationName(id: Long, name: String) { diff --git a/data/src/main/java/com/moez/QKSMS/repository/ImageRepostoryImpl.kt b/data/src/main/java/com/moez/QKSMS/repository/ImageRepostoryImpl.kt index 2d70bfaf015aebed1a7cb17861a22579223c7b9e..0b597f8ded958e70d089730222fbb41e01ed7d01 100644 --- a/data/src/main/java/com/moez/QKSMS/repository/ImageRepostoryImpl.kt +++ b/data/src/main/java/com/moez/QKSMS/repository/ImageRepostoryImpl.kt @@ -32,7 +32,6 @@ import java.io.FileOutputStream import java.io.IOException import javax.inject.Inject - class ImageRepostoryImpl @Inject constructor(private val context: Context) : ImageRepository { override fun loadImage(uri: Uri): Bitmap? { diff --git a/data/src/main/java/com/moez/QKSMS/repository/MessageRepositoryImpl.kt b/data/src/main/java/com/moez/QKSMS/repository/MessageRepositoryImpl.kt index 0c95a25ce85ebcf30a2a410f29def0cb77b40277..91d8cd7e2b2eddad36c112d193ffe2e567c6f789 100644 --- a/data/src/main/java/com/moez/QKSMS/repository/MessageRepositoryImpl.kt +++ b/data/src/main/java/com/moez/QKSMS/repository/MessageRepositoryImpl.kt @@ -210,7 +210,14 @@ class MessageRepositoryImpl @Inject constructor( } } - override fun sendMessage(subId: Int, threadId: Long, addresses: List, body: String, attachments: List, delay: Int) { + override fun sendMessage( + subId: Int, + threadId: Long, + addresses: List, + body: String, + attachments: List, + delay: Int + ) { if (addresses.size == 1 && attachments.isEmpty()) { // SMS if (delay > 0) { // With delay val sendTime = System.currentTimeMillis() + delay @@ -273,7 +280,8 @@ class MessageRepositoryImpl @Inject constructor( ?.let(SmsManagerFactory::createSmsManager) ?: SmsManager.getDefault() - val parts = smsManager.divideMessage(if (prefs.unicode.get()) StripAccents.stripAccents(message.body) else message.body) + val parts = smsManager + .divideMessage(if (prefs.unicode.get()) StripAccents.stripAccents(message.body) else message.body) ?: arrayListOf() val sentIntents = parts.map { @@ -285,11 +293,18 @@ class MessageRepositoryImpl @Inject constructor( val deliveredIntents = parts.map { context.registerReceiver(SmsDeliveredReceiver(), IntentFilter(SmsDeliveredReceiver.ACTION)) val intent = Intent(SmsDeliveredReceiver.ACTION).putExtra("id", message.id) - val pendingIntent = PendingIntent.getBroadcast(context, message.id.toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT) + val pendingIntent = PendingIntent + .getBroadcast(context, message.id.toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT) if (prefs.delivery.get()) pendingIntent else null } - smsManager.sendMultipartTextMessage(message.address, null, parts, ArrayList(sentIntents), ArrayList(deliveredIntents)) + smsManager.sendMultipartTextMessage( + message.address, + null, + parts, + ArrayList(sentIntents), + ArrayList(deliveredIntents) + ) } override fun cancelDelayedSms(id: Long) { diff --git a/data/src/main/java/com/moez/QKSMS/repository/ScheduledMessageRepositoryImpl.kt b/data/src/main/java/com/moez/QKSMS/repository/ScheduledMessageRepositoryImpl.kt index a35539ecea729bdfab05eb08bf24df4c3077ea9b..4c382bed979f8d40356b8272baac263073a560dd 100644 --- a/data/src/main/java/com/moez/QKSMS/repository/ScheduledMessageRepositoryImpl.kt +++ b/data/src/main/java/com/moez/QKSMS/repository/ScheduledMessageRepositoryImpl.kt @@ -26,15 +26,21 @@ import javax.inject.Inject class ScheduledMessageRepositoryImpl @Inject constructor() : ScheduledMessageRepository { - override fun saveScheduledMessage(date: Long, subId: Int, recipients: List, sendAsGroup: Boolean, - body: String, attachments: List) { - + override fun saveScheduledMessage( + date: Long, + subId: Int, + recipients: List, + sendAsGroup: Boolean, + body: String, + attachments: List + ) { Realm.getDefaultInstance().use { realm -> val id = (realm.where(ScheduledMessage::class.java).max("id")?.toLong() ?: -1) + 1 val recipientsRealmList = RealmList(*recipients.toTypedArray()) val attachmentsRealmList = RealmList(*attachments.toTypedArray()) - val message = ScheduledMessage(id, date, subId, recipientsRealmList, sendAsGroup, body, attachmentsRealmList) + val message = ScheduledMessage(id, date, subId, recipientsRealmList, sendAsGroup, body, + attachmentsRealmList) realm.executeTransaction { realm.insertOrUpdate(message) } } diff --git a/data/src/main/java/com/moez/QKSMS/repository/SyncRepositoryImpl.kt b/data/src/main/java/com/moez/QKSMS/repository/SyncRepositoryImpl.kt index 0ecd558ea03492a3d2a93bd78e9eef8f0d5491a5..fb9a601d4153406f6df6b79122d6bee62560d57e 100644 --- a/data/src/main/java/com/moez/QKSMS/repository/SyncRepositoryImpl.kt +++ b/data/src/main/java/com/moez/QKSMS/repository/SyncRepositoryImpl.kt @@ -20,7 +20,6 @@ package com.moez.QKSMS.repository import android.content.ContentResolver import android.content.ContentUris -import android.content.Context import android.net.Uri import android.provider.Telephony import android.telephony.PhoneNumberUtils @@ -48,7 +47,6 @@ import javax.inject.Singleton @Singleton class SyncRepositoryImpl @Inject constructor( - private val context: Context, private val contentResolver: ContentResolver, private val conversationRepo: ConversationRepository, private val cursorToConversation: CursorToConversation, @@ -67,9 +65,11 @@ class SyncRepositoryImpl @Inject constructor( val archived: Boolean, val blocked: Boolean, val pinned: Boolean, - val name: String) + val name: String + ) - override val syncProgress: Subject = BehaviorSubject.createDefault(SyncRepository.SyncProgress.Idle()) + override val syncProgress: Subject = + BehaviorSubject.createDefault(SyncRepository.SyncProgress.Idle()) override fun syncMessages() { @@ -111,7 +111,6 @@ class SyncRepositoryImpl @Inject constructor( var progress = 0 - // Sync messages messageCursor?.use { val messageColumns = CursorToMessage.MessageColumns(messageCursor) @@ -152,7 +151,8 @@ class SyncRepositoryImpl @Inject constructor( .distinct("threadId") .findAll() .forEach { message -> - val conversation = conversations.firstOrNull { conversation -> conversation.id == message.threadId } + val conversation = conversations + .firstOrNull { conversation -> conversation.id == message.threadId } conversation?.date = message.date conversation?.snippet = message.getSummary() conversation?.me = message.isMe() @@ -161,7 +161,6 @@ class SyncRepositoryImpl @Inject constructor( realm.insertOrUpdate(conversations) } - // Sync recipients recipientCursor?.use { val contacts = realm.copyToRealm(getContacts()) @@ -276,7 +275,11 @@ class SyncRepositoryImpl @Inject constructor( // Update all the matching recipients with the new contact val updatedRecipients = recipients - .filter { recipient -> contact.numbers.any { number -> PhoneNumberUtils.compare(recipient.address, number.address) } } + .filter { recipient -> + contact.numbers.any { number -> + PhoneNumberUtils.compare(recipient.address, number.address) + } + } .map { recipient -> recipient.apply { this.contact = contact } } realm.insertOrUpdate(updatedRecipients) diff --git a/data/src/main/java/com/moez/QKSMS/util/ContactImageLoader.kt b/data/src/main/java/com/moez/QKSMS/util/ContactImageLoader.kt index 4f6eb5c56196a57c20a658a42c919d18ac863ca3..8ab0f75676e6ba4a89678923670e77581960c125 100644 --- a/data/src/main/java/com/moez/QKSMS/util/ContactImageLoader.kt +++ b/data/src/main/java/com/moez/QKSMS/util/ContactImageLoader.kt @@ -36,7 +36,6 @@ import io.reactivex.schedulers.Schedulers import java.io.InputStream import java.security.MessageDigest - class ContactImageLoader( private val context: Context, private val contactRepo: ContactRepository @@ -46,12 +45,20 @@ class ContactImageLoader( return PhoneNumberUtils.isGlobalPhoneNumber(model) } - override fun buildLoadData(model: String, width: Int, height: Int, options: Options): ModelLoader.LoadData? { + override fun buildLoadData( + model: String, + width: Int, + height: Int, + options: Options + ): ModelLoader.LoadData? { return ModelLoader.LoadData(ContactImageKey(model), ContactImageFetcher(context, contactRepo, model)) } class Factory(val context: Context, val prefs: Preferences) : ModelLoaderFactory { - override fun build(multiFactory: MultiModelLoaderFactory) = ContactImageLoader(context, ContactRepositoryImpl(context, prefs)) + override fun build(multiFactory: MultiModelLoaderFactory): ContactImageLoader { + return ContactImageLoader(context, ContactRepositoryImpl(context, prefs)) + } + override fun teardown() {} // nothing to do here } diff --git a/data/src/main/java/com/moez/QKSMS/util/GifEncoder.kt b/data/src/main/java/com/moez/QKSMS/util/GifEncoder.kt index 3f1bb56c89c333a5055b84f45d31ae24be0cdd91..177c28d5d68a90a534dac79a662377593ebc098a 100644 --- a/data/src/main/java/com/moez/QKSMS/util/GifEncoder.kt +++ b/data/src/main/java/com/moez/QKSMS/util/GifEncoder.kt @@ -86,10 +86,15 @@ class GifEncoder internal constructor( return decoder } - private fun getTransformedFrame(currentFrame: Bitmap?, transformation: Transformation, drawable: GifDrawable): Resource { + private fun getTransformedFrame( + currentFrame: Bitmap?, + transformation: Transformation, + drawable: GifDrawable + ): Resource { // TODO: what if current frame is null? val bitmapResource = factory.buildFrameResource(currentFrame!!, bitmapPool) - val transformedResource = transformation.transform(context, bitmapResource, drawable.intrinsicWidth, drawable.intrinsicHeight) + val transformedResource = transformation.transform( + context, bitmapResource, drawable.intrinsicWidth, drawable.intrinsicHeight) if (bitmapResource != transformedResource) { bitmapResource.recycle() } diff --git a/data/src/main/java/com/moez/QKSMS/util/GlideAppModule.kt b/data/src/main/java/com/moez/QKSMS/util/GlideAppModule.kt index 124d3f4081339bdff4c23a5c6ba4e9f021e17e3f..59bd98737bd3ab6c242e3c8b42c1b3be9dac9980 100644 --- a/data/src/main/java/com/moez/QKSMS/util/GlideAppModule.kt +++ b/data/src/main/java/com/moez/QKSMS/util/GlideAppModule.kt @@ -38,7 +38,8 @@ class GlideAppModule : AppGlideModule() { override fun registerComponents(context: Context, glide: Glide, registry: Registry) { // TODO use DI to create the ContactImageLoader.Factory - registry.append(String::class.java, InputStream::class.java, ContactImageLoader.Factory(context, Preferences(RxSharedPreferences.create(PreferenceManager.getDefaultSharedPreferences(context))))) + registry.append(String::class.java, InputStream::class.java, ContactImageLoader.Factory(context, + Preferences(RxSharedPreferences.create(PreferenceManager.getDefaultSharedPreferences(context))))) } } \ No newline at end of file diff --git a/data/src/main/java/com/moez/QKSMS/util/ImageUtils.kt b/data/src/main/java/com/moez/QKSMS/util/ImageUtils.kt index 82dc3e9d34528aef8c43b283e15ea9b8d9c39c85..c9d613d833d0ce2c3697e725cdb7cb0b46b935d5 100644 --- a/data/src/main/java/com/moez/QKSMS/util/ImageUtils.kt +++ b/data/src/main/java/com/moez/QKSMS/util/ImageUtils.kt @@ -24,7 +24,6 @@ import android.net.Uri import com.bumptech.glide.load.resource.bitmap.CenterCrop import java.io.ByteArrayOutputStream - object ImageUtils { fun compressGif(context: Context, uri: Uri, maxBytes: Int): ByteArray { diff --git a/data/src/main/java/com/moez/QKSMS/util/NightModeManager.kt b/data/src/main/java/com/moez/QKSMS/util/NightModeManager.kt index 595e509333f45891d4c3b9a0a1c1150ee4ebebf3..4ecaa1ebd35dcd6f6506798f22c7f0f9a51c49c2 100644 --- a/data/src/main/java/com/moez/QKSMS/util/NightModeManager.kt +++ b/data/src/main/java/com/moez/QKSMS/util/NightModeManager.kt @@ -87,8 +87,18 @@ class NightModeManager @Inject constructor( val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager if (prefs.nightMode.get() == Preferences.NIGHT_MODE_AUTO) { - alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, dayCalendar.timeInMillis, AlarmManager.INTERVAL_DAY, dayIntent) - alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, nightCalendar.timeInMillis, AlarmManager.INTERVAL_DAY, nightIntent) + alarmManager.setInexactRepeating( + AlarmManager.RTC_WAKEUP, + dayCalendar.timeInMillis, + AlarmManager.INTERVAL_DAY, + dayIntent + ) + alarmManager.setInexactRepeating( + AlarmManager.RTC_WAKEUP, + nightCalendar.timeInMillis, + AlarmManager.INTERVAL_DAY, + nightIntent + ) } else { alarmManager.cancel(dayIntent) alarmManager.cancel(nightIntent) diff --git a/domain/src/main/java/com/moez/QKSMS/interactor/AddScheduledMessage.kt b/domain/src/main/java/com/moez/QKSMS/interactor/AddScheduledMessage.kt index db4496b818f59780fc889abf3c5513c0d459fe0b..3b31402aed86eaa90c847bbe40d5615c3094d545 100644 --- a/domain/src/main/java/com/moez/QKSMS/interactor/AddScheduledMessage.kt +++ b/domain/src/main/java/com/moez/QKSMS/interactor/AddScheduledMessage.kt @@ -27,16 +27,21 @@ class AddScheduledMessage @Inject constructor( private val updateScheduledMessageAlarms: UpdateScheduledMessageAlarms ) : Interactor() { - data class Params(val date: Long, - val subId: Int, - val recipients: List, - val sendAsGroup: Boolean, - val body: String, - val attachments: List) + data class Params( + val date: Long, + val subId: Int, + val recipients: List, + val sendAsGroup: Boolean, + val body: String, + val attachments: List + ) override fun buildObservable(params: Params): Flowable<*> { return Flowable.just(params) - .map { scheduledMessageRepo.saveScheduledMessage(it.date, it.subId, it.recipients, it.sendAsGroup, it.body, it.attachments) } + .map { + scheduledMessageRepo.saveScheduledMessage(it.date, it.subId, it.recipients, it.sendAsGroup, it.body, + it.attachments) + } .flatMap { updateScheduledMessageAlarms.buildObservable(Unit) } } diff --git a/domain/src/main/java/com/moez/QKSMS/interactor/DeleteMessages.kt b/domain/src/main/java/com/moez/QKSMS/interactor/DeleteMessages.kt index 3361106762e1548e612f8a63e4e4ef6ef7476626..8c0969b63179cdcc4aa5f80d1590724ccbcc2c1b 100644 --- a/domain/src/main/java/com/moez/QKSMS/interactor/DeleteMessages.kt +++ b/domain/src/main/java/com/moez/QKSMS/interactor/DeleteMessages.kt @@ -36,7 +36,9 @@ class DeleteMessages @Inject constructor( override fun buildObservable(params: Params): Flowable<*> { return Flowable.just(params.messageIds.toLongArray()) .doOnNext { messageIds -> messageRepo.deleteMessages(*messageIds) } // Delete the messages - .doOnNext { params.threadId?.let { conversationRepo.updateConversations(it) } } // Update the conversation + .doOnNext { + params.threadId?.let { conversationRepo.updateConversations(it) } // Update the conversation + } .doOnNext { params.threadId?.let { notificationManager.update(it) } } .flatMap { updateBadge.buildObservable(Unit) } // Update the badge } diff --git a/domain/src/main/java/com/moez/QKSMS/interactor/ReceiveMms.kt b/domain/src/main/java/com/moez/QKSMS/interactor/ReceiveMms.kt index 361dde2ec6fcfd21016474e20bf6cb55b61f40d1..bf540dfe01e0a1e318bab3ee9802cb951fde8a70 100644 --- a/domain/src/main/java/com/moez/QKSMS/interactor/ReceiveMms.kt +++ b/domain/src/main/java/com/moez/QKSMS/interactor/ReceiveMms.kt @@ -58,10 +58,17 @@ class ReceiveMms @Inject constructor( if (blocked) messageRepo.deleteMessages(message.id) } } - .doOnNext { message -> conversationRepo.updateConversations(message.threadId) } // Update the conversation - .mapNotNull { message -> conversationRepo.getOrCreateConversation(message.threadId) } // Map message to conversation + .doOnNext { message -> + conversationRepo.updateConversations(message.threadId) // Update the conversation + } + .mapNotNull { message -> + conversationRepo.getOrCreateConversation(message.threadId) // Map message to conversation + } .filter { conversation -> !conversation.blocked } // Don't notify for blocked conversations - .doOnNext { conversation -> if (conversation.archived) conversationRepo.markUnarchived(conversation.id) } // Unarchive conversation if necessary + .doOnNext { conversation -> + // Unarchive conversation if necessary + if (conversation.archived) conversationRepo.markUnarchived(conversation.id) + } .map { conversation -> conversation.id } // Map to the id because [delay] will put us on the wrong thread .doOnNext(notificationManager::update) // Update the notification .flatMap { updateBadge.buildObservable(Unit) } // Update the badge diff --git a/domain/src/main/java/com/moez/QKSMS/interactor/ReceiveSms.kt b/domain/src/main/java/com/moez/QKSMS/interactor/ReceiveSms.kt index 89cc50d6bdfd335635c0812cc36f27d65b78cfba..aaa6052e0381a8de8d177d8b10a9e858bdb2a687 100644 --- a/domain/src/main/java/com/moez/QKSMS/interactor/ReceiveSms.kt +++ b/domain/src/main/java/com/moez/QKSMS/interactor/ReceiveSms.kt @@ -53,14 +53,21 @@ class ReceiveSms @Inject constructor( val time = messages[0].timestampMillis val body: String = messages .mapNotNull { message -> message.displayMessageBody } - .reduce { body, new -> body + new } ?: "" + .reduce { body, new -> body + new } messageRepo.insertReceivedSms(it.subId, address, body, time) // Add the message to the db } - .doOnNext { message -> conversationRepo.updateConversations(message.threadId) } // Update the conversation - .mapNotNull { message -> conversationRepo.getOrCreateConversation(message.threadId) } // Map message to conversation + .doOnNext { message -> + conversationRepo.updateConversations(message.threadId) // Update the conversation + } + .mapNotNull { message -> + conversationRepo.getOrCreateConversation(message.threadId) // Map message to conversation + } .filter { conversation -> !conversation.blocked } // Don't notify for blocked conversations - .doOnNext { conversation -> if (conversation.archived) conversationRepo.markUnarchived(conversation.id) } // Unarchive conversation if necessary + .doOnNext { conversation -> + // Unarchive conversation if necessary + if (conversation.archived) conversationRepo.markUnarchived(conversation.id) + } .map { conversation -> conversation.id } // Map to the id because [delay] will put us on the wrong thread .doOnNext { threadId -> notificationManager.update(threadId) } // Update the notification .doOnNext { shortcutManager.updateShortcuts() } // Update shortcuts diff --git a/domain/src/main/java/com/moez/QKSMS/interactor/SendMessage.kt b/domain/src/main/java/com/moez/QKSMS/interactor/SendMessage.kt index 29e08068698d34c7b18e42f0eba3b7fff4c2792f..687a5f8195ece46bde83b7c48cece823aa7a8b2f 100644 --- a/domain/src/main/java/com/moez/QKSMS/interactor/SendMessage.kt +++ b/domain/src/main/java/com/moez/QKSMS/interactor/SendMessage.kt @@ -21,14 +21,12 @@ package com.moez.QKSMS.interactor import com.moez.QKSMS.model.Attachment import com.moez.QKSMS.repository.ConversationRepository import com.moez.QKSMS.repository.MessageRepository -import com.moez.QKSMS.repository.SyncRepository import io.reactivex.Flowable import javax.inject.Inject class SendMessage @Inject constructor( private val conversationRepo: ConversationRepository, - private val messageRepo: MessageRepository, - private val syncRepo: SyncRepository + private val messageRepo: MessageRepository ) : Interactor() { data class Params( @@ -37,11 +35,15 @@ class SendMessage @Inject constructor( val addresses: List, val body: String, val attachments: List = listOf(), - val delay: Int = 0) + val delay: Int = 0 + ) override fun buildObservable(params: Params): Flowable<*> = Flowable.just(Unit) .filter { params.addresses.isNotEmpty() } - .doOnNext { messageRepo.sendMessage(params.subId, params.threadId, params.addresses, params.body, params.attachments, params.delay) } + .doOnNext { + messageRepo.sendMessage(params.subId, params.threadId, params.addresses, params.body, + params.attachments, params.delay) + } .map { // On some manufacturers, we can't obtain a threadId for a new conversation. In // this case, find the threadId manually now that it contains a message diff --git a/domain/src/main/java/com/moez/QKSMS/interactor/UpdateScheduledMessageAlarms.kt b/domain/src/main/java/com/moez/QKSMS/interactor/UpdateScheduledMessageAlarms.kt index 58e8a6f71efc515a3056b0edafba702303e29b07..5b8ab1cadf3be5047a45ed891eff3c5078b392e3 100644 --- a/domain/src/main/java/com/moez/QKSMS/interactor/UpdateScheduledMessageAlarms.kt +++ b/domain/src/main/java/com/moez/QKSMS/interactor/UpdateScheduledMessageAlarms.kt @@ -34,7 +34,9 @@ class UpdateScheduledMessageAlarms @Inject constructor( .map { scheduledMessageRepo.getScheduledMessages() } // Get all the scheduled messages .map { it.map { message -> Pair(message.id, message.date) } } // Map the data we need out of Realm .flatMap { messages -> Flowable.fromIterable(messages) } // Turn the list into a stream - .doOnNext { (id, date) -> alarmManager.setAlarm(date, alarmManager.getScheduledMessageIntent(id)) } // Create alarm + .doOnNext { (id, date) -> + alarmManager.setAlarm(date, alarmManager.getScheduledMessageIntent(id)) // Create alarm + } .filter { (_, date) -> date < System.currentTimeMillis() } // Filter messages that should have already been sent .flatMap { (id, _) -> sendScheduledMessage.buildObservable(id) } // Send them } diff --git a/domain/src/main/java/com/moez/QKSMS/model/Attachment.kt b/domain/src/main/java/com/moez/QKSMS/model/Attachment.kt index df8d78a331fefbd4fd27eafef5204a1fe5ca5b03..ca2ff3b4a3a7177ca16f12fde30eff97802cdee8 100644 --- a/domain/src/main/java/com/moez/QKSMS/model/Attachment.kt +++ b/domain/src/main/java/com/moez/QKSMS/model/Attachment.kt @@ -51,4 +51,4 @@ sealed class Attachment { } -class Attachments(attachments: List): List by attachments +class Attachments(attachments: List) : List by attachments diff --git a/domain/src/main/java/com/moez/QKSMS/model/Conversation.kt b/domain/src/main/java/com/moez/QKSMS/model/Conversation.kt index 6c12b9a3860485ed9f1d84a4374f38fbffb93af8..dfb60780debfbbc71f1efbef5bbc94b03f732b78 100644 --- a/domain/src/main/java/com/moez/QKSMS/model/Conversation.kt +++ b/domain/src/main/java/com/moez/QKSMS/model/Conversation.kt @@ -36,8 +36,7 @@ open class Conversation( var me: Boolean = false, var draft: String = "", - // For group chats, the user is allowed to set a custom title for the conversation - var name: String = "" + var name: String = "" // For group chats, the user is allowed to set a custom title for the conversation ) : RealmObject() { fun getTitle(): String { diff --git a/domain/src/main/java/com/moez/QKSMS/model/ScheduledMessage.kt b/domain/src/main/java/com/moez/QKSMS/model/ScheduledMessage.kt index 7cdaa3a596cdfeca9f8273e48440e457096a31fe..de07f19527ed7cc6d1ebd2b047e9e12ffe76427d 100644 --- a/domain/src/main/java/com/moez/QKSMS/model/ScheduledMessage.kt +++ b/domain/src/main/java/com/moez/QKSMS/model/ScheduledMessage.kt @@ -32,13 +32,15 @@ open class ScheduledMessage( var attachments: RealmList = RealmList() ) : RealmObject() { - fun copy(id: Long = this.id, - date: Long = this.date, - subId: Int = this.subId, - recipients: RealmList = this.recipients, - sendAsGroup: Boolean = this.sendAsGroup, - body: String = this.body, - attachments: RealmList = this.attachments): ScheduledMessage { + fun copy( + id: Long = this.id, + date: Long = this.date, + subId: Int = this.subId, + recipients: RealmList = this.recipients, + sendAsGroup: Boolean = this.sendAsGroup, + body: String = this.body, + attachments: RealmList = this.attachments + ): ScheduledMessage { return ScheduledMessage(id, date, subId, recipients, sendAsGroup, body, attachments) } diff --git a/domain/src/main/java/com/moez/QKSMS/repository/MessageRepository.kt b/domain/src/main/java/com/moez/QKSMS/repository/MessageRepository.kt index ce8cf4babaf7ec0b3d1ccd208fe3599a8119daa7..b3ec7b5891b26a707b9156537cc118fb7a911fd9 100644 --- a/domain/src/main/java/com/moez/QKSMS/repository/MessageRepository.kt +++ b/domain/src/main/java/com/moez/QKSMS/repository/MessageRepository.kt @@ -57,7 +57,14 @@ interface MessageRepository { fun markUnread(vararg threadIds: Long) - fun sendMessage(subId: Int, threadId: Long, addresses: List, body: String, attachments: List, delay: Int = 0) + fun sendMessage( + subId: Int, + threadId: Long, + addresses: List, + body: String, + attachments: List, + delay: Int = 0 + ) /** * Attempts to send the SMS message. This can be called if the message has already been persisted diff --git a/domain/src/main/java/com/moez/QKSMS/repository/ScheduledMessageRepository.kt b/domain/src/main/java/com/moez/QKSMS/repository/ScheduledMessageRepository.kt index a13ea778cfb92a4a680538dbf0807b1041704940..06f8e04fca3e33a0b12201608c152bfbe2471f32 100644 --- a/domain/src/main/java/com/moez/QKSMS/repository/ScheduledMessageRepository.kt +++ b/domain/src/main/java/com/moez/QKSMS/repository/ScheduledMessageRepository.kt @@ -26,8 +26,14 @@ interface ScheduledMessageRepository { /** * Saves a scheduled message */ - fun saveScheduledMessage(date: Long, subId: Int, recipients: List, sendAsGroup: Boolean, body: String, - attachments: List) + fun saveScheduledMessage( + date: Long, + subId: Int, + recipients: List, + sendAsGroup: Boolean, + body: String, + attachments: List + ) /** * Returns all of the scheduled messages, sorted chronologically diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 57884974250274279ece074fe61f47a5944ade13..013022680dae6360029068d062f66904b2923e98 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sat Nov 17 17:39:22 EST 2018 +#Sat Jan 19 18:02:14 EST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip diff --git a/presentation/build.gradle b/presentation/build.gradle index 4fb2c788793710b9e408cedbf700494f21538249..6af18fbb0456893e78b1ec430f0adfe5d09e7693 100644 --- a/presentation/build.gradle +++ b/presentation/build.gradle @@ -31,8 +31,8 @@ android { applicationId "foundation.e.message" minSdkVersion 21 targetSdkVersion 28 - versionCode 191 - versionName "3.6.1" + versionCode 194 + versionName "3.6.4" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" buildConfigField "String", "BUGSNAG_API_KEY", "\"${System.getenv("BUGSNAG_API_KEY")}\"" @@ -113,8 +113,9 @@ dependencies { implementation "androidx.appcompat:appcompat:$androidx_appcompat_version" implementation "androidx.constraintlayout:constraintlayout:$androidx_constraintlayout_version" implementation "androidx.core:core-ktx:$androidx_core_version" - implementation "com.google.android.material:material:$material_version" implementation "androidx.emoji:emoji-appcompat:$androidx_emoji_version" + implementation "androidx.viewpager2:viewpager2:$androidx_viewpager_version" + implementation "com.google.android.material:material:$material_version" // conductor implementation "com.bluelinelabs:conductor:$conductor_version" diff --git a/presentation/src/main/java/com/moez/QKSMS/common/MenuItemAdapter.kt b/presentation/src/main/java/com/moez/QKSMS/common/MenuItemAdapter.kt index 9e91adb955e3571b16e2b7a595d0843efb12d92a..bc506cd802c61744a6de2f3363ee4218b426c037 100644 --- a/presentation/src/main/java/com/moez/QKSMS/common/MenuItemAdapter.kt +++ b/presentation/src/main/java/com/moez/QKSMS/common/MenuItemAdapter.kt @@ -83,7 +83,7 @@ class MenuItemAdapter @Inject constructor(private val context: Context, private override fun onBindViewHolder(holder: QkViewHolder, position: Int) { val menuItem = getItem(position) - val view = holder.itemView + val view = holder.containerView view.title.text = menuItem.title view.check.isActivated = (menuItem.actionId == selectedItem) diff --git a/presentation/src/main/java/com/moez/QKSMS/common/Navigator.kt b/presentation/src/main/java/com/moez/QKSMS/common/Navigator.kt index 6f46654064e6e7dbee008be2246a8ede7d74056a..6bd8532011b07e265f4e67b339c04860cfcdd244 100644 --- a/presentation/src/main/java/com/moez/QKSMS/common/Navigator.kt +++ b/presentation/src/main/java/com/moez/QKSMS/common/Navigator.kt @@ -189,7 +189,9 @@ class Navigator @Inject constructor( .append("Version: ${BuildConfig.VERSION_NAME}\n") .append("Device: ${Build.BRAND} ${Build.MODEL}\n") .append("SDK: ${Build.VERSION.SDK_INT}\n") - .append("Upgraded".takeIf { billingManager.upgradeStatus.blockingFirst() } ?: "") + .append("Upgraded" + .takeIf { BuildConfig.FLAVOR != "noAnalytics" } + .takeIf { billingManager.upgradeStatus.blockingFirst() } ?: "") .toString()) startActivityExternal(intent) } @@ -204,9 +206,15 @@ class Navigator @Inject constructor( } fun addContact(address: String) { - val intent = Intent(Intent.ACTION_INSERT) - .setType(ContactsContract.Contacts.CONTENT_TYPE) - .putExtra(ContactsContract.Intents.Insert.PHONE, address) + val uri = Uri.parse("tel: $address") + var intent = Intent(ContactsContract.Intents.SHOW_OR_CREATE_CONTACT, uri) + + if (intent.resolveActivity(context.packageManager) == null) { + intent = Intent(Intent.ACTION_INSERT) + .setType(ContactsContract.Contacts.CONTENT_TYPE) + .putExtra(ContactsContract.Intents.Insert.PHONE, address) + } + startActivityExternal(intent) } diff --git a/presentation/src/main/java/com/moez/QKSMS/common/base/QkRealmAdapter.kt b/presentation/src/main/java/com/moez/QKSMS/common/base/QkRealmAdapter.kt index 093635fec42d77c257ebdc3eb5978a2ceb7cb0d3..6934ead8ca4b2e2083365779cff0fe4d958d4267 100644 --- a/presentation/src/main/java/com/moez/QKSMS/common/base/QkRealmAdapter.kt +++ b/presentation/src/main/java/com/moez/QKSMS/common/base/QkRealmAdapter.kt @@ -28,6 +28,7 @@ import io.realm.RealmList import io.realm.RealmModel import io.realm.RealmRecyclerViewAdapter import io.realm.RealmResults +import timber.log.Timber abstract class QkRealmAdapter : RealmRecyclerViewAdapter(null, true) { @@ -79,6 +80,15 @@ abstract class QkRealmAdapter : RealmRecyclerViewAdapter= 0 are allowed. Input was: $index") + return null + } + + return super.getItem(index) + } + override fun updateData(data: OrderedRealmCollection?) { if (getData() === data) return diff --git a/presentation/src/main/java/com/moez/QKSMS/common/base/QkViewHolder.kt b/presentation/src/main/java/com/moez/QKSMS/common/base/QkViewHolder.kt index accb7d0ed5894d06b086a2339130a83109384ad1..56e6743498a44e80fee0eaecb811798e45a21f71 100644 --- a/presentation/src/main/java/com/moez/QKSMS/common/base/QkViewHolder.kt +++ b/presentation/src/main/java/com/moez/QKSMS/common/base/QkViewHolder.kt @@ -20,5 +20,8 @@ package com.moez.QKSMS.common.base import android.view.View import androidx.recyclerview.widget.RecyclerView +import kotlinx.android.extensions.LayoutContainer -class QkViewHolder(view: View) : RecyclerView.ViewHolder(view) \ No newline at end of file +class QkViewHolder(view: View) : RecyclerView.ViewHolder(view), LayoutContainer { + override val containerView: View = view +} diff --git a/presentation/src/main/java/com/moez/QKSMS/common/util/BillingManager.kt b/presentation/src/main/java/com/moez/QKSMS/common/util/BillingManager.kt index bc6481ca426004a1f789622dd076b623ff2a4df7..1a2919d55b525c969386b6464d0f0b6c7be71dc5 100644 --- a/presentation/src/main/java/com/moez/QKSMS/common/util/BillingManager.kt +++ b/presentation/src/main/java/com/moez/QKSMS/common/util/BillingManager.kt @@ -77,10 +77,13 @@ class BillingManager @Inject constructor( private fun queryPurchases() { executeServiceRequest { - val purchasesResult = billingClient.queryPurchases(SkuType.INAPP) + // Load the cached data + purchaseListObservable.onNext(billingClient.queryPurchases(SkuType.INAPP).purchasesList.orEmpty()) - // Handle purchase result - purchaseListObservable.onNext(purchasesResult.purchasesList.orEmpty()) + // On a fresh device, the purchase might not be cached, and so we'll need to force a refresh + billingClient.queryPurchaseHistoryAsync(SkuType.INAPP) { _, _ -> + purchaseListObservable.onNext(billingClient.queryPurchases(SkuType.INAPP).purchasesList.orEmpty()) + } } } diff --git a/presentation/src/main/java/com/moez/QKSMS/common/util/DateFormatter.kt b/presentation/src/main/java/com/moez/QKSMS/common/util/DateFormatter.kt index eb89bc99fcb769f86f4c3b8c663de6c90a9e8acb..00974bbc34c48809a2cc87d49a1d2deb4dd82e13 100644 --- a/presentation/src/main/java/com/moez/QKSMS/common/util/DateFormatter.kt +++ b/presentation/src/main/java/com/moez/QKSMS/common/util/DateFormatter.kt @@ -32,13 +32,17 @@ import javax.inject.Singleton class DateFormatter @Inject constructor(val context: Context) { /** - * Replace 12 hour format with 24 hour format if necessary + * Formats the [pattern] correctly for the current locale, and replaces 12 hour format with + * 24 hour format if necessary */ private fun getFormatter(pattern: String): SimpleDateFormat { - val isUsing24HourTime = DateFormat.is24HourFormat(context) + var formattedPattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), pattern) - return if (isUsing24HourTime) SimpleDateFormat(pattern.replace("h", "HH").replace(" a".toRegex(), ""), Locale.getDefault()) - else SimpleDateFormat(pattern, Locale.getDefault()) + if (DateFormat.is24HourFormat(context)) { + formattedPattern = formattedPattern.replace("h", "HH").replace(" a".toRegex(), "") + } + + return SimpleDateFormat(formattedPattern, Locale.getDefault()) } fun getDetailedTimestamp(date: Long): String { @@ -71,7 +75,7 @@ class DateFormatter @Inject constructor(val context: Context) { now.isSameDay(then) -> getFormatter("h:mm a") now.isSameWeek(then) -> getFormatter("E") now.isSameYear(then) -> getFormatter("MMM d") - else -> getFormatter("d/MM/yy") + else -> getFormatter("MM/d/yy") }.format(date) } diff --git a/presentation/src/main/java/com/moez/QKSMS/common/widget/AvatarView.kt b/presentation/src/main/java/com/moez/QKSMS/common/widget/AvatarView.kt index 7fd4b93687591947e39c69012f9869b864ce07b3..1f12c5575fbe4d4b8f12f624c51a73bc7dcf2b8a 100644 --- a/presentation/src/main/java/com/moez/QKSMS/common/widget/AvatarView.kt +++ b/presentation/src/main/java/com/moez/QKSMS/common/widget/AvatarView.kt @@ -89,10 +89,14 @@ class AvatarView @JvmOverloads constructor(context: Context, attrs: AttributeSet } } + /** + * If the [recipient] has a contact: use the contact's avatar, but keep the address. + * Use the recipient address otherwise. + */ fun setContact(recipient: Recipient?) { // If the recipient has a contact, just use that and return recipient?.contact?.let { contact -> - setContact(contact) + setContact(contact, recipient.address) return } @@ -102,10 +106,15 @@ class AvatarView @JvmOverloads constructor(context: Context, attrs: AttributeSet updateView() } - fun setContact(contact: Contact?) { + /** + * Use the [contact] information to display the avatar. + * A specific [contactAddress] can be specified (useful when the contact has several addresses). + */ + fun setContact(contact: Contact?, contactAddress: String? = null) { lookupKey = contact?.lookupKey name = contact?.name - address = contact?.numbers?.firstOrNull()?.address + // If a contactAddress has been given, we use it. Use the contact address otherwise. + address = contactAddress ?: contact?.numbers?.firstOrNull()?.address updateView() } diff --git a/presentation/src/main/java/com/moez/QKSMS/common/widget/TightTextView.kt b/presentation/src/main/java/com/moez/QKSMS/common/widget/TightTextView.kt index 6f5e9fc52b81d76b72063c6cc3fc5e5a4f29eb34..0c104b81c7a67f19b519898a619c3d93d9a7bed7 100644 --- a/presentation/src/main/java/com/moez/QKSMS/common/widget/TightTextView.kt +++ b/presentation/src/main/java/com/moez/QKSMS/common/widget/TightTextView.kt @@ -21,20 +21,28 @@ package com.moez.QKSMS.common.widget import android.content.Context import android.util.AttributeSet -class TightTextView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : QkTextView(context, attrs) { +class TightTextView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null +) : QkTextView(context, attrs) { override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { super.onMeasure(widthMeasureSpec, heightMeasureSpec) - layout?.let { - val maxLineWidth = (0 until layout.lineCount) - .asSequence() - .map { layout.getLineMax(it) } - .max() ?: 0f + // Get a non-null copy of the layout, if available. Then ensure we've got multiple lines + val layout = layout ?: return + if (layout.lineCount <= 1) { + return + } + + val maxLineWidth = (0 until layout.lineCount) + .map(layout::getLineWidth) + .max() ?: 0f - val width = (Math.ceil(maxLineWidth.toDouble()).toInt() + compoundPaddingLeft + compoundPaddingRight) - val height = measuredHeight - setMeasuredDimension(width, height) + val width = Math.ceil(maxLineWidth.toDouble()).toInt() + compoundPaddingLeft + compoundPaddingRight + if (width < measuredWidth) { + val widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.getMode(widthMeasureSpec)) + super.onMeasure(widthSpec, heightMeasureSpec) } } diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/backup/BackupAdapter.kt b/presentation/src/main/java/com/moez/QKSMS/feature/backup/BackupAdapter.kt index a06acecbca1a647bcf758e6e7d60784930e9372d..ff9270e8f57bc3ff5d8df47231ec83c21a11810b 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/backup/BackupAdapter.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/backup/BackupAdapter.kt @@ -50,7 +50,7 @@ class BackupAdapter @Inject constructor( override fun onBindViewHolder(holder: QkViewHolder, position: Int) { val backup = getItem(position) - val view = holder.itemView + val view = holder.containerView val count = backup.messages diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/blocked/BlockedAdapter.kt b/presentation/src/main/java/com/moez/QKSMS/feature/blocked/BlockedAdapter.kt index 42206ae5ac722cd63de8c6410d9bb730c7a4f056..f6c1df30905e3956e51d0fd851e527849ad9c07f 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/blocked/BlockedAdapter.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/blocked/BlockedAdapter.kt @@ -36,15 +36,15 @@ class BlockedAdapter @Inject constructor() : QkRealmAdapter() { val view = LayoutInflater.from(parent.context).inflate(R.layout.blocked_list_item, parent, false) return QkViewHolder(view).apply { view.setOnClickListener { - val conversation = getItem(adapterPosition)!! + val conversation = getItem(adapterPosition) ?: return@setOnClickListener unblock.onNext(conversation.id) } } } override fun onBindViewHolder(holder: QkViewHolder, position: Int) { - val conversation = getItem(position)!! - val view = holder.itemView + val conversation = getItem(position) ?: return + val view = holder.containerView view.avatars.contacts = conversation.recipients view.title.text = conversation.getTitle() diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/compose/AttachmentAdapter.kt b/presentation/src/main/java/com/moez/QKSMS/feature/compose/AttachmentAdapter.kt index ba740ec0ebef9c54af8acd73893fcb80d905151c..a158a3951cc8e328fbaa28c7cb79cfa6d27bace1 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/compose/AttachmentAdapter.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/compose/AttachmentAdapter.kt @@ -69,7 +69,7 @@ class AttachmentAdapter @Inject constructor( override fun onBindViewHolder(holder: QkViewHolder, position: Int) { val attachment = getItem(position) - val view = holder.itemView + val view = holder.containerView when (attachment) { is Attachment.Image -> Glide.with(context) diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/compose/ChipsAdapter.kt b/presentation/src/main/java/com/moez/QKSMS/feature/compose/ChipsAdapter.kt index c543676b81dfc65fb87eb434f296858d2552571e..2d7d7d50a177d330e699689c710a9228ccbab1d3 100755 --- a/presentation/src/main/java/com/moez/QKSMS/feature/compose/ChipsAdapter.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/compose/ChipsAdapter.kt @@ -98,7 +98,7 @@ class ChipsAdapter @Inject constructor(private val context: Context) : QkAdapter when (getItemViewType(position)) { TYPE_ITEM -> { val contact = getItem(position) - val view = holder.itemView + val view = holder.containerView view.avatar.setContact(contact) diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeActivity.kt b/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeActivity.kt index aa55ae6c84c584e90dc6d3c30ee5bbd38672b0f5..20307ba6d31938762c337a084e786463feb4df0a 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeActivity.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeActivity.kt @@ -19,6 +19,7 @@ package com.moez.QKSMS.feature.compose import android.Manifest +import android.animation.LayoutTransition import android.app.Activity import android.app.DatePickerDialog import android.app.TimePickerDialog @@ -120,6 +121,10 @@ class ComposeActivity : QkThemedActivity(), ComposeView { showBackButton(true) viewModel.bindView(this) + contentView.layoutTransition = LayoutTransition().apply { + disableTransitionType(LayoutTransition.CHANGING) + } + chipsAdapter.view = chips contacts.itemAnimator = null diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/compose/ContactAdapter.kt b/presentation/src/main/java/com/moez/QKSMS/feature/compose/ContactAdapter.kt index 10ad2473d9facbe8395bdda95e196b03ada0a640..cdceeda050891f97b80593f632ba6ac16e8e1506 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/compose/ContactAdapter.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/compose/ContactAdapter.kt @@ -57,7 +57,7 @@ class ContactAdapter @Inject constructor() : QkAdapter() { override fun onBindViewHolder(holder: QkViewHolder, position: Int) { val contact = getItem(position) - val view = holder.itemView + val view = holder.containerView view.avatar.setContact(contact) view.name.text = contact.name diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/compose/MessagesAdapter.kt b/presentation/src/main/java/com/moez/QKSMS/feature/compose/MessagesAdapter.kt index ddbef618fc316e7a2bf9ca9179927097e3db531c..df60bbee512ceff78f4d030cd53949b84b2cc596 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/compose/MessagesAdapter.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/compose/MessagesAdapter.kt @@ -151,7 +151,7 @@ class MessagesAdapter @Inject constructor( return QkViewHolder(view).apply { view.setOnClickListener { - val message = getItem(adapterPosition)!! + val message = getItem(adapterPosition) ?: return@setOnClickListener when (toggleSelection(message.id, false)) { true -> view.isActivated = isSelected(message.id) false -> { @@ -162,7 +162,7 @@ class MessagesAdapter @Inject constructor( } } view.setOnLongClickListener { - val message = getItem(adapterPosition)!! + val message = getItem(adapterPosition) ?: return@setOnLongClickListener true toggleSelection(message.id) view.isActivated = isSelected(message.id) true @@ -171,10 +171,10 @@ class MessagesAdapter @Inject constructor( } override fun onBindViewHolder(viewHolder: QkViewHolder, position: Int) { - val message = getItem(position)!! + val message = getItem(position) ?: return val previous = if (position == 0) null else getItem(position - 1) val next = if (position == itemCount - 1) null else getItem(position + 1) - val view = viewHolder.itemView + val view = viewHolder.containerView // Update the selected state @@ -267,7 +267,7 @@ class MessagesAdapter @Inject constructor( } private fun bindStatus(viewHolder: QkViewHolder, message: Message, next: Message?) { - val view = viewHolder.itemView + val view = viewHolder.containerView val age = TimeUnit.MILLISECONDS.toMinutes(System.currentTimeMillis() - message.date) @@ -296,7 +296,7 @@ class MessagesAdapter @Inject constructor( } override fun getItemViewType(position: Int): Int { - val message = getItem(position)!! + val message = getItem(position) ?: return -1 return when (message.isMe()) { true -> VIEW_TYPE_MESSAGE_OUT false -> VIEW_TYPE_MESSAGE_IN diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/compose/PhoneNumberAdapter.kt b/presentation/src/main/java/com/moez/QKSMS/feature/compose/PhoneNumberAdapter.kt index 59d98d1ea1a42565bc92cc431f84dc886646800b..3e8689434fa3864e1eeb2b667d60a4297096977e 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/compose/PhoneNumberAdapter.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/compose/PhoneNumberAdapter.kt @@ -41,7 +41,7 @@ class PhoneNumberAdapter( override fun onBindViewHolder(holder: QkViewHolder, position: Int) { val number = getItem(position) - val view = holder.itemView + val view = holder.containerView // Setting this in onCreateViewHolder causes a crash sometimes. [contact] returns the // contact from a different row, I'm not sure why diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/compose/part/PartsAdapter.kt b/presentation/src/main/java/com/moez/QKSMS/feature/compose/part/PartsAdapter.kt index 26a4b958e49ddd212f7d1c2d7c18d83968829d36..a2e4a047611f805dc95b1ed10478d795cb24d08b 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/compose/part/PartsAdapter.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/compose/part/PartsAdapter.kt @@ -66,7 +66,7 @@ class PartsAdapter(context: Context, navigator: Navigator, theme: Colors.Theme) override fun onBindViewHolder(holder: QkViewHolder, position: Int) { val part = data[position] - val view = holder.itemView + val view = holder.containerView val canGroupWithPrevious = canGroup(message, previous) || position > 0 val canGroupWithNext = canGroup(message, next) || position < itemCount - 1 || bodyVisible diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/conversationinfo/ConversationMediaAdapter.kt b/presentation/src/main/java/com/moez/QKSMS/feature/conversationinfo/ConversationMediaAdapter.kt index 48a775d23b8911270182ec365acd7ed44c8382fe..e34063c0f3b1b2119db179b2874320b2ba5cc0ea 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/conversationinfo/ConversationMediaAdapter.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/conversationinfo/ConversationMediaAdapter.kt @@ -42,15 +42,15 @@ class ConversationMediaAdapter @Inject constructor( val view = inflater.inflate(R.layout.conversation_media_list_item, parent, false) return QkViewHolder(view).apply { view.thumbnail.setOnClickListener { - val part = getItem(adapterPosition)!! + val part = getItem(adapterPosition) ?: return@setOnClickListener navigator.showMedia(part.id) } } } override fun onBindViewHolder(holder: QkViewHolder, position: Int) { - val part = getItem(position)!! - val view = holder.itemView + val part = getItem(position) ?: return + val view = holder.containerView GlideApp.with(context) .load(part.getUri()) diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/conversationinfo/ConversationRecipientAdapter.kt b/presentation/src/main/java/com/moez/QKSMS/feature/conversationinfo/ConversationRecipientAdapter.kt index 0b55dbd6302b7d1e9f87a72cdfa5acdb83a7ada4..83c6701380844176d6e9ae671191aec843fe988c 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/conversationinfo/ConversationRecipientAdapter.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/conversationinfo/ConversationRecipientAdapter.kt @@ -44,7 +44,7 @@ class ConversationRecipientAdapter @Inject constructor( val view = layoutInflater.inflate(R.layout.conversation_recipient_list_item, parent, false) return QkViewHolder(view).apply { view.setOnClickListener { - val recipient = getItem(adapterPosition)!! + val recipient = getItem(adapterPosition) ?: return@setOnClickListener if (recipient.contact == null) { navigator.addContact(recipient.address) } else { @@ -55,8 +55,8 @@ class ConversationRecipientAdapter @Inject constructor( } override fun onBindViewHolder(holder: QkViewHolder, position: Int) { - val recipient = getItem(position)!! - val view = holder.itemView + val recipient = getItem(position) ?: return + val view = holder.containerView view.avatar.threadId = threadId view.avatar.setContact(recipient) diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/conversations/ConversationsAdapter.kt b/presentation/src/main/java/com/moez/QKSMS/feature/conversations/ConversationsAdapter.kt index 4b29f169e573bfab901fe41ccb4b699939ba9749..5413f23197b31c7530d0649160d1581c3130d932 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/conversations/ConversationsAdapter.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/conversations/ConversationsAdapter.kt @@ -62,14 +62,14 @@ class ConversationsAdapter @Inject constructor( return QkViewHolder(view).apply { view.setOnClickListener { - val conversation = getItem(adapterPosition)!! + val conversation = getItem(adapterPosition) ?: return@setOnClickListener when (toggleSelection(conversation.id, false)) { true -> view.isActivated = isSelected(conversation.id) false -> navigator.showConversation(conversation.id) } } view.setOnLongClickListener { - val conversation = getItem(adapterPosition)!! + val conversation = getItem(adapterPosition) ?: return@setOnLongClickListener true toggleSelection(conversation.id) view.isActivated = isSelected(conversation.id) true @@ -78,8 +78,8 @@ class ConversationsAdapter @Inject constructor( } override fun onBindViewHolder(viewHolder: QkViewHolder, position: Int) { - val conversation = getItem(position)!! - val view = viewHolder.itemView + val conversation = getItem(position) ?: return + val view = viewHolder.containerView view.isActivated = isSelected(conversation.id) @@ -98,6 +98,6 @@ class ConversationsAdapter @Inject constructor( } override fun getItemViewType(position: Int): Int { - return if (getItem(position)!!.read) 0 else 1 + return if (getItem(position)?.read == true) 0 else 1 } } \ No newline at end of file diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/gallery/GalleryActivity.kt b/presentation/src/main/java/com/moez/QKSMS/feature/gallery/GalleryActivity.kt index 4a0e02cc79699e587f959c757f6bf569805e641e..ce7b851a781c392fb92bd8a31e63e5f8db55efa4 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/gallery/GalleryActivity.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/gallery/GalleryActivity.kt @@ -24,8 +24,11 @@ import android.view.MenuItem import androidx.core.view.isVisible import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProviders +import androidx.recyclerview.widget.RecyclerView +import androidx.viewpager2.widget.ViewPager2 import com.moez.QKSMS.R import com.moez.QKSMS.common.base.QkActivity +import com.moez.QKSMS.common.util.DateFormatter import com.moez.QKSMS.common.util.extensions.addOnPageChangeListener import com.moez.QKSMS.common.util.extensions.setVisible import com.moez.QKSMS.model.MmsPart @@ -38,9 +41,12 @@ import javax.inject.Inject class GalleryActivity : QkActivity(), GalleryView { + @Inject lateinit var dateFormatter: DateFormatter @Inject lateinit var viewModelFactory: ViewModelProvider.Factory @Inject lateinit var pagerAdapter: GalleryPagerAdapter + val partId by lazy { intent.getLongExtra("partId", 0L) } + private val optionsItemSubject: Subject = PublishSubject.create() private val pageChangedSubject: Subject = PublishSubject.create() private val viewModel by lazy { ViewModelProviders.of(this, viewModelFactory)[GalleryViewModel::class.java] } @@ -52,20 +58,29 @@ class GalleryActivity : QkActivity(), GalleryView { showBackButton(true) viewModel.bindView(this) - pagerAdapter.setInitialPositionHandler = { position -> - pager.setCurrentItem(position, false) - - // The ViewPager's page change listener isn't called if the initial position is 0 - if (position == 0) { - pageChanged(0) - } - } pager.adapter = pagerAdapter - pager.addOnPageChangeListener(this::pageChanged) + pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + this@GalleryActivity.onPageSelected(position) + } + }) + + pagerAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { + override fun onChanged() { + pagerAdapter.data?.takeIf { pagerAdapter.itemCount > 0 } + ?.indexOfFirst { part -> part.id == partId } + ?.let { index -> + onPageSelected(index) + pager.setCurrentItem(index, false) + pagerAdapter.unregisterAdapterDataObserver(this) + } + } + }) } - private fun pageChanged(position: Int) { - toolbarSubtitle.text = pagerAdapter.getPageTitle(position) + fun onPageSelected(position: Int) { + toolbarSubtitle.text = pagerAdapter.getItem(position)?.messages?.firstOrNull()?.date + ?.let(dateFormatter::getDetailedTimestamp) toolbarSubtitle.isVisible = toolbarTitle.text.isNotBlank() pagerAdapter.getItem(position)?.run(pageChangedSubject::onNext) @@ -75,7 +90,7 @@ class GalleryActivity : QkActivity(), GalleryView { toolbar.setVisible(state.navigationVisible) title = state.title - pagerAdapter.parts = state.parts + pagerAdapter.updateData(state.parts) } override fun optionsItemSelected(): Observable = optionsItemSubject diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/gallery/GalleryActivityModule.kt b/presentation/src/main/java/com/moez/QKSMS/feature/gallery/GalleryActivityModule.kt index 742a6f3b6a5043c0de3ae0fd6984de64caf40ec4..62cdcabe9d98739830ce9b76d400c29a2125d6a0 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/gallery/GalleryActivityModule.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/gallery/GalleryActivityModule.kt @@ -34,7 +34,7 @@ class GalleryActivityModule { @Provides @Named("partId") - fun providePartId(activity: GalleryActivity): Long = activity.intent.getLongExtra("partId", 0L) + fun providePartId(activity: GalleryActivity): Long = activity.partId @Provides @IntoMap diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/gallery/GalleryPagerAdapter.kt b/presentation/src/main/java/com/moez/QKSMS/feature/gallery/GalleryPagerAdapter.kt index 04e6db9f517790abc39ffb260fb8e0eb87e9c831..2569a6c075280ae567ca5078b9981295c732e7db 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/gallery/GalleryPagerAdapter.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/gallery/GalleryPagerAdapter.kt @@ -22,7 +22,6 @@ import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.viewpager.widget.PagerAdapter import com.google.android.exoplayer2.ExoPlayer import com.google.android.exoplayer2.ExoPlayerFactory import com.google.android.exoplayer2.source.ExtractorMediaSource @@ -32,57 +31,36 @@ import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory import com.google.android.exoplayer2.util.Util import com.google.android.mms.ContentType import com.moez.QKSMS.R -import com.moez.QKSMS.common.util.DateFormatter +import com.moez.QKSMS.common.base.QkRealmAdapter +import com.moez.QKSMS.common.base.QkViewHolder import com.moez.QKSMS.extensions.isImage import com.moez.QKSMS.extensions.isVideo import com.moez.QKSMS.model.MmsPart import com.moez.QKSMS.util.GlideApp -import io.reactivex.disposables.CompositeDisposable import io.reactivex.subjects.PublishSubject import io.reactivex.subjects.Subject -import io.realm.RealmResults import kotlinx.android.synthetic.main.gallery_image_page.view.* import kotlinx.android.synthetic.main.gallery_video_page.view.* import java.util.* import javax.inject.Inject -import javax.inject.Named - -class GalleryPagerAdapter @Inject constructor( - context: Context, - @Named("partId") private val partId: Long, - private val dateFormatter: DateFormatter -) : PagerAdapter() { - - var parts: RealmResults? = null - set(value) { - if (field === value) return - field = value - - field?.asFlowable() - ?.filter { it.isLoaded } - ?.subscribe { notifyDataSetChanged() } - ?.run(disposables::add) - } - val clicks: Subject = PublishSubject.create() +class GalleryPagerAdapter @Inject constructor(private val context: Context) : QkRealmAdapter() { - /** - * The Adapter isn't able to set the position itself, so it's owner must apply the initial - * position once the data is loaded - */ - var setInitialPositionHandler: ((Int) -> Unit)? = null + companion object { + private const val VIEW_TYPE_INVALID = 0 + private const val VIEW_TYPE_IMAGE = 1 + private const val VIEW_TYPE_VIDEO = 2 + } - private var loaded = false + val clicks: Subject = PublishSubject.create() private val contentResolver = context.contentResolver - private val disposables = CompositeDisposable() private val exoPlayers = Collections.newSetFromMap(WeakHashMap()) - override fun instantiateItem(container: ViewGroup, position: Int): Any { - val part = getItem(position) - val inflater = LayoutInflater.from(container.context) - return when { - part?.isImage() == true -> inflater.inflate(R.layout.gallery_image_page, container, false).apply { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QkViewHolder { + val inflater = LayoutInflater.from(parent.context) + return QkViewHolder(when (viewType) { + VIEW_TYPE_IMAGE -> inflater.inflate(R.layout.gallery_image_page, parent, false).apply { // When calling the public setter, it doesn't allow the midscale to be the same as the // maxscale or the minscale. We don't want 3 levels and we don't want to modify the library @@ -101,72 +79,59 @@ class GalleryPagerAdapter @Inject constructor( setFloat(image.attacher, 3f) } } + } + VIEW_TYPE_VIDEO -> inflater.inflate(R.layout.gallery_video_page, parent, false) + + else -> inflater.inflate(R.layout.gallery_invalid_page, parent, false) + + }.apply { setOnClickListener(clicks::onNext) }) + } + + override fun onBindViewHolder(holder: QkViewHolder, position: Int) { + val part = getItem(position) ?: return + val view = holder.containerView + when (getItemViewType(position)) { + VIEW_TYPE_IMAGE -> { // We need to explicitly request a gif from glide for animations to work when (part.getUri().let(contentResolver::getType)) { ContentType.IMAGE_GIF -> GlideApp.with(context) .asGif() .load(part.getUri()) - .into(image) + .into(view.image) else -> GlideApp.with(context) .asBitmap() .load(part.getUri()) - .into(image) + .into(view.image) } - - container.addView(this) } - part?.isVideo() == true -> inflater.inflate(R.layout.gallery_video_page, container, false).apply { + VIEW_TYPE_VIDEO -> { val videoTrackSelectionFactory = AdaptiveTrackSelection.Factory(null) val trackSelector = DefaultTrackSelector(videoTrackSelectionFactory) val exoPlayer = ExoPlayerFactory.newSimpleInstance(context, trackSelector) - video.player = exoPlayer + view.video.player = exoPlayer exoPlayers.add(exoPlayer) val dataSourceFactory = DefaultDataSourceFactory(context, Util.getUserAgent(context, "QKSMS")) val videoSource = ExtractorMediaSource.Factory(dataSourceFactory).createMediaSource(part.getUri()) exoPlayer?.prepare(videoSource) - - container.addView(this) } - - else -> inflater.inflate(R.layout.gallery_invalid_page, container, false).apply { - container.addView(this) - } - }.apply { setOnClickListener(clicks::onNext) } - } - - override fun getPageTitle(position: Int): CharSequence? { - return getItem(position)?.messages?.firstOrNull()?.date?.let(dateFormatter::getDetailedTimestamp) - } - - override fun isViewFromObject(view: View, `object`: Any): Boolean { - return view == `object` - } - - override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) { - container.removeView(`object` as? View) + } } - override fun getCount() = parts?.size ?: 0 - - fun getItem(position: Int): MmsPart? = parts?.get(position) - - override fun notifyDataSetChanged() { - super.notifyDataSetChanged() - - if (!loaded && parts?.isLoaded == true) { - loaded = true - parts?.indexOfFirst { it.id == partId } - ?.let { setInitialPositionHandler?.invoke(it) } + override fun getItemViewType(position: Int): Int { + val part = getItem(position) + return when { + part?.isImage() == true -> VIEW_TYPE_IMAGE + part?.isVideo() == true -> VIEW_TYPE_VIDEO + else -> VIEW_TYPE_INVALID } } fun destroy() { - disposables.dispose() exoPlayers.forEach { exoPlayer -> exoPlayer?.release() } } -} \ No newline at end of file +} diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/main/MainActivity.kt b/presentation/src/main/java/com/moez/QKSMS/feature/main/MainActivity.kt index bcaefcdb522dde124ebf1591969bf75ea15ad333..85ac2c5f26f97c7673d1de941d9f41dee809ec9a 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/main/MainActivity.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/main/MainActivity.kt @@ -69,13 +69,20 @@ import javax.inject.Inject class MainActivity : QkThemedActivity(), MainView { - @Inject lateinit var disposables: CompositeDisposable - @Inject lateinit var navigator: Navigator - @Inject lateinit var conversationsAdapter: ConversationsAdapter - @Inject lateinit var drawerBadgesExperiment: DrawerBadgesExperiment - @Inject lateinit var searchAdapter: SearchAdapter - @Inject lateinit var itemTouchCallback: ConversationItemTouchCallback - @Inject lateinit var viewModelFactory: ViewModelProvider.Factory + @Inject + lateinit var disposables: CompositeDisposable + @Inject + lateinit var navigator: Navigator + @Inject + lateinit var conversationsAdapter: ConversationsAdapter + @Inject + lateinit var drawerBadgesExperiment: DrawerBadgesExperiment + @Inject + lateinit var searchAdapter: SearchAdapter + @Inject + lateinit var itemTouchCallback: ConversationItemTouchCallback + @Inject + lateinit var viewModelFactory: ViewModelProvider.Factory override val activityResumedIntent: Subject = PublishSubject.create() override val queryChangedIntent by lazy { toolbarSearch.textChanges() } @@ -177,6 +184,8 @@ class MainActivity : QkThemedActivity(), MainView { itemTouchCallback.adapter = conversationsAdapter conversationsAdapter.autoScrollToStart(recyclerView) + + } override fun render(state: MainState) { @@ -295,6 +304,10 @@ class MainActivity : QkThemedActivity(), MainView { snackbarButton?.setText(R.string.main_permission_allow) } } + +// hide the QKSMS+ + plusBanner.visibility = View.GONE + } override fun onResume() { diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/main/SearchAdapter.kt b/presentation/src/main/java/com/moez/QKSMS/feature/main/SearchAdapter.kt index 2d3fe75463c0c7c8b46523fc51a731744a627d4f..7f32e69108163397a20a3d0be2ff68c48c49e5bd 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/main/SearchAdapter.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/main/SearchAdapter.kt @@ -58,7 +58,7 @@ class SearchAdapter @Inject constructor( override fun onBindViewHolder(viewHolder: QkViewHolder, position: Int) { val previous = data.getOrNull(position - 1) val result = getItem(position) - val view = viewHolder.itemView + val view = viewHolder.containerView view.resultsHeader.setVisible(result.messages > 0 && previous?.messages == 0) diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/qkreply/QkReplyActivity.kt b/presentation/src/main/java/com/moez/QKSMS/feature/qkreply/QkReplyActivity.kt index ae0d98bb8bbdc4c913c318cdf74c8da650a498b2..f141efcef3384a7f9824ca554c6adf848bcb997c 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/qkreply/QkReplyActivity.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/qkreply/QkReplyActivity.kt @@ -79,8 +79,8 @@ class QkReplyActivity : QkThemedActivity(), QkReplyView { toolbar.setBackgroundTint(resolveThemeColor(R.attr.colorPrimary)) background.setBackgroundTint(resolveThemeColor(R.attr.composeBackground)) messageBackground.setBackgroundTint(resolveThemeColor(R.attr.bubbleColor)) - composeBackground.setBackgroundTint(resolveThemeColor(R.attr.composeBackground)) - composeBackground2.setBackgroundTint(resolveThemeColor(R.attr.composeBackground)) + composeBackgroundGradient.setBackgroundTint(resolveThemeColor(R.attr.composeBackground)) + composeBackgroundSolid.setBackgroundTint(resolveThemeColor(R.attr.composeBackground)) } } diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/scheduled/ScheduledMessageAdapter.kt b/presentation/src/main/java/com/moez/QKSMS/feature/scheduled/ScheduledMessageAdapter.kt index abd9d415a4ec51ad501b7f23dd11254f0525dd78..0ac1abc42bef3924f88d392347a844034408c703 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/scheduled/ScheduledMessageAdapter.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/scheduled/ScheduledMessageAdapter.kt @@ -56,15 +56,15 @@ class ScheduledMessageAdapter @Inject constructor( return QkViewHolder(view).apply { view.setOnClickListener { - val message = getItem(adapterPosition)!! + val message = getItem(adapterPosition) ?: return@setOnClickListener clicks.onNext(message.id) } } } override fun onBindViewHolder(holder: QkViewHolder, position: Int) { - val message = getItem(position)!! - val view = holder.itemView + val message = getItem(position) ?: return + val view = holder.containerView // GroupAvatarView only accepts recipients, so map the phone numbers to recipients view.avatars.contacts = message.recipients.map { address -> Recipient(address = address) } diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/scheduled/ScheduledMessageAttachmentAdapter.kt b/presentation/src/main/java/com/moez/QKSMS/feature/scheduled/ScheduledMessageAttachmentAdapter.kt index 520026b2f1a471f47b361105fd3a780f9e10b298..8b4e8838dfa46e6197ab514d8505030dd2050024 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/scheduled/ScheduledMessageAttachmentAdapter.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/scheduled/ScheduledMessageAttachmentAdapter.kt @@ -39,7 +39,7 @@ class ScheduledMessageAttachmentAdapter @Inject constructor() : QkAdapter() override fun onBindViewHolder(holder: QkViewHolder, position: Int) { val attachment = getItem(position) - val view = holder.itemView + val view = holder.containerView GlideApp.with(view).load(attachment).into(view.thumbnail) } diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/themepicker/ThemeAdapter.kt b/presentation/src/main/java/com/moez/QKSMS/feature/themepicker/ThemeAdapter.kt index 0e01a36390fb69cb00d525d5017be27a2cada0e3..7489a34ad7b53d436da9b2a81f9caa09331fcf44 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/themepicker/ThemeAdapter.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/themepicker/ThemeAdapter.kt @@ -70,7 +70,7 @@ class ThemeAdapter @Inject constructor( override fun onBindViewHolder(holder: QkViewHolder, position: Int) { val palette = getItem(position) - val view = holder.itemView + val view = holder.containerView val screenWidth = Resources.getSystem().displayMetrics.widthPixels val minPadding = (16 * 6).dpToPx(context) diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/themepicker/ThemePagerAdapter.kt b/presentation/src/main/java/com/moez/QKSMS/feature/themepicker/ThemePagerAdapter.kt index c7185c2114356260c22eb97099a6efe3820043c5..f3925b2558b4258730be35650b8d8b02fcf629c0 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/themepicker/ThemePagerAdapter.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/themepicker/ThemePagerAdapter.kt @@ -49,7 +49,8 @@ class ThemePagerAdapter @Inject constructor(private val context: Context) : Page } override fun getCount(): Int { - return 2 + // return 2 + return 1 } } \ No newline at end of file diff --git a/presentation/src/main/res/layout/compose_activity.xml b/presentation/src/main/res/layout/compose_activity.xml index a59ee65b0e8b3b4fda27ce9eb74eed576b87eea9..cdbb3f75976d7b5d52a60282f3cd13d337e52beb 100644 --- a/presentation/src/main/res/layout/compose_activity.xml +++ b/presentation/src/main/res/layout/compose_activity.xml @@ -23,7 +23,6 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:animateLayoutChanges="true" android:background="?attr/composeBackground" android:orientation="vertical" tools:context="com.moez.QKSMS.feature.compose.ComposeActivity"> diff --git a/presentation/src/main/res/layout/gallery_activity.xml b/presentation/src/main/res/layout/gallery_activity.xml index 8f55f52b7e2d0fc7c80f11eef79de7c56ebc7fd9..ca8c3b899918f9488dfded3e1f657eab06311d14 100644 --- a/presentation/src/main/res/layout/gallery_activity.xml +++ b/presentation/src/main/res/layout/gallery_activity.xml @@ -25,7 +25,7 @@ android:layout_height="match_parent" tools:context="com.moez.QKSMS.feature.gallery.GalleryActivity"> - diff --git a/presentation/src/main/res/layout/qkreply_activity.xml b/presentation/src/main/res/layout/qkreply_activity.xml index c04c2cdb1e457c1181271f74c1081aa06f6a338a..36a5a88d370da2fc338c41f00d8f6a249f9b28a2 100644 --- a/presentation/src/main/res/layout/qkreply_activity.xml +++ b/presentation/src/main/res/layout/qkreply_activity.xml @@ -17,10 +17,10 @@ ~ You should have received a copy of the GNU General Public License ~ along with QKSMS. If not, see . --> - + app:layout_constraintBottom_toTopOf="@id/composeBackgroundSolid" /> + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toTopOf="@id/message" /> + app:layout_constraintStart_toStartOf="parent" /> \ No newline at end of file diff --git a/presentation/src/main/res/values-ar/strings.xml b/presentation/src/main/res/values-ar/strings.xml index 47bad941797704f24a083a9c2db2744b943c4a97..491b75e40abf652c316a274ed477a0908ca48683 100644 --- a/presentation/src/main/res/values-ar/strings.xml +++ b/presentation/src/main/res/values-ar/strings.xml @@ -120,8 +120,8 @@ إرفاق صورة التقاط صورة جدوِل الرسالة - Attach a contact - Error reading contact + إرفاق جهة اتصال + خطأ في قراءة جهة الاتصال %s محددة، غيّر شريحة SIM إرسال الرسالة الإرسال جارٍ… @@ -147,7 +147,7 @@ أبداً استعادة اختر نسخة احتياطية - Please unlock QKSMS+ to use backup and restore + يرجى إلغاء تأمين QKSMS+ للتمكن من استخدام النسخ الاحتياطي والاستعادة النسخ الاحتياطي قيد التقدم… الاستعادة قيد التقدُّم… الاستعادة من النسخ الاحتياطي diff --git a/presentation/src/main/res/values-cs/strings.xml b/presentation/src/main/res/values-cs/strings.xml index 1aaae2fe398ecb63282b140b85de3522a73e1fca..7126480ec512c80d882d1f79b93b79ce27866dd1 100644 --- a/presentation/src/main/res/values-cs/strings.xml +++ b/presentation/src/main/res/values-cs/strings.xml @@ -118,8 +118,8 @@ Připojit fotografii Fotografovat Naplánovat zprávu - Attach a contact - Error reading contact + Přiložit kontakt + Chyba čtení kontaktu %s vybráno, změňte SIM kartu Odeslat zprávu Odesílání… diff --git a/presentation/src/main/res/values-da/strings.xml b/presentation/src/main/res/values-da/strings.xml index 926517d703ac911973105cca05428fdc03cc2605..9ac4bd6f0bc5390b2703546e3ec379538939a243 100644 --- a/presentation/src/main/res/values-da/strings.xml +++ b/presentation/src/main/res/values-da/strings.xml @@ -116,8 +116,8 @@ Vedhæft et foto Tag et foto Planlæg besked - Attach a contact - Error reading contact + Vedhæft en kontakt + Fejl ved læsning af kontakt %s valgt, skift SIM-kortet Send besked Afsender… diff --git a/presentation/src/main/res/values-de/strings.xml b/presentation/src/main/res/values-de/strings.xml index 1b6c5cfecb9ce0c8ce643e8d01bd471ff60bec1c..a7fce6a13995097f5a59d02c13b4c9231af619e2 100644 --- a/presentation/src/main/res/values-de/strings.xml +++ b/presentation/src/main/res/values-de/strings.xml @@ -116,8 +116,8 @@ Foto anhängen Foto aufnehmen Plane Nachricht - Attach a contact - Error reading contact + Kontakt anhängen + Fehler beim Lesen des Kontaktes %s ausgewählt, SIM-Karte ändern Nachricht senden Wird gesendet… diff --git a/presentation/src/main/res/values-es/strings.xml b/presentation/src/main/res/values-es/strings.xml index 6702249da4bcb55fff4cd1755f01e26090ce938d..50d65361f5daa5ae18f130b679064d678daef555 100644 --- a/presentation/src/main/res/values-es/strings.xml +++ b/presentation/src/main/res/values-es/strings.xml @@ -19,9 +19,9 @@ ~ along with QKSMS. If not, see . --> - New conversation - Compose - Shortcut disabled + Nueva conversación + Redactar + Acceso directo deshabilitado Archivados Configuración Notificaciones @@ -39,8 +39,8 @@ Archivo Desarchivar Eliminar - Pin to top - Unpin + Anclar hacia arriba + Desanclar Marcar como leído Marcar como no leído Bloquear @@ -67,17 +67,17 @@ Ajustes QKSMS+ Ayuda & comentarios - Invite friends + Invitar amigos QKSMS+ - Unlock amazing new features, and support development + Desbloquea nuevas e impresionantes características y apoya el desarrollo ¿Te diviertes con QKSMS? Comparte un poco de amor y puntúanos en Google Play! DE ACUERDO DESESTIMAR Eliminar - Are you sure you would like to delete this conversation? - Are you sure you would like to delete %d conversations? + ¿Está seguro de que desea eliminar la conversación? + ¿Está seguro de que desea eliminar %d conversaciones? Copiar texto @@ -115,9 +115,9 @@ Agregar un archivo adjunto Adjuntar una foto Tomar una foto - Schedule message - Attach a contact - Error reading contact + Programar mensaje + Adjuntar un contacto + Error al leer contacto %s seleccionado, cambia la tarjeta SIM Enviar mensaje Enviando… @@ -133,44 +133,44 @@ Bloquear Desbloquear Borrar conversación - Couldn\'t load media + No se pudo cargar el medio Guardado en Galería - Backup and restore - Backing up messages - Restoring from backup - Last backup - Loading… - Never - Restore - Select a backup - Please unlock QKSMS+ to use backup and restore - Backup in progress… - Restore in progress… - Restore from backup - Are you sure you would like to restore your messages from this backup? - Stop restore - Messages that have already been restored will remain on your device - Backups - No backups found + Copia de seguridad y restauración + Respaldando mensajes + Restaurar desde copia de seguridad + Última copia de seguridad + Cargando… + Nunca + Restaurar + Seleccione una copia de seguridad + Por favor desbloquee QKSMS+ para usar copia de seguridad y restauración + Copia de seguridad en progreso… + Restauración en progreso… + Restaurar desde copia de seguridad + ¿Está seguro de que desea restaurar sus mensajes desde esta copia de seguridad? + Detener restauración + Los mensajes que ya han sido restaurados permanecerán en su dispositivo + Copias de seguridad + No se encontraron copias de seguridad - %d message - %d messages + %dNuevo mensaje + %dMensajes nuevos - Currently, only SMS is supported by Backup and Restore. MMS support and scheduled backups will be coming soon! - Backup now - Parsing backup… - %d/%d messages - Saving backup… - Syncing messages… - Finished! - Backup and restore + Actualmente, sólo los SMS son soportados por la copia de seguridad y restauración. ¡El soporte para MMS y las copias de seguridad programadas se realizarán pronto! + Copiar ahora + Analizando copia de seguridad… + %d/%d mensajes + Guardando copia de seguridad… + Sincronizando mensajes… + ¡Terminado! + Copiar y restaurar Programado - Automatically send a message, at the exact moment you\'d like - Hey! When was your birthday again? - It\'s on December 23rd - Happy birthday! Look at what a great friend I am, remembering your birthday - Sending on December 23rd  - Schedule a message + Enviar automáticamente un mensaje, en el momento exacto que te gustaría + ¡Hola! ¿Cuándo será tu cumpleaños de nuevo? + Es el 23 de Diciembre + ¡Feliz cumpleaños! Mira qué gran amigo soy, recordando tu cumpleaños + Enviando el 23 de Diciembre + Programar un mensaje Mensaje programado Enviar ahora @@ -188,11 +188,11 @@ Usar fuente del sistema Emojis automáticos Notificaciones - Tap to customize - Actions - Button 1 - Button 2 - Button 3 + Toca para personalizar + Acciones + Botón 1 + Botón 2 + Botón 3 Previsualización de notificaciones Vibración Sonido @@ -206,31 +206,31 @@ ¿Debo responder? Filtra automáticamente los mensajes de números no solicitados mediante la aplicación \"¿Debo responder?\" Retraso en el envío - Swipe actions - Configure swipe actions for conversations - Right swipe - Left swipe - CHANGE + Acciones al deslizar + Configurar acciones de deslizamiento para conversaciones + Deslizar a la derecha + Deslizar a la izquierda + CAMBIAR - None - Archive - Delete - Call - Mark read + Ninguno + Archivar + Eliminar + Llamar + Marcar como leído Confirmaciones de envió Confirmar cuando los mensajes se envien con éxito Detalles en tiras Quitar acentos de caracteres en los mensajes SMS salientes - Mobile numbers only - When composing a message, only show mobile numbers + Sólo números móviles + Al componer un mensaje, sólo mostrar números móviles Autocomprimir archivos adjuntos MMS Sincronizar mensajes Vuelve a sincronizar tus mensajes con la base de datos nativa de Android SMS Acerca de QKSMS Versión %s - Debug logging enabled - Debug logging disabled + Registro de depuración activado + Registro de depuración desactivado Introduzca la duración (segundos) Bloqueado Sus conversaciones bloqueadas aparecerán aquí @@ -277,7 +277,7 @@ Ver más conversaciones Marcar como leído Llamar - Delete + Eliminar Mostrar más Mostrar menos Abrir conversación @@ -286,16 +286,16 @@ HEXAGONAL Aplicar - None - Mark read - Reply - Call - Delete + Ninguno + Marcar como leído + Responder + Llamar + Eliminar Cancelar Eliminar Guardar - Stop + Detener Más Establecer Desbloquear diff --git a/presentation/src/main/res/values-fr/strings.xml b/presentation/src/main/res/values-fr/strings.xml index 766e9814e8fc8c2aae15ae46d5669c44b7885c1f..83efb7e95f746cca6bbea47a92a286ab85132980 100644 --- a/presentation/src/main/res/values-fr/strings.xml +++ b/presentation/src/main/res/values-fr/strings.xml @@ -116,8 +116,8 @@ Joindre une photo Prendre une photo Programmer un message - Attach a contact - Error reading contact + Joindre un contact + Erreur de lecture du contact %s sélectionné, changer la carte SIM Envoyer un message Envoi… diff --git a/presentation/src/main/res/values-hu/strings.xml b/presentation/src/main/res/values-hu/strings.xml index f958eee127ba0077c0f425eb373a0f551df2ee6b..74c5c177317bd0b0d123d046053a0da538c8d3dd 100644 --- a/presentation/src/main/res/values-hu/strings.xml +++ b/presentation/src/main/res/values-hu/strings.xml @@ -19,8 +19,8 @@ ~ along with QKSMS. If not, see . --> - New conversation - Compose + Új beszélgetés + Írás Shortcut disabled Archivált Beállítások @@ -39,10 +39,10 @@ Archiválás Törlés az archívumból Törlés - Pin to top - Unpin - Mark read - Mark unread + Rögzítés a tetejére + Rögzítés törlése + Megjelölés olvasottként + Megjelölés olvasatlanként Blokkolás Üzenetek szinkronizálása… Te: %s @@ -62,22 +62,22 @@ Bejövő üzenetek Archivált Ütemezett - Blocking + Blokkolás Továbbiák Beállítások QKSMS+ Súgó és visszajelzés - Invite friends + Ismerősök meghívása QKSMS+ - Unlock amazing new features, and support development + Juss érdekes, új lehetőségekhez és támogasd fejlesztést Tetszik a QKSMS? Oszd meg a szeretetet és értékelj minket a Google Playen! OKÉ! MELLŐZÉS Törlés - Are you sure you would like to delete this conversation? - Are you sure you would like to delete %d conversations? + Biztos, hogy törölni szeretnéd ezt a beszélgetést? + Biztos, hogy törölni szeretnél %d beszélgetést? Szöveg másolása @@ -85,122 +85,122 @@ Törlés %d kijelölve - %1$d of %2$d results - Send as group message - Recipients and replies will be visible to everyone - This is the start of your conversation. Say something nice! + %1$d. a %2$d találatból + Küldés csoportos üzenetként + A címzettek és a válaszok mindenkinek láthatóak lesznek + Ez a társalgás kezdete. Írj valami kedveset! Contact card Scheduled for - Selected time must be in the future! - You must unlock QKSMS+ to use scheduled messaging - Added to scheduled messages - Write a message… - Copy text - Forward - Delete - Previous - Next - Clear - Message details - Type: %s - From: %s - To: %s - Subject: %s - Priority: %s - Size: %s - Sent: %s - Received: %s - Delivered: %s - Error code: %d - Add an attachment - Attach a photo - Take a photo - Schedule message - Attach a contact - Error reading contact - %s selected, change SIM card - Send message - Sending… + A kiválasztott időpontnak a jövőben kell lennie! + Aktiválnod kell a QKSMS+-t hogy időzített üzeneteteket tudj használni + Hozzáadva az időzített üzenetekhez + Írj egy üzenetet… + Szöveg másolás + Továbbítás + Törlés + Előző + Következő + Kiürítés + Üzenet részletei + Típus: %s + Feladó: %s + Cím: %s + Tárgy: %s + Prioritás: %s + Méret: %s + Elküldve: %s + Megkapva: %s + Kézbesítve: %s + Hibakód: %d + Csatolás hozzáadása + Fénykép hozzáadása + Fénykép készítése + Időzített küldés + Személy hozzáadása + Hiba a személy olvasása közben + %s kiválasztva, cserélj SIM kártyát + Üzenet küldése + Küldés… Kézbesítve %s - Failed to send. Tap to try again + Sikertelen küldés. Érintsd meg az újraküldéshez Részletek - Conversation title - Enter title - Notifications - Theme - Archive - Unarchive - Block - Unblock - Delete conversation - Couldn\'t load media - Saved to gallery - Backup and restore - Backing up messages - Restoring from backup - Last backup - Loading… - Never - Restore - Select a backup + A beszélgetés témája + Add meg a témát + Értesítések + Téma + Archivál + Archiválás visszavonása + Blokkolás + Blokkolás visszavonása + Beszélgetés törlése + A média nem érhető el + Mentés galériába + Mentés és visszaállítás + Üzenetek mentése + Üzenetek visszaállítása mentésből + Utolsó mentés + Betöltés… + Soha + Visszaállítás + Válassz egy mentést Please unlock QKSMS+ to use backup and restore - Backup in progress… - Restore in progress… - Restore from backup - Are you sure you would like to restore your messages from this backup? - Stop restore - Messages that have already been restored will remain on your device - Backups - No backups found + Mentés folyamatban… + Visszaállítás folyamatban… + Visszaállítás mentésből + Biztos vagy benne hogy vissza akarod állítani a mentésből a üzeneteket? + Visszaállítás megállítása + A már visszaállított üzenetek a készülékeden maradnak + Mentések + Nincsenek mentések - %d message - %d messages + %d üzenet + %d üzenet - Currently, only SMS is supported by Backup and Restore. MMS support and scheduled backups will be coming soon! - Backup now - Parsing backup… - %d/%d messages - Saving backup… - Syncing messages… - Finished! - Backup and restore - Scheduled + Jelenleg a mentés csak az SMS üzeneteket menti. Az MMS támogatás és az időzített mentések hamarosan jönnek! + Mentés most + Mentés elemzése… + %d/%d üzenet + Mentés… + Üzenetek szinkronizálása… + Kész! + Mentés és visszaállítás + Ütemezés Automatically send a message, at the exact moment you\'d like Hey! When was your birthday again? It\'s on December 23rd Happy birthday! Look at what a great friend I am, remembering your birthday Sending on December 23rd  - Schedule a message - Scheduled message + Küldés időzítése + Időzített üzenet - Send now - Delete + Küldés most + Törlés - Appearance - General - QK Reply - Theme - Night mode - Pure black night mode - Start time - End time - Font size - Use system font - Automatic emoji - Notifications + Megjelenés + Általános + QK válasz + Téma + Éjszakai mód + Éjfekete mód + Kezdés ideje + Befejezés ideje + Betűméret + Rendszer betűtípus visszaállítása + Automatikus emoji + Értesítések Tap to customize - Actions - Button 1 - Button 2 - Button 3 - Notification previews - Vibration - Sound - None - QK Reply - Popup for new messages - Tap to dismiss - Tap outside of the popup to close it + Műveletek + Gomb 1 + Gomb 2 + Gomb 3 + Értesítés előnézet + Rezgés + Hang + Nincs + QK válasz + Felugró új üzenetek + Érintsd meg az eltüntetéshez + Érintsd meg a felugró ablakon kívül a lezáráshoz Blokkolás Should I Answer? @@ -210,20 +210,20 @@ Configure swipe actions for conversations Right swipe Left swipe - CHANGE + MEGVÁLTOZTATÁS - None - Archive - Delete - Call - Mark read + Nincs + Archiválás + Törlés + Hívás + Megjelölés olvasottként Elküldés megerősítése Megerősíti, hogy az üzenetek sikeresen el lettek-e küldve Ékezetek törlése A kimenő SMS-üzenetek ékezetes karaktereinek átalakítása - Mobile numbers only - When composing a message, only show mobile numbers + Csak mobilszámok + Üzenet írásánál csak a mobilszámokat mutassa MMS-mellékletek automatikus tömörítése Üzenetek szinkronizálása Üzenetek szinkronizálása a natív Android SMS-adatbázisban @@ -245,19 +245,19 @@ Licensz Copyright Support development, unlock everything - You can save a starving developer for just %s + Csak %s hogy megmenthess egy éhező fejlesztőt Lifetime upgrade for %1$s %2$s Unlock + donate for %1$s %2$s - Thank you for supporting QKSMS! - You now have access to all QKSMS+ features - QKSMS+ is free for F-Droid users! If you\'d like to support development, feel free to make a donation. - Donate via PayPal - Coming soon - Premium themes + Köszönjük, hogy támogatot a QKSMS-t! + MOst hozzáférsz minden QKSMS+ funkcióhoz + A QKSMS+ minden F-Droid felhasználónak ingyenes. Ha támogatni szeretnéd a fejlesztést az alábbi módon megteheted. + Támogatás PayPalon keresztül + Hamarosan + Prémium témák Unlock beautiful theme colors not available on the Material Design palette Custom auto-emoji Create custom auto-emoji shortcuts - Message backup + Üzenet biztonsági mentés Automatically back up your messages. Never again worry about losing your history if you change phones or your phone gets lost Scheduled messages Schedule messages to automatically be sent at a specific time and date @@ -286,59 +286,59 @@ HEX Apply - None - Mark read - Reply - Call - Delete + Nincs + Megjelölés olvasottként + Válasz + Hívás + Törlés Cancel Delete Save Stop More - Set - Unblock - Undo - Copied - Archived conversation + Beállítás + Blokk feloldás + Visszavonás + Átmásolva + Archivált beszélgetés You must unlock QKSMS+ to use this - New message - %s new messages + Új üzenet + %s új üzenet - Message not sent - The message to %s failed to send + Az üzenet küldése sikertelen + Nem sikerült az üzenetetet %s címzettnek elküldeni - Disabled - Always on - Automatic + Letiltva + Mindig bekapcsolva + Automatikus - Show name and message - Show name - Hide contents + Név és üzenet látható + Név látható + A tartalom rejtett - Small - Normal - Large - Larger + Kicsi + Normál + Nagy + Nagyobb - No delay - Short - Medium - Long + Nincs késleltetés + Rövid + Közepes + Hosszú 100KB 200KB - 300KB (Recommended) + 300KB (Javsolt) 600KB 1000KB 2000KB - No compression + Nincs tömörítés Oké diff --git a/presentation/src/main/res/values-in/strings.xml b/presentation/src/main/res/values-in/strings.xml index 1e03f9623df514d6156a058c8e23669e6a2ecaab..91516be097e19924c44b8bcf6911e9a98ccb7906 100644 --- a/presentation/src/main/res/values-in/strings.xml +++ b/presentation/src/main/res/values-in/strings.xml @@ -19,187 +19,187 @@ ~ along with QKSMS. If not, see . --> - New conversation - Compose - Shortcut disabled - Archived - Setelan + Percakapan baru + Buat + Pintasan dinonaktifkan + Diarsipkan + Pengaturan Notifikasi Tema Cari kotak masuk… - Tulis Pesan baru + Ketik nama atau nomor Lewati Lanjutkan Panggil Detail - Save to gallery - Open navigation drawer - %d selected + Simpan ke galeri + Buka laci navigasi + %d dipilih Clear Archive Unarchive - Delete - Pin to top - Unpin - Mark read - Mark unread - Block - Syncing messages… - You: %s - Results in messages - %d messages - Percakapan Anda akan ditampilkan di sini - No results - Arsip percakapan Anda akan ditampilkan di sini - Start new conversation + Hapus + Pin ke atas + Lepas pin + Tandai dibaca + Tandai belum dibaca + Blokir + Menyinkronkan pesan… + Anda: %s + Hasil dalam pesan + %d pesan + Percakapan anda akan ditampilkan di sini + Tidak ada hasil + Arsip percakapan anda akan ditampilkan di sini + Mulai percakapan baru Love texting again - Make QKSMS your default SMS app - Change - Permission required - QKSMS needs permission to send and view SMS messages - QKSMS needs permission to view your contacts - Allow + Tetapkan QKSMS sebagai apl SMS baku anda + Ubah + Membutuhkan izin + QKSMS membutuhkan izin untuk mengirim dan membaca pesan SMS + QKSMS membutuhkan izin untuk membaca kontak anda + Izinkan Kotak Masuk - Archived + Diarsipkan Scheduled - Blocking - More + Pemblokiran + Lainnya Pengaturan QKSMS+ Bantuan & Umpan balik - Invite friends + Undang teman QKSMS+ - Unlock amazing new features, and support development - Enjoying QKSMS? + Membuka fitur-fitur baru yang menakjubkan, dan mendukung pengembangan + Menikmati QKSMS? Share some love and rate us on Google Play! - OKAY! - DISMISS + OKE! + ABAIKAN Hapus - Are you sure you would like to delete %d conversations? + Apakah anda yakin ingin menghapus percakapan ini? Salin teks Teruskan Hapus - %d selected - %1$d of %2$d results - Send as group message - Recipients and replies will be visible to everyone - This is the start of your conversation. Say something nice! - Contact card - Scheduled for - Selected time must be in the future! - You must unlock QKSMS+ to use scheduled messaging - Added to scheduled messages + %d dipilih + %1$d dari %2$d hasil + Kirim sebagai pesan grup + Penerima dan balasan akan ditampilkan ke semua orang + Ini adalah awal percakapan anda. Katakan sesuatu yang baik! + Kartu nama + Jadwalkan pada + Waktu yang dipilih harus berada di masa depan! + Anda harus membuka akses QKSMS+ untuk menggunakan pesan terjadwal + Ditambahkan ke pesan terjadwal Tulis pesan… - Copy text - Forward - Delete - Previous - Next + Salin teks + Teruskan + Hapus + Sebelumnya + Selanjutnya Clear - Message details - Type: %s - From: %s - To: %s - Subject: %s - Priority: %s - Size: %s - Sent: %s - Received: %s - Delivered: %s - Error code: %d - Add an attachment - Attach a photo - Take a photo - Schedule message - Attach a contact - Error reading contact - %s selected, change SIM card - Send message + Detail pesan + Tipe: %s + Dari: %s + Ke: %s + Subyek: %s + Prioritas: %s + Ukuran: %s + Dikirim: %s + Diterima: %s + Terkirim: %s + Kode galat: %d + Tambah lampiran + Lampirkan foto + Ambil foto + Jadwalkan pesan + Lampirkan kontak + Galat membaca kontak + %s dipilih, ganti kartu SIM + Kirim pesan Mengirim… - Delivered %s - Failed to send. Tap to try again + Terkirim %s + Gagal mengirim. Sentuh untuk mencoba lagi Detail - Conversation title - Enter title + Judul percakapan + Masukkan judul Notifikasi Tema Arsip - Unarchive + Batalkan arsip Blokir Buka blokir Hapus percakapan - Couldn\'t load media - Saved to gallery - Backup and restore - Backing up messages - Restoring from backup - Last backup - Loading… - Never - Restore - Select a backup - Please unlock QKSMS+ to use backup and restore - Backup in progress… - Restore in progress… - Restore from backup - Are you sure you would like to restore your messages from this backup? - Stop restore - Messages that have already been restored will remain on your device - Backups - No backups found + Tidak bisa memuat media + Disimpan ke galeri + Pencadangan dan pemulihan + Membuat cadangan pesan + Memulihkan dari cadangan + Pencadangan terakhir + Memuat… + Tidak pernah + Pulihkan + Pilih cadangan + Silakan buka akses QKSMS+ untuk menggunakan pencadangan dan pemulihan + Pencadangan sedang berlangsung… + Pemulihan sedang berlangsung… + Pulihkan dari cadangan + Apakah anda yakin ingin memulihkan pesan anda dari cadangan ini? + Setop pemulihan + Pesan yang telah dipulihkan akan tetap berada di perangkat anda + Cadangan + Cadangan tidak ditemukan - %d messages + %d pesan - Currently, only SMS is supported by Backup and Restore. MMS support and scheduled backups will be coming soon! - Backup now - Parsing backup… - %d/%d messages - Saving backup… - Syncing messages… - Finished! - Backup and restore + Saat ini, hanya SMS yang didukung oleh Pencadangan dan Pemulihan. Dukungan MMS dan pencadangan terjadwal akan segera datang! + Cadangkan sekarang + Membaca cadangan… + %d/%d pesan + Menyimpan cadangan… + Menyinkronkan pesan… + Selesai! + Pencadangan dan pemulihan Scheduled - Automatically send a message, at the exact moment you\'d like - Hey! When was your birthday again? - It\'s on December 23rd + Otomatis mengirim pesan pada waktu yang anda inginkan + Hai! Kapan kamu ulang tahun? + 23 Desember Happy birthday! Look at what a great friend I am, remembering your birthday - Sending on December 23rd  - Schedule a message - Scheduled message + Mengirim pada tanggal 23 Desember + Jadwalkan pesan + Pesan terjadwal - Send now - Delete + Kirim sekarang + Hapus - Appearance + Tampilan Umum - QK Reply + QK Balas Tema Mode malam - Pure black night mode + Mode malam hitam pekat Waktu mulai Waktu berakhir - Font size - Use system font - Automatic emoji + Ukuran fon + Gunakan fon sistem + Emoji otomatis Notifikasi - Tap to customize - Actions - Button 1 - Button 2 - Button 3 - Notification previews + Sentuh untuk ubahsuai + Tindakan + Tombol 1 + Tombol 2 + Tombol 3 + Pratinjau notifikasi Getar Suara - None - Balasan QK + Tidak ada + QK Balas Popup untuk pesan baru - Tap to dismiss - Tap outside of the popup to close it - Blocking + Sentuh untuk mengabaikan + Sentuh di luar popup untuk menutupnya + Pemblokiran Should I Answer? Automatically filter messages from unsolicited numbers by using the \"Should I Answer\" app @@ -208,138 +208,138 @@ Configure swipe actions for conversations Right swipe Left swipe - CHANGE + UBAH - None - Archive - Delete - Call - Mark read + Tidak ada + Arsipkan + Hapus + Panggil + Tandai dibaca Konfirmasi pengiriman - Mengkonfirmasi bahwa pesan telah berhasil dikirim + Mengonfirmasi bahwa pesan telah berhasil dikirim Strip accents Remove accents from characters in outgoing SMS messages - Mobile numbers only - When composing a message, only show mobile numbers - Auto-compress MMS attachments - Sync messages - Re-sync your messages with the native Android SMS database - About QKSMS - Version %s - Debug logging enabled - Debug logging disabled - Enter duration (seconds) - Blocking - Your blocked conversations will appear here - Unblock - Would you like to unblock this conversation? - About - Version - Developer - Source code - Changelog - Contact - License - Copyright - Support development, unlock everything - You can save a starving developer for just %s + Hanya nomor seluler + Saat menulis pesan, hanya tampilkan nomor seluler + Otomatis kompres lampiran MMS + Sinkronisasi pesan + Sinkron ulang pesan anda dengan basis data apl SMS Android bawaan + Tentang QKSMS + Versi %s + Log awakutu diaktifkan + Log awakutu dinonaktifkan + Masukkan durasi (detik) + Pemblokiran + Percakapan yang anda blokir akan muncul di sini + Buka blokir + Apakah anda ingin membuka blokir percakapan ini? + Tentang + Versi + Pengembang + Kode sumber + Catatan perubahan + Kontak + Lisensi + Hak cipta + Mendukung pengembangan, membuka semua fitur + Anda bisa membantu pengembang hanya dengan %s Lifetime upgrade for %1$s %2$s Unlock + donate for %1$s %2$s - Thank you for supporting QKSMS! - You now have access to all QKSMS+ features - QKSMS+ is free for F-Droid users! If you\'d like to support development, feel free to make a donation. - Donate via PayPal - Coming soon + Terima kasih telah mendukung QKSMS! + Anda sekarang bisa mengakses semua fitur QKSMS+ + QKSMS+ selalu gratis untuk pengguna F-Droid! Jika anda ingin mendukung pengembangan, jangan ragu untuk donasi. + Donasi via PayPal + Akan datang Tema premium Unlock beautiful theme colors not available on the Material Design palette - Custom auto-emoji - Create custom auto-emoji shortcuts - Message backup + Emoji otomatis khusus + Buat pintasan emoji otomatis khusus + Pencadangan pesan Automatically back up your messages. Never again worry about losing your history if you change phones or your phone gets lost - Scheduled messages + Pesan terjadwal Schedule messages to automatically be sent at a specific time and date Delayed sending - Wait a few seconds before sending your message - Automatic night mode - Enable night mode based on the time of day - Advanced blocking - Block messages that contain keywords or match patterns - Auto-forward - Automatically forward messages from certain senders - Auto-respond + Tunggu beberapa detik sebelum mengirim pesan + Mode malam otomatis + Aktifkan mode malam berdasarkan waktu + Pemblokiran lanjutan + Blokir pesan yang mengandung kata kunci atau cocok dengan pola + Penerusan otomatis + Otomatis meneruskan pesan dari pengirim tertentu + Balasan otomatis Automatically respond to incoming messages with a preset response - More - QKSMS is under active development, and your purchase will include all future QKSMS+ features! + Lainnya + QKSMS selalu dalam pengembangan aktif, dan pembelian anda sudah termasuk semua fitur QKSMS+ yang akan datang! Memuat… - Lihat lebih banyak percakapan - Mark read + Lihat percakapan lainnya + Tandai dibaca Panggil - Delete - Lebih banyak - Lebih sedikit + Hapus + Tampilkan lainnya + Tampilkan sedikit Buka percakapan Material QKSMS+ HEX Terapkan - None - Mark read - Reply - Call - Delete + Tidak ada + Tandai dibaca + Balas + Panggil + Hapus Batal Hapus - Save - Stop - More + Simpan + Setop + Lainnya Set Buka blokir Urungkan Disalin - Archived conversation - Anda harus membuka QKSMS+ untuk menggunakan ini + Percakapan diarsipkan + Anda harus membuka akses QKSMS+ untuk menggunakan ini - %s new messages + %s pesan baru - Message not sent - The message to %s failed to send + Pesan tidak terkirim + Pesan ke %s gagal untuk dikirim Nonaktif Selalu nyala Otomatis - Show name and message - Show name - Hide contents + Tampilkan nama dan pesan + Tampilkan nama + Sembunyikan konten - Small + Kecil Normal - Large - Larger + Besar + Lebih besar - No delay - Short - Medium - Long + Tanpa tundaan + Singkat + Sedang + Lama 100KB 200KB - 300KB (Recommended) + 300KB (Direkomendasikan) 600KB 1000KB 2000KB - No compression + Tanpa kompresi Oke - Beri Saya waktu sebentar + Beri saya waktu sebentar Sedang dalam perjalanan Terima kasih Kelihatannya bagus diff --git a/presentation/src/main/res/values-it/strings.xml b/presentation/src/main/res/values-it/strings.xml index b4eaeb3c50240389e23b9cc11e3e9f0a66e650d9..64b53c219e6681446357220b63baf0685e9638f9 100644 --- a/presentation/src/main/res/values-it/strings.xml +++ b/presentation/src/main/res/values-it/strings.xml @@ -116,8 +116,8 @@ Allega una foto Scatta una foto Pianifica messaggio - Attach a contact - Error reading contact + Collegare un contatto + Errore nella lettura del contatto %s selezionato, cambiare SIM Invia messaggio Invio in corso… diff --git a/presentation/src/main/res/values-iw/strings.xml b/presentation/src/main/res/values-iw/strings.xml index e2ab91a3fbecc046bde5639bcd3b9bb6e2578b38..052eaca95285d11b9280e2d06512a171ceeea104 100644 --- a/presentation/src/main/res/values-iw/strings.xml +++ b/presentation/src/main/res/values-iw/strings.xml @@ -118,8 +118,8 @@ צירוף תמונה צילום תמונה תזמון הודעה - Attach a contact - Error reading contact + צירוף איש קשר + שגיאה בקריאת איש קשר %s נבחרו, החלפת כרטיס SIM שליחת הודעה מתבצעת שליחה… diff --git a/presentation/src/main/res/values-ja/strings.xml b/presentation/src/main/res/values-ja/strings.xml index 5820ee4b78fbe5fef65fa4ee6c6e2067ed3a9b9f..ad52ffb357661a43a26e5e714127b993371d4dfe 100644 --- a/presentation/src/main/res/values-ja/strings.xml +++ b/presentation/src/main/res/values-ja/strings.xml @@ -19,9 +19,9 @@ ~ along with QKSMS. If not, see . --> - New conversation - Compose - Shortcut disabled + 新しい会話 + メッセージを書く + 無効にするショートカット アーカイブ済 設定 通知 @@ -32,99 +32,99 @@ 続行 通話 詳細 - Save to gallery - Open navigation drawer - %d selected - Clear - Archive - Unarchive - Delete - Pin to top - Unpin - Mark read - Mark unread - Block - Syncing messages… + ギャラリーに保存 + ナビゲーション引き出しを開く + %d 選択された + 消去しました + アーカイブ + アーカイブしない + 削除 + 上部に固定します。 + 固定を解除します。 + 既読にする + 未読にします。 + ブロック + 同期メッセージ… あなた: %s - Results in messages - %d messages + メッセージの結果 + %d メッセージ 会話がここに表示されます 結果はありません 保存した会話がここに表示されます - Start new conversation - Love texting again - Make QKSMS your default SMS app - Change - Permission required - QKSMS needs permission to send and view SMS messages - QKSMS needs permission to view your contacts - Allow + 新しい会話を開始します。 + もう一度テキストメッセージが大好き + QKSMS のデフォルトの SMS アプリを作る + 変更 + 必要なアクセス許可 + QKSMSはSMSメッセージを送信および表示する権限を必要とします + QKSMSはあなたの連絡先を閲覧する許可を必要とします + 許可する 受信トレイ アーカイブ済 スケジュール済 - Blocking + ブロック もっと 設定 QKSMS+ ヘルプとフィードバック - Invite friends - QKSMS+ - Unlock amazing new features, and support development + 友達を招待する + QKSMSプラス + 驚くべき新機能のロックを解除し、開発をサポート QKSMS をお楽みいただいていますか? Google Play で愛を共有して私たちを評価してください! OK! - 消去 + 免じる 削除 - Are you sure you would like to delete %d conversations? + %d の会話を削除するよろしいですか。 テキストをコピー 転送 削除 - %d selected - %1$d of %2$d results - Send as group message - Recipients and replies will be visible to everyone + %d件を選択中 + %2$d 結果の %1$d + グループ メッセージとして送信します。 + 受信者と返信は全員に表示されます これが会話の始まりです。 何か素敵なことを言ってください! - Contact card - Scheduled for - Selected time must be in the future! - You must unlock QKSMS+ to use scheduled messaging - Added to scheduled messages + 連絡先カード + 予定されている + 選択した時間は、将来的にする必要があります! + あなたのロックを解除する必要があります QKSMS + スケジュールされたメッセージングを使用するには + スケジュールされたメッセージに追加 メッセージを書く… テキストのコピー 転送 削除 - Previous - Next - Clear - Message details - Type: %s - From: %s - To: %s - Subject: %s - Priority: %s - Size: %s - Sent: %s - Received: %s - Delivered: %s - Error code: %d - Add an attachment - Attach a photo - Take a photo - Schedule message - Attach a contact - Error reading contact - %s selected, change SIM card - Send message + 前へ + 次へ + 消去 + メッセージの詳細 + タイプ: %s + 宛先: %s + 宛先:%s + 件名: %s + 優先順位: %s + サイズ: %s + 送られた: %s + 受け取った: %s + 配達: %s + エラーコード:%d + 添付ファイルを追加する + 写真を添付する + 写真を撮る + スケジュールメッセージ + 連絡先を添付する + 連絡先の読み取りエラー + %s 選択、SIMカードを交換 + メッセージを送る 送信中… %s を配信しました 送信に失敗しました。タップすると、もう一度やり直します 詳細 - Conversation title - Enter title + 会話のタイトル + タイトルを入力 通知 テーマ アーカイブ @@ -132,47 +132,47 @@ ブロック ブロックを解除 会話を削除 - Couldn\'t load media - Saved to gallery - Backup and restore - Backing up messages - Restoring from backup - Last backup - Loading… - Never - Restore - Select a backup - Please unlock QKSMS+ to use backup and restore - Backup in progress… - Restore in progress… - Restore from backup - Are you sure you would like to restore your messages from this backup? - Stop restore - Messages that have already been restored will remain on your device - Backups - No backups found + メディアを読み込めませんでした + ギャラリーに保存 + バックアップと復元 + メッセージをバックアップする + バックアップからの復元 + 最後のバックアップ + ロード中... + 決して + リストア + バックアップを選択 + バックアップと復元を使用するにはQKSMS+のロックを解除してください + バックアップ中… + 復元中… + バックアップから復元 + このバックアップからメッセージを復元してもよろしいですか? + 復元を停止する + 既に復元されたメッセージはあなたのデバイスに残ります + バックアップ + バックアップが見つかりません - %d messages + %d メッセージ - Currently, only SMS is supported by Backup and Restore. MMS support and scheduled backups will be coming soon! - Backup now - Parsing backup… - %d/%d messages - Saving backup… - Syncing messages… - Finished! - Backup and restore - Scheduled - Automatically send a message, at the exact moment you\'d like - Hey! When was your birthday again? - It\'s on December 23rd - Happy birthday! Look at what a great friend I am, remembering your birthday - Sending on December 23rd  - Schedule a message - Scheduled message + 現在、SMSのみがバックアップと復元でサポートされています。 MMSのサポートと定期的なバックアップは近日中に開始されます。 + 今すぐバックアップ + バックアップの解析中... + %d/%dすべてのメッセージ + バックアップを保存しています... + メッセージを同期しています... + 終了しました。 + バックアップと復元 + 予定 + 希望する瞬間に自動的にメッセージを送信する + ちょっと!あなたの誕生日はいつでしたか? + 12月23日です + お誕生日おめでとうございます! 私はあなたの誕生日を思い出して、何という素晴らしい友達なのか見て + 12月23日に発送 + メッセージをスケジュールする + スケジュールされたメッセージ - Send now - Delete + 今すぐ送る + 削除 外観 全般 @@ -186,50 +186,50 @@ システムフォントを使用する 自動絵文字 通知 - Tap to customize - Actions - Button 1 - Button 2 - Button 3 + カスタマイズするためにタップ + アクション + ボタン 1 + ボタン 2 + ボタン 3 通知プレビュー 振動 サウンド - None + 無し QK 返信 新しいメッセージのポップアップ - タップして消去 + タップして閉じる ポップアップの外側をタップすると閉じます - ブロック + ブロッキング Should I Answer? \"Should I Answer\" アプリを使用して、迷惑な番号からのメッセージを自動的にフィルタします - Delayed sending - Swipe actions - Configure swipe actions for conversations - Right swipe - Left swipe - CHANGE + 遅延送信 + スワイプアクション + 会話のスワイプアクションを設定する + 右スワイプ + 左スワイプ + 変更 - None - Archive - Delete - Call - Mark read + 無し + アーカイブ + 削除 + 電話 + 既読にする 配信確認 メッセージが正常に送信されたことを確認します アクセントの除去 送信する SMS メッセージの文字からアクセントを削除します - Mobile numbers only - When composing a message, only show mobile numbers - Auto-compress MMS attachments + 携帯電話番号のみ + メッセージを作成するときは、携帯電話番号のみを表示します + MMS添付ファイルの自動圧縮 メッセージを同期 メッセージをネイティブの Android SMS データベースと再同期します QKSMS について バージョン %s - Debug logging enabled - Debug logging disabled - Enter duration (seconds) + デバッグログが有効 + デバッグログを無効にする + 期間を入力してください (秒) ブロック ブロックした会話がここに表示されます ブロックを解除 @@ -258,8 +258,8 @@ メッセージのバックアップ メッセージを自動的にバックアップします。携帯電話を変更したり、お使いの携帯電話をなくしても、もう二度と履歴がなくなる心配はいりません。 スケジュールされたメッセージ - Schedule messages to automatically be sent at a specific time and date - 送信の遅延 + 特定の日時に自動的に送信されるようにメッセージをスケジュールする + 送信を遅らせる メッセージを送信する前に、数秒間待ちます 自動夜間モード 時間に基づいて夜間モードを有効にします @@ -270,32 +270,32 @@ 自動応答 あらかじめ設定された応答で、受信したメッセージに自動的に返信します さらに - QKSMS is under active development, and your purchase will include all future QKSMS+ features! - 読み込み中… + QKMSは活発に開発中です、そして、あなたの購入はすべての将来のQKMS+機能を含みます! + ロード中... さらに会話を表示 既読にする - 通話 - Delete - 表示を増やす - 表示を減らす + 電話する + 削除 + もっと見せる + 少なく表示 会話を開く マテリアル QKSMS+ 16 進数 適用 - None - Mark read - Reply - Call - Delete + 無し + 既読にする + 返信 + 電話 + 削除 キャンセル 削除 - Save - Stop + 保存する + やめる さらに - Set + 据える ブロックを解除 元に戻す コピーしました @@ -307,35 +307,35 @@ メッセージは送信されませんでした %s へのメッセージの送信に失敗しました - 無効 + 無効です 常にオン 自動 名前とメッセージを表示 名前を表示 - 内容を非表示 + コンテンツを隠す - - 標準 - - 最大 + 小さい + 普通の + 大き + 大きい - No delay - Short - Medium - Long + 遅延なし + 短い + ミディアム + 長い 100KB 200KB - 300KB (Recommended) + 300KB 【推奨】 600KB 1000KB 2000KB - No compression + 圧縮なし OK @@ -346,9 +346,9 @@ 最近どうですか? 了解 いいえ - 好き + 好きです ごめんなさい 大爆笑です - 大丈夫です + いいんだよ diff --git a/presentation/src/main/res/values-ko/strings.xml b/presentation/src/main/res/values-ko/strings.xml index 47509017166bcd34a19505c1fd054dda76f49132..01feef233d6ca824abef1b143df63a94d8ddb2b2 100644 --- a/presentation/src/main/res/values-ko/strings.xml +++ b/presentation/src/main/res/values-ko/strings.xml @@ -19,9 +19,9 @@ ~ along with QKSMS. If not, see . --> - New conversation - Compose - Shortcut disabled + 새로운 대화 + 메시지 작성 + 바로가기 해제 보관됨 설정 알림 @@ -52,7 +52,7 @@ 결과없음 보관된 메시지가 여기 나타납니다 새로운 대화 시작하기 - Love texting again + 다시 문자 보내는게 좋네요 QKSMS 앱을 기본 메시지 앱으로 설정하기 변경하기 권한을 부여해 주세요 @@ -104,8 +104,8 @@ 삭제 세부 정보 종류: %s - From: %s - To: %s + %s가 + %s에게 제목: %s 우선 순위: %s 크기: %s @@ -117,8 +117,8 @@ 사진 첨부 사진 찍기 예약 전송 메시지 - Attach a contact - Error reading contact + 연락처 첨부 + 연락처를 불러오는 중에 문제가 발생했습니다 %s 선택됨, SIM 카드를 변경하세요. 메시지 보내기 전송 중... @@ -134,36 +134,36 @@ 차단 차단 해제 대화 삭제 - Couldn\'t load media + 미디어를 불러올 수 없습니다 갤러리에 저장 - Backup and restore - Backing up messages - Restoring from backup - Last backup - Loading… - Never - Restore - Select a backup - Please unlock QKSMS+ to use backup and restore - Backup in progress… - Restore in progress… - Restore from backup - Are you sure you would like to restore your messages from this backup? - Stop restore - Messages that have already been restored will remain on your device - Backups - No backups found + 백업과 복원 + 메시지 백업하기 + 복원하기 + 최근 백업 + 로딩 중... + 사용 안 함 + 복원 + 백업 선택하기 + QKSMS+을 구입하여 백업과 복원기능을 사용하세요 + 백업 중... + 복원 중... + 복원이 되었습니다 + 이 백업 파일로부터 복원하시겠어요? + 복원 중지 + 이미 저장된 메시지들은 내 기기에 남아있습니다. + 백업 + 백업을 찾지 못했어요 - %d messages + %d 개의 새로운 메시지 - Currently, only SMS is supported by Backup and Restore. MMS support and scheduled backups will be coming soon! - Backup now - Parsing backup… - %d/%d messages - Saving backup… - Syncing messages… - Finished! - Backup and restore + 아직 SMS만 백업과 복원기능이 지원됩니다. MMS지원과 백업 스케줄 기능은 곧 찾아옵니다! + 지금 백업하기 + 분석 중... + %d/%d 메시지 + 저장 중... + 동기화 중... + 끝! + 백업과 복원 예약됨 원하는 그 시간에 자동으로 메시지를 보냅니다. 잠깐만요! 생일이 언제였나요..? @@ -221,16 +221,16 @@ 전송 확인 전송 성공을 확인합니다 스트립 악센트 - Remove accents from characters in outgoing SMS messages - Mobile numbers only - When composing a message, only show mobile numbers + SMS를 주고받을 때, 대문자 없애기 + 전화번호만 보이기 + 메시지를 쓸 때, 전화번호만 보이게 하기 자동으로 MMS 첨부 파일 압축하기 메시지 동기화 하기 기본 안드로이드 SMS 데이터베이스에 다시 동기화하기 QKSMS에 대해... 버전 정보 %s - Debug logging enabled - Debug logging disabled + 디버그 로깅 설정됨 + 디버그 로깅 해제됨 기간 (초)을 입력 차단 차단된 대화가 여기에 나타납니다. @@ -262,7 +262,7 @@ 예약 전송 메시지 특정한 시간에 메시지가 보내지도록 메시지 전송을 예약하세요 지연 전송 - 메시지를 보내기 전에 몇 초를 기다리세요 + 지연 전송이라는 멋진 기능이 있답니다! 자동 나이트 모드 시간에 따라 나이트 모드 설정하기 고급 차단 기능 @@ -277,7 +277,7 @@ 더 많은 대화 보기 읽음으로 표시 전화하기 - Delete + 삭제하기 더 보기 적게 표시 대화 열기 @@ -290,12 +290,12 @@ 읽음으로 표시 답장 전화하기 - Delete + 삭제하기 취소 삭제 저장 - Stop + 중지 더보기 설정 차단 해제 @@ -308,7 +308,7 @@ %s 개의 새로운 메시지 - 메시지 보내지지 않음 + 메시지 전송 실패 %s 에게 보낼 메시지 전송이 실패했습니다 비활성화됨 diff --git a/presentation/src/main/res/values-nl/strings.xml b/presentation/src/main/res/values-nl/strings.xml index 11f8cf1383cace6657960a1a3f28ba7e7dea7314..e6b5e522f0fb00f387d7d7d80750ecc21b784f32 100644 --- a/presentation/src/main/res/values-nl/strings.xml +++ b/presentation/src/main/res/values-nl/strings.xml @@ -116,8 +116,8 @@ Voeg een foto toe Neem een foto Gepland bericht - Attach a contact - Error reading contact + Een contactpersoon toevoegen + Fout bij het lezen van contact %s geselecteerd, wijzig SIM-kaart Verstuur bericht Verzenden… diff --git a/presentation/src/main/res/values-pl/strings.xml b/presentation/src/main/res/values-pl/strings.xml index 58ef95b3a7d17b31568c26e02ccf6da36be43c70..0d9c635f8b0e6209656f1b88fc6ebe4f80e0d594 100644 --- a/presentation/src/main/res/values-pl/strings.xml +++ b/presentation/src/main/res/values-pl/strings.xml @@ -118,8 +118,8 @@ Załącz zdjęcie Zrób zdjęcie Zaplanuj wiadomość - Attach a contact - Error reading contact + Załącz kontakt + Nie można odczytać kontaktu Wybrano %s, zmień kartę SIM Wyślij wiadomość Wysyłanie… diff --git a/presentation/src/main/res/values-pt-rBR/strings.xml b/presentation/src/main/res/values-pt-rBR/strings.xml index dbe3b1d68fcbba859ed71384ae65f3e04b40de8f..01cb0282e56e351ef4a408ab2e501a1785f2b673 100644 --- a/presentation/src/main/res/values-pt-rBR/strings.xml +++ b/presentation/src/main/res/values-pt-rBR/strings.xml @@ -116,8 +116,8 @@ Anexar fotografia Tirar fotografia Agendar mensagem - Attach a contact - Error reading contact + Anexar um contacto + Erro ao ler o contacto %s selecionado, alterar cartão SIM Enviar mensagem A enviar… diff --git a/presentation/src/main/res/values-pt/strings.xml b/presentation/src/main/res/values-pt/strings.xml index 74952af11ee821d6ab2fa1b9b18951bbeebe4133..724dfc15f86142513f11fa0596048bbb985fd4a2 100644 --- a/presentation/src/main/res/values-pt/strings.xml +++ b/presentation/src/main/res/values-pt/strings.xml @@ -116,8 +116,8 @@ Anexar fotografia Tirar fotografia Agendar mensagem - Attach a contact - Error reading contact + Anexar um contacto + Erro ao ler o contacto %s selecionado, alterar cartão SIM Enviar mensagem A enviar… diff --git a/presentation/src/main/res/values-ru/strings.xml b/presentation/src/main/res/values-ru/strings.xml index 2a688a35ecb044dac24e9c8f146c7887bc8c17d3..4a51482242ac4604d8209c73fd990221a95d7255 100644 --- a/presentation/src/main/res/values-ru/strings.xml +++ b/presentation/src/main/res/values-ru/strings.xml @@ -118,8 +118,8 @@ Прикрепить фото Сделать снимок Запланировать сообщение - Attach a contact - Error reading contact + Прикрепите контакт + Ошибка чтения контакта Выбрано %s, сменить SIM-карту Отправить сообщение Отправка… diff --git a/presentation/src/main/res/values-sk/strings.xml b/presentation/src/main/res/values-sk/strings.xml index d36b9efcd17059aa34fef95fe994ae973c88a938..5399274faea9accd323b101a4d5a1d1cf5db3ea1 100644 --- a/presentation/src/main/res/values-sk/strings.xml +++ b/presentation/src/main/res/values-sk/strings.xml @@ -19,10 +19,10 @@ ~ along with QKSMS. If not, see . --> - New conversation - Compose + Nová konverzácia + Písať Shortcut disabled - Archived + Archivované Nastavenia Upozornenia Téma @@ -33,48 +33,48 @@ Volať Podrobnosti Uložiť do galérie - Open navigation drawer + Otvoriť navigačné menu %d vybraných Vymazať Archivovať - Unarchive + Zrušiť archiváciu Odstrániť - Pin to top - Unpin - Mark read - Mark unread - Block + Pripnúť na začiatok + Odopnúť + Označiť ako prečítané + Označiť ako neprečítané + Zablokovať Synchronizácia správ… - You: %s - Results in messages - %d messages - Your conversations will appear here + Vy: %s + Výsledky v správach + %d správ + Tu sa zobrazia vaše konverzácie Žiadne výsledky - Your archived conversations will appear here + Tu sa zobrazia vaše archivované konverzácie Začať novú konverzáciu - Love texting again - Make QKSMS your default SMS app - Change - Permission required - QKSMS needs permission to send and view SMS messages - QKSMS needs permission to view your contacts + Opäť si zamilujte písanie + Nastavte QKSMS ako predvolenú SMS aplikáciu + Zmeniť + Vyžadované povolenie + QKSMS potrebuje povolenie posielať a zobrazovať správy SMS + QKSMS potrebuje povolenie na zobrazenie kontaktov Povoliť - Inbox - Archived - Scheduled - Blocking + Doručené + Archivované + Naplánované + Blokovanie Viac - Settings + Nastavenia QKSMS+ - Help & feedback - Invite friends + Nápoveda & spätná väzba + Pozvať priateľov QKSMS+ - Unlock amazing new features, and support development - Enjoying QKSMS? - Share some love and rate us on Google Play! - OKAY! - DISMISS - Delete + Odomknutie úžasné nové funkcie a podporte vývoj + Máte radi QKSMS? + Podeľte sa o trochu lásky a ohodnoťte nás na Google Play! + OUKEJ! + ODMIETNUŤ + Zmazať Are you sure you would like to delete this conversation? Are you sure you would like to delete %d conversations? @@ -82,92 +82,92 @@ Are you sure you would like to delete %d conversations? - Copy text - Forward - Delete + Kopírovať text + Preposlať + Odstrániť %d selected %1$d of %2$d results - Send as group message + Poslať ako skupinovú správu Recipients and replies will be visible to everyone - This is the start of your conversation. Say something nice! + Toto je začiatok vašej konverzácie. Napíšte niečo pekného! Contact card Scheduled for Selected time must be in the future! You must unlock QKSMS+ to use scheduled messaging Added to scheduled messages - Write a message… - Copy text - Forward - Delete + Napíšte správu… + Kopírovať text + Preposlať + Odstrániť Previous Next Clear - Message details - Type: %s - From: %s - To: %s - Subject: %s - Priority: %s - Size: %s - Sent: %s - Received: %s - Delivered: %s - Error code: %d - Add an attachment + Detaily správy + Typ: %s + Od: %s + Pre: %s + Predmet: %s + Priorita: %s + Veľkosť: %s + Odoslané: %s + Prijaté: %s + Doručené: %s + Kód chyby: %d + Pridať prílohu Attach a photo Take a photo Schedule message - Attach a contact + Pripojiť kontakt Error reading contact %s selected, change SIM card - Send message - Sending… - Delivered %s + Odoslať správu + Odosiela sa… + Doručené %s Failed to send. Tap to try again - Details + Detaily Conversation title Enter title - Notifications - Theme - Archive - Unarchive - Block - Unblock - Delete conversation + Upozornenia + Motív + Archivovať + Zrušiť archiváciu + Blokovať + Odblokovať + Odstrániť konverzáciu Couldn\'t load media Saved to gallery Backup and restore Backing up messages Restoring from backup - Last backup - Loading… - Never - Restore - Select a backup + Posledná záloha + Načítavanie… + Nikdy + Obnoviť + Zvoľte zálohu Please unlock QKSMS+ to use backup and restore - Backup in progress… - Restore in progress… - Restore from backup + Prebieha zálohovanie… + Prebieha obnova… + Obnoviť zo zálohy Are you sure you would like to restore your messages from this backup? - Stop restore + Zastaviť obnovu Messages that have already been restored will remain on your device - Backups - No backups found + Zálohy + Nenašli sa žiadne zálohy %d message %d messages %d messages - %d messages + %d správ Currently, only SMS is supported by Backup and Restore. MMS support and scheduled backups will be coming soon! - Backup now - Parsing backup… - %d/%d messages - Saving backup… - Syncing messages… + Zálohovať + Analýza zálohy… + %d/%d správ + Ukladá sa záloha… + Synchronizácia správ… Finished! - Backup and restore + Zálohovanie a obnovenie Scheduled Automatically send a message, at the exact moment you\'d like Hey! When was your birthday again? @@ -177,27 +177,27 @@ Schedule a message Scheduled message - Send now - Delete + Odoslať teraz + Odstrániť - Appearance - General + Vzhľad + Všeobecné QK Reply - Theme - Night mode + Motív + Nočný režim Pure black night mode - Start time - End time - Font size - Use system font - Automatic emoji - Notifications + Čas spustenia + Čas ukončenia + Veľkosť písma + Použiť systémové písmo + Automatické emoji + Upozornenia Tap to customize - Actions - Button 1 - Button 2 - Button 3 - Notification previews + Akcie + Tlačidlo 1 + Tlačidlo 2 + Tlačidlo 3 + Obsah upozornení Vibration Sound None @@ -205,7 +205,7 @@ Popup for new messages Tap to dismiss Tap outside of the popup to close it - Blocking + Blokované Should I Answer? Automatically filter messages from unsolicited numbers by using the \"Should I Answer\" app @@ -217,9 +217,9 @@ CHANGE None - Archive - Delete - Call + Archivovať + Odstrániť + Zavolať Mark read Delivery confirmations @@ -231,23 +231,23 @@ Auto-compress MMS attachments Sync messages Re-sync your messages with the native Android SMS database - About QKSMS - Version %s + O aplikácii QKSMS + Verzia %s Debug logging enabled Debug logging disabled Enter duration (seconds) - Blocking - Your blocked conversations will appear here - Unblock - Would you like to unblock this conversation? - About - Version - Developer - Source code - Changelog - Contact - License - Copyright + Blokované + Blokované konverzácie sa zobrazia tu + Odblokovať + Chcete odblokovať túto konverzáciu? + O aplikácii + Verzia + Vývojár + Zdrojový kód + Zoznam zmien + Konktakt + Licencia + Autorské práva Support development, unlock everything You can save a starving developer for just %s Lifetime upgrade for %1$s %2$s @@ -256,7 +256,7 @@ You now have access to all QKSMS+ features QKSMS+ is free for F-Droid users! If you\'d like to support development, feel free to make a donation. Donate via PayPal - Coming soon + Už čoskoro Premium themes Unlock beautiful theme colors not available on the Material Design palette Custom auto-emoji @@ -267,31 +267,31 @@ Schedule messages to automatically be sent at a specific time and date Delayed sending Wait a few seconds before sending your message - Automatic night mode + Automatický nočný režim Enable night mode based on the time of day - Advanced blocking - Block messages that contain keywords or match patterns + Rozšírené blokovanie + Blokujte správy, ktoré obsahujú kľúčové slová, alebo odpovedajú vzoru Auto-forward Automatically forward messages from certain senders Auto-respond Automatically respond to incoming messages with a preset response - More + Viac QKSMS is under active development, and your purchase will include all future QKSMS+ features! - Loading… + Načítavanie… View more conversations - Mark read - Call - Delete - Show more - Show less - Open conversation - Material + Označiť ako prečítané + Volať + Odstrániť + Zobraziť viac + Zobraziť menej + Otvoriť konverzáciu + Materiál QKSMS+ HEX - Apply + Použiť None - Mark read + Označiť ako prečítané Reply Call Delete @@ -302,7 +302,7 @@ Stop More Set - Unblock + Odblokovať Undo Copied Archived conversation @@ -321,18 +321,18 @@ Automatic - Show name and message - Show name - Hide contents + Zobraziť meno a správu + Zobraziť meno + Skryť obsah - Small - Normal - Large - Larger + Malý + Stredný + Veľký + Najväčší - No delay + Bez oneskorenia Short Medium Long @@ -340,11 +340,11 @@ 100KB 200KB - 300KB (Recommended) + 300KB (odporúča sa) 600KB 1000KB 2000KB - No compression + Bez kompresie V poriadku diff --git a/presentation/src/main/res/values-tr/strings.xml b/presentation/src/main/res/values-tr/strings.xml index 980a89c14fb1955d22796e7161d1324e08d046c1..768cb8a0e518738406f55ffa9c83c9c19cef920e 100644 --- a/presentation/src/main/res/values-tr/strings.xml +++ b/presentation/src/main/res/values-tr/strings.xml @@ -116,8 +116,8 @@ Bir fotoğraf eklemek Fotoğraf Çek Mesajı zamanlama - Attach a contact - Error reading contact + Bir kişi ekle + Kişi okunurken hata oluştu Seçili, %s SIM kartı değiştirin Mesaj gönder Gönderiliyor... diff --git a/presentation/src/main/res/values-uk/strings.xml b/presentation/src/main/res/values-uk/strings.xml index b7354fd1d0eb4a7610df5ea92db007a42cdba151..48da75c73151e0bbfd326084565ff7c180f2ba18 100644 --- a/presentation/src/main/res/values-uk/strings.xml +++ b/presentation/src/main/res/values-uk/strings.xml @@ -32,7 +32,7 @@ Продовжити Зателефонувати Деталі - Зберегти в галерії + Зберегти в галереї Відкрити панель навігації Обрано: %d Очистити @@ -118,8 +118,8 @@ Прикріпити фотографію Сфотографувати Запланувати повідомлення - Attach a contact - Error reading contact + Прикріпити контакт + Помилка читання контакту %s обрано, змініть SIM-картку Надіслати повідомлення Надсилання… @@ -136,7 +136,7 @@ Розблокувати Видалити бесіду Не вдалося завантажити медіа - Зберегти до галереї + Збережено в галереї Резервне копіювання та відновлення Резервне копіювання повідомлень Відновити з резервної копії @@ -305,7 +305,7 @@ Розблокувати Скасувати Скопійовано - Архівована бесіда + Бесіду заархівовано Для використання Ви повинні розблокувати QKSMS+ Нове повідомлення diff --git a/presentation/src/main/res/values-vi/strings.xml b/presentation/src/main/res/values-vi/strings.xml index dbcc3a00af0466bd619bd94f8b4ba5eae23ba290..380ab8d72b59139e0679a6f1ea56fa84f7d67e4d 100644 --- a/presentation/src/main/res/values-vi/strings.xml +++ b/presentation/src/main/res/values-vi/strings.xml @@ -19,64 +19,64 @@ ~ along with QKSMS. If not, see . --> - New conversation - Compose - Shortcut disabled - Đã được lưu trữ + Cuộc trò chuyện mới + Soạn tin + Đã tắt lối tắt + Lưu trữ Cài đặt Thông báo Chủ đề - Tìm kiếm các tin đã nhận… + Tìm kiếm tin nhắn… Nhập tên hoặc số điện thoại Bỏ qua Tiếp tục Cuộc gọi Chi tiết Lưu vào bộ sưu tập - Open navigation drawer + Mở menu điều hướng %d đã chọn Dọn sạch Lưu trữ Hủy lưu trữ Xóa - Pin to top - Unpin + Ghim lên đầu + Bỏ ghim Đánh dấu đã đọc - Mark unread - Block + Đánh dấu là chưa đọc + Chặn liên hệ Đang đồng bộ tin nhắn… Bạn: %s Kết quả trong tin nhắn %d tin nhắn - Các cuộc hội thoại của bạn sẽ xuất hiện ở đây + Các cuộc trò chuyện của bạn sẽ xuất hiện ở đây Không có kết quả - Các cuộc hội thoại đã lưu trữ của bạn sẽ xuất hiện ở đây - Bắt đầu cuộc hội thoại - Love texting again + Các cuộc trò chuyện đã lưu trữ của bạn sẽ xuất hiện ở đây + Bắt đầu cuộc trò chuyện + Yêu thích nhắn tin ở đây Đặt QKSMS là ứng dụng SMS mặc định Thay đổi Yêu cầu quyền truy cập QKSMS cần cấp quyền để gửi và xem tin nhắn SMS QKSMS cần cấp quyền để xem danh bạ Cho phép - Hộp thư đến + Trò chuyện Lưu trữ - Lịch gửi - Blocking + Hẹn giờ tin nhắn + Chặn liên hệ Xem thêm Cài đặt QKSMS+ Giúp đỡ và phản hồi - Invite friends + Giới thiệu đến bạn bè QKSMS+ - Unlock amazing new features, and support development + Mở khoá những tính năng đặc sắc và hỗ trợ cho nhà phát triển Bạn có thích QKSMS không? Chia sẻ và vote chúng tôi trên Google Play! Đồng ý! Bỏ qua Xóa - Are you sure you would like to delete %d conversations? + Bạn chắc chắn muốn xoá %d cuộc trò chuyện này chứ ? Sao chép văn bản @@ -84,95 +84,95 @@ Xóa %d đã chọn - %1$d trên %2$d results - Send as group message - Recipients and replies will be visible to everyone - Bắt đầu cuộc hội thoại nào. Hãy nói điều gì đó tốt đẹp! - Contact card - Scheduled for - Selected time must be in the future! - You must unlock QKSMS+ to use scheduled messaging - Added to scheduled messages - Viết một tin nhắn… + %1$d trên %2$d kết quả + Gửi theo nhóm tin nhắn + Người nhận và các câu trả lời sẽ được hiển thị với mọi người + Đây là sự khởi đầu của một cuộc trò chuyện. Hãy nói điều gì đó tốt đẹp nhé! + Thẻ danh thiếp + Hẹn giờ cho + Thời gian chọn phải trong tương lai! + Bạn phải mở khoá QKSMS+ để dùng tính năng hẹn giờ + Đã hẹn giờ cho tin nhắn + Viết tin nhắn… Sao chép văn bản Chuyển tiếp Xóa Trước đó Tiếp theo Xóa - Message details - Type: %s - From: %s - To: %s - Subject: %s - Priority: %s - Size: %s - Sent: %s - Received: %s - Delivered: %s - Error code: %d + Chi tiết tin nhắn + Loại: %s + Từ: %s + Tới: %s + Chủ đề: %s + Độ ưu tiên: %s + Kích cỡ: %s + Đã gửi: %s + Đã nhận: %s + Đã chuyển: %s + Mã lỗi: %d Thêm tập tin đính kèm Đính kèm ảnh Chụp ảnh - Schedule message - Attach a contact - Error reading contact - %s selected, change SIM card + Hẹn giờ tin nhắn + Đính kèm liên hệ + Lỗi đọc liên hệ + %s đã được chọn, hãy đổi thẻ SIM Gửi tin nhắn Đang gửi… Đã chuyển %s Gửi thất bại. Bấm để thử lại lần nữa Chi tiết - Conversation title - Enter title + Tiêu đề cuộc trò chuyện + Nhập tiêu đề Thông báo Chủ đề Lưu trữ Hủy lưu trữ - Chặn - Bỏ chặn - Xóa hội thoại - Couldn\'t load media - Saved to gallery - Backup and restore - Backing up messages - Restoring from backup - Last backup - Loading… - Never - Restore - Select a backup - Please unlock QKSMS+ to use backup and restore - Backup in progress… - Restore in progress… - Restore from backup - Are you sure you would like to restore your messages from this backup? - Stop restore - Messages that have already been restored will remain on your device - Backups - No backups found + Chặn liên hệ + Bỏ chặn liên hệ + Xóa cuộc trò chuyện + Không thể tải thư viện + Đã lưu vào thư viện + Sao lưu và khôi phục + Đang sao lưu tin nhắn + Đang khôi phục từ bản sao lưu + Sao lưu lần cuối + Đang tải… + Chưa bao giờ + Khôi phục + Chọn một bản sao lưu + Vui lòng mở khoá QKSMS+ để dùng tính năng sao lưu và khôi phục + Đang sao lưu… + Đang khôi phục… + Khôi phục từ bản sao lưu này + Bạn chắc chắn muốn khôi phục tin nhắn từ bản sao lưu này chứ ? + Dừng khôi phục + Tin nhắn đã được khôi phục sẽ vẫn còn trên thiết bị của bạn + Các bản sao lưu + Không tìm thấy bản sao lưu nào - %d messages + %d tin nhắn - Currently, only SMS is supported by Backup and Restore. MMS support and scheduled backups will be coming soon! - Backup now - Parsing backup… - %d/%d messages - Saving backup… - Syncing messages… - Finished! - Backup and restore - Scheduled - Automatically send a message, at the exact moment you\'d like - Hey! When was your birthday again? - It\'s on December 23rd - Happy birthday! Look at what a great friend I am, remembering your birthday - Sending on December 23rd  - Schedule a message - Scheduled message + Hiện tại, tính năng sao lưu và khôi phục chỉ hỗ trợ SMS. Việc hỗ trợ MMS và tính năng sao lưu định kì sẽ có sớm! + Sao lưu ngay + Đang phân tích bản sao lưu… + %d/%d tin nhắn + Đang lưu bản sao lưu… + Đang đồng bộ tin nhắn… + Hoàn tất! + Sao lưu và khôi phục + Hẹn giờ tin nhắn + Tự động gửi tin nhắn vào lúc mà bạn muốn + Này, khi nào thì tới sinh nhật bạn ? + Vào ngày 23 tháng 12 đó + Chúc mừng sinh nhật! Nhìn xem, tôi đúng là một người bạn tuyệt vời, nhớ ngày sinh nhật của bạn đấy + Sẽ gửi vào ngày 23 tháng 12 + Hẹn giờ tin nhắn + Tin nhắn đã hẹn giờ - Send now - Delete + Gửi ngay + Xoá Giao diện Chung @@ -186,54 +186,54 @@ Sử dụng font chữ hệ thống Emoji tự động Thông báo - Tap to customize - Actions - Button 1 - Button 2 - Button 3 + Chạm để tuỳ chỉnh + Các hành động + Nút 1 + Nút 2 + Nút 3 Xem trước các thông báo Rung Âm thanh - None + Không dùng Trả lời nhanh Hiển thị popup khi có tin nhắn mới Chạm để bỏ qua - Chạm vào bên ngoài popup để đóng - Đang chặn + Chạm vào phía ngoài của hộp thoại để đóng + Chặn liên hệ - Tôi trả lời nhé? + Bộ lọc liên hệ giả mạo Tự động lọc tin nhắn từ số điện thoại không mong muốn bằng cách sử dụng ứng dụng \"Should I Answer\" - Delayed sending - Swipe actions - Configure swipe actions for conversations - Right swipe - Left swipe - CHANGE + Trì hoãn gửi tin + Các hành động vuốt + Tuỳ chỉnh hành động vuốt cho cuộc trò chuyện + Vuốt phải + Vuốt trái + THAY ĐỔI - None - Archive - Delete - Call - Mark read + Không dùng + Lưu trữ + Xoá + Gọi điện + Đánh dấu là đã đọc - Xác nhận đã nhận tin + Xác nhận đã gửi tin Xác nhận rằng các tin nhắn được gửi thành công Bỏ dấu Bỏ dấu ký tự khi gửi SMS - Mobile numbers only - When composing a message, only show mobile numbers - Auto-compress MMS attachments + Lọc số di động + Khi soạn tin mới, chỉ hiển thị liên hệ có số di động + Tự động nén các tệp đính kèm MMS Đồng bộ tin nhắn Đồng bộ lại tin nhắn với thiết bị Thông tin QKSMS Phiên bản: %s - Debug logging enabled - Debug logging disabled - Enter duration (seconds) - Đang chặn - Tin nhắn bị chặn sẽ hiện ở đây - Bỏ chặn - Bạn có muốn bỏ chặn hội thoại này? + Đã bật bản ghi gỡ lỗi + Đã tắt bản ghi gỡ lỗi + Nhập khoảng thời gian (giây) + Chặn liên hệ + Liên hệ bị chặn sẽ hiện ở đây + Bỏ chặn liên hệ + Bạn có muốn bỏ chặn liên hệ này ? Giới thiệu Phiên bản Nhà phát triển @@ -242,8 +242,8 @@ Liên hệ Giấy phép Bản quyền - Hỗ trợ phát triển, mở khóa tất cả mọi thứ - Bạn có thể cứu chúng tôi khỏi sự đói khát chỉ với %s + Hỗ trợ phát triển, mở khóa mọi thứ + Bạn có thể ủng hộ chúng tôi chỉ với %s Nâng cấp trọn đời với %1$s %2$s Mở khoá và tài trợ với %1$s %2$s Cảm ơn bạn đã ủng hộ QKSMS! @@ -253,15 +253,15 @@ Sắp có Chủ đề đặc biệt Mở khóa các màu sắc đẹp không có sẵn trong bản thiết kế Material - Tuỳ biến auto-emoji - Tạo phím tắt auto-emoji + Tuỳ biến Emoji tự động + Tạo phím tắt Emoji tự động Sao lưu tin nhắn Tự động sao lưu tin nhắn của bạn. Không bao giờ lo lắng về việc mất đi lịch sử của bạn nếu bạn thay đổi điện thoại hoặc điện thoại của bạn bị mất - Tin nhắn theo lịch - Schedule messages to automatically be sent at a specific time and date - Gửi đã bị chậm chễ - Chờ một vài giây trước khi gửi tin nhắn - Chế độ tự động ban đêm + Hẹn giờ tin nhắn + Tin nhắn hẹn giờ sẽ được tự động gửi vào thời gian cụ thể + Trì hoãn gửi tin nhắn + Chờ một vài giây trước khi gửi tin nhắn để bạn có thể hoàn tác nó + Chế độ ban đêm tự động Kích hoạt chế độ ban đêm dựa trên thời gian trong ngày Chặn nâng cao Chặn tin nhắn có chứa các từ khóa hoặc phù hợp với các mẫu @@ -269,37 +269,37 @@ Tự động chuyển tiếp tin nhắn từ người gửi Tự động trả lời Tự động trả lời các tin nhắn với nội dung cài sẵn - Xem thêm - QKSMS is under active development, and your purchase will include all future QKSMS+ features! + Nhiều tính năng khác + QKSMS đang trong giai đoạn phát triển, và việc thanh toán của bạn sẽ bao gồm tất cả tính năng QKSMS+ tương lai! Đang tải… - Xem thêm các cuộc hội thoại + Xem thêm các cuộc trò chuyện Đánh dấu đã đọc Đàm thoại - Delete + Xoá Hiển thị nhiều hơn Hiển thị ít hơn - Mở hội thoại + Mở cuộc trò chuyện Material QKSMS+ Hệ 16 Áp dụng - None - Mark read - Reply - Call - Delete + Không dùng + Đánh dấu là đã đọc + Trả lời + Gọi + Xoá Hủy Xóa - Save - Stop + Lưu + Dừng Xem thêm - Set + Chọn Bỏ chặn Undo Đã sao chép - Hội thoại đã được lưu trữ + Cuộc trò chuyện đã được lưu trữ Bạn phải mở khóa QKSMS+ để sử dụng chức năng này %s tin nhắn mới @@ -323,19 +323,19 @@ Lớn hơn - No delay - Short - Medium - Long + Không trì hoãn + Ngắn + Vừa + Dài 100KB 200KB - 300KB (Recommended) + 300KB (khuyến nghị) 600KB 1000KB 2M - No compression + Không nén Ok diff --git a/presentation/src/main/res/values-zh-rCN/strings.xml b/presentation/src/main/res/values-zh-rCN/strings.xml index c5edd66305ddc3877707462ebd7689362942425f..6adc9074b97833d31eb6bce5f73b7d00414b31bf 100644 --- a/presentation/src/main/res/values-zh-rCN/strings.xml +++ b/presentation/src/main/res/values-zh-rCN/strings.xml @@ -115,8 +115,8 @@ 添加照片 拍照 定时消息 - Attach a contact - Error reading contact + 附加联系人 + 读取联系人时出错 %s已选择, 更改SIM卡 发送消息 正在发送... diff --git a/presentation/src/main/res/values-zh/strings.xml b/presentation/src/main/res/values-zh/strings.xml index 5c8125af3add6e0b82ffda6c7e2f15e4a322320e..39783ccc66c978683ca75af70b4a1c512603b466 100644 --- a/presentation/src/main/res/values-zh/strings.xml +++ b/presentation/src/main/res/values-zh/strings.xml @@ -33,7 +33,7 @@ 致電 詳細資訊 保存到圖庫 - Open navigation drawer + 打開導航抽屜 已選擇%d項 清除 封存 @@ -67,8 +67,8 @@ 設定 QKSMS+ 幫助與反饋 - Invite friends - QKSMS+ + 邀請朋友 + QKSMS + Unlock amazing new features, and support development 喜歡QKSMS? 在Google Play分享你的感受,並給我們評分! @@ -171,8 +171,8 @@ Schedule a message Scheduled message - Send now - Delete + 立即發送 + 刪除 外觀 通用 @@ -188,9 +188,9 @@ 通知 Tap to customize Actions - Button 1 - Button 2 - Button 3 + 按鈕1 + 按鈕2 + 按鈕3 通知預覽 震動 音效 @@ -204,10 +204,10 @@ Should I Answer? 使用\"Should I Answer\"應用過濾未知號碼的消息 延遲發送 - Swipe actions - Configure swipe actions for conversations - Right swipe - Left swipe + 滑動操作 + 配置對話的滑動操作 + 向右滑動 + 向左滑動 CHANGE None