package com.instructure.canvasapi.utilities; import android.content.Context; import android.content.SharedPreferences; import android.text.TextUtils; import android.util.Log; import com.google.gson.Gson; import com.instructure.canvasapi.api.AlertAPI; import com.instructure.canvasapi.model.User; import java.io.BufferedReader; import java.io.File; import java.io.InputStreamReader; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Locale; import retrofit.client.Header; import retrofit.client.Response; /** * Created by Joshua Dutton on 8/9/13. * * Copyright (c) 2014 Instructure. All rights reserved. */ public class APIHelpers { /** * SharedPreferences tags */ //We would need migration code to update NAME and MASQUERADED_USER to snake case, so we will leave them as is for now. private final static String SHARED_PREFERENCES_NAME = "canvas-kit-sp"; private final static String SHARED_PREFERENCES_MASQUERADED_USER = "masq-user"; private final static String SHARED_PREFERENCES_USER = "user"; private final static String SHARED_PREFERENCES_DOMAIN = "domain"; private final static String SHARED_PREFERENCES_MASQUERADED_DOMAIN = "masq-domain"; private final static String SHARED_PREFERENCES_KALTURA_DOMAIN = "kaltura_domain"; private final static String SHARED_PREFERENCES_TOKEN = "token"; private final static String SHARED_PREFERENCES_KALTURA_TOKEN = "kaltura_token"; private final static String SHARED_PREFERENCES_USER_AGENT = "user_agent"; private final static String SHARED_PREFERENCES_API_PROTOCOL = "api_protocol"; private final static String SHARED_PREFERENCES_KALTURA_PROTOCOL = "kaltura_protocol"; private final static String SHARED_PREFERENCES_ERROR_DELEGATE_CLASS_NAME = "error_delegate_class_name"; private final static String SHARED_PREFERENCES_DISMISSED_NETWORK_ERROR = "dismissed_network_error"; private final static String SHARED_PREFERENCES_AIRWOLF_DOMAIN = "airwolf_domain"; /** * Log Tag */ public final static String LOG_TAG = "canvas-api"; /** * * GetAssetsFile allows you to open a file that exists in the Assets directory. * * @param context * @param fileName * @return the contents of the file. */ public static String getAssetsFile(Context context, String fileName) { try { String file = ""; BufferedReader reader = new BufferedReader( new InputStreamReader(context.getAssets().open(fileName))); // do reading String line = ""; while (line != null) { file += line; line = reader.readLine(); } reader.close(); return file; } catch (Exception e) { return ""; } } /** * clearAllData is essentially a Logout. * Clears all data including credentials and cache. * * @param context * @return */ public static boolean clearAllData(Context context) { if(context == null){ return false; } //Clear the cached API Responses CanvasRestAdapter.deleteHttpCache(); //Clear credentials. SharedPreferences sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPreferences.edit(); editor.clear(); boolean sharedPreferencesDeleted = editor.commit(); //Delete cache. File cacheDir = new File(context.getFilesDir(), FileUtilities.FILE_DIRECTORY); boolean cacheDeleted = FileUtilities.deleteAllFilesInDirectory(cacheDir); return sharedPreferencesDeleted && cacheDeleted; } /** * setCacheUser saves the currently signed in user to cache. * @param context * @param user * @return */ public static boolean setCacheUser(Context context, User user) { if (user == null) { return false; } else { Gson gson = CanvasRestAdapter.getGSONParser(); String userString = gson.toJson(user); if (userString == null) { return false; } SharedPreferences sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPreferences.edit(); String sharedPrefsKey = SHARED_PREFERENCES_USER; if(Masquerading.isMasquerading(context)){ sharedPrefsKey = SHARED_PREFERENCES_MASQUERADED_USER; } editor.putString(sharedPrefsKey, userString); return editor.commit(); } } /** * setCachedAvatarURL is a helper to set a value on the cached user. * @param context * @param avatarURL * @return */ public static boolean setCachedAvatarURL(Context context, String avatarURL){ User user = getCacheUser(context); if(user == null){ return false; } user.setAvatarURL(avatarURL); return setCacheUser(context, user); } /** * setCachedShortName is a helper to set a value on the cached user. * @param context * @param shortName * @return */ public static boolean setCachedShortName(Context context, String shortName){ User user = getCacheUser(context); if(user == null){ return false; } user.setShortName(shortName); return setCacheUser(context, user); } /** * setCachedEmail is a helper to set a value on the cached user. * @param context * @param email * @return */ public static boolean setCachedEmail(Context context, String email){ User user = getCacheUser(context); if(user == null){ return false; } user.setEmail(email); return setCacheUser(context, user); } /** * setCachedName is a helper to set a value on the cached user. * @param context * @param name * @return */ public static boolean setCachedName(Context context, String name){ User user = getCacheUser(context); if(user == null){ return false; } user.setName(name); return setCacheUser(context, user); } /** * getCacheUser returns the signed-in user from cache. Returns null if there isn't one. * @param context * @return */ public static User getCacheUser(Context context) { if(context == null){ return null; } SharedPreferences sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); String sharedPrefsKey = SHARED_PREFERENCES_USER; if(Masquerading.isMasquerading(context)){ sharedPrefsKey = SHARED_PREFERENCES_MASQUERADED_USER; } String userString = sharedPreferences.getString(sharedPrefsKey, null); if (userString == null) { return null; } else { Gson gson = CanvasRestAdapter.getGSONParser(); return gson.fromJson(userString, User.class); } } /** * getUserAgent returns the current user agent. * @param context * @return */ public static String getUserAgent(Context context) { if(context == null){ return ""; } SharedPreferences sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); return sharedPreferences.getString(SHARED_PREFERENCES_USER_AGENT, ""); } /** * setUserAgent sets the user agent * @param context * @param userAgent * @return */ public static boolean setUserAgent(Context context, String userAgent) { if(userAgent == null || userAgent.equals("")){ return false; } SharedPreferences sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putString(SHARED_PREFERENCES_USER_AGENT, userAgent); return editor.commit(); } /** * getFullDomain returns the protocol plus the domain. * * Returns "" if context is null or if the domain/token isn't set. * @return */ public static String getFullDomain(Context context){ String protocol = loadProtocol(context); String domain = getDomain(context); if (protocol == null || domain == null || protocol.equals("") || domain.equals("") ){ return ""; } return protocol + "://" + domain; } /** * getDomain returns the current domain. This function strips off all trailing / characters and the protocol. * @link APIHelpers.loadProtocol(context) * @param context * @return */ public static String getDomain(Context context) { if(context == null){ return ""; } SharedPreferences sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); String sharedPrefsKey = SHARED_PREFERENCES_DOMAIN; if(Masquerading.isMasquerading(context)){ sharedPrefsKey = SHARED_PREFERENCES_MASQUERADED_DOMAIN; } String domain = sharedPreferences.getString(sharedPrefsKey, ""); while (domain != null && domain.endsWith("/")) { domain = domain.substring(0, domain.length() - 1); } return domain; } /** * getFullKalturaDomain returns the protocol plus the domain. * * Returns "" if context is null or if the domain/token isn't set. * @return */ public static String getFullKalturaDomain(Context context){ String protocol = loadProtocol(context); String domain = getKalturaDomain(context); if (protocol == null || domain == null || protocol.equals("") || domain.equals("") ){ return ""; } return protocol + "://" + domain; } /** * getKalturaDomain returns the current domain. This function strips off all trailing / characters and the protocol. * @link APIHelpers.loadProtocol(context) * @param context * @return */ public static String getKalturaDomain(Context context){ if(context == null){ return ""; } SharedPreferences sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); String domain = sharedPreferences.getString(SHARED_PREFERENCES_KALTURA_DOMAIN, ""); while (domain != null && domain.endsWith("/")) { domain = domain.substring(0, domain.length() - 1); } return domain; } /** * setDomain sets the current domain. It strips off the protocol. * * @param context * @param domain * @return */ public static boolean setDomain(Context context, String domain) { if(domain == null || domain.equals("")){ return false; } domain = removeProtocol(domain); String sharedPrefsKey = SHARED_PREFERENCES_DOMAIN; if(Masquerading.isMasquerading(context)){ sharedPrefsKey = SHARED_PREFERENCES_MASQUERADED_DOMAIN; } SharedPreferences sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putString(sharedPrefsKey, domain); return editor.commit(); } /** * setDomain sets the current Kaltura domain. It strips off the protocol. * * @param context * @param kalturaDomain * @return */ public static boolean setKalturaDomain(Context context, String kalturaDomain) { if(kalturaDomain == null || kalturaDomain.equals("")){ return false; } kalturaDomain = removeProtocol(kalturaDomain); SharedPreferences sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putString(SHARED_PREFERENCES_KALTURA_DOMAIN, kalturaDomain); return editor.commit(); } /** * Check to see if the Airwolf domain is set. We don't want to return an empty domain, so this check * will return whether the user has set a domain. * * @param context * @return True if an Airwolf domain has been set, false otherwise */ public static boolean isAirwolfDomainSet(Context context) { if(context == null){ return false; } SharedPreferences sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); //if the shared preference here is empty, that means a domain hasn't been set return !TextUtils.isEmpty(sharedPreferences.getString(SHARED_PREFERENCES_AIRWOLF_DOMAIN, "")); } /** * Get the Airwolf region to use. This will be set when the user first opens the app depending on which region is fastest * * If no region is set it will use the American Region as default * * @param context * @return Domain to use for Airwolf API calls */ public static String getAirwolfDomain(Context context) { if(context == null){ return ""; } SharedPreferences sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); //make america the default region return sharedPreferences.getString(SHARED_PREFERENCES_AIRWOLF_DOMAIN, AlertAPI.AIRWOLF_DOMAIN_AMERICA); } /** * Sets the current Airwolf domain * * @param context * @param airwolfDomain * @return True if saved successfully, false otherwise */ public static boolean setAirwolfDomain(Context context, String airwolfDomain) { if(airwolfDomain == null || airwolfDomain.equals("")){ return false; } SharedPreferences sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putString(SHARED_PREFERENCES_AIRWOLF_DOMAIN, airwolfDomain); return editor.commit(); } /** * getToken returns the OAuth token or "" if there isn't one. * @param context * @return */ public static String getToken(Context context) { SharedPreferences sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); return sharedPreferences.getString(SHARED_PREFERENCES_TOKEN, ""); } /** * setToken sets the OAuth token * @param context * @param token * @return */ public static boolean setToken(Context context, String token) { if(token == null || token.equals("")){ return false; } SharedPreferences sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putString(SHARED_PREFERENCES_TOKEN, token); return editor.commit(); } /** * resetToken sets the OAuth token to an empty string * @param context * @return */ public static boolean resetToken(Context context) { SharedPreferences sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putString(SHARED_PREFERENCES_TOKEN, ""); return editor.commit(); } /** * getToken returns the OAuth token or "" if there isn't one. * @param context * @return */ public static String getKalturaToken(Context context) { SharedPreferences sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); return sharedPreferences.getString(SHARED_PREFERENCES_KALTURA_TOKEN, ""); } /** * setToken sets the OAuth token * @param context * @param token * @return */ public static boolean setKalturaToken(Context context, String token) { if(token == null || token.equals("")){ return false; } SharedPreferences sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putString(SHARED_PREFERENCES_KALTURA_TOKEN, token); return editor.commit(); } /** * loadProtocol returns the protocol or 'https' if there isn't one. * @param context * @return */ public static String loadProtocol(Context context) { if(context == null){ return "https"; } SharedPreferences sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); return sharedPreferences.getString(SHARED_PREFERENCES_API_PROTOCOL, "https"); } /** * setProtocol sets the protocol * @param protocol * @param context * @return */ public static boolean setProtocol(String protocol, Context context) { if(protocol == null || protocol.equals("")){ return false; } SharedPreferences sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putString(SHARED_PREFERENCES_API_PROTOCOL, protocol); return editor.commit(); } /** * Sets the default error delegate. This is the default if one isn't specified in the constructor * * @param errorDelegateClassName */ public static void setDefaultErrorDelegateClass(Context context, String errorDelegateClassName) { SharedPreferences sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putString(SHARED_PREFERENCES_ERROR_DELEGATE_CLASS_NAME, errorDelegateClassName); editor.apply(); } /** * Get the default error delegate. * * @param context */ public static String getDefaultErrorDelegateClass(Context context) { SharedPreferences sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); return sharedPreferences.getString(SHARED_PREFERENCES_ERROR_DELEGATE_CLASS_NAME, null); } /** * booleanToInt is a Helper function for Converting boolean to URL booleans (ints) */ public static int booleanToInt(boolean bool) { if (bool) { return 1; } return 0; } /** * removeDomainFromUrl is a helper function for removing the domain from a url. Used for pagination/routing * @param url * @return */ public static String removeDomainFromUrl(String url) { if(url == null){ return null; } String prefix = "/api/v1/"; int index = url.indexOf(prefix); if (index != -1) { url = url.substring(index + prefix.length()); } return url; } public static boolean isCachedResponse(Response response) { return response != null && response.getHeaders() != null && response.getHeaders().contains(new Header(CanvasOkClient.CANVAS_API_CACHE_HEADER, CanvasOkClient.CANVAS_API_CACHE_HEADER_VALUE)); } /** * Helper methods for handling ISO 8601 strings of the following format: * "2008-03-01T13:00:00+01:00". It also supports parsing the "Z" timezone. */ /** * Transform Calendar to ISO 8601 string. */ public static String dateToString(final Date date) { if (date == null){ return null; } String formatted = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US).format(date); return formatted.substring(0, 22) + ":" + formatted.substring(22); } /** * Transform Calendar to ISO 8601 string. */ public static String dateToDayMonthYearString(Context context, final Date date) { if (date == null){ return null; } return DateHelpers.getFormattedDate(context, date); } /** * Helper methods for handling ISO 8601 strings of the following format: * "2008-03-01T13:00:00+01:00". It also supports parsing the "Z" timezone. */ /** * Transform ISO 8601 string to Calendar. */ public static Date stringToDate(final String iso8601string) { try { String s = iso8601string.replace("Z", "+00:00"); s = s.substring(0, 22) + s.substring(23); return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US).parse(s); } catch (Exception e) { return null; } } /** * parseLinkHeaderResponse parses HTTP headers to return the first, next, prev, and last urls. Used for pagination. * @param context * @param headers * @return */ public static LinkHeaders parseLinkHeaderResponse(Context context, List<Header> headers) { LinkHeaders linkHeaders = new LinkHeaders(); for (int i = 0; i < headers.size(); i++) { if ("link".equalsIgnoreCase(headers.get(i).getName())) { String[] split = headers.get(i).getValue().split(","); for (int j = 0; j < split.length; j++) { int index = split[j].indexOf(">"); String url = split[j].substring(0, index); url = url.substring(1); //Remove the domain. url = removeDomainFromUrl(url); if (split[j].contains("rel=\"next\"")) { linkHeaders.nextURL = url; } else if (split[j].contains("rel=\"prev\"")) { linkHeaders.prevURL = url; } else if (split[j].contains("rel=\"first\"")) { linkHeaders.firstURL = url; } else if (split[j].contains("rel=\"last\"")) { linkHeaders.lastURL = url; } } break; } } return linkHeaders; } public static LinkHeaders parseLinkHeaderResponse(String linkField) { LinkHeaders linkHeaders = new LinkHeaders(); if (TextUtils.isEmpty(linkField)) { return linkHeaders; } String[] split = linkField.split(","); for (int j = 0; j < split.length; j++) { int index = split[j].indexOf(">"); String url = split[j].substring(0, index); url = url.substring(1); //Remove the domain. url = removeDomainFromUrl(url); if (split[j].contains("rel=\"next\"")) { linkHeaders.nextURL = url; } else if (split[j].contains("rel=\"prev\"")) { linkHeaders.prevURL = url; } else if (split[j].contains("rel=\"first\"")) { linkHeaders.firstURL = url; } else if (split[j].contains("rel=\"last\"")) { linkHeaders.lastURL = url; } } return linkHeaders; } public static APIStatusDelegate statusDelegateWithContext(final Context context) { return new APIStatusDelegate() { @Override public void onCallbackStarted() { } @Override public void onCallbackFinished(CanvasCallback.SOURCE source) { } @Override public void onNoNetwork() { } @Override public Context getContext() { return context; } }; } /** * paramIsNull is a helper function for determining if callbacks/other objects are null; * @param callback * @param args * @return */ public static boolean paramIsNull(CanvasCallback<?> callback, Object... args) { if (callback == null || callback.getContext() == null) { logParamsNull(); return true; } return paramIsNull(args); } /** * paramIsNull is a helper function for determining if callbacks/other objects are null; * @param args * @return */ public static boolean paramIsNull(Object... args) { for (Object arg : args) { if (arg == null) { logParamsNull(); return true; } } return false; } /** * logParamsNull is a logging function helper */ private static void logParamsNull() { Log.d(APIHelpers.LOG_TAG, "One or more parameters is null"); } private static String removeProtocol(String domain){ if (domain.contains("https://")) { return domain.substring(8); } if (domain.startsWith("http://")) { return domain.substring(7); } else return domain; } /** * Check to see if the user has seen the network error message so we don't need to display it repeatedly * @param context * @return True if the user has seen the network error message, false otherwise */ public static boolean hasSeenNetworkErrorMessage(Context context) { SharedPreferences sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); return sharedPreferences.getBoolean(SHARED_PREFERENCES_DISMISSED_NETWORK_ERROR, false); } /** * Sets whether the user has seen the network error message * * @param context * @param hasSeenErrorMessage */ public static void setHasSeenNetworkErrorMessage(Context context, boolean hasSeenErrorMessage) { SharedPreferences sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPreferences.edit(); String sharedPrefsKey = SHARED_PREFERENCES_DISMISSED_NETWORK_ERROR; editor.putBoolean(sharedPrefsKey, hasSeenErrorMessage); editor.apply(); } }