Skip to content

Instantly share code, notes, and snippets.

@parthdesai1208
Last active April 6, 2022 13:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save parthdesai1208/a96f69b23a238c0d5cda8ea08e4f941d to your computer and use it in GitHub Desktop.
Save parthdesai1208/a96f69b23a238c0d5cda8ea08e4f941d to your computer and use it in GitHub Desktop.
Retrofit
object NursingApiClient {
lateinit var retrofit: Retrofit
val service: ApiInterface by lazy {
val builder = Retrofit.Builder()
.baseUrl(BASE_URL) //e.g., "https://api.github.com/"
//(we can change it at runtime to deal with multiple API versions)
//like, Production, staging, developments etc.
.addConverterFactory(GsonConverterFactory.create())
val client = OkHttpClient.Builder()
client.connectTimeout(60, TimeUnit.SECONDS)
client.readTimeout(60, TimeUnit.SECONDS)
client.writeTimeout(60, TimeUnit.SECONDS)
client.sslSocketFactory(SSLSocketFactory = , X509TrustManager = ) <-- SSL --
//region Interceptor
/**
* Interceptor will intercept the request before it send to the server
* Hereby it will add Authorization and BarerToken in header for security reason
* Every time it will generate new random string and new encrypted token
* */
val interceptor = Interceptor {
var request = it.request()
try {
val newBuilder = request.newBuilder()
val randomStr = randomString(53)
val token = Base64.encodeToString(
getDeviceBearerToken(randomStr).toByteArray(),
Base64.NO_WRAP
)
//add Authorization in header of the request
newBuilder.addHeader("Authorization", randomStr) <-- Key-value pair
//add BarerToken in header of the request
newBuilder.addHeader("BarerToken", token)
//override existing header key-value
newBuilder.header("BarerToken", token)
//in case you want to pass query in every request
newBuilder.url(request.url().newBuilder().addQueryParameter("apikey","your-actual-api-key").build())
request = newBuilder.build()
} catch (e: Exception) {
}
val response = it.proceed(request)
response
}
client.addInterceptor(interceptor)
//endregion of interceptor code
if (BuildConfig.DEBUG) { //debug request/response log
val loggingInterceptor = HttpLoggingInterceptor(HttpLoggingInterceptor.Logger.DEFAULT)
loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
client.addInterceptor(loggingInterceptor)
client.addInterceptor(ChuckerInterceptor.Builder(App.getApplicationContext()).build())
}
builder.client(client.build())
retrofit = builder.build()
retrofit.create(ApiInterface::class.java)
}
}
-----------------------------------------------------------------------------------------------------------------
interface ApiInterface {
//all @GET @POST method goes here
}
-----------------------------------------------------------------------------------------------------------------
for header
-----------------------------------------------------------------------------------------------------------------
fun getDeviceBearerToken(publicKey: String): String {
// val publicKey = randomString(53)
val firstKey = publicKey.substring(5, 15)
val lastKey = publicKey.substring(publicKey.length - 6)
val privateKey = firstKey + lastKey
// val plainText = "DKisWorkingOnAES"
val plainText = "5tf28+!VmhRVsCGX-nF3-D^EwH#G"
val cryptLib = CryptLib()
val bearerToken = cryptLib.encryptPlainTextWithRandomIV(plainText, privateKey)
return bearerToken
}
-----------------------------------------------------------------------------------------------------------------
public class CryptLib {
/**
* Encryption mode enumeration
*/
private enum EncryptMode {
ENCRYPT, DECRYPT
}
// cipher to be used for encryption and decryption
private Cipher _cx;
// encryption key and initialization vector
private byte[] _key, _iv;
public CryptLib() throws NoSuchAlgorithmException, NoSuchPaddingException {
// initialize the cipher with transformation AES/CBC/PKCS5Padding
_cx = Cipher.getInstance("AES/CBC/PKCS5Padding");
_key = new byte[32]; //256 bit key space
_iv = new byte[16]; //128 bit IV
}
/**
*
* @param inputText Text to be encrypted or decrypted
* @param encryptionKey Encryption key to used for encryption / decryption
* @param mode specify the mode encryption / decryption
* @param initVector Initialization vector
* @return encrypted or decrypted bytes based on the mode
* @throws UnsupportedEncodingException
* @throws InvalidKeyException
* @throws InvalidAlgorithmParameterException
* @throws IllegalBlockSizeException
* @throws BadPaddingException
*/
private byte[] encryptDecrypt(String inputText, String encryptionKey,
EncryptMode mode, String initVector) throws UnsupportedEncodingException,
InvalidKeyException, InvalidAlgorithmParameterException,
IllegalBlockSizeException, BadPaddingException {
int len = encryptionKey.getBytes("UTF-8").length; // length of the key provided
if (encryptionKey.getBytes("UTF-8").length > _key.length)
len = _key.length;
int ivlength = initVector.getBytes("UTF-8").length;
if(initVector.getBytes("UTF-8").length > _iv.length)
ivlength = _iv.length;
System.arraycopy(encryptionKey.getBytes("UTF-8"), 0, _key, 0, len);
System.arraycopy(initVector.getBytes("UTF-8"), 0, _iv, 0, ivlength);
SecretKeySpec keySpec = new SecretKeySpec(_key, "AES"); // Create a new SecretKeySpec for the specified key data and algorithm name.
IvParameterSpec ivSpec = new IvParameterSpec(_iv); // Create a new IvParameterSpec instance with the bytes from the specified buffer iv used as initialization vector.
// encryption
if (mode.equals(EncryptMode.ENCRYPT)) {
// Potentially insecure random numbers on Android 4.3 and older. Read for more info.
// https://android-developers.blogspot.com/2013/08/some-securerandom-thoughts.html
_cx.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);// Initialize this cipher instance
return _cx.doFinal(inputText.getBytes("UTF-8")); // Finish multi-part transformation (encryption)
} else {
_cx.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);// Initialize this cipher instance
byte[] decodedValue = Base64.decode(inputText.getBytes(), Base64.DEFAULT);
return _cx.doFinal(decodedValue); // Finish multi-part transformation (decryption)
}
}
/***
* This function computes the SHA256 hash of input string
* @param text input text whose SHA256 hash has to be computed
* @param length length of the text to be returned
* @return returns SHA256 hash of input text
* @throws NoSuchAlgorithmException
* @throws UnsupportedEncodingException
*/
private static String SHA256 (String text, int length) throws NoSuchAlgorithmException, UnsupportedEncodingException {
String resultString;
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(text.getBytes("UTF-8"));
byte[] digest = md.digest();
StringBuilder result = new StringBuilder();
for (byte b : digest) {
result.append(String.format("%02x", b)); //convert to hex
}
if(length > result.toString().length()) {
resultString = result.toString();
} else {
resultString = result.toString().substring(0, length);
}
return resultString;
}
public String encryptPlainText(String plainText, String key, String iv) throws Exception {
byte[] bytes = encryptDecrypt(plainText, CryptLib.SHA256(key, 32), EncryptMode.ENCRYPT, iv);
return Base64.encodeToString(bytes, Base64.DEFAULT);
}
public String decryptCipherText(String cipherText, String key, String iv) throws Exception {
byte[] bytes = encryptDecrypt(cipherText, CryptLib.SHA256(key, 32), EncryptMode.DECRYPT, iv);
return new String(bytes);
}
public String encryptPlainTextWithRandomIV(String plainText, String key) throws Exception {
byte[] bytes = encryptDecrypt(generateRandomIV16() + plainText, CryptLib.SHA256(key, 32), EncryptMode.ENCRYPT, generateRandomIV16());
return Base64.encodeToString(bytes, Base64.DEFAULT);
}
public String decryptCipherTextWithRandomIV(String cipherText, String key) throws Exception {
byte[] bytes = encryptDecrypt(cipherText, CryptLib.SHA256(key, 32), EncryptMode.DECRYPT, generateRandomIV16());
String out = new String(bytes);
return out.substring(16, out.length());
}
/**
* Generate IV with 16 bytes
* @return
*/
public String generateRandomIV16() {
SecureRandom ranGen = new SecureRandom();
byte[] aesKey = new byte[16];
ranGen.nextBytes(aesKey);
StringBuilder result = new StringBuilder();
for (byte b : aesKey) {
result.append(String.format("%02x", b)); //convert to hex
}
if (16 > result.toString().length()) {
return result.toString();
} else {
return result.toString().substring(0, 16);
}
}
}
-----------------------------------------------------------------------------------------------------------------
@parthdesai1208
Copy link
Author

parthdesai1208 commented Aug 29, 2021

Converters

interface Service{
   @GET("/user")
    Call<User> user(); // <- xml

    @GET("/friends")
    Call<Friends> friends(); // <- json
}
Retrofit retrofit = new Retrofit.Builder()
                              .baseUrl("http://example.com")
                              .addConverterFactory(
                                                AnnotatedConverterFactory.builder()
                                               .add(Xml.class, SimpleXmlConvertterFactory.create())
                                               .add(Json.class,GsonConverterFactory.create())
                                               .build())
                               .build();

class AnnotatedConverterFactory extends Converter.Factory {
  final Map < Class < ? > , Converter.Factory > factories;

  AnnotationConverterFactory(
    Map < Class < ? > , Converter.Factory > factories) {
    this.factories = new LinkedHashMap < > (factories);
  }

  @Override public Converter < ResponseBody, ? > responseBodyConverter(

    Type type, Annotation[] annotations, Retrofit retrofit) {

    for (Annotation annotation: annotations) {
      Converter.Factory factory =
        factories.get(annotation.getClass());
      if (factory != null) {
        return factory.responseBodyConverter(
          type, annotations, retrofit);

      }
    }

    return null;

  }
}

@parthdesai1208
Copy link
Author

Gson Builder

Gson gson = new GsonBuilder()
     .registerTypeAdapter(Id.class, new IdTypeAdapter())
     .enableComplexMapKeySerialization()
     .serializeNulls()
     .setDateFormat(DateFormat.LONG)
     .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
     .setPrettyPrinting()
     .setVersion(1.0)
     .create();
Retrofit retrofit = new Retrofit.Builder()  
     .baseUrl("https://api.github.com")
     .addConverterFactory(GsonConverterFactory.create(gson))
     .build();

@parthdesai1208
Copy link
Author

xml converter

compile 'com.squareup.retrofit2:converter-simplexml:2.5.0'

Retrofit retrofit = new Retrofit.Builder()  
    .baseUrl(API_BASE_URL)
    .client(new OkHttpClient())
    .addConverterFactory(**SimpleXmlConverterFactory**.create())
    .build();

tasks.xml

<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">  
    <task>
        <id link="http://url-to-link.com/task-id">1</id>
        <title>Retrofit XML Converter Blog Post</title>
        <description>Write blog post: XML Converter with Retrofit</description>
        <language>de-de</language>
    </task>
</rss> 

Task

@Root(name = "task")
public class Task {  
    @Element(name = "id")
    private long id;

    @Element(name = "title")
    private String title;

    @Element(name = "description")
    private String description;

    @Attribute(required = false)
    private String link;

    public Task() {}
}

@parthdesai1208
Copy link
Author

Parse JSON which starts with JSON array

val resp = response.body()?.string()
val listType: Type = object : TypeToken<List<YourModelClass?>?>() {}.type
val posts: ArrayList<YourModelClass> = gson.fromJson(resp, listType)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment