/** * Copyright 2012 Neurowork Consulting S.L. * * This file is part of eMobc. * * ApplicationData.java * eMobc Android Framework * * eMobc is free software: you can redistribute it and/or modify * it under the terms of the Affero GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * eMobc is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the Affero GNU General Public License * along with eMobc. If not, see <http://www.gnu.org/licenses/>. * */ package com.emobc.android; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import android.app.Activity; import android.content.Context; import android.graphics.drawable.Drawable; import android.support.v4.util.LruCache; import android.util.Log; import com.emobc.android.activities.generators.ActivityGenerator; import com.emobc.android.activities.generators.ActivityGeneratorFactory; import com.emobc.android.activities.generators.CoverActivityGenerator; import com.emobc.android.levels.AppLevel; import com.emobc.android.levels.AppLevelData; import com.emobc.android.levels.AppLevelDataItem; import com.emobc.android.levels.impl.BannerDataItem; import com.emobc.android.levels.impl.ServerPushDataItem; import com.emobc.android.menu.Menu; import com.emobc.android.menu.parse.MenuParser; import com.emobc.android.parse.CoverParser; import com.emobc.android.parse.ParseUtils; import com.emobc.android.profiling.Profile; import com.emobc.android.themes.FormatStyle; import com.emobc.android.themes.ActivityTypeStyle; import com.emobc.android.themes.LevelStyle; import com.emobc.android.themes.parse.FormatParser; import com.emobc.android.themes.parse.StyleParser; import com.emobc.android.themes.parse.StyleResult; import com.emobc.android.utils.InvalidFileException; /** * Singleton is used for read and manage the app.xml file * From this class you can access to application Levels. * <p>ApplicationData operates in local or remove model.</p> * <p><strong>Local mode</strong> seeks for * Level Ids only in memory tables. If a Level Id is not found, a <code>null</code> * {@link AppLevel} is returned.</p> * <p><strong>Remote mode</strong> seeks for Level Ids in memory first and if it not present then * seeks for the Level Id in the <i>Remote Application File</i>. * </p> * @author Jorge E. Villaverde * @author Jonatan Alcocer Luna * @version 0.1 * @sine 1.0 */ public class ApplicationData { private static final String TAG = "ApplicationData"; private static final String _APP_DATA_FILE_NAME_ = "app.xml"; public static final String NEXT_LEVEL_TAG = "_NEXT_LEVEL_"; public static final String IS_ENTRY_POINT_TAG = "_IS_ENTRY_POINT_"; // public static final String IS_SIDE_MENU_TAG = "_IS_SIDE_MENU_"; // The ID of your application that you received from PayPal private static final String PAYPAL_APP_ID = "APP-80W284485P519543T"; public static final int SEARCH_LIMIT = 20; public static final String EMOBC_LEVEL_ID = "emobc"; private String title; private String coverFileName; private String stylesFileName; private String formatsFileName; private String profileFileName; private NextLevel entryPoint; // Menues private String topMenu; private String bottomMenu; private String contextMenu; private String sideMenu; private String rotation; private BannerDataItem banner; private ServerPushDataItem serverPush; private List<AppLevel> levels = new ArrayList<AppLevel>(); private Map<String, AppLevel> levelMap = new HashMap<String, AppLevel>(); private StyleResult styleResult = null; private Map<String, FormatStyle> formatStyleMap = null; private String payPalRecipient; private String payPalApplicationId = PAYPAL_APP_ID; private static ApplicationData instance = null; /** * 6 MiB Cache Size */ private static final int MAX_CACHE_SIZE = 6 * 1024 * 1024; private LruCache<String, Drawable> cache = null; private Profile profile; /** * If <tt>true</tt>, {@link AppLevel} could be read from a remote * location defined in {@link ApplicationData#removeApplicationFileUrl}. */ private final boolean remote; /** * URL of the Remote Application File. */ private final String remoteApplicationFileUrl; /** * Menu Level Map * LevelId -> Menu * if (levelId == null) -> General Menu */ private Map<String, Menu> topMenuLevelMap = null; private Map<String, Menu> bottomMenuLevelMap = null; private Map<String, Menu> contextMenuLevelMap = null; private Map<String, Menu> sideMenuLevelMap = null; /** * Default Constructor. * <p>Used for Local Application File</p> */ public ApplicationData (){ this(null); } /** * Remote Application File Constructor. * Set the application Data in remote mode. * @param remoteApplicationFileUrl */ public ApplicationData(String remoteApplicationFileUrl){ super(); this.remoteApplicationFileUrl = remoteApplicationFileUrl; this.remote = (remoteApplicationFileUrl != null && !remoteApplicationFileUrl.isEmpty()); loadDefaultLevels(); } /** * Load default levels */ private void loadDefaultLevels() { if(levels == null) levels = new ArrayList<AppLevel>(); // // AppLevel emobcLevel = new AppLevel(0); // emobcLevel.setId(EMOBC_LEVEL_ID); // emobcLevel.setActivityType(ActivityType.PROFILE_ACTIVITY); // emobcLevel.setTitle("Profile"); // // addLevel(emobcLevel, false); } /** * Returns an ApplicationData with data obtained after parsing app.xml file * @param context * @return ApplicationData */ public static ApplicationData readApplicationData(Context context) throws InvalidFileException { return readApplicationData(context, Locale.getDefault()); } /** * Returns an ApplicationData with data obtained after parsing app.xml file * @param context * @param locale * @return ApplicationData */ public static ApplicationData readApplicationData(Context context, Locale locale) throws InvalidFileException { if(instance == null){ instance = ParseUtils.parseApplicationData(context, locale, _APP_DATA_FILE_NAME_); } return instance; } /** * Check the app.xml file again and add the new data * @param context * @param str * @return */ public static ApplicationData mergeAppDataFromString(Context context, String str) { if(instance == null){ throw new IllegalStateException("Application Data instance is null"); } ApplicationData appData = ParseUtils.parseApplicationData(context, str); if(appData != null){ instance.merge(appData); } return instance; } /** * Add new levels to list * @param appData */ public void merge(ApplicationData appData) { for(AppLevel level : appData.getLevels()){ levels.add(level); if(level.getId() != null){ levelMap.put(level.getId(), level); } } } /** * Returns the AppLevelDataItem associated to nextLevel * @param context * @param nextLevel * @return */ public AppLevelDataItem getDataItem(Context context, NextLevel nextLevel){ AppLevel appLevel = getNextAppLevel(nextLevel, context); AppLevelData data = appLevel.getData(context); AppLevelDataItem item = data.findByNextLevel(nextLevel); return item; } /** * Returns an ActivityGenerator with data obtained after parsing cover file * @param context * @return ActivityGenerator */ public ActivityGenerator getAppCoverData(Context context) { return getAppCoverData(context, Locale.getDefault()); } /** * Returns an ActivityGenerator with data obtained after parsing cover file * @param context * @param locale * @return CoverActivityGenerator */ public CoverActivityGenerator getAppCoverData(Context context, Locale locale) { CoverParser parser = new CoverParser(ParseUtils.createXpp( context, locale, coverFileName, false)); return parser.parse(); } /** * Returns the AppLevel associated with a nextLevel * @param NextLevel nextLevel * @return AppLevel */ public AppLevel getNextAppLevel(NextLevel nextLevel, Context context){ if(!isRemote()) return localNextLevel(nextLevel); return remoteNextLevel(nextLevel, context); } private AppLevel remoteNextLevel(NextLevel nextLevel, Context context) { AppLevel localAppLevel = localNextLevel(nextLevel); if(localAppLevel != null) return localAppLevel; ApplicationData appData = ParseUtils.parseApplicationData(context, remoteApplicationFileUrl); merge(appData); return localNextLevel(nextLevel); } private AppLevel localNextLevel(NextLevel nextLevel) { AppLevel defaultLevel = checkDefaultLevel(nextLevel); if(defaultLevel != null) return defaultLevel; String levelId = nextLevel.getLevelId(); if(levelId != null){ return levelMap.get(levelId); } if(nextLevel.getLevelNumber() > NextLevel.NO_LEVEL && nextLevel.getLevelNumber() < levels.size()) return levels.get(nextLevel.getLevelNumber()); return null; } private AppLevel checkDefaultLevel(NextLevel nextLevel) { if(NextLevel.COVER_NEXT_LEVEL.equals(nextLevel)){ return AppLevel.COVER_APP_LEVEL; } if(NextLevel.PROFILE_NEXT_LEVEL.equals(nextLevel)){ return AppLevel.PROFILE_APP_LEVEL; } if(NextLevel.SEARCH_NEXT_LEVEL.equals(nextLevel)){ return AppLevel.SEARCH_APP_LEVEL; } return null; } /** * Returns an ActivityGenerator (if exists), of a specific type * @param Context context * @param NextLevel nextLevel * @return ActivityGenerator */ public ActivityGenerator getFromNextLevel(Activity activity, NextLevel nextLevel) { try { return ActivityGeneratorFactory.createActivityGenerator(activity, nextLevel); } catch (InvalidFileException e) { } return null; } /** * Returns all images associated to one level. * @param context * @return */ public List<SearchResult> findAllLevelsImages(Context context){ return findAllLevelsImages(context, Locale.getDefault()); } /** * Returns a list with all levelImages * @param context * @param locale * @return */ public List<SearchResult> findAllLevelsImages(Context context, Locale locale){ List<SearchResult> ret = new ArrayList<SearchResult>(); for(AppLevel level : levels){ ret.addAll(level.getAllImages(context, locale)); } return ret; } /** * Returns a list with all levelImages * @param context * @param text * @return */ public List<SearchResult> findWidthText(Context context, final String text){ return findWidthText(context, Locale.getDefault(), text); } /** * Returns a list with all search results * @param context * @param locale * @param text * @return */ public List<SearchResult> findWidthText(Context context, Locale locale, final String text){ List<SearchResult> ret = new ArrayList<SearchResult>(); for(AppLevel level : levels){ if(ret.size() < SEARCH_LIMIT) ret.addAll(level.findWidthText(context, locale, text)); } return ret; } /** * Returns a list with all geo-referrals * @param context * @return */ public List<SearchResult> findAllGeoref(Context context){ return findAllGeoref(context, Locale.getDefault()); } /** * Returns a list with all geo-referrals * @param context * @param locale * @return */ public List<SearchResult> findAllGeoref(Context context, Locale locale){ List<SearchResult> ret = new ArrayList<SearchResult>(); for(AppLevel level : levels){ ret.addAll(level.findAllGeoref(context, locale)); } return ret; } public void setTitle(String title) { this.title = title; } public String getTitle() { return title; } public void setCoverFileName(String coverFileName) { this.coverFileName = coverFileName; } public String getCoverFileName() { return coverFileName; } public String getStylesFileName() { return stylesFileName; } public void setStylesFileName(String stylesFileName) { this.stylesFileName = stylesFileName; } public String getFormatsFileName() { return formatsFileName; } public void setFormatsFileName(String formatsFileName) { this.formatsFileName = formatsFileName; } public NextLevel getEntryPoint() { return entryPoint; } public void setEntryPoint(NextLevel entryPoint) { this.entryPoint = entryPoint; } public String getRotation() { return rotation; } public void setRotation(String rotation) { this.rotation = rotation; } public void setLevels(List<AppLevel> levels) { this.levels = levels; levelMap.clear(); loadDefaultLevels(); for(AppLevel level : levels){ if(level.getId() != null) levelMap.put(level.getId(), level); } } public List<AppLevel> getLevels() { return levels; } public Map<ActivityType, ActivityTypeStyle> getActivityTypeStyleTypeMap(Context context) { if(styleResult == null){ // Create Parser StyleParser styleParser = new StyleParser(ParseUtils.createXpp( context, Locale.getDefault(), this.stylesFileName, false)); styleResult = styleParser.parse(); } return styleResult.getActivityTypeStyleMap(); } public Map<String, LevelStyle> getLevelStyleTypeMap(Context context) { if(styleResult == null){ // Create Parser StyleParser styleParser = new StyleParser(ParseUtils.createXpp( context, Locale.getDefault(), this.stylesFileName, false)); styleResult = styleParser.parse(); } return styleResult.getLevelStyleMap(); } public Map<String, FormatStyle> getFormatStyleMap(Context context) { if(formatStyleMap == null){ // Create Parser FormatParser formatParser = new FormatParser(ParseUtils.createXpp( context, Locale.getDefault(), this.formatsFileName, false)); this.formatStyleMap = formatParser.parse(); } return formatStyleMap; } public void initStylesAndFormats(Context context){ if(styleResult == null){ // Create Parser StyleParser styleParser = new StyleParser(ParseUtils.createXpp( context, Locale.getDefault(), this.stylesFileName, false)); styleResult = styleParser.parse(); } Log.i(TAG, "Styles Initialized"); if(formatStyleMap == null){ // Create Parser FormatParser formatParser = new FormatParser(ParseUtils.createXpp( context, Locale.getDefault(), this.formatsFileName, false)); this.formatStyleMap = formatParser.parse(); } Log.i(TAG, "Formats Initialized"); } public ServerPushDataItem getServerPush() { return serverPush; } public void setServerPush(ServerPushDataItem serverPush) { this.serverPush = serverPush; } public BannerDataItem getBanner() { return banner; } public void setBanner(BannerDataItem banner) { this.banner = banner; } public String getProfileFileName() { return profileFileName; } public void setProfileFileName(String profileFileName) { this.profileFileName = profileFileName; } public Profile getProfile() { return profile; } public void setProfile(Profile profile) { this.profile = profile; } public LruCache<String, Drawable> getCache() { if(this.cache == null) this.cache = new LruCache<String, Drawable>(MAX_CACHE_SIZE); return cache; } public void addLevels(List<AppLevel> addingLevles) { for(AppLevel level : addingLevles){ addLevel(level); } } public void addLevel(AppLevel level) { addLevel(level, true); } /** * Adds a new Level if the levelId is valid or if * <code>checkDefaults</code>is false. * @param level * @param checkDefaults */ private void addLevel(AppLevel level, boolean checkDefaults) { if(!checkDefaults || validLevelId(level.getId())){ levels.add(level); if(level.getId() != null) levelMap.put(level.getId(), level); }else{ Log.w("ApplicationData: addLevel", "Invalid Level Id: " + level.getId()); } } private boolean validLevelId(String levelId) { if(EMOBC_LEVEL_ID.equals(levelId)) return false; return true; } public ActivityTypeStyle getActivityTypeStyle(ActivityType activityType) { if(styleResult != null) return styleResult.getActivityTypeStyleMap().get(activityType); return null; } public LevelStyle getLevelStyle(String levelId) { if(styleResult != null) return styleResult.getLevelStyleMap().get(levelId); return null; } public boolean isRemote() { return remote; } // ---------------- Menues -------------------------- public String getRemoteApplicationFileUrl() { return remoteApplicationFileUrl; } public void setTopMenuFileName(String topMenu) { this.topMenu = topMenu; } public void setBottomMenuFileName(String bottomMenu) { this.bottomMenu = bottomMenu; } public void setContextMenuFileName(String contextMenu) { this.contextMenu = contextMenu; } public void setSideMenuFileName(String sideMenu) { this.sideMenu = sideMenu; } private Map<String, Menu> loadMenuLevelMap(String xmlMenuFileName, Context context) { Map<String, Menu> ret = new HashMap<String, Menu>(); if(xmlMenuFileName != null && !xmlMenuFileName.isEmpty()){ // Create Menu Parser MenuParser menuParser = new MenuParser(ParseUtils.createXpp( context, Locale.getDefault(), xmlMenuFileName, false)); List<Menu> menues = menuParser.parse(); for(Menu menu : menues){ ret.put(menu.getLevelId(), menu); } } return ret; } /** * Alex: Los menus no tienen que agregar sobre el level. * Si tiene defecto usa el de defecto y si tiene para su level solo el de su level (no suma con defecto). * Ej en la portada solo se debe de ver en el top el back e info. * @param levelId * @param menuLevelMap * @return */ private static Menu buildLevelMenuFromMap(NextLevel nextLevel, Map<String, Menu> menuLevelMap){ String levelId = nextLevel.getLevelId(); Menu generalMenu = menuLevelMap.get(null); Menu menu = menuLevelMap.get(levelId); if(menu == null) return generalMenu; return menu; // Menu retMenu = new Menu(menu.getLevelId()); // if(generalMenu != null) // retMenu.addMenuItems(generalMenu.getItems()); // // retMenu.addMenuItems(menu.getItems()); // return retMenu; } public Menu getTopMenu(NextLevel nextLevel, Context context){ if(this.topMenuLevelMap == null) this.topMenuLevelMap = loadMenuLevelMap(this.topMenu, context); return buildLevelMenuFromMap(nextLevel, topMenuLevelMap); } public Menu getBottomMenu(NextLevel nextLevel, Context context){ if(this.bottomMenuLevelMap == null) this.bottomMenuLevelMap = loadMenuLevelMap(this.bottomMenu, context); return buildLevelMenuFromMap(nextLevel, bottomMenuLevelMap); } public Menu getContextMenu(NextLevel nextLevel, Context context){ if(this.contextMenuLevelMap == null) this.contextMenuLevelMap = loadMenuLevelMap(this.contextMenu, context); return buildLevelMenuFromMap(nextLevel, contextMenuLevelMap); } public Menu getSideMenu(NextLevel nextLevel, Context context){ if(this.sideMenuLevelMap == null) this.sideMenuLevelMap = loadMenuLevelMap(this.sideMenu, context); return buildLevelMenuFromMap(nextLevel, sideMenuLevelMap); } // PayPal public String getPayPalRecipient() { return payPalRecipient; } public void setPayPalRecipient(String payPalRecipient) { this.payPalRecipient = payPalRecipient; } public String getPayPalApplicationId() { return payPalApplicationId; } public void setPayPalApplicationId(String payPalApplicationId) { this.payPalApplicationId = payPalApplicationId; } }