package org.edx.mobile.util; import android.content.Context; import android.net.Uri; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.annotations.SerializedName; import com.google.inject.Inject; import com.google.inject.Singleton; import com.squareup.phrase.Phrase; import org.edx.mobile.BuildConfig; import org.edx.mobile.logger.Logger; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import java.util.Locale; @Singleton public class Config { private static final Logger logger = new Logger(Config.class.getName()); private JsonObject mProperties; /* Individual configuration keys */ private static final String API_HOST_URL = "API_HOST_URL"; private static final String ENVIRONMENT_DISPLAY_NAME = "ENVIRONMENT_DISPLAY_NAME"; private static final String PLATFORM_NAME = "PLATFORM_NAME"; private static final String PLATFORM_DESTINATION_NAME = "PLATFORM_DESTINATION_NAME"; private static final String FEEDBACK_EMAIL_ADDRESS = "FEEDBACK_EMAIL_ADDRESS"; private static final String OAUTH_CLIENT_ID = "OAUTH_CLIENT_ID"; private static final String SPEED_TEST_ENABLED = "SPEED_TEST_ENABLED"; private static final String APP_UPDATE_URIS = "APP_UPDATE_URIS"; private static final String ORGANIZATION_CODE = "ORGANIZATION_CODE"; /* Composite configuration keys */ private static final String COURSE_ENROLLMENT = "COURSE_ENROLLMENT"; private static final String ZERO_RATING = "ZERO_RATING"; private static final String FACEBOOK = "FACEBOOK"; private static final String GOOGLE = "GOOGLE"; private static final String TWITTER = "TWITTER"; private static final String FABRIC = "FABRIC"; private static final String NEW_RELIC = "NEW_RELIC"; private static final String SEGMENT_IO = "SEGMENT_IO"; private static final String PUSH_NOTIFICATIONS_FLAG = "PUSH_NOTIFICATIONS"; private static final String WHITE_LIST_OF_DOMAINS = "WHITE_LIST_OF_DOMAINS"; // Features private static final String USER_PROFILES_ENABLED = "USER_PROFILES_ENABLED"; private static final String DISCUSSIONS_ENABLED = "DISCUSSIONS_ENABLED"; private static final String CERTIFICATES_ENABLED = "CERTIFICATES_ENABLED"; private static final String COURSE_SHARING_ENABLED = "COURSE_SHARING_ENABLED"; private static final String BADGES_ENABLED = "BADGES_ENABLED"; private static final String END_TO_END_TEST = "END_TO_END_TEST"; private static final String NEW_LOGISTRATION_ENABLED = "NEW_LOGISTRATION_ENABLED"; private static final String DISCUSSIONS_ENABLE_PROFILE_PICTURE_PARAM = "DISCUSSIONS_ENABLE_PROFILE_PICTURE_PARAM"; private static final String REGISTRATION_ENABLED = "REGISTRATION_ENABLED"; private static final String FIREBASE_ENABLED = "FIREBASE_ENABLED"; public static class ZeroRatingConfig { @SerializedName("ENABLED") private boolean mEnabled; @SerializedName("CARRIERS") private List<String> mCarriers; @SerializedName("WHITE_LIST_OF_DOMAINS") private List<String> mWhiteListedDomains; public boolean isEnabled() { return mEnabled; } public List<String> getCarriers() { return mCarriers != null ? mCarriers : new ArrayList<String>(); } public List<String> getWhiteListedDomains() { return mWhiteListedDomains != null ? mWhiteListedDomains : new ArrayList<String>(); } } public static class EnrollmentConfig { @SerializedName("WEBVIEW") private WebViewConfig mWebViewConfig; @SerializedName("TYPE") private String mCourseEnrollmentType; public enum CourseDiscoveryType { WEBVIEW, NATIVE } @Nullable private CourseDiscoveryType getCourseDiscoveryType() { if (null == mCourseEnrollmentType) { return null; } return CourseDiscoveryType.valueOf(mCourseEnrollmentType.toUpperCase(Locale.US)); } public boolean isCourseDiscoveryEnabled() { return getCourseDiscoveryType() != null; } public boolean isExploreSubjectsEnabled() { // Explore Subjects is only supported for web course discovery, and requires a URL return isWebviewCourseDiscoveryEnabled() && null != getWebViewConfig().getExploreSubjectsUrl() && !getWebViewConfig().getExploreSubjectsUrl().isEmpty(); } public boolean isWebviewCourseDiscoveryEnabled() { return getCourseDiscoveryType() == CourseDiscoveryType.WEBVIEW; } public WebViewConfig getWebViewConfig() { return mWebViewConfig; } public String getCourseSearchUrl() { return null == mWebViewConfig ? null : mWebViewConfig.getCourseSearchUrl(); } public String getCourseInfoUrlTemplate() { return null == mWebViewConfig ? null : mWebViewConfig.getCourseInfoUrlTemplate(); } public boolean isWebCourseSearchEnabled() { return null != mWebViewConfig && mWebViewConfig.isWebCourseSearchEnabled(); } } public static class WebViewConfig { @SerializedName("COURSE_SEARCH_URL") private String mSearchUrl; @SerializedName("EXPLORE_SUBJECTS_URL") private String mExploreSubjectsUrl; @SerializedName("COURSE_INFO_URL_TEMPLATE") private String mCourseInfoUrlTemplate; @SerializedName("SEARCH_BAR_ENABLED") private boolean mSearchBarEnabled; public String getCourseSearchUrl() { return mSearchUrl; } public String getExploreSubjectsUrl() { return mExploreSubjectsUrl; } public String getCourseInfoUrlTemplate() { return mCourseInfoUrlTemplate; } public boolean isWebCourseSearchEnabled() { return mSearchBarEnabled; } } public static class FacebookConfig { @SerializedName("ENABLED") private boolean mEnabled; @SerializedName("FACEBOOK_APP_ID") private String mFacebookAppId; public FacebookConfig(boolean mEnabled, String mFacebookAppId) { this.mEnabled = mEnabled; this.mFacebookAppId = mFacebookAppId; } public FacebookConfig() { } public boolean isEnabled() { return mEnabled && !TextUtils.isEmpty(mFacebookAppId); } public String getFacebookAppId() { return mFacebookAppId; } } public static class GoogleConfig { @SerializedName("ENABLED") private boolean mEnabled; public GoogleConfig(boolean mEnabled) { this.mEnabled = mEnabled; } public GoogleConfig() { } public boolean isEnabled() { return mEnabled; } } public static class TwitterConfig { @SerializedName("HASHTAG") private String mHashTag; public String getHashTag() { return mHashTag; } } public static class FabricConfig { @SerializedName("ENABLED") private boolean mEnabled; @SerializedName("FABRIC_KEY") private String mFabricKey; @SerializedName("FABRIC_BUILD_SECRET") private String mFabricBuildSecret; public boolean isEnabled() { return mEnabled && !TextUtils.isEmpty(mFabricKey) && !TextUtils.isEmpty(mFabricBuildSecret); } public String getFabricKey() { return mFabricKey; } public String getFabricBuildSecret() { return mFabricBuildSecret; } } public static class NewRelicConfig { @SerializedName("ENABLED") private boolean mEnabled; @SerializedName("NEW_RELIC_KEY") private String mNewRelicKey; public boolean isEnabled() { return mEnabled && !TextUtils.isEmpty(mNewRelicKey); } public String getNewRelicKey() { return mNewRelicKey; } } public static class EndToEndConfig { private static final String DEFAULT_EMAIL_TEMPLATE = "test-{unique_id}@example.com"; @SerializedName("EMAIL_TEMPLATE") private String mEmailTemplate; @SerializedName("TEST_COURSE_ID") private String mTestCourseId; public String getEmailTemplate() { return TextUtils.isEmpty(mEmailTemplate) ? DEFAULT_EMAIL_TEMPLATE : mEmailTemplate; } public String getTestCourseId() { return mTestCourseId; } } public static class SegmentConfig { @SerializedName("ENABLED") private boolean mEnabled; @SerializedName("SEGMENT_IO_WRITE_KEY") private String mSegmentWriteKey; public boolean isEnabled() { return mEnabled && !TextUtils.isEmpty(mSegmentWriteKey); } public String getSegmentWriteKey() { return mSegmentWriteKey; } } public static class DomainWhiteListConfig { @SerializedName("ENABLED") private boolean mEnabled; @SerializedName("DOMAINS") private List<String> mDomains; public boolean isEnabled() { return mEnabled; } public List<String> getDomains() { return mDomains != null ? mDomains : new ArrayList<String>(); } } @Inject public Config(Context context) { try { InputStream in = context.getAssets().open("config/config.json"); JsonParser parser = new JsonParser(); JsonElement config = parser.parse(new InputStreamReader(in)); mProperties = config.getAsJsonObject(); } catch (Exception e) { mProperties = new JsonObject(); logger.error(e); } } public Config(JsonObject properties) { mProperties = properties; } private String getString(String key) { return getString(key, null); } private String getString(String key, String defaultValue) { JsonElement element = getObject(key); if (element != null) { return element.getAsString(); } else { return defaultValue; } } private boolean getBoolean(String key, boolean defaultValue) { JsonElement element = getObject(key); if (element != null) { return element.getAsBoolean(); } else { return defaultValue; } } private int getInteger(String key, int defaultValue) { JsonElement element = getObject(key); if (element != null) { return element.getAsInt(); } else { return defaultValue; } } private JsonElement getObject(String key) { return mProperties.get(key); } /// Known Configurations /// Create methods instead of just using the key names directly for a little extra flexibility /// Please keep this alphabetized public String getApiHostURL() { return getString(API_HOST_URL); } // User facing name of the platform like "edX" public String getPlatformName() { return getString(PLATFORM_NAME); } // User facing name of the platform as a destination like "edx.org" public String getPlatformDestinationName() { return getString(PLATFORM_DESTINATION_NAME); } // Debug name of the current configuration public String getEnvironmentDisplayName() { return getString(ENVIRONMENT_DISPLAY_NAME); } public String getFeedbackEmailAddress() { return getString(FEEDBACK_EMAIL_ADDRESS); } public String getOAuthClientId() { return getString(OAUTH_CLIENT_ID); } /** * @return A list of URIs for updating the app, or an empty list if none are available. */ @NonNull public List<Uri> getAppUpdateUris() { //noinspection unchecked final List<String> uriStrings = getObjectOrNewInstance(APP_UPDATE_URIS, ArrayList.class); final List<Uri> uris = new ArrayList<>(uriStrings.size()); for (final String uriString : uriStrings) { if (uriString != null) { // Replace the 'application_id' token with the actual application ID. uris.add(Uri.parse(Phrase.from(uriString) .put("application_id", BuildConfig.APPLICATION_ID) .format() .toString())); } } return uris; } public String getOrganizationCode() { return getString(ORGANIZATION_CODE); } public boolean isNotificationEnabled() { return getBoolean(PUSH_NOTIFICATIONS_FLAG, false); } public boolean isNewLogistrationEnabled() { return getBoolean(NEW_LOGISTRATION_ENABLED, false); } public boolean isDiscussionProfilePicturesEnabled() { return getBoolean(DISCUSSIONS_ENABLE_PROFILE_PICTURE_PARAM, false); } public boolean isRegistrationEnabled() { return getBoolean(REGISTRATION_ENABLED, true); } /** * Empty or no config returns false. * Otherwise, returns the value from the config. * * @return */ public boolean isSpeedTestEnabled() { return getBoolean(SPEED_TEST_ENABLED, false); } public boolean isUserProfilesEnabled() { return getBoolean(USER_PROFILES_ENABLED, false); } public boolean isBadgesEnabled() { return getBoolean(BADGES_ENABLED, false); } public boolean isDiscussionsEnabled() { return getBoolean(DISCUSSIONS_ENABLED, false); } public boolean areCertificateLinksEnabled() { return getBoolean(CERTIFICATES_ENABLED, false); } public boolean isCourseSharingEnabled() { return getBoolean(COURSE_SHARING_ENABLED, false); } @NonNull public EnrollmentConfig getCourseDiscoveryConfig() { return getObjectOrNewInstance(COURSE_ENROLLMENT, EnrollmentConfig.class); } @NonNull public ZeroRatingConfig getZeroRatingConfig() { return getObjectOrNewInstance(ZERO_RATING, ZeroRatingConfig.class); } @NonNull public FacebookConfig getFacebookConfig() { return getObjectOrNewInstance(FACEBOOK, FacebookConfig.class); } @NonNull public GoogleConfig getGoogleConfig() { return getObjectOrNewInstance(GOOGLE, GoogleConfig.class); } @NonNull public TwitterConfig getTwitterConfig() { return getObjectOrNewInstance(TWITTER, TwitterConfig.class); } @NonNull public FabricConfig getFabricConfig() { return getObjectOrNewInstance(FABRIC, FabricConfig.class); } @NonNull public NewRelicConfig getNewRelicConfig() { return getObjectOrNewInstance(NEW_RELIC, NewRelicConfig.class); } @NonNull public SegmentConfig getSegmentConfig() { return getObjectOrNewInstance(SEGMENT_IO, SegmentConfig.class); } @NonNull public EndToEndConfig getEndToEndConfig() { return getObjectOrNewInstance(END_TO_END_TEST, EndToEndConfig.class); } @NonNull private <T> T getObjectOrNewInstance(@NonNull String key, @NonNull Class<T> cls) { JsonElement element = getObject(key); if (element != null) { Gson gson = new Gson(); return gson.fromJson(element, cls); } else { try { return cls.newInstance(); } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } } }