com.layer.atlas.messenger.AtlasIdentityProvider.java Source code

Java tutorial

Introduction

Here is the source code for com.layer.atlas.messenger.AtlasIdentityProvider.java

Source

/*
 * Copyright (c) 2015 Layer. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.layer.atlas.messenger;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.drawable.Drawable;
import android.util.Log;

import com.layer.atlas.Atlas;

/**
 * @author Oleg Orlov
 * @since  17 Jul 2015
 */
public class AtlasIdentityProvider implements Atlas.ParticipantProvider {
    private final static String TAG = AtlasIdentityProvider.class.getSimpleName();
    private static final boolean debug = false;

    private static final int REFRESH_TIMEOUT_MILLIS = 60 * 1000;

    private final Map<String, Participant> participantsMap = new HashMap<String, Participant>();

    private String appId;
    private final Context context;

    private Thread refresher;
    private boolean refreshRequired = false;
    private final Object refreshLock = new Object();
    private long lastRefreshMs = System.currentTimeMillis();

    public AtlasIdentityProvider(Context context) {
        this.context = context;
        load();

        // refreshThread
        refresher = new Thread(new Runnable() {
            public void run() {
                while (true) {
                    synchronized (refreshLock) {
                        long waitMillis = 0;
                        while (appId == null || (!refreshRequired && (waitMillis = lastRefreshMs
                                + REFRESH_TIMEOUT_MILLIS - System.currentTimeMillis()) > 0)) {
                            try {
                                refreshLock.wait(waitMillis);
                            } catch (InterruptedException e) {
                                Log.e(TAG, "refresh() interrupted ", e);
                            }
                        }
                        refreshContacts(false, null, null);
                        lastRefreshMs = System.currentTimeMillis();
                        refreshRequired = false;
                    }
                }
            }
        }, "atlas-contacts-refresher");
        refresher.setDaemon(true);
        refresher.start();

    }

    @Override
    public Map<String, Atlas.Participant> getParticipants(String filter, Map<String, Atlas.Participant> result) {
        if (result == null) {
            result = new HashMap<String, Atlas.Participant>();
        }

        // With no filter, return all Participants
        if (filter == null) {
            result.putAll(participantsMap);
            return result;
        }

        // Filter participants by substring matching first- and last- names
        for (Participant p : participantsMap.values()) {
            boolean matches = false;
            if (p.firstName != null && p.firstName.toLowerCase().contains(filter))
                matches = true;
            if (!matches && p.lastName != null && p.lastName.toLowerCase().contains(filter))
                matches = true;
            if (matches) {
                result.put(p.getId(), p);
            } else {
                result.remove(p.getId());
            }
        }
        return result;
    }

    @Override
    public Atlas.Participant getParticipant(String userId) {
        Participant participant = participantsMap.get(userId);
        if (participant == null && appId != null) {
            requestRefresh();
        }
        return participant;
    }

    /** @return String[] { indentityToken (may be null), status/error description } */
    public String[] getIdentityToken(String nonce, String userName) {
        if (appId == null)
            return new String[] { null, "App ID is not set!" };
        return refreshContacts(true, nonce, userName);
    }

    public void requestRefresh() {
        synchronized (refreshLock) {
            refreshRequired = true;
            refreshLock.notifyAll();
        }
    }

    public void setAppId(String appId) {
        try {
            UUID.fromString(appId);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("appId must be valid UUID value. appId: " + appId, e);
        }
        synchronized (refreshLock) {
            this.appId = appId;
            refreshRequired = true;
            refreshLock.notifyAll();
        }
    }

    private String[] refreshContacts(boolean requestIdentityToken, String nonce, String userName) {
        try {
            String url = "https://layer-identity-provider.herokuapp.com/apps/" + appId + "/atlas_identities";
            HttpPost post = new HttpPost(url);
            post.setHeader("Content-Type", "application/json");
            post.setHeader("Accept", "application/json");
            post.setHeader("X_LAYER_APP_ID", appId);

            JSONObject rootObject = new JSONObject();
            if (requestIdentityToken) {
                rootObject.put("nonce", nonce);
                rootObject.put("name", userName);
            } else {
                rootObject.put("name", "Web"); // name must be specified to make entiry valid
            }
            StringEntity entity = new StringEntity(rootObject.toString(), "UTF-8");
            entity.setContentType("application/json");
            post.setEntity(entity);

            HttpResponse response = (new DefaultHttpClient()).execute(post);
            if (HttpStatus.SC_OK != response.getStatusLine().getStatusCode()
                    && HttpStatus.SC_CREATED != response.getStatusLine().getStatusCode()) {
                StringBuilder sb = new StringBuilder();
                sb.append("Got status ").append(response.getStatusLine().getStatusCode()).append(" [")
                        .append(response.getStatusLine()).append("] when logging in. Request: ").append(url);
                if (requestIdentityToken)
                    sb.append(" login: ").append(userName).append(", nonce: ").append(nonce);
                Log.e(TAG, sb.toString());
                return new String[] { null, sb.toString() };
            }

            String responseString = EntityUtils.toString(response.getEntity());
            JSONObject jsonResp = new JSONObject(responseString);

            JSONArray atlasIdentities = jsonResp.getJSONArray("atlas_identities");
            List<Participant> participants = new ArrayList<Participant>(atlasIdentities.length());
            for (int i = 0; i < atlasIdentities.length(); i++) {
                JSONObject identity = atlasIdentities.getJSONObject(i);
                Participant participant = new Participant();
                participant.firstName = identity.getString("name");
                participant.userId = identity.getString("id");
                participants.add(participant);
            }
            if (participants.size() > 0) {
                setParticipants(participants);
                save();
                if (debug)
                    Log.d(TAG, "refreshContacts() contacts: " + atlasIdentities);
            }

            if (requestIdentityToken) {
                String error = jsonResp.optString("error", null);
                String identityToken = jsonResp.optString("identity_token");
                return new String[] { identityToken, error };
            }
            return new String[] { null, "Refreshed " + participants.size() + " contacts" };
        } catch (Exception e) {
            Log.e(TAG, "Error when fetching identity token", e);
            return new String[] { null, "Cannot obtain identity token. " + e };
        }
    }

    /**
     * Overwrites the current list of Contacts with the provided list.
     *
     * @param participants New list of Contacts to apply.
     */
    private void setParticipants(List<Participant> participants) {
        synchronized (participantsMap) {
            participantsMap.clear();
            for (Participant participant : participants) {
                participantsMap.put(participant.userId, participant);
            }
        }
    }

    private boolean load() {
        String jsonString = context.getSharedPreferences("contacts", Context.MODE_PRIVATE).getString("json", null);
        if (jsonString == null)
            return false;

        List<Participant> participants;
        try {
            JSONArray contactsJson = new JSONArray(jsonString);
            participants = new ArrayList<Participant>(contactsJson.length());
            for (int i = 0; i < contactsJson.length(); i++) {
                JSONObject contactJson = contactsJson.getJSONObject(i);
                Participant participant = new Participant();
                participant.userId = contactJson.optString("id");
                participant.firstName = contactJson.optString("first_name");
                participant.lastName = contactJson.optString("last_name");
                participants.add(participant);
            }
        } catch (JSONException e) {
            Log.e(TAG, "Error while saving", e);
            return false;
        }

        setParticipants(participants);

        return true;
    }

    private boolean save() {
        Collection<Participant> participants;
        synchronized (participantsMap) {
            participants = participantsMap.values();
        }

        JSONArray contactsJson;
        try {
            contactsJson = new JSONArray();
            for (Participant participant : participants) {
                JSONObject contactJson = new JSONObject();
                contactJson.put("id", participant.userId);
                contactJson.put("first_name", participant.firstName);
                contactJson.put("last_name", participant.lastName);
                contactsJson.put(contactJson);
            }
        } catch (JSONException e) {
            Log.e(TAG, "Error while saving", e);
            return false;
        }

        SharedPreferences.Editor editor = context.getSharedPreferences("contacts", Context.MODE_PRIVATE).edit();
        editor.putString("json", contactsJson.toString());
        editor.commit();

        return true;
    }

    public class Participant implements Atlas.Participant {
        public String userId;
        public String firstName;
        public String lastName;

        public String getId() {
            return userId;
        }

        @Override
        public String getFirstName() {
            return firstName;
        }

        @Override
        public String getLastName() {
            return lastName;
        }

        @Override
        public Drawable getAvatarDrawable() {
            return null;
        }

        public String toString() {
            StringBuilder builder = new StringBuilder();
            builder.append("Contact [userId: ").append(userId).append(", firstName: ").append(firstName)
                    .append(", lastName: ").append(lastName).append("]");
            return builder.toString();
        }
    }

}