/* * Funambol is a mobile platform developed by Funambol, Inc. * Copyright (C) 2009 Funambol, Inc. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU Affero General Public License version 3 as published by * the Free Software Foundation with the addition of the following permission * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED * WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE * WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. * * This program 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 GNU Affero General Public License * along with this program; if not, see http://www.gnu.org/licenses or write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA. * * You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite * 305, Redwood City, CA 94063, USA, or at email address info@funambol.com. * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU Affero General Public License version 3. * * In accordance with Section 7(b) of the GNU Affero General Public License * version 3, these Appropriate Legal Notices must retain the display of the * "Powered by Funambol" logo. If the display of the logo is not reasonably * feasible for technical reasons, the Appropriate Legal Notices must display * the words "Powered by Funambol". */ package de.chbosync.android.syncmlclient; import java.util.Enumeration; import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AccountManagerCallback; import android.accounts.AccountManagerFuture; import android.app.Activity; import android.app.Application; import android.content.Context; import android.net.wifi.WifiManager; import android.net.wifi.WifiManager.WifiLock; import android.os.Build; import android.os.Bundle; import android.os.Environment; import com.funambol.client.controller.Controller; import com.funambol.client.controller.DevSettingsScreenController; import com.funambol.client.controller.UISyncSourceController; import com.funambol.client.customization.Customization; import com.funambol.client.localization.Localization; import com.funambol.client.source.AppSyncSource; import com.funambol.platform.NetworkStatus; import com.funambol.storage.StringKeyValueStoreFactory; import com.funambol.sync.SyncSource; import com.funambol.util.AndroidLogAppender; import com.funambol.util.FileAppender; import com.funambol.util.Log; import com.funambol.util.MultipleAppender; import de.chbosync.android.syncmlclient.R; import de.chbosync.android.syncmlclient.activities.AndroidActivitiesFactory; import de.chbosync.android.syncmlclient.activities.AndroidDisplayManager; import de.chbosync.android.syncmlclient.controller.AndroidController; import de.chbosync.android.syncmlclient.controller.AndroidHomeScreenController; import de.chbosync.android.syncmlclient.controller.AndroidSettingsScreenController; /** * This class is used to initialize the entire application. It can be invoked by * a starting activity or by a service. Once the application got initialized * once, any call to init has no effect. The Singleton instance is realized, so * this class reference can be got using the static related getter method. */ public class AppInitializer { private static final String TAG_LOG = "AppInitializer"; private Localization localization; private AndroidConfiguration configuration; private Customization customization; private AndroidAppSyncSourceManager appSyncSourceManager; private AndroidController controller; private Context context; private WifiLock wifiLock = null; private SyncLock syncLock; private boolean initialized = false; /** * @param app the application reference */ public AppInitializer(Application app) { this.context = app.getApplicationContext(); } /** * AndroidController instance Getter method * @return AndroidController the instance of AndroidController object * initialized by this class */ public AndroidController getController() { return controller; } /** * AndroidLocalization instance Getter method * @return AndroidLocalization the instance of AndroidLocalization object * used by this class */ public Localization getLocalization() { return localization; } /** * AndroidConfiguration instance Getter method * @return AndroidConfiguration the instance of AndroidConfiguration object * used by this class */ public AndroidConfiguration getConfiguration() { return configuration; } /** * AndroidCustomization instance Getter method * @return AndroidCustomization the instance of AndroidCustomization object * used by this class */ public Customization getCustomization() { return customization; } public SyncLock getSyncLock() { return syncLock; } /** * AndroidAppSyncSourceManager instance Getter method * @return AndroidAppSyncSourceManager the instance of * AndroidAppSyncSourceManager initialized by this class */ public AndroidAppSyncSourceManager getAppSyncSourceManager() { return appSyncSourceManager; } private void initLog() { MultipleAppender ma = new MultipleAppender(); if (isFileTracingAllowed()) { String fileName = "synclog.txt"; String userDir; if (AndroidUtils.isSDCardMounted()) { userDir = Environment.getExternalStorageDirectory().getPath() + System.getProperty("file.separator"); } else { userDir = context.getFilesDir().getAbsolutePath() + System.getProperty("file.separator"); } FileAppender fileAppender = new FileAppender(userDir, fileName); fileAppender.setLogContentType(!AndroidUtils.isSDCardMounted()); fileAppender.setMaxFileSize(256*1024); // Set 256KB log size ma.addAppender(fileAppender); if (Log.isLoggable(Log.INFO)) { Log.info(TAG_LOG, "Memory card present: " + AndroidUtils.isSDCardMounted()); Log.info(TAG_LOG, "Log file created into: " + userDir + fileName); } } // If we are running in the emulator, we also use the AndroidLogger if (AndroidUtils.isAndroidEmulator() || "debug".equals(BuildInfo.MODE)) { // This is an emulator, or a debug build AndroidLogAppender androidLogAppender = new AndroidLogAppender("SyncMLClient"); ma.addAppender(androidLogAppender); } Log.initLog(ma, Log.TRACE); //for customer who wants to have log locked on specified level if(customization.lockLogLevel()){ Log.lockLogLevel(customization.getLockedLogLevel()); } } /** * Whether a trace/log file may be written. Requires Android permission, which is currently not granted * @return */ private boolean isFileTracingAllowed() { return false; } /** * Initialize the application. Call this method to have the main application * objects fully initialized. * Call this method more than once per instance produces no effect. * This realize the same effect of init(Activity activity) but passing a * null value and generating the condition to not create the funambol * account. Use it carefully. */ public synchronized void init() { init(null); } /** * Initialize the application. Call this method to have the main application * objects fully initialized. Call this method more than once per instance * produces no effect. * * @param Activity is the Activity that is used to trigger the account * creation if it doesn't exist. Pass null if you don't want to create the * account. */ public synchronized void init(Activity activity) { if (initialized) { if(configuration.getCredentialsCheckPending()) { // Show the login screen if the credential check is still pending initAccount(activity); } return; } // Init all the Controller components initController(); // Init account information initAccount(activity); // Init the SyncLock syncLock = new SyncLock(); // Reset the proper log level Log.setLogLevel(configuration.getLogLevel()); // Init the wifi lock if we need one initWifiLock(); initialized = true; } /** * Initializes the controller of this application. this represents the core * of the initialization logic; in particular: * - Init the log system * - Set our own contacts activity as preferred * - Try to create all the necessary sources * - Create the home screen controller * - Set the HomeScreenController reference to the Controller */ public synchronized void initController() { boolean isControllerNull = controller == null; if(isControllerNull) { customization = AndroidCustomization.getInstance(); // Init the log system initLog(); localization = AndroidLocalization.getInstance(context); appSyncSourceManager = AndroidAppSyncSourceManager.getInstance(customization, localization, context); configuration = AndroidConfiguration.getInstance(context, customization, appSyncSourceManager); configuration.load(); } controller = AndroidController.getInstance(App.i(), new AndroidActivitiesFactory(), configuration, customization, localization, appSyncSourceManager); configuration.setController(controller); if(isControllerNull) { // Initialize the StringKeyValueStoreFactory StringKeyValueStoreFactory.getInstance().init(context, ((AndroidCustomization)customization).getFunambolSQLiteDbName()); // Register all the necessary sources Enumeration<Integer> sources = this.customization.getAvailableSources(); while(sources.hasMoreElements()) { Integer appSourceId = sources.nextElement(); try { AppSyncSource appSource = appSyncSourceManager.setupSource(appSourceId.intValue(), configuration); // Create the sync source controller UISyncSourceController itemController = appSource.createUISyncSourceController(); appSource.setUISyncSourceController(itemController); itemController.init(customization, localization, appSyncSourceManager, controller, appSource); SyncSource source = appSource.getSyncSource(); source.setListener(itemController); appSyncSourceManager.registerSource(appSource); } catch (Exception e) { Log.error(TAG_LOG, "Cannot setup source: " + appSourceId, e); } } } NetworkStatus netStatus = new NetworkStatus(context); AndroidHomeScreenController homeScreenController = new AndroidHomeScreenController(context, controller, null, netStatus); controller.setHomeScreenController(homeScreenController); AndroidSettingsScreenController settingsScreenController = new AndroidSettingsScreenController(context, controller); controller.setSettingsScreenController(settingsScreenController); DevSettingsScreenController devSettingsScreenController = new DevSettingsScreenController(controller, null); controller.setDevSettingsScreenController(devSettingsScreenController); initialized = true; } /** * Acquires a lock on wifi connection if it is not already locked */ public void acquireWiFiLock() { if (wifiLock == null) { WifiManager wm = (WifiManager) context.getSystemService (Context.WIFI_SERVICE); // WIFI_FULL_MODE is bugged, couldn't work with some devices :( // http://www.mail-archive.com/android-developers@googlegroups.com/msg145553.html wifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL, "Funambol sync"); } if (!wifiLock.isHeld()) { wifiLock.setReferenceCounted(false); wifiLock.acquire(); if (Log.isLoggable(Log.INFO)) { Log.info(TAG_LOG, "wifi lock=" + wifiLock.toString()); } } } /** * Releases a lock on wifi, only if it's already locked and * if the bandwidth saver feature is not enabled. * * @param requestComesFromSync True if the release request comes from a * sync. In this case, the lock is released only if the bandwidth * option is disabled. If false, releases the wifi without further checks */ public void releaseWiFiLock(boolean requestComesFromSync) { if (wifiLock != null && wifiLock.isHeld()) { if (!requestComesFromSync || (requestComesFromSync && !configuration.getBandwidthSaverActivated())) { if (Log.isLoggable(Log.INFO)) { Log.info(TAG_LOG, "Releasing wifi lock"); } wifiLock.release(); } } } private void initWifiLock() { if (configuration.getBandwidthSaverActivated()) { acquireWiFiLock(); } } private void initAccount(Activity activity) { Account account = AndroidController.getNativeAccount(); // Do nothing if the request doesn't come from an activity if(activity == null) { return; } // Check if there is not funambol account if(account == null) { if (Log.isLoggable(Log.DEBUG)) { Log.debug(TAG_LOG, "Account not found, create a default one"); } // Create the account through our account authenticator AccountManager am = AccountManager.get(context); am.addAccount(context.getString(R.string.account_type), null, null, null, activity, new AccountManagerCallback<Bundle>() { public void run(AccountManagerFuture<Bundle> result) { try { // Get the authenticator result, it is blocking until the // account authenticator completes result.getResult(); if (Log.isLoggable(Log.DEBUG)) { Log.debug(TAG_LOG, "Account created"); } } catch (Exception e) { Log.error(TAG_LOG, "Exception during account creation: ", e); } } }, null); } else { if (Log.isLoggable(Log.DEBUG)) { Log.debug(TAG_LOG, "Account already defined"); } // Show the login screen if credentials check is pending and // if the init request comes from the main activity if(configuration.getCredentialsCheckPending()) { try { ((AndroidDisplayManager)controller.getDisplayManager()) .showScreen(context, Controller.LOGIN_SCREEN_ID, null); } catch(Exception ex) { Log.error(TAG_LOG, "Cannot show login screen", ex); } } } } }