extraMap = getExtraMap(context);
- return extraMap.isPresent() && extraMap.get().isValidDialpadCharacter(ch);
- }
-
- /**
- * Returns true if the provided character is a letter, and can be mapped to a key on the dialpad.
- *
- * The provided character is expected to be a normalized character. See {@link
- * SmartDialMap#normalizeCharacter(char)} for details.
- */
- public static boolean isValidDialpadAlphabeticChar(Context context, char ch) {
- if (DEFAULT_MAP.isValidDialpadAlphabeticChar(ch)) {
- return true;
+ /**
+ * Returns true if the provided character can be mapped to a key on the dialpad.
+ *
+ *
The provided character is expected to be a normalized character. See {@link
+ * SmartDialMap#normalizeCharacter(char)} for details.
+ */
+ public static boolean isValidDialpadCharacter(Context context, char ch) {
+ if (DEFAULT_MAP.isValidDialpadCharacter(ch)) {
+ return true;
+ }
+
+ Optional extraMap = getExtraMap(context);
+ return extraMap.isPresent() && extraMap.get().isValidDialpadCharacter(ch);
}
- Optional extraMap = getExtraMap(context);
- return extraMap.isPresent() && extraMap.get().isValidDialpadAlphabeticChar(ch);
- }
-
- /**
- * Returns true if the provided character is a digit, and can be mapped to a key on the dialpad.
- */
- public static boolean isValidDialpadNumericChar(Context context, char ch) {
- if (DEFAULT_MAP.isValidDialpadNumericChar(ch)) {
- return true;
+ /**
+ * Returns true if the provided character is a letter, and can be mapped to a key on the dialpad.
+ *
+ * The provided character is expected to be a normalized character. See {@link
+ * SmartDialMap#normalizeCharacter(char)} for details.
+ */
+ public static boolean isValidDialpadAlphabeticChar(Context context, char ch) {
+ if (DEFAULT_MAP.isValidDialpadAlphabeticChar(ch)) {
+ return true;
+ }
+
+ Optional extraMap = getExtraMap(context);
+ return extraMap.isPresent() && extraMap.get().isValidDialpadAlphabeticChar(ch);
}
- Optional extraMap = getExtraMap(context);
- return extraMap.isPresent() && extraMap.get().isValidDialpadNumericChar(ch);
- }
+ /**
+ * Returns true if the provided character is a digit, and can be mapped to a key on the dialpad.
+ */
+ public static boolean isValidDialpadNumericChar(Context context, char ch) {
+ if (DEFAULT_MAP.isValidDialpadNumericChar(ch)) {
+ return true;
+ }
- /**
- * Get the index of the key on the dialpad which the character corresponds to.
- *
- * The provided character is expected to be a normalized character. See {@link
- * SmartDialMap#normalizeCharacter(char)} for details.
- *
- *
If the provided character can't be mapped to a key on the dialpad, return -1.
- */
- public static byte getDialpadIndex(Context context, char ch) {
- Optional dialpadIndex = DEFAULT_MAP.getDialpadIndex(ch);
- if (dialpadIndex.isPresent()) {
- return dialpadIndex.get();
+ Optional extraMap = getExtraMap(context);
+ return extraMap.isPresent() && extraMap.get().isValidDialpadNumericChar(ch);
}
- Optional extraMap = getExtraMap(context);
- if (extraMap.isPresent()) {
- dialpadIndex = extraMap.get().getDialpadIndex(ch);
+ /**
+ * Get the index of the key on the dialpad which the character corresponds to.
+ *
+ * The provided character is expected to be a normalized character. See {@link
+ * SmartDialMap#normalizeCharacter(char)} for details.
+ *
+ *
If the provided character can't be mapped to a key on the dialpad, return -1.
+ */
+ public static byte getDialpadIndex(Context context, char ch) {
+ Optional dialpadIndex = DEFAULT_MAP.getDialpadIndex(ch);
+ if (dialpadIndex.isPresent()) {
+ return dialpadIndex.get();
+ }
+
+ Optional extraMap = getExtraMap(context);
+ if (extraMap.isPresent()) {
+ dialpadIndex = extraMap.get().getDialpadIndex(ch);
+ }
+
+ return dialpadIndex.isPresent() ? dialpadIndex.get() : -1;
}
- return dialpadIndex.isPresent() ? dialpadIndex.get() : -1;
- }
-
- /**
- * Get the actual numeric character on the dialpad which the character corresponds to.
- *
- * The provided character is expected to be a normalized character. See {@link
- * SmartDialMap#normalizeCharacter(char)} for details.
- *
- *
If the provided character can't be mapped to a key on the dialpad, return the character.
- */
- public static char getDialpadNumericCharacter(Context context, char ch) {
- Optional dialpadNumericChar = DEFAULT_MAP.getDialpadNumericCharacter(ch);
- if (dialpadNumericChar.isPresent()) {
- return dialpadNumericChar.get();
+ /**
+ * Get the actual numeric character on the dialpad which the character corresponds to.
+ *
+ * The provided character is expected to be a normalized character. See {@link
+ * SmartDialMap#normalizeCharacter(char)} for details.
+ *
+ *
If the provided character can't be mapped to a key on the dialpad, return the character.
+ */
+ public static char getDialpadNumericCharacter(Context context, char ch) {
+ Optional dialpadNumericChar = DEFAULT_MAP.getDialpadNumericCharacter(ch);
+ if (dialpadNumericChar.isPresent()) {
+ return dialpadNumericChar.get();
+ }
+
+ Optional extraMap = getExtraMap(context);
+ if (extraMap.isPresent()) {
+ dialpadNumericChar = extraMap.get().getDialpadNumericCharacter(ch);
+ }
+
+ return dialpadNumericChar.isPresent() ? dialpadNumericChar.get() : ch;
}
- Optional extraMap = getExtraMap(context);
- if (extraMap.isPresent()) {
- dialpadNumericChar = extraMap.get().getDialpadNumericCharacter(ch);
+ /**
+ * Converts uppercase characters to lower case ones, and on a best effort basis, strips accents
+ * from accented characters.
+ *
+ * If the provided character can't be mapped to a key on the dialpad, return the character.
+ */
+ public static char normalizeCharacter(Context context, char ch) {
+ Optional normalizedChar = DEFAULT_MAP.normalizeCharacter(ch);
+ if (normalizedChar.isPresent()) {
+ return normalizedChar.get();
+ }
+
+ Optional extraMap = getExtraMap(context);
+ if (extraMap.isPresent()) {
+ normalizedChar = extraMap.get().normalizeCharacter(ch);
+ }
+
+ return normalizedChar.isPresent() ? normalizedChar.get() : ch;
}
- return dialpadNumericChar.isPresent() ? dialpadNumericChar.get() : ch;
- }
-
- /**
- * Converts uppercase characters to lower case ones, and on a best effort basis, strips accents
- * from accented characters.
- *
- * If the provided character can't be mapped to a key on the dialpad, return the character.
- */
- public static char normalizeCharacter(Context context, char ch) {
- Optional normalizedChar = DEFAULT_MAP.normalizeCharacter(ch);
- if (normalizedChar.isPresent()) {
- return normalizedChar.get();
- }
-
- Optional extraMap = getExtraMap(context);
- if (extraMap.isPresent()) {
- normalizedChar = extraMap.get().normalizeCharacter(ch);
+ @VisibleForTesting
+ static Optional getExtraMap(Context context) {
+ String languageCode = LocaleUtils.getLocale(context).getISO3Language();
+ return EXTRA_MAPS.containsKey(languageCode)
+ ? Optional.of(EXTRA_MAPS.get(languageCode))
+ : Optional.absent();
}
-
- return normalizedChar.isPresent() ? normalizedChar.get() : ch;
- }
-
- @VisibleForTesting
- static Optional getExtraMap(Context context) {
- String languageCode = LocaleUtils.getLocale(context).getISO3Language();
- return EXTRA_MAPS.containsKey(languageCode)
- ? Optional.of(EXTRA_MAPS.get(languageCode))
- : Optional.absent();
- }
}
diff --git a/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/ContactMatch.java b/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/ContactMatch.java
index b830a3b..41f623e 100644
--- a/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/ContactMatch.java
+++ b/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/ContactMatch.java
@@ -7,28 +7,28 @@
*/
public class ContactMatch {
- private final String lookupKey;
- private final long id;
+ private final String lookupKey;
+ private final long id;
- public ContactMatch(String lookupKey, long id) {
- this.lookupKey = lookupKey;
- this.id = id;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(lookupKey, id);
- }
+ public ContactMatch(String lookupKey, long id) {
+ this.lookupKey = lookupKey;
+ this.id = id;
+ }
- @Override
- public boolean equals(Object object) {
- if (this == object) {
- return true;
+ @Override
+ public int hashCode() {
+ return Objects.hash(lookupKey, id);
}
- if (object instanceof ContactMatch) {
- final ContactMatch that = (ContactMatch) object;
- return Objects.equals(this.lookupKey, that.lookupKey) && Objects.equals(this.id, that.id);
+
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (object instanceof ContactMatch) {
+ final ContactMatch that = (ContactMatch) object;
+ return Objects.equals(this.lookupKey, that.lookupKey) && Objects.equals(this.id, that.id);
+ }
+ return false;
}
- return false;
- }
}
\ No newline at end of file
diff --git a/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/DialpadCharMappings.java b/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/DialpadCharMappings.java
index 695edd8..ced5f49 100644
--- a/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/DialpadCharMappings.java
+++ b/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/DialpadCharMappings.java
@@ -26,211 +26,211 @@
*/
public class DialpadCharMappings {
- // A map in which each key is an ISO 639-2 language code and the corresponding value is a
- // character-key map.
- private static final SimpleArrayMap>
- CHAR_TO_KEY_MAPS = new SimpleArrayMap<>();
- // A map in which each key is an ISO 639-2 language code and the corresponding value is an array
- // defining a key-characters map.
- private static final SimpleArrayMap KEY_TO_CHAR_MAPS = new SimpleArrayMap<>();
-
- static {
- CHAR_TO_KEY_MAPS.put("bul", Bul.CHAR_TO_KEY);
- CHAR_TO_KEY_MAPS.put("rus", Rus.CHAR_TO_KEY);
- CHAR_TO_KEY_MAPS.put("ukr", Ukr.CHAR_TO_KEY);
-
- KEY_TO_CHAR_MAPS.put("bul", Bul.KEY_TO_CHARS);
- KEY_TO_CHAR_MAPS.put("rus", Rus.KEY_TO_CHARS);
- KEY_TO_CHAR_MAPS.put("ukr", Ukr.KEY_TO_CHARS);
- }
-
- /**
- * Returns the character-key map of the ISO 639-2 language code of the 1st language preference or
- * null if no character-key map for the language code is defined.
- */
- public static SimpleArrayMap getCharToKeyMap(@NonNull Context context) {
- return CHAR_TO_KEY_MAPS.get(LocaleUtils.getLocale(context).getISO3Language());
- }
-
- /**
- * Returns the character-key map of the provided ISO 639-2 language code.
- *
- * Note: this method is for implementations of {@link
- * com.android.dialer.smartdial.map.SmartDialMap} only. {@link #getCharToKeyMap(Context)} should
- * be used for all other purposes.
- *
- *
It is the caller's responsibility to ensure the language code is valid and a character
- * mapping is defined for that language. Otherwise, an exception will be thrown.
- */
- public static SimpleArrayMap getCharToKeyMap(String languageCode) {
- SimpleArrayMap charToKeyMap = CHAR_TO_KEY_MAPS.get(languageCode);
-
- return charToKeyMap;
- }
-
- /**
- * Returns the default character-key map (the one that uses the Latin alphabet).
- */
- public static SimpleArrayMap getDefaultCharToKeyMap() {
- return Latin.CHAR_TO_KEY;
- }
-
- /**
- * Returns the default key-characters map (the one that uses the Latin alphabet).
- */
- public static String[] getDefaultKeyToCharsMap() {
- return Latin.KEY_TO_CHARS;
- }
-
- /**
- * Given a array representing a key-characters map, return its reverse map.
- *
- * It is the caller's responsibility to ensure that
- *
- *
- * - the array contains only 12 elements,
- *
- the 0th element ~ the 9th element are the mappings for keys "0" ~ "9",
- *
- the 10th element is for key "*", and
- *
- the 11th element is for key "#".
- *
- *
- * @param keyToChars An array representing a key-characters map. It must satisfy the conditions
- * above.
- * @return A character-key map.
- */
- private static SimpleArrayMap getCharToKeyMap(
- @NonNull String[] keyToChars) {
- //Assert.checkArgument(keyToChars.length == 12);
-
- SimpleArrayMap charToKeyMap = new SimpleArrayMap<>();
-
- for (int keyIndex = 0; keyIndex < keyToChars.length; keyIndex++) {
- String chars = keyToChars[keyIndex];
-
- for (int j = 0; j < chars.length(); j++) {
- char c = chars.charAt(j);
- if (Character.isAlphabetic(c)) {
- charToKeyMap.put(Character.toLowerCase(c), getKeyChar(keyIndex));
+ // A map in which each key is an ISO 639-2 language code and the corresponding value is a
+ // character-key map.
+ private static final SimpleArrayMap>
+ CHAR_TO_KEY_MAPS = new SimpleArrayMap<>();
+ // A map in which each key is an ISO 639-2 language code and the corresponding value is an array
+ // defining a key-characters map.
+ private static final SimpleArrayMap KEY_TO_CHAR_MAPS = new SimpleArrayMap<>();
+
+ static {
+ CHAR_TO_KEY_MAPS.put("bul", Bul.CHAR_TO_KEY);
+ CHAR_TO_KEY_MAPS.put("rus", Rus.CHAR_TO_KEY);
+ CHAR_TO_KEY_MAPS.put("ukr", Ukr.CHAR_TO_KEY);
+
+ KEY_TO_CHAR_MAPS.put("bul", Bul.KEY_TO_CHARS);
+ KEY_TO_CHAR_MAPS.put("rus", Rus.KEY_TO_CHARS);
+ KEY_TO_CHAR_MAPS.put("ukr", Ukr.KEY_TO_CHARS);
+ }
+
+ /**
+ * Returns the character-key map of the ISO 639-2 language code of the 1st language preference or
+ * null if no character-key map for the language code is defined.
+ */
+ public static SimpleArrayMap getCharToKeyMap(@NonNull Context context) {
+ return CHAR_TO_KEY_MAPS.get(LocaleUtils.getLocale(context).getISO3Language());
+ }
+
+ /**
+ * Returns the character-key map of the provided ISO 639-2 language code.
+ *
+ * Note: this method is for implementations of {@link
+ * com.android.dialer.smartdial.map.SmartDialMap} only. {@link #getCharToKeyMap(Context)} should
+ * be used for all other purposes.
+ *
+ *
It is the caller's responsibility to ensure the language code is valid and a character
+ * mapping is defined for that language. Otherwise, an exception will be thrown.
+ */
+ public static SimpleArrayMap getCharToKeyMap(String languageCode) {
+ SimpleArrayMap charToKeyMap = CHAR_TO_KEY_MAPS.get(languageCode);
+
+ return charToKeyMap;
+ }
+
+ /**
+ * Returns the default character-key map (the one that uses the Latin alphabet).
+ */
+ public static SimpleArrayMap getDefaultCharToKeyMap() {
+ return Latin.CHAR_TO_KEY;
+ }
+
+ /**
+ * Returns the default key-characters map (the one that uses the Latin alphabet).
+ */
+ public static String[] getDefaultKeyToCharsMap() {
+ return Latin.KEY_TO_CHARS;
+ }
+
+ /**
+ * Given a array representing a key-characters map, return its reverse map.
+ *
+ * It is the caller's responsibility to ensure that
+ *
+ *
+ * - the array contains only 12 elements,
+ *
- the 0th element ~ the 9th element are the mappings for keys "0" ~ "9",
+ *
- the 10th element is for key "*", and
+ *
- the 11th element is for key "#".
+ *
+ *
+ * @param keyToChars An array representing a key-characters map. It must satisfy the conditions
+ * above.
+ * @return A character-key map.
+ */
+ private static SimpleArrayMap getCharToKeyMap(
+ @NonNull String[] keyToChars) {
+ //Assert.checkArgument(keyToChars.length == 12);
+
+ SimpleArrayMap charToKeyMap = new SimpleArrayMap<>();
+
+ for (int keyIndex = 0; keyIndex < keyToChars.length; keyIndex++) {
+ String chars = keyToChars[keyIndex];
+
+ for (int j = 0; j < chars.length(); j++) {
+ char c = chars.charAt(j);
+ if (Character.isAlphabetic(c)) {
+ charToKeyMap.put(Character.toLowerCase(c), getKeyChar(keyIndex));
+ }
+ }
}
- }
+
+ return charToKeyMap;
}
- return charToKeyMap;
- }
-
- /**
- * Given a key index of the dialpad, returns the corresponding character.
- */
- private static char getKeyChar(int keyIndex) {
- //Assert.checkArgument(0 <= keyIndex && keyIndex <= 11);
-
- switch (keyIndex) {
- case 10:
- return '*';
- case 11:
- return '#';
- default:
- return (char) ('0' + keyIndex);
+ /**
+ * Given a key index of the dialpad, returns the corresponding character.
+ */
+ private static char getKeyChar(int keyIndex) {
+ //Assert.checkArgument(0 <= keyIndex && keyIndex <= 11);
+
+ switch (keyIndex) {
+ case 10:
+ return '*';
+ case 11:
+ return '#';
+ default:
+ return (char) ('0' + keyIndex);
+ }
}
- }
-
- /**
- * The character mapping for the Latin alphabet (the default mapping)
- */
- private static class Latin {
- private static final String[] KEY_TO_CHARS = {
- "+" /* 0 */,
- "" /* 1 */,
- "ABC" /* 2 */,
- "DEF" /* 3 */,
- "GHI" /* 4 */,
- "JKL" /* 5 */,
- "MNO" /* 6 */,
- "PQRS" /* 7 */,
- "TUV" /* 8 */,
- "WXYZ" /* 9 */,
- "" /* * */,
- "" /* # */,
- };
-
- private static final SimpleArrayMap CHAR_TO_KEY =
- getCharToKeyMap(KEY_TO_CHARS);
- }
-
- /**
- * Returns the key-characters map of the given ISO 639-2 language code of the 1st language
- * preference or null if no key-characters map for the language code is defined.
- */
+
+ /**
+ * The character mapping for the Latin alphabet (the default mapping)
+ */
+ private static class Latin {
+ private static final String[] KEY_TO_CHARS = {
+ "+" /* 0 */,
+ "" /* 1 */,
+ "ABC" /* 2 */,
+ "DEF" /* 3 */,
+ "GHI" /* 4 */,
+ "JKL" /* 5 */,
+ "MNO" /* 6 */,
+ "PQRS" /* 7 */,
+ "TUV" /* 8 */,
+ "WXYZ" /* 9 */,
+ "" /* * */,
+ "" /* # */,
+ };
+
+ private static final SimpleArrayMap CHAR_TO_KEY =
+ getCharToKeyMap(KEY_TO_CHARS);
+ }
+
+ /**
+ * Returns the key-characters map of the given ISO 639-2 language code of the 1st language
+ * preference or null if no key-characters map for the language code is defined.
+ */
/*static String[] getKeyToCharsMap(@NonNull Context context) {
return KEY_TO_CHAR_MAPS.get(LocaleUtils.getLocale(context).getISO3Language());
}*/
- /**
- * The character mapping for the Bulgarian alphabet
- */
- private static class Bul {
- private static final String[] KEY_TO_CHARS = {
- "" /* 0 */,
- "" /* 1 */,
- "АБВГ" /* 2 */,
- "ДЕЖЗ" /* 3 */,
- "ИЙКЛ" /* 4 */,
- "МНО" /* 5 */,
- "ПРС" /* 6 */,
- "ТУФХ" /* 7 */,
- "ЦЧШЩ" /* 8 */,
- "ЪЬЮЯ" /* 9 */,
- "" /* * */,
- "" /* # */,
- };
-
- private static final SimpleArrayMap CHAR_TO_KEY =
- getCharToKeyMap(KEY_TO_CHARS);
- }
-
- /**
- * The character mapping for the Russian alphabet
- */
- private static class Rus {
- private static final String[] KEY_TO_CHARS = {
- "" /* 0 */,
- "" /* 1 */,
- "АБВГ" /* 2 */,
- "ДЕЁЖЗ" /* 3 */,
- "ИЙКЛ" /* 4 */,
- "МНОП" /* 5 */,
- "РСТУ" /* 6 */,
- "ФХЦЧ" /* 7 */,
- "ШЩЪЫ" /* 8 */,
- "ЬЭЮЯ" /* 9 */,
- "" /* * */,
- "" /* # */,
- };
-
- private static final SimpleArrayMap CHAR_TO_KEY =
- getCharToKeyMap(KEY_TO_CHARS);
- }
-
- /**
- * The character mapping for the Ukrainian alphabet
- */
- private static class Ukr {
- private static final String[] KEY_TO_CHARS = {
- "" /* 0 */,
- "" /* 1 */,
- "АБВГҐ" /* 2 */,
- "ДЕЄЖЗ" /* 3 */,
- "ИІЇЙКЛ" /* 4 */,
- "МНОП" /* 5 */,
- "РСТУ" /* 6 */,
- "ФХЦЧ" /* 7 */,
- "ШЩ" /* 8 */,
- "ЬЮЯ" /* 9 */,
- "" /* * */,
- "" /* # */,
- };
-
- private static final SimpleArrayMap CHAR_TO_KEY =
- getCharToKeyMap(KEY_TO_CHARS);
- }
+ /**
+ * The character mapping for the Bulgarian alphabet
+ */
+ private static class Bul {
+ private static final String[] KEY_TO_CHARS = {
+ "" /* 0 */,
+ "" /* 1 */,
+ "АБВГ" /* 2 */,
+ "ДЕЖЗ" /* 3 */,
+ "ИЙКЛ" /* 4 */,
+ "МНО" /* 5 */,
+ "ПРС" /* 6 */,
+ "ТУФХ" /* 7 */,
+ "ЦЧШЩ" /* 8 */,
+ "ЪЬЮЯ" /* 9 */,
+ "" /* * */,
+ "" /* # */,
+ };
+
+ private static final SimpleArrayMap CHAR_TO_KEY =
+ getCharToKeyMap(KEY_TO_CHARS);
+ }
+
+ /**
+ * The character mapping for the Russian alphabet
+ */
+ private static class Rus {
+ private static final String[] KEY_TO_CHARS = {
+ "" /* 0 */,
+ "" /* 1 */,
+ "АБВГ" /* 2 */,
+ "ДЕЁЖЗ" /* 3 */,
+ "ИЙКЛ" /* 4 */,
+ "МНОП" /* 5 */,
+ "РСТУ" /* 6 */,
+ "ФХЦЧ" /* 7 */,
+ "ШЩЪЫ" /* 8 */,
+ "ЬЭЮЯ" /* 9 */,
+ "" /* * */,
+ "" /* # */,
+ };
+
+ private static final SimpleArrayMap CHAR_TO_KEY =
+ getCharToKeyMap(KEY_TO_CHARS);
+ }
+
+ /**
+ * The character mapping for the Ukrainian alphabet
+ */
+ private static class Ukr {
+ private static final String[] KEY_TO_CHARS = {
+ "" /* 0 */,
+ "" /* 1 */,
+ "АБВГҐ" /* 2 */,
+ "ДЕЄЖЗ" /* 3 */,
+ "ИІЇЙКЛ" /* 4 */,
+ "МНОП" /* 5 */,
+ "РСТУ" /* 6 */,
+ "ФХЦЧ" /* 7 */,
+ "ШЩ" /* 8 */,
+ "ЬЮЯ" /* 9 */,
+ "" /* * */,
+ "" /* # */,
+ };
+
+ private static final SimpleArrayMap CHAR_TO_KEY =
+ getCharToKeyMap(KEY_TO_CHARS);
+ }
}
diff --git a/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/LatinSmartDialMap.java b/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/LatinSmartDialMap.java
index 01217cb..feb8853 100644
--- a/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/LatinSmartDialMap.java
+++ b/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/LatinSmartDialMap.java
@@ -26,726 +26,726 @@
@SuppressWarnings("Guava")
final class LatinSmartDialMap extends SmartDialMap {
- private static LatinSmartDialMap instance;
+ private static LatinSmartDialMap instance;
- private LatinSmartDialMap() {
- }
-
- static LatinSmartDialMap getInstance() {
- if (instance == null) {
- instance = new LatinSmartDialMap();
+ private LatinSmartDialMap() {
}
- return instance;
- }
+ static LatinSmartDialMap getInstance() {
+ if (instance == null) {
+ instance = new LatinSmartDialMap();
+ }
- /*
- * The switch statement in this function was generated using the python code:
- * from unidecode import unidecode
- * for i in range(192, 564):
- * char = unichr(i)
- * decoded = unidecode(char)
- * # Unicode characters that decompose into multiple characters i.e.
- * # into ss are not supported for now
- * if (len(decoded) == 1 and decoded.isalpha()):
- * print "case '" + char + "': return Optional.of('" + unidecode(char) + "');"
- *
- * This gives us a way to map characters containing accents/diacritics to their
- * alphabetic equivalents. The unidecode library can be found at:
- * http://pypi.python.org/pypi/Unidecode/0.04.1
- *
- * Also remaps all upper case latin characters to their lower case equivalents.
- */
- @Override
- Optional normalizeCharacter(char ch) {
- if (isValidDialpadAlphabeticChar(ch)) {
- return Optional.of(ch);
+ return instance;
}
- switch (ch) {
- case 'À':
- return Optional.of('a');
- case 'Á':
- return Optional.of('a');
- case 'Â':
- return Optional.of('a');
- case 'Ã':
- return Optional.of('a');
- case 'Ä':
- return Optional.of('a');
- case 'Å':
- return Optional.of('a');
- case 'Ç':
- return Optional.of('c');
- case 'È':
- return Optional.of('e');
- case 'É':
- return Optional.of('e');
- case 'Ê':
- return Optional.of('e');
- case 'Ë':
- return Optional.of('e');
- case 'Ì':
- return Optional.of('i');
- case 'Í':
- return Optional.of('i');
- case 'Î':
- return Optional.of('i');
- case 'Ï':
- return Optional.of('i');
- case 'Ð':
- return Optional.of('d');
- case 'Ñ':
- return Optional.of('n');
- case 'Ò':
- return Optional.of('o');
- case 'Ó':
- return Optional.of('o');
- case 'Ô':
- return Optional.of('o');
- case 'Õ':
- return Optional.of('o');
- case 'Ö':
- return Optional.of('o');
- case '×':
- return Optional.of('x');
- case 'Ø':
- return Optional.of('o');
- case 'Ù':
- return Optional.of('u');
- case 'Ú':
- return Optional.of('u');
- case 'Û':
- return Optional.of('u');
- case 'Ü':
- return Optional.of('u');
- case 'Ý':
- return Optional.of('u');
- case 'à':
- return Optional.of('a');
- case 'á':
- return Optional.of('a');
- case 'â':
- return Optional.of('a');
- case 'ã':
- return Optional.of('a');
- case 'ä':
- return Optional.of('a');
- case 'å':
- return Optional.of('a');
- case 'ç':
- return Optional.of('c');
- case 'è':
- return Optional.of('e');
- case 'é':
- return Optional.of('e');
- case 'ê':
- return Optional.of('e');
- case 'ë':
- return Optional.of('e');
- case 'ì':
- return Optional.of('i');
- case 'í':
- return Optional.of('i');
- case 'î':
- return Optional.of('i');
- case 'ï':
- return Optional.of('i');
- case 'ð':
- return Optional.of('d');
- case 'ñ':
- return Optional.of('n');
- case 'ò':
- return Optional.of('o');
- case 'ó':
- return Optional.of('o');
- case 'ô':
- return Optional.of('o');
- case 'õ':
- return Optional.of('o');
- case 'ö':
- return Optional.of('o');
- case 'ø':
- return Optional.of('o');
- case 'ù':
- return Optional.of('u');
- case 'ú':
- return Optional.of('u');
- case 'û':
- return Optional.of('u');
- case 'ü':
- return Optional.of('u');
- case 'ý':
- return Optional.of('y');
- case 'ÿ':
- return Optional.of('y');
- case 'Ā':
- return Optional.of('a');
- case 'ā':
- return Optional.of('a');
- case 'Ă':
- return Optional.of('a');
- case 'ă':
- return Optional.of('a');
- case 'Ą':
- return Optional.of('a');
- case 'ą':
- return Optional.of('a');
- case 'Ć':
- return Optional.of('c');
- case 'ć':
- return Optional.of('c');
- case 'Ĉ':
- return Optional.of('c');
- case 'ĉ':
- return Optional.of('c');
- case 'Ċ':
- return Optional.of('c');
- case 'ċ':
- return Optional.of('c');
- case 'Č':
- return Optional.of('c');
- case 'č':
- return Optional.of('c');
- case 'Ď':
- return Optional.of('d');
- case 'ď':
- return Optional.of('d');
- case 'Đ':
- return Optional.of('d');
- case 'đ':
- return Optional.of('d');
- case 'Ē':
- return Optional.of('e');
- case 'ē':
- return Optional.of('e');
- case 'Ĕ':
- return Optional.of('e');
- case 'ĕ':
- return Optional.of('e');
- case 'Ė':
- return Optional.of('e');
- case 'ė':
- return Optional.of('e');
- case 'Ę':
- return Optional.of('e');
- case 'ę':
- return Optional.of('e');
- case 'Ě':
- return Optional.of('e');
- case 'ě':
- return Optional.of('e');
- case 'Ĝ':
- return Optional.of('g');
- case 'ĝ':
- return Optional.of('g');
- case 'Ğ':
- return Optional.of('g');
- case 'ğ':
- return Optional.of('g');
- case 'Ġ':
- return Optional.of('g');
- case 'ġ':
- return Optional.of('g');
- case 'Ģ':
- return Optional.of('g');
- case 'ģ':
- return Optional.of('g');
- case 'Ĥ':
- return Optional.of('h');
- case 'ĥ':
- return Optional.of('h');
- case 'Ħ':
- return Optional.of('h');
- case 'ħ':
- return Optional.of('h');
- case 'Ĩ':
- return Optional.of('i');
- case 'ĩ':
- return Optional.of('i');
- case 'Ī':
- return Optional.of('i');
- case 'ī':
- return Optional.of('i');
- case 'Ĭ':
- return Optional.of('i');
- case 'ĭ':
- return Optional.of('i');
- case 'Į':
- return Optional.of('i');
- case 'į':
- return Optional.of('i');
- case 'İ':
- return Optional.of('i');
- case 'ı':
- return Optional.of('i');
- case 'Ĵ':
- return Optional.of('j');
- case 'ĵ':
- return Optional.of('j');
- case 'Ķ':
- return Optional.of('k');
- case 'ķ':
- return Optional.of('k');
- case 'ĸ':
- return Optional.of('k');
- case 'Ĺ':
- return Optional.of('l');
- case 'ĺ':
- return Optional.of('l');
- case 'Ļ':
- return Optional.of('l');
- case 'ļ':
- return Optional.of('l');
- case 'Ľ':
- return Optional.of('l');
- case 'ľ':
- return Optional.of('l');
- case 'Ŀ':
- return Optional.of('l');
- case 'ŀ':
- return Optional.of('l');
- case 'Ł':
- return Optional.of('l');
- case 'ł':
- return Optional.of('l');
- case 'Ń':
- return Optional.of('n');
- case 'ń':
- return Optional.of('n');
- case 'Ņ':
- return Optional.of('n');
- case 'ņ':
- return Optional.of('n');
- case 'Ň':
- return Optional.of('n');
- case 'ň':
- return Optional.of('n');
- case 'Ō':
- return Optional.of('o');
- case 'ō':
- return Optional.of('o');
- case 'Ŏ':
- return Optional.of('o');
- case 'ŏ':
- return Optional.of('o');
- case 'Ő':
- return Optional.of('o');
- case 'ő':
- return Optional.of('o');
- case 'Ŕ':
- return Optional.of('r');
- case 'ŕ':
- return Optional.of('r');
- case 'Ŗ':
- return Optional.of('r');
- case 'ŗ':
- return Optional.of('r');
- case 'Ř':
- return Optional.of('r');
- case 'ř':
- return Optional.of('r');
- case 'Ś':
- return Optional.of('s');
- case 'ś':
- return Optional.of('s');
- case 'Ŝ':
- return Optional.of('s');
- case 'ŝ':
- return Optional.of('s');
- case 'Ş':
- return Optional.of('s');
- case 'ş':
- return Optional.of('s');
- case 'Š':
- return Optional.of('s');
- case 'š':
- return Optional.of('s');
- case 'Ţ':
- return Optional.of('t');
- case 'ţ':
- return Optional.of('t');
- case 'Ť':
- return Optional.of('t');
- case 'ť':
- return Optional.of('t');
- case 'Ŧ':
- return Optional.of('t');
- case 'ŧ':
- return Optional.of('t');
- case 'Ũ':
- return Optional.of('u');
- case 'ũ':
- return Optional.of('u');
- case 'Ū':
- return Optional.of('u');
- case 'ū':
- return Optional.of('u');
- case 'Ŭ':
- return Optional.of('u');
- case 'ŭ':
- return Optional.of('u');
- case 'Ů':
- return Optional.of('u');
- case 'ů':
- return Optional.of('u');
- case 'Ű':
- return Optional.of('u');
- case 'ű':
- return Optional.of('u');
- case 'Ų':
- return Optional.of('u');
- case 'ų':
- return Optional.of('u');
- case 'Ŵ':
- return Optional.of('w');
- case 'ŵ':
- return Optional.of('w');
- case 'Ŷ':
- return Optional.of('y');
- case 'ŷ':
- return Optional.of('y');
- case 'Ÿ':
- return Optional.of('y');
- case 'Ź':
- return Optional.of('z');
- case 'ź':
- return Optional.of('z');
- case 'Ż':
- return Optional.of('z');
- case 'ż':
- return Optional.of('z');
- case 'Ž':
- return Optional.of('z');
- case 'ž':
- return Optional.of('z');
- case 'ſ':
- return Optional.of('s');
- case 'ƀ':
- return Optional.of('b');
- case 'Ɓ':
- return Optional.of('b');
- case 'Ƃ':
- return Optional.of('b');
- case 'ƃ':
- return Optional.of('b');
- case 'Ɔ':
- return Optional.of('o');
- case 'Ƈ':
- return Optional.of('c');
- case 'ƈ':
- return Optional.of('c');
- case 'Ɖ':
- return Optional.of('d');
- case 'Ɗ':
- return Optional.of('d');
- case 'Ƌ':
- return Optional.of('d');
- case 'ƌ':
- return Optional.of('d');
- case 'ƍ':
- return Optional.of('d');
- case 'Ɛ':
- return Optional.of('e');
- case 'Ƒ':
- return Optional.of('f');
- case 'ƒ':
- return Optional.of('f');
- case 'Ɠ':
- return Optional.of('g');
- case 'Ɣ':
- return Optional.of('g');
- case 'Ɩ':
- return Optional.of('i');
- case 'Ɨ':
- return Optional.of('i');
- case 'Ƙ':
- return Optional.of('k');
- case 'ƙ':
- return Optional.of('k');
- case 'ƚ':
- return Optional.of('l');
- case 'ƛ':
- return Optional.of('l');
- case 'Ɯ':
- return Optional.of('w');
- case 'Ɲ':
- return Optional.of('n');
- case 'ƞ':
- return Optional.of('n');
- case 'Ɵ':
- return Optional.of('o');
- case 'Ơ':
- return Optional.of('o');
- case 'ơ':
- return Optional.of('o');
- case 'Ƥ':
- return Optional.of('p');
- case 'ƥ':
- return Optional.of('p');
- case 'ƫ':
- return Optional.of('t');
- case 'Ƭ':
- return Optional.of('t');
- case 'ƭ':
- return Optional.of('t');
- case 'Ʈ':
- return Optional.of('t');
- case 'Ư':
- return Optional.of('u');
- case 'ư':
- return Optional.of('u');
- case 'Ʊ':
- return Optional.of('y');
- case 'Ʋ':
- return Optional.of('v');
- case 'Ƴ':
- return Optional.of('y');
- case 'ƴ':
- return Optional.of('y');
- case 'Ƶ':
- return Optional.of('z');
- case 'ƶ':
- return Optional.of('z');
- case 'ƿ':
- return Optional.of('w');
- case 'Ǎ':
- return Optional.of('a');
- case 'ǎ':
- return Optional.of('a');
- case 'Ǐ':
- return Optional.of('i');
- case 'ǐ':
- return Optional.of('i');
- case 'Ǒ':
- return Optional.of('o');
- case 'ǒ':
- return Optional.of('o');
- case 'Ǔ':
- return Optional.of('u');
- case 'ǔ':
- return Optional.of('u');
- case 'Ǖ':
- return Optional.of('u');
- case 'ǖ':
- return Optional.of('u');
- case 'Ǘ':
- return Optional.of('u');
- case 'ǘ':
- return Optional.of('u');
- case 'Ǚ':
- return Optional.of('u');
- case 'ǚ':
- return Optional.of('u');
- case 'Ǜ':
- return Optional.of('u');
- case 'ǜ':
- return Optional.of('u');
- case 'Ǟ':
- return Optional.of('a');
- case 'ǟ':
- return Optional.of('a');
- case 'Ǡ':
- return Optional.of('a');
- case 'ǡ':
- return Optional.of('a');
- case 'Ǥ':
- return Optional.of('g');
- case 'ǥ':
- return Optional.of('g');
- case 'Ǧ':
- return Optional.of('g');
- case 'ǧ':
- return Optional.of('g');
- case 'Ǩ':
- return Optional.of('k');
- case 'ǩ':
- return Optional.of('k');
- case 'Ǫ':
- return Optional.of('o');
- case 'ǫ':
- return Optional.of('o');
- case 'Ǭ':
- return Optional.of('o');
- case 'ǭ':
- return Optional.of('o');
- case 'ǰ':
- return Optional.of('j');
- case 'Dz':
- return Optional.of('d');
- case 'Ǵ':
- return Optional.of('g');
- case 'ǵ':
- return Optional.of('g');
- case 'Ƿ':
- return Optional.of('w');
- case 'Ǹ':
- return Optional.of('n');
- case 'ǹ':
- return Optional.of('n');
- case 'Ǻ':
- return Optional.of('a');
- case 'ǻ':
- return Optional.of('a');
- case 'Ǿ':
- return Optional.of('o');
- case 'ǿ':
- return Optional.of('o');
- case 'Ȁ':
- return Optional.of('a');
- case 'ȁ':
- return Optional.of('a');
- case 'Ȃ':
- return Optional.of('a');
- case 'ȃ':
- return Optional.of('a');
- case 'Ȅ':
- return Optional.of('e');
- case 'ȅ':
- return Optional.of('e');
- case 'Ȇ':
- return Optional.of('e');
- case 'ȇ':
- return Optional.of('e');
- case 'Ȉ':
- return Optional.of('i');
- case 'ȉ':
- return Optional.of('i');
- case 'Ȋ':
- return Optional.of('i');
- case 'ȋ':
- return Optional.of('i');
- case 'Ȍ':
- return Optional.of('o');
- case 'ȍ':
- return Optional.of('o');
- case 'Ȏ':
- return Optional.of('o');
- case 'ȏ':
- return Optional.of('o');
- case 'Ȑ':
- return Optional.of('r');
- case 'ȑ':
- return Optional.of('r');
- case 'Ȓ':
- return Optional.of('r');
- case 'ȓ':
- return Optional.of('r');
- case 'Ȕ':
- return Optional.of('u');
- case 'ȕ':
- return Optional.of('u');
- case 'Ȗ':
- return Optional.of('u');
- case 'ȗ':
- return Optional.of('u');
- case 'Ș':
- return Optional.of('s');
- case 'ș':
- return Optional.of('s');
- case 'Ț':
- return Optional.of('t');
- case 'ț':
- return Optional.of('t');
- case 'Ȝ':
- return Optional.of('y');
- case 'ȝ':
- return Optional.of('y');
- case 'Ȟ':
- return Optional.of('h');
- case 'ȟ':
- return Optional.of('h');
- case 'Ȥ':
- return Optional.of('z');
- case 'ȥ':
- return Optional.of('z');
- case 'Ȧ':
- return Optional.of('a');
- case 'ȧ':
- return Optional.of('a');
- case 'Ȩ':
- return Optional.of('e');
- case 'ȩ':
- return Optional.of('e');
- case 'Ȫ':
- return Optional.of('o');
- case 'ȫ':
- return Optional.of('o');
- case 'Ȭ':
- return Optional.of('o');
- case 'ȭ':
- return Optional.of('o');
- case 'Ȯ':
- return Optional.of('o');
- case 'ȯ':
- return Optional.of('o');
- case 'Ȱ':
- return Optional.of('o');
- case 'ȱ':
- return Optional.of('o');
- case 'Ȳ':
- return Optional.of('y');
- case 'ȳ':
- return Optional.of('y');
- case 'A':
- return Optional.of('a');
- case 'B':
- return Optional.of('b');
- case 'C':
- return Optional.of('c');
- case 'D':
- return Optional.of('d');
- case 'E':
- return Optional.of('e');
- case 'F':
- return Optional.of('f');
- case 'G':
- return Optional.of('g');
- case 'H':
- return Optional.of('h');
- case 'I':
- return Optional.of('i');
- case 'J':
- return Optional.of('j');
- case 'K':
- return Optional.of('k');
- case 'L':
- return Optional.of('l');
- case 'M':
- return Optional.of('m');
- case 'N':
- return Optional.of('n');
- case 'O':
- return Optional.of('o');
- case 'P':
- return Optional.of('p');
- case 'Q':
- return Optional.of('q');
- case 'R':
- return Optional.of('r');
- case 'S':
- return Optional.of('s');
- case 'T':
- return Optional.of('t');
- case 'U':
- return Optional.of('u');
- case 'V':
- return Optional.of('v');
- case 'W':
- return Optional.of('w');
- case 'X':
- return Optional.of('x');
- case 'Y':
- return Optional.of('y');
- case 'Z':
- return Optional.of('z');
- default:
- return Optional.absent();
+ /*
+ * The switch statement in this function was generated using the python code:
+ * from unidecode import unidecode
+ * for i in range(192, 564):
+ * char = unichr(i)
+ * decoded = unidecode(char)
+ * # Unicode characters that decompose into multiple characters i.e.
+ * # into ss are not supported for now
+ * if (len(decoded) == 1 and decoded.isalpha()):
+ * print "case '" + char + "': return Optional.of('" + unidecode(char) + "');"
+ *
+ * This gives us a way to map characters containing accents/diacritics to their
+ * alphabetic equivalents. The unidecode library can be found at:
+ * http://pypi.python.org/pypi/Unidecode/0.04.1
+ *
+ * Also remaps all upper case latin characters to their lower case equivalents.
+ */
+ @Override
+ Optional normalizeCharacter(char ch) {
+ if (isValidDialpadAlphabeticChar(ch)) {
+ return Optional.of(ch);
+ }
+
+ switch (ch) {
+ case 'À':
+ return Optional.of('a');
+ case 'Á':
+ return Optional.of('a');
+ case 'Â':
+ return Optional.of('a');
+ case 'Ã':
+ return Optional.of('a');
+ case 'Ä':
+ return Optional.of('a');
+ case 'Å':
+ return Optional.of('a');
+ case 'Ç':
+ return Optional.of('c');
+ case 'È':
+ return Optional.of('e');
+ case 'É':
+ return Optional.of('e');
+ case 'Ê':
+ return Optional.of('e');
+ case 'Ë':
+ return Optional.of('e');
+ case 'Ì':
+ return Optional.of('i');
+ case 'Í':
+ return Optional.of('i');
+ case 'Î':
+ return Optional.of('i');
+ case 'Ï':
+ return Optional.of('i');
+ case 'Ð':
+ return Optional.of('d');
+ case 'Ñ':
+ return Optional.of('n');
+ case 'Ò':
+ return Optional.of('o');
+ case 'Ó':
+ return Optional.of('o');
+ case 'Ô':
+ return Optional.of('o');
+ case 'Õ':
+ return Optional.of('o');
+ case 'Ö':
+ return Optional.of('o');
+ case '×':
+ return Optional.of('x');
+ case 'Ø':
+ return Optional.of('o');
+ case 'Ù':
+ return Optional.of('u');
+ case 'Ú':
+ return Optional.of('u');
+ case 'Û':
+ return Optional.of('u');
+ case 'Ü':
+ return Optional.of('u');
+ case 'Ý':
+ return Optional.of('u');
+ case 'à':
+ return Optional.of('a');
+ case 'á':
+ return Optional.of('a');
+ case 'â':
+ return Optional.of('a');
+ case 'ã':
+ return Optional.of('a');
+ case 'ä':
+ return Optional.of('a');
+ case 'å':
+ return Optional.of('a');
+ case 'ç':
+ return Optional.of('c');
+ case 'è':
+ return Optional.of('e');
+ case 'é':
+ return Optional.of('e');
+ case 'ê':
+ return Optional.of('e');
+ case 'ë':
+ return Optional.of('e');
+ case 'ì':
+ return Optional.of('i');
+ case 'í':
+ return Optional.of('i');
+ case 'î':
+ return Optional.of('i');
+ case 'ï':
+ return Optional.of('i');
+ case 'ð':
+ return Optional.of('d');
+ case 'ñ':
+ return Optional.of('n');
+ case 'ò':
+ return Optional.of('o');
+ case 'ó':
+ return Optional.of('o');
+ case 'ô':
+ return Optional.of('o');
+ case 'õ':
+ return Optional.of('o');
+ case 'ö':
+ return Optional.of('o');
+ case 'ø':
+ return Optional.of('o');
+ case 'ù':
+ return Optional.of('u');
+ case 'ú':
+ return Optional.of('u');
+ case 'û':
+ return Optional.of('u');
+ case 'ü':
+ return Optional.of('u');
+ case 'ý':
+ return Optional.of('y');
+ case 'ÿ':
+ return Optional.of('y');
+ case 'Ā':
+ return Optional.of('a');
+ case 'ā':
+ return Optional.of('a');
+ case 'Ă':
+ return Optional.of('a');
+ case 'ă':
+ return Optional.of('a');
+ case 'Ą':
+ return Optional.of('a');
+ case 'ą':
+ return Optional.of('a');
+ case 'Ć':
+ return Optional.of('c');
+ case 'ć':
+ return Optional.of('c');
+ case 'Ĉ':
+ return Optional.of('c');
+ case 'ĉ':
+ return Optional.of('c');
+ case 'Ċ':
+ return Optional.of('c');
+ case 'ċ':
+ return Optional.of('c');
+ case 'Č':
+ return Optional.of('c');
+ case 'č':
+ return Optional.of('c');
+ case 'Ď':
+ return Optional.of('d');
+ case 'ď':
+ return Optional.of('d');
+ case 'Đ':
+ return Optional.of('d');
+ case 'đ':
+ return Optional.of('d');
+ case 'Ē':
+ return Optional.of('e');
+ case 'ē':
+ return Optional.of('e');
+ case 'Ĕ':
+ return Optional.of('e');
+ case 'ĕ':
+ return Optional.of('e');
+ case 'Ė':
+ return Optional.of('e');
+ case 'ė':
+ return Optional.of('e');
+ case 'Ę':
+ return Optional.of('e');
+ case 'ę':
+ return Optional.of('e');
+ case 'Ě':
+ return Optional.of('e');
+ case 'ě':
+ return Optional.of('e');
+ case 'Ĝ':
+ return Optional.of('g');
+ case 'ĝ':
+ return Optional.of('g');
+ case 'Ğ':
+ return Optional.of('g');
+ case 'ğ':
+ return Optional.of('g');
+ case 'Ġ':
+ return Optional.of('g');
+ case 'ġ':
+ return Optional.of('g');
+ case 'Ģ':
+ return Optional.of('g');
+ case 'ģ':
+ return Optional.of('g');
+ case 'Ĥ':
+ return Optional.of('h');
+ case 'ĥ':
+ return Optional.of('h');
+ case 'Ħ':
+ return Optional.of('h');
+ case 'ħ':
+ return Optional.of('h');
+ case 'Ĩ':
+ return Optional.of('i');
+ case 'ĩ':
+ return Optional.of('i');
+ case 'Ī':
+ return Optional.of('i');
+ case 'ī':
+ return Optional.of('i');
+ case 'Ĭ':
+ return Optional.of('i');
+ case 'ĭ':
+ return Optional.of('i');
+ case 'Į':
+ return Optional.of('i');
+ case 'į':
+ return Optional.of('i');
+ case 'İ':
+ return Optional.of('i');
+ case 'ı':
+ return Optional.of('i');
+ case 'Ĵ':
+ return Optional.of('j');
+ case 'ĵ':
+ return Optional.of('j');
+ case 'Ķ':
+ return Optional.of('k');
+ case 'ķ':
+ return Optional.of('k');
+ case 'ĸ':
+ return Optional.of('k');
+ case 'Ĺ':
+ return Optional.of('l');
+ case 'ĺ':
+ return Optional.of('l');
+ case 'Ļ':
+ return Optional.of('l');
+ case 'ļ':
+ return Optional.of('l');
+ case 'Ľ':
+ return Optional.of('l');
+ case 'ľ':
+ return Optional.of('l');
+ case 'Ŀ':
+ return Optional.of('l');
+ case 'ŀ':
+ return Optional.of('l');
+ case 'Ł':
+ return Optional.of('l');
+ case 'ł':
+ return Optional.of('l');
+ case 'Ń':
+ return Optional.of('n');
+ case 'ń':
+ return Optional.of('n');
+ case 'Ņ':
+ return Optional.of('n');
+ case 'ņ':
+ return Optional.of('n');
+ case 'Ň':
+ return Optional.of('n');
+ case 'ň':
+ return Optional.of('n');
+ case 'Ō':
+ return Optional.of('o');
+ case 'ō':
+ return Optional.of('o');
+ case 'Ŏ':
+ return Optional.of('o');
+ case 'ŏ':
+ return Optional.of('o');
+ case 'Ő':
+ return Optional.of('o');
+ case 'ő':
+ return Optional.of('o');
+ case 'Ŕ':
+ return Optional.of('r');
+ case 'ŕ':
+ return Optional.of('r');
+ case 'Ŗ':
+ return Optional.of('r');
+ case 'ŗ':
+ return Optional.of('r');
+ case 'Ř':
+ return Optional.of('r');
+ case 'ř':
+ return Optional.of('r');
+ case 'Ś':
+ return Optional.of('s');
+ case 'ś':
+ return Optional.of('s');
+ case 'Ŝ':
+ return Optional.of('s');
+ case 'ŝ':
+ return Optional.of('s');
+ case 'Ş':
+ return Optional.of('s');
+ case 'ş':
+ return Optional.of('s');
+ case 'Š':
+ return Optional.of('s');
+ case 'š':
+ return Optional.of('s');
+ case 'Ţ':
+ return Optional.of('t');
+ case 'ţ':
+ return Optional.of('t');
+ case 'Ť':
+ return Optional.of('t');
+ case 'ť':
+ return Optional.of('t');
+ case 'Ŧ':
+ return Optional.of('t');
+ case 'ŧ':
+ return Optional.of('t');
+ case 'Ũ':
+ return Optional.of('u');
+ case 'ũ':
+ return Optional.of('u');
+ case 'Ū':
+ return Optional.of('u');
+ case 'ū':
+ return Optional.of('u');
+ case 'Ŭ':
+ return Optional.of('u');
+ case 'ŭ':
+ return Optional.of('u');
+ case 'Ů':
+ return Optional.of('u');
+ case 'ů':
+ return Optional.of('u');
+ case 'Ű':
+ return Optional.of('u');
+ case 'ű':
+ return Optional.of('u');
+ case 'Ų':
+ return Optional.of('u');
+ case 'ų':
+ return Optional.of('u');
+ case 'Ŵ':
+ return Optional.of('w');
+ case 'ŵ':
+ return Optional.of('w');
+ case 'Ŷ':
+ return Optional.of('y');
+ case 'ŷ':
+ return Optional.of('y');
+ case 'Ÿ':
+ return Optional.of('y');
+ case 'Ź':
+ return Optional.of('z');
+ case 'ź':
+ return Optional.of('z');
+ case 'Ż':
+ return Optional.of('z');
+ case 'ż':
+ return Optional.of('z');
+ case 'Ž':
+ return Optional.of('z');
+ case 'ž':
+ return Optional.of('z');
+ case 'ſ':
+ return Optional.of('s');
+ case 'ƀ':
+ return Optional.of('b');
+ case 'Ɓ':
+ return Optional.of('b');
+ case 'Ƃ':
+ return Optional.of('b');
+ case 'ƃ':
+ return Optional.of('b');
+ case 'Ɔ':
+ return Optional.of('o');
+ case 'Ƈ':
+ return Optional.of('c');
+ case 'ƈ':
+ return Optional.of('c');
+ case 'Ɖ':
+ return Optional.of('d');
+ case 'Ɗ':
+ return Optional.of('d');
+ case 'Ƌ':
+ return Optional.of('d');
+ case 'ƌ':
+ return Optional.of('d');
+ case 'ƍ':
+ return Optional.of('d');
+ case 'Ɛ':
+ return Optional.of('e');
+ case 'Ƒ':
+ return Optional.of('f');
+ case 'ƒ':
+ return Optional.of('f');
+ case 'Ɠ':
+ return Optional.of('g');
+ case 'Ɣ':
+ return Optional.of('g');
+ case 'Ɩ':
+ return Optional.of('i');
+ case 'Ɨ':
+ return Optional.of('i');
+ case 'Ƙ':
+ return Optional.of('k');
+ case 'ƙ':
+ return Optional.of('k');
+ case 'ƚ':
+ return Optional.of('l');
+ case 'ƛ':
+ return Optional.of('l');
+ case 'Ɯ':
+ return Optional.of('w');
+ case 'Ɲ':
+ return Optional.of('n');
+ case 'ƞ':
+ return Optional.of('n');
+ case 'Ɵ':
+ return Optional.of('o');
+ case 'Ơ':
+ return Optional.of('o');
+ case 'ơ':
+ return Optional.of('o');
+ case 'Ƥ':
+ return Optional.of('p');
+ case 'ƥ':
+ return Optional.of('p');
+ case 'ƫ':
+ return Optional.of('t');
+ case 'Ƭ':
+ return Optional.of('t');
+ case 'ƭ':
+ return Optional.of('t');
+ case 'Ʈ':
+ return Optional.of('t');
+ case 'Ư':
+ return Optional.of('u');
+ case 'ư':
+ return Optional.of('u');
+ case 'Ʊ':
+ return Optional.of('y');
+ case 'Ʋ':
+ return Optional.of('v');
+ case 'Ƴ':
+ return Optional.of('y');
+ case 'ƴ':
+ return Optional.of('y');
+ case 'Ƶ':
+ return Optional.of('z');
+ case 'ƶ':
+ return Optional.of('z');
+ case 'ƿ':
+ return Optional.of('w');
+ case 'Ǎ':
+ return Optional.of('a');
+ case 'ǎ':
+ return Optional.of('a');
+ case 'Ǐ':
+ return Optional.of('i');
+ case 'ǐ':
+ return Optional.of('i');
+ case 'Ǒ':
+ return Optional.of('o');
+ case 'ǒ':
+ return Optional.of('o');
+ case 'Ǔ':
+ return Optional.of('u');
+ case 'ǔ':
+ return Optional.of('u');
+ case 'Ǖ':
+ return Optional.of('u');
+ case 'ǖ':
+ return Optional.of('u');
+ case 'Ǘ':
+ return Optional.of('u');
+ case 'ǘ':
+ return Optional.of('u');
+ case 'Ǚ':
+ return Optional.of('u');
+ case 'ǚ':
+ return Optional.of('u');
+ case 'Ǜ':
+ return Optional.of('u');
+ case 'ǜ':
+ return Optional.of('u');
+ case 'Ǟ':
+ return Optional.of('a');
+ case 'ǟ':
+ return Optional.of('a');
+ case 'Ǡ':
+ return Optional.of('a');
+ case 'ǡ':
+ return Optional.of('a');
+ case 'Ǥ':
+ return Optional.of('g');
+ case 'ǥ':
+ return Optional.of('g');
+ case 'Ǧ':
+ return Optional.of('g');
+ case 'ǧ':
+ return Optional.of('g');
+ case 'Ǩ':
+ return Optional.of('k');
+ case 'ǩ':
+ return Optional.of('k');
+ case 'Ǫ':
+ return Optional.of('o');
+ case 'ǫ':
+ return Optional.of('o');
+ case 'Ǭ':
+ return Optional.of('o');
+ case 'ǭ':
+ return Optional.of('o');
+ case 'ǰ':
+ return Optional.of('j');
+ case 'Dz':
+ return Optional.of('d');
+ case 'Ǵ':
+ return Optional.of('g');
+ case 'ǵ':
+ return Optional.of('g');
+ case 'Ƿ':
+ return Optional.of('w');
+ case 'Ǹ':
+ return Optional.of('n');
+ case 'ǹ':
+ return Optional.of('n');
+ case 'Ǻ':
+ return Optional.of('a');
+ case 'ǻ':
+ return Optional.of('a');
+ case 'Ǿ':
+ return Optional.of('o');
+ case 'ǿ':
+ return Optional.of('o');
+ case 'Ȁ':
+ return Optional.of('a');
+ case 'ȁ':
+ return Optional.of('a');
+ case 'Ȃ':
+ return Optional.of('a');
+ case 'ȃ':
+ return Optional.of('a');
+ case 'Ȅ':
+ return Optional.of('e');
+ case 'ȅ':
+ return Optional.of('e');
+ case 'Ȇ':
+ return Optional.of('e');
+ case 'ȇ':
+ return Optional.of('e');
+ case 'Ȉ':
+ return Optional.of('i');
+ case 'ȉ':
+ return Optional.of('i');
+ case 'Ȋ':
+ return Optional.of('i');
+ case 'ȋ':
+ return Optional.of('i');
+ case 'Ȍ':
+ return Optional.of('o');
+ case 'ȍ':
+ return Optional.of('o');
+ case 'Ȏ':
+ return Optional.of('o');
+ case 'ȏ':
+ return Optional.of('o');
+ case 'Ȑ':
+ return Optional.of('r');
+ case 'ȑ':
+ return Optional.of('r');
+ case 'Ȓ':
+ return Optional.of('r');
+ case 'ȓ':
+ return Optional.of('r');
+ case 'Ȕ':
+ return Optional.of('u');
+ case 'ȕ':
+ return Optional.of('u');
+ case 'Ȗ':
+ return Optional.of('u');
+ case 'ȗ':
+ return Optional.of('u');
+ case 'Ș':
+ return Optional.of('s');
+ case 'ș':
+ return Optional.of('s');
+ case 'Ț':
+ return Optional.of('t');
+ case 'ț':
+ return Optional.of('t');
+ case 'Ȝ':
+ return Optional.of('y');
+ case 'ȝ':
+ return Optional.of('y');
+ case 'Ȟ':
+ return Optional.of('h');
+ case 'ȟ':
+ return Optional.of('h');
+ case 'Ȥ':
+ return Optional.of('z');
+ case 'ȥ':
+ return Optional.of('z');
+ case 'Ȧ':
+ return Optional.of('a');
+ case 'ȧ':
+ return Optional.of('a');
+ case 'Ȩ':
+ return Optional.of('e');
+ case 'ȩ':
+ return Optional.of('e');
+ case 'Ȫ':
+ return Optional.of('o');
+ case 'ȫ':
+ return Optional.of('o');
+ case 'Ȭ':
+ return Optional.of('o');
+ case 'ȭ':
+ return Optional.of('o');
+ case 'Ȯ':
+ return Optional.of('o');
+ case 'ȯ':
+ return Optional.of('o');
+ case 'Ȱ':
+ return Optional.of('o');
+ case 'ȱ':
+ return Optional.of('o');
+ case 'Ȳ':
+ return Optional.of('y');
+ case 'ȳ':
+ return Optional.of('y');
+ case 'A':
+ return Optional.of('a');
+ case 'B':
+ return Optional.of('b');
+ case 'C':
+ return Optional.of('c');
+ case 'D':
+ return Optional.of('d');
+ case 'E':
+ return Optional.of('e');
+ case 'F':
+ return Optional.of('f');
+ case 'G':
+ return Optional.of('g');
+ case 'H':
+ return Optional.of('h');
+ case 'I':
+ return Optional.of('i');
+ case 'J':
+ return Optional.of('j');
+ case 'K':
+ return Optional.of('k');
+ case 'L':
+ return Optional.of('l');
+ case 'M':
+ return Optional.of('m');
+ case 'N':
+ return Optional.of('n');
+ case 'O':
+ return Optional.of('o');
+ case 'P':
+ return Optional.of('p');
+ case 'Q':
+ return Optional.of('q');
+ case 'R':
+ return Optional.of('r');
+ case 'S':
+ return Optional.of('s');
+ case 'T':
+ return Optional.of('t');
+ case 'U':
+ return Optional.of('u');
+ case 'V':
+ return Optional.of('v');
+ case 'W':
+ return Optional.of('w');
+ case 'X':
+ return Optional.of('x');
+ case 'Y':
+ return Optional.of('y');
+ case 'Z':
+ return Optional.of('z');
+ default:
+ return Optional.absent();
+ }
}
- }
- @Override
- SimpleArrayMap getCharToKeyMap() {
- return DialpadCharMappings.getDefaultCharToKeyMap();
- }
+ @Override
+ SimpleArrayMap getCharToKeyMap() {
+ return DialpadCharMappings.getDefaultCharToKeyMap();
+ }
}
diff --git a/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/LocaleUtils.java b/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/LocaleUtils.java
index f1ff323..67565d2 100644
--- a/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/LocaleUtils.java
+++ b/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/LocaleUtils.java
@@ -24,14 +24,14 @@
*/
public final class LocaleUtils {
- /**
- * Returns the default locale of the device.
- */
- public static Locale getLocale(Context context) {
- Locale locale = context.getResources().getConfiguration().locale;
- if (locale != null) {
- return locale;
+ /**
+ * Returns the default locale of the device.
+ */
+ public static Locale getLocale(Context context) {
+ Locale locale = context.getResources().getConfiguration().locale;
+ if (locale != null) {
+ return locale;
+ }
+ return Locale.getDefault();
}
- return Locale.getDefault();
- }
}
diff --git a/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/PhoneNumberHelper.java b/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/PhoneNumberHelper.java
index 1faadc4..d2c90f2 100644
--- a/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/PhoneNumberHelper.java
+++ b/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/PhoneNumberHelper.java
@@ -32,103 +32,103 @@
public class PhoneNumberHelper {
- private static final Set LEGACY_UNKNOWN_NUMBERS =
- new HashSet<>(Arrays.asList("-1", "-2", "-3"));
-
- /**
- * Returns true if it is possible to place a call to the given number.
- */
- public static boolean canPlaceCallsTo(CharSequence number, int presentation) {
- return presentation == CallLog.Calls.PRESENTATION_ALLOWED
- && !TextUtils.isEmpty(number)
- && !isLegacyUnknownNumbers(number);
- }
-
- /**
- * Returns true if the input phone number contains special characters.
- */
- public static boolean numberHasSpecialChars(String number) {
- return !TextUtils.isEmpty(number) && number.contains("#");
- }
-
- /**
- * Returns true if the raw numbers of the two input phone numbers are the same.
- */
- public static boolean sameRawNumbers(String number1, String number2) {
- String rawNumber1 =
- PhoneNumberUtils.stripSeparators(PhoneNumberUtils.convertKeypadLettersToDigits(number1));
- String rawNumber2 =
- PhoneNumberUtils.stripSeparators(PhoneNumberUtils.convertKeypadLettersToDigits(number2));
-
- return rawNumber1.equals(rawNumber2);
- }
-
- /**
- * Returns true if the given number is a SIP address. To be able to mock-out this, it is not a
- * static method.
- */
- public static boolean isSipNumber(CharSequence number) {
- return number != null && isUriNumber(number.toString());
- }
-
- public static boolean isLegacyUnknownNumbers(CharSequence number) {
- return number != null && LEGACY_UNKNOWN_NUMBERS.contains(number.toString());
- }
-
- /**
- * An enhanced version of {@link PhoneNumberUtils#formatNumber(String, String, String)}.
- *
- * The {@link Context} parameter allows us to tweak formatting according to device properties.
- *
- *
Returns the formatted phone number (e.g, 1-123-456-7890) or the original number if
- * formatting fails or is intentionally ignored.
- */
- public static String formatNumber(
- Context context, @Nullable String number, @Nullable String numberE164, String countryIso) {
- // The number can be null e.g. schema is voicemail and uri content is empty.
- if (number == null) {
- return null;
+ private static final Set LEGACY_UNKNOWN_NUMBERS =
+ new HashSet<>(Arrays.asList("-1", "-2", "-3"));
+
+ /**
+ * Returns true if it is possible to place a call to the given number.
+ */
+ public static boolean canPlaceCallsTo(CharSequence number, int presentation) {
+ return presentation == CallLog.Calls.PRESENTATION_ALLOWED
+ && !TextUtils.isEmpty(number)
+ && !isLegacyUnknownNumbers(number);
}
+ /**
+ * Returns true if the input phone number contains special characters.
+ */
+ public static boolean numberHasSpecialChars(String number) {
+ return !TextUtils.isEmpty(number) && number.contains("#");
+ }
+
+ /**
+ * Returns true if the raw numbers of the two input phone numbers are the same.
+ */
+ public static boolean sameRawNumbers(String number1, String number2) {
+ String rawNumber1 =
+ PhoneNumberUtils.stripSeparators(PhoneNumberUtils.convertKeypadLettersToDigits(number1));
+ String rawNumber2 =
+ PhoneNumberUtils.stripSeparators(PhoneNumberUtils.convertKeypadLettersToDigits(number2));
+
+ return rawNumber1.equals(rawNumber2);
+ }
+
+ /**
+ * Returns true if the given number is a SIP address. To be able to mock-out this, it is not a
+ * static method.
+ */
+ public static boolean isSipNumber(CharSequence number) {
+ return number != null && isUriNumber(number.toString());
+ }
+
+ public static boolean isLegacyUnknownNumbers(CharSequence number) {
+ return number != null && LEGACY_UNKNOWN_NUMBERS.contains(number.toString());
+ }
+
+ /**
+ * An enhanced version of {@link PhoneNumberUtils#formatNumber(String, String, String)}.
+ *
+ * The {@link Context} parameter allows us to tweak formatting according to device properties.
+ *
+ *
Returns the formatted phone number (e.g, 1-123-456-7890) or the original number if
+ * formatting fails or is intentionally ignored.
+ */
+ public static String formatNumber(
+ Context context, @Nullable String number, @Nullable String numberE164, String countryIso) {
+ // The number can be null e.g. schema is voicemail and uri content is empty.
+ if (number == null) {
+ return null;
+ }
+
/* if (MotorolaUtils.shouldDisablePhoneNumberFormatting(context)) {
return number;
}
*/
- String formattedNumber = PhoneNumberUtils.formatNumber(number, numberE164, countryIso);
- return formattedNumber != null ? formattedNumber : number;
- }
-
- /**
- * @see #formatNumber(Context, String, String, String).
- */
- public static String formatNumber(Context context, @Nullable String number, String countryIso) {
- return formatNumber(context, number, /* numberE164 = */ null, countryIso);
- }
-
- @Nullable
- public static CharSequence formatNumberForDisplay(
- Context context, @Nullable String number, @NonNull String countryIso) {
- if (number == null) {
- return null;
+ String formattedNumber = PhoneNumberUtils.formatNumber(number, numberE164, countryIso);
+ return formattedNumber != null ? formattedNumber : number;
+ }
+
+ /**
+ * @see #formatNumber(Context, String, String, String).
+ */
+ public static String formatNumber(Context context, @Nullable String number, String countryIso) {
+ return formatNumber(context, number, /* numberE164 = */ null, countryIso);
}
- return PhoneNumberUtils.createTtsSpannable(
- BidiFormatter.getInstance()
- .unicodeWrap(formatNumber(context, number, countryIso), TextDirectionHeuristics.LTR));
- }
-
- /**
- * Determines if the specified number is actually a URI (i.e. a SIP address) rather than a regular
- * PSTN phone number, based on whether or not the number contains an "@" character.
- *
- * @param number Phone number
- * @return true if number contains @
- *
TODO: Remove if PhoneNumberUtils.isUriNumber(String number) is made public.
- */
- public static boolean isUriNumber(String number) {
- // Note we allow either "@" or "%40" to indicate a URI, in case
- // the passed-in string is URI-escaped. (Neither "@" nor "%40"
- // will ever be found in a legal PSTN number.)
- return number != null && (number.contains("@") || number.contains("%40"));
- }
+ @Nullable
+ public static CharSequence formatNumberForDisplay(
+ Context context, @Nullable String number, @NonNull String countryIso) {
+ if (number == null) {
+ return null;
+ }
+
+ return PhoneNumberUtils.createTtsSpannable(
+ BidiFormatter.getInstance()
+ .unicodeWrap(formatNumber(context, number, countryIso), TextDirectionHeuristics.LTR));
+ }
+
+ /**
+ * Determines if the specified number is actually a URI (i.e. a SIP address) rather than a regular
+ * PSTN phone number, based on whether or not the number contains an "@" character.
+ *
+ * @param number Phone number
+ * @return true if number contains @
+ *
TODO: Remove if PhoneNumberUtils.isUriNumber(String number) is made public.
+ */
+ public static boolean isUriNumber(String number) {
+ // Note we allow either "@" or "%40" to indicate a URI, in case
+ // the passed-in string is URI-escaped. (Neither "@" nor "%40"
+ // will ever be found in a legal PSTN number.)
+ return number != null && (number.contains("@") || number.contains("%40"));
+ }
}
diff --git a/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/QueryBoldingUtil.java b/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/QueryBoldingUtil.java
index 58ff22e..879fca4 100644
--- a/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/QueryBoldingUtil.java
+++ b/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/QueryBoldingUtil.java
@@ -35,137 +35,137 @@
*/
public class QueryBoldingUtil {
- /**
- * Compares a name and query and returns a {@link CharSequence} with bolded characters.
- *
- *
Some example of matches:
- *
- *
- * - "query" would bold "John [query] Smith"
- *
- "222" would bold "[AAA] Mom"
- *
- "222" would bold "[A]llen [A]lex [A]aron"
- *
- "2226" would bold "[AAA M]om"
- *
- *
- * Some examples of non-matches:
- *
- *
- * - "ss" would not match "Jessica Jones"
- *
- "77" would not match "Jessica Jones"
- *
- *
- * @param query containing any characters
- * @param name of a contact/string that query will compare to
- * @param context of the app
- * @return name with query bolded if query can be found in the name.
- */
- public static CharSequence getNameWithQueryBolded(
- @Nullable String query, @NonNull String name, @NonNull Context context) {
- if (TextUtils.isEmpty(query)) {
- return name;
- }
-
- if (!QueryFilteringUtil.nameMatchesT9Query(query, name, context)) {
- Pattern pattern = Pattern.compile("(^|\\s)" + Pattern.quote(query.toLowerCase()));
- Matcher matcher = pattern.matcher(name.toLowerCase());
- if (matcher.find()) {
- // query matches the start of a name (i.e. "jo" -> "Jessica [Jo]nes")
- return getBoldedString(name, matcher.start(), query.length());
- } else {
- // query not found in name
- return name;
- }
- }
+ /**
+ * Compares a name and query and returns a {@link CharSequence} with bolded characters.
+ *
+ * Some example of matches:
+ *
+ *
+ * - "query" would bold "John [query] Smith"
+ *
- "222" would bold "[AAA] Mom"
+ *
- "222" would bold "[A]llen [A]lex [A]aron"
+ *
- "2226" would bold "[AAA M]om"
+ *
+ *
+ * Some examples of non-matches:
+ *
+ *
+ * - "ss" would not match "Jessica Jones"
+ *
- "77" would not match "Jessica Jones"
+ *
+ *
+ * @param query containing any characters
+ * @param name of a contact/string that query will compare to
+ * @param context of the app
+ * @return name with query bolded if query can be found in the name.
+ */
+ public static CharSequence getNameWithQueryBolded(
+ @Nullable String query, @NonNull String name, @NonNull Context context) {
+ if (TextUtils.isEmpty(query)) {
+ return name;
+ }
- int indexOfT9Match = QueryFilteringUtil.getIndexOfT9Substring(query, name, context);
- if (indexOfT9Match != -1) {
- // query matches the start of a T9 name (i.e. 75 -> "Jessica [Jo]nes")
- int numBolded = query.length();
+ if (!QueryFilteringUtil.nameMatchesT9Query(query, name, context)) {
+ Pattern pattern = Pattern.compile("(^|\\s)" + Pattern.quote(query.toLowerCase()));
+ Matcher matcher = pattern.matcher(name.toLowerCase());
+ if (matcher.find()) {
+ // query matches the start of a name (i.e. "jo" -> "Jessica [Jo]nes")
+ return getBoldedString(name, matcher.start(), query.length());
+ } else {
+ // query not found in name
+ return name;
+ }
+ }
- // Bold an extra character for each non-letter
- for (int i = indexOfT9Match; i <= indexOfT9Match + numBolded && i < name.length(); i++) {
- if (!Character.isLetter(name.charAt(i))) {
- numBolded++;
+ int indexOfT9Match = QueryFilteringUtil.getIndexOfT9Substring(query, name, context);
+ if (indexOfT9Match != -1) {
+ // query matches the start of a T9 name (i.e. 75 -> "Jessica [Jo]nes")
+ int numBolded = query.length();
+
+ // Bold an extra character for each non-letter
+ for (int i = indexOfT9Match; i <= indexOfT9Match + numBolded && i < name.length(); i++) {
+ if (!Character.isLetter(name.charAt(i))) {
+ numBolded++;
+ }
+ }
+ return getBoldedString(name, indexOfT9Match, numBolded);
+ } else {
+ // query match the T9 initials (i.e. 222 -> "[A]l [B]ob [C]harlie")
+ return getNameWithInitialsBolded(query, name, context);
}
- }
- return getBoldedString(name, indexOfT9Match, numBolded);
- } else {
- // query match the T9 initials (i.e. 222 -> "[A]l [B]ob [C]harlie")
- return getNameWithInitialsBolded(query, name, context);
}
- }
-
- private static CharSequence getNameWithInitialsBolded(
- String query, String name, Context context) {
- SpannableString boldedInitials = new SpannableString(name);
- name = name.toLowerCase();
- int initialsBolded = 0;
- int nameIndex = -1;
-
- while (++nameIndex < name.length() && initialsBolded < query.length()) {
- if ((nameIndex == 0 || name.charAt(nameIndex - 1) == ' ')
- && QueryFilteringUtil.getDigit(name.charAt(nameIndex), context)
- == query.charAt(initialsBolded)) {
- boldedInitials.setSpan(
- new StyleSpan(Typeface.BOLD),
- nameIndex,
- nameIndex + 1,
- Spanned.SPAN_INCLUSIVE_INCLUSIVE);
- initialsBolded++;
- }
- }
- return boldedInitials;
- }
-
- /**
- * Compares a number and a query and returns a {@link CharSequence} with bolded characters.
- *
- *
- * - "123" would bold "(650)34[1-23]24"
- *
- "123" would bold "+1([123])111-2222
- *
- *
- * @param query containing only numbers and phone number related characters "(", ")", "-", "+"
- * @param number phone number of a contact that the query will compare to.
- * @return number with query bolded if query can be found in the number.
- */
- public static CharSequence getNumberWithQueryBolded(
- @Nullable String query, @NonNull String number) {
- if (TextUtils.isEmpty(query) || !QueryFilteringUtil.numberMatchesNumberQuery(query, number)) {
- return number;
+
+ private static CharSequence getNameWithInitialsBolded(
+ String query, String name, Context context) {
+ SpannableString boldedInitials = new SpannableString(name);
+ name = name.toLowerCase();
+ int initialsBolded = 0;
+ int nameIndex = -1;
+
+ while (++nameIndex < name.length() && initialsBolded < query.length()) {
+ if ((nameIndex == 0 || name.charAt(nameIndex - 1) == ' ')
+ && QueryFilteringUtil.getDigit(name.charAt(nameIndex), context)
+ == query.charAt(initialsBolded)) {
+ boldedInitials.setSpan(
+ new StyleSpan(Typeface.BOLD),
+ nameIndex,
+ nameIndex + 1,
+ Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ initialsBolded++;
+ }
+ }
+ return boldedInitials;
}
- int index = QueryFilteringUtil.indexOfQueryNonDigitsIgnored(query, number);
- int boldedCharacters = query.length();
+ /**
+ * Compares a number and a query and returns a {@link CharSequence} with bolded characters.
+ *
+ *
+ * - "123" would bold "(650)34[1-23]24"
+ *
- "123" would bold "+1([123])111-2222
+ *
+ *
+ * @param query containing only numbers and phone number related characters "(", ")", "-", "+"
+ * @param number phone number of a contact that the query will compare to.
+ * @return number with query bolded if query can be found in the number.
+ */
+ public static CharSequence getNumberWithQueryBolded(
+ @Nullable String query, @NonNull String number) {
+ if (TextUtils.isEmpty(query) || !QueryFilteringUtil.numberMatchesNumberQuery(query, number)) {
+ return number;
+ }
- for (char c : query.toCharArray()) {
- if (!Character.isDigit(c)) {
- boldedCharacters--;
- }
- }
+ int index = QueryFilteringUtil.indexOfQueryNonDigitsIgnored(query, number);
+ int boldedCharacters = query.length();
- for (int i = 0; i < index + boldedCharacters; i++) {
- if (!Character.isDigit(number.charAt(i))) {
- if (i <= index) {
- index++;
- } else {
- boldedCharacters++;
+ for (char c : query.toCharArray()) {
+ if (!Character.isDigit(c)) {
+ boldedCharacters--;
+ }
}
- }
+
+ for (int i = 0; i < index + boldedCharacters; i++) {
+ if (!Character.isDigit(number.charAt(i))) {
+ if (i <= index) {
+ index++;
+ } else {
+ boldedCharacters++;
+ }
+ }
+ }
+ return getBoldedString(number, index, boldedCharacters);
}
- return getBoldedString(number, index, boldedCharacters);
- }
-
- private static SpannableString getBoldedString(String s, int index, int numBolded) {
- if (numBolded + index > s.length()) {
- Log.e(
- QueryBoldingUtil.class.getSimpleName(),
- "number of bolded characters exceeded length of string.");
- numBolded = s.length() - index;
+
+ private static SpannableString getBoldedString(String s, int index, int numBolded) {
+ if (numBolded + index > s.length()) {
+ Log.e(
+ QueryBoldingUtil.class.getSimpleName(),
+ "number of bolded characters exceeded length of string.");
+ numBolded = s.length() - index;
+ }
+ SpannableString span = new SpannableString(s);
+ span.setSpan(
+ new StyleSpan(Typeface.BOLD), index, index + numBolded, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ return span;
}
- SpannableString span = new SpannableString(s);
- span.setSpan(
- new StyleSpan(Typeface.BOLD), index, index + numBolded, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
- return span;
- }
}
diff --git a/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/QueryFilteringUtil.java b/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/QueryFilteringUtil.java
index 36c498d..c655a67 100644
--- a/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/QueryFilteringUtil.java
+++ b/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/QueryFilteringUtil.java
@@ -30,198 +30,198 @@
*/
public class QueryFilteringUtil {
- /**
- * The default character-digit map that will be used to find the digit associated with a given
- * character on a T9 keyboard.
- */
- private static final SimpleArrayMap DEFAULT_CHAR_TO_DIGIT_MAP =
- DialpadCharMappings.getDefaultCharToKeyMap();
-
- /**
- * Matches strings with "-", "(", ")", 2-9 of at least length one.
- */
- private static final Pattern T9_PATTERN = Pattern.compile("[\\-()2-9]+");
-
- /**
- * Returns true if the query is of T9 format and the name's T9 representation belongs to the query
- *
- * Examples:
- *
- *
- * - #nameMatchesT9Query("7", "John Smith") returns true, 7 -> 'S'
- *
- #nameMatchesT9Query("55", "Jessica Jones") returns true, 55 -> 'JJ'
- *
- #nameMatchesT9Query("56", "Jessica Jones") returns true, 56 -> 'Jo'
- *
- #nameMatchesT9Query("7", "Jessica Jones") returns false, no names start with P,Q,R or S
- *
- *
- * When the 1st language preference uses a non-Latin alphabet (e.g., Russian) and the character
- * mappings for the alphabet is defined in {@link DialpadCharMappings}, the Latin alphabet will be
- * used first to check if the name matches the query. If they don't match, the non-Latin alphabet
- * will be used.
- *
- *
Examples (when the 1st language preference is Russian):
- *
- *
- * - #nameMatchesT9Query("7", "John Smith") returns true, 7 -> 'S'
- *
- #nameMatchesT9Query("7", "Павел Чехов") returns true, 7 -> 'Ч'
- *
- #nameMatchesT9Query("77", "Pavel Чехов") returns true, 7 -> 'P' (in the Latin alphabet),
- * 7 -> 'Ч' (in the Russian alphabet)
- *
- */
- public static boolean nameMatchesT9Query(String query, String name, Context context) {
- if (!T9_PATTERN.matcher(query).matches()) {
- return false;
+ /**
+ * The default character-digit map that will be used to find the digit associated with a given
+ * character on a T9 keyboard.
+ */
+ private static final SimpleArrayMap DEFAULT_CHAR_TO_DIGIT_MAP =
+ DialpadCharMappings.getDefaultCharToKeyMap();
+
+ /**
+ * Matches strings with "-", "(", ")", 2-9 of at least length one.
+ */
+ private static final Pattern T9_PATTERN = Pattern.compile("[\\-()2-9]+");
+
+ /**
+ * Returns true if the query is of T9 format and the name's T9 representation belongs to the query
+ *
+ * Examples:
+ *
+ *
+ * - #nameMatchesT9Query("7", "John Smith") returns true, 7 -> 'S'
+ *
- #nameMatchesT9Query("55", "Jessica Jones") returns true, 55 -> 'JJ'
+ *
- #nameMatchesT9Query("56", "Jessica Jones") returns true, 56 -> 'Jo'
+ *
- #nameMatchesT9Query("7", "Jessica Jones") returns false, no names start with P,Q,R or S
+ *
+ *
+ * When the 1st language preference uses a non-Latin alphabet (e.g., Russian) and the character
+ * mappings for the alphabet is defined in {@link DialpadCharMappings}, the Latin alphabet will be
+ * used first to check if the name matches the query. If they don't match, the non-Latin alphabet
+ * will be used.
+ *
+ *
Examples (when the 1st language preference is Russian):
+ *
+ *
+ * - #nameMatchesT9Query("7", "John Smith") returns true, 7 -> 'S'
+ *
- #nameMatchesT9Query("7", "Павел Чехов") returns true, 7 -> 'Ч'
+ *
- #nameMatchesT9Query("77", "Pavel Чехов") returns true, 7 -> 'P' (in the Latin alphabet),
+ * 7 -> 'Ч' (in the Russian alphabet)
+ *
+ */
+ public static boolean nameMatchesT9Query(String query, String name, Context context) {
+ if (!T9_PATTERN.matcher(query).matches()) {
+ return false;
+ }
+
+ query = digitsOnly(query);
+ if (getIndexOfT9Substring(query, name, context) != -1) {
+ return true;
+ }
+
+ // Check matches initials
+ // TODO(calderwoodra) investigate faster implementation
+ int queryIndex = 0;
+
+ String[] names = name.toLowerCase().split("\\s");
+ for (int i = 0; i < names.length && queryIndex < query.length(); i++) {
+ if (TextUtils.isEmpty(names[i])) {
+ continue;
+ }
+
+ if (getDigit(names[i].charAt(0), context) == query.charAt(queryIndex)) {
+ queryIndex++;
+ }
+ }
+
+ return queryIndex == query.length();
}
- query = digitsOnly(query);
- if (getIndexOfT9Substring(query, name, context) != -1) {
- return true;
+ /**
+ * Returns the index where query is contained in the T9 representation of the name.
+ *
+ * Examples:
+ *
+ *
+ * - #getIndexOfT9Substring("76", "John Smith") returns 5, 76 -> 'Sm'
+ *
- #nameMatchesT9Query("2226", "AAA Mom") returns 0, 2226 -> 'AAAM'
+ *
- #nameMatchesT9Query("2", "Jessica Jones") returns -1, Neither 'Jessica' nor 'Jones' start
+ * with A, B or C
+ *
+ */
+ public static int getIndexOfT9Substring(String query, String name, Context context) {
+ query = digitsOnly(query);
+ String t9Name = getT9Representation(name, context);
+ String t9NameDigitsOnly = digitsOnly(t9Name);
+ if (t9NameDigitsOnly.startsWith(query)) {
+ return 0;
+ }
+
+ int nonLetterCount = 0;
+ for (int i = 1; i < t9NameDigitsOnly.length(); i++) {
+ char cur = t9Name.charAt(i);
+ if (!Character.isDigit(cur)) {
+ nonLetterCount++;
+ continue;
+ }
+
+ // If the previous character isn't a digit and the current is, check for a match
+ char prev = t9Name.charAt(i - 1);
+ int offset = i - nonLetterCount;
+ if (!Character.isDigit(prev) && t9NameDigitsOnly.startsWith(query, offset)) {
+ return i;
+ }
+ }
+ return -1;
}
- // Check matches initials
- // TODO(calderwoodra) investigate faster implementation
- int queryIndex = 0;
-
- String[] names = name.toLowerCase().split("\\s");
- for (int i = 0; i < names.length && queryIndex < query.length(); i++) {
- if (TextUtils.isEmpty(names[i])) {
- continue;
- }
-
- if (getDigit(names[i].charAt(0), context) == query.charAt(queryIndex)) {
- queryIndex++;
- }
+ /**
+ * Returns true if the subparts of the name (split by white space) begin with the query.
+ *
+ * Examples:
+ *
+ *
+ * - #nameContainsQuery("b", "Brandon") returns true
+ *
- #nameContainsQuery("o", "Bob") returns false
+ *
- #nameContainsQuery("o", "Bob Olive") returns true
+ *
+ */
+ public static boolean nameContainsQuery(String query, String name) {
+ if (TextUtils.isEmpty(name)) {
+ return false;
+ }
+
+ return Pattern.compile("(^|\\s)" + Pattern.quote(query.toLowerCase()))
+ .matcher(name.toLowerCase())
+ .find();
}
- return queryIndex == query.length();
- }
-
- /**
- * Returns the index where query is contained in the T9 representation of the name.
- *
- * Examples:
- *
- *
- * - #getIndexOfT9Substring("76", "John Smith") returns 5, 76 -> 'Sm'
- *
- #nameMatchesT9Query("2226", "AAA Mom") returns 0, 2226 -> 'AAAM'
- *
- #nameMatchesT9Query("2", "Jessica Jones") returns -1, Neither 'Jessica' nor 'Jones' start
- * with A, B or C
- *
- */
- public static int getIndexOfT9Substring(String query, String name, Context context) {
- query = digitsOnly(query);
- String t9Name = getT9Representation(name, context);
- String t9NameDigitsOnly = digitsOnly(t9Name);
- if (t9NameDigitsOnly.startsWith(query)) {
- return 0;
+ /**
+ * @return true if the number belongs to the query.
+ */
+ public static boolean numberMatchesNumberQuery(String query, String number) {
+ return PhoneNumberUtils.isGlobalPhoneNumber(query)
+ && indexOfQueryNonDigitsIgnored(query, number) != -1;
}
- int nonLetterCount = 0;
- for (int i = 1; i < t9NameDigitsOnly.length(); i++) {
- char cur = t9Name.charAt(i);
- if (!Character.isDigit(cur)) {
- nonLetterCount++;
- continue;
- }
-
- // If the previous character isn't a digit and the current is, check for a match
- char prev = t9Name.charAt(i - 1);
- int offset = i - nonLetterCount;
- if (!Character.isDigit(prev) && t9NameDigitsOnly.startsWith(query, offset)) {
- return i;
- }
- }
- return -1;
- }
-
- /**
- * Returns true if the subparts of the name (split by white space) begin with the query.
- *
- * Examples:
- *
- *
- * - #nameContainsQuery("b", "Brandon") returns true
- *
- #nameContainsQuery("o", "Bob") returns false
- *
- #nameContainsQuery("o", "Bob Olive") returns true
- *
- */
- public static boolean nameContainsQuery(String query, String name) {
- if (TextUtils.isEmpty(name)) {
- return false;
+ /**
+ * Checks if query is contained in number while ignoring all characters in both that are not
+ * digits (i.e. {@link Character#isDigit(char)} returns false).
+ *
+ * @return index where query is found with all non-digits removed, -1 if it's not found.
+ */
+ static int indexOfQueryNonDigitsIgnored(@NonNull String query, @NonNull String number) {
+ return digitsOnly(number).indexOf(digitsOnly(query));
}
- return Pattern.compile("(^|\\s)" + Pattern.quote(query.toLowerCase()))
- .matcher(name.toLowerCase())
- .find();
- }
-
- /**
- * @return true if the number belongs to the query.
- */
- public static boolean numberMatchesNumberQuery(String query, String number) {
- return PhoneNumberUtils.isGlobalPhoneNumber(query)
- && indexOfQueryNonDigitsIgnored(query, number) != -1;
- }
-
- /**
- * Checks if query is contained in number while ignoring all characters in both that are not
- * digits (i.e. {@link Character#isDigit(char)} returns false).
- *
- * @return index where query is found with all non-digits removed, -1 if it's not found.
- */
- static int indexOfQueryNonDigitsIgnored(@NonNull String query, @NonNull String number) {
- return digitsOnly(number).indexOf(digitsOnly(query));
- }
-
- /**
- * Replaces characters in the given string with their T9 representations.
- *
- * @param s The original string
- * @param context The context
- * @return The original string with characters replaced with T9 representations.
- */
- public static String getT9Representation(String s, Context context) {
- StringBuilder builder = new StringBuilder(s.length());
- for (char c : s.toLowerCase().toCharArray()) {
- builder.append(getDigit(c, context));
- }
- return builder.toString();
- }
-
- /**
- * @return String s with only digits recognized by Character#isDigit() remaining
- */
- public static String digitsOnly(String s) {
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < s.length(); i++) {
- char c = s.charAt(i);
- if (Character.isDigit(c)) {
- sb.append(c);
- }
- }
- return sb.toString();
- }
-
- /**
- * Returns the digit on a T9 keyboard which is associated with the given lower case character.
- *
- * The default character-key mapping will be used first to find a digit. If no digit is found,
- * try the mapping of the current default locale if it is defined in {@link DialpadCharMappings}.
- * If the second attempt fails, return the original character.
- */
- static char getDigit(char c, Context context) {
- Character digit = DEFAULT_CHAR_TO_DIGIT_MAP.get(c);
- if (digit != null) {
- return digit;
+ /**
+ * Replaces characters in the given string with their T9 representations.
+ *
+ * @param s The original string
+ * @param context The context
+ * @return The original string with characters replaced with T9 representations.
+ */
+ public static String getT9Representation(String s, Context context) {
+ StringBuilder builder = new StringBuilder(s.length());
+ for (char c : s.toLowerCase().toCharArray()) {
+ builder.append(getDigit(c, context));
+ }
+ return builder.toString();
}
- SimpleArrayMap charToKeyMap =
- DialpadCharMappings.getCharToKeyMap(context);
- if (charToKeyMap != null) {
- digit = charToKeyMap.get(c);
- return digit != null ? digit : c;
+ /**
+ * @return String s with only digits recognized by Character#isDigit() remaining
+ */
+ public static String digitsOnly(String s) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if (Character.isDigit(c)) {
+ sb.append(c);
+ }
+ }
+ return sb.toString();
}
- return c;
- }
+ /**
+ * Returns the digit on a T9 keyboard which is associated with the given lower case character.
+ *
+ * The default character-key mapping will be used first to find a digit. If no digit is found,
+ * try the mapping of the current default locale if it is defined in {@link DialpadCharMappings}.
+ * If the second attempt fails, return the original character.
+ */
+ static char getDigit(char c, Context context) {
+ Character digit = DEFAULT_CHAR_TO_DIGIT_MAP.get(c);
+ if (digit != null) {
+ return digit;
+ }
+
+ SimpleArrayMap charToKeyMap =
+ DialpadCharMappings.getCharToKeyMap(context);
+ if (charToKeyMap != null) {
+ digit = charToKeyMap.get(c);
+ return digit != null ? digit : c;
+ }
+
+ return c;
+ }
}
diff --git a/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/SmartDialMap.java b/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/SmartDialMap.java
index cae63e9..2c21c44 100644
--- a/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/SmartDialMap.java
+++ b/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/SmartDialMap.java
@@ -26,81 +26,81 @@
@SuppressWarnings("Guava")
abstract class SmartDialMap {
- /**
- * Returns true if the provided character can be mapped to a key on the dialpad.
- *
- * The provided character is expected to be a normalized character. See {@link
- * SmartDialMap#normalizeCharacter(char)} for details.
- */
- protected boolean isValidDialpadCharacter(char ch) {
- return isValidDialpadAlphabeticChar(ch) || isValidDialpadNumericChar(ch);
- }
-
- /**
- * Returns true if the provided character is a letter and can be mapped to a key on the dialpad.
- *
- *
The provided character is expected to be a normalized character. See {@link
- * SmartDialMap#normalizeCharacter(char)} for details.
- */
- protected boolean isValidDialpadAlphabeticChar(char ch) {
- return getCharToKeyMap().containsKey(ch);
- }
-
- /**
- * Returns true if the provided character is a digit, and can be mapped to a key on the dialpad.
- */
- protected boolean isValidDialpadNumericChar(char ch) {
- return '0' <= ch && ch <= '9';
- }
+ /**
+ * Returns true if the provided character can be mapped to a key on the dialpad.
+ *
+ *
The provided character is expected to be a normalized character. See {@link
+ * SmartDialMap#normalizeCharacter(char)} for details.
+ */
+ protected boolean isValidDialpadCharacter(char ch) {
+ return isValidDialpadAlphabeticChar(ch) || isValidDialpadNumericChar(ch);
+ }
- /**
- * Get the index of the key on the dialpad which the character corresponds to.
- *
- *
The provided character is expected to be a normalized character. See {@link
- * SmartDialMap#normalizeCharacter(char)} for details.
- *
- *
An {@link Optional#absent()} is returned if the provided character can't be mapped to a key
- * on the dialpad.
- */
- protected Optional getDialpadIndex(char ch) {
- if (isValidDialpadNumericChar(ch)) {
- return Optional.of((byte) (ch - '0'));
+ /**
+ * Returns true if the provided character is a letter and can be mapped to a key on the dialpad.
+ *
+ * The provided character is expected to be a normalized character. See {@link
+ * SmartDialMap#normalizeCharacter(char)} for details.
+ */
+ protected boolean isValidDialpadAlphabeticChar(char ch) {
+ return getCharToKeyMap().containsKey(ch);
}
- if (isValidDialpadAlphabeticChar(ch)) {
- return Optional.of((byte) (getCharToKeyMap().get(ch) - '0'));
+ /**
+ * Returns true if the provided character is a digit, and can be mapped to a key on the dialpad.
+ */
+ protected boolean isValidDialpadNumericChar(char ch) {
+ return '0' <= ch && ch <= '9';
}
- return Optional.absent();
- }
+ /**
+ * Get the index of the key on the dialpad which the character corresponds to.
+ *
+ *
The provided character is expected to be a normalized character. See {@link
+ * SmartDialMap#normalizeCharacter(char)} for details.
+ *
+ *
An {@link Optional#absent()} is returned if the provided character can't be mapped to a key
+ * on the dialpad.
+ */
+ protected Optional getDialpadIndex(char ch) {
+ if (isValidDialpadNumericChar(ch)) {
+ return Optional.of((byte) (ch - '0'));
+ }
+
+ if (isValidDialpadAlphabeticChar(ch)) {
+ return Optional.of((byte) (getCharToKeyMap().get(ch) - '0'));
+ }
- /**
- * Get the actual numeric character on the dialpad which the character corresponds to.
- *
- * The provided character is expected to be a normalized character. See {@link
- * SmartDialMap#normalizeCharacter(char)} for details.
- *
- *
An {@link Optional#absent()} is returned if the provided character can't be mapped to a key
- * on the dialpad.
- */
- protected Optional getDialpadNumericCharacter(char ch) {
- return isValidDialpadAlphabeticChar(ch)
- ? Optional.of(getCharToKeyMap().get(ch))
- : Optional.absent();
- }
+ return Optional.absent();
+ }
+
+ /**
+ * Get the actual numeric character on the dialpad which the character corresponds to.
+ *
+ * The provided character is expected to be a normalized character. See {@link
+ * SmartDialMap#normalizeCharacter(char)} for details.
+ *
+ *
An {@link Optional#absent()} is returned if the provided character can't be mapped to a key
+ * on the dialpad.
+ */
+ protected Optional getDialpadNumericCharacter(char ch) {
+ return isValidDialpadAlphabeticChar(ch)
+ ? Optional.of(getCharToKeyMap().get(ch))
+ : Optional.absent();
+ }
- /**
- * Converts uppercase characters to lower case ones, and on a best effort basis, strips accents
- * from accented characters.
- *
- * An {@link Optional#absent()} is returned if the provided character can't be mapped to a key
- * on the dialpad.
- */
- abstract Optional normalizeCharacter(char ch);
+ /**
+ * Converts uppercase characters to lower case ones, and on a best effort basis, strips accents
+ * from accented characters.
+ *
+ * An {@link Optional#absent()} is returned if the provided character can't be mapped to a key
+ * on the dialpad.
+ */
+ abstract Optional normalizeCharacter(char ch);
- /**
- * Returns a map in which each key is a normalized character and the corresponding value is a
- * dialpad key.
- */
- abstract SimpleArrayMap getCharToKeyMap();
+ /**
+ * Returns a map in which each key is a normalized character and the corresponding value is a
+ * dialpad key.
+ */
+ abstract SimpleArrayMap getCharToKeyMap();
}
diff --git a/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/SmartDialMatchPosition.java b/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/SmartDialMatchPosition.java
index 772fac8..2bd6f24 100644
--- a/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/SmartDialMatchPosition.java
+++ b/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/SmartDialMatchPosition.java
@@ -28,44 +28,44 @@
*/
public class SmartDialMatchPosition {
- private static final String TAG = SmartDialMatchPosition.class.getSimpleName();
+ private static final String TAG = SmartDialMatchPosition.class.getSimpleName();
- public int start;
- public int end;
+ public int start;
+ public int end;
- public SmartDialMatchPosition(int start, int end) {
- this.start = start;
- this.end = end;
- }
+ public SmartDialMatchPosition(int start, int end) {
+ this.start = start;
+ this.end = end;
+ }
- /**
- * Used by {@link SmartDialNameMatcher} to advance the positions of a match position found in a
- * sub query.
- *
- * @param inList ArrayList of SmartDialMatchPositions to modify.
- * @param toAdvance Offset to modify by.
- */
- public static void advanceMatchPositions(
- ArrayList inList, int toAdvance) {
- for (int i = 0; i < inList.size(); i++) {
- inList.get(i).advance(toAdvance);
+ /**
+ * Used by {@link SmartDialNameMatcher} to advance the positions of a match position found in a
+ * sub query.
+ *
+ * @param inList ArrayList of SmartDialMatchPositions to modify.
+ * @param toAdvance Offset to modify by.
+ */
+ public static void advanceMatchPositions(
+ ArrayList inList, int toAdvance) {
+ for (int i = 0; i < inList.size(); i++) {
+ inList.get(i).advance(toAdvance);
+ }
}
- }
- /**
- * Used mainly for debug purposes. Displays contents of an ArrayList of SmartDialMatchPositions.
- *
- * @param list ArrayList of SmartDialMatchPositions to print out in a human readable fashion.
- */
- public static void print(ArrayList list) {
- for (int i = 0; i < list.size(); i++) {
- SmartDialMatchPosition m = list.get(i);
- Log.d(TAG, "[" + m.start + "," + m.end + "]");
+ /**
+ * Used mainly for debug purposes. Displays contents of an ArrayList of SmartDialMatchPositions.
+ *
+ * @param list ArrayList of SmartDialMatchPositions to print out in a human readable fashion.
+ */
+ public static void print(ArrayList list) {
+ for (int i = 0; i < list.size(); i++) {
+ SmartDialMatchPosition m = list.get(i);
+ Log.d(TAG, "[" + m.start + "," + m.end + "]");
+ }
}
- }
- private void advance(int toAdvance) {
- this.start += toAdvance;
- this.end += toAdvance;
- }
+ private void advance(int toAdvance) {
+ this.start += toAdvance;
+ this.end += toAdvance;
+ }
}
diff --git a/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/SmartDialNameMatcher.java b/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/SmartDialNameMatcher.java
index 5d59aaa..89afe7b 100644
--- a/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/SmartDialNameMatcher.java
+++ b/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/SmartDialNameMatcher.java
@@ -31,400 +31,400 @@
* (J)ohn (S)mith.
*/
public class SmartDialNameMatcher {
- // Whether or not we allow matches like 57 - (J)ohn (S)mith
- private static final boolean ALLOW_INITIAL_MATCH = true;
-
- // The maximum length of the initial we will match - typically set to 1 to minimize false
- // positives
- private static final int INITIAL_LENGTH_LIMIT = 1;
-
- private final ArrayList matchPositions = new ArrayList<>();
- private String query;
-
- // Controls whether to treat an empty query as a match (with anything).
- private boolean shouldMatchEmptyQuery = false;
-
- public SmartDialNameMatcher(String query) {
- this.query = query;
- }
-
- /**
- * Strips a phone number of unnecessary characters (spaces, dashes, etc.)
- *
- * @param number Phone number we want to normalize
- * @return Phone number consisting of digits from 0-9
- */
- public static String normalizeNumber(Context context, String number) {
- return normalizeNumber(context, number, /* offset = */ 0);
- }
-
- /**
- * Strips a phone number of unnecessary characters (spaces, dashes, etc.)
- *
- * @param number Phone number we want to normalize
- * @param offset Offset to start from
- * @return Phone number consisting of digits from 0-9
- */
- public static String normalizeNumber(Context context, String number, int offset) {
- final StringBuilder s = new StringBuilder();
- for (int i = offset; i < number.length(); i++) {
- char ch = number.charAt(i);
- if (CompositeSmartDialMap.isValidDialpadNumericChar(context, ch)) {
- s.append(ch);
- }
- }
- return s.toString();
- }
-
- /**
- * Constructs empty highlight mask. Bit 0 at a position means there is no match, Bit 1 means there
- * is a match and should be highlighted in the TextView.
- *
- * @param builder StringBuilder object
- * @param length Length of the desired mask.
- */
- private void constructEmptyMask(StringBuilder builder, int length) {
- for (int i = 0; i < length; ++i) {
- builder.append("0");
- }
- }
-
- /**
- * Replaces the 0-bit at a position with 1-bit, indicating that there is a match.
- *
- * @param builder StringBuilder object.
- * @param matchPos Match Positions to mask as 1.
- */
- private void replaceBitInMask(StringBuilder builder, SmartDialMatchPosition matchPos) {
- for (int i = matchPos.start; i < matchPos.end; ++i) {
- builder.replace(i, i + 1, "1");
+ // Whether or not we allow matches like 57 - (J)ohn (S)mith
+ private static final boolean ALLOW_INITIAL_MATCH = true;
+
+ // The maximum length of the initial we will match - typically set to 1 to minimize false
+ // positives
+ private static final int INITIAL_LENGTH_LIMIT = 1;
+
+ private final ArrayList matchPositions = new ArrayList<>();
+ private String query;
+
+ // Controls whether to treat an empty query as a match (with anything).
+ private boolean shouldMatchEmptyQuery = false;
+
+ public SmartDialNameMatcher(String query) {
+ this.query = query;
}
- }
-
- /**
- * Matches a phone number against a query. Let the test application overwrite the NANP setting.
- *
- * @param phoneNumber - Raw phone number
- * @param query - Normalized query (only contains numbers from 0-9)
- * @return {@literal null} if the number and the query don't match, a valid SmartDialMatchPosition
- * with the matching positions otherwise
- */
- @Nullable
- public SmartDialMatchPosition matchesNumber(Context context, String phoneNumber, String query) {
- if (TextUtils.isEmpty(phoneNumber)) {
- return shouldMatchEmptyQuery ? new SmartDialMatchPosition(0, 0) : null;
+
+ /**
+ * Strips a phone number of unnecessary characters (spaces, dashes, etc.)
+ *
+ * @param number Phone number we want to normalize
+ * @return Phone number consisting of digits from 0-9
+ */
+ public static String normalizeNumber(Context context, String number) {
+ return normalizeNumber(context, number, /* offset = */ 0);
}
- StringBuilder builder = new StringBuilder();
- constructEmptyMask(builder, phoneNumber.length());
-
- // Try matching the number as is
- SmartDialMatchPosition matchPos =
- matchesNumberWithOffset(context, phoneNumber, query, /* offset = */ 0);
- if (matchPos == null) {
- SmartDialPrefix.PhoneNumberTokens phoneNumberTokens = SmartDialPrefix.parsePhoneNumber(context, phoneNumber);
-
- if (phoneNumberTokens.countryCodeOffset != 0) {
- matchPos =
- matchesNumberWithOffset(
- context, phoneNumber, query, phoneNumberTokens.countryCodeOffset);
- }
- if (matchPos == null && phoneNumberTokens.nanpCodeOffset != 0) {
- matchPos =
- matchesNumberWithOffset(context, phoneNumber, query, phoneNumberTokens.nanpCodeOffset);
- }
+
+ /**
+ * Strips a phone number of unnecessary characters (spaces, dashes, etc.)
+ *
+ * @param number Phone number we want to normalize
+ * @param offset Offset to start from
+ * @return Phone number consisting of digits from 0-9
+ */
+ public static String normalizeNumber(Context context, String number, int offset) {
+ final StringBuilder s = new StringBuilder();
+ for (int i = offset; i < number.length(); i++) {
+ char ch = number.charAt(i);
+ if (CompositeSmartDialMap.isValidDialpadNumericChar(context, ch)) {
+ s.append(ch);
+ }
+ }
+ return s.toString();
}
- if (matchPos != null) {
- replaceBitInMask(builder, matchPos);
+
+ /**
+ * Constructs empty highlight mask. Bit 0 at a position means there is no match, Bit 1 means there
+ * is a match and should be highlighted in the TextView.
+ *
+ * @param builder StringBuilder object
+ * @param length Length of the desired mask.
+ */
+ private void constructEmptyMask(StringBuilder builder, int length) {
+ for (int i = 0; i < length; ++i) {
+ builder.append("0");
+ }
}
- return matchPos;
- }
-
- /**
- * Matches a phone number against the saved query, taking care of formatting characters and also
- * taking into account country code prefixes and special NANP number treatment.
- *
- * @param phoneNumber - Raw phone number
- * @return {@literal null} if the number and the query don't match, a valid SmartDialMatchPosition
- * with the matching positions otherwise
- */
- public SmartDialMatchPosition matchesNumber(Context context, String phoneNumber) {
- return matchesNumber(context, phoneNumber, query);
- }
-
- /**
- * Matches a phone number against a query, taking care of formatting characters
- *
- * @param phoneNumber - Raw phone number
- * @param query - Normalized query (only contains numbers from 0-9)
- * @param offset - The position in the number to start the match against (used to ignore leading
- * prefixes/country codes)
- * @return {@literal null} if the number and the query don't match, a valid SmartDialMatchPosition
- * with the matching positions otherwise
- */
- private SmartDialMatchPosition matchesNumberWithOffset(
- Context context, String phoneNumber, String query, int offset) {
- if (TextUtils.isEmpty(phoneNumber) || TextUtils.isEmpty(query)) {
- return shouldMatchEmptyQuery ? new SmartDialMatchPosition(offset, offset) : null;
+
+ /**
+ * Replaces the 0-bit at a position with 1-bit, indicating that there is a match.
+ *
+ * @param builder StringBuilder object.
+ * @param matchPos Match Positions to mask as 1.
+ */
+ private void replaceBitInMask(StringBuilder builder, SmartDialMatchPosition matchPos) {
+ for (int i = matchPos.start; i < matchPos.end; ++i) {
+ builder.replace(i, i + 1, "1");
+ }
}
- int queryAt = 0;
- int numberAt = offset;
- for (int i = offset; i < phoneNumber.length(); i++) {
- if (queryAt == query.length()) {
- break;
- }
- char ch = phoneNumber.charAt(i);
- if (CompositeSmartDialMap.isValidDialpadNumericChar(context, ch)) {
- if (ch != query.charAt(queryAt)) {
- return null;
+
+ /**
+ * Matches a phone number against a query. Let the test application overwrite the NANP setting.
+ *
+ * @param phoneNumber - Raw phone number
+ * @param query - Normalized query (only contains numbers from 0-9)
+ * @return {@literal null} if the number and the query don't match, a valid SmartDialMatchPosition
+ * with the matching positions otherwise
+ */
+ @Nullable
+ public SmartDialMatchPosition matchesNumber(Context context, String phoneNumber, String query) {
+ if (TextUtils.isEmpty(phoneNumber)) {
+ return shouldMatchEmptyQuery ? new SmartDialMatchPosition(0, 0) : null;
+ }
+ StringBuilder builder = new StringBuilder();
+ constructEmptyMask(builder, phoneNumber.length());
+
+ // Try matching the number as is
+ SmartDialMatchPosition matchPos =
+ matchesNumberWithOffset(context, phoneNumber, query, /* offset = */ 0);
+ if (matchPos == null) {
+ SmartDialPrefix.PhoneNumberTokens phoneNumberTokens = SmartDialPrefix.parsePhoneNumber(context, phoneNumber);
+
+ if (phoneNumberTokens.countryCodeOffset != 0) {
+ matchPos =
+ matchesNumberWithOffset(
+ context, phoneNumber, query, phoneNumberTokens.countryCodeOffset);
+ }
+ if (matchPos == null && phoneNumberTokens.nanpCodeOffset != 0) {
+ matchPos =
+ matchesNumberWithOffset(context, phoneNumber, query, phoneNumberTokens.nanpCodeOffset);
+ }
}
- queryAt++;
- } else {
- if (queryAt == 0) {
- // Found a separator before any part of the query was matched, so advance the
- // offset to avoid prematurely highlighting separators before the rest of the
- // query.
- // E.g. don't highlight the first '-' if we're matching 1-510-111-1111 with
- // '510'.
- // However, if the current offset is 0, just include the beginning separators
- // anyway, otherwise the highlighting ends up looking weird.
- // E.g. if we're matching (510)-111-1111 with '510', we should include the
- // first '('.
- if (offset != 0) {
- offset++;
- }
+ if (matchPos != null) {
+ replaceBitInMask(builder, matchPos);
}
- }
- numberAt++;
+ return matchPos;
}
- return new SmartDialMatchPosition(0 + offset, numberAt);
- }
-
- /**
- * This function iterates through each token in the display name, trying to match the query to the
- * numeric equivalent of the token.
- *
- * A token is defined as a range in the display name delimited by characters that have no latin
- * alphabet equivalents (e.g. spaces - ' ', periods - ',', underscores - '_' or chinese characters
- * - '王'). Transliteration from non-latin characters to latin character will be done on a best
- * effort basis - e.g. 'Ü' - 'u'.
- *
- *
For example, the display name "Phillips Thomas Jr" contains three tokens: "phillips",
- * "thomas", and "jr".
- *
- *
A match must begin at the start of a token. For example, typing 846(Tho) would match
- * "Phillips Thomas", but 466(hom) would not.
- *
- *
Also, a match can extend across tokens. For example, typing 37337(FredS) would match (Fred
- * S)mith.
- *
- * @param displayName The normalized(no accented characters) display name we intend to match
- * against.
- * @param query The string of digits that we want to match the display name to.
- * @param matchList An array list of {@link SmartDialMatchPosition}s that we add matched positions
- * to.
- * @return Returns true if a combination of the tokens in displayName match the query string
- * contained in query. If the function returns true, matchList will contain an ArrayList of
- * match positions (multiple matches correspond to initial matches).
- */
- private boolean matchesCombination(
- Context context,
- String displayName,
- String query,
- ArrayList matchList) {
- StringBuilder builder = new StringBuilder();
- constructEmptyMask(builder, displayName.length());
- final int nameLength = displayName.length();
- final int queryLength = query.length();
-
- if (nameLength < queryLength) {
- return false;
+
+ /**
+ * Matches a phone number against the saved query, taking care of formatting characters and also
+ * taking into account country code prefixes and special NANP number treatment.
+ *
+ * @param phoneNumber - Raw phone number
+ * @return {@literal null} if the number and the query don't match, a valid SmartDialMatchPosition
+ * with the matching positions otherwise
+ */
+ public SmartDialMatchPosition matchesNumber(Context context, String phoneNumber) {
+ return matchesNumber(context, phoneNumber, query);
}
- if (queryLength == 0) {
- return false;
+ /**
+ * Matches a phone number against a query, taking care of formatting characters
+ *
+ * @param phoneNumber - Raw phone number
+ * @param query - Normalized query (only contains numbers from 0-9)
+ * @param offset - The position in the number to start the match against (used to ignore leading
+ * prefixes/country codes)
+ * @return {@literal null} if the number and the query don't match, a valid SmartDialMatchPosition
+ * with the matching positions otherwise
+ */
+ private SmartDialMatchPosition matchesNumberWithOffset(
+ Context context, String phoneNumber, String query, int offset) {
+ if (TextUtils.isEmpty(phoneNumber) || TextUtils.isEmpty(query)) {
+ return shouldMatchEmptyQuery ? new SmartDialMatchPosition(offset, offset) : null;
+ }
+ int queryAt = 0;
+ int numberAt = offset;
+ for (int i = offset; i < phoneNumber.length(); i++) {
+ if (queryAt == query.length()) {
+ break;
+ }
+ char ch = phoneNumber.charAt(i);
+ if (CompositeSmartDialMap.isValidDialpadNumericChar(context, ch)) {
+ if (ch != query.charAt(queryAt)) {
+ return null;
+ }
+ queryAt++;
+ } else {
+ if (queryAt == 0) {
+ // Found a separator before any part of the query was matched, so advance the
+ // offset to avoid prematurely highlighting separators before the rest of the
+ // query.
+ // E.g. don't highlight the first '-' if we're matching 1-510-111-1111 with
+ // '510'.
+ // However, if the current offset is 0, just include the beginning separators
+ // anyway, otherwise the highlighting ends up looking weird.
+ // E.g. if we're matching (510)-111-1111 with '510', we should include the
+ // first '('.
+ if (offset != 0) {
+ offset++;
+ }
+ }
+ }
+ numberAt++;
+ }
+ return new SmartDialMatchPosition(0 + offset, numberAt);
}
- // The current character index in displayName
- // E.g. 3 corresponds to 'd' in "Fred Smith"
- int nameStart = 0;
-
- // The current character in the query we are trying to match the displayName against
- int queryStart = 0;
-
- // The start position of the current token we are inspecting
- int tokenStart = 0;
-
- // The number of non-alphabetic characters we've encountered so far in the current match.
- // E.g. if we've currently matched 3733764849 to (Fred Smith W)illiam, then the
- // seperatorCount should be 2. This allows us to correctly calculate offsets for the match
- // positions
- int seperatorCount = 0;
-
- ArrayList partial = new ArrayList();
- // Keep going until we reach the end of displayName
- while (nameStart < nameLength && queryStart < queryLength) {
- char ch = displayName.charAt(nameStart);
- // Strip diacritics from accented characters if any
- ch = CompositeSmartDialMap.normalizeCharacter(context, ch);
- if (CompositeSmartDialMap.isValidDialpadCharacter(context, ch)) {
- if (CompositeSmartDialMap.isValidDialpadAlphabeticChar(context, ch)) {
- ch = CompositeSmartDialMap.getDialpadNumericCharacter(context, ch);
+ /**
+ * This function iterates through each token in the display name, trying to match the query to the
+ * numeric equivalent of the token.
+ *
+ * A token is defined as a range in the display name delimited by characters that have no latin
+ * alphabet equivalents (e.g. spaces - ' ', periods - ',', underscores - '_' or chinese characters
+ * - '王'). Transliteration from non-latin characters to latin character will be done on a best
+ * effort basis - e.g. 'Ü' - 'u'.
+ *
+ *
For example, the display name "Phillips Thomas Jr" contains three tokens: "phillips",
+ * "thomas", and "jr".
+ *
+ *
A match must begin at the start of a token. For example, typing 846(Tho) would match
+ * "Phillips Thomas", but 466(hom) would not.
+ *
+ *
Also, a match can extend across tokens. For example, typing 37337(FredS) would match (Fred
+ * S)mith.
+ *
+ * @param displayName The normalized(no accented characters) display name we intend to match
+ * against.
+ * @param query The string of digits that we want to match the display name to.
+ * @param matchList An array list of {@link SmartDialMatchPosition}s that we add matched positions
+ * to.
+ * @return Returns true if a combination of the tokens in displayName match the query string
+ * contained in query. If the function returns true, matchList will contain an ArrayList of
+ * match positions (multiple matches correspond to initial matches).
+ */
+ private boolean matchesCombination(
+ Context context,
+ String displayName,
+ String query,
+ ArrayList matchList) {
+ StringBuilder builder = new StringBuilder();
+ constructEmptyMask(builder, displayName.length());
+ final int nameLength = displayName.length();
+ final int queryLength = query.length();
+
+ if (nameLength < queryLength) {
+ return false;
+ }
+
+ if (queryLength == 0) {
+ return false;
}
- if (ch != query.charAt(queryStart)) {
- // Failed to match the current character in the query.
-
- // Case 1: Failed to match the first character in the query. Skip to the next
- // token since there is no chance of this token matching the query.
-
- // Case 2: Previous characters in the query matched, but the current character
- // failed to match. This happened in the middle of a token. Skip to the next
- // token since there is no chance of this token matching the query.
-
- // Case 3: Previous characters in the query matched, but the current character
- // failed to match. This happened right at the start of the current token. In
- // this case, we should restart the query and try again with the current token.
- // Otherwise, we would fail to match a query like "964"(yog) against a name
- // Yo-Yoghurt because the query match would fail on the 3rd character, and
- // then skip to the end of the "Yoghurt" token.
-
- if (queryStart == 0
- || CompositeSmartDialMap.isValidDialpadCharacter(
- context,
- CompositeSmartDialMap.normalizeCharacter(
- context, displayName.charAt(nameStart - 1)))) {
- // skip to the next token, in the case of 1 or 2.
- while (nameStart < nameLength
- && CompositeSmartDialMap.isValidDialpadCharacter(
- context,
- CompositeSmartDialMap.normalizeCharacter(
- context, displayName.charAt(nameStart)))) {
- nameStart++;
+
+ // The current character index in displayName
+ // E.g. 3 corresponds to 'd' in "Fred Smith"
+ int nameStart = 0;
+
+ // The current character in the query we are trying to match the displayName against
+ int queryStart = 0;
+
+ // The start position of the current token we are inspecting
+ int tokenStart = 0;
+
+ // The number of non-alphabetic characters we've encountered so far in the current match.
+ // E.g. if we've currently matched 3733764849 to (Fred Smith W)illiam, then the
+ // seperatorCount should be 2. This allows us to correctly calculate offsets for the match
+ // positions
+ int seperatorCount = 0;
+
+ ArrayList partial = new ArrayList();
+ // Keep going until we reach the end of displayName
+ while (nameStart < nameLength && queryStart < queryLength) {
+ char ch = displayName.charAt(nameStart);
+ // Strip diacritics from accented characters if any
+ ch = CompositeSmartDialMap.normalizeCharacter(context, ch);
+ if (CompositeSmartDialMap.isValidDialpadCharacter(context, ch)) {
+ if (CompositeSmartDialMap.isValidDialpadAlphabeticChar(context, ch)) {
+ ch = CompositeSmartDialMap.getDialpadNumericCharacter(context, ch);
+ }
+ if (ch != query.charAt(queryStart)) {
+ // Failed to match the current character in the query.
+
+ // Case 1: Failed to match the first character in the query. Skip to the next
+ // token since there is no chance of this token matching the query.
+
+ // Case 2: Previous characters in the query matched, but the current character
+ // failed to match. This happened in the middle of a token. Skip to the next
+ // token since there is no chance of this token matching the query.
+
+ // Case 3: Previous characters in the query matched, but the current character
+ // failed to match. This happened right at the start of the current token. In
+ // this case, we should restart the query and try again with the current token.
+ // Otherwise, we would fail to match a query like "964"(yog) against a name
+ // Yo-Yoghurt because the query match would fail on the 3rd character, and
+ // then skip to the end of the "Yoghurt" token.
+
+ if (queryStart == 0
+ || CompositeSmartDialMap.isValidDialpadCharacter(
+ context,
+ CompositeSmartDialMap.normalizeCharacter(
+ context, displayName.charAt(nameStart - 1)))) {
+ // skip to the next token, in the case of 1 or 2.
+ while (nameStart < nameLength
+ && CompositeSmartDialMap.isValidDialpadCharacter(
+ context,
+ CompositeSmartDialMap.normalizeCharacter(
+ context, displayName.charAt(nameStart)))) {
+ nameStart++;
+ }
+ nameStart++;
+ }
+
+ // Restart the query and set the correct token position
+ queryStart = 0;
+ seperatorCount = 0;
+ tokenStart = nameStart;
+ } else {
+ if (queryStart == queryLength - 1) {
+
+ // As much as possible, we prioritize a full token match over a sub token
+ // one so if we find a full token match, we can return right away
+ matchList.add(
+ new SmartDialMatchPosition(tokenStart, queryLength + tokenStart + seperatorCount));
+ for (SmartDialMatchPosition match : matchList) {
+ replaceBitInMask(builder, match);
+ }
+ return true;
+ } else if (ALLOW_INITIAL_MATCH && queryStart < INITIAL_LENGTH_LIMIT) {
+ // we matched the first character.
+ // branch off and see if we can find another match with the remaining
+ // characters in the query string and the remaining tokens
+ // find the next separator in the query string
+ int j;
+ for (j = nameStart; j < nameLength; j++) {
+ if (!CompositeSmartDialMap.isValidDialpadCharacter(
+ context,
+ CompositeSmartDialMap.normalizeCharacter(context, displayName.charAt(j)))) {
+ break;
+ }
+ }
+ // this means there is at least one character left after the separator
+ if (j < nameLength - 1) {
+ final String remainder = displayName.substring(j + 1);
+ final ArrayList partialTemp = new ArrayList<>();
+ if (matchesCombination(
+ context, remainder, query.substring(queryStart + 1), partialTemp)) {
+
+ // store the list of possible match positions
+ SmartDialMatchPosition.advanceMatchPositions(partialTemp, j + 1);
+ partialTemp.add(0, new SmartDialMatchPosition(nameStart, nameStart + 1));
+ // we found a partial token match, store the data in a
+ // temp buffer and return it if we end up not finding a full
+ // token match
+ partial = partialTemp;
+ }
+ }
+ }
+ nameStart++;
+ queryStart++;
+ // we matched the current character in the name against one in the query,
+ // continue and see if the rest of the characters match
+ }
+ } else {
+ // found a separator, we skip this character and continue to the next one
+ nameStart++;
+ if (queryStart == 0) {
+ // This means we found a separator before the start of a token,
+ // so we should increment the token's start position to reflect its true
+ // start position
+ tokenStart = nameStart;
+ } else {
+ // Otherwise this separator was found in the middle of a token being matched,
+ // so increase the separator count
+ seperatorCount++;
+ }
}
- nameStart++;
- }
-
- // Restart the query and set the correct token position
- queryStart = 0;
- seperatorCount = 0;
- tokenStart = nameStart;
- } else {
- if (queryStart == queryLength - 1) {
-
- // As much as possible, we prioritize a full token match over a sub token
- // one so if we find a full token match, we can return right away
- matchList.add(
- new SmartDialMatchPosition(tokenStart, queryLength + tokenStart + seperatorCount));
+ }
+ // if we have no complete match at this point, then we attempt to fall back to the partial
+ // token match(if any). If we don't allow initial matching (ALLOW_INITIAL_MATCH = false)
+ // then partial will always be empty.
+ if (!partial.isEmpty()) {
+ matchList.addAll(partial);
for (SmartDialMatchPosition match : matchList) {
- replaceBitInMask(builder, match);
+ replaceBitInMask(builder, match);
}
return true;
- } else if (ALLOW_INITIAL_MATCH && queryStart < INITIAL_LENGTH_LIMIT) {
- // we matched the first character.
- // branch off and see if we can find another match with the remaining
- // characters in the query string and the remaining tokens
- // find the next separator in the query string
- int j;
- for (j = nameStart; j < nameLength; j++) {
- if (!CompositeSmartDialMap.isValidDialpadCharacter(
- context,
- CompositeSmartDialMap.normalizeCharacter(context, displayName.charAt(j)))) {
- break;
- }
- }
- // this means there is at least one character left after the separator
- if (j < nameLength - 1) {
- final String remainder = displayName.substring(j + 1);
- final ArrayList partialTemp = new ArrayList<>();
- if (matchesCombination(
- context, remainder, query.substring(queryStart + 1), partialTemp)) {
-
- // store the list of possible match positions
- SmartDialMatchPosition.advanceMatchPositions(partialTemp, j + 1);
- partialTemp.add(0, new SmartDialMatchPosition(nameStart, nameStart + 1));
- // we found a partial token match, store the data in a
- // temp buffer and return it if we end up not finding a full
- // token match
- partial = partialTemp;
- }
- }
- }
- nameStart++;
- queryStart++;
- // we matched the current character in the name against one in the query,
- // continue and see if the rest of the characters match
}
- } else {
- // found a separator, we skip this character and continue to the next one
- nameStart++;
- if (queryStart == 0) {
- // This means we found a separator before the start of a token,
- // so we should increment the token's start position to reflect its true
- // start position
- tokenStart = nameStart;
- } else {
- // Otherwise this separator was found in the middle of a token being matched,
- // so increase the separator count
- seperatorCount++;
- }
- }
+ return false;
+ }
+
+ /**
+ * This function iterates through each token in the display name, trying to match the query to the
+ * numeric equivalent of the token.
+ *
+ * A token is defined as a range in the display name delimited by characters that have no latin
+ * alphabet equivalents (e.g. spaces - ' ', periods - ',', underscores - '_' or chinese characters
+ * - '王'). Transliteration from non-latin characters to latin character will be done on a best
+ * effort basis - e.g. 'Ü' - 'u'.
+ *
+ *
For example, the display name "Phillips Thomas Jr" contains three tokens: "phillips",
+ * "thomas", and "jr".
+ *
+ *
A match must begin at the start of a token. For example, typing 846(Tho) would match
+ * "Phillips Thomas", but 466(hom) would not.
+ *
+ *
Also, a match can extend across tokens. For example, typing 37337(FredS) would match (Fred
+ * S)mith.
+ *
+ * @param displayName The normalized(no accented characters) display name we intend to match
+ * against.
+ * @return Returns true if a combination of the tokens in displayName match the query string
+ * contained in query. If the function returns true, matchList will contain an ArrayList of
+ * match positions (multiple matches correspond to initial matches).
+ */
+ public boolean matches(Context context, String displayName) {
+ matchPositions.clear();
+ return matchesCombination(context, displayName, query, matchPositions);
+ }
+
+ public ArrayList getMatchPositions() {
+ // Return a clone of mMatchPositions so that the caller can use it without
+ // worrying about it changing
+ return new ArrayList<>(matchPositions);
}
- // if we have no complete match at this point, then we attempt to fall back to the partial
- // token match(if any). If we don't allow initial matching (ALLOW_INITIAL_MATCH = false)
- // then partial will always be empty.
- if (!partial.isEmpty()) {
- matchList.addAll(partial);
- for (SmartDialMatchPosition match : matchList) {
- replaceBitInMask(builder, match);
- }
- return true;
+
+ public String getQuery() {
+ return query;
+ }
+
+ public void setQuery(String query) {
+ this.query = query;
+ }
+
+ public void setShouldMatchEmptyQuery(boolean matches) {
+ shouldMatchEmptyQuery = matches;
}
- return false;
- }
-
- /**
- * This function iterates through each token in the display name, trying to match the query to the
- * numeric equivalent of the token.
- *
- * A token is defined as a range in the display name delimited by characters that have no latin
- * alphabet equivalents (e.g. spaces - ' ', periods - ',', underscores - '_' or chinese characters
- * - '王'). Transliteration from non-latin characters to latin character will be done on a best
- * effort basis - e.g. 'Ü' - 'u'.
- *
- *
For example, the display name "Phillips Thomas Jr" contains three tokens: "phillips",
- * "thomas", and "jr".
- *
- *
A match must begin at the start of a token. For example, typing 846(Tho) would match
- * "Phillips Thomas", but 466(hom) would not.
- *
- *
Also, a match can extend across tokens. For example, typing 37337(FredS) would match (Fred
- * S)mith.
- *
- * @param displayName The normalized(no accented characters) display name we intend to match
- * against.
- * @return Returns true if a combination of the tokens in displayName match the query string
- * contained in query. If the function returns true, matchList will contain an ArrayList of
- * match positions (multiple matches correspond to initial matches).
- */
- public boolean matches(Context context, String displayName) {
- matchPositions.clear();
- return matchesCombination(context, displayName, query, matchPositions);
- }
-
- public ArrayList getMatchPositions() {
- // Return a clone of mMatchPositions so that the caller can use it without
- // worrying about it changing
- return new ArrayList<>(matchPositions);
- }
-
- public String getQuery() {
- return query;
- }
-
- public void setQuery(String query) {
- this.query = query;
- }
-
- public void setShouldMatchEmptyQuery(boolean matches) {
- shouldMatchEmptyQuery = matches;
- }
}
diff --git a/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/SmartDialPrefix.java b/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/SmartDialPrefix.java
index 01a84e8..b2553e8 100644
--- a/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/SmartDialPrefix.java
+++ b/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/SmartDialPrefix.java
@@ -37,589 +37,589 @@
*/
public class SmartDialPrefix {
- /**
- * The number of starting and ending tokens in a contact's name considered for initials. For
- * example, if both constants are set to 2, and a contact's name is "Albert Ben Charles Daniel Ed
- * Foster", the first two tokens "Albert" "Ben", and last two tokens "Ed" "Foster" can be replaced
- * by their initials in contact name matching. Users can look up this contact by combinations of
- * his initials such as "AF" "BF" "EF" "ABF" "BEF" "ABEF" etc, but can not use combinations such
- * as "CF" "DF" "ACF" "ADF" etc.
- */
- private static final int LAST_TOKENS_FOR_INITIALS = 2;
-
- private static final int FIRST_TOKENS_FOR_INITIALS = 2;
-
- /**
- * The country code of the user's sim card obtained by calling getSimCountryIso
- */
- private static final String PREF_USER_SIM_COUNTRY_CODE =
- "DialtactsActivity_user_sim_country_code";
-
- private static final String PREF_USER_SIM_COUNTRY_CODE_DEFAULT = null;
-
- private static String userSimCountryCode = PREF_USER_SIM_COUNTRY_CODE_DEFAULT;
- /**
- * Indicates whether user is in NANP regions.
- */
- private static boolean userInNanpRegion = false;
- /**
- * Set of country names that use NANP code.
- */
- private static Set nanpCountries = null;
- /**
- * Set of supported country codes in front of the phone number.
- */
- private static Set countryCodes = null;
-
- private static boolean nanpInitialized = false;
-
- /**
- * Initializes the Nanp settings, and finds out whether user is in a NANP region.
- */
- public static void initializeNanpSettings(Context context) {
- final TelephonyManager manager =
- (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
- if (manager != null) {
- userSimCountryCode = manager.getSimCountryIso();
- }
+ /**
+ * The number of starting and ending tokens in a contact's name considered for initials. For
+ * example, if both constants are set to 2, and a contact's name is "Albert Ben Charles Daniel Ed
+ * Foster", the first two tokens "Albert" "Ben", and last two tokens "Ed" "Foster" can be replaced
+ * by their initials in contact name matching. Users can look up this contact by combinations of
+ * his initials such as "AF" "BF" "EF" "ABF" "BEF" "ABEF" etc, but can not use combinations such
+ * as "CF" "DF" "ACF" "ADF" etc.
+ */
+ private static final int LAST_TOKENS_FOR_INITIALS = 2;
+
+ private static final int FIRST_TOKENS_FOR_INITIALS = 2;
+
+ /**
+ * The country code of the user's sim card obtained by calling getSimCountryIso
+ */
+ private static final String PREF_USER_SIM_COUNTRY_CODE =
+ "DialtactsActivity_user_sim_country_code";
+
+ private static final String PREF_USER_SIM_COUNTRY_CODE_DEFAULT = null;
+
+ private static String userSimCountryCode = PREF_USER_SIM_COUNTRY_CODE_DEFAULT;
+ /**
+ * Indicates whether user is in NANP regions.
+ */
+ private static boolean userInNanpRegion = false;
+ /**
+ * Set of country names that use NANP code.
+ */
+ private static Set nanpCountries = null;
+ /**
+ * Set of supported country codes in front of the phone number.
+ */
+ private static Set countryCodes = null;
+
+ private static boolean nanpInitialized = false;
+
+ /**
+ * Initializes the Nanp settings, and finds out whether user is in a NANP region.
+ */
+ public static void initializeNanpSettings(Context context) {
+ final TelephonyManager manager =
+ (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ if (manager != null) {
+ userSimCountryCode = manager.getSimCountryIso();
+ }
- final SharedPreferences prefs =
- PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext());
+ final SharedPreferences prefs =
+ PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext());
- if (userSimCountryCode != null) {
- /** Updates shared preferences with the latest country obtained from getSimCountryIso. */
- prefs.edit().putString(PREF_USER_SIM_COUNTRY_CODE, userSimCountryCode).apply();
- } else {
- /** Uses previously stored country code if loading fails. */
- userSimCountryCode =
- prefs.getString(PREF_USER_SIM_COUNTRY_CODE, PREF_USER_SIM_COUNTRY_CODE_DEFAULT);
+ if (userSimCountryCode != null) {
+ /** Updates shared preferences with the latest country obtained from getSimCountryIso. */
+ prefs.edit().putString(PREF_USER_SIM_COUNTRY_CODE, userSimCountryCode).apply();
+ } else {
+ /** Uses previously stored country code if loading fails. */
+ userSimCountryCode =
+ prefs.getString(PREF_USER_SIM_COUNTRY_CODE, PREF_USER_SIM_COUNTRY_CODE_DEFAULT);
+ }
+ /** Queries the NANP country list to find out whether user is in a NANP region. */
+ userInNanpRegion = isCountryNanp(userSimCountryCode);
+ nanpInitialized = true;
}
- /** Queries the NANP country list to find out whether user is in a NANP region. */
- userInNanpRegion = isCountryNanp(userSimCountryCode);
- nanpInitialized = true;
- }
-
- /**
- * Parses a contact's name into a list of separated tokens.
- *
- * @param contactName Contact's name stored in string.
- * @return A list of name tokens, for example separated first names, last name, etc.
- */
- public static ArrayList parseToIndexTokens(Context context, String contactName) {
- final int length = contactName.length();
- final ArrayList result = new ArrayList<>();
- char c;
- final StringBuilder currentIndexToken = new StringBuilder();
+
/**
- * Iterates through the whole name string. If the current character is a valid character, append
- * it to the current token. If the current character is not a valid character, for example space
- * " ", mark the current token as complete and add it to the list of tokens.
+ * Parses a contact's name into a list of separated tokens.
+ *
+ * @param contactName Contact's name stored in string.
+ * @return A list of name tokens, for example separated first names, last name, etc.
*/
- for (int i = 0; i < length; i++) {
- c = CompositeSmartDialMap.normalizeCharacter(context, contactName.charAt(i));
- if (CompositeSmartDialMap.isValidDialpadCharacter(context, c)) {
- /** Converts a character into the number on dialpad that represents the character. */
- currentIndexToken.append(CompositeSmartDialMap.getDialpadIndex(context, c));
- } else {
+ public static ArrayList parseToIndexTokens(Context context, String contactName) {
+ final int length = contactName.length();
+ final ArrayList result = new ArrayList<>();
+ char c;
+ final StringBuilder currentIndexToken = new StringBuilder();
+ /**
+ * Iterates through the whole name string. If the current character is a valid character, append
+ * it to the current token. If the current character is not a valid character, for example space
+ * " ", mark the current token as complete and add it to the list of tokens.
+ */
+ for (int i = 0; i < length; i++) {
+ c = CompositeSmartDialMap.normalizeCharacter(context, contactName.charAt(i));
+ if (CompositeSmartDialMap.isValidDialpadCharacter(context, c)) {
+ /** Converts a character into the number on dialpad that represents the character. */
+ currentIndexToken.append(CompositeSmartDialMap.getDialpadIndex(context, c));
+ } else {
+ if (currentIndexToken.length() != 0) {
+ result.add(currentIndexToken.toString());
+ }
+ currentIndexToken.delete(0, currentIndexToken.length());
+ }
+ }
+
+ /** Adds the last token in case it has not been added. */
if (currentIndexToken.length() != 0) {
- result.add(currentIndexToken.toString());
+ result.add(currentIndexToken.toString());
}
- currentIndexToken.delete(0, currentIndexToken.length());
- }
+ return result;
}
- /** Adds the last token in case it has not been added. */
- if (currentIndexToken.length() != 0) {
- result.add(currentIndexToken.toString());
- }
- return result;
- }
-
- /**
- * Generates a list of strings that any prefix of any string in the list can be used to look up
- * the contact's name.
- *
- * @param index The contact's name in string.
- * @return A List of strings, whose prefix can be used to look up the contact.
- */
- public static ArrayList generateNamePrefixes(Context context, String index) {
- final ArrayList result = new ArrayList<>();
-
- /** Parses the name into a list of tokens. */
- final ArrayList indexTokens = parseToIndexTokens(context, index);
-
- if (indexTokens.size() > 0) {
- /**
- * Adds the full token combinations to the list. For example, a contact with name "Albert Ben
- * Ed Foster" can be looked up by any prefix of the following strings "Foster" "EdFoster"
- * "BenEdFoster" and "AlbertBenEdFoster". This covers all cases of look up that contains only
- * one token, and that spans multiple continuous tokens.
- */
- final StringBuilder fullNameToken = new StringBuilder();
- for (int i = indexTokens.size() - 1; i >= 0; i--) {
- fullNameToken.insert(0, indexTokens.get(i));
- result.add(fullNameToken.toString());
- }
-
- /**
- * Adds initial combinations to the list, with the number of initials restricted by {@link
- * #LAST_TOKENS_FOR_INITIALS} and {@link #FIRST_TOKENS_FOR_INITIALS}. For example, a contact
- * with name "Albert Ben Ed Foster" can be looked up by any prefix of the following strings
- * "EFoster" "BFoster" "BEFoster" "AFoster" "ABFoster" "AEFoster" and "ABEFoster". This covers
- * all cases of initial lookup.
- */
- ArrayList fullNames = new ArrayList<>();
- fullNames.add(indexTokens.get(indexTokens.size() - 1));
- final int recursiveNameStart = result.size();
- int recursiveNameEnd = result.size();
- String initial = "";
- for (int i = indexTokens.size() - 2; i >= 0; i--) {
- if ((i >= indexTokens.size() - LAST_TOKENS_FOR_INITIALS)
- || (i < FIRST_TOKENS_FOR_INITIALS)) {
- initial = indexTokens.get(i).substring(0, 1);
-
- /** Recursively adds initial combinations to the list. */
- for (int j = 0; j < fullNames.size(); ++j) {
- result.add(initial + fullNames.get(j));
- }
- for (int j = recursiveNameStart; j < recursiveNameEnd; ++j) {
- result.add(initial + result.get(j));
- }
- recursiveNameEnd = result.size();
- final String currentFullName = fullNames.get(fullNames.size() - 1);
- fullNames.add(indexTokens.get(i) + currentFullName);
+ /**
+ * Generates a list of strings that any prefix of any string in the list can be used to look up
+ * the contact's name.
+ *
+ * @param index The contact's name in string.
+ * @return A List of strings, whose prefix can be used to look up the contact.
+ */
+ public static ArrayList generateNamePrefixes(Context context, String index) {
+ final ArrayList result = new ArrayList<>();
+
+ /** Parses the name into a list of tokens. */
+ final ArrayList indexTokens = parseToIndexTokens(context, index);
+
+ if (indexTokens.size() > 0) {
+ /**
+ * Adds the full token combinations to the list. For example, a contact with name "Albert Ben
+ * Ed Foster" can be looked up by any prefix of the following strings "Foster" "EdFoster"
+ * "BenEdFoster" and "AlbertBenEdFoster". This covers all cases of look up that contains only
+ * one token, and that spans multiple continuous tokens.
+ */
+ final StringBuilder fullNameToken = new StringBuilder();
+ for (int i = indexTokens.size() - 1; i >= 0; i--) {
+ fullNameToken.insert(0, indexTokens.get(i));
+ result.add(fullNameToken.toString());
+ }
+
+ /**
+ * Adds initial combinations to the list, with the number of initials restricted by {@link
+ * #LAST_TOKENS_FOR_INITIALS} and {@link #FIRST_TOKENS_FOR_INITIALS}. For example, a contact
+ * with name "Albert Ben Ed Foster" can be looked up by any prefix of the following strings
+ * "EFoster" "BFoster" "BEFoster" "AFoster" "ABFoster" "AEFoster" and "ABEFoster". This covers
+ * all cases of initial lookup.
+ */
+ ArrayList fullNames = new ArrayList<>();
+ fullNames.add(indexTokens.get(indexTokens.size() - 1));
+ final int recursiveNameStart = result.size();
+ int recursiveNameEnd = result.size();
+ String initial = "";
+ for (int i = indexTokens.size() - 2; i >= 0; i--) {
+ if ((i >= indexTokens.size() - LAST_TOKENS_FOR_INITIALS)
+ || (i < FIRST_TOKENS_FOR_INITIALS)) {
+ initial = indexTokens.get(i).substring(0, 1);
+
+ /** Recursively adds initial combinations to the list. */
+ for (int j = 0; j < fullNames.size(); ++j) {
+ result.add(initial + fullNames.get(j));
+ }
+ for (int j = recursiveNameStart; j < recursiveNameEnd; ++j) {
+ result.add(initial + result.get(j));
+ }
+ recursiveNameEnd = result.size();
+ final String currentFullName = fullNames.get(fullNames.size() - 1);
+ fullNames.add(indexTokens.get(i) + currentFullName);
+ }
+ }
}
- }
- }
- return result;
- }
-
- /**
- * Computes a list of number strings based on tokens of a given phone number. Any prefix of any
- * string in the list can be used to look up the phone number. The list include the full phone
- * number, the national number if there is a country code in the phone number, and the local
- * number if there is an area code in the phone number following the NANP format. For example, if
- * a user has phone number +41 71 394 8392, the list will contain 41713948392 and 713948392. Any
- * prefix to either of the strings can be used to look up the phone number. If a user has a phone
- * number +1 555-302-3029 (NANP format), the list will contain 15553023029, 5553023029, and
- * 3023029.
- *
- * @param number String of user's phone number.
- * @return A list of strings where any prefix of any entry can be used to look up the number.
- */
- public static ArrayList parseToNumberTokens(Context context, String number) {
- final ArrayList result = new ArrayList<>();
- if (!TextUtils.isEmpty(number)) {
- /** Adds the full number to the list. */
- result.add(SmartDialNameMatcher.normalizeNumber(context, number));
-
- final PhoneNumberTokens phoneNumberTokens = parsePhoneNumber(context, number);
- if (phoneNumberTokens == null) {
return result;
- }
-
- if (phoneNumberTokens.countryCodeOffset != 0) {
- result.add(
- SmartDialNameMatcher.normalizeNumber(
- context, number, phoneNumberTokens.countryCodeOffset));
- }
-
- if (phoneNumberTokens.nanpCodeOffset != 0) {
- result.add(
- SmartDialNameMatcher.normalizeNumber(
- context, number, phoneNumberTokens.nanpCodeOffset));
- }
}
- return result;
- }
-
- /**
- * Parses a phone number to find out whether it has country code and NANP area code.
- *
- * @param number Raw phone number.
- * @return a PhoneNumberToken instance with country code, NANP code information.
- */
- public static PhoneNumberTokens parsePhoneNumber(Context context, String number) {
- String countryCode = "";
- int countryCodeOffset = 0;
- int nanpNumberOffset = 0;
-
- if (!TextUtils.isEmpty(number)) {
- String normalizedNumber = SmartDialNameMatcher.normalizeNumber(context, number);
- if (number.charAt(0) == '+') {
- /** If the number starts with '+', tries to find valid country code. */
- for (int i = 1; i <= 1 + 3; i++) {
- if (number.length() <= i) {
- break;
- }
- countryCode = number.substring(1, i);
- if (isValidCountryCode(countryCode)) {
- countryCodeOffset = i;
- break;
- }
- }
- } else {
- /**
- * If the number does not start with '+', finds out whether it is in NANP format and has '1'
- * preceding the number.
- */
- if ((normalizedNumber.length() == 11)
- && (normalizedNumber.charAt(0) == '1')
- && (userInNanpRegion)) {
- countryCode = "1";
- countryCodeOffset = number.indexOf(normalizedNumber.charAt(1));
- if (countryCodeOffset == -1) {
- countryCodeOffset = 0;
- }
+
+ /**
+ * Computes a list of number strings based on tokens of a given phone number. Any prefix of any
+ * string in the list can be used to look up the phone number. The list include the full phone
+ * number, the national number if there is a country code in the phone number, and the local
+ * number if there is an area code in the phone number following the NANP format. For example, if
+ * a user has phone number +41 71 394 8392, the list will contain 41713948392 and 713948392. Any
+ * prefix to either of the strings can be used to look up the phone number. If a user has a phone
+ * number +1 555-302-3029 (NANP format), the list will contain 15553023029, 5553023029, and
+ * 3023029.
+ *
+ * @param number String of user's phone number.
+ * @return A list of strings where any prefix of any entry can be used to look up the number.
+ */
+ public static ArrayList parseToNumberTokens(Context context, String number) {
+ final ArrayList result = new ArrayList<>();
+ if (!TextUtils.isEmpty(number)) {
+ /** Adds the full number to the list. */
+ result.add(SmartDialNameMatcher.normalizeNumber(context, number));
+
+ final PhoneNumberTokens phoneNumberTokens = parsePhoneNumber(context, number);
+ if (phoneNumberTokens == null) {
+ return result;
+ }
+
+ if (phoneNumberTokens.countryCodeOffset != 0) {
+ result.add(
+ SmartDialNameMatcher.normalizeNumber(
+ context, number, phoneNumberTokens.countryCodeOffset));
+ }
+
+ if (phoneNumberTokens.nanpCodeOffset != 0) {
+ result.add(
+ SmartDialNameMatcher.normalizeNumber(
+ context, number, phoneNumberTokens.nanpCodeOffset));
+ }
}
- }
-
- /** If user is in NANP region, finds out whether a number is in NANP format. */
- if (userInNanpRegion) {
- String areaCode = "";
- if (countryCode.equals("") && normalizedNumber.length() == 10) {
- /**
- * if the number has no country code but fits the NANP format, extracts the NANP area
- * code, and finds out offset of the local number.
- */
- areaCode = normalizedNumber.substring(0, 3);
- } else if (countryCode.equals("1") && normalizedNumber.length() == 11) {
- /**
- * If the number has country code '1', finds out area code and offset of the local number.
- */
- areaCode = normalizedNumber.substring(1, 4);
+ return result;
+ }
+
+ /**
+ * Parses a phone number to find out whether it has country code and NANP area code.
+ *
+ * @param number Raw phone number.
+ * @return a PhoneNumberToken instance with country code, NANP code information.
+ */
+ public static PhoneNumberTokens parsePhoneNumber(Context context, String number) {
+ String countryCode = "";
+ int countryCodeOffset = 0;
+ int nanpNumberOffset = 0;
+
+ if (!TextUtils.isEmpty(number)) {
+ String normalizedNumber = SmartDialNameMatcher.normalizeNumber(context, number);
+ if (number.charAt(0) == '+') {
+ /** If the number starts with '+', tries to find valid country code. */
+ for (int i = 1; i <= 1 + 3; i++) {
+ if (number.length() <= i) {
+ break;
+ }
+ countryCode = number.substring(1, i);
+ if (isValidCountryCode(countryCode)) {
+ countryCodeOffset = i;
+ break;
+ }
+ }
+ } else {
+ /**
+ * If the number does not start with '+', finds out whether it is in NANP format and has '1'
+ * preceding the number.
+ */
+ if ((normalizedNumber.length() == 11)
+ && (normalizedNumber.charAt(0) == '1')
+ && (userInNanpRegion)) {
+ countryCode = "1";
+ countryCodeOffset = number.indexOf(normalizedNumber.charAt(1));
+ if (countryCodeOffset == -1) {
+ countryCodeOffset = 0;
+ }
+ }
+ }
+
+ /** If user is in NANP region, finds out whether a number is in NANP format. */
+ if (userInNanpRegion) {
+ String areaCode = "";
+ if (countryCode.equals("") && normalizedNumber.length() == 10) {
+ /**
+ * if the number has no country code but fits the NANP format, extracts the NANP area
+ * code, and finds out offset of the local number.
+ */
+ areaCode = normalizedNumber.substring(0, 3);
+ } else if (countryCode.equals("1") && normalizedNumber.length() == 11) {
+ /**
+ * If the number has country code '1', finds out area code and offset of the local number.
+ */
+ areaCode = normalizedNumber.substring(1, 4);
+ }
+ if (!areaCode.equals("")) {
+ final int areaCodeIndex = number.indexOf(areaCode);
+ if (areaCodeIndex != -1) {
+ nanpNumberOffset = number.indexOf(areaCode) + 3;
+ }
+ }
+ }
}
- if (!areaCode.equals("")) {
- final int areaCodeIndex = number.indexOf(areaCode);
- if (areaCodeIndex != -1) {
- nanpNumberOffset = number.indexOf(areaCode) + 3;
- }
+ return new PhoneNumberTokens(countryCode, countryCodeOffset, nanpNumberOffset);
+ }
+
+ /**
+ * Checkes whether a country code is valid.
+ */
+ private static boolean isValidCountryCode(String countryCode) {
+ if (countryCodes == null) {
+ countryCodes = initCountryCodes();
}
- }
+ return countryCodes.contains(countryCode);
}
- return new PhoneNumberTokens(countryCode, countryCodeOffset, nanpNumberOffset);
- }
-
- /**
- * Checkes whether a country code is valid.
- */
- private static boolean isValidCountryCode(String countryCode) {
- if (countryCodes == null) {
- countryCodes = initCountryCodes();
+
+ private static Set initCountryCodes() {
+ final HashSet result = new HashSet();
+ result.add("1");
+ result.add("7");
+ result.add("20");
+ result.add("27");
+ result.add("30");
+ result.add("31");
+ result.add("32");
+ result.add("33");
+ result.add("34");
+ result.add("36");
+ result.add("39");
+ result.add("40");
+ result.add("41");
+ result.add("43");
+ result.add("44");
+ result.add("45");
+ result.add("46");
+ result.add("47");
+ result.add("48");
+ result.add("49");
+ result.add("51");
+ result.add("52");
+ result.add("53");
+ result.add("54");
+ result.add("55");
+ result.add("56");
+ result.add("57");
+ result.add("58");
+ result.add("60");
+ result.add("61");
+ result.add("62");
+ result.add("63");
+ result.add("64");
+ result.add("65");
+ result.add("66");
+ result.add("81");
+ result.add("82");
+ result.add("84");
+ result.add("86");
+ result.add("90");
+ result.add("91");
+ result.add("92");
+ result.add("93");
+ result.add("94");
+ result.add("95");
+ result.add("98");
+ result.add("211");
+ result.add("212");
+ result.add("213");
+ result.add("216");
+ result.add("218");
+ result.add("220");
+ result.add("221");
+ result.add("222");
+ result.add("223");
+ result.add("224");
+ result.add("225");
+ result.add("226");
+ result.add("227");
+ result.add("228");
+ result.add("229");
+ result.add("230");
+ result.add("231");
+ result.add("232");
+ result.add("233");
+ result.add("234");
+ result.add("235");
+ result.add("236");
+ result.add("237");
+ result.add("238");
+ result.add("239");
+ result.add("240");
+ result.add("241");
+ result.add("242");
+ result.add("243");
+ result.add("244");
+ result.add("245");
+ result.add("246");
+ result.add("247");
+ result.add("248");
+ result.add("249");
+ result.add("250");
+ result.add("251");
+ result.add("252");
+ result.add("253");
+ result.add("254");
+ result.add("255");
+ result.add("256");
+ result.add("257");
+ result.add("258");
+ result.add("260");
+ result.add("261");
+ result.add("262");
+ result.add("263");
+ result.add("264");
+ result.add("265");
+ result.add("266");
+ result.add("267");
+ result.add("268");
+ result.add("269");
+ result.add("290");
+ result.add("291");
+ result.add("297");
+ result.add("298");
+ result.add("299");
+ result.add("350");
+ result.add("351");
+ result.add("352");
+ result.add("353");
+ result.add("354");
+ result.add("355");
+ result.add("356");
+ result.add("357");
+ result.add("358");
+ result.add("359");
+ result.add("370");
+ result.add("371");
+ result.add("372");
+ result.add("373");
+ result.add("374");
+ result.add("375");
+ result.add("376");
+ result.add("377");
+ result.add("378");
+ result.add("379");
+ result.add("380");
+ result.add("381");
+ result.add("382");
+ result.add("385");
+ result.add("386");
+ result.add("387");
+ result.add("389");
+ result.add("420");
+ result.add("421");
+ result.add("423");
+ result.add("500");
+ result.add("501");
+ result.add("502");
+ result.add("503");
+ result.add("504");
+ result.add("505");
+ result.add("506");
+ result.add("507");
+ result.add("508");
+ result.add("509");
+ result.add("590");
+ result.add("591");
+ result.add("592");
+ result.add("593");
+ result.add("594");
+ result.add("595");
+ result.add("596");
+ result.add("597");
+ result.add("598");
+ result.add("599");
+ result.add("670");
+ result.add("672");
+ result.add("673");
+ result.add("674");
+ result.add("675");
+ result.add("676");
+ result.add("677");
+ result.add("678");
+ result.add("679");
+ result.add("680");
+ result.add("681");
+ result.add("682");
+ result.add("683");
+ result.add("685");
+ result.add("686");
+ result.add("687");
+ result.add("688");
+ result.add("689");
+ result.add("690");
+ result.add("691");
+ result.add("692");
+ result.add("800");
+ result.add("808");
+ result.add("850");
+ result.add("852");
+ result.add("853");
+ result.add("855");
+ result.add("856");
+ result.add("870");
+ result.add("878");
+ result.add("880");
+ result.add("881");
+ result.add("882");
+ result.add("883");
+ result.add("886");
+ result.add("888");
+ result.add("960");
+ result.add("961");
+ result.add("962");
+ result.add("963");
+ result.add("964");
+ result.add("965");
+ result.add("966");
+ result.add("967");
+ result.add("968");
+ result.add("970");
+ result.add("971");
+ result.add("972");
+ result.add("973");
+ result.add("974");
+ result.add("975");
+ result.add("976");
+ result.add("977");
+ result.add("979");
+ result.add("992");
+ result.add("993");
+ result.add("994");
+ result.add("995");
+ result.add("996");
+ result.add("998");
+ return result;
}
- return countryCodes.contains(countryCode);
- }
-
- private static Set initCountryCodes() {
- final HashSet result = new HashSet();
- result.add("1");
- result.add("7");
- result.add("20");
- result.add("27");
- result.add("30");
- result.add("31");
- result.add("32");
- result.add("33");
- result.add("34");
- result.add("36");
- result.add("39");
- result.add("40");
- result.add("41");
- result.add("43");
- result.add("44");
- result.add("45");
- result.add("46");
- result.add("47");
- result.add("48");
- result.add("49");
- result.add("51");
- result.add("52");
- result.add("53");
- result.add("54");
- result.add("55");
- result.add("56");
- result.add("57");
- result.add("58");
- result.add("60");
- result.add("61");
- result.add("62");
- result.add("63");
- result.add("64");
- result.add("65");
- result.add("66");
- result.add("81");
- result.add("82");
- result.add("84");
- result.add("86");
- result.add("90");
- result.add("91");
- result.add("92");
- result.add("93");
- result.add("94");
- result.add("95");
- result.add("98");
- result.add("211");
- result.add("212");
- result.add("213");
- result.add("216");
- result.add("218");
- result.add("220");
- result.add("221");
- result.add("222");
- result.add("223");
- result.add("224");
- result.add("225");
- result.add("226");
- result.add("227");
- result.add("228");
- result.add("229");
- result.add("230");
- result.add("231");
- result.add("232");
- result.add("233");
- result.add("234");
- result.add("235");
- result.add("236");
- result.add("237");
- result.add("238");
- result.add("239");
- result.add("240");
- result.add("241");
- result.add("242");
- result.add("243");
- result.add("244");
- result.add("245");
- result.add("246");
- result.add("247");
- result.add("248");
- result.add("249");
- result.add("250");
- result.add("251");
- result.add("252");
- result.add("253");
- result.add("254");
- result.add("255");
- result.add("256");
- result.add("257");
- result.add("258");
- result.add("260");
- result.add("261");
- result.add("262");
- result.add("263");
- result.add("264");
- result.add("265");
- result.add("266");
- result.add("267");
- result.add("268");
- result.add("269");
- result.add("290");
- result.add("291");
- result.add("297");
- result.add("298");
- result.add("299");
- result.add("350");
- result.add("351");
- result.add("352");
- result.add("353");
- result.add("354");
- result.add("355");
- result.add("356");
- result.add("357");
- result.add("358");
- result.add("359");
- result.add("370");
- result.add("371");
- result.add("372");
- result.add("373");
- result.add("374");
- result.add("375");
- result.add("376");
- result.add("377");
- result.add("378");
- result.add("379");
- result.add("380");
- result.add("381");
- result.add("382");
- result.add("385");
- result.add("386");
- result.add("387");
- result.add("389");
- result.add("420");
- result.add("421");
- result.add("423");
- result.add("500");
- result.add("501");
- result.add("502");
- result.add("503");
- result.add("504");
- result.add("505");
- result.add("506");
- result.add("507");
- result.add("508");
- result.add("509");
- result.add("590");
- result.add("591");
- result.add("592");
- result.add("593");
- result.add("594");
- result.add("595");
- result.add("596");
- result.add("597");
- result.add("598");
- result.add("599");
- result.add("670");
- result.add("672");
- result.add("673");
- result.add("674");
- result.add("675");
- result.add("676");
- result.add("677");
- result.add("678");
- result.add("679");
- result.add("680");
- result.add("681");
- result.add("682");
- result.add("683");
- result.add("685");
- result.add("686");
- result.add("687");
- result.add("688");
- result.add("689");
- result.add("690");
- result.add("691");
- result.add("692");
- result.add("800");
- result.add("808");
- result.add("850");
- result.add("852");
- result.add("853");
- result.add("855");
- result.add("856");
- result.add("870");
- result.add("878");
- result.add("880");
- result.add("881");
- result.add("882");
- result.add("883");
- result.add("886");
- result.add("888");
- result.add("960");
- result.add("961");
- result.add("962");
- result.add("963");
- result.add("964");
- result.add("965");
- result.add("966");
- result.add("967");
- result.add("968");
- result.add("970");
- result.add("971");
- result.add("972");
- result.add("973");
- result.add("974");
- result.add("975");
- result.add("976");
- result.add("977");
- result.add("979");
- result.add("992");
- result.add("993");
- result.add("994");
- result.add("995");
- result.add("996");
- result.add("998");
- return result;
- }
-
- /**
- * Indicates whether the given country uses NANP numbers
- *
- * @param country ISO 3166 country code (case doesn't matter)
- * @return True if country uses NANP numbers (e.g. US, Canada), false otherwise
- * @see
- * https://en.wikipedia.org/wiki/North_American_Numbering_Plan
- */
- @VisibleForTesting
- public static boolean isCountryNanp(String country) {
- if (TextUtils.isEmpty(country)) {
- return false;
+
+ /**
+ * Indicates whether the given country uses NANP numbers
+ *
+ * @param country ISO 3166 country code (case doesn't matter)
+ * @return True if country uses NANP numbers (e.g. US, Canada), false otherwise
+ * @see
+ * https://en.wikipedia.org/wiki/North_American_Numbering_Plan
+ */
+ @VisibleForTesting
+ public static boolean isCountryNanp(String country) {
+ if (TextUtils.isEmpty(country)) {
+ return false;
+ }
+ if (nanpCountries == null) {
+ nanpCountries = initNanpCountries();
+ }
+ return nanpCountries.contains(country.toUpperCase());
}
- if (nanpCountries == null) {
- nanpCountries = initNanpCountries();
+
+ private static Set initNanpCountries() {
+ final HashSet result = new HashSet();
+ result.add("US"); // United States
+ result.add("CA"); // Canada
+ result.add("AS"); // American Samoa
+ result.add("AI"); // Anguilla
+ result.add("AG"); // Antigua and Barbuda
+ result.add("BS"); // Bahamas
+ result.add("BB"); // Barbados
+ result.add("BM"); // Bermuda
+ result.add("VG"); // British Virgin Islands
+ result.add("KY"); // Cayman Islands
+ result.add("DM"); // Dominica
+ result.add("DO"); // Dominican Republic
+ result.add("GD"); // Grenada
+ result.add("GU"); // Guam
+ result.add("JM"); // Jamaica
+ result.add("PR"); // Puerto Rico
+ result.add("MS"); // Montserrat
+ result.add("MP"); // Northern Mariana Islands
+ result.add("KN"); // Saint Kitts and Nevis
+ result.add("LC"); // Saint Lucia
+ result.add("VC"); // Saint Vincent and the Grenadines
+ result.add("TT"); // Trinidad and Tobago
+ result.add("TC"); // Turks and Caicos Islands
+ result.add("VI"); // U.S. Virgin Islands
+ return result;
}
- return nanpCountries.contains(country.toUpperCase());
- }
-
- private static Set initNanpCountries() {
- final HashSet result = new HashSet();
- result.add("US"); // United States
- result.add("CA"); // Canada
- result.add("AS"); // American Samoa
- result.add("AI"); // Anguilla
- result.add("AG"); // Antigua and Barbuda
- result.add("BS"); // Bahamas
- result.add("BB"); // Barbados
- result.add("BM"); // Bermuda
- result.add("VG"); // British Virgin Islands
- result.add("KY"); // Cayman Islands
- result.add("DM"); // Dominica
- result.add("DO"); // Dominican Republic
- result.add("GD"); // Grenada
- result.add("GU"); // Guam
- result.add("JM"); // Jamaica
- result.add("PR"); // Puerto Rico
- result.add("MS"); // Montserrat
- result.add("MP"); // Northern Mariana Islands
- result.add("KN"); // Saint Kitts and Nevis
- result.add("LC"); // Saint Lucia
- result.add("VC"); // Saint Vincent and the Grenadines
- result.add("TT"); // Trinidad and Tobago
- result.add("TC"); // Turks and Caicos Islands
- result.add("VI"); // U.S. Virgin Islands
- return result;
- }
-
- /**
- * Returns whether the user is in a region that uses Nanp format based on the sim location.
- *
- * @return Whether user is in Nanp region.
- */
- public static boolean getUserInNanpRegion() {
- return userInNanpRegion;
- }
-
- /**
- * Explicitly setting the user Nanp to the given boolean
- */
- @VisibleForTesting
- public static void setUserInNanpRegion(boolean userInNanpRegion) {
- SmartDialPrefix.userInNanpRegion = userInNanpRegion;
- }
-
- /**
- * Class to record phone number parsing information.
- */
- public static class PhoneNumberTokens {
/**
- * Country code of the phone number.
+ * Returns whether the user is in a region that uses Nanp format based on the sim location.
+ *
+ * @return Whether user is in Nanp region.
*/
- final String countryCode;
+ public static boolean getUserInNanpRegion() {
+ return userInNanpRegion;
+ }
/**
- * Offset of national number after the country code.
+ * Explicitly setting the user Nanp to the given boolean
*/
- final int countryCodeOffset;
+ @VisibleForTesting
+ public static void setUserInNanpRegion(boolean userInNanpRegion) {
+ SmartDialPrefix.userInNanpRegion = userInNanpRegion;
+ }
/**
- * Offset of local number after NANP area code.
+ * Class to record phone number parsing information.
*/
- final int nanpCodeOffset;
+ public static class PhoneNumberTokens {
+
+ /**
+ * Country code of the phone number.
+ */
+ final String countryCode;
+
+ /**
+ * Offset of national number after the country code.
+ */
+ final int countryCodeOffset;
- public PhoneNumberTokens(String countryCode, int countryCodeOffset, int nanpCodeOffset) {
- this.countryCode = countryCode;
- this.countryCodeOffset = countryCodeOffset;
- this.nanpCodeOffset = nanpCodeOffset;
+ /**
+ * Offset of local number after NANP area code.
+ */
+ final int nanpCodeOffset;
+
+ public PhoneNumberTokens(String countryCode, int countryCodeOffset, int nanpCodeOffset) {
+ this.countryCode = countryCode;
+ this.countryCodeOffset = countryCodeOffset;
+ this.nanpCodeOffset = nanpCodeOffset;
+ }
}
- }
}
diff --git a/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/UriUtils.java b/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/UriUtils.java
index 1c33327..05cfb91 100644
--- a/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/UriUtils.java
+++ b/core/aosp/src/main/java/dev/alenajam/opendialer/core/aosp/UriUtils.java
@@ -24,63 +24,63 @@
*/
public class UriUtils {
- private static final String LOOKUP_URI_ENCODED = "encoded";
+ private static final String LOOKUP_URI_ENCODED = "encoded";
- /**
- * Static helper, not instantiable.
- */
- private UriUtils() {
- }
-
- /**
- * Checks whether two URI are equal, taking care of the case where either is null.
- */
- public static boolean areEqual(Uri uri1, Uri uri2) {
- if (uri1 == null && uri2 == null) {
- return true;
- }
- if (uri1 == null || uri2 == null) {
- return false;
+ /**
+ * Static helper, not instantiable.
+ */
+ private UriUtils() {
}
- return uri1.equals(uri2);
- }
- /**
- * Parses a string into a URI and returns null if the given string is null.
- */
- public static Uri parseUriOrNull(String uriString) {
- if (uriString == null) {
- return null;
+ /**
+ * Checks whether two URI are equal, taking care of the case where either is null.
+ */
+ public static boolean areEqual(Uri uri1, Uri uri2) {
+ if (uri1 == null && uri2 == null) {
+ return true;
+ }
+ if (uri1 == null || uri2 == null) {
+ return false;
+ }
+ return uri1.equals(uri2);
}
- return Uri.parse(uriString);
- }
- /**
- * Converts a URI into a string, returns null if the given URI is null.
- */
- public static String uriToString(Uri uri) {
- return uri == null ? null : uri.toString();
- }
+ /**
+ * Parses a string into a URI and returns null if the given string is null.
+ */
+ public static Uri parseUriOrNull(String uriString) {
+ if (uriString == null) {
+ return null;
+ }
+ return Uri.parse(uriString);
+ }
- public static boolean isEncodedContactUri(Uri uri) {
- if (uri == null) {
- return false;
+ /**
+ * Converts a URI into a string, returns null if the given URI is null.
+ */
+ public static String uriToString(Uri uri) {
+ return uri == null ? null : uri.toString();
}
- final String lastPathSegment = uri.getLastPathSegment();
- if (lastPathSegment == null) {
- return false;
+
+ public static boolean isEncodedContactUri(Uri uri) {
+ if (uri == null) {
+ return false;
+ }
+ final String lastPathSegment = uri.getLastPathSegment();
+ if (lastPathSegment == null) {
+ return false;
+ }
+ return lastPathSegment.equals(LOOKUP_URI_ENCODED);
}
- return lastPathSegment.equals(LOOKUP_URI_ENCODED);
- }
- /**
- * @return {@code uri} as-is if the authority is of contacts provider. Otherwise or {@code uri} is
- * null, return null otherwise
- */
- public static Uri nullForNonContactsUri(Uri uri) {
- if (uri == null) {
- return null;
+ /**
+ * @return {@code uri} as-is if the authority is of contacts provider. Otherwise or {@code uri} is
+ * null, return null otherwise
+ */
+ public static Uri nullForNonContactsUri(Uri uri) {
+ if (uri == null) {
+ return null;
+ }
+ return ContactsContract.AUTHORITY.equals(uri.getAuthority()) ? uri : null;
}
- return ContactsContract.AUTHORITY.equals(uri.getAuthority()) ? uri : null;
- }
}
diff --git a/core/aosp/src/test/java/dev/alenajam/opendialer/core/aosp/ExampleUnitTest.kt b/core/aosp/src/test/java/dev/alenajam/opendialer/core/aosp/ExampleUnitTest.kt
index 3983006..f05e498 100644
--- a/core/aosp/src/test/java/dev/alenajam/opendialer/core/aosp/ExampleUnitTest.kt
+++ b/core/aosp/src/test/java/dev/alenajam/opendialer/core/aosp/ExampleUnitTest.kt
@@ -1,8 +1,7 @@
package dev.alenajam.opendialer.core.aosp
-import org.junit.Test
-
import org.junit.Assert.*
+import org.junit.Test
/**
* Example local unit test, which will execute on the development machine (host).
@@ -10,8 +9,8 @@ import org.junit.Assert.*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
- @Test
- fun addition_isCorrect() {
- assertEquals(4, 2 + 2)
- }
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
}
\ No newline at end of file
diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts
index b4aa165..93b27c9 100644
--- a/core/common/build.gradle.kts
+++ b/core/common/build.gradle.kts
@@ -1,47 +1,63 @@
plugins {
- id("com.android.library")
- id("org.jetbrains.kotlin.android")
- id("kotlin-kapt")
+ id("com.android.library")
+ id("org.jetbrains.kotlin.android")
+ id("kotlin-kapt")
+ alias(libs.plugins.compose.compiler)
}
android {
- namespace = "dev.alenajam.opendialer.core.common"
- compileSdk = 34
+ namespace = "dev.alenajam.opendialer.core.common"
+ compileSdk = 34
- defaultConfig {
- minSdk = 24
+ defaultConfig {
+ minSdk = 24
- testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
- consumerProguardFiles("consumer-rules.pro")
- }
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles("consumer-rules.pro")
+ }
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ }
+ }
+ buildFeatures {
+ viewBinding = true
+ compose = true
}
- }
- buildFeatures {
- viewBinding = true
- }
}
dependencies {
-
- implementation("androidx.core:core-ktx:1.9.0")
- implementation("androidx.appcompat:appcompat:1.6.1")
- implementation("com.google.android.material:material:1.10.0")
- testImplementation("junit:junit:4.13.2")
- androidTestImplementation("androidx.test.ext:junit:1.1.5")
- androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
-
- implementation("com.squareup.picasso:picasso:2.71828")
- implementation("androidx.navigation:navigation-fragment-ktx:2.7.5")
- implementation("androidx.navigation:navigation-ui-ktx:2.7.5")
- implementation("androidx.preference:preference-ktx:1.2.1")
- implementation("com.google.code.gson:gson:2.9.0")
+ implementation("androidx.core:core-ktx:1.9.0")
+ implementation("androidx.appcompat:appcompat:1.6.1")
+ implementation("com.google.android.material:material:1.10.0")
+ testImplementation("junit:junit:4.13.2")
+ androidTestImplementation("androidx.test.ext:junit:1.1.5")
+ androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
+
+ implementation("com.squareup.picasso:picasso:2.71828")
+ implementation("androidx.navigation:navigation-fragment-ktx:2.7.5")
+ implementation("androidx.navigation:navigation-ui-ktx:2.7.5")
+ implementation("androidx.preference:preference-ktx:1.2.1")
+ implementation("com.google.code.gson:gson:2.9.0")
+
+ val composeBom = platform("androidx.compose:compose-bom:2024.09.03")
+ implementation(composeBom)
+ androidTestImplementation(composeBom)
+ implementation(libs.androidx.material3)
+ implementation("androidx.compose.ui:ui-tooling-preview")
+ debugImplementation("androidx.compose.ui:ui-tooling")
+ androidTestImplementation("androidx.compose.ui:ui-test-junit4")
+ debugImplementation("androidx.compose.ui:ui-test-manifest")
+ implementation("androidx.compose.material:material-icons-extended")
+ implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2")
+ implementation("androidx.compose.runtime:runtime-livedata")
}
kotlin {
- jvmToolchain(17)
+ jvmToolchain(17)
}
\ No newline at end of file
diff --git a/core/common/src/androidTest/java/dev/alenajam/opendialer/core/common/ExampleInstrumentedTest.kt b/core/common/src/androidTest/java/dev/alenajam/opendialer/core/common/ExampleInstrumentedTest.kt
index 5ac3bcd..c60c39b 100644
--- a/core/common/src/androidTest/java/dev/alenajam/opendialer/core/common/ExampleInstrumentedTest.kt
+++ b/core/common/src/androidTest/java/dev/alenajam/opendialer/core/common/ExampleInstrumentedTest.kt
@@ -1,13 +1,11 @@
package dev.alenajam.opendialer.core.common
-import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
-
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assert.*
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.Assert.*
-
/**
* Instrumented test, which will execute on an Android device.
*
@@ -15,10 +13,10 @@ import org.junit.Assert.*
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
- @Test
- fun useAppContext() {
- // Context of the app under test.
- val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("dev.alenajam.opendialer.core.common.test", appContext.packageName)
- }
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("dev.alenajam.opendialer.core.common.test", appContext.packageName)
+ }
}
\ No newline at end of file
diff --git a/core/common/src/main/AndroidManifest.xml b/core/common/src/main/AndroidManifest.xml
index a5918e6..44008a4 100644
--- a/core/common/src/main/AndroidManifest.xml
+++ b/core/common/src/main/AndroidManifest.xml
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/BackPressedListener.kt b/core/common/src/main/java/dev/alenajam/opendialer/core/common/BackPressedListener.kt
deleted file mode 100644
index ab65c9a..0000000
--- a/core/common/src/main/java/dev/alenajam/opendialer/core/common/BackPressedListener.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package dev.alenajam.opendialer.core.common
-
-interface BackPressedListener {
- /** Should return false to override default behaviour */
- fun onBackPressed(): Boolean
-}
\ No newline at end of file
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/CircleTransform.java b/core/common/src/main/java/dev/alenajam/opendialer/core/common/CircleTransform.java
deleted file mode 100644
index ab4527f..0000000
--- a/core/common/src/main/java/dev/alenajam/opendialer/core/common/CircleTransform.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2014 Julian Shen
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dev.alenajam.opendialer.core.common;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-
-import com.squareup.picasso.Transformation;
-
-/**
- * Created by julian on 13/6/21.
- */
-public class CircleTransform implements Transformation {
- @Override
- public Bitmap transform(Bitmap source) {
- int size = Math.min(source.getWidth(), source.getHeight());
-
- int x = (source.getWidth() - size) / 2;
- int y = (source.getHeight() - size) / 2;
-
- Bitmap squaredBitmap = Bitmap.createBitmap(source, x, y, size, size);
- if (squaredBitmap != source) {
- source.recycle();
- }
-
- Bitmap bitmap = Bitmap.createBitmap(size, size, source.getConfig());
-
- Canvas canvas = new Canvas(bitmap);
- Paint paint = new Paint();
- BitmapShader shader = new BitmapShader(squaredBitmap, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);
- paint.setShader(shader);
- paint.setAntiAlias(true);
-
- float r = size / 2f;
- canvas.drawCircle(r, r, r, paint);
-
- squaredBitmap.recycle();
- return bitmap;
- }
-
- @Override
- public String key() {
- return "circle";
- }
-}
\ No newline at end of file
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/CommonUtils.java b/core/common/src/main/java/dev/alenajam/opendialer/core/common/CommonUtils.java
index 2bed1d8..c5787ce 100644
--- a/core/common/src/main/java/dev/alenajam/opendialer/core/common/CommonUtils.java
+++ b/core/common/src/main/java/dev/alenajam/opendialer/core/common/CommonUtils.java
@@ -40,195 +40,200 @@
public abstract class CommonUtils {
- @SuppressLint("DefaultLocale")
- public static String getDurationTimeString(long durationMilliseconds) {
-
- return String.format("%02d:%02d:%02d",
- TimeUnit.MILLISECONDS.toHours(durationMilliseconds),
- TimeUnit.MILLISECONDS.toMinutes(durationMilliseconds) -
- TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(durationMilliseconds)),
- TimeUnit.MILLISECONDS.toSeconds(durationMilliseconds) -
- TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(durationMilliseconds))
- );
- }
-
- @SuppressLint("DefaultLocale")
- public static String getDurationTimeStringMinimal(long durationMilliseconds) {
- long hours = TimeUnit.MILLISECONDS.toHours(durationMilliseconds);
- long minutes = TimeUnit.MILLISECONDS.toMinutes(durationMilliseconds) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(durationMilliseconds));
- long seconds = TimeUnit.MILLISECONDS.toSeconds(durationMilliseconds) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(durationMilliseconds));
-
- String timeString = "";
- if (hours > 0) timeString = timeString.concat(hours + "h ");
- if (minutes > 0) timeString = timeString.concat(minutes + "m ");
- if (seconds > 0) timeString = timeString.concat(seconds + "s");
-
- return timeString.isEmpty() ? "0s" : timeString;
- }
-
- public static Bitmap textToBitmap(Context context, String messageText, float textSize, int textColor) {
- Paint paint = new Paint();
- int pixelTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- textSize, context.getResources().getDisplayMetrics());
- paint.setTextSize(pixelTextSize);
- paint.setColor(textColor);
- paint.setTextAlign(Paint.Align.LEFT);
-
- float baseline = -paint.ascent() + 10; // ascent() is negative
- int width = (int) (paint.measureText(messageText) + 0.5f); // round
- int height = (int) (baseline + paint.descent() + 0.5f);
-
- Bitmap image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-
- Canvas canvas = new Canvas(image);
- canvas.drawText(messageText, 0, baseline, paint);
-
- return image;
- }
-
- public static String getEditTextSelectedText(EditText editText) {
- if (!editText.hasSelection()) return null;
-
- final int start = editText.getSelectionStart();
- final int end = editText.getSelectionEnd();
-
- return String.valueOf(
- start > end ? editText.getText().subSequence(end, start) : editText.getText().subSequence(start, end));
- }
-
- @Deprecated
- public static void startInCallUI(Context context) throws ClassNotFoundException {
- Intent intent = new Intent(context, Class.forName("dev.alenajam.opendialer.features.inCall.ui.InCallActivity"));
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(intent);
- }
-
- @SuppressLint("MissingPermission")
- public static void makeCall(Context context, String number) {
- if (PermissionUtils.hasMakeCallPermission(context)) {
- Intent intent = new Intent(Intent.ACTION_CALL, Uri.fromParts("tel", number, null));
-
- TelecomManager telecomManager = (TelecomManager) context.getSystemService(TELECOM_SERVICE);
- if (telecomManager == null) return;
- PhoneAccountHandle defaultPhoneAccount = telecomManager.getDefaultOutgoingPhoneAccount("tel");
- if (defaultPhoneAccount == null) {
- List phoneAccounts = telecomManager.getCallCapablePhoneAccounts();
- if (phoneAccounts.size() < 2) return;
- else {
- // Dual SIM
- SubscriptionManager subscriptionManager = (SubscriptionManager) context.getSystemService(TELEPHONY_SUBSCRIPTION_SERVICE);
- if (subscriptionManager == null) return;
-
- MyDialog dialog = new MyDialog(context);
- dialog.setTitle(context.getString(R.string.choose_sim_card));
- LinearLayout linearLayout = new LinearLayout(context);
- linearLayout.setOrientation(LinearLayout.VERTICAL);
- for (int i = 0; i < phoneAccounts.size(); i++) {
- SubscriptionInfo subscriptionInfo = subscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(i);
- if (subscriptionInfo == null) continue;
-
- View item = LayoutInflater.from(context).inflate(R.layout.item_sim, null);
- TextView carrier = item.findViewById(R.id.carrier);
- carrier.setText(subscriptionInfo.getCarrierName());
- TextView numberTv = item.findViewById(R.id.number);
- numberTv.setText(subscriptionInfo.getNumber());
- subscriptionInfo.getIconTint();
- ImageView icon = item.findViewById(R.id.icon);
- icon.setColorFilter(subscriptionInfo.getIconTint());
- int finalI = i;
- item.setOnClickListener(v -> {
- dialog.hide();
- intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccounts.get(finalI));
- context.startActivity(intent);
- });
- linearLayout.addView(item);
- }
- dialog.setContent(linearLayout);
- dialog.show();
- return;
+ @SuppressLint("DefaultLocale")
+ public static String getDurationTimeString(long durationMilliseconds) {
+
+ return String.format("%02d:%02d:%02d",
+ TimeUnit.MILLISECONDS.toHours(durationMilliseconds),
+ TimeUnit.MILLISECONDS.toMinutes(durationMilliseconds) -
+ TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(durationMilliseconds)),
+ TimeUnit.MILLISECONDS.toSeconds(durationMilliseconds) -
+ TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(durationMilliseconds))
+ );
+ }
+
+ @SuppressLint("DefaultLocale")
+ public static String getDurationTimeStringMinimal(long durationMilliseconds) {
+ long hours = TimeUnit.MILLISECONDS.toHours(durationMilliseconds);
+ long minutes = TimeUnit.MILLISECONDS.toMinutes(durationMilliseconds) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(durationMilliseconds));
+ long seconds = TimeUnit.MILLISECONDS.toSeconds(durationMilliseconds) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(durationMilliseconds));
+
+ String timeString = "";
+ if (hours > 0) timeString = timeString.concat(hours + "h ");
+ if (minutes > 0) timeString = timeString.concat(minutes + "m ");
+ if (seconds > 0) timeString = timeString.concat(seconds + "s");
+
+ return timeString.isEmpty() ? "0s" : timeString;
+ }
+
+ public static Bitmap textToBitmap(Context context, String messageText, float textSize, int textColor) {
+ Paint paint = new Paint();
+ int pixelTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ textSize, context.getResources().getDisplayMetrics());
+ paint.setTextSize(pixelTextSize);
+ paint.setColor(textColor);
+ paint.setTextAlign(Paint.Align.LEFT);
+
+ float baseline = -paint.ascent() + 10; // ascent() is negative
+ int width = (int) (paint.measureText(messageText) + 0.5f); // round
+ int height = (int) (baseline + paint.descent() + 0.5f);
+
+ Bitmap image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+
+ Canvas canvas = new Canvas(image);
+ canvas.drawText(messageText, 0, baseline, paint);
+
+ return image;
+ }
+
+ public static String getEditTextSelectedText(EditText editText) {
+ if (!editText.hasSelection()) return null;
+
+ final int start = editText.getSelectionStart();
+ final int end = editText.getSelectionEnd();
+
+ return String.valueOf(
+ start > end ? editText.getText().subSequence(end, start) : editText.getText().subSequence(start, end));
+ }
+
+ @Deprecated
+ public static void startInCallUI(Context context) throws ClassNotFoundException {
+ Intent intent = new Intent(context, Class.forName("dev.alenajam.opendialer.features.inCall.ui.InCallActivity"));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ }
+
+ @SuppressLint("MissingPermission")
+ public static void makeCall(Context context, String number) {
+ if (PermissionUtils.hasMakeCallPermission(context)) {
+ Intent intent = new Intent(Intent.ACTION_CALL, Uri.fromParts("tel", number, null));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ TelecomManager telecomManager = (TelecomManager) context.getSystemService(TELECOM_SERVICE);
+ if (telecomManager == null) return;
+ PhoneAccountHandle defaultPhoneAccount = telecomManager.getDefaultOutgoingPhoneAccount("tel");
+ if (defaultPhoneAccount == null) {
+ List phoneAccounts = telecomManager.getCallCapablePhoneAccounts();
+ if (phoneAccounts.size() < 2) return;
+ else {
+ // Dual SIM
+ SubscriptionManager subscriptionManager = (SubscriptionManager) context.getSystemService(TELEPHONY_SUBSCRIPTION_SERVICE);
+ if (subscriptionManager == null) return;
+
+ MyDialog dialog = new MyDialog(context);
+ dialog.setTitle(context.getString(R.string.choose_sim_card));
+ LinearLayout linearLayout = new LinearLayout(context);
+ linearLayout.setOrientation(LinearLayout.VERTICAL);
+ for (int i = 0; i < phoneAccounts.size(); i++) {
+ SubscriptionInfo subscriptionInfo = subscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(i);
+ if (subscriptionInfo == null) continue;
+
+ View item = LayoutInflater.from(context).inflate(R.layout.item_sim, null);
+ TextView carrier = item.findViewById(R.id.carrier);
+ carrier.setText(subscriptionInfo.getCarrierName());
+ TextView numberTv = item.findViewById(R.id.number);
+ numberTv.setText(subscriptionInfo.getNumber());
+ subscriptionInfo.getIconTint();
+ ImageView icon = item.findViewById(R.id.icon);
+ icon.setColorFilter(subscriptionInfo.getIconTint());
+ int finalI = i;
+ item.setOnClickListener(v -> {
+ dialog.hide();
+ intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccounts.get(finalI));
+ context.startActivity(intent);
+ });
+ linearLayout.addView(item);
+ }
+ dialog.setContent(linearLayout);
+ dialog.show();
+ return;
+ }
+ }
+ context.startActivity(intent);
+ }
+ }
+
+ public static void makeSms(Context context, String number) {
+ Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts("smsto", number, null));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ }
+
+ public static void copyToClipobard(Context context, String text) {
+ ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
+ ClipData clip = ClipData.newPlainText(null, text);
+ if (clipboard != null) {
+ clipboard.setPrimaryClip(clip);
+ Toast.makeText(context, context.getString(R.string.text_copied), Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ public static void showContactDetail(Context context, int contactId) {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, String.valueOf(contactId));
+ intent.setData(uri);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ }
+
+ public static void setTheme(int mode) {
+ AppCompatDelegate.setDefaultNightMode(mode);
+ }
+
+ public static void hideKeyboard(Activity activity) {
+ InputMethodManager imm = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
+ //Find the currently focused view, so we can grab the correct window token from it.
+ View view = activity.getCurrentFocus();
+ //If no view currently has focus, create a new one, just so we can grab a window token from it
+ if (view == null) {
+ view = new View(activity);
}
- }
- context.startActivity(intent);
- }
- }
-
- public static void makeSms(Context context, String number) {
- context.startActivity(new Intent(Intent.ACTION_SENDTO, Uri.fromParts("smsto", number, null)));
- }
-
- public static void copyToClipobard(Context context, String text) {
- ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
- ClipData clip = ClipData.newPlainText(null, text);
- if (clipboard != null) {
- clipboard.setPrimaryClip(clip);
- Toast.makeText(context, context.getString(R.string.text_copied), Toast.LENGTH_SHORT).show();
- }
- }
-
- public static void showContactDetail(Context context, int contactId) {
- Intent intent = new Intent(Intent.ACTION_VIEW);
- Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, String.valueOf(contactId));
- intent.setData(uri);
- context.startActivity(intent);
- }
-
- public static void setTheme(int mode) {
- AppCompatDelegate.setDefaultNightMode(mode);
- }
-
- public static void hideKeyboard(Activity activity) {
- InputMethodManager imm = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
- //Find the currently focused view, so we can grab the correct window token from it.
- View view = activity.getCurrentFocus();
- //If no view currently has focus, create a new one, just so we can grab a window token from it
- if (view == null) {
- view = new View(activity);
- }
- if (imm != null) {
- imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
- }
- }
-
- public static float convertDpToPixels(float dp, Context context) {
- return dp * ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT);
- }
-
- public static void addContactAsExisting(Context context, String number) {
- Intent addExistingIntent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
- addExistingIntent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE);
- addExistingIntent.putExtra(ContactsContract.Intents.Insert.PHONE, number);
- context.startActivity(addExistingIntent);
- }
-
- public static void createContact(Context context, String number) {
- Intent createContactIntent = new Intent(Intent.ACTION_INSERT);
- createContactIntent.setType(ContactsContract.Contacts.CONTENT_TYPE);
- createContactIntent.putExtra(ContactsContract.Intents.Insert.PHONE, number);
- context.startActivity(createContactIntent);
- }
-
- public static boolean isDmtfSettingEnabled(Context context) {
- return Settings.System.getInt(context.getContentResolver(), Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
- }
-
- public static boolean isSoundEffectsEnabled(Context context) {
- return Settings.System.getInt(context.getContentResolver(), Settings.System.SOUND_EFFECTS_ENABLED, 1) == 1;
- }
-
- public static boolean isRingerModeSilentOrVibrate(Context context) {
- AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- if (audioManager == null) return false;
- int ringerMode = audioManager.getRingerMode();
- return ringerMode == AudioManager.RINGER_MODE_SILENT || ringerMode == AudioManager.RINGER_MODE_VIBRATE;
- }
-
- public static int getColorFromAttr(Context context, int attrInt) {
- TypedValue typedValue = new TypedValue();
- context.getTheme().resolveAttribute(attrInt, typedValue, true);
- return ContextCompat.getColor(context, typedValue.resourceId);
- }
-
- public static long getCurrentTime() {
- return SystemClock.elapsedRealtime();
- }
+ if (imm != null) {
+ imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
+ }
+ }
+
+ public static float convertDpToPixels(float dp, Context context) {
+ return dp * ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT);
+ }
+
+ public static void addContactAsExisting(Context context, String number) {
+ Intent addExistingIntent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
+ addExistingIntent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE);
+ addExistingIntent.putExtra(ContactsContract.Intents.Insert.PHONE, number);
+ addExistingIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(addExistingIntent);
+ }
+
+ public static void createContact(Context context, String number) {
+ Intent createContactIntent = new Intent(Intent.ACTION_INSERT);
+ createContactIntent.setType(ContactsContract.Contacts.CONTENT_TYPE);
+ createContactIntent.putExtra(ContactsContract.Intents.Insert.PHONE, number);
+ context.startActivity(createContactIntent);
+ }
+
+ public static boolean isDmtfSettingEnabled(Context context) {
+ return Settings.System.getInt(context.getContentResolver(), Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
+ }
+
+ public static boolean isSoundEffectsEnabled(Context context) {
+ return Settings.System.getInt(context.getContentResolver(), Settings.System.SOUND_EFFECTS_ENABLED, 1) == 1;
+ }
+
+ public static boolean isRingerModeSilentOrVibrate(Context context) {
+ AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ if (audioManager == null) return false;
+ int ringerMode = audioManager.getRingerMode();
+ return ringerMode == AudioManager.RINGER_MODE_SILENT || ringerMode == AudioManager.RINGER_MODE_VIBRATE;
+ }
+
+ public static int getColorFromAttr(Context context, int attrInt) {
+ TypedValue typedValue = new TypedValue();
+ context.getTheme().resolveAttribute(attrInt, typedValue, true);
+ return ContextCompat.getColor(context, typedValue.resourceId);
+ }
+
+ public static long getCurrentTime() {
+ return SystemClock.elapsedRealtime();
+ }
}
\ No newline at end of file
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/Contact.java b/core/common/src/main/java/dev/alenajam/opendialer/core/common/Contact.java
index 28df12d..9c015fd 100644
--- a/core/common/src/main/java/dev/alenajam/opendialer/core/common/Contact.java
+++ b/core/common/src/main/java/dev/alenajam/opendialer/core/common/Contact.java
@@ -3,84 +3,84 @@
import java.io.Serializable;
public class Contact implements Serializable {
- private int id;
- private String name;
- private String number;
- private boolean starred;
- private String imageUri;
- private String lookupKey;
-
- public Contact(int id) {
- this.id = id;
- }
-
- public Contact(int id, String name) {
- this.id = id;
- this.name = name;
- }
-
- public Contact(String name, String number) {
- this.name = name;
- this.number = number;
- }
-
- public Contact(int id, String name, boolean starred, String imageUri) {
- this.id = id;
- this.name = name;
- this.starred = starred;
- this.imageUri = imageUri;
- }
-
- public Contact(int id, String name, String number) {
- this.id = id;
- this.name = name;
- this.number = number;
- }
-
- public Contact(int id, String name, String number, String imageUri) {
- this.id = id;
- this.name = name;
- this.number = number;
- this.imageUri = imageUri;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getNumber() {
- return number;
- }
-
- public void setNumber(String number) {
- this.number = number;
- }
-
- public int getId() {
- return id;
- }
-
- public void setId(int id) {
- this.id = id;
- }
-
- public boolean isStarred() {
- return starred;
- }
-
- public String getImageUri() {
- return imageUri;
- }
-
- public String getLookupKey() {
- return lookupKey;
- }
-
- public void setLookupKey(String lookupKey) {
- this.lookupKey = lookupKey;
- }
+ private int id;
+ private String name;
+ private String number;
+ private boolean starred;
+ private String imageUri;
+ private String lookupKey;
+
+ public Contact(int id) {
+ this.id = id;
+ }
+
+ public Contact(int id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public Contact(String name, String number) {
+ this.name = name;
+ this.number = number;
+ }
+
+ public Contact(int id, String name, boolean starred, String imageUri) {
+ this.id = id;
+ this.name = name;
+ this.starred = starred;
+ this.imageUri = imageUri;
+ }
+
+ public Contact(int id, String name, String number) {
+ this.id = id;
+ this.name = name;
+ this.number = number;
+ }
+
+ public Contact(int id, String name, String number, String imageUri) {
+ this.id = id;
+ this.name = name;
+ this.number = number;
+ this.imageUri = imageUri;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getNumber() {
+ return number;
+ }
+
+ public void setNumber(String number) {
+ this.number = number;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public boolean isStarred() {
+ return starred;
+ }
+
+ public String getImageUri() {
+ return imageUri;
+ }
+
+ public String getLookupKey() {
+ return lookupKey;
+ }
+
+ public void setLookupKey(String lookupKey) {
+ this.lookupKey = lookupKey;
+ }
}
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/ContactsHelper.java b/core/common/src/main/java/dev/alenajam/opendialer/core/common/ContactsHelper.java
index 22b7644..fd39fba 100644
--- a/core/common/src/main/java/dev/alenajam/opendialer/core/common/ContactsHelper.java
+++ b/core/common/src/main/java/dev/alenajam/opendialer/core/common/ContactsHelper.java
@@ -8,39 +8,39 @@
import androidx.annotation.Nullable;
public abstract class ContactsHelper {
- private static String[] projectionPhoneLookup = new String[]{
- ContactsContract.PhoneLookup.CONTACT_ID,
- ContactsContract.PhoneLookup.DISPLAY_NAME,
- ContactsContract.PhoneLookup.TYPE,
- ContactsContract.PhoneLookup.LABEL,
- ContactsContract.PhoneLookup.NUMBER,
- ContactsContract.PhoneLookup.NORMALIZED_NUMBER,
- ContactsContract.PhoneLookup.PHOTO_ID,
- ContactsContract.PhoneLookup.LOOKUP_KEY,
- ContactsContract.PhoneLookup.PHOTO_URI,
- ContactsContract.PhoneLookup._ID
- };
+ private static String[] projectionPhoneLookup = new String[]{
+ ContactsContract.PhoneLookup.CONTACT_ID,
+ ContactsContract.PhoneLookup.DISPLAY_NAME,
+ ContactsContract.PhoneLookup.TYPE,
+ ContactsContract.PhoneLookup.LABEL,
+ ContactsContract.PhoneLookup.NUMBER,
+ ContactsContract.PhoneLookup.NORMALIZED_NUMBER,
+ ContactsContract.PhoneLookup.PHOTO_ID,
+ ContactsContract.PhoneLookup.LOOKUP_KEY,
+ ContactsContract.PhoneLookup.PHOTO_URI,
+ ContactsContract.PhoneLookup._ID
+ };
- @Nullable
- public static Contact getContactByPhoneNumber(Context context, String phoneNumber) {
- if (!PermissionUtils.hasContactsPermission(context) || phoneNumber == null || phoneNumber.isEmpty())
- return null;
+ @Nullable
+ public static Contact getContactByPhoneNumber(Context context, String phoneNumber) {
+ if (!PermissionUtils.hasContactsPermission(context) || phoneNumber == null || phoneNumber.isEmpty())
+ return null;
- Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
- Cursor cursor = context.getContentResolver().query(
- uri, projectionPhoneLookup, null, null, null, null
- );
+ Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
+ Cursor cursor = context.getContentResolver().query(
+ uri, projectionPhoneLookup, null, null, null, null
+ );
- if (cursor != null && cursor.moveToFirst()) {
- Contact contact = new Contact(
- cursor.getInt(cursor.getColumnIndexOrThrow(ContactsContract.PhoneLookup._ID)),
- cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.PhoneLookup.DISPLAY_NAME)),
- phoneNumber,
- cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.PhoneLookup.PHOTO_URI))
- );
- cursor.close();
- return contact;
- } else if (cursor != null) cursor.close();
- return null;
- }
+ if (cursor != null && cursor.moveToFirst()) {
+ Contact contact = new Contact(
+ cursor.getInt(cursor.getColumnIndexOrThrow(ContactsContract.PhoneLookup._ID)),
+ cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.PhoneLookup.DISPLAY_NAME)),
+ phoneNumber,
+ cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.PhoneLookup.PHOTO_URI))
+ );
+ cursor.close();
+ return contact;
+ } else if (cursor != null) cursor.close();
+ return null;
+ }
}
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/DefaultPhoneUtils.java b/core/common/src/main/java/dev/alenajam/opendialer/core/common/DefaultPhoneUtils.java
index 6d61785..ace05a2 100644
--- a/core/common/src/main/java/dev/alenajam/opendialer/core/common/DefaultPhoneUtils.java
+++ b/core/common/src/main/java/dev/alenajam/opendialer/core/common/DefaultPhoneUtils.java
@@ -14,49 +14,49 @@
public abstract class DefaultPhoneUtils {
- public static boolean hasDefault(Context context) {
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
- @SuppressLint("WrongConstant") RoleManager roleManager = (RoleManager) context.getSystemService(ROLE_SERVICE);
- if (roleManager != null && roleManager.isRoleAvailable(RoleManager.ROLE_DIALER))
- return roleManager.isRoleHeld(RoleManager.ROLE_DIALER);
- } else {
- TelecomManager telecomManager = (TelecomManager) context.getSystemService(TELECOM_SERVICE);
- String defaultDialer = telecomManager.getDefaultDialerPackage();
- if (telecomManager != null && defaultDialer != null)
- return defaultDialer.equals(context.getPackageName());
+ public static boolean hasDefault(Context context) {
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
+ @SuppressLint("WrongConstant") RoleManager roleManager = (RoleManager) context.getSystemService(ROLE_SERVICE);
+ if (roleManager != null && roleManager.isRoleAvailable(RoleManager.ROLE_DIALER))
+ return roleManager.isRoleHeld(RoleManager.ROLE_DIALER);
+ } else {
+ TelecomManager telecomManager = (TelecomManager) context.getSystemService(TELECOM_SERVICE);
+ String defaultDialer = telecomManager.getDefaultDialerPackage();
+ if (telecomManager != null && defaultDialer != null)
+ return defaultDialer.equals(context.getPackageName());
+ }
+ return false;
}
- return false;
- }
- public static void requestDefault(Activity activity, int requestId) {
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
- @SuppressLint("WrongConstant") RoleManager roleManager = (RoleManager) activity.getSystemService(ROLE_SERVICE);
- if (roleManager != null && roleManager.isRoleAvailable(RoleManager.ROLE_DIALER) && !roleManager.isRoleHeld(RoleManager.ROLE_DIALER)) {
- Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_DIALER);
- activity.startActivityForResult(intent, requestId);
- }
- } else {
- TelecomManager telecomManager = (TelecomManager) activity.getSystemService(TELECOM_SERVICE);
- if (telecomManager != null && !telecomManager.getDefaultDialerPackage().equals(activity.getPackageName())) {
- Intent intent = new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER).putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, activity.getPackageName());
- activity.startActivityForResult(intent, requestId);
- }
+ public static void requestDefault(Activity activity, int requestId) {
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
+ @SuppressLint("WrongConstant") RoleManager roleManager = (RoleManager) activity.getSystemService(ROLE_SERVICE);
+ if (roleManager != null && roleManager.isRoleAvailable(RoleManager.ROLE_DIALER) && !roleManager.isRoleHeld(RoleManager.ROLE_DIALER)) {
+ Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_DIALER);
+ activity.startActivityForResult(intent, requestId);
+ }
+ } else {
+ TelecomManager telecomManager = (TelecomManager) activity.getSystemService(TELECOM_SERVICE);
+ if (telecomManager != null && !telecomManager.getDefaultDialerPackage().equals(activity.getPackageName())) {
+ Intent intent = new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER).putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, activity.getPackageName());
+ activity.startActivityForResult(intent, requestId);
+ }
+ }
}
- }
- public static void requestDefault(Fragment fragment, int requestId) {
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
- @SuppressLint("WrongConstant") RoleManager roleManager = (RoleManager) fragment.getContext().getSystemService(ROLE_SERVICE);
- if (roleManager != null && roleManager.isRoleAvailable(RoleManager.ROLE_DIALER) && !roleManager.isRoleHeld(RoleManager.ROLE_DIALER)) {
- Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_DIALER);
- fragment.startActivityForResult(intent, requestId);
- }
- } else {
- TelecomManager telecomManager = (TelecomManager) fragment.getContext().getSystemService(TELECOM_SERVICE);
- if (telecomManager != null && !telecomManager.getDefaultDialerPackage().equals(fragment.getContext().getPackageName())) {
- Intent intent = new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER).putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, fragment.getContext().getPackageName());
- fragment.startActivityForResult(intent, requestId);
- }
+ public static void requestDefault(Fragment fragment, int requestId) {
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
+ @SuppressLint("WrongConstant") RoleManager roleManager = (RoleManager) fragment.getContext().getSystemService(ROLE_SERVICE);
+ if (roleManager != null && roleManager.isRoleAvailable(RoleManager.ROLE_DIALER) && !roleManager.isRoleHeld(RoleManager.ROLE_DIALER)) {
+ Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_DIALER);
+ fragment.startActivityForResult(intent, requestId);
+ }
+ } else {
+ TelecomManager telecomManager = (TelecomManager) fragment.getContext().getSystemService(TELECOM_SERVICE);
+ if (telecomManager != null && !telecomManager.getDefaultDialerPackage().equals(fragment.getContext().getPackageName())) {
+ Intent intent = new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER).putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, fragment.getContext().getPackageName());
+ fragment.startActivityForResult(intent, requestId);
+ }
+ }
}
- }
}
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/EditTextDialog.java b/core/common/src/main/java/dev/alenajam/opendialer/core/common/EditTextDialog.java
deleted file mode 100644
index 9e4da69..0000000
--- a/core/common/src/main/java/dev/alenajam/opendialer/core/common/EditTextDialog.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package dev.alenajam.opendialer.core.common;
-
-import android.content.Context;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-public class EditTextDialog extends MyDialog implements View.OnClickListener {
- private Context context;
- private EditText editText;
- private TextView description;
- private LinearLayout linearLayout;
- private OnTextChangeListener onTextChangeListener = null;
-
- public EditTextDialog(@NonNull Context context) {
- super(context);
- this.context = context;
- linearLayout = new LinearLayout(context);
- linearLayout.setOrientation(LinearLayout.VERTICAL);
- editText = new EditText(context);
- editText.setHint(context.getString(R.string.your_text_here));
- editText.requestFocus();
- editText.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- if (onTextChangeListener != null) {
- onTextChangeListener.onTextChange(s.toString());
- }
- }
-
- @Override
- public void afterTextChanged(Editable s) {
-
- }
- });
- setPositiveButton(context.getString(android.R.string.ok));
- setNegativeButton(context.getString(android.R.string.cancel));
- linearLayout.addView(editText);
- getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
- }
-
- public void setDescription(String string) {
- description = new TextView(context);
- description.setText(string);
- description.setTextColor(CommonUtils.getColorFromAttr(context, android.R.attr.textColorSecondary));
- description.setTextSize(13);
- description.setPadding(0, 0, 0, 20);
- linearLayout.addView(description, 0);
- }
-
- public void build() {
- super.setContent(linearLayout);
- }
-
- @Nullable
- public String getTypedString() {
- return editText.getText().toString();
- }
-
- public void setTypedString(String text) {
- editText.setText(text);
- editText.setSelection(text.length());
- }
-
- public void setOnTextChangeListener(OnTextChangeListener listener) {
- this.onTextChangeListener = listener;
- }
-
- public void setPositiveButtonEnabled(boolean enabled) {
- positiveButton.setEnabled(enabled);
- }
-
- public interface OnTextChangeListener {
- void onTextChange(String text);
- }
-}
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/IntentExtras.kt b/core/common/src/main/java/dev/alenajam/opendialer/core/common/IntentExtras.kt
new file mode 100644
index 0000000..a5d1dea
--- /dev/null
+++ b/core/common/src/main/java/dev/alenajam/opendialer/core/common/IntentExtras.kt
@@ -0,0 +1,3 @@
+package dev.alenajam.opendialer.core.common
+
+const val MAIN_ACTIVITY_INTENT_DIAL_EXTRA_ADD_CALL = "dev.alenajam.opendialer_add_call"
\ No newline at end of file
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/KeyButton.java b/core/common/src/main/java/dev/alenajam/opendialer/core/common/KeyButton.java
deleted file mode 100644
index 425e864..0000000
--- a/core/common/src/main/java/dev/alenajam/opendialer/core/common/KeyButton.java
+++ /dev/null
@@ -1,124 +0,0 @@
-package dev.alenajam.opendialer.core.common;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.TextView;
-
-import androidx.annotation.ColorRes;
-import androidx.constraintlayout.widget.ConstraintLayout;
-import androidx.core.content.ContextCompat;
-
-public class KeyButton extends ConstraintLayout {
- private Context context;
- private View rootView;
- private String mainText, secondaryText;
- private TextView textView1, textView2;
- private KeyPressedListener keyPressedListener;
-
- public KeyButton(Context context, AttributeSet attrs) {
- super(context, attrs);
- this.context = context;
- LayoutInflater mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- assert mInflater != null;
- rootView = mInflater.inflate(R.layout.key_button, this, true);
-
- TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.KeyButton);
-
- int key = typedArray.getInt(0, 0);
-
- typedArray.recycle();
-
- textView1 = rootView.findViewById(R.id.textView1);
- textView2 = rootView.findViewById(R.id.textView2);
-
- mainText = String.valueOf(key);
- secondaryText = "";
-
- switch (key) {
- case 0:
- secondaryText = "+";
- case 1:
- break;
- case 2:
- secondaryText = "abc";
- break;
- case 3:
- secondaryText = "def";
- break;
- case 4:
- secondaryText = "ghi";
- break;
- case 5:
- secondaryText = "jkl";
- break;
- case 6:
- secondaryText = "mno";
- break;
- case 7:
- secondaryText = "pqrs";
- break;
- case 8:
- secondaryText = "tuv";
- break;
- case 9:
- secondaryText = "wxyz";
- break;
- case 10:
- mainText = "*";
- break;
- case 11:
- mainText = "#";
- break;
- }
-
- textView1.setText(mainText);
- textView2.setText(secondaryText);
-
- setSoundEffectsEnabled(false);
- }
-
- public String getMainText() {
- return mainText;
- }
-
- public String getSecondaryText() {
- return secondaryText;
- }
-
- public void setMainTextSize(float sp) {
- textView1.setTextSize(sp);
- }
-
- public void setMainTextColor(@ColorRes int color) {
- textView1.setTextColor(ContextCompat.getColor(context, color));
- }
-
- public void hideSubtitle() {
- textView2.setVisibility(View.GONE);
- }
-
- public void setTitleTopMargin(float margin) {
- LayoutParams layoutParams = (LayoutParams) textView1.getLayoutParams();
- layoutParams.topMargin = (int) CommonUtils.convertDpToPixels(margin, context);
- textView1.setLayoutParams(layoutParams);
- }
-
- @Override
- public void setPressed(boolean pressed) {
- super.setPressed(pressed);
- if (keyPressedListener != null) {
- keyPressedListener.onKeyPressed(this, pressed);
- }
- }
-
- public void setKeyPressedListener(KeyPressedListener keyPressedListener) {
- this.keyPressedListener = keyPressedListener;
- }
-
- public interface KeyPressedListener {
- void onKeyPressed(View view, boolean pressed);
- }
-}
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/KeyboardSearchListener.kt b/core/common/src/main/java/dev/alenajam/opendialer/core/common/KeyboardSearchListener.kt
deleted file mode 100644
index 44feb26..0000000
--- a/core/common/src/main/java/dev/alenajam/opendialer/core/common/KeyboardSearchListener.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package dev.alenajam.opendialer.core.common
-
-interface KeyboardSearchListener {
- fun closeSearchKeyboard()
-}
\ No newline at end of file
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/MyBottomSheetDialog.java b/core/common/src/main/java/dev/alenajam/opendialer/core/common/MyBottomSheetDialog.java
deleted file mode 100644
index 793825c..0000000
--- a/core/common/src/main/java/dev/alenajam/opendialer/core/common/MyBottomSheetDialog.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package dev.alenajam.opendialer.core.common;
-
-import android.content.Context;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-
-import com.google.android.material.bottomsheet.BottomSheetDialog;
-
-public class MyBottomSheetDialog extends BottomSheetDialog {
-
- protected MyBottomSheetDialog(Context context) {
- super(context, R.style.CustomBottomSheetDialogTheme);
- }
-
- protected MyBottomSheetDialog(@NonNull Context context, int viewResId) {
- super(context);
- View sheetView = getLayoutInflater().inflate(viewResId, null);
- setContentView(sheetView);
- }
-}
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/MyDialog.java b/core/common/src/main/java/dev/alenajam/opendialer/core/common/MyDialog.java
index b2e89da..59bf190 100644
--- a/core/common/src/main/java/dev/alenajam/opendialer/core/common/MyDialog.java
+++ b/core/common/src/main/java/dev/alenajam/opendialer/core/common/MyDialog.java
@@ -13,74 +13,74 @@
import androidx.appcompat.app.AlertDialog;
public class MyDialog extends AlertDialog implements View.OnClickListener {
- protected View view;
- private OnClickListener listener;
- protected Button positiveButton, negativeButton;
-
- public MyDialog(@NonNull Context context) {
- super(context);
- init(context);
- }
-
- public MyDialog(@NonNull Context context, @StyleRes int style) {
- super(context, style);
- init(context);
- }
-
- private void init(Context context) {
- view = LayoutInflater.from(context).inflate(R.layout.dialog_my, null);
- setView(view);
- positiveButton = view.findViewById(R.id.buttonPositive);
- negativeButton = view.findViewById(R.id.buttonNegative);
- }
-
- public void setTitle(String title) {
- TextView titleTextView = view.findViewById(R.id.title);
- titleTextView.setText(title);
- }
-
- public void setTitle(@StringRes int title) {
- TextView titleTextView = view.findViewById(R.id.title);
- titleTextView.setText(title);
- }
-
- public void setContent(View view) {
- FrameLayout frameLayout = this.view.findViewById(R.id.content);
- frameLayout.addView(view);
- }
-
- public void setPositiveButton(String label) {
- positiveButton.setVisibility(View.VISIBLE);
- positiveButton.setText(label);
- positiveButton.setOnClickListener(this);
- }
-
- public void setPositiveButton(@StringRes int label) {
- setPositiveButton(getContext().getString(label));
- }
-
- public void setNegativeButton(String label) {
- negativeButton.setVisibility(View.VISIBLE);
- negativeButton.setText(label);
- negativeButton.setOnClickListener(this);
- }
-
- public void setNegativeButton(@StringRes int label) {
- setNegativeButton(getContext().getString(label));
- }
-
- public void setOnClickListener(OnClickListener listener) {
- this.listener = listener;
- }
-
- @Override
- public void onClick(View v) {
- if (listener == null) return;
- if (v.getId() == R.id.buttonPositive) listener.onClick(this, BUTTON_POSITIVE);
- else if (v.getId() == R.id.buttonNegative) listener.onClick(this, BUTTON_NEGATIVE);
- }
-
- public interface OnClickListener {
- void onClick(MyDialog dialog, int whichButton);
- }
+ protected View view;
+ private OnClickListener listener;
+ protected Button positiveButton, negativeButton;
+
+ public MyDialog(@NonNull Context context) {
+ super(context);
+ init(context);
+ }
+
+ public MyDialog(@NonNull Context context, @StyleRes int style) {
+ super(context, style);
+ init(context);
+ }
+
+ private void init(Context context) {
+ view = LayoutInflater.from(context).inflate(R.layout.dialog_my, null);
+ setView(view);
+ positiveButton = view.findViewById(R.id.buttonPositive);
+ negativeButton = view.findViewById(R.id.buttonNegative);
+ }
+
+ public void setTitle(String title) {
+ TextView titleTextView = view.findViewById(R.id.title);
+ titleTextView.setText(title);
+ }
+
+ public void setTitle(@StringRes int title) {
+ TextView titleTextView = view.findViewById(R.id.title);
+ titleTextView.setText(title);
+ }
+
+ public void setContent(View view) {
+ FrameLayout frameLayout = this.view.findViewById(R.id.content);
+ frameLayout.addView(view);
+ }
+
+ public void setPositiveButton(String label) {
+ positiveButton.setVisibility(View.VISIBLE);
+ positiveButton.setText(label);
+ positiveButton.setOnClickListener(this);
+ }
+
+ public void setPositiveButton(@StringRes int label) {
+ setPositiveButton(getContext().getString(label));
+ }
+
+ public void setNegativeButton(String label) {
+ negativeButton.setVisibility(View.VISIBLE);
+ negativeButton.setText(label);
+ negativeButton.setOnClickListener(this);
+ }
+
+ public void setNegativeButton(@StringRes int label) {
+ setNegativeButton(getContext().getString(label));
+ }
+
+ public void setOnClickListener(OnClickListener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (listener == null) return;
+ if (v.getId() == R.id.buttonPositive) listener.onClick(this, BUTTON_POSITIVE);
+ else if (v.getId() == R.id.buttonNegative) listener.onClick(this, BUTTON_NEGATIVE);
+ }
+
+ public interface OnClickListener {
+ void onClick(MyDialog dialog, int whichButton);
+ }
}
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/OnStatusBarColorChange.kt b/core/common/src/main/java/dev/alenajam/opendialer/core/common/OnStatusBarColorChange.kt
deleted file mode 100644
index 28f8872..0000000
--- a/core/common/src/main/java/dev/alenajam/opendialer/core/common/OnStatusBarColorChange.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package dev.alenajam.opendialer.core.common
-
-import androidx.annotation.ColorInt
-
-interface OnStatusBarColorChange {
- fun onColorChange(@ColorInt color: Int)
-}
\ No newline at end of file
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/OpenSearchListener.kt b/core/common/src/main/java/dev/alenajam/opendialer/core/common/OpenSearchListener.kt
deleted file mode 100644
index 8fe6ab9..0000000
--- a/core/common/src/main/java/dev/alenajam/opendialer/core/common/OpenSearchListener.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package dev.alenajam.opendialer.core.common
-
-interface OpenSearchListener {
- fun openSearch()
- fun closeSearch()
-}
\ No newline at end of file
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/PermissionUtils.java b/core/common/src/main/java/dev/alenajam/opendialer/core/common/PermissionUtils.java
index 1468b68..064c7d7 100644
--- a/core/common/src/main/java/dev/alenajam/opendialer/core/common/PermissionUtils.java
+++ b/core/common/src/main/java/dev/alenajam/opendialer/core/common/PermissionUtils.java
@@ -8,54 +8,54 @@
import androidx.fragment.app.Fragment;
public abstract class PermissionUtils {
- public static String[] recentsPermissions = new String[]{Manifest.permission.READ_CALL_LOG, Manifest.permission.WRITE_CALL_LOG};
- public static String[] contactsPermissions = new String[]{Manifest.permission.READ_CONTACTS};
- public static String[] searchPermissions = new String[]{Manifest.permission.READ_CONTACTS};
- public static String[] makeCallPermissions = new String[]{Manifest.permission.CALL_PHONE, Manifest.permission.READ_PHONE_STATE};
-
- public static boolean hasRecentsPermission(Context context) {
- return hasPermissions(context, recentsPermissions);
- }
-
- public static boolean hasContactsPermission(Context context) {
- return hasPermissions(context, contactsPermissions);
- }
-
- public static boolean hasSearchPermission(Context context) {
- return hasPermissions(context, searchPermissions);
- }
-
- public static boolean hasMakeCallPermission(Context context) {
- return hasPermissions(context, makeCallPermissions);
- }
-
- private static boolean hasPermissions(Context context, String[] permissions) {
- if (context == null) return false;
- for (String permission : permissions) {
- if (context.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
- return false;
- }
- }
- return true;
- }
-
- public static void requestRecentsPermission(Activity activity, int requestCode) {
- activity.requestPermissions(recentsPermissions, requestCode);
- }
-
- public static void requestContactsPermission(Fragment fragment, int requestCode) {
- fragment.requestPermissions(contactsPermissions, requestCode);
- }
-
- public static void requestContactsPermission(Activity activity, int requestCode) {
- activity.requestPermissions(contactsPermissions, requestCode);
- }
-
- public static void requestMakeCallPermission(Activity activity, int requestCode) {
- activity.requestPermissions(makeCallPermissions, requestCode);
- }
-
- public static void requestSearchPermission(Activity activity, int requestCode) {
- activity.requestPermissions(searchPermissions, requestCode);
- }
+ public static String[] recentsPermissions = new String[]{Manifest.permission.READ_CALL_LOG, Manifest.permission.WRITE_CALL_LOG};
+ public static String[] contactsPermissions = new String[]{Manifest.permission.READ_CONTACTS};
+ public static String[] searchPermissions = new String[]{Manifest.permission.READ_CONTACTS};
+ public static String[] makeCallPermissions = new String[]{Manifest.permission.CALL_PHONE, Manifest.permission.READ_PHONE_STATE};
+
+ public static boolean hasRecentsPermission(Context context) {
+ return hasPermissions(context, recentsPermissions);
+ }
+
+ public static boolean hasContactsPermission(Context context) {
+ return hasPermissions(context, contactsPermissions);
+ }
+
+ public static boolean hasSearchPermission(Context context) {
+ return hasPermissions(context, searchPermissions);
+ }
+
+ public static boolean hasMakeCallPermission(Context context) {
+ return hasPermissions(context, makeCallPermissions);
+ }
+
+ private static boolean hasPermissions(Context context, String[] permissions) {
+ if (context == null) return false;
+ for (String permission : permissions) {
+ if (context.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static void requestRecentsPermission(Activity activity, int requestCode) {
+ activity.requestPermissions(recentsPermissions, requestCode);
+ }
+
+ public static void requestContactsPermission(Fragment fragment, int requestCode) {
+ fragment.requestPermissions(contactsPermissions, requestCode);
+ }
+
+ public static void requestContactsPermission(Activity activity, int requestCode) {
+ activity.requestPermissions(contactsPermissions, requestCode);
+ }
+
+ public static void requestMakeCallPermission(Activity activity, int requestCode) {
+ activity.requestPermissions(makeCallPermissions, requestCode);
+ }
+
+ public static void requestSearchPermission(Activity activity, int requestCode) {
+ activity.requestPermissions(searchPermissions, requestCode);
+ }
}
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/SearchListener.kt b/core/common/src/main/java/dev/alenajam/opendialer/core/common/SearchListener.kt
deleted file mode 100644
index fa2d780..0000000
--- a/core/common/src/main/java/dev/alenajam/opendialer/core/common/SearchListener.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package dev.alenajam.opendialer.core.common
-
-interface SearchListener {
- fun onSearch(query: String)
-}
\ No newline at end of file
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/SearchOpenChangeListener.kt b/core/common/src/main/java/dev/alenajam/opendialer/core/common/SearchOpenChangeListener.kt
deleted file mode 100644
index f4f7b78..0000000
--- a/core/common/src/main/java/dev/alenajam/opendialer/core/common/SearchOpenChangeListener.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package dev.alenajam.opendialer.core.common
-
-interface SearchOpenChangeListener {
- fun onOpenChange(isOpen: Boolean);
-}
\ No newline at end of file
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/SharedPreferenceHelper.java b/core/common/src/main/java/dev/alenajam/opendialer/core/common/SharedPreferenceHelper.java
index ce22d2a..bd61751 100644
--- a/core/common/src/main/java/dev/alenajam/opendialer/core/common/SharedPreferenceHelper.java
+++ b/core/common/src/main/java/dev/alenajam/opendialer/core/common/SharedPreferenceHelper.java
@@ -13,33 +13,33 @@
import java.util.Arrays;
public abstract class SharedPreferenceHelper {
- public static final String SP_QUICK_RESPONSES = "SP_QUICK_RESPONSES";
- public static final String KEY_SETTING_THEME = "theme";
- public static final String KEY_SETTING_DEFAULT = "default";
- public static final String KEY_SETTING_SOUND_VIBRATION = "sound";
- public static final String KEY_SETTING_QUICK_RESPONSES = "quick_responses";
- public static final String KEY_SETTING_BLOCKED_NUMBERS = "blockedNumbers";
- public static final String KEY_SETTING_NOTIFICATION_SETTINGS = "notificationSettings";
-
- public static SharedPreferences getSharedPreferences(Context context) {
- return PreferenceManager.getDefaultSharedPreferences(context);
- }
-
- public static void init(Context context) {
- SharedPreferences sharedPreferences = getSharedPreferences(context);
-
- String theme = sharedPreferences.getString(SharedPreferenceHelper.KEY_SETTING_THEME, null);
- try {
- CommonUtils.setTheme(theme == null ? AppCompatDelegate.MODE_NIGHT_NO : Integer.parseInt(theme));
- } catch (NumberFormatException e) {
- if (e.getLocalizedMessage() != null)
- Log.d(SharedPreferenceHelper.class.getSimpleName(), e.getLocalizedMessage());
+ public static final String SP_QUICK_RESPONSES = "SP_QUICK_RESPONSES";
+ public static final String KEY_SETTING_THEME = "theme";
+ public static final String KEY_SETTING_DEFAULT = "default";
+ public static final String KEY_SETTING_SOUND_VIBRATION = "sound";
+ public static final String KEY_SETTING_QUICK_RESPONSES = "quick_responses";
+ public static final String KEY_SETTING_BLOCKED_NUMBERS = "blockedNumbers";
+ public static final String KEY_SETTING_NOTIFICATION_SETTINGS = "notificationSettings";
+
+ public static SharedPreferences getSharedPreferences(Context context) {
+ return PreferenceManager.getDefaultSharedPreferences(context);
}
- if (!sharedPreferences.contains(SharedPreferenceHelper.SP_QUICK_RESPONSES)) {
- String[] quickResponses = context.getResources().getStringArray(R.array.array_quick_responses);
- ArrayList quickResponseList = new ArrayList<>(Arrays.asList(quickResponses));
- sharedPreferences.edit().putString(SharedPreferenceHelper.SP_QUICK_RESPONSES, new Gson().toJson(quickResponseList)).apply();
+ public static void init(Context context) {
+ SharedPreferences sharedPreferences = getSharedPreferences(context);
+
+ String theme = sharedPreferences.getString(SharedPreferenceHelper.KEY_SETTING_THEME, null);
+ try {
+ CommonUtils.setTheme(theme == null ? AppCompatDelegate.MODE_NIGHT_NO : Integer.parseInt(theme));
+ } catch (NumberFormatException e) {
+ if (e.getLocalizedMessage() != null)
+ Log.d(SharedPreferenceHelper.class.getSimpleName(), e.getLocalizedMessage());
+ }
+
+ if (!sharedPreferences.contains(SharedPreferenceHelper.SP_QUICK_RESPONSES)) {
+ String[] quickResponses = context.getResources().getStringArray(R.array.array_quick_responses);
+ ArrayList quickResponseList = new ArrayList<>(Arrays.asList(quickResponses));
+ sharedPreferences.edit().putString(SharedPreferenceHelper.SP_QUICK_RESPONSES, new Gson().toJson(quickResponseList)).apply();
+ }
}
- }
}
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/ToolbarListener.kt b/core/common/src/main/java/dev/alenajam/opendialer/core/common/ToolbarListener.kt
deleted file mode 100644
index 891c7b8..0000000
--- a/core/common/src/main/java/dev/alenajam/opendialer/core/common/ToolbarListener.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package dev.alenajam.opendialer.core.common
-
-interface ToolbarListener {
- fun showToolbar(animate: Boolean)
- fun hideToolbar(animate: Boolean)
-}
\ No newline at end of file
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/composeUtils.kt b/core/common/src/main/java/dev/alenajam/opendialer/core/common/composeUtils.kt
new file mode 100644
index 0000000..d773a92
--- /dev/null
+++ b/core/common/src/main/java/dev/alenajam/opendialer/core/common/composeUtils.kt
@@ -0,0 +1,30 @@
+package dev.alenajam.opendialer.core.common
+
+import android.content.Context
+import android.content.ContextWrapper
+import androidx.activity.ComponentActivity
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.calculateEndPadding
+import androidx.compose.foundation.layout.calculateStartPadding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.unit.Dp
+
+fun Context.getActivity(): ComponentActivity? = when (this) {
+ is ComponentActivity -> this
+ is ContextWrapper -> baseContext.getActivity()
+ else -> null
+}
+
+@Composable
+fun PaddingValues.copy(
+ start: Dp = calculateStartPadding(LocalLayoutDirection.current),
+ top: Dp = calculateTopPadding(),
+ end: Dp = calculateEndPadding(LocalLayoutDirection.current),
+ bottom: Dp = calculateBottomPadding(),
+) = PaddingValues(
+ start = start,
+ top = top,
+ end = end,
+ bottom = bottom
+)
\ No newline at end of file
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/exception/Failure.kt b/core/common/src/main/java/dev/alenajam/opendialer/core/common/exception/Failure.kt
index b644611..e94afc3 100644
--- a/core/common/src/main/java/dev/alenajam/opendialer/core/common/exception/Failure.kt
+++ b/core/common/src/main/java/dev/alenajam/opendialer/core/common/exception/Failure.kt
@@ -1,8 +1,8 @@
package dev.alenajam.opendialer.core.common.exception
sealed class Failure(var source: String? = null) {
- class GenericFailure(val throwable: Throwable? = null, source: String? = null) : Failure(source)
- object NoData : Failure()
- object NotPermitted : Failure()
- object LocalFailure : Failure()
+ class GenericFailure(val throwable: Throwable? = null, source: String? = null) : Failure(source)
+ object NoData : Failure()
+ object NotPermitted : Failure()
+ object LocalFailure : Failure()
}
\ No newline at end of file
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/forwardingPainter.kt b/core/common/src/main/java/dev/alenajam/opendialer/core/common/forwardingPainter.kt
new file mode 100644
index 0000000..f5dd242
--- /dev/null
+++ b/core/common/src/main/java/dev/alenajam/opendialer/core/common/forwardingPainter.kt
@@ -0,0 +1,60 @@
+package dev.alenajam.opendialer.core.common
+
+import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.graphics.DefaultAlpha
+import androidx.compose.ui.graphics.drawscope.DrawScope
+import androidx.compose.ui.graphics.painter.Painter
+
+/**
+ * Create and return a new [Painter] that wraps [painter] with its [alpha], [colorFilter], or [onDraw] overwritten.
+ */
+fun forwardingPainter(
+ painter: Painter,
+ alpha: Float = DefaultAlpha,
+ colorFilter: ColorFilter? = null,
+ onDraw: DrawScope.(ForwardingDrawInfo) -> Unit = DefaultOnDraw,
+): Painter = ForwardingPainter(painter, alpha, colorFilter, onDraw)
+
+data class ForwardingDrawInfo(
+ val painter: Painter,
+ val alpha: Float,
+ val colorFilter: ColorFilter?,
+)
+
+private class ForwardingPainter(
+ private val painter: Painter,
+ private var alpha: Float,
+ private var colorFilter: ColorFilter?,
+ private val onDraw: DrawScope.(ForwardingDrawInfo) -> Unit,
+) : Painter() {
+
+ private var info = newInfo()
+
+ override val intrinsicSize get() = painter.intrinsicSize
+
+ override fun applyAlpha(alpha: Float): Boolean {
+ if (alpha != DefaultAlpha) {
+ this.alpha = alpha
+ this.info = newInfo()
+ }
+ return true
+ }
+
+ override fun applyColorFilter(colorFilter: ColorFilter?): Boolean {
+ if (colorFilter != null) {
+ this.colorFilter = colorFilter
+ this.info = newInfo()
+ }
+ return true
+ }
+
+ override fun DrawScope.onDraw() = onDraw(info)
+
+ private fun newInfo() = ForwardingDrawInfo(painter, alpha, colorFilter)
+}
+
+private val DefaultOnDraw: DrawScope.(ForwardingDrawInfo) -> Unit = { info ->
+ with(info.painter) {
+ draw(size, info.alpha, info.colorFilter)
+ }
+}
\ No newline at end of file
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/functional/Either.kt b/core/common/src/main/java/dev/alenajam/opendialer/core/common/functional/Either.kt
index 1de0365..3b541fb 100644
--- a/core/common/src/main/java/dev/alenajam/opendialer/core/common/functional/Either.kt
+++ b/core/common/src/main/java/dev/alenajam/opendialer/core/common/functional/Either.kt
@@ -10,46 +10,46 @@ package dev.alenajam.opendialer.core.common.functional
* @see Right
*/
sealed class Either {
- /** * Represents the left side of [Either] class which by convention is a "Failure". */
- data class Left(val a: L) : Either()
+ /** * Represents the left side of [Either] class which by convention is a "Failure". */
+ data class Left(val a: L) : Either()
- /** * Represents the right side of [Either] class which by convention is a "Success". */
- data class Right(val b: R) : Either()
+ /** * Represents the right side of [Either] class which by convention is a "Success". */
+ data class Right(val b: R) : Either()
- /**
- * Returns true if this is a Right, false otherwise.
- * @see Right
- */
- val isRight get() = this is Right
+ /**
+ * Returns true if this is a Right, false otherwise.
+ * @see Right
+ */
+ val isRight get() = this is Right
- /**
- * Returns true if this is a Left, false otherwise.
- * @see Left
- */
- val isLeft get() = this is Left
+ /**
+ * Returns true if this is a Left, false otherwise.
+ * @see Left
+ */
+ val isLeft get() = this is Left
- /**
- * Creates a Left type.
- * @see Left
- */
- fun left(a: L) = Left(a)
+ /**
+ * Creates a Left type.
+ * @see Left
+ */
+ fun left(a: L) = Left(a)
- /**
- * Creates a Left type.
- * @see Right
- */
- fun right(b: R) = Right(b)
+ /**
+ * Creates a Left type.
+ * @see Right
+ */
+ fun right(b: R) = Right(b)
- /**
- * Applies fnL if this is a Left or fnR if this is a Right.
- * @see Left
- * @see Right
- */
- fun fold(fnL: (L) -> Any, fnR: (R) -> Any): Any =
- when (this) {
- is Left -> fnL(a)
- is Right -> fnR(b)
- }
+ /**
+ * Applies fnL if this is a Left or fnR if this is a Right.
+ * @see Left
+ * @see Right
+ */
+ fun fold(fnL: (L) -> Any, fnR: (R) -> Any): Any =
+ when (this) {
+ is Left -> fnL(a)
+ is Right -> fnR(b)
+ }
}
/**
@@ -57,7 +57,7 @@ sealed class Either {
* See Credits to Alex Hart.
*/
fun ((A) -> B).c(f: (B) -> C): (A) -> C = {
- f(this(it))
+ f(this(it))
}
/**
@@ -65,10 +65,10 @@ fun ((A) -> B).c(f: (B) -> C): (A) -> C = {
* to operate on. If it is Left, operations like map, flatMap, ... return the Left value unchanged.
*/
fun Either.flatMap(fn: (R) -> Either): Either =
- when (this) {
- is Either.Left -> Either.Left(a)
- is Either.Right -> fn(b)
- }
+ when (this) {
+ is Either.Left -> Either.Left(a)
+ is Either.Right -> fn(b)
+ }
/**
* Right-biased map() FP convention which means that Right is assumed to be the default case
@@ -80,7 +80,7 @@ fun Either.map(fn: (R) -> (T)): Either = this.flatMap(fn.c
* Right(12).getOrElse(17) RETURNS 12 and Left(12).getOrElse(17) RETURNS 17
*/
fun Either.getOrElse(value: R): R =
- when (this) {
- is Either.Left -> value
- is Either.Right -> b
- }
\ No newline at end of file
+ when (this) {
+ is Either.Left -> value
+ is Either.Right -> b
+ }
\ No newline at end of file
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/functional/Event.kt b/core/common/src/main/java/dev/alenajam/opendialer/core/common/functional/Event.kt
index 4f4af4d..d953cb5 100644
--- a/core/common/src/main/java/dev/alenajam/opendialer/core/common/functional/Event.kt
+++ b/core/common/src/main/java/dev/alenajam/opendialer/core/common/functional/Event.kt
@@ -5,23 +5,23 @@ package dev.alenajam.opendialer.core.common.functional
*/
open class Event(private val content: T) {
- var hasBeenHandled = false
- private set // Allow external read but not write
+ var hasBeenHandled = false
+ private set // Allow external read but not write
- /**
- * Returns the content and prevents its use again.
- */
- fun getContentIfNotHandled(): T? {
- return if (hasBeenHandled) {
- null
- } else {
- hasBeenHandled = true
- content
+ /**
+ * Returns the content and prevents its use again.
+ */
+ fun getContentIfNotHandled(): T? {
+ return if (hasBeenHandled) {
+ null
+ } else {
+ hasBeenHandled = true
+ content
+ }
}
- }
- /**
- * Returns the content, even if it's already been handled.
- */
- fun peekContent(): T = content
+ /**
+ * Returns the content, even if it's already been handled.
+ */
+ fun peekContent(): T = content
}
\ No newline at end of file
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/functional/EventObserver.kt b/core/common/src/main/java/dev/alenajam/opendialer/core/common/functional/EventObserver.kt
index daf1114..62f85e4 100644
--- a/core/common/src/main/java/dev/alenajam/opendialer/core/common/functional/EventObserver.kt
+++ b/core/common/src/main/java/dev/alenajam/opendialer/core/common/functional/EventObserver.kt
@@ -9,7 +9,7 @@ import androidx.lifecycle.Observer
* [onEventUnhandledContent] is *only* called if the [Event]'s contents has not been handled.
*/
class EventObserver(private val onEventUnhandledContent: (T) -> Unit) : Observer> {
- override fun onChanged(value: Event) {
- value.getContentIfNotHandled()?.let { onEventUnhandledContent(it) }
- }
+ override fun onChanged(value: Event) {
+ value.getContentIfNotHandled()?.let { onEventUnhandledContent(it) }
+ }
}
\ No newline at end of file
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/functional/Extensions.kt b/core/common/src/main/java/dev/alenajam/opendialer/core/common/functional/Extensions.kt
index 4cae6d1..1bc5de5 100644
--- a/core/common/src/main/java/dev/alenajam/opendialer/core/common/functional/Extensions.kt
+++ b/core/common/src/main/java/dev/alenajam/opendialer/core/common/functional/Extensions.kt
@@ -11,67 +11,67 @@ import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume
fun NavController.safeNavigate(directions: NavDirections) {
- currentDestination?.getAction(directions.actionId)?.let {
- navigate(directions)
- }
+ currentDestination?.getAction(directions.actionId)?.let {
+ navigate(directions)
+ }
}
fun NavController.safeNavigate(@IdRes resId: Int) {
- currentDestination?.getAction(resId)?.let {
- navigate(resId)
- }
+ currentDestination?.getAction(resId)?.let {
+ navigate(resId)
+ }
}
suspend fun View.awaitNextLayout() = suspendCancellableCoroutine { cont ->
- // This lambda is invoked immediately, allowing us to create
- // a callback/listener
+ // This lambda is invoked immediately, allowing us to create
+ // a callback/listener
- val listener = object : View.OnLayoutChangeListener {
- override fun onLayoutChange(
- v: View?,
- left: Int,
- top: Int,
- right: Int,
- bottom: Int,
- oldLeft: Int,
- oldTop: Int,
- oldRight: Int,
- oldBottom: Int
- ) {
- // The next layout has happened!
- // First remove the listener to not leak the coroutine
- v?.removeOnLayoutChangeListener(this)
- // Finally resume the continuation, and
- // wake the coroutine up
- cont.resume(Unit)
+ val listener = object : View.OnLayoutChangeListener {
+ override fun onLayoutChange(
+ v: View?,
+ left: Int,
+ top: Int,
+ right: Int,
+ bottom: Int,
+ oldLeft: Int,
+ oldTop: Int,
+ oldRight: Int,
+ oldBottom: Int
+ ) {
+ // The next layout has happened!
+ // First remove the listener to not leak the coroutine
+ v?.removeOnLayoutChangeListener(this)
+ // Finally resume the continuation, and
+ // wake the coroutine up
+ cont.resume(Unit)
+ }
}
- }
- // If the coroutine is cancelled, remove the listener
- cont.invokeOnCancellation { removeOnLayoutChangeListener(listener) }
- // And finally add the listener to view
- addOnLayoutChangeListener(listener)
+ // If the coroutine is cancelled, remove the listener
+ cont.invokeOnCancellation { removeOnLayoutChangeListener(listener) }
+ // And finally add the listener to view
+ addOnLayoutChangeListener(listener)
- // The coroutine will now be suspended. It will only be resumed
- // when calling cont.resume() in the listener above
+ // The coroutine will now be suspended. It will only be resumed
+ // when calling cont.resume() in the listener above
}
val Int.px: Int get() = (this * getSystem().displayMetrics.density).toInt()
fun LiveData.combineLatest(b: LiveData): LiveData> {
- return MediatorLiveData>().apply {
- var lastA: A? = null
- var lastB: B? = null
+ return MediatorLiveData>().apply {
+ var lastA: A? = null
+ var lastB: B? = null
- addSource(this@combineLatest) {
- if (it == null && value != null) value = null
- lastA = it
- if (lastA != null && lastB != null) value = lastA!! to lastB!!
- }
+ addSource(this@combineLatest) {
+ if (it == null && value != null) value = null
+ lastA = it
+ if (lastA != null && lastB != null) value = lastA!! to lastB!!
+ }
- addSource(b) {
- if (it == null && value != null) value = null
- lastB = it
- if (lastA != null && lastB != null) value = lastA!! to lastB!!
+ addSource(b) {
+ if (it == null && value != null) value = null
+ lastB = it
+ if (lastA != null && lastB != null) value = lastA!! to lastB!!
+ }
}
- }
}
\ No newline at end of file
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/interactor/UseCase.kt b/core/common/src/main/java/dev/alenajam/opendialer/core/common/interactor/UseCase.kt
index eb8021e..5413f50 100644
--- a/core/common/src/main/java/dev/alenajam/opendialer/core/common/interactor/UseCase.kt
+++ b/core/common/src/main/java/dev/alenajam/opendialer/core/common/interactor/UseCase.kt
@@ -8,14 +8,14 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
abstract class UseCase {
- abstract suspend fun run(params: Params): Either
+ abstract suspend fun run(params: Params): Either
- operator fun invoke(
- scope: CoroutineScope,
- params: Params,
- onResult: (Either) -> Unit
- ) = scope.launch {
- val result = withContext(Dispatchers.IO) { run(params) }
- onResult(result)
- }
+ operator fun invoke(
+ scope: CoroutineScope,
+ params: Params,
+ onResult: (Either) -> Unit
+ ) = scope.launch {
+ val result = withContext(Dispatchers.IO) { run(params) }
+ onResult(result)
+ }
}
\ No newline at end of file
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/interactor/UseCaseFlow.kt b/core/common/src/main/java/dev/alenajam/opendialer/core/common/interactor/UseCaseFlow.kt
index 0c1f9ed..5537d86 100644
--- a/core/common/src/main/java/dev/alenajam/opendialer/core/common/interactor/UseCaseFlow.kt
+++ b/core/common/src/main/java/dev/alenajam/opendialer/core/common/interactor/UseCaseFlow.kt
@@ -13,33 +13,33 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
abstract class UseCaseFlow {
- private var job: Job? = null
+ private var job: Job? = null
- abstract fun run(params: Params): Flow
+ abstract fun run(params: Params): Flow
- operator fun invoke(
- scope: CoroutineScope,
- params: Params,
- onResult: (Either) -> Unit,
- onCompletion: (suspend (flowCollector: FlowCollector) -> Unit)? = null
- ) {
- if (job?.isActive == true) {
- return
- }
+ operator fun invoke(
+ scope: CoroutineScope,
+ params: Params,
+ onResult: (Either) -> Unit,
+ onCompletion: (suspend (flowCollector: FlowCollector) -> Unit)? = null
+ ) {
+ if (job?.isActive == true) {
+ return
+ }
- job = scope.launch {
- withContext(Dispatchers.IO) {
- run(params)
- .catch {
- onResult(Either.Left(Failure.GenericFailure(it)))
- }
- .onCompletion {
- onCompletion?.invoke(this)
- }
- .collect {
- onResult(Either.Right(it))
- }
- }
+ job = scope.launch {
+ withContext(Dispatchers.IO) {
+ run(params)
+ .catch {
+ onResult(Either.Left(Failure.GenericFailure(it)))
+ }
+ .onCompletion {
+ onCompletion?.invoke(this)
+ }
+ .collect {
+ onResult(Either.Right(it))
+ }
+ }
+ }
}
- }
}
\ No newline at end of file
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/navigateUtils.kt b/core/common/src/main/java/dev/alenajam/opendialer/core/common/navigateUtils.kt
deleted file mode 100644
index 19bf3b6..0000000
--- a/core/common/src/main/java/dev/alenajam/opendialer/core/common/navigateUtils.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package dev.alenajam.opendialer.core.common
-
-import androidx.core.net.toUri
-import androidx.navigation.NavController
-import androidx.navigation.NavDeepLinkRequest
-
-const val CALL_DETAIL_PARAM_CALL_IDS =
- "dev.alenajam.opendialer.feature.callDetail.CallDetailFragment_param_call_ids"
-
-fun NavController.navigateToCallDetail(callIds: List) {
- val ids = callIds.joinToString(",")
- SharedPreferenceHelper.getSharedPreferences(context).edit()
- .putString(CALL_DETAIL_PARAM_CALL_IDS, ids).apply()
- val request = NavDeepLinkRequest.Builder
- .fromUri("android-app://dev.alenajam.opendialer/feature/callDetail/callDetailFragment".toUri())
- .build()
- navigate(request)
-}
\ No newline at end of file
diff --git a/core/common/src/main/java/dev/alenajam/opendialer/core/common/ui/Dialpad.kt b/core/common/src/main/java/dev/alenajam/opendialer/core/common/ui/Dialpad.kt
new file mode 100644
index 0000000..cbcfa46
--- /dev/null
+++ b/core/common/src/main/java/dev/alenajam/opendialer/core/common/ui/Dialpad.kt
@@ -0,0 +1,149 @@
+package dev.alenajam.opendialer.core.common.ui
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.height
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.FilledTonalButton
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+
+@Composable
+fun Dialpad(
+ onDigitClick: (digit: Char) -> Unit
+) {
+ Column(
+ verticalArrangement = Arrangement.spacedBy(10.dp)
+ ) {
+ Row(
+ modifier = Modifier.height(60.dp),
+ horizontalArrangement = Arrangement.spacedBy(10.dp)
+ ) {
+ DigitButton(
+ digit = '1',
+ subtitle = "",
+ onClick = onDigitClick
+ )
+
+ DigitButton(
+ digit = '2',
+ subtitle = "abc",
+ onClick = onDigitClick
+ )
+
+ DigitButton(
+ digit = '3',
+ subtitle = "def",
+ onClick = onDigitClick
+ )
+ }
+
+ Row(
+ modifier = Modifier.height(60.dp),
+ horizontalArrangement = Arrangement.spacedBy(10.dp)
+ ) {
+ DigitButton(
+ digit = '4',
+ subtitle = "ghi",
+ onClick = onDigitClick
+ )
+
+ DigitButton(
+ digit = '5',
+ subtitle = "jkl",
+ onClick = onDigitClick
+ )
+
+ DigitButton(
+ digit = '6',
+ subtitle = "mno",
+ onClick = onDigitClick
+ )
+ }
+
+ Row(
+ modifier = Modifier.height(60.dp),
+ horizontalArrangement = Arrangement.spacedBy(10.dp)
+ ) {
+ DigitButton(
+ digit = '7',
+ subtitle = "pqrs",
+ onClick = onDigitClick
+ )
+
+ DigitButton(
+ digit = '8',
+ subtitle = "tuv",
+ onClick = onDigitClick
+ )
+
+ DigitButton(
+ digit = '9',
+ subtitle = "wxyz",
+ onClick = onDigitClick
+ )
+ }
+
+ Row(
+ modifier = Modifier.height(60.dp),
+ horizontalArrangement = Arrangement.spacedBy(10.dp)
+ ) {
+ DigitButton(
+ digit = '*',
+ onClick = onDigitClick
+ )
+
+ DigitButton(
+ digit = '0',
+ subtitle = "+",
+ onClick = onDigitClick
+ )
+
+ DigitButton(
+ digit = '#',
+ onClick = onDigitClick
+ )
+ }
+ }
+}
+
+@Composable
+private fun RowScope.DigitButton(
+ digit: Char,
+ subtitle: String? = null,
+ onClick: (digit: Char) -> Unit
+) {
+ FilledTonalButton(
+ onClick = { onClick(digit) },
+ modifier = Modifier
+ .weight(1f)
+ .fillMaxHeight(),
+ contentPadding = PaddingValues(),
+ colors = ButtonDefaults.filledTonalButtonColors().copy(
+ containerColor = Color.White,
+ )
+ ) {
+
+ Column(horizontalAlignment = Alignment.CenterHorizontally) {
+ Text(
+ text = digit.toString(),
+ fontSize = 25.sp
+ )
+
+ if (subtitle != null) {
+ Text(
+ text = subtitle,
+ )
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/common/src/main/res/drawable-hdpi/ic_arrow_back.png b/core/common/src/main/res/drawable-hdpi/ic_arrow_back.png
deleted file mode 100644
index 0f7b2c7..0000000
Binary files a/core/common/src/main/res/drawable-hdpi/ic_arrow_back.png and /dev/null differ
diff --git a/core/common/src/main/res/drawable-mdpi/ic_arrow_back.png b/core/common/src/main/res/drawable-mdpi/ic_arrow_back.png
deleted file mode 100644
index 1e4b7c3..0000000
Binary files a/core/common/src/main/res/drawable-mdpi/ic_arrow_back.png and /dev/null differ
diff --git a/core/common/src/main/res/drawable-xhdpi/ic_arrow_back.png b/core/common/src/main/res/drawable-xhdpi/ic_arrow_back.png
deleted file mode 100644
index bdfd67e..0000000
Binary files a/core/common/src/main/res/drawable-xhdpi/ic_arrow_back.png and /dev/null differ
diff --git a/core/common/src/main/res/drawable-xxhdpi/ic_arrow_back.png b/core/common/src/main/res/drawable-xxhdpi/ic_arrow_back.png
deleted file mode 100644
index a1f4d26..0000000
Binary files a/core/common/src/main/res/drawable-xxhdpi/ic_arrow_back.png and /dev/null differ
diff --git a/core/common/src/main/res/drawable-xxxhdpi/ic_arrow_back.png b/core/common/src/main/res/drawable-xxxhdpi/ic_arrow_back.png
deleted file mode 100644
index aa86273..0000000
Binary files a/core/common/src/main/res/drawable-xxxhdpi/ic_arrow_back.png and /dev/null differ
diff --git a/core/common/src/main/res/layout/dialog_keypad.xml b/core/common/src/main/res/layout/dialog_keypad.xml
deleted file mode 100644
index 36d4668..0000000
--- a/core/common/src/main/res/layout/dialog_keypad.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/core/common/src/main/res/layout/key_button.xml b/core/common/src/main/res/layout/key_button.xml
deleted file mode 100644
index 61a8511..0000000
--- a/core/common/src/main/res/layout/key_button.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
diff --git a/core/common/src/main/res/layout/keypad_incall.xml b/core/common/src/main/res/layout/keypad_incall.xml
deleted file mode 100644
index 72c944d..0000000
--- a/core/common/src/main/res/layout/keypad_incall.xml
+++ /dev/null
@@ -1,163 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/core/common/src/main/res/layout/toolbar.xml b/core/common/src/main/res/layout/toolbar.xml
deleted file mode 100644
index 14e2083..0000000
--- a/core/common/src/main/res/layout/toolbar.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/core/common/src/main/res/values/attrs.xml b/core/common/src/main/res/values/attrs.xml
deleted file mode 100644
index 8559951..0000000
--- a/core/common/src/main/res/values/attrs.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/core/common/src/main/res/values/styles.xml b/core/common/src/main/res/values/styles.xml
deleted file mode 100644
index 3299829..0000000
--- a/core/common/src/main/res/values/styles.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/core/common/src/test/java/dev/alenajam/opendialer/core/common/ExampleUnitTest.kt b/core/common/src/test/java/dev/alenajam/opendialer/core/common/ExampleUnitTest.kt
index 504e13c..5723474 100644
--- a/core/common/src/test/java/dev/alenajam/opendialer/core/common/ExampleUnitTest.kt
+++ b/core/common/src/test/java/dev/alenajam/opendialer/core/common/ExampleUnitTest.kt
@@ -1,8 +1,7 @@
package dev.alenajam.opendialer.core.common
-import org.junit.Test
-
import org.junit.Assert.*
+import org.junit.Test
/**
* Example local unit test, which will execute on the development machine (host).
@@ -10,8 +9,8 @@ import org.junit.Assert.*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
- @Test
- fun addition_isCorrect() {
- assertEquals(4, 2 + 2)
- }
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
}
\ No newline at end of file
diff --git a/data/calls/build.gradle.kts b/data/calls/build.gradle.kts
index 9f5a60f..d2c8845 100644
--- a/data/calls/build.gradle.kts
+++ b/data/calls/build.gradle.kts
@@ -1,45 +1,48 @@
plugins {
- id("com.android.library")
- id("org.jetbrains.kotlin.android")
- id("kotlin-kapt")
- id("com.google.dagger.hilt.android")
+ id("com.android.library")
+ id("org.jetbrains.kotlin.android")
+ id("kotlin-kapt")
+ id("com.google.dagger.hilt.android")
}
android {
- namespace = "dev.alenajam.opendialer.data.calls"
- compileSdk = 34
+ namespace = "dev.alenajam.opendialer.data.calls"
+ compileSdk = 34
- defaultConfig {
- minSdk = 24
+ defaultConfig {
+ minSdk = 24
- testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
- consumerProguardFiles("consumer-rules.pro")
- }
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles("consumer-rules.pro")
+ }
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ }
}
- }
}
dependencies {
- implementation(project(":core:common"))
-
- implementation("androidx.core:core-ktx:1.12.0")
- testImplementation("junit:junit:4.13.2")
- androidTestImplementation("androidx.test.ext:junit:1.1.5")
- androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
-
- implementation("com.google.dagger:hilt-android:2.48.1")
- kapt("com.google.dagger:hilt-compiler:2.48.1")
- androidTestImplementation("com.google.dagger:hilt-android-testing:2.48.1")
- kaptAndroidTest("com.google.dagger:hilt-compiler:2.48.1")
- testImplementation("com.google.dagger:hilt-android-testing:2.48.1")
- kaptTest("com.google.dagger:hilt-compiler:2.48.1")
+ implementation(project(":core:common"))
+
+ implementation("androidx.core:core-ktx:1.12.0")
+ testImplementation("junit:junit:4.13.2")
+ androidTestImplementation("androidx.test.ext:junit:1.1.5")
+ androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
+
+ implementation("com.google.dagger:hilt-android:2.48.1")
+ kapt("com.google.dagger:hilt-compiler:2.48.1")
+ androidTestImplementation("com.google.dagger:hilt-android-testing:2.48.1")
+ kaptAndroidTest("com.google.dagger:hilt-compiler:2.48.1")
+ testImplementation("com.google.dagger:hilt-android-testing:2.48.1")
+ kaptTest("com.google.dagger:hilt-compiler:2.48.1")
}
kotlin {
- jvmToolchain(17)
+ jvmToolchain(17)
}
\ No newline at end of file
diff --git a/data/calls/src/androidTest/java/dev/alenajam/opendialer/data/calls/ExampleInstrumentedTest.kt b/data/calls/src/androidTest/java/dev/alenajam/opendialer/data/calls/ExampleInstrumentedTest.kt
index 2827fe0..7c7e66d 100644
--- a/data/calls/src/androidTest/java/dev/alenajam/opendialer/data/calls/ExampleInstrumentedTest.kt
+++ b/data/calls/src/androidTest/java/dev/alenajam/opendialer/data/calls/ExampleInstrumentedTest.kt
@@ -1,13 +1,11 @@
package dev.alenajam.opendialer.data.calls
-import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
-
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assert.*
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.Assert.*
-
/**
* Instrumented test, which will execute on an Android device.
*
@@ -15,10 +13,10 @@ import org.junit.Assert.*
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
- @Test
- fun useAppContext() {
- // Context of the app under test.
- val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("dev.alenajam.opendialer.data.calls.test", appContext.packageName)
- }
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("dev.alenajam.opendialer.data.calls.test", appContext.packageName)
+ }
}
\ No newline at end of file
diff --git a/data/calls/src/main/AndroidManifest.xml b/data/calls/src/main/AndroidManifest.xml
index a5918e6..44008a4 100644
--- a/data/calls/src/main/AndroidManifest.xml
+++ b/data/calls/src/main/AndroidManifest.xml
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/CallDetailData.kt b/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/CallDetailData.kt
index 115aff3..41c1674 100644
--- a/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/CallDetailData.kt
+++ b/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/CallDetailData.kt
@@ -7,39 +7,39 @@ import android.provider.CallLog.Calls
import android.util.Log
abstract class CallDetailData {
- companion object {
- private val TAG = CallDetailData::class.simpleName
- val URI: Uri = Calls.CONTENT_URI
- private const val LIMIT = 1000
+ companion object {
+ private val TAG = CallDetailData::class.simpleName
+ val URI: Uri = Calls.CONTENT_URI
+ private const val LIMIT = 1000
- private val projection = arrayOf(
- Calls._ID,
- Calls.NUMBER,
- Calls.CACHED_NORMALIZED_NUMBER,
- Calls.CACHED_NAME,
- Calls.DATE,
- Calls.DURATION,
- Calls.TYPE,
- Calls.NEW,
- Calls.CACHED_PHOTO_URI,
- Calls.COUNTRY_ISO,
- Calls.CACHED_NUMBER_LABEL,
- Calls.CACHED_PHOTO_ID,
- Calls.GEOCODED_LOCATION,
- Calls.CACHED_FORMATTED_NUMBER,
- Calls.CACHED_NORMALIZED_NUMBER,
- Calls.CACHED_LOOKUP_URI,
- Calls.POST_DIAL_DIGITS,
- Calls.CACHED_MATCHED_NUMBER,
- Calls.CACHED_NUMBER_TYPE
- )
+ private val projection = arrayOf(
+ Calls._ID,
+ Calls.NUMBER,
+ Calls.CACHED_NORMALIZED_NUMBER,
+ Calls.CACHED_NAME,
+ Calls.DATE,
+ Calls.DURATION,
+ Calls.TYPE,
+ Calls.NEW,
+ Calls.CACHED_PHOTO_URI,
+ Calls.COUNTRY_ISO,
+ Calls.CACHED_NUMBER_LABEL,
+ Calls.CACHED_PHOTO_ID,
+ Calls.GEOCODED_LOCATION,
+ Calls.CACHED_FORMATTED_NUMBER,
+ Calls.CACHED_NORMALIZED_NUMBER,
+ Calls.CACHED_LOOKUP_URI,
+ Calls.POST_DIAL_DIGITS,
+ Calls.CACHED_MATCHED_NUMBER,
+ Calls.CACHED_NUMBER_TYPE
+ )
- /** Filter out:
- * - Blocked calls
- * - Non-video Duo calls
- */
- private val where = { ids: List ->
- """
+ /** Filter out:
+ * - Blocked calls
+ * - Non-video Duo calls
+ */
+ private val where = { ids: List ->
+ """
${Calls.TYPE} != ${Calls.BLOCKED_TYPE}
AND (
${Calls.PHONE_ACCOUNT_COMPONENT_NAME} IS NULL
@@ -48,54 +48,55 @@ abstract class CallDetailData {
)
AND ${Calls._ID} in (${ids.joinToString(",")})
"""
- }
+ }
- fun getCursor(contentResolver: ContentResolver, ids: List): Cursor? = contentResolver.query(
- URI
- .buildUpon()
- .appendQueryParameter(Calls.LIMIT_PARAM_KEY, LIMIT.toString())
- .build(),
- projection,
- where(ids),
- null,
- Calls.DEFAULT_SORT_ORDER
- )
+ fun getCursor(contentResolver: ContentResolver, ids: List): Cursor? =
+ contentResolver.query(
+ URI
+ .buildUpon()
+ .appendQueryParameter(Calls.LIMIT_PARAM_KEY, LIMIT.toString())
+ .build(),
+ projection,
+ where(ids),
+ null,
+ Calls.DEFAULT_SORT_ORDER
+ )
- fun getData(cursor: Cursor): List {
- val start = System.currentTimeMillis()
- val list = mutableListOf()
+ fun getData(cursor: Cursor): List {
+ val start = System.currentTimeMillis()
+ val list = mutableListOf()
- if (cursor.moveToFirst()) {
- do {
- list.add(
- DialerCallEntity(
- id = cursor.getInt(cursor.getColumnIndexOrThrow(Calls._ID)),
- number = cursor.getString(cursor.getColumnIndexOrThrow(Calls.NUMBER)),
- name = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_NAME)),
- date = cursor.getLong(cursor.getColumnIndexOrThrow(Calls.DATE)),
- duration = cursor.getLong(cursor.getColumnIndexOrThrow(Calls.DURATION)),
- type = cursor.getInt(cursor.getColumnIndexOrThrow(Calls.TYPE)),
- isNew = cursor.getInt(cursor.getColumnIndexOrThrow(Calls.NEW)),
- photoUri = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_PHOTO_URI))
- ?.takeIf { it.isNotBlank() },
- countryIso = cursor.getString(cursor.getColumnIndexOrThrow(Calls.COUNTRY_ISO)),
- label = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_NUMBER_LABEL)),
- photoId = cursor.getLong(cursor.getColumnIndexOrThrow(Calls.CACHED_PHOTO_ID)),
- geoDescription = cursor.getString(cursor.getColumnIndexOrThrow(Calls.GEOCODED_LOCATION)),
- formattedNumber = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_FORMATTED_NUMBER)),
- normalizedNumber = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_NORMALIZED_NUMBER)),
- lookupUri = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_LOOKUP_URI)),
- postDialDigits = cursor.getString(cursor.getColumnIndexOrThrow(Calls.POST_DIAL_DIGITS)),
- matchedNumber = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_MATCHED_NUMBER)),
- numberType = cursor.getInt(cursor.getColumnIndexOrThrow(Calls.CACHED_NUMBER_TYPE))
- )
- )
- } while (cursor.moveToNext())
- }
+ if (cursor.moveToFirst()) {
+ do {
+ list.add(
+ DialerCallEntity(
+ id = cursor.getInt(cursor.getColumnIndexOrThrow(Calls._ID)),
+ number = cursor.getString(cursor.getColumnIndexOrThrow(Calls.NUMBER)),
+ name = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_NAME)),
+ date = cursor.getLong(cursor.getColumnIndexOrThrow(Calls.DATE)),
+ duration = cursor.getLong(cursor.getColumnIndexOrThrow(Calls.DURATION)),
+ type = cursor.getInt(cursor.getColumnIndexOrThrow(Calls.TYPE)),
+ isNew = cursor.getInt(cursor.getColumnIndexOrThrow(Calls.NEW)),
+ photoUri = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_PHOTO_URI))
+ ?.takeIf { it.isNotBlank() },
+ countryIso = cursor.getString(cursor.getColumnIndexOrThrow(Calls.COUNTRY_ISO)),
+ label = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_NUMBER_LABEL)),
+ photoId = cursor.getLong(cursor.getColumnIndexOrThrow(Calls.CACHED_PHOTO_ID)),
+ geoDescription = cursor.getString(cursor.getColumnIndexOrThrow(Calls.GEOCODED_LOCATION)),
+ formattedNumber = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_FORMATTED_NUMBER)),
+ normalizedNumber = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_NORMALIZED_NUMBER)),
+ lookupUri = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_LOOKUP_URI)),
+ postDialDigits = cursor.getString(cursor.getColumnIndexOrThrow(Calls.POST_DIAL_DIGITS)),
+ matchedNumber = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_MATCHED_NUMBER)),
+ numberType = cursor.getInt(cursor.getColumnIndexOrThrow(Calls.CACHED_NUMBER_TYPE))
+ )
+ )
+ } while (cursor.moveToNext())
+ }
- val time = (System.currentTimeMillis() - start) / 1000f
- Log.d(TAG, "Call log query time: $time seconds")
- return list
+ val time = (System.currentTimeMillis() - start) / 1000f
+ Log.d(TAG, "Call log query time: $time seconds")
+ return list
+ }
}
- }
}
\ No newline at end of file
diff --git a/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/CallOption.java b/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/CallOption.java
index d16cce1..a031d79 100644
--- a/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/CallOption.java
+++ b/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/CallOption.java
@@ -5,45 +5,45 @@
import java.io.Serializable;
public class CallOption implements Serializable {
- public static final int ID_COPY_NUMBER = 0;
- public static final int ID_EDIT_BEFORE_CALL = 1;
- public static final int ID_DELETE = 2;
- public static final int ID_SEND_MESSAGE = 3;
- public static final int ID_ADD_EXISTING = 4;
- public static final int ID_CREATE_CONTACT = 5;
- public static final int ID_CALL_DETAILS = 6;
- public static final int ID_BLOCK_CALLER = 7;
- public static final int ID_UNBLOCK_CALLER = 8;
-
- @DrawableRes
- private int id;
- private int icon;
- private String text;
-
- public CallOption(int id, int icon, String text) {
- this.id = id;
- this.icon = icon;
- this.text = text;
- }
-
- public CallOption(int id, int icon) {
- this.id = id;
- this.icon = icon;
- }
-
- public int getIcon() {
- return icon;
- }
-
- public String getText() {
- return text;
- }
-
- public void setText(String text) {
- this.text = text;
- }
-
- public int getId() {
- return id;
- }
+ public static final int ID_COPY_NUMBER = 0;
+ public static final int ID_EDIT_BEFORE_CALL = 1;
+ public static final int ID_DELETE = 2;
+ public static final int ID_SEND_MESSAGE = 3;
+ public static final int ID_ADD_EXISTING = 4;
+ public static final int ID_CREATE_CONTACT = 5;
+ public static final int ID_CALL_DETAILS = 6;
+ public static final int ID_BLOCK_CALLER = 7;
+ public static final int ID_UNBLOCK_CALLER = 8;
+
+ @DrawableRes
+ private int id;
+ private int icon;
+ private String text;
+
+ public CallOption(int id, int icon, String text) {
+ this.id = id;
+ this.icon = icon;
+ this.text = text;
+ }
+
+ public CallOption(int id, int icon) {
+ this.id = id;
+ this.icon = icon;
+ }
+
+ public int getIcon() {
+ return icon;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ public int getId() {
+ return id;
+ }
}
diff --git a/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/CallType.kt b/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/CallType.kt
index e7c6944..4c53e9c 100644
--- a/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/CallType.kt
+++ b/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/CallType.kt
@@ -1,27 +1,27 @@
package dev.alenajam.opendialer.data.calls
enum class CallType(val value: Int) {
- INCOMING(1),
+ INCOMING(1),
- /** Call log type for outgoing calls. */
- OUTGOING(2),
+ /** Call log type for outgoing calls. */
+ OUTGOING(2),
- /** Call log type for missed calls. */
- MISSED(3),
+ /** Call log type for missed calls. */
+ MISSED(3),
- /** Call log type for voicemails. */
- VOICEMAIL(4),
+ /** Call log type for voicemails. */
+ VOICEMAIL(4),
- /** Call log type for calls rejected by direct user action. */
- REJECTED(5),
+ /** Call log type for calls rejected by direct user action. */
+ REJECTED(5),
- /** Call log type for calls blocked automatically. */
- BLOCKED(6),
+ /** Call log type for calls blocked automatically. */
+ BLOCKED(6),
- /**
- * Call log type for a call which was answered on another device. Used in situations where
- * a call rings on multiple devices simultaneously and it ended up being answered on a
- * device other than the current one.
- */
- ANSWERED_EXTERNALLY(7)
+ /**
+ * Call log type for a call which was answered on another device. Used in situations where
+ * a call rings on multiple devices simultaneously and it ended up being answered on a
+ * device other than the current one.
+ */
+ ANSWERED_EXTERNALLY(7)
}
\ No newline at end of file
diff --git a/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/CallsData.kt b/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/CallsData.kt
index d53009d..798eadc 100644
--- a/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/CallsData.kt
+++ b/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/CallsData.kt
@@ -4,42 +4,41 @@ import android.content.ContentResolver
import android.database.Cursor
import android.net.Uri
import android.provider.CallLog.Calls
-import android.telephony.PhoneNumberUtils
import android.util.Log
abstract class CallsData {
- companion object {
- private val TAG = CallsData::class.simpleName
- val URI: Uri = Calls.CONTENT_URI
- private const val LIMIT = 1000
+ companion object {
+ private val TAG = CallsData::class.simpleName
+ val URI: Uri = Calls.CONTENT_URI
+ private const val LIMIT = 1000
- private val projection = arrayOf(
- Calls._ID,
- Calls.NUMBER,
- Calls.CACHED_NORMALIZED_NUMBER,
- Calls.CACHED_NAME,
- Calls.DATE,
- Calls.DURATION,
- Calls.TYPE,
- Calls.NEW,
- Calls.CACHED_PHOTO_URI,
- Calls.COUNTRY_ISO,
- Calls.CACHED_NUMBER_LABEL,
- Calls.CACHED_PHOTO_ID,
- Calls.GEOCODED_LOCATION,
- Calls.CACHED_FORMATTED_NUMBER,
- Calls.CACHED_NORMALIZED_NUMBER,
- Calls.CACHED_LOOKUP_URI,
- Calls.POST_DIAL_DIGITS,
- Calls.CACHED_MATCHED_NUMBER,
- Calls.CACHED_NUMBER_TYPE
- )
+ private val projection = arrayOf(
+ Calls._ID,
+ Calls.NUMBER,
+ Calls.CACHED_NORMALIZED_NUMBER,
+ Calls.CACHED_NAME,
+ Calls.DATE,
+ Calls.DURATION,
+ Calls.TYPE,
+ Calls.NEW,
+ Calls.CACHED_PHOTO_URI,
+ Calls.COUNTRY_ISO,
+ Calls.CACHED_NUMBER_LABEL,
+ Calls.CACHED_PHOTO_ID,
+ Calls.GEOCODED_LOCATION,
+ Calls.CACHED_FORMATTED_NUMBER,
+ Calls.CACHED_NORMALIZED_NUMBER,
+ Calls.CACHED_LOOKUP_URI,
+ Calls.POST_DIAL_DIGITS,
+ Calls.CACHED_MATCHED_NUMBER,
+ Calls.CACHED_NUMBER_TYPE
+ )
- /** Filter out:
- * - Blocked calls
- * - Non-video Duo calls
- */
- private const val where = """
+ /** Filter out:
+ * - Blocked calls
+ * - Non-video Duo calls
+ */
+ private const val where = """
${Calls.TYPE} != ${Calls.BLOCKED_TYPE}
AND (
${Calls.PHONE_ACCOUNT_COMPONENT_NAME} IS NULL
@@ -48,52 +47,52 @@ abstract class CallsData {
)
"""
- fun getCursor(contentResolver: ContentResolver): Cursor? = contentResolver.query(
- URI
- .buildUpon()
- .appendQueryParameter(Calls.LIMIT_PARAM_KEY, LIMIT.toString())
- .build(),
- projection,
- where,
- null,
- Calls.DEFAULT_SORT_ORDER
- )
+ fun getCursor(contentResolver: ContentResolver): Cursor? = contentResolver.query(
+ URI
+ .buildUpon()
+ .appendQueryParameter(Calls.LIMIT_PARAM_KEY, LIMIT.toString())
+ .build(),
+ projection,
+ where,
+ null,
+ Calls.DEFAULT_SORT_ORDER
+ )
- fun getData(cursor: Cursor): List {
- val start = System.currentTimeMillis()
+ fun getData(cursor: Cursor): List {
+ val start = System.currentTimeMillis()
- val list = mutableListOf()
- if (cursor.moveToFirst()) {
- do {
- list.add(
- DialerCallEntity(
- id = cursor.getInt(cursor.getColumnIndexOrThrow(Calls._ID)),
- number = cursor.getString(cursor.getColumnIndexOrThrow(Calls.NUMBER)),
- name = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_NAME)),
- date = cursor.getLong(cursor.getColumnIndexOrThrow(Calls.DATE)),
- duration = cursor.getLong(cursor.getColumnIndexOrThrow(Calls.DURATION)),
- type = cursor.getInt(cursor.getColumnIndexOrThrow(Calls.TYPE)),
- isNew = cursor.getInt(cursor.getColumnIndexOrThrow(Calls.NEW)),
- photoUri = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_PHOTO_URI))
- ?.takeIf { it.isNotBlank() },
- countryIso = cursor.getString(cursor.getColumnIndexOrThrow(Calls.COUNTRY_ISO)),
- label = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_NUMBER_LABEL)),
- photoId = cursor.getLong(cursor.getColumnIndexOrThrow(Calls.CACHED_PHOTO_ID)),
- geoDescription = cursor.getString(cursor.getColumnIndexOrThrow(Calls.GEOCODED_LOCATION)),
- formattedNumber = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_FORMATTED_NUMBER)),
- normalizedNumber = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_NORMALIZED_NUMBER)),
- lookupUri = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_LOOKUP_URI)),
- postDialDigits = cursor.getString(cursor.getColumnIndexOrThrow(Calls.POST_DIAL_DIGITS)),
- matchedNumber = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_MATCHED_NUMBER)),
- numberType = cursor.getInt(cursor.getColumnIndexOrThrow(Calls.CACHED_NUMBER_TYPE))
- )
- )
- } while (cursor.moveToNext())
- }
+ val list = mutableListOf()
+ if (cursor.moveToFirst()) {
+ do {
+ list.add(
+ DialerCallEntity(
+ id = cursor.getInt(cursor.getColumnIndexOrThrow(Calls._ID)),
+ number = cursor.getString(cursor.getColumnIndexOrThrow(Calls.NUMBER)),
+ name = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_NAME)),
+ date = cursor.getLong(cursor.getColumnIndexOrThrow(Calls.DATE)),
+ duration = cursor.getLong(cursor.getColumnIndexOrThrow(Calls.DURATION)),
+ type = cursor.getInt(cursor.getColumnIndexOrThrow(Calls.TYPE)),
+ isNew = cursor.getInt(cursor.getColumnIndexOrThrow(Calls.NEW)),
+ photoUri = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_PHOTO_URI))
+ ?.takeIf { it.isNotBlank() },
+ countryIso = cursor.getString(cursor.getColumnIndexOrThrow(Calls.COUNTRY_ISO)),
+ label = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_NUMBER_LABEL)),
+ photoId = cursor.getLong(cursor.getColumnIndexOrThrow(Calls.CACHED_PHOTO_ID)),
+ geoDescription = cursor.getString(cursor.getColumnIndexOrThrow(Calls.GEOCODED_LOCATION)),
+ formattedNumber = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_FORMATTED_NUMBER)),
+ normalizedNumber = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_NORMALIZED_NUMBER)),
+ lookupUri = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_LOOKUP_URI)),
+ postDialDigits = cursor.getString(cursor.getColumnIndexOrThrow(Calls.POST_DIAL_DIGITS)),
+ matchedNumber = cursor.getString(cursor.getColumnIndexOrThrow(Calls.CACHED_MATCHED_NUMBER)),
+ numberType = cursor.getInt(cursor.getColumnIndexOrThrow(Calls.CACHED_NUMBER_TYPE))
+ )
+ )
+ } while (cursor.moveToNext())
+ }
- val time = (System.currentTimeMillis() - start) / 1000f
- Log.d(TAG, "Call log query time: $time seconds")
- return list
+ val time = (System.currentTimeMillis() - start) / 1000f
+ Log.d(TAG, "Call log query time: $time seconds")
+ return list
+ }
}
- }
}
\ No newline at end of file
diff --git a/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/CallsRepository.kt b/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/CallsRepository.kt
new file mode 100644
index 0000000..b40128e
--- /dev/null
+++ b/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/CallsRepository.kt
@@ -0,0 +1,19 @@
+package dev.alenajam.opendialer.data.calls
+
+import android.content.ContentResolver
+import dev.alenajam.opendialer.core.common.exception.Failure
+import dev.alenajam.opendialer.core.common.functional.Either
+import kotlinx.coroutines.flow.Flow
+
+interface CallsRepository {
+ fun getCalls(): Flow>
+ suspend fun getCallByIds(
+ contentResolver: ContentResolver,
+ ids: List
+ ): Either>
+
+ suspend fun getDetailOptions(call: DialerCall): Either>
+ suspend fun deleteCalls(calls: List): Either
+ suspend fun blockCaller(number: String): Either
+ suspend fun unblockCaller(number: String): Either
+}
\ No newline at end of file
diff --git a/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/CallsRepositoryImpl.kt b/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/CallsRepositoryImpl.kt
new file mode 100644
index 0000000..952f5c4
--- /dev/null
+++ b/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/CallsRepositoryImpl.kt
@@ -0,0 +1,130 @@
+package dev.alenajam.opendialer.data.calls
+
+import android.app.Application
+import android.content.ContentResolver
+import android.content.ContentValues
+import android.database.ContentObserver
+import android.net.Uri
+import android.provider.BlockedNumberContract
+import android.provider.CallLog
+import dev.alenajam.opendialer.core.common.DefaultPhoneUtils
+import dev.alenajam.opendialer.core.common.PermissionUtils
+import dev.alenajam.opendialer.core.common.exception.Failure
+import dev.alenajam.opendialer.core.common.functional.Either
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import javax.inject.Inject
+
+class CallsRepositoryImpl
+@Inject constructor(private val app: Application) : CallsRepository {
+ override fun getCalls(): Flow> =
+ callbackFlow {
+ val observer = object : ContentObserver(null) {
+ override fun onChange(selfChange: Boolean) {
+ CallsData.getCursor(app.contentResolver)?.let {
+ trySend(CallsData.getData(it))
+ it.close()
+ }
+ }
+ }
+
+ app.contentResolver.registerContentObserver(CallsData.URI, true, observer)
+
+ CallsData.getCursor(app.contentResolver)?.let {
+ trySend(CallsData.getData(it))
+ it.close()
+ }
+
+ awaitClose {
+ app.contentResolver.unregisterContentObserver(observer)
+ }
+ }
+
+ override suspend fun getCallByIds(
+ contentResolver: ContentResolver,
+ ids: List
+ ): Either> {
+ val cursor =
+ CallDetailData.getCursor(contentResolver, ids) ?: return Either.Left(Failure.NoData)
+ val data = CallDetailData.getData(cursor)
+
+ return if (data.isEmpty()) {
+ Either.Left(Failure.NoData)
+ } else {
+ Either.Right(data)
+ }
+ }
+
+ override suspend fun getDetailOptions(call: DialerCall): Either> {
+ return with(app) {
+ val options = mutableListOf()
+
+ if (!call.isAnonymous()) {
+ options.addAll(
+ listOf(
+ CallOption(CallOption.ID_COPY_NUMBER, 0),
+ CallOption(
+ CallOption.ID_EDIT_BEFORE_CALL,
+ 0
+ )
+ )
+ )
+ }
+
+ options.add(
+ CallOption(
+ CallOption.ID_DELETE,
+ 0
+ )
+ )
+
+ if (!call.isAnonymous()) {
+ val hasDefault = DefaultPhoneUtils.hasDefault(this)
+ val canUserBlockNumbers = BlockedNumberContract.canCurrentUserBlockNumbers(this)
+ if (hasDefault && canUserBlockNumbers) {
+ val isBlocked =
+ BlockedNumberContract.isBlocked(this, call.contactInfo.number)
+ val blockOption = CallOption(
+ if (isBlocked) CallOption.ID_UNBLOCK_CALLER else CallOption.ID_BLOCK_CALLER,
+ 0
+ )
+ options.add(blockOption)
+ }
+ }
+
+ Either.Right(options)
+ }
+ }
+
+ override suspend fun deleteCalls(calls: List): Either {
+ if (!PermissionUtils.hasRecentsPermission(app)) {
+ return Either.Left(Failure.NotPermitted)
+ }
+
+ var deleted = 0
+ calls.forEach {
+ deleted += app.contentResolver.delete(
+ CallLog.Calls.CONTENT_URI,
+ "${CallLog.Calls._ID} = ${it.id}",
+ null
+ )
+ }
+
+ return if (deleted > 0) Either.Right(Unit) else Either.Left(Failure.LocalFailure)
+ }
+
+ override suspend fun blockCaller(number: String): Either {
+ val values = ContentValues().apply {
+ put(BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER, number)
+ }
+ val uri: Uri? =
+ app.contentResolver.insert(BlockedNumberContract.BlockedNumbers.CONTENT_URI, values)
+ return if (uri == null) Either.Left(Failure.LocalFailure) else Either.Right(Unit)
+ }
+
+ override suspend fun unblockCaller(number: String): Either {
+ val blocked = BlockedNumberContract.unblock(app, number)
+ return if (blocked < 1) Either.Left(Failure.LocalFailure) else Either.Right(Unit)
+ }
+}
\ No newline at end of file
diff --git a/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/ContactInfo.kt b/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/ContactInfo.kt
index 9721368..d6b8ad9 100644
--- a/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/ContactInfo.kt
+++ b/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/ContactInfo.kt
@@ -4,76 +4,76 @@ import android.text.TextUtils
import java.io.Serializable
class ContactInfo(
- val name: String? = null,
- val number: String? = null,
- val photoUri: String? = null,
- var type: Int? = 0,
- val label: String? = null,
- val lookupUri: String? = null,
- val normalizedNumber: String? = null,
- val formattedNumber: String? = null,
- val geoDescription: String? = null,
- var photoId: Long? = 0
+ val name: String? = null,
+ val number: String? = null,
+ val photoUri: String? = null,
+ var type: Int? = 0,
+ val label: String? = null,
+ val lookupUri: String? = null,
+ val normalizedNumber: String? = null,
+ val formattedNumber: String? = null,
+ val geoDescription: String? = null,
+ var photoId: Long? = 0
) : Serializable {
- companion object {
- val EMPTY = ContactInfo()
- }
-
- override fun equals(other: Any?): Boolean {
- if (this === other) {
- return true
+ companion object {
+ val EMPTY = ContactInfo()
}
- if (other === null) {
- return false
- }
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
- if (other !is ContactInfo) {
- return false
- }
+ if (other === null) {
+ return false
+ }
- if (!TextUtils.equals(lookupUri, other.lookupUri)) {
- return false
- }
- if (!TextUtils.equals(name, other.name)) {
- return false
- }
- if (type != other.type) {
- return false
- }
- if (!TextUtils.equals(label, other.label)) {
- return false
- }
- if (!TextUtils.equals(number, other.number)) {
- return false
- }
- if (!TextUtils.equals(formattedNumber, other.formattedNumber)) {
- return false
- }
- if (!TextUtils.equals(normalizedNumber, other.normalizedNumber)) {
- return false
- }
- if (photoId != other.photoId) {
- return false
- }
- if (!TextUtils.equals(photoUri, other.photoUri)) {
- return false
- }
- if (!TextUtils.equals(geoDescription, other.geoDescription)) {
- return false
- }
+ if (other !is ContactInfo) {
+ return false
+ }
+
+ if (!TextUtils.equals(lookupUri, other.lookupUri)) {
+ return false
+ }
+ if (!TextUtils.equals(name, other.name)) {
+ return false
+ }
+ if (type != other.type) {
+ return false
+ }
+ if (!TextUtils.equals(label, other.label)) {
+ return false
+ }
+ if (!TextUtils.equals(number, other.number)) {
+ return false
+ }
+ if (!TextUtils.equals(formattedNumber, other.formattedNumber)) {
+ return false
+ }
+ if (!TextUtils.equals(normalizedNumber, other.normalizedNumber)) {
+ return false
+ }
+ if (photoId != other.photoId) {
+ return false
+ }
+ if (!TextUtils.equals(photoUri, other.photoUri)) {
+ return false
+ }
+ if (!TextUtils.equals(geoDescription, other.geoDescription)) {
+ return false
+ }
- return true
- }
+ return true
+ }
- override fun hashCode(): Int {
- // Uses only name and contactUri to determine hashcode.
- // This should be sufficient to have a reasonable distribution of hash codes.
- // Moreover, there should be no two people with the same lookupUri.
- val prime = 31
- var result = 1
- result = prime * result + (lookupUri?.hashCode() ?: 0)
- result = prime * result + (name?.hashCode() ?: 0)
- return result
- }
+ override fun hashCode(): Int {
+ // Uses only name and contactUri to determine hashcode.
+ // This should be sufficient to have a reasonable distribution of hash codes.
+ // Moreover, there should be no two people with the same lookupUri.
+ val prime = 31
+ var result = 1
+ result = prime * result + (lookupUri?.hashCode() ?: 0)
+ result = prime * result + (name?.hashCode() ?: 0)
+ return result
+ }
}
\ No newline at end of file
diff --git a/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/DetailCall.kt b/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/DetailCall.kt
index fba4281..c7da853 100644
--- a/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/DetailCall.kt
+++ b/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/DetailCall.kt
@@ -4,24 +4,24 @@ import java.io.Serializable
import java.util.Date
class DetailCall(
- val id: Int,
- val type: CallType,
- val date: Date,
- val duration: Long
+ val id: Int,
+ val type: CallType,
+ val date: Date,
+ val duration: Long
) : Serializable {
- companion object {
- fun map(call: DialerCallEntity): DetailCall? {
- val type = CallType.values().find { t -> t.value == call.type }
+ companion object {
+ fun map(call: DialerCallEntity): DetailCall? {
+ val type = CallType.values().find { t -> t.value == call.type }
- type?.let {
- return DetailCall(
- id = call.id,
- type = it,
- date = Date(call.date),
- duration = call.duration
- )
- }
- return null
+ type?.let {
+ return DetailCall(
+ id = call.id,
+ type = it,
+ date = Date(call.date),
+ duration = call.duration
+ )
+ }
+ return null
+ }
}
- }
}
\ No newline at end of file
diff --git a/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/DialerCall.kt b/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/DialerCall.kt
index b8c18c0..03bc916 100644
--- a/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/DialerCall.kt
+++ b/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/DialerCall.kt
@@ -6,109 +6,110 @@ import java.io.Serializable
import java.util.Date
@Keep
-class DialerCall(
- val id: Int,
- val number: String?,
- val date: Date,
- val type: CallType,
- val options: List,
- var childCalls: List,
- val countryIso: String? = null,
- val contactInfo: ContactInfo
+data class DialerCall(
+ val id: Int,
+ val number: String?,
+ val date: Date,
+ val type: CallType,
+ val options: List,
+ var childCalls: List,
+ val countryIso: String? = null,
+ val contactInfo: ContactInfo
) : Serializable {
- companion object {
- fun mapList(list: List): List {
- val calls = mutableListOf()
- list.forEach {
- if (
- // Never group anonymous calls
- !it.number.isNullOrBlank()
- && calls.isNotEmpty()
- ) {
- val last = calls.last()
- if (equalNumbers(last.contactInfo.number, it.number)) {
- DetailCall.map(it)?.let { call ->
- last.childCalls = last.childCalls.plus(call)
+ companion object {
+ fun mapList(list: List): List {
+ val calls = mutableListOf()
+ list.forEach {
+ if (
+ // Never group anonymous calls
+ !it.number.isNullOrBlank()
+ && calls.isNotEmpty()
+ ) {
+ val last = calls.last()
+ if (equalNumbers(last.contactInfo.number, it.number)) {
+ DetailCall.map(it)?.let { call ->
+ last.childCalls = last.childCalls.plus(call)
+ }
+ return@forEach
+ }
+ }
+
+ map(it)?.let { call -> calls.add(call) }
}
- return@forEach
- }
+ return calls
}
- map(it)?.let { call -> calls.add(call) }
- }
- return calls
- }
+ fun map(call: DialerCallEntity): DialerCall? {
+ val type = CallType.values().find { t -> t.value == call.type }
- fun map(call: DialerCallEntity): DialerCall? {
- val type = CallType.values().find { t -> t.value == call.type }
+ type?.let { t ->
+ val options = mutableListOf()
+ // Not anonymous
+ if (!call.number.isNullOrBlank()) {
+ // Not contact
+ if (call.name.isNullOrBlank()) {
+ options.addAll(
+ listOf(
+ CallOption(
+ CallOption.ID_CREATE_CONTACT,
+ 0
+ ),
+ CallOption(
+ CallOption.ID_ADD_EXISTING,
+ 0
+ )
+ )
+ )
+ }
- type?.let { t ->
- val options = mutableListOf()
- // Not anonymous
- if (!call.number.isNullOrBlank()) {
- // Not contact
- if (call.name.isNullOrBlank()) {
- options.addAll(
- listOf(
- CallOption(
- CallOption.ID_CREATE_CONTACT,
- 0
- ),
- CallOption(
- CallOption.ID_ADD_EXISTING,
- 0
- )
- )
- )
- }
-
- options.add(
- CallOption(
- CallOption.ID_SEND_MESSAGE,
- 0
- )
- )
- }
+ options.add(
+ CallOption(
+ CallOption.ID_SEND_MESSAGE,
+ 0
+ )
+ )
+ }
- // Always
- options.add(
- CallOption(
- CallOption.ID_CALL_DETAILS,
- 0
- )
- )
+ // Always
+ options.add(
+ CallOption(
+ CallOption.ID_CALL_DETAILS,
+ 0
+ )
+ )
- val contactNumber = call.matchedNumber ?: (call.number + call.postDialDigits)
+ val contactNumber = call.matchedNumber ?: (call.number + call.postDialDigits)
- return DialerCall(
- id = call.id,
- number = call.number,
- date = Date(call.date),
- type = t,
- options = options,
- childCalls = listOfNotNull(DetailCall.map(call)),
- countryIso = call.countryIso,
- contactInfo = ContactInfo(
- name = call.name,
- number = contactNumber,
- photoUri = call.photoUri,
- type = call.numberType,
- label = call.label,
- lookupUri = call.lookupUri,
- normalizedNumber = call.normalizedNumber,
- formattedNumber = call.formattedNumber,
- geoDescription = call.geoDescription,
- photoId = call.photoId
- )
- )
- }
- return null
+ return DialerCall(
+ id = call.id,
+ number = call.number,
+ date = Date(call.date),
+ type = t,
+ options = options,
+ childCalls = listOfNotNull(DetailCall.map(call)),
+ countryIso = call.countryIso,
+ contactInfo = ContactInfo(
+ name = call.name,
+ number = contactNumber,
+ photoUri = call.photoUri,
+ type = call.numberType,
+ label = call.label,
+ lookupUri = call.lookupUri,
+ normalizedNumber = call.normalizedNumber,
+ formattedNumber = call.formattedNumber,
+ geoDescription = call.geoDescription,
+ photoId = call.photoId
+ )
+ )
+ }
+ return null
+ }
}
- }
- fun isAnonymous(): Boolean = contactInfo.number.isNullOrBlank()
+ fun isAnonymous(): Boolean = contactInfo.number.isNullOrBlank()
+ fun isContactSaved(): Boolean = !contactInfo.name.isNullOrBlank()
}
fun equalNumbers(number1: String?, number2: String?): Boolean {
- return PhoneNumberUtils.compare(number1, number2)
+ return PhoneNumberUtils.compare(number1, number2)
}
\ No newline at end of file
diff --git a/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/DialerCallEntity.kt b/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/DialerCallEntity.kt
index 5b97f52..b7807e3 100644
--- a/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/DialerCallEntity.kt
+++ b/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/DialerCallEntity.kt
@@ -1,22 +1,22 @@
package dev.alenajam.opendialer.data.calls
class DialerCallEntity(
- val id: Int,
- val number: String?,
- val name: String?,
- val date: Long,
- val duration: Long,
- val type: Int,
- val isNew: Int,
- val photoUri: String?,
- val countryIso: String? = null,
- val label: String?,
- val lookupUri: String?,
- val normalizedNumber: String? = null,
- val formattedNumber: String? = null,
- val geoDescription: String? = null,
- val photoId: Long? = null,
- val postDialDigits: String? = null,
- val matchedNumber: String? = null,
- val numberType: Int? = null
+ val id: Int,
+ val number: String?,
+ val name: String?,
+ val date: Long,
+ val duration: Long,
+ val type: Int,
+ val isNew: Int,
+ val photoUri: String?,
+ val countryIso: String? = null,
+ val label: String?,
+ val lookupUri: String?,
+ val normalizedNumber: String? = null,
+ val formattedNumber: String? = null,
+ val geoDescription: String? = null,
+ val photoId: Long? = null,
+ val postDialDigits: String? = null,
+ val matchedNumber: String? = null,
+ val numberType: Int? = null
)
\ No newline at end of file
diff --git a/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/DialerRepository.kt b/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/DialerRepository.kt
deleted file mode 100644
index cb383ab..0000000
--- a/data/calls/src/main/java/dev/alenajam/opendialer/data/calls/DialerRepository.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package dev.alenajam.opendialer.data.calls
-
-import android.content.ContentResolver
-import dev.alenajam.opendialer.core.common.exception.Failure
-import dev.alenajam.opendialer.core.common.functional.Either
-import kotlinx.coroutines.flow.Flow
-
-interface DialerRepository {
- fun getCalls(contentResolver: ContentResolver): Flow>
- suspend fun getCallByIds(contentResolver: ContentResolver, ids: List): Either>
- suspend fun getDetailOptions(call: DialerCall): Either>
- suspend fun deleteCalls(calls: List