diff --git a/build.gradle b/build.gradle
index 5e5be8917..3230ef1b4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -156,8 +156,8 @@ android {
applicationId 'io.forsta.relay'
minSdkVersion 14
targetSdkVersion 22
- versionName "0.1.78"
- versionCode 178
+ versionName "0.1.80"
+ versionCode 180
multiDexEnabled true
buildConfigField "long", "BUILD_TIMESTAMP", getLastCommitTimestamp() + "L"
resValue "string", "forsta_authorities", applicationId + '.provider.ccsm'
diff --git a/res/drawable-mdpi/ic_forsta_pin_dark.png b/res/drawable-mdpi/ic_forsta_pin_dark.png
new file mode 100644
index 000000000..3b85892ce
Binary files /dev/null and b/res/drawable-mdpi/ic_forsta_pin_dark.png differ
diff --git a/res/layout/activity_dashboard.xml b/res/layout/activity_dashboard.xml
index a27fa3c92..bb042e901 100644
--- a/res/layout/activity_dashboard.xml
+++ b/res/layout/activity_dashboard.xml
@@ -23,7 +23,8 @@
+ android:layout_height="wrap_content" android:orientation="horizontal"
+ android:visibility="gone">
-
-
-
+
+
\ No newline at end of file
diff --git a/res/layout/contact_selection_list_fragment.xml b/res/layout/contact_selection_list_fragment.xml
index 54aac9e2f..5435976af 100644
--- a/res/layout/contact_selection_list_fragment.xml
+++ b/res/layout/contact_selection_list_fragment.xml
@@ -3,14 +3,26 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
-
+
+
+
+
+
-
-
-
+ android:orientation="vertical"
+ android:layout_above="@id/layout_container"/>
-
-
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index e6446c9a7..147d9c9b0 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -71,6 +71,7 @@
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 307c0ac15..e84b504c3 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -646,6 +646,7 @@
Enter a tag or tag expression
Create Conversation
No results.
+ To find a specific user, enter a fully qualified identifier (john.doe:forsta), valid phone number or email address.
No contacts.
@@ -834,8 +835,10 @@
Conversation Title
Recipients
+ Pinned
Mute conversation
Disable notifications for this conversation
+ Pin this conversation
Notification sound
Vibrate
Block
diff --git a/res/values/themes.xml b/res/values/themes.xml
index 7d9e3cbcf..01d24a4a6 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -158,6 +158,7 @@
- @drawable/ic_info_outline_light
- @drawable/ic_warning_light
+ - @drawable/ic_forsta_pin_dark
- @color/device_link_item_background_light
@@ -241,6 +242,7 @@
- @drawable/ic_info_outline_dark
- @drawable/ic_warning_dark
+ - @drawable/ic_forsta_pin_light
- @color/device_link_item_background_dark
diff --git a/res/xml/preferences_advanced.xml b/res/xml/preferences_advanced.xml
index 87a10b481..7de83c2cc 100644
--- a/res/xml/preferences_advanced.xml
+++ b/res/xml/preferences_advanced.xml
@@ -3,12 +3,6 @@
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
+
-
-
-
-
-
+
+
diff --git a/res/xml/thread_preferences.xml b/res/xml/thread_preferences.xml
index f9db204e6..9000e68a1 100644
--- a/res/xml/thread_preferences.xml
+++ b/res/xml/thread_preferences.xml
@@ -1,6 +1,13 @@
+
2) {
- new AsyncTask() {
- @Override
- protected Void doInBackground(String[] objects) {
- JSONObject response = CcsmApi.searchUserDirectory(DashboardActivity.this, objects[0]);
- List users = CcsmApi.parseUsers(DashboardActivity.this, response);
- Log.w(TAG, "Getting response");
- return null;
- }
- }.execute(charSequence.toString());
- }
- }
-
- @Override
- public void afterTextChanged(Editable editable) {
-
- }
- });
printLoginInformation();
}
@@ -718,10 +691,10 @@ protected JSONObject doInBackground(Void... voids) {
@Override
protected void onPostExecute(JSONObject jsonObject) {
- List groups = CcsmApi.parseTagGroups(jsonObject);
+ List groups = CcsmApi.parseTagGroups(jsonObject);
StringBuilder sb = new StringBuilder();
- for (ForstaGroup group : groups) {
+ for (ForstaTag group : groups) {
String groupId = group.id;
sb.append(groupId).append("\n");
String encoded = GroupUtil.getEncodedId(groupId.getBytes());
@@ -769,7 +742,7 @@ private class GetDirectory extends AsyncTask {
@Override
protected JSONObject doInBackground(Void... voids) {
- return CcsmApi.getUserDirectory(DashboardActivity.this, null);
+ return CcsmApi.getUserDirectory(DashboardActivity.this, new ArrayList());
}
@Override
diff --git a/src/io/forsta/ccsm/ThreadPreferenceActivity.java b/src/io/forsta/ccsm/ThreadPreferenceActivity.java
index f2822484e..1111a0e14 100644
--- a/src/io/forsta/ccsm/ThreadPreferenceActivity.java
+++ b/src/io/forsta/ccsm/ThreadPreferenceActivity.java
@@ -20,11 +20,14 @@
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
+import android.widget.ToggleButton;
import java.util.ArrayList;
import java.util.LinkedList;
@@ -63,6 +66,7 @@ public class ThreadPreferenceActivity extends PassphraseRequiredActionBarActivit
public static final String TAG = ThreadPreferenceActivity.class.getSimpleName();
public static final String THREAD_ID_EXTRA = "thread_id";
+ private static final String PREFERENCE_PINNED = "pref_key_thread_pinned";
private static final String PREFERENCE_MUTED = "pref_key_thread_mute";
private static final String PREFERENCE_TONE = "pref_key_thread_ringtone";
private static final String PREFERENCE_VIBRATE = "pref_key_thread_vibrate";
@@ -74,6 +78,7 @@ public class ThreadPreferenceActivity extends PassphraseRequiredActionBarActivit
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
private long threadId;
+ private ForstaThread threadDetail;
private AvatarImageView avatar;
private Toolbar toolbar;
private TextView title;
@@ -151,10 +156,10 @@ private void initializeToolbar() {
private void initializeThread() {
recipients = DatabaseFactory.getThreadDatabase(ThreadPreferenceActivity.this).getRecipientsForThreadId(threadId);
- threadRecipients.setText(getRecipientData(recipients));
- final ForstaThread thread = DatabaseFactory.getThreadDatabase(ThreadPreferenceActivity.this).getForstaThread(threadId);
- if (!TextUtils.isEmpty(thread.getTitle())) {
- forstaTitle.setText(thread.getTitle());
+ threadDetail = DatabaseFactory.getThreadDatabase(ThreadPreferenceActivity.this).getForstaThread(threadId);
+ threadRecipients.setText(threadDetail.getPrettyExpression());
+ if (!TextUtils.isEmpty(threadDetail.getTitle())) {
+ forstaTitle.setText(threadDetail.getTitle());
} else {
forstaTitle.setHint("Add a Title");
}
@@ -166,7 +171,7 @@ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
- String saved = thread.getTitle() != null ? thread.getTitle() : "";
+ String saved = threadDetail.getTitle() != null ? threadDetail.getTitle() : "";
if (!saved.equals(s.toString())) {
forstaSaveTitle.setVisibility(View.VISIBLE);
} else {
@@ -179,11 +184,11 @@ public void afterTextChanged(Editable s) {
}
});
- title.setText(TextUtils.isEmpty(thread.title) ? recipients.toShortString() : thread.title);
+ title.setText(TextUtils.isEmpty(threadDetail.title) ? recipients.toShortString() : threadDetail.title);
if (BuildConfig.FLAVOR.equals("dev")) {
- forstaUid.setText(thread.uid);
- forstaExpression.setText(thread.distribution);
+ forstaUid.setText(threadDetail.uid);
+ forstaExpression.setText(threadDetail.distribution);
LinearLayout debugLayout = (LinearLayout) findViewById(R.id.forsta_thread_debug_details);
debugLayout.setVisibility(View.VISIBLE);
}
@@ -221,6 +226,7 @@ protected Void doInBackground(Void... voids) {
public static class ThreadPreferenceFragment extends PreferenceFragment {
private MasterSecret masterSecret;
private long threadId;
+ private CheckBoxPreference pinnedPreference;
private CheckBoxPreference mutePreference;
private AdvancedRingtonePreference notificationPreference;
private ListPreference vibratePreference;
@@ -237,6 +243,7 @@ public void onCreate(Bundle icicle) {
addPreferencesFromResource(R.xml.thread_preferences);
initializePreferences();
+ this.findPreference(PREFERENCE_PINNED).setOnPreferenceChangeListener(new PinnedChangedListener());
this.findPreference(PREFERENCE_MUTED)
.setOnPreferenceClickListener(new MuteClickListener());
this.findPreference(PREFERENCE_COLOR)
@@ -253,16 +260,20 @@ public void onCreate(Bundle icicle) {
private void initializePreferences() {
ThreadPreferenceDatabase db = DatabaseFactory.getThreadPreferenceDatabase(getActivity());
ThreadPreferenceDatabase.ThreadPreference threadPreference = db.getThreadPreferences(threadId);
+ ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(getActivity());
+ ForstaThread forstaThead = threadDatabase.getForstaThread(threadId);
+ pinnedPreference = (CheckBoxPreference) this.findPreference(PREFERENCE_PINNED);
mutePreference = (CheckBoxPreference) this.findPreference(PREFERENCE_MUTED);
colorPreference = (ColorPreference) this.findPreference(PREFERENCE_COLOR);
+
colorPreference.setChoices(MaterialColors.CONVERSATION_PALETTE.asConversationColorArray(getActivity()));
+ colorPreference.setValue(threadPreference.getColor().toConversationColor(getActivity()));
+ pinnedPreference.setChecked(forstaThead.isPinned());
mutePreference.setChecked(threadPreference.isMuted());
if (threadPreference.getColor() == null) {
db.setColor(threadId, MaterialColors.getRandomConversationColor());
}
- colorPreference.setValue(threadPreference.getColor().toConversationColor(getActivity()));
-
// notificationPreference = (AdvancedRingtonePreference) this.findPreference(PREFERENCE_TONE);
// notificationPreference.setSummary(R.string.preferences__default);
// vibratePreference = (ListPreference) this.findPreference(PREFERENCE_VIBRATE);
@@ -323,6 +334,21 @@ public boolean onPreferenceChange(Preference preference, Object o) {
}
}
+ private class PinnedChangedListener implements Preference.OnPreferenceChangeListener {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final boolean pinned = (boolean) newValue;
+ new AsyncTask() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ DatabaseFactory.getThreadDatabase(getActivity()).updatePinned(threadId, pinned);
+ return null;
+ }
+ }.execute();
+ return true;
+ }
+ }
+
private class MuteClickListener implements Preference.OnPreferenceClickListener {
@Override
@@ -353,10 +379,6 @@ protected Void doInBackground(Long... params) {
}
});
}
-
-
-
-
return false;
}
}
diff --git a/src/io/forsta/ccsm/api/CcsmApi.java b/src/io/forsta/ccsm/api/CcsmApi.java
index 139c3ebe1..cdd3f964f 100644
--- a/src/io/forsta/ccsm/api/CcsmApi.java
+++ b/src/io/forsta/ccsm/api/CcsmApi.java
@@ -18,8 +18,10 @@
import io.forsta.ccsm.api.model.ForstaDistribution;
import io.forsta.ccsm.database.ContactDb;
import io.forsta.ccsm.database.DbFactory;
-import io.forsta.ccsm.database.model.ForstaGroup;
+import io.forsta.ccsm.database.model.ForstaOrg;
+import io.forsta.ccsm.database.model.ForstaTag;
import io.forsta.ccsm.database.model.ForstaUser;
+import io.forsta.ccsm.util.InvalidUserException;
import io.forsta.securesms.BuildConfig;
import java.io.UnsupportedEncodingException;
@@ -32,9 +34,12 @@
import io.forsta.ccsm.ForstaPreferences;
import io.forsta.securesms.contacts.ContactsDatabase;
+import io.forsta.securesms.database.CanonicalAddressDatabase;
import io.forsta.securesms.database.DatabaseFactory;
import io.forsta.securesms.database.GroupDatabase;
import io.forsta.securesms.database.TextSecureDirectory;
+import io.forsta.securesms.recipients.Recipient;
+import io.forsta.securesms.recipients.Recipients;
import io.forsta.securesms.util.DirectoryHelper;
import io.forsta.securesms.util.TextSecurePreferences;
import io.forsta.securesms.util.Util;
@@ -64,6 +69,16 @@ public class CcsmApi {
private CcsmApi() {
}
+ public static JSONObject provisionAccount(Context context, JSONObject obj) throws Exception {
+ return hardFetchResource(context, "PUT", API_PROVISION_PROXY, obj);
+ }
+
+ public static JSONObject createAccount(JSONObject jsonObject) {
+ String host = BuildConfig.FORSTA_API_URL;
+ String serviceToken = BuildConfig.FORSTA_PROVISION_SERVICE_TOKEN;
+ return NetworkUtils.apiFetchWithServiceToken("POST", serviceToken, host + API_USER + "?login=true", jsonObject);
+ }
+
public static JSONObject forstaLogin(Context context, String authToken) {
String host = BuildConfig.FORSTA_API_URL;
JSONObject result = new JSONObject();
@@ -98,22 +113,54 @@ public static JSONObject forstaSendToken(Context context, String org, String use
return result;
}
- public static void syncForstaContacts(Context context) {
- JSONObject response = getOrgUsers(context);
- if (isUnauthorizedResponse(response)) {
- return;
+ public static boolean tokenNeedsRefresh(Context context) {
+ Date expireDate = ForstaPreferences.getTokenExpireDate(context);
+ if (expireDate == null) {
+ return false;
}
- List forstaContacts = parseUsers(context, response);
- List filteredContacts = new ArrayList<>();
+ Date current = new Date();
+ long expiresIn = (expireDate.getTime() - current.getTime()) / (1000 * 60 * 60 * 24);
+ long expireDelta = EXPIRE_REFRESH_DELTA;
+ boolean expired = expiresIn < expireDelta;
+ Log.d(TAG, "Token expires in: " + expiresIn);
+ return expired;
+ }
+ public static JSONObject forstaRefreshToken(Context context) {
+ JSONObject result = new JSONObject();
+ try {
+ JSONObject obj = new JSONObject();
+ obj.put("token", ForstaPreferences.getRegisteredKey(context));
+ result = fetchResource(context, "POST", API_TOKEN_REFRESH, obj);
+ if (result.has("token")) {
+ Log.w(TAG, "Token refresh. New token issued.");
+ String token = result.getString("token");
+ ForstaPreferences.setRegisteredForsta(context, token);
+ } else {
+ Log.w(TAG, "Token refresh failed.");
+ ForstaPreferences.setRegisteredForsta(context, "");
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ Log.e(TAG, "forstaRefreshToken failed");
+ }
+ return result;
+ }
+
+ public static void syncForstaContacts(Context context) {
Set systemNumbers = new HashSet<>();
Cursor cursor = null;
try {
cursor = DatabaseFactory.getContactsDatabase(context).querySystemContacts("");
while (cursor != null && cursor.moveToNext()) {
String number = cursor.getString(cursor.getColumnIndex(ContactsDatabase.NUMBER_COLUMN));
- systemNumbers.add(number);
+ try {
+ number = Util.canonicalizeNumberE164(number);
+ systemNumbers.add(number);
+ } catch (InvalidNumberException e) {
+ e.printStackTrace();
+ }
}
} finally {
if (cursor != null) {
@@ -121,74 +168,111 @@ public static void syncForstaContacts(Context context) {
}
}
- for (int i=0; i< forstaContacts.size(); i++) {
- if (systemNumbers.contains(forstaContacts.get(i).getPhone())) {
- // forstaContacts.remove(i);
- // For debugging, use another filtered list.
- // This would restrict full directory sync to only people in local contacts db.
- filteredContacts.add(forstaContacts.get(i));
+ try {
+ List allContacts = new ArrayList<>();
+ if (systemNumbers.size() > 0) {
+ // This is not returning anything.
+ allContacts = getForstaUsersByPhone(context, systemNumbers);
+ }
+ // Sync contacts in existing threads.
+ List addresses = DatabaseFactory.getThreadDatabase(context).getAllRecipients();
+ if (addresses.size() > 0) {
+ JSONObject threadUsers = getUserDirectory(context, addresses);
+ List threadContacts = CcsmApi.parseUsers(context, threadUsers);
+ for (ForstaUser user : threadContacts) {
+ if (!allContacts.contains(user)) {
+ allContacts.add(user);
+ }
+ }
}
- }
- List dbContacts = DbFactory.getContactDb(context).getUsers();
- for (ForstaUser user : dbContacts) {
- if (!forstaContacts.contains(user)) {
- forstaContacts.add(user);
+ ForstaOrg org = ForstaOrg.getLocalForstaOrg(context);
+ if (!(org.getSlug().equals("public") || org.getSlug().equals("forsta"))) {
+ JSONObject orgUsers = getOrgUsers(context);
+ List orgContacts = parseUsers(context, orgUsers);
+ for (ForstaUser user : orgContacts) {
+ if (!allContacts.contains(user)) {
+ allContacts.add(user);
+ }
+ }
+ syncForstaContactsDb(context, allContacts, false);
+ CcsmApi.syncOrgTags(context);
+ } else {
+ syncForstaContactsDb(context, allContacts, true);
}
+ ForstaPreferences.setForstaContactSync(context, new Date().getTime());
+ } catch (Exception e) {
+ e.printStackTrace();
}
-
- syncForstaContactsDb(context, forstaContacts, false);
- CcsmApi.syncForstaGroups(context);
- ForstaPreferences.setForstaContactSync(context, new Date().getTime());
}
- public static void syncForstaContacts(Context context, String ids) {
- JSONObject response = getUserDirectory(context, ids);
- if (isUnauthorizedResponse(response)) {
- return;
- }
+ public static void syncForstaContacts(Context context, List addresses) {
+ JSONObject response = getUserDirectory(context, addresses);
List forstaContacts = parseUsers(context, response);
syncForstaContactsDb(context, forstaContacts, false);
}
- public static JSONObject getUserDirectory(Context context, String ids) {
- String query = "";
- if (!TextUtils.isEmpty(ids)) {
- query = "?id_in=" + ids;
- }
+ private static void syncForstaContactsDb(Context context, List contacts, boolean removeExisting) {
+ DbFactory.getContactDb(context).updateUsers(contacts, removeExisting);
+ }
+
+ private static void syncOrgTags(Context context) {
+ JSONObject response = getTags(context);
+ List groups = parseTagGroups(response);
+ GroupDatabase db = DatabaseFactory.getGroupDatabase(context);
+ TextSecureDirectory dir = TextSecureDirectory.getInstance(context);
+ List activeNumbers = dir.getActiveNumbers();
+ db.updateGroups(groups, activeNumbers);
+ }
+
+ private static JSONObject getUsersByPhone(Context context, Set phoneNumbers) {
+ String query = "?phone_in=" + TextUtils.join(",", phoneNumbers);
return fetchResource(context, "GET", API_DIRECTORY_USER + query);
}
- public static JSONObject getDomainDirectory(Context context) {
- return fetchResource(context, "GET", API_DIRECTORY_DOMAIN);
+ private static JSONObject getUsersByEmail(Context context, Set emailAddresses) {
+ String query = "?email_in=" + TextUtils.join(",", emailAddresses);
+ return fetchResource(context, "GET", API_DIRECTORY_USER + query);
}
- public static JSONObject provisionAccount(Context context, JSONObject obj) throws Exception {
- return hardFetchResource(context, "PUT", API_PROVISION_PROXY, obj);
+ public static JSONObject getUserDirectory(Context context, List addresses) {
+ String addressesString = TextUtils.join(",", addresses);
+ String query = "";
+ if (!TextUtils.isEmpty(addressesString)) {
+ query = "?id_in=" + addressesString;
+ }
+ return fetchResource(context, "GET", API_DIRECTORY_USER + query);
}
- public static JSONObject createAccount(JSONObject jsonObject) {
- String host = BuildConfig.FORSTA_API_URL;
- String serviceToken = BuildConfig.FORSTA_PROVISION_SERVICE_TOKEN;
- return NetworkUtils.apiFetchWithServiceToken("POST", serviceToken, host + API_USER + "?login=true", jsonObject);
+ public static JSONObject getOrgDirectory(Context context) {
+ return fetchResource(context, "GET", API_DIRECTORY_DOMAIN);
}
- public static JSONObject getForstaUser(Context context) {
- return fetchResource(context, "GET", API_USER + TextSecurePreferences.getLocalNumber(context) + "/");
+ public static JSONObject getOrg(Context context) {
+ ForstaUser localAccount = ForstaUser.getLocalForstaUser(context);
+ return getOrg(context, localAccount.org_id);
}
- public static JSONObject getUserPick(Context context) {
- return fetchResource(context, "GET", API_USER_PICK);
+ public static JSONObject getOrg(Context context, String id) {
+ return fetchResource(context, "GET", API_ORG + id + "/");
}
public static JSONObject getOrgUsers(Context context) {
return fetchResource(context, "GET", API_USER);
}
+ public static JSONObject getForstaUser(Context context) {
+ return fetchResource(context, "GET", API_USER + TextSecurePreferences.getLocalNumber(context) + "/");
+ }
+
public static JSONObject getTags(Context context) {
return fetchResource(context, "GET", API_TAG);
}
+ public static JSONObject getUserPick(Context context) {
+ return fetchResource(context, "GET", API_USER_PICK);
+ }
+
public static JSONObject getTagPick(Context context) {
return fetchResource(context, "GET", API_TAG_PICK);
}
@@ -208,13 +292,17 @@ public static JSONObject getDistribution(Context context, String expression) {
return response;
}
- public static JSONObject getOrg(Context context) {
- ForstaUser localAccount = ForstaUser.getLocalForstaUser(context);
- return getOrg(context, localAccount.org_id);
- }
-
- public static JSONObject getOrg(Context context, String id) {
- return fetchResource(context, "GET", API_ORG + id + "/");
+ public static JSONObject searchUserDirectory(Context context, String searchText) {
+ JSONObject response = new JSONObject();
+ try {
+ String urlEncoded = TextUtils.isEmpty(searchText) ? "" : URLEncoder.encode(searchText, "UTF-8");
+ response = fetchResource(context, "GET", API_DIRECTORY_USER + "?q=" + urlEncoded);
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return response;
}
private static JSONObject fetchResource(Context context, String method, String urn) {
@@ -233,6 +321,7 @@ private static JSONObject hardFetchResource(Context context, String method, Stri
return NetworkUtils.apiHardFetch(method, authKey, baseUrl + urn, body);
}
+ // Helper methods and mapper functions. Move these.
public static String parseLoginToken(String authtoken) {
if (authtoken.contains("/")) {
String[] parts = authtoken.split("/");
@@ -246,19 +335,22 @@ public static List parseUsers(Context context, JSONObject jsonObject
try {
JSONArray results = jsonObject.getJSONArray("results");
for (int i = 0; i < results.length(); i++) {
- JSONObject user = results.getJSONObject(i);
- ForstaUser forstaUser = new ForstaUser(user);
- users.add(forstaUser);
+ try {
+ JSONObject user = results.getJSONObject(i);
+ ForstaUser forstaUser = new ForstaUser(user);
+ users.add(forstaUser);
+ } catch (Exception e) {
+ Log.e(TAG, "parseUsers exception: " + e.getMessage());
+ }
}
- } catch (Exception e) {
- Log.e(TAG, "parseUsers exception: " + e.getMessage());
- e.printStackTrace();
+ } catch (JSONException e) {
+ Log.e(TAG, "No results array.");
}
return users;
}
- public static List parseTagGroups(JSONObject jsonObject) {
- List groups = new ArrayList<>();
+ public static List parseTagGroups(JSONObject jsonObject) {
+ List groups = new ArrayList<>();
try {
JSONArray results = jsonObject.getJSONArray("results");
@@ -280,7 +372,7 @@ public static List parseTagGroups(JSONObject jsonObject) {
}
}
if (isGroup) {
- ForstaGroup group = new ForstaGroup(result);
+ ForstaTag group = new ForstaTag(result);
group.addMembers(members);
groups.add(group);
}
@@ -293,22 +385,18 @@ public static List parseTagGroups(JSONObject jsonObject) {
}
public static ForstaDistribution getMessageDistribution(Context context, String expression) {
- JSONObject response = CcsmApi.getDistribution(context, expression);
+ JSONObject response = getDistribution(context, expression);
return ForstaDistribution.fromJson(response);
}
- private static void syncForstaContactsDb(Context context, List contacts, boolean removeExisting) {
- ContactDb forstaDb = DbFactory.getContactDb(context);
- forstaDb.updateUsers(contacts, removeExisting);
+ public static List getForstaUsersByPhone(Context context, Set phoneNumbers) {
+ JSONObject jsonObject = getUsersByPhone(context, phoneNumbers);
+ return parseUsers(context, jsonObject);
}
- private static void syncForstaGroups(Context context) {
- JSONObject response = getTags(context);
- List groups = parseTagGroups(response);
- GroupDatabase db = DatabaseFactory.getGroupDatabase(context);
- TextSecureDirectory dir = TextSecureDirectory.getInstance(context);
- List activeNumbers = dir.getActiveNumbers();
- db.updateGroups(groups, activeNumbers);
+ public static List getForstaUsersByEmail(Context context, Set emailAddresses) {
+ JSONObject jsonObject = getUsersByEmail(context, emailAddresses);
+ return parseUsers(context, jsonObject);
}
private static boolean isUnauthorizedResponse(JSONObject response) {
@@ -330,54 +418,6 @@ private static boolean isUnauthorizedResponse(JSONObject response) {
return false;
}
- public static boolean tokenNeedsRefresh(Context context) {
- Date expireDate = ForstaPreferences.getTokenExpireDate(context);
- if (expireDate == null) {
- return false;
- }
- Date current = new Date();
- long expiresIn = (expireDate.getTime() - current.getTime()) / (1000 * 60 * 60 * 24);
- long expireDelta = EXPIRE_REFRESH_DELTA;
- boolean expired = expiresIn < expireDelta;
-
- Log.d(TAG, "Token expires in: " + expiresIn);
- return expired;
- }
-
- public static JSONObject forstaRefreshToken(Context context) {
- JSONObject result = new JSONObject();
- try {
- JSONObject obj = new JSONObject();
- obj.put("token", ForstaPreferences.getRegisteredKey(context));
- result = fetchResource(context, "POST", API_TOKEN_REFRESH, obj);
- if (result.has("token")) {
- Log.w(TAG, "Token refresh. New token issued.");
- String token = result.getString("token");
- ForstaPreferences.setRegisteredForsta(context, token);
- } else {
- Log.w(TAG, "Token refresh failed.");
- ForstaPreferences.setRegisteredForsta(context, "");
- }
- } catch (Exception e) {
- e.printStackTrace();
- Log.e(TAG, "forstaRefreshToken failed");
- }
- return result;
- }
-
- public static JSONObject searchUserDirectory(Context context, String searchText) {
- JSONObject response = new JSONObject();
- try {
- String urlEncoded = TextUtils.isEmpty(searchText) ? "" : URLEncoder.encode(searchText, "UTF-8");
- response = fetchResource(context, "GET", API_DIRECTORY_USER + "?q=" + urlEncoded);
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- } catch (Exception e) {
- e.printStackTrace();
- }
- return response;
- }
-
// XXX obsolete XXX
private static Set getSystemContacts(Context context) {
Set results = new HashSet<>();
diff --git a/src/io/forsta/ccsm/database/ContactDb.java b/src/io/forsta/ccsm/database/ContactDb.java
index 740b1b424..cde68357d 100644
--- a/src/io/forsta/ccsm/database/ContactDb.java
+++ b/src/io/forsta/ccsm/database/ContactDb.java
@@ -46,6 +46,7 @@ public class ContactDb extends DbBase {
public static final String TSREGISTERED = "tsregistered";
public static final String ISACTIVE = "isactive";
public static final String ISMONITOR = "ismonitor";
+ public static final String USERTYPE = "type";
public static final String CREATE_TABLE = "create table " +
TABLE_NAME + "(" +
@@ -64,6 +65,7 @@ public class ContactDb extends DbBase {
TSREGISTERED + " integer default 0, " +
ISACTIVE + " integer default 0, " +
ISMONITOR + " integer default 0, " +
+ USERTYPE + ", " +
"CONSTRAINT item_number_unique UNIQUE (" + UID + ")" +
")";
@@ -82,7 +84,8 @@ public class ContactDb extends DbBase {
DATE,
TSREGISTERED,
ISACTIVE,
- ISMONITOR
+ ISMONITOR,
+ USERTYPE
};
public ContactDb(Context context, DbHelper dbHelper) {
@@ -179,6 +182,21 @@ public Set getAddresses() {
return addresses;
}
+ public Set getOtherAddresses(Set apiAddresses) {
+ Set addresses = new HashSet<>();
+ String stringAddresses = TextUtils.join(",", apiAddresses);
+ try {
+ Cursor c = getRecords(TABLE_NAME, allColumns, UID + " not in (?)", new String[] { }, UID);
+ while (c.moveToNext()) {
+ addresses.add(c.getString(c.getColumnIndex(UID)));
+ }
+ c.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return addresses;
+ }
+
public Map getUids() {
Map ids = new HashMap<>();
try {
@@ -245,6 +263,7 @@ public void updateUsers(List users, boolean removeExisting) {
values.put(ContactDb.TSREGISTERED, user.tsRegistered);
values.put(ContactDb.ISACTIVE, user.isActive);
values.put(ContactDb.ISMONITOR, user.isMonitor);
+ values.put(ContactDb.USERTYPE, user.type.toString());
if (uids.containsKey(user.uid)) {
String id = uids.get(user.getUid());
if (TextUtils.isEmpty(user.getUid())) {
@@ -319,11 +338,21 @@ public void setActiveForstaAddresses(List activeTokens) {
}
public Cursor getActiveRecipients(String filter) {
- String queryFilter = "(" + TSREGISTERED + " = 1 AND " + ISACTIVE + " = 1 AND " + ISMONITOR + " = 0)";
+ String queryFilter = "(" + TSREGISTERED + " = 1 AND " + ISACTIVE + " = 1 AND " + ISMONITOR + " = 0 AND " + USERTYPE + " = 'PERSON')";
+
String[] queryValues = null;
if (filter != null && filter.length() > 0) {
+ String user = filter;
+ String org = filter;
+ String[] parts = filter.split(":");
+ if (parts.length > 0) {
+ user = parts[0];
+ if (parts.length > 1) {
+ org = parts[1];
+ }
+ }
queryFilter += " AND (" + NAME + " LIKE ? OR " + SLUG + " LIKE ? OR " + ORGSLUG + " LIKE ?)";
- queryValues = new String[] { "%" + filter + "%", "%" + filter + "%", "%" + filter + "%"};
+ queryValues = new String[] { "%" + user + "%", "%" + user + "%", "%" + org + "%"};
}
try {
diff --git a/src/io/forsta/ccsm/database/DbHelper.java b/src/io/forsta/ccsm/database/DbHelper.java
index af8b65a2a..4c19b86e3 100644
--- a/src/io/forsta/ccsm/database/DbHelper.java
+++ b/src/io/forsta/ccsm/database/DbHelper.java
@@ -15,7 +15,8 @@
public class DbHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "relay.db";
private static final int ADD_CONTACT_FIELDS = 5;
- private static final int VERSION = 5;
+ private static final int ADD_CONTACT_TYPE = 6;
+ private static final int VERSION = 6;
public DbHelper(Context context) {
super(context, DB_NAME, null, VERSION);
@@ -32,5 +33,9 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("ALTER TABLE contacts ADD COLUMN isactive INTEGER DEFAULT 0");
db.execSQL("ALTER TABLE contacts ADD COLUMN ismonitor INTEGER DEFAULT 0");
}
+
+ if (oldVersion < ADD_CONTACT_TYPE) {
+ db.execSQL("ALTER TABLE contacts ADD COLUMN type");
+ }
}
}
diff --git a/src/io/forsta/ccsm/database/model/ForstaOrg.java b/src/io/forsta/ccsm/database/model/ForstaOrg.java
index ca8bd2e5d..b6b3acc7e 100644
--- a/src/io/forsta/ccsm/database/model/ForstaOrg.java
+++ b/src/io/forsta/ccsm/database/model/ForstaOrg.java
@@ -1,13 +1,20 @@
package io.forsta.ccsm.database.model;
+import android.content.Context;
+import android.util.Log;
+
import org.json.JSONException;
import org.json.JSONObject;
+import io.forsta.ccsm.ForstaPreferences;
+import io.forsta.ccsm.util.InvalidUserException;
+
/**
* Created by jlewis on 9/21/17.
*/
public class ForstaOrg {
+ private static final String TAG = ForstaOrg.class.getSimpleName();
private String uid;
private String name;
private String slug;
@@ -52,4 +59,8 @@ public static ForstaOrg fromJsonString(String jsonString) {
}
return null;
}
+
+ public static ForstaOrg getLocalForstaOrg(Context context) {
+ return fromJsonString(ForstaPreferences.getForstaOrg(context));
+ }
}
diff --git a/src/io/forsta/ccsm/database/model/ForstaGroup.java b/src/io/forsta/ccsm/database/model/ForstaTag.java
similarity index 87%
rename from src/io/forsta/ccsm/database/model/ForstaGroup.java
rename to src/io/forsta/ccsm/database/model/ForstaTag.java
index 74cdf0cdc..892404479 100644
--- a/src/io/forsta/ccsm/database/model/ForstaGroup.java
+++ b/src/io/forsta/ccsm/database/model/ForstaTag.java
@@ -7,10 +7,7 @@
import org.json.JSONObject;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
import java.util.Set;
import io.forsta.securesms.database.GroupDatabase;
@@ -20,8 +17,8 @@
* Created by jlewis on 2/24/17.
*/
-public class ForstaGroup {
- private static final String TAG = ForstaGroup.class.getSimpleName();
+public class ForstaTag {
+ private static final String TAG = ForstaTag.class.getSimpleName();
public String id;
public String slug;
public String org_id;
@@ -30,7 +27,7 @@ public class ForstaGroup {
public String parent;
public Set members = new HashSet<>();
- public ForstaGroup(JSONObject jsonObject) {
+ public ForstaTag(JSONObject jsonObject) {
try {
this.id = jsonObject.getString("id");
this.slug = jsonObject.getString("slug");
@@ -46,7 +43,7 @@ public ForstaGroup(JSONObject jsonObject) {
}
}
- public ForstaGroup(Cursor cursor) {
+ public ForstaTag(Cursor cursor) {
this.id = cursor.getString(cursor.getColumnIndex(GroupDatabase.GROUP_ID));
this.slug = cursor.getString(cursor.getColumnIndex(GroupDatabase.SLUG));
this.org_id = cursor.getString(cursor.getColumnIndex(GroupDatabase.ORG_ID));
diff --git a/src/io/forsta/ccsm/database/model/ForstaThread.java b/src/io/forsta/ccsm/database/model/ForstaThread.java
index e9ba59e54..e5012089a 100644
--- a/src/io/forsta/ccsm/database/model/ForstaThread.java
+++ b/src/io/forsta/ccsm/database/model/ForstaThread.java
@@ -17,12 +17,16 @@ public class ForstaThread {
public String title;
public String distribution;
private String recipientIds;
+ private String pretty;
+ private boolean pinned;
- public ForstaThread(long threadid, String uid, String title, String distribution) {
+ private ForstaThread(long threadid, String uid, String title, String distribution, String pretty) {
this.threadid = threadid;
this.uid = uid;
this.title = title;
this.distribution = distribution;
+ this.pretty = pretty;
+ this.pinned = false;
}
public ForstaThread(Cursor cursor) {
@@ -31,6 +35,8 @@ public ForstaThread(Cursor cursor) {
distribution = cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.DISTRIBUTION));
title = cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.TITLE));
recipientIds = cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.RECIPIENT_IDS));
+ pretty = cursor.getString(cursor.getColumnIndex(ThreadDatabase.PRETTY_EXPRESSION));
+ pinned = cursor.getInt(cursor.getColumnIndex(ThreadDatabase.PINNED)) != 0;
}
public long getThreadid() {
@@ -48,4 +54,16 @@ public String getDistribution() {
public String getTitle() {
return !TextUtils.isEmpty(title) ? title : "";
}
+
+ public String getRecipientIds() {
+ return recipientIds;
+ }
+
+ public String getPrettyExpression() {
+ return pretty;
+ }
+
+ public boolean isPinned() {
+ return pinned;
+ }
}
diff --git a/src/io/forsta/ccsm/database/model/ForstaUser.java b/src/io/forsta/ccsm/database/model/ForstaUser.java
index d8aced946..44dc2cc87 100644
--- a/src/io/forsta/ccsm/database/model/ForstaUser.java
+++ b/src/io/forsta/ccsm/database/model/ForstaUser.java
@@ -33,6 +33,12 @@ public class ForstaUser {
public boolean tsRegistered;
public boolean isActive = true;
public boolean isMonitor = false;
+ public RecipientType type = RecipientType.PERSON;
+
+ public enum RecipientType {
+ PERSON,
+ BOT
+ }
public ForstaUser() {
@@ -46,6 +52,17 @@ public ForstaUser(JSONObject userObj) throws InvalidUserException {
if (userObj.has("is_active")) {
this.isActive = userObj.getBoolean("is_active");
}
+ if (userObj.has("user_type")) {
+ String userType = userObj.getString("user_type");
+ switch (userType) {
+ case "PERSON":
+ this.type = RecipientType.PERSON;
+ break;
+ case "BOT":
+ this.type = RecipientType.BOT;
+ break;
+ }
+ }
this.uid = userObj.getString("id");
if (TextUtils.isEmpty(this.uid)) {
throw new InvalidUserException("UID is empty of null");
diff --git a/src/io/forsta/ccsm/messaging/ForstaMessageManager.java b/src/io/forsta/ccsm/messaging/ForstaMessageManager.java
index f0ec9deec..d2f629cb1 100644
--- a/src/io/forsta/ccsm/messaging/ForstaMessageManager.java
+++ b/src/io/forsta/ccsm/messaging/ForstaMessageManager.java
@@ -97,9 +97,9 @@ public static ForstaMessage fromJsonString(String messageBody) {
try {
forstaMessage = fromMessagBodyString(messageBody);
} catch (InvalidMessagePayloadException e) {
- Log.e(TAG, "Invalid message payload: " + e.getMessage());
+ Log.e(TAG, "Invalid message payload!");
Log.e(TAG, messageBody);
- forstaMessage.setTextBody("Invalid message body:" + messageBody);
+ forstaMessage.setTextBody(messageBody);
}
return forstaMessage;
}
diff --git a/src/io/forsta/securesms/ContactSelectionListFragment.java b/src/io/forsta/securesms/ContactSelectionListFragment.java
index 9ea218931..467f9a293 100644
--- a/src/io/forsta/securesms/ContactSelectionListFragment.java
+++ b/src/io/forsta/securesms/ContactSelectionListFragment.java
@@ -38,6 +38,7 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.LinearLayout;
import android.widget.TextView;
import io.forsta.ccsm.api.CcsmApi;
@@ -79,7 +80,7 @@ public class ContactSelectionListFragment extends Fragment
public final static int DISPLAY_MODE_OTHER_ONLY = ContactsCursorLoader.MODE_OTHER_ONLY;
private TextView emptyText;
- private TextView noResultsText;
+ private LinearLayout noResultsText;
private Map selectedContacts;
private Set selectedAddresses;
@@ -235,8 +236,6 @@ public void onItemClick(ContactSelectionListItem contact) {
}
}
-
-
public void removeAddress(String address) {
selectedAddresses.remove(address);
}
diff --git a/src/io/forsta/securesms/ConversationActivity.java b/src/io/forsta/securesms/ConversationActivity.java
index b48202952..cf3ecd109 100644
--- a/src/io/forsta/securesms/ConversationActivity.java
+++ b/src/io/forsta/securesms/ConversationActivity.java
@@ -29,6 +29,8 @@
import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
import android.graphics.drawable.ColorDrawable;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
@@ -214,6 +216,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private Recipients recipients;
private long threadId;
+ private ForstaThread forstaThread;
private int distributionType;
private boolean archived;
private boolean isSecureText = true;
@@ -246,7 +249,7 @@ protected void onCreate(Bundle state, @NonNull final MasterSecret masterSecret)
initializeResources();
initializeSecurity();
initializeDraft();
- initializeDirectory();
+// initializeDirectory();
}
@Override
@@ -268,7 +271,7 @@ protected void onNewIntent(Intent intent) {
initializeResources();
initializeSecurity();
initializeDraft();
- initializeDirectory();
+// initializeDirectory();
if (fragment != null) {
fragment.onNewIntent();
@@ -622,23 +625,44 @@ private void checkInvalidRecipients() {
private void checkThreadState() {
ForstaThread thread = DatabaseFactory.getThreadDatabase(ConversationActivity.this).getForstaThread(threadId);
- if (thread == null) {
- // This should never happen.
- new AsyncTask() {
- @Override
- protected ForstaThread doInBackground(Void... voids) {
- ForstaDistribution distribution = CcsmApi.getMessageDistribution(ConversationActivity.this, recipients.getRecipientExpression());
- return DatabaseFactory.getThreadDatabase(ConversationActivity.this).allocateThread(recipients, distribution);
- }
-
- @Override
- protected void onPostExecute(ForstaThread thread) {
- threadId = thread.getThreadid();
- }
- }.execute();
- }
+ forstaThread = thread;
+ ConnectivityManager cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
+ boolean isConnected = activeNetwork != null && activeNetwork.isConnectedOrConnecting();
+ if (isConnected) {
+ if (thread == null) {
+ // This should never happen.
+ new AsyncTask() {
+ @Override
+ protected ForstaDistribution doInBackground(Void... voids) {
+ return CcsmApi.getMessageDistribution(ConversationActivity.this, recipients.getRecipientExpression());
+ }
+ @Override
+ protected void onPostExecute(ForstaDistribution distribution) {
+ ForstaThread updatedThread = DatabaseFactory.getThreadDatabase(ConversationActivity.this).allocateThread(recipients, distribution);
+ threadId = updatedThread.getThreadid();
+ forstaThread = updatedThread;
+ }
+ }.execute();
+ } else {
+ // Update existing tag distribution users in thread.
+ new AsyncTask() {
+ @Override
+ protected ForstaDistribution doInBackground(Void... voids) {
+ return CcsmApi.getMessageDistribution(ConversationActivity.this, forstaThread.getDistribution());
+ }
+ @Override
+ protected void onPostExecute(ForstaDistribution distribution) {
+ if (distribution != null) {
+ recipients = RecipientFactory.getRecipientsFromStrings(ConversationActivity.this, distribution.getRecipients(ConversationActivity.this), false);
+ DatabaseFactory.getThreadDatabase(ConversationActivity.this).updateForstaThread(threadId, distribution);
+ }
+ }
+ }.execute();
+ }
+ }
}
private void initThread() {
@@ -730,25 +754,12 @@ private ListenableFuture initializeDirectory()
protected Boolean doInBackground(Recipients... params) {
try {
Context context = ConversationActivity.this;
- Recipients recipients = params[0];
-
- UserCapabilities capabilities = DirectoryHelper.refreshDirectoryFor(context, masterSecret, recipients);
- RecipientFactory.clearCache(context);
- recipients = RecipientFactory.getRecipientsFor(context, recipients.getRecipientsList(), false);
- if (capabilities.getTextCapability() == Capability.SUPPORTED) {
- return true;
- }
+ DirectoryHelper.refreshDirectoryFor(context, masterSecret, recipients);
+ RecipientFactory.getRecipientsFor(context, recipients.getRecipientsList(), false);
} catch (Exception e) {
- Log.w(TAG, e);
- }
- return false;
- }
-
- @Override
- protected void onPostExecute(Boolean result) {
- if (!result) {
- initializeEnabled(false);
+ e.printStackTrace();
}
+ return true;
}
}.execute(recipients);
diff --git a/src/io/forsta/securesms/ConversationListActivity.java b/src/io/forsta/securesms/ConversationListActivity.java
index 91285a60b..d6d10a711 100644
--- a/src/io/forsta/securesms/ConversationListActivity.java
+++ b/src/io/forsta/securesms/ConversationListActivity.java
@@ -108,11 +108,6 @@ protected void onCreate(Bundle savedState, @NonNull MasterSecret masterSecret) {
RefreshUserOrg task = new RefreshUserOrg();
task.execute();
- // Force set enable thread trimming to 30 days.
- if (!TextSecurePreferences.isThreadLengthTrimmingEnabled(getApplicationContext())) {
- TextSecurePreferences.setThreadTrimEnabled(getApplicationContext(), true);
- }
-
if (ForstaPreferences.getForstaContactSync(this) == -1) {
syncIndicator.setVisibility(View.VISIBLE);
Account account = ForstaSyncAdapter.getAccount(getApplicationContext());
@@ -206,7 +201,6 @@ public boolean onOptionsItemSelected(MenuItem item) {
case R.id.menu_invite: handleInvite(); return true;
case R.id.menu_help: handleHelp(); return true;
case R.id.menu_logout: handleLogout(); return true;
- case R.id.menu_directory: handleDirectoryRefresh(); return true;
case R.id.menu_linked_devices: handleLinkedDevices(); return true;
case R.id.menu_archive:
onSwitchToArchive();
@@ -255,11 +249,6 @@ private void handleLogout() {
finish();
}
- private void handleDirectoryRefresh() {
- syncIndicator.setVisibility(View.VISIBLE);
- ApplicationContext.getInstance(getApplicationContext()).getJobManager().add(new DirectoryRefreshJob(getApplicationContext()));
- }
-
private void handleDisplaySettings() {
Intent preferencesIntent = new Intent(this, ApplicationPreferencesActivity.class);
startActivity(preferencesIntent);
@@ -312,7 +301,6 @@ private class ContactsSyncReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Sync complete");
syncIndicator.setVisibility(View.GONE);
- RecipientFactory.clearCache(ConversationListActivity.this);
fragment.getListAdapter().notifyDataSetChanged();
}
}
diff --git a/src/io/forsta/securesms/ConversationListItem.java b/src/io/forsta/securesms/ConversationListItem.java
index ca0500d82..03333ba86 100644
--- a/src/io/forsta/securesms/ConversationListItem.java
+++ b/src/io/forsta/securesms/ConversationListItem.java
@@ -37,6 +37,7 @@
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
+import org.w3c.dom.Text;
import io.forsta.ccsm.ForstaPreferences;
import io.forsta.ccsm.api.CcsmApi;
@@ -53,6 +54,7 @@
import io.forsta.securesms.database.DatabaseFactory;
import io.forsta.securesms.database.ThreadPreferenceDatabase;
import io.forsta.securesms.database.model.ThreadRecord;
+import io.forsta.securesms.recipients.Recipient;
import io.forsta.securesms.recipients.Recipients;
import io.forsta.securesms.util.DateUtils;
import io.forsta.securesms.util.ResUtil;
@@ -100,6 +102,7 @@ public class ConversationListItem extends RelativeLayout
private final Handler handler = new Handler();
private int distributionType;
private String forstaThreadTitle;
+ private String threadExpression;
private MaterialColor threadColor;
public ConversationListItem(Context context) {
@@ -140,6 +143,7 @@ public void bind(@NonNull final MasterSecret masterSecret, @NonNull ThreadRecord
this.recipients.addListener(this);
this.forstaThreadTitle = thread.getTitle();
this.threadColor = thread.getColor();
+ this.threadExpression = thread.getPrettyExpression();
ForstaMessage forstaMessage = ForstaMessageManager.fromJsonString(thread.getDisplayBody().toString());
subjectView.setText(forstaMessage.getTextBody());
@@ -164,6 +168,7 @@ public void bind(@NonNull final MasterSecret masterSecret, @NonNull ThreadRecord
setBackground(thread);
setRippleColor(threadColor);
this.contactPhotoImage.setAvatar(recipients, threadColor);
+
}
@Override
@@ -227,11 +232,14 @@ private void setStatusIcons(ThreadRecord thread) {
alertView.setPendingApproval();
} else {
alertView.setNone();
-
if (thread.isDelivered()) deliveryStatusIndicator.setDelivered();
else if (thread.isPending()) deliveryStatusIndicator.setPending();
else deliveryStatusIndicator.setSent();
}
+
+ if (thread.isPinned()) {
+ alertView.setPinned();
+ }
}
private void setBackground(ThreadRecord thread) {
@@ -304,11 +312,14 @@ public void run() {
}
private void setForstaThreadTitle() {
-
if (!TextUtils.isEmpty(forstaThreadTitle)) {
this.fromView.setForstaTitle(forstaThreadTitle, read);
} else {
- this.fromView.setText(recipients, read);
+ if (!TextUtils.isEmpty(this.threadExpression) && !recipients.isSingleRecipient()) {
+ this.fromView.setForstaTitle(this.threadExpression, read);
+ } else {
+ this.fromView.setText(recipients, read);
+ }
}
}
}
diff --git a/src/io/forsta/securesms/NewConversationActivity.java b/src/io/forsta/securesms/NewConversationActivity.java
index 6393bdf99..923cb945e 100644
--- a/src/io/forsta/securesms/NewConversationActivity.java
+++ b/src/io/forsta/securesms/NewConversationActivity.java
@@ -16,24 +16,28 @@
*/
package io.forsta.securesms;
-import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AlertDialog;
+import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
+import android.util.Log;
+import android.util.Patterns;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
-import org.w3c.dom.Text;
+import org.whispersystems.signalservice.api.util.InvalidNumberException;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import io.forsta.ccsm.api.CcsmApi;
import io.forsta.ccsm.api.model.ForstaDistribution;
@@ -43,10 +47,13 @@
import io.forsta.ccsm.database.model.ForstaUser;
import io.forsta.securesms.crypto.MasterSecret;
import io.forsta.securesms.database.DatabaseFactory;
+import io.forsta.securesms.database.GroupDatabase;
import io.forsta.securesms.database.ThreadDatabase;
import io.forsta.securesms.recipients.RecipientFactory;
import io.forsta.securesms.recipients.Recipients;
import io.forsta.securesms.util.DirectoryHelper;
+import io.forsta.securesms.util.GroupUtil;
+import io.forsta.securesms.util.Util;
/**
* Activity container for starting a new conversation.
@@ -63,48 +70,39 @@ public class NewConversationActivity extends ContactSelectionActivity {
@Override
public void onCreate(Bundle bundle, @NonNull final MasterSecret masterSecret) {
super.onCreate(bundle, masterSecret);
-
-
getToolbar().setShowCustomNavigationButton(false);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
-// toolbar.setSearchOnClickListener(new View.OnClickListener() {
-// @Override
-// public void onClick(View view) {
-// showProgressBar();
-// String searchText = toolbar.getSearchText();
-// if (!searchText.startsWith("@")) {
-// searchText = "@" + searchText;
-// }
-// new AsyncTask() {
-//
-// @Override
-// protected ForstaDistribution doInBackground(String... strings) {
-// ForstaDistribution distribution = CcsmApi.getMessageDistribution(NewConversationActivity.this, strings[0]);
-// if (distribution.hasRecipients()) {
-// DirectoryHelper.refreshDirectoryFor(NewConversationActivity.this, masterSecret, distribution.getRecipients(NewConversationActivity.this));
-// }
-// return distribution;
-// }
-//
-// @Override
-// protected void onPostExecute(ForstaDistribution distribution) {
-// hideProgressBar();
-// if (distribution.hasWarnings()) {
-// Toast.makeText(NewConversationActivity.this, distribution.getWarnings(), Toast.LENGTH_LONG).show();
-// }
-// if (distribution.hasRecipients()) {
-// String searchText = toolbar.getSearchText();
-// if (searchText.contains(":")) {
-// String removeDomain = searchText.substring(0, searchText.indexOf(":"));
-// toolbar.setSearchTexttoolbar.setSearchText(removeDomain);
-// } else {
-// toolbar.setSearchText(searchText);
-// }
-// }
-// }
-// }.execute(searchText);
-// }
-// });
+
+ initListeners();
+ }
+
+ private void initListeners() {
+ toolbar.setSearchOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ showProgressBar();
+ String searchText = toolbar.getSearchText();
+ // check to see if it is a phone number or email. If not, assume expression.
+ if (PhoneNumberUtils.isGlobalPhoneNumber(searchText)) {
+ Log.w(TAG, "Phone input");
+ try {
+ String e164Number = Util.canonicalizeNumberE164(searchText);
+ lookupUsersByPhone(e164Number);
+ } catch (InvalidNumberException e) {
+ Toast.makeText(NewConversationActivity.this, "Invalid phone", Toast.LENGTH_LONG).show();
+ }
+ } else if (Patterns.EMAIL_ADDRESS.matcher(searchText).matches()) {
+ Log.w(TAG, "Email input");
+ lookupUsersByEmail(searchText);
+ } else {
+ Log.w(TAG, "Expression input");
+ if (!searchText.startsWith("@")) {
+ searchText = "@" + searchText;
+ }
+ lookupUsersByExpression(searchText);
+ }
+ }
+ });
toolbar.setCreateConversationListener(new View.OnClickListener() {
@Override
@@ -131,24 +129,43 @@ public void onContactSelected(final String address) {
@Override
protected Void doInBackground(String... strings) {
List addresses = new ArrayList<>();
- addresses. add(strings[0]);
- RecipientFactory.clearCache(NewConversationActivity.this);
- DirectoryHelper.refreshDirectoryFor(NewConversationActivity.this, masterSecret, addresses);
+ if (GroupUtil.isEncodedGroup(strings[0])) {
+ addresses.add(strings[0]);
+ GroupDatabase.GroupRecord tag = DatabaseFactory.getGroupDatabase(NewConversationActivity.this).getGroup(address);
+ if (tag == null || TextUtils.isEmpty(tag.getTitle())) {
+ DirectoryHelper.refreshDirectoryFor(NewConversationActivity.this, masterSecret, addresses);
+ }
+
+ } else {
+ addresses.add(strings[0]);
+ ForstaUser user = DbFactory.getContactDb(NewConversationActivity.this).getUserByAddress(address);
+ if (user == null || TextUtils.isEmpty(user.getName())) {
+ DirectoryHelper.refreshDirectoryFor(NewConversationActivity.this, masterSecret, addresses);
+ }
+ }
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
- ForstaUser user = DbFactory.getContactDb(NewConversationActivity.this).getUserByAddress(address);
- if (user == null || TextUtils.isEmpty(user.getName())) {
- Toast.makeText(NewConversationActivity.this, "Unable to retrieve user information.", Toast.LENGTH_LONG).show();
+ if (GroupUtil.isEncodedGroup(address)) {
+ GroupDatabase.GroupRecord tag = DatabaseFactory.getGroupDatabase(NewConversationActivity.this).getGroup(address);
+ if (tag == null || TextUtils.isEmpty(tag.getTitle())) {
+ Toast.makeText(NewConversationActivity.this, "Unable to retrieve tag information.", Toast.LENGTH_LONG).show();
+ return;
+ }
} else {
- addRecipientChip(address);
- selectedRecipients = RecipientFactory.getRecipientsFromStrings(NewConversationActivity.this, contactsFragment.getSelectedAddresses(), false);
- toolbar.clear();
- updateToggleBar();
- contactsFragment.resetQueryFilter();
+ ForstaUser user = DbFactory.getContactDb(NewConversationActivity.this).getUserByAddress(address);
+ if (user == null || TextUtils.isEmpty(user.getName())) {
+ Toast.makeText(NewConversationActivity.this, "Unable to retrieve user information.", Toast.LENGTH_LONG).show();
+ return ;
+ }
}
+ addRecipientChip(address);
+ selectedRecipients = RecipientFactory.getRecipientsFromStrings(NewConversationActivity.this, contactsFragment.getSelectedAddresses(), false);
+ toolbar.clear();
+ updateToggleBar();
+ contactsFragment.resetQueryFilter();
}
}.execute(address);
}
@@ -289,4 +306,82 @@ protected boolean onPrepareOptionsPanel(View view, Menu menu) {
super.onPrepareOptionsMenu(menu);
return true;
}
+
+ private void lookupUsersByPhone(final String number) {
+ new AsyncTask() {
+
+ @Override
+ protected Void doInBackground(String... strings) {
+ String phone = strings[0];
+ Set numbers = new HashSet<>();
+ numbers.add(phone);
+ List users = CcsmApi.getForstaUsersByPhone(NewConversationActivity.this, numbers);
+ List results = new ArrayList<>();
+ for (ForstaUser user: users) {
+ results.add(user.getUid());
+ }
+ if (results.size() > 0) {
+ DirectoryHelper.refreshDirectoryFor(NewConversationActivity.this, masterSecret, results);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void aVoid) {
+ hideProgressBar();
+ toolbar.setSearchText("");
+ contactsFragment.setQueryFilter("");
+ }
+ }.execute(number);
+ }
+
+ private void lookupUsersByEmail(String email) {
+ new AsyncTask() {
+
+ @Override
+ protected Void doInBackground(String... strings) {
+ String email = strings[0];
+ Set emails = new HashSet<>();
+ emails.add(email);
+ List users = CcsmApi.getForstaUsersByEmail(NewConversationActivity.this, emails);
+ List results = new ArrayList<>();
+ for (ForstaUser user: users) {
+ results.add(user.getUid());
+ }
+ if (results.size() > 0) {
+ DirectoryHelper.refreshDirectoryFor(NewConversationActivity.this, masterSecret, results);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void aVoid) {
+ hideProgressBar();
+ toolbar.setSearchText("");
+ contactsFragment.setQueryFilter("");
+ }
+ }.execute(email);
+ }
+
+ private void lookupUsersByExpression(String expression) {
+ new AsyncTask() {
+ @Override
+ protected ForstaDistribution doInBackground(String... strings) {
+ ForstaDistribution distribution = CcsmApi.getMessageDistribution(NewConversationActivity.this, strings[0]);
+ if (distribution.hasRecipients()) {
+ DirectoryHelper.refreshDirectoryFor(NewConversationActivity.this, masterSecret, distribution.getRecipients(NewConversationActivity.this));
+ }
+ return distribution;
+ }
+
+ @Override
+ protected void onPostExecute(ForstaDistribution distribution) {
+ hideProgressBar();
+ if (distribution.hasWarnings()) {
+ Toast.makeText(NewConversationActivity.this, distribution.getWarnings(), Toast.LENGTH_LONG).show();
+ }
+ contactsFragment.setQueryFilter(toolbar.getSearchText());
+ }
+ }.execute(expression);
+ }
}
diff --git a/src/io/forsta/securesms/components/AlertView.java b/src/io/forsta/securesms/components/AlertView.java
index a96551c31..ebee7db87 100644
--- a/src/io/forsta/securesms/components/AlertView.java
+++ b/src/io/forsta/securesms/components/AlertView.java
@@ -18,6 +18,7 @@ public class AlertView extends LinearLayout {
private ImageView approvalIndicator;
private ImageView failedIndicator;
+ private ImageView pinnedIndicator;
public AlertView(Context context) {
this(context, null);
@@ -39,6 +40,7 @@ private void initialize(AttributeSet attrs) {
approvalIndicator = (ImageView) findViewById(R.id.pending_approval_indicator);
failedIndicator = (ImageView) findViewById(R.id.sms_failed_indicator);
+ pinnedIndicator = (ImageView) findViewById(R.id.pinned_indicator);
if (attrs != null) {
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.AlertView, 0, 0);
@@ -57,13 +59,22 @@ public void setNone() {
public void setPendingApproval() {
this.setVisibility(View.VISIBLE);
- approvalIndicator.setVisibility(View.VISIBLE);
failedIndicator.setVisibility(View.GONE);
+ pinnedIndicator.setVisibility(GONE);
+ approvalIndicator.setVisibility(View.VISIBLE);
}
public void setFailed() {
this.setVisibility(View.VISIBLE);
approvalIndicator.setVisibility(View.GONE);
+ pinnedIndicator.setVisibility(GONE);
failedIndicator.setVisibility(View.VISIBLE);
}
+
+ public void setPinned() {
+ this.setVisibility(View.VISIBLE);
+ approvalIndicator.setVisibility(View.GONE);
+ failedIndicator.setVisibility(View.GONE);
+ pinnedIndicator.setVisibility(VISIBLE);
+ }
}
diff --git a/src/io/forsta/securesms/contacts/ContactsCursorLoader.java b/src/io/forsta/securesms/contacts/ContactsCursorLoader.java
index 2ce5be637..b7e427c77 100644
--- a/src/io/forsta/securesms/contacts/ContactsCursorLoader.java
+++ b/src/io/forsta/securesms/contacts/ContactsCursorLoader.java
@@ -106,26 +106,26 @@ public Cursor loadInBackground() {
List localUsers = DbFactory.getContactDb(getContext()).getActiveForstaUsers(filter);
try {
- if (!TextUtils.isEmpty(filter) && localUsers.size() < 1) {
- Log.w(TAG, "No local users found for filter.");
- NetworkInfo network = ServiceUtil.getConnectivityManager(getContext()).getActiveNetworkInfo();
- if (network != null && network.isConnectedOrConnecting()) {
- JSONObject results = CcsmApi.searchUserDirectory(getContext(), filter);
- Log.w(TAG, results.toString());
- List searchResults = CcsmApi.parseUsers(getContext(), results);
- for (ForstaUser user : searchResults) {
- if (!localUsers.contains(user)) {
- localUsers.add(user);
- }
- }
- }
- }
- Collections.sort(localUsers, new Comparator() {
- @Override
- public int compare(ForstaUser forstaUser, ForstaUser t1) {
- return forstaUser.getName().compareToIgnoreCase(t1.getName());
- }
- });
+// if (!TextUtils.isEmpty(filter) && localUsers.size() < 1) {
+// Log.w(TAG, "No local users found for filter.");
+// NetworkInfo network = ServiceUtil.getConnectivityManager(getContext()).getActiveNetworkInfo();
+// if (network != null && network.isConnectedOrConnecting()) {
+// JSONObject results = CcsmApi.searchUserDirectory(getContext(), filter);
+// Log.w(TAG, results.toString());
+// List searchResults = CcsmApi.parseUsers(getContext(), results);
+// for (ForstaUser user : searchResults) {
+// if (!localUsers.contains(user)) {
+// localUsers.add(user);
+// }
+// }
+// }
+// }
+// Collections.sort(localUsers, new Comparator() {
+// @Override
+// public int compare(ForstaUser forstaUser, ForstaUser t1) {
+// return forstaUser.getName().compareToIgnoreCase(t1.getName());
+// }
+// });
for (ForstaUser user : localUsers) {
if (!localUid.equals(user.getUid())) {
diff --git a/src/io/forsta/securesms/database/CanonicalAddressDatabase.java b/src/io/forsta/securesms/database/CanonicalAddressDatabase.java
index ba90b3f75..e53f6cec5 100644
--- a/src/io/forsta/securesms/database/CanonicalAddressDatabase.java
+++ b/src/io/forsta/securesms/database/CanonicalAddressDatabase.java
@@ -177,6 +177,25 @@ public long getCanonicalAddressId(@NonNull String address) {
return addressList;
}
+ public @NonNull List getCanonicalAddresses(@NonNull List ids) {
+ List addressList = new LinkedList<>();
+ Cursor cursor = null;
+ try {
+ SQLiteDatabase db = databaseHelper.getReadableDatabase();
+ String values = TextUtils.join(",", ids);
+ String query = ID_COLUMN + " IN (" + values + ")";
+ cursor = db.query(TABLE, null, query, null, null, null, null);
+ while (cursor != null && cursor.moveToNext()) {
+ addressList.add(cursor.getString(cursor.getColumnIndex(ADDRESS_COLUMN)));
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ return addressList;
+ }
+
private long getCanonicalAddressFromCache(String address) {
Long cachedAddress = addressCache.get(address);
return cachedAddress == null ? -1L : cachedAddress;
diff --git a/src/io/forsta/securesms/database/DatabaseFactory.java b/src/io/forsta/securesms/database/DatabaseFactory.java
index e5da9e14b..6d11556a7 100644
--- a/src/io/forsta/securesms/database/DatabaseFactory.java
+++ b/src/io/forsta/securesms/database/DatabaseFactory.java
@@ -78,7 +78,9 @@ public class DatabaseFactory {
private static final int INTRODUCE_FORSTA_ORGSLUG = 31;
private static final int INTRODUCE_FORSTA_PREFERENCES_THREADID = 32;
private static final int INTRODUCE_FORSTA_THEAD_PREFERENCES = 33;
- private static final int DATABASE_VERSION = 33;
+ private static final int INTRODUCE_FORSTA_THREAD_PRETTY_EXPRESSION = 34;
+ private static final int INTRODUCE_FORSTA_THREAD_PINNING = 35;
+ private static final int DATABASE_VERSION = 35;
private static final String DATABASE_NAME = "messages.db";
@@ -857,6 +859,14 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("CREATE TABLE thread_preferences (_id INTEGER PRIMARY KEY, thread_id INTEGER DEFAULT -1, block INTEGER DEFAULT 0, notification TEXT DEFAULT NULL, vibrate INTEGER DEFAULT 0, mute_until INTEGER DEFAULT 0, color TEXT DEFAULT NULL, expire_messages INTEGER DEFAULT 0);");
}
+ if (oldVersion < INTRODUCE_FORSTA_THREAD_PRETTY_EXPRESSION) {
+ db.execSQL("ALTER TABLE thread ADD COLUMN pretty_expression TEXT");
+ }
+
+ if (oldVersion < INTRODUCE_FORSTA_THREAD_PINNING) {
+ db.execSQL("ALTER TABLE thread ADD COLUMN pinned INTEGER DEFAULT 0");
+ }
+
db.setTransactionSuccessful();
db.endTransaction();
}
diff --git a/src/io/forsta/securesms/database/GroupDatabase.java b/src/io/forsta/securesms/database/GroupDatabase.java
index 59555d492..8b22b26fb 100644
--- a/src/io/forsta/securesms/database/GroupDatabase.java
+++ b/src/io/forsta/securesms/database/GroupDatabase.java
@@ -13,10 +13,9 @@
import android.support.annotation.Nullable;
import android.text.TextUtils;
-import io.forsta.ccsm.database.model.ForstaGroup;
+import io.forsta.ccsm.database.model.ForstaTag;
import io.forsta.ccsm.database.model.ForstaRecipient;
import io.forsta.ccsm.database.model.ForstaUser;
-import io.forsta.securesms.BuildConfig;
import io.forsta.securesms.recipients.Recipient;
import io.forsta.securesms.recipients.RecipientFactory;
import io.forsta.securesms.recipients.Recipients;
@@ -171,7 +170,7 @@ Recipients getGroupMembers(byte[] groupId, boolean includeSelf) {
return RecipientFactory.getRecipientsFor(context, recipients, false);
}
- public void updateGroups(List groups, List activeNumbers) {
+ public void updateGroups(List groups, List activeNumbers) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
Set groupIds = new HashSet<>();
// Don't get locally created groups.
@@ -184,7 +183,7 @@ public void updateGroups(List groups, List activeNumbers) {
db.beginTransaction();
try {
- for (ForstaGroup group : groups) {
+ for (ForstaTag group : groups) {
String id = group.getEncodedId();
List members = new ArrayList<>(group.members);
for (int i=0; i 1 ? new MergeCursor(cursors.toArray(new Cursor[cursors.size()])) : cursors.get(0);
setNotifyConverationListListeners(cursor);
@@ -361,7 +362,7 @@ public Cursor getConversationList() {
SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
builder.setTables(TABLE_NAME + " LEFT JOIN " + ThreadPreferenceDatabase.TABLE_NAME +
" ON " + TABLE_NAME + "." + ID + " = " + ThreadPreferenceDatabase.TABLE_NAME + "." + ThreadPreferenceDatabase.THREAD_ID);
- Cursor cursor = builder.query(db, null, ARCHIVED + " = ?", new String[] {"0"}, null, null, DATE + " DESC");
+ Cursor cursor = builder.query(db, null, ARCHIVED + " = ?", new String[] {"0"}, null, null, PINNED + " DESC, " + DATE + " DESC");
setNotifyConverationListListeners(cursor);
return cursor;
}
@@ -516,6 +517,7 @@ public ForstaThread allocateThread(Recipients recipients, ForstaDistribution dis
contentValues.put(TYPE, DistributionTypes.DEFAULT);
contentValues.put(UID, UUID.randomUUID().toString());
contentValues.put(DISTRIBUTION, distribution.universal);
+ contentValues.put(PRETTY_EXPRESSION, distribution.pretty);
contentValues.put(MESSAGE_COUNT, 0);
SQLiteDatabase db = databaseHelper.getWritableDatabase();
@@ -524,7 +526,7 @@ public ForstaThread allocateThread(Recipients recipients, ForstaDistribution dis
return getForstaThread(threadId);
}
- public long allocateThreadId(Recipients recipients, ForstaMessage forstaMessage) {
+ public long allocateThreadId(Recipients recipients, ForstaMessage forstaMessage, ForstaDistribution distribution) {
long[] recipientIds = getRecipientIds(recipients);
String recipientsList = getRecipientsAsString(recipientIds);
ContentValues contentValues = new ContentValues(5);
@@ -535,6 +537,7 @@ public long allocateThreadId(Recipients recipients, ForstaMessage forstaMessage)
contentValues.put(TYPE, DistributionTypes.DEFAULT);
contentValues.put(UID, forstaMessage.getThreadUId());
contentValues.put(DISTRIBUTION, forstaMessage.getUniversalExpression());
+ contentValues.put(PRETTY_EXPRESSION, distribution.pretty);
contentValues.put(TITLE, forstaMessage.getThreadTitle());
contentValues.put(MESSAGE_COUNT, 0);
@@ -544,13 +547,13 @@ public long allocateThreadId(Recipients recipients, ForstaMessage forstaMessage)
return threadId;
}
- public long getOrAllocateThreadId(Recipients recipients, ForstaMessage forstaMessage) {
+ public long getOrAllocateThreadId(Recipients recipients, ForstaMessage forstaMessage, ForstaDistribution distribution) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
long threadId = getThreadIdForUid(forstaMessage.getThreadUId());
if (threadId == -1) {
- threadId = allocateThreadId(recipients, forstaMessage);
+ threadId = allocateThreadId(recipients, forstaMessage, distribution);
} else {
- updateForstaThread(threadId, recipients, forstaMessage.getUniversalExpression(), forstaMessage.getThreadTitle());
+ updateForstaThread(threadId, recipients, forstaMessage, distribution);
}
return threadId;
}
@@ -571,6 +574,28 @@ public long getThreadIdForUid(String threadUid) {
return -1;
}
+ public List getAllRecipients() {
+ Set recipients = new HashSet<>();
+ SQLiteDatabase db = databaseHelper.getReadableDatabase();
+ Cursor cursor = null;
+ try {
+ cursor = db.query(TABLE_NAME, new String[] {RECIPIENT_IDS}, null, null, null, null, null);
+ while (cursor != null && cursor.moveToNext()) {
+ String ids = cursor.getString(0);
+ String[] idsArray = ids.split(" ");
+ for (int i=0; i(recipients));
+ }
+
public @Nullable Recipients getRecipientsForThreadId(long threadId) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
Cursor cursor = null;
@@ -590,6 +615,13 @@ public long getThreadIdForUid(String threadUid) {
return null;
}
+ public void updatePinned(long threadId, boolean pinned) {
+ SQLiteDatabase db = databaseHelper.getReadableDatabase();
+ ContentValues values = new ContentValues();
+ values.put(PINNED, pinned);
+ db.update(TABLE_NAME, values, ID_WHERE, new String[] {String.valueOf(threadId)});
+ }
+
public void updateReadState(long threadId) {
int unreadCount = DatabaseFactory.getMmsSmsDatabase(context).getUnreadCount(threadId);
@@ -635,18 +667,38 @@ public boolean update(long threadId, boolean unarchive) {
}
}
- public void updateForstaThread(long threadId, Recipients recipients, String distribution, String title) {
+ public void updateForstaThread(long threadId, ForstaDistribution distribution) {
+ Recipients recipients = RecipientFactory.getRecipientsFromStrings(context, distribution.getRecipients(context), false);
+ long[] recipientIds = getRecipientIds(recipients);
+ String recipientsList = getRecipientsAsString(recipientIds);
+ ForstaThread forstaThread = getForstaThread(threadId);
+
+ ContentValues values = new ContentValues();
+ if (!TextUtils.isEmpty(distribution.universal) && !distribution.universal.equals(forstaThread.distribution) || !forstaThread.getRecipientIds().equals(recipientsList)) {
+ values.put(RECIPIENT_IDS, recipientsList);
+ values.put(DISTRIBUTION, distribution.universal);
+ }
+ values.put(PRETTY_EXPRESSION, distribution.pretty);
+ if (values.size() > 0) {
+ SQLiteDatabase db = databaseHelper.getWritableDatabase();
+ db.update(TABLE_NAME, values, ID + " = ?", new String[] {threadId + ""});
+ notifyConversationListListeners();
+ }
+ }
+
+ public void updateForstaThread(long threadId, Recipients recipients, ForstaMessage message, ForstaDistribution distribution) {
long[] recipientIds = getRecipientIds(recipients);
String recipientsList = getRecipientsAsString(recipientIds);
ForstaThread forstaThread = getForstaThread(threadId);
ContentValues values = new ContentValues();
- if (!TextUtils.equals(forstaThread.title, title)) {
- values.put(TITLE, title);
+ if (!TextUtils.equals(forstaThread.title, message.getThreadTitle())) {
+ values.put(TITLE, message.getThreadTitle());
}
- if (!TextUtils.isEmpty(distribution) && !distribution.equals(forstaThread.distribution)) {
+ if (!TextUtils.isEmpty(distribution.universal) && !distribution.universal.equals(forstaThread.distribution)) {
values.put(RECIPIENT_IDS, recipientsList);
- values.put(DISTRIBUTION, distribution);
+ values.put(DISTRIBUTION, distribution.universal);
}
+ values.put(PRETTY_EXPRESSION, distribution.pretty);
if (values.size() > 0) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.update(TABLE_NAME, values, ID + " = ?", new String[] {threadId + ""});
@@ -763,10 +815,12 @@ public ThreadRecord getCurrent() {
String threadUid = cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.UID));
Uri snippetUri = getSnippetUri(cursor);
String color = cursor.getString(cursor.getColumnIndexOrThrow(ThreadPreferenceDatabase.COLOR));
+ String expression = cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.PRETTY_EXPRESSION));
+ boolean pinned = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.PINNED)) != 0;
return new ThreadRecord(context, body, snippetUri, recipients, date, count, read == 1,
threadId, receiptCount, status, type, distributionType, archived,
- expiresIn, distribution, title, threadUid, color);
+ expiresIn, distribution, title, threadUid, color, expression, pinned);
}
private DisplayRecord.Body getPlaintextBody(Cursor cursor) {
diff --git a/src/io/forsta/securesms/database/model/ThreadRecord.java b/src/io/forsta/securesms/database/model/ThreadRecord.java
index 07ecf42f4..5f188ba99 100644
--- a/src/io/forsta/securesms/database/model/ThreadRecord.java
+++ b/src/io/forsta/securesms/database/model/ThreadRecord.java
@@ -50,14 +50,16 @@ public class ThreadRecord extends DisplayRecord {
private final boolean archived;
private final long expiresIn;
private final String distribution;
+ private final String expression;
private final String title;
private final String threadUid;
private final String color;
+ private final boolean pinned;
public ThreadRecord(@NonNull Context context, @NonNull Body body, @Nullable Uri snippetUri,
@NonNull Recipients recipients, long date, long count, boolean read,
long threadId, int receiptCount, int status, long snippetType,
- int distributionType, boolean archived, long expiresIn, String distribution, String title, String threadUid, String color)
+ int distributionType, boolean archived, long expiresIn, String distribution, String title, String threadUid, String color, String expression, boolean pinned)
{
super(context, body, recipients, date, date, threadId, status, receiptCount, snippetType);
this.context = context.getApplicationContext();
@@ -71,6 +73,8 @@ public ThreadRecord(@NonNull Context context, @NonNull Body body, @Nullable Uri
this.title = title;
this.threadUid = threadUid;
this.color = color;
+ this.expression = expression;
+ this.pinned = pinned;
}
public @Nullable Uri getSnippetUri() {
@@ -176,4 +180,12 @@ public MaterialColor getColor() {
}
return MaterialColor.GREY;
}
+
+ public String getPrettyExpression() {
+ return expression;
+ }
+
+ public boolean isPinned() {
+ return pinned;
+ }
}
diff --git a/src/io/forsta/securesms/jobs/PushDecryptJob.java b/src/io/forsta/securesms/jobs/PushDecryptJob.java
index 4c26b94bd..b678cddb0 100644
--- a/src/io/forsta/securesms/jobs/PushDecryptJob.java
+++ b/src/io/forsta/securesms/jobs/PushDecryptJob.java
@@ -221,8 +221,9 @@ private void handleExpirationUpdate(@NonNull MasterSecretUnion masterSecret,
Optional.>absent());
ForstaMessage forstaMessage = ForstaMessageManager.fromMessagBodyString(body);
- Recipients recipients = getDistributionRecipients(forstaMessage.getUniversalExpression());
- long threadId = DatabaseFactory.getThreadDatabase(context).getOrAllocateThreadId(recipients, forstaMessage);
+ ForstaDistribution distribution = CcsmApi.getMessageDistribution(context, forstaMessage.getUniversalExpression());
+ Recipients recipients = getDistributionRecipients(distribution);
+ long threadId = DatabaseFactory.getThreadDatabase(context).getOrAllocateThreadId(recipients, forstaMessage, distribution);
database.insertSecureDecryptedMessageInbox(masterSecret, mediaMessage, threadId);
DatabaseFactory.getThreadPreferenceDatabase(context).setExpireMessages(threadId, message.getExpiresInSeconds());
@@ -320,8 +321,9 @@ private long handleSynchronizeSentExpirationUpdate(@NonNull MasterSecretUnion ma
Recipients sender = getSyncMessageDestination(message);
ForstaMessage forstaMessage = ForstaMessageManager.fromMessagBodyString(message.getMessage().getBody().get());
- Recipients recipients = getDistributionRecipients(forstaMessage.getUniversalExpression());
- long threadId = DatabaseFactory.getThreadDatabase(context).getOrAllocateThreadId(recipients, forstaMessage);
+ ForstaDistribution distribution = CcsmApi.getMessageDistribution(context, forstaMessage.getUniversalExpression());
+ Recipients recipients = getDistributionRecipients(distribution);
+ long threadId = DatabaseFactory.getThreadDatabase(context).getOrAllocateThreadId(recipients, forstaMessage, distribution);
OutgoingExpirationUpdateMessage expirationUpdateMessage = new OutgoingExpirationUpdateMessage(recipients,
message.getTimestamp(),
@@ -350,8 +352,9 @@ private long handleSynchronizeSentMediaMessage(@NonNull MasterSecretUnion master
ForstaMessage forstaMessage = ForstaMessageManager.fromMessagBodyString(message.getMessage().getBody().get());
if (forstaMessage.getMessageType() == ForstaMessage.MessageType.CONTENT) {
- Recipients recipients = getDistributionRecipients(forstaMessage.getUniversalExpression());
- long threadId = DatabaseFactory.getThreadDatabase(context).getOrAllocateThreadId(recipients, forstaMessage);
+ ForstaDistribution distribution = CcsmApi.getMessageDistribution(context, forstaMessage.getUniversalExpression());
+ Recipients recipients = getDistributionRecipients(distribution);
+ long threadId = DatabaseFactory.getThreadDatabase(context).getOrAllocateThreadId(recipients, forstaMessage, distribution);
if (DatabaseFactory.getThreadPreferenceDatabase(context).getExpireMessages(threadId) != message.getMessage().getExpiresInSeconds()) {
handleSynchronizeSentExpirationUpdate(masterSecret, message, Optional.absent());
@@ -403,6 +406,13 @@ private Recipients getDistributionRecipients(String expression) throws InvalidMe
throw new InvalidMessagePayloadException("No recipients found in message.");
}
+ private Recipients getDistributionRecipients(ForstaDistribution distribution) throws InvalidMessagePayloadException {
+ if (distribution.hasRecipients()) {
+ return RecipientFactory.getRecipientsFromStrings(context, distribution.getRecipients(context), false);
+ }
+ throw new InvalidMessagePayloadException("No recipients found in message.");
+ }
+
private void handleContentMessage(ForstaMessage forstaMessage,
MasterSecretUnion masterSecret,
SignalServiceDataMessage message,
@@ -418,12 +428,13 @@ private void handleContentMessage(ForstaMessage forstaMessage,
message.getGroupInfo(),
message.getAttachments());
- Recipients recipients = getDistributionRecipients(forstaMessage.getUniversalExpression());
+
+ ForstaDistribution distribution = CcsmApi.getMessageDistribution(context, forstaMessage.getUniversalExpression());
+ Recipients recipients = getDistributionRecipients(distribution);
DirectoryHelper.refreshDirectoryFor(context, masterSecret.getMasterSecret().get(), recipients);
- // Refresh recipients cache on message receive to populate new users.
- RecipientFactory.clearCache(context);
- recipients = RecipientFactory.getRecipientsFromStrings(context, recipients.toNumberStringList(false), false);
- long threadId = DatabaseFactory.getThreadDatabase(context).getOrAllocateThreadId(recipients, forstaMessage);
+ // Update the recipients cache?
+// RecipientFactory.getRecipientsFor(context, recipients.getRecipientsList(), false);
+ long threadId = DatabaseFactory.getThreadDatabase(context).getOrAllocateThreadId(recipients, forstaMessage, distribution);
if (message.getExpiresInSeconds() != DatabaseFactory.getThreadPreferenceDatabase(context).getExpireMessages(threadId)) {
handleExpirationUpdate(masterSecret, envelope, message, Optional.absent());
diff --git a/src/io/forsta/securesms/preferences/AdvancedPreferenceFragment.java b/src/io/forsta/securesms/preferences/AdvancedPreferenceFragment.java
index 49c5f334c..82414eadd 100644
--- a/src/io/forsta/securesms/preferences/AdvancedPreferenceFragment.java
+++ b/src/io/forsta/securesms/preferences/AdvancedPreferenceFragment.java
@@ -47,7 +47,6 @@ public class AdvancedPreferenceFragment extends PreferenceFragment {
private static final String PUSH_MESSAGING_PREF = "pref_toggle_push_messaging";
private static final String SUBMIT_DEBUG_LOG_PREF = "pref_submit_debug_logs";
private static final String FORSTA_DASHBOARD_PREFERENCE = "preference_forsta_dashboard";
- private static final String FORSTA_OTR_PREF = "pref_forsta_otr";
private static final int PICK_IDENTITY_CONTACT = 1;
private MasterSecret masterSecret;
@@ -70,7 +69,6 @@ public void onCreate(Bundle paramBundle) {
}
submitDebugLog.setOnPreferenceClickListener(new SubmitDebugLogListener());
submitDebugLog.setSummary(getVersion(getActivity()));
- initializeOffTheRecordToggle();
}
@Override
@@ -89,18 +87,6 @@ public void onActivityResult(int reqCode, int resultCode, Intent data) {
}
}
- private void initializeOffTheRecordToggle() {
- CheckBoxPreference preference = (CheckBoxPreference)this.findPreference(FORSTA_OTR_PREF);
- preference.setEnabled(false);
- if (ForstaPreferences.getOffTheRecord(getActivity())) {
- preference.setChecked(true);
- } else {
- preference.setChecked(false);
- }
-
- preference.setOnPreferenceChangeListener(new OffTheRecordClickListener());
- }
-
private void initializeIdentitySelection() {
ContactIdentityManager identity = ContactIdentityManager.getInstance(getActivity());
@@ -163,17 +149,6 @@ public boolean onPreferenceClick(Preference preference) {
}
}
- private class OffTheRecordClickListener implements Preference.OnPreferenceChangeListener {
-
- @Override
- public boolean onPreferenceChange(Preference preference, Object o) {
- boolean value = (boolean) o;
- ForstaPreferences.setOffTheRecord(getActivity(), value);
- initializeOffTheRecordToggle();
- return value;
- }
- }
-
private class DashboardClickListener implements Preference.OnPreferenceClickListener {
@Override
diff --git a/src/io/forsta/securesms/preferences/ChatsPreferenceFragment.java b/src/io/forsta/securesms/preferences/ChatsPreferenceFragment.java
index 72489ff32..0d08113ef 100644
--- a/src/io/forsta/securesms/preferences/ChatsPreferenceFragment.java
+++ b/src/io/forsta/securesms/preferences/ChatsPreferenceFragment.java
@@ -34,12 +34,10 @@ public void onCreate(Bundle paramBundle) {
.setOnPreferenceChangeListener(new MediaDownloadChangeListener());
findPreference(TextSecurePreferences.MEDIA_DOWNLOAD_ROAMING_PREF)
.setOnPreferenceChangeListener(new MediaDownloadChangeListener());
-
-// findPreference(TextSecurePreferences.THREAD_TRIM_NOW)
-// .setOnPreferenceClickListener(new TrimNowClickListener());
-// findPreference(TextSecurePreferences.THREAD_TRIM_LENGTH)
-// .setOnPreferenceChangeListener(new TrimLengthValidationListener());
-
+ findPreference(TextSecurePreferences.THREAD_TRIM_NOW)
+ .setOnPreferenceClickListener(new TrimNowClickListener());
+ findPreference(TextSecurePreferences.THREAD_TRIM_LENGTH)
+ .setOnPreferenceChangeListener(new TrimLengthValidationListener());
}
@Override
diff --git a/src/io/forsta/securesms/recipients/Recipient.java b/src/io/forsta/securesms/recipients/Recipient.java
index 6e1362ba6..e9c8cad06 100644
--- a/src/io/forsta/securesms/recipients/Recipient.java
+++ b/src/io/forsta/securesms/recipients/Recipient.java
@@ -47,6 +47,8 @@ public class Recipient {
private @Nullable String name;
private @Nullable String slug;
private @Nullable String orgSlug;
+ private boolean isActive;
+ private String userType;
private String email;
private boolean stale;
@@ -72,6 +74,8 @@ public class Recipient {
this.color = stale.color;
this.slug = stale.slug;
this.orgSlug = stale.orgSlug;
+ this.isActive = stale.isActive;
+ this.userType = stale.userType;
}
future.addListener(new FutureTaskListener() {
@@ -86,6 +90,8 @@ public void onSuccess(RecipientDetails result) {
Recipient.this.color = result.color;
Recipient.this.slug = result.slug;
Recipient.this.orgSlug = result.orgSlug;
+ Recipient.this.isActive = result.isActive;
+ Recipient.this.userType = result.userType;
}
notifyListeners();
@@ -108,6 +114,8 @@ public void onFailure(Throwable error) {
this.color = details.color;
this.slug = details.slug;
this.orgSlug = details.orgSlug;
+ this.isActive = details.isActive;
+ this.userType = details.userType;
}
public synchronized @Nullable Uri getContactUri() {
@@ -126,6 +134,14 @@ public void onFailure(Throwable error) {
return this.orgSlug;
}
+ public synchronized boolean getIsActive() {
+ return this.isActive;
+ }
+
+ public synchronized String getUserType() {
+ return this.userType;
+ }
+
public synchronized @NonNull MaterialColor getColor() {
if (color != null) return color;
else if (name != null) return ContactColors.generateFor(name);
@@ -178,7 +194,7 @@ public synchronized String toShortString() {
public static Recipient getUnknownRecipient() {
return new Recipient(-1, new RecipientDetails("Unknown", "Unknown", null,
- ContactPhotoFactory.getDefaultContactPhoto(null), null, null, null));
+ ContactPhotoFactory.getDefaultContactPhoto(null), null, null, null, false, "PERSON"));
}
@Override
diff --git a/src/io/forsta/securesms/recipients/RecipientProvider.java b/src/io/forsta/securesms/recipients/RecipientProvider.java
index b42d7a7b9..e27fb183a 100644
--- a/src/io/forsta/securesms/recipients/RecipientProvider.java
+++ b/src/io/forsta/securesms/recipients/RecipientProvider.java
@@ -76,7 +76,7 @@ public class RecipientProvider {
private static final Map STATIC_DETAILS = new HashMap() {{
put("262966", new RecipientDetails("Amazon", "262966", null,
ContactPhotoFactory.getResourceContactPhoto(R.drawable.ic_amazon),
- ContactColors.UNKNOWN_COLOR, null, null));
+ ContactColors.UNKNOWN_COLOR, null, null, false, "PERSON"));
}};
@NonNull Recipient getRecipient(Context context, long recipientId, boolean asynchronous) {
@@ -157,13 +157,15 @@ public RecipientDetails call() throws Exception {
String slug = cursor.getString(cursor.getColumnIndex(ContactDb.SLUG));
String orgSlug = cursor.getString(cursor.getColumnIndex(ContactDb.ORGSLUG));
ContactPhoto contactPhoto = ContactPhotoFactory.getDefaultContactPhoto(name);
+ boolean isActive = cursor.getInt(cursor.getColumnIndex(ContactDb.ISACTIVE)) != 0;
+ String userType= cursor.getString(cursor.getColumnIndex(ContactDb.USERTYPE));
if (avatarUrl != null) {
Bitmap gravatar = getContactGravatar(avatarUrl);
if (gravatar != null) {
contactPhoto = new BitmapContactPhoto(gravatar);
}
}
- return new RecipientDetails(name, uid, Uri.EMPTY, contactPhoto, color, slug, orgSlug);
+ return new RecipientDetails(name, uid, Uri.EMPTY, contactPhoto, color, slug, orgSlug, isActive, userType);
} else {
Log.w(TAG, "resultNumber is null");
}
@@ -175,7 +177,7 @@ public RecipientDetails call() throws Exception {
cursor.close();
}
- return new RecipientDetails(null, number, null, ContactPhotoFactory.getDefaultContactPhoto(null), color, null, null);
+ return new RecipientDetails(null, number, null, ContactPhotoFactory.getDefaultContactPhoto(null), color, null, null, false, "PERSON");
}
private @NonNull RecipientDetails getGroupRecipientDetails(Context context, String groupId) {
@@ -185,13 +187,13 @@ public RecipientDetails call() throws Exception {
if (record != null) {
ContactPhoto contactPhoto = ContactPhotoFactory.getGroupContactPhoto(record.getAvatar());
- return new RecipientDetails(record.getTitle(), groupId, null, contactPhoto, null, record.getTag(), record.getOrgTag());
+ return new RecipientDetails(record.getTitle(), groupId, null, contactPhoto, null, record.getTag(), record.getOrgTag(), true, "TAG");
}
- return new RecipientDetails(null, groupId, null, ContactPhotoFactory.getDefaultGroupPhoto(), null, null, null);
+ return new RecipientDetails(null, groupId, null, ContactPhotoFactory.getDefaultGroupPhoto(), null, null, null, false, "TAG");
} catch (IOException e) {
Log.w("RecipientProvider", e);
- return new RecipientDetails(null, groupId, null, ContactPhotoFactory.getDefaultGroupPhoto(), null, null, null);
+ return new RecipientDetails(null, groupId, null, ContactPhotoFactory.getDefaultGroupPhoto(), null, null, null, false, "TAG");
}
}
@@ -211,7 +213,6 @@ public RecipientPreferenceDatabase.RecipientsPreferences call() throws Exception
});
asyncRecipientResolver.execute(task);
-
return task;
}
@@ -221,12 +222,14 @@ public static class RecipientDetails {
@NonNull public final ContactPhoto avatar;
@Nullable public final String slug;
@Nullable public final String orgSlug;
+ public final boolean isActive;
+ public final String userType;
@Nullable public final Uri contactUri;
@Nullable public final MaterialColor color;
public RecipientDetails(@Nullable String name, @NonNull String number,
@Nullable Uri contactUri, @NonNull ContactPhoto avatar,
- @Nullable MaterialColor color, @Nullable String slug, @Nullable String orgSlug)
+ @Nullable MaterialColor color, @Nullable String slug, @Nullable String orgSlug, boolean isActive, String userType)
{
this.name = name;
this.number = number;
@@ -235,6 +238,8 @@ public RecipientDetails(@Nullable String name, @NonNull String number,
this.color = color;
this.slug = slug;
this.orgSlug = orgSlug;
+ this.isActive = isActive;
+ this.userType = userType;
}
}
@@ -272,7 +277,6 @@ public synchronized void reset() {
recipient.setStale();
}
}
-
}
private static class RecipientsCache {
diff --git a/src/io/forsta/securesms/recipients/Recipients.java b/src/io/forsta/securesms/recipients/Recipients.java
index c304fc898..0d0d1a7ba 100644
--- a/src/io/forsta/securesms/recipients/Recipients.java
+++ b/src/io/forsta/securesms/recipients/Recipients.java
@@ -34,6 +34,7 @@
import io.forsta.securesms.util.NumberUtil;
import io.forsta.securesms.util.Util;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
@@ -289,6 +290,14 @@ public long[] getIds() {
return ids;
}
+ public List getAddresses() {
+ List addresses = new ArrayList<>();
+ for (Recipient recipient : recipients) {
+ addresses.add(recipient.getNumber());
+ }
+ return addresses;
+ }
+
public String getSortedIdsString() {
Set recipientSet = new HashSet<>();
@@ -380,7 +389,7 @@ void setStale() {
}
public interface RecipientsModifiedListener {
- public void onModified(Recipients recipient);
+ void onModified(Recipients recipient);
}
}
diff --git a/src/io/forsta/securesms/util/DirectoryHelper.java b/src/io/forsta/securesms/util/DirectoryHelper.java
index fdf308a28..a7559077c 100644
--- a/src/io/forsta/securesms/util/DirectoryHelper.java
+++ b/src/io/forsta/securesms/util/DirectoryHelper.java
@@ -6,7 +6,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.OperationApplicationException;
-import android.os.Build;
import android.os.RemoteException;
import android.provider.ContactsContract;
import android.support.annotation.NonNull;
@@ -15,14 +14,11 @@
import android.util.Log;
import android.util.Pair;
-import io.forsta.ccsm.ForstaPreferences;
import io.forsta.ccsm.api.CcsmApi;
import io.forsta.ccsm.api.ForstaSyncAdapter;
import io.forsta.ccsm.database.ContactDb;
import io.forsta.ccsm.database.DbFactory;
-import io.forsta.ccsm.database.model.ForstaUser;
import io.forsta.ccsm.service.ForstaServiceAccountManager;
-import io.forsta.securesms.ApplicationContext;
import io.forsta.securesms.BuildConfig;
import io.forsta.securesms.R;
import io.forsta.securesms.crypto.MasterSecret;
@@ -30,7 +26,6 @@
import io.forsta.securesms.database.DatabaseFactory;
import io.forsta.securesms.database.NotInDirectoryException;
import io.forsta.securesms.database.TextSecureDirectory;
-import io.forsta.securesms.jobs.MultiDeviceContactUpdateJob;
import io.forsta.securesms.notifications.MessageNotifier;
import io.forsta.securesms.push.TextSecureCommunicationFactory;
import io.forsta.securesms.recipients.Recipient;
@@ -39,7 +34,6 @@
import io.forsta.securesms.sms.IncomingJoinedMessage;
import io.forsta.securesms.util.DirectoryHelper.UserCapabilities.Capability;
-import org.w3c.dom.Text;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.push.ContactTokenDetails;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
@@ -47,65 +41,20 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
-import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
public class DirectoryHelper {
-
- public static class UserCapabilities {
-
- public static final UserCapabilities UNKNOWN = new UserCapabilities(Capability.UNKNOWN, Capability.UNKNOWN);
- public static final UserCapabilities UNSUPPORTED = new UserCapabilities(Capability.UNSUPPORTED, Capability.UNSUPPORTED);
-
- public enum Capability {
- UNKNOWN, SUPPORTED, UNSUPPORTED
- }
-
- private final Capability text;
- private final Capability voice;
-
- public UserCapabilities(Capability text, Capability voice) {
- this.text = text;
- this.voice = voice;
- }
-
- public Capability getTextCapability() {
- return text;
- }
-
- public Capability getVoiceCapability() {
- return voice;
- }
- }
-
private static final String TAG = DirectoryHelper.class.getSimpleName();
- public static void refreshDirectory(@NonNull Context context, @Nullable MasterSecret masterSecret)
- throws IOException
- {
- CcsmApi.syncForstaContacts(context);
- List newUsers = refreshDirectory(context,
- TextSecureCommunicationFactory.createManager(context),
- TextSecurePreferences.getLocalNumber(context));
-
-// if (!newUsers.isEmpty() && TextSecurePreferences.isMultiDevice(context)) {
-// ApplicationContext.getInstance(context)
-// .getJobManager()
-// .add(new MultiDeviceContactUpdateJob(context));
-// }
-
-// notifyNewUsers(context, masterSecret, newUsers);
- context.sendBroadcast(new Intent(ForstaSyncAdapter.FORSTA_SYNC_COMPLETE));
+ public static void refreshDirectory(@NonNull Context context, @Nullable MasterSecret masterSecret) throws IOException {
+ refreshDirectory(context, TextSecureCommunicationFactory.createManager(context), TextSecurePreferences.getLocalNumber(context));
}
- public static @NonNull List refreshDirectory(@NonNull Context context,
- @NonNull ForstaServiceAccountManager accountManager,
- @NonNull String localNumber)
- throws IOException
- {
+ public static void refreshDirectory(@NonNull Context context, @NonNull ForstaServiceAccountManager accountManager, @NonNull String localNumber) throws IOException {
+ CcsmApi.syncForstaContacts(context);
ContactDb contactsDb = DbFactory.getContactDb(context);
Set eligibleContactAddresses = contactsDb.getAddresses();
// Two devices had crashes because of a null UID value in the contactsDb
@@ -121,65 +70,65 @@ public static void refreshDirectory(@NonNull Context context, @Nullable MasterSe
if (activeTokens != null) {
for (ContactTokenDetails activeToken : activeTokens) {
eligibleContactAddresses.remove(activeToken.getNumber());
- activeToken.setNumber(activeToken.getNumber()); //Huh?
}
directory.setNumbers(activeTokens, eligibleContactAddresses);
contactsDb.setActiveForstaAddresses(activeTokens, eligibleContactAddresses);
}
-
- return new LinkedList<>();
+ notifyRefresh(context);
}
- public static UserCapabilities refreshDirectoryFor(@NonNull Context context, @Nullable MasterSecret masterSecret, @NonNull Recipients recipients) {
+ public static void refreshDirectoryFor(@NonNull Context context, @Nullable MasterSecret masterSecret, @NonNull Recipients recipients) {
TextSecureDirectory directory = TextSecureDirectory.getInstance(context);
ForstaServiceAccountManager accountManager = TextSecureCommunicationFactory.createManager(context);
+ ContactDb contactsDb = DbFactory.getContactDb(context);
try {
- List addresses = new ArrayList<>();
- for (Recipient recipient : recipients) {
- if (TextUtils.isEmpty(recipient.getSlug())) {
- addresses.add(recipient.getNumber());
- }
- }
+ List addresses = recipients.toNumberStringList(false);
if (addresses.size() > 0) {
- String ids = TextUtils.join(",", addresses);
- CcsmApi.syncForstaContacts(context, ids);
-
+ CcsmApi.syncForstaContacts(context, addresses);
List details = accountManager.getContacts(new HashSet<>(addresses));
if (details.size() > 0) {
directory.setNumbers(details, new ArrayList());
- ContactDb contactsDb = DbFactory.getContactDb(context);
contactsDb.setActiveForstaAddresses(details);
+ notifyRefresh(context);
}
}
} catch (IOException e) {
e.printStackTrace();
}
- return new UserCapabilities(Capability.SUPPORTED, Capability.UNSUPPORTED);
}
public static void refreshDirectoryFor(Context context, MasterSecret masterSecret, List addresses) {
TextSecureDirectory directory = TextSecureDirectory.getInstance(context);
ForstaServiceAccountManager accountManager = TextSecureCommunicationFactory.createManager(context);
- String ids = TextUtils.join(",", addresses);
- CcsmApi.syncForstaContacts(context, ids);
try {
- List details = accountManager.getContacts(new HashSet(addresses));
- if (details.size() > 0) {
- directory.setNumbers(details, new ArrayList());
- ContactDb contactsDb = DbFactory.getContactDb(context);
- contactsDb.setActiveForstaAddresses(details);
+ if (addresses.size() > 0) {
+ CcsmApi.syncForstaContacts(context, addresses);
+ List details = accountManager.getContacts(new HashSet(addresses));
+ if (details.size() > 0) {
+ directory.setNumbers(details, new ArrayList());
+ ContactDb contactsDb = DbFactory.getContactDb(context);
+ contactsDb.setActiveForstaAddresses(details);
+ notifyRefresh(context);
+ }
}
} catch (IOException e) {
e.printStackTrace();
}
}
- public static @NonNull UserCapabilities getUserCapabilities(@NonNull Context context,
- @Nullable Recipients recipients)
- {
+ private static void notifyRefresh(Context context) {
+ RecipientFactory.clearCache(context);
+ context.sendBroadcast(new Intent(ForstaSyncAdapter.FORSTA_SYNC_COMPLETE));
+ }
+
+ private static void refreshRecipients(Context context, List addresses) {
+ RecipientFactory.getRecipientsFromStrings(context, new ArrayList(addresses), false);
+ }
+
+ public static @NonNull UserCapabilities getUserCapabilities(@NonNull Context context, @Nullable Recipients recipients) {
try {
if (recipients == null) {
return UserCapabilities.UNSUPPORTED;
@@ -303,4 +252,30 @@ private static Optional createAccount(Context context) {
return Optional.absent();
}
}
+
+ public static class UserCapabilities {
+
+ public static final UserCapabilities UNKNOWN = new UserCapabilities(Capability.UNKNOWN, Capability.UNKNOWN);
+ public static final UserCapabilities UNSUPPORTED = new UserCapabilities(Capability.UNSUPPORTED, Capability.UNSUPPORTED);
+
+ public enum Capability {
+ UNKNOWN, SUPPORTED, UNSUPPORTED
+ }
+
+ private final Capability text;
+ private final Capability voice;
+
+ public UserCapabilities(Capability text, Capability voice) {
+ this.text = text;
+ this.voice = voice;
+ }
+
+ public Capability getTextCapability() {
+ return text;
+ }
+
+ public Capability getVoiceCapability() {
+ return voice;
+ }
+ }
}
diff --git a/src/io/forsta/securesms/util/Util.java b/src/io/forsta/securesms/util/Util.java
index 29a92a8e9..0509f4f95 100644
--- a/src/io/forsta/securesms/util/Util.java
+++ b/src/io/forsta/securesms/util/Util.java
@@ -187,7 +187,7 @@ public static String canonicalizeNumber(Context context, String number)
return number;
}
String localNumber = TextSecurePreferences.getLocalNumber(context);
- return PhoneNumberFormatter.formatNumber(number, localNumber);
+ return PhoneNumberFormatter.formatNumber(number, "+12081234567");
}
public static String canonicalizeNumberOrGroup(@NonNull Context context, @NonNull String number)