public interface Action<T> {
    void call(T t);
} public class Response<T, S> {

    private T result;
    private S error;

    public static <T, S> Response<T, S> fromValue(T value){
        Response<T, S> response = new Response<>();
        response.setResult(value);
        return response;
    }

    public static <T, S> Response<T, S> fromError(S error){
        Response<T, S> response = new Response<>();
        response.setError(error);
        return response;
    }

    public static <T, S> Response<T, S> from(T value, S error){
        Response<T, S> response = new Response<>();
        response.setResult(value);
        response.setError(error);
        return response;
    }

    private Response(){}

    private void setError(S error) {
        this.error = error;
    }

    void setResult(T result) {
        this.result = result;
    }

    public boolean isError() {
        return error != null;
    }

    public T getResult() {
        return result;
    }

    public S getError() {
        return error;
    }
} public class IdentityTokens {
    private final String AuthorizationCode;

    private final String IdentityToken;

    private final String CodeVerifier;

    public IdentityTokens(String authorizationCode, String identityToken, String codeVerifier) {
        AuthorizationCode = authorizationCode;
        IdentityToken = identityToken;
        CodeVerifier = codeVerifier;
    }

    public IdentityTokens(String authorizationCode, String identityToken) {
        this(authorizationCode, identityToken, null);
    }

    public String getAuthorizationCode() {
        return AuthorizationCode;
    }

    public String getCodeVerifier() {
        return CodeVerifier;
    }

    public String getIdentityToken() {
        return IdentityToken;
    }
} public class ResponseAdminApiError {
    private String code;
    private String message;
    private ResponseExtensionException exception;

    public ResponseAdminApiError(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    @Override
    public String toString() {
        return String.format("code: %s, message: %s", code, message);
    }
} public class ResponseAdminApiInitUserRegistration  {
    private String userId;
    private String regSessionId;

    public String getUserId() {
        return userId;
    }

    public String getRegSessionId() {
        return regSessionId;
    }


} public class ResponseAdminApiLoginByCode {
    private String userId;

    private String id;

    private String validUntil;

    public String getId() {
        return id;
    }

} public class ResponseZerokitChangePassword extends ZerokitJson {
    private int MasterFragmentVersion;

    public int getMasterFragmentVersion() {
        return MasterFragmentVersion;
    }

    @SuppressWarnings("unchecked")
    @Override
    public ResponseZerokitChangePassword parse(String json) {
        JSONObject jsonobject = new JSONObject(json);
        MasterFragmentVersion = jsonobject.getInt("MasterFragmentVersion");
        return this;
    }

} public class ResponseZerokitCreateInvitationLink extends ZerokitJson {
    private String url;
    private String id;

    public String getId() {
        return id;
    }

    public String getUrl() {
        return url;
    }

    @SuppressWarnings("unchecked")
    @Override
    public ResponseZerokitCreateInvitationLink parse(String json){
        try {
            JSONObject jsonobject = new JSONObject(json);
            url = jsonobject.getString("url");
            id = jsonobject.getString("id");
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return this;
    }
} public class ResponseZerokitError extends ZerokitJson {
    private String type;
    private String code;
    private String message;
    private String description;
    private final ResponseZerokitInternalException internalException;

    public ResponseZerokitError(String type, String code, String message, String description) {
        this.type = type;
        this.code = code;
        this.message = message;
        this.description = description;
        this.internalException = new ResponseZerokitInternalException();
    }

    @Override
    public String toString() {
        return String.format("type: %s, code: %s, message: %s, description: %s", getType(), getCode(), getMessage(), getDescription());
    }

    public ResponseZerokitError(String description) {
        this("", "", "", description);
    }

    public ResponseZerokitError(String message, String description) {
        this("", "", message, description);
    }

    public ResponseZerokitError() {
        this("", "", "", "");
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public String getType() {
        return type;
    }

    public String getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

    public String getDescription() {
        return description;
    }

    public String toJSON() {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("description", description);
        jsonObject.put("code", code);
        jsonObject.put("message", message);
        jsonObject.put("type", type);
        return jsonObject.toString();
    }

    @SuppressWarnings("unchecked")
    @Override
    public ResponseZerokitError parse(String json) {
        JSONObject jsonobject = new JSONObject(json);
        description = jsonobject.getString("description");
        type = jsonobject.getString("type");
        code = jsonobject.getString("code");
        message = jsonobject.getString("message");
        internalException.parse(jsonobject.getJSONObject("internalException").toString());
        return this;
    }

} public class ResponseZerokitInvitationLinkInfo extends ZerokitJson {

    /**
     * the user id of the creator of this link
     */
    private String creatorUserId;
    /**
     * bool value indicating if the links needs a password entered.
     */
    private boolean isPasswordProtected;

    /**
     * arbitrary string data, given at the time of creation
     */
    private String message;


    /**
     *  link information for internal use, used as a parameter for acceptInvitationLink
     */
    private String $token;

    public String getCreatorUserId() {
        return creatorUserId;
    }

    public Boolean getPasswordProtected() {
        return isPasswordProtected;
    }

    public String getMessage() {
        return message;
    }

    public String getToken() {
        return $token;
    }

    @SuppressWarnings("unchecked")
    @Override
    public ResponseZerokitInvitationLinkInfo parse(String json){
        try {
            JSONObject jsonobject = new JSONObject(json);
            creatorUserId = jsonobject.getString("creatorUserId");
            isPasswordProtected = jsonobject.getBoolean("isPasswordProtected");
            message = jsonobject.getString("message");
            $token = jsonobject.getString("$token");
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return this;
    }
} public class ResponseZerokitLogin extends ZerokitJson {
    private String userId;

    public String getUserId() {
        return userId;
    }

    @SuppressWarnings("unchecked")
    @Override
    public ResponseZerokitLogin parse(String json){
        try {
            JSONObject jsonobject = new JSONObject(json);
            userId = jsonobject.getString("userId");
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return this;
    }

} public class ResponseZerokitPasswordStrength extends ZerokitJson {
    private CrackTimesSeconds crack_times_seconds;
    private Feedback feedback;
    private double guesses_log10;
    private int length;
    private int score;

    public CrackTimesSeconds getCrack_times_seconds() {
        return crack_times_seconds;
    }

    public double getGuesses_log10() {
        return guesses_log10;
    }

    public Feedback getFeedback() {
        return feedback;
    }

    public int getLength() {
        return length;
    }

    public int getScore() {
        return score;
    }

    @Override
    public String toString() {
        return String.format("crack_times_seconds: %s, feedback: %s, guesses_log10: %s, length: %s, score: %s", crack_times_seconds, feedback, guesses_log10, length, score);
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T extends ZerokitJson> T parse(String json) {
        JSONObject jsonObject = new JSONObject(json);
        crack_times_seconds = new CrackTimesSeconds().parse(jsonObject.getJSONObject("crack_times_seconds").toString());
        feedback = new Feedback().parse(jsonObject.getJSONObject("feedback").toString());
        guesses_log10 = jsonObject.getDouble("guesses_log10");
        length = jsonObject.getInt("length");
        score = jsonObject.getInt("score");
        return (T) this;
    }
} public class ResponseZerokitRegister extends ZerokitJson {
    private String RegValidationVerifier;

    public String getRegValidationVerifier() {
        return RegValidationVerifier;
    }

    @SuppressWarnings("unchecked")
    @Override
    public ResponseZerokitRegister parse(String json) {
        try {
            JSONObject jsonobject = new JSONObject(json);
            RegValidationVerifier = jsonobject.getString("RegValidationVerifier");
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return this;
    }

} public class Holder<T>{
    public T t;
} public class JSONObject {

    private org.json.JSONObject jsonObject;

    public JSONObject() {
        jsonObject = new org.json.JSONObject();
    }

    public JSONObject(String json) {
        try {
            jsonObject = new org.json.JSONObject(json);
        } catch (JSONException e) {
            //e.printStackTrace();
            jsonObject = new org.json.JSONObject();
        }
    }

    public org.json.JSONObject getJSONObject(String name) {
        if (jsonObject != null)
            try {
                return jsonObject.getJSONObject(name);
            } catch (JSONException e) {
                //e.printStackTrace();
            }
        return new org.json.JSONObject();
    }


    public String getString(String name) {
        if (jsonObject != null)
            try {
                return jsonObject.getString(name);
            } catch (JSONException e) {
                //e.printStackTrace();
            }
        return "";
    }

    public double getDouble(String name) {
        if (jsonObject != null)
            try {
                return jsonObject.getDouble(name);
            } catch (JSONException e) {
                //e.printStackTrace();
            }
        return 0;
    }

    public int getInt(String name) {
        if (jsonObject != null)
            try {
                return jsonObject.getInt(name);
            } catch (JSONException e) {
                //e.printStackTrace();
            }
        return 0;
    }

    public List<String> getStringArray(String name) {
        if (jsonObject != null)
            try {
                JSONArray jsonArray = jsonObject.getJSONArray(name);
                List<String> result = new ArrayList<>();
                for (int i = 0; i < jsonArray.length(); i++)
                    result.add(jsonArray.getString(i));
                return result;
            } catch (JSONException e) {
                //e.printStackTrace();
            }
        return new ArrayList<>();
    }

    public JSONObject put(String name, Object value)  {
        if (jsonObject != null)
            try {
                jsonObject.put(name, value);
            } catch (JSONException e) {
                //e.printStackTrace();
            }
        return this;
    }

    @Override
    public String toString() {
        return jsonObject != null ? jsonObject.toString() : "";
    }
}
import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.text.TextUtils;
import com.tresorit.zerokit.call.Action;
import com.tresorit.zerokit.call.Response;
import com.tresorit.zerokit.response.IdentityTokens;
import com.tresorit.zerokit.response.ResponseAdminApiError;
import com.tresorit.zerokit.response.ResponseAdminApiInitUserRegistration;
import com.tresorit.zerokit.response.ResponseAdminApiLoginByCode;
import com.tresorit.zerokit.response.ResponseZerokitChangePassword;
import com.tresorit.zerokit.response.ResponseZerokitCreateInvitationLink;
import com.tresorit.zerokit.response.ResponseZerokitError;
import com.tresorit.zerokit.response.ResponseZerokitInvitationLinkInfo;
import com.tresorit.zerokit.response.ResponseZerokitLogin;
import com.tresorit.zerokit.response.ResponseZerokitPasswordStrength;
import com.tresorit.zerokit.response.ResponseZerokitRegister;
import com.tresorit.zerokit.util.Holder;
import com.tresorit.zerokit.util.JSONObject;
import junit.framework.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
package com.tresorit.zerokit;







/**
 * Instrumentation test, which will execute on an Android device.
 *
 * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
 */
@RunWith(AndroidJUnit4.class)
public class InstrumentedTest {

    static private Zerokit zerokit;
    static private AdminApi adminApi;

    static private String pass01;
    static private String user01;
    static private String pass02;
    static private String user02;

    private Context targetContext;
    private BackgroundThreadExecutor backgroundThreadExecutor;

    private final static class BackgroundThreadExecutor implements Executor {
        private final Handler handler;

        BackgroundThreadExecutor() {
            HandlerThread handlerThread = new HandlerThread("Background thread");
            handlerThread.start();
            handler = new Handler(handlerThread.getLooper());
        }

        @Override
        public void execute(final Runnable r) {
            final CountDownLatch signal = new CountDownLatch(1);
            handler.post(new Runnable() {
                @Override
                public void run() {
                    r.run();
                    signal.countDown();
                }
            });
            try {
                signal.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Before
    public void init() throws Throwable {
        backgroundThreadExecutor = new BackgroundThreadExecutor();
        targetContext = InstrumentationRegistry.getTargetContext();

        if (adminApi == null) {
            Properties properties = new Properties();
            properties.load(targetContext.getAssets().open("zerokit.properties"));
            adminApi = AdminApi.init(properties.getProperty("appbackend", ""), properties.getProperty("clientid", ""));
        }

        if (zerokit == null) {
            Properties properties = new Properties();
            properties.load(targetContext.getAssets().open("zerokit.properties"));
            Zerokit.init(targetContext, properties.getProperty("apiroot", ""));
            zerokit = Zerokit.getInstance();
            zerokit.getIdlingResource();
        }

        assertNotNull(adminApi);
        assertNotNull(zerokit);

        initUsers();

        loginIfNeededTest(user01, pass01);
    }

    private void initUsers() {
        if (TextUtils.isEmpty(user01) && TextUtils.isEmpty(user02)){
            pass01 = USER_01_PASS;
            user01 = registrationTest(getUserName(USER_01_ALIAS), pass01);
            pass02 = USER_02_PASS;
            user02 = registrationTest(getUserName(USER_02_ALIAS), pass02);
        }

        Assert.assertFalse(TextUtils.isEmpty(user01));
        Assert.assertFalse(TextUtils.isEmpty(user02));
        Assert.assertFalse(TextUtils.isEmpty(pass01));
        Assert.assertFalse(TextUtils.isEmpty(pass02));
    }

    private static final String USER_01_ALIAS = "test-user01";
    private static final String USER_02_ALIAS = "test-user02";
    private static final String USER_01_PASS = "password01";
    private static final String USER_02_PASS = "password02";

    private String getUserName(String userName) {
        return String.format("%s-%s", userName, UUID.randomUUID());
    }

    @Test
    public void testPasswordStrength () {
        getPasswordStrengthTest(pass01);
    }

    @Test
    public void testZerokitError () {
        logoutTest();
        Response<ResponseZerokitLogin, ResponseZerokitError> response = login_noassert(user01, pass01 + ".");