/******************************************************************************* * Code contributed to the webinos project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Copyright 2011-2012 Paddy Byers * ******************************************************************************/ package org.webinos.android.app.platform; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.webinos.android.R; import org.webinos.android.app.pzp.PzpService; import org.webinos.android.util.AssetUtils; import org.webinos.android.util.ModuleUtils; import org.webinos.android.util.ModuleUtils.ModuleType; import android.app.Activity; import android.app.ProgressDialog; import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.res.AssetManager; import android.net.Uri; import android.os.AsyncTask; import android.os.IBinder; import android.util.Log; public class PlatformInit extends Service { public static final String ACTION_POSTINSTALL = "org.webinos.android.app.POSTINSTALL"; private static String ACTION_POSTINSTALL_COMPLETE = "org.webinos.installer.POSTINSTALL_COMPLETE"; private static final String TAG = "org.webinos.android.app.platform.PlatformInit"; private static final String MODULE_PATH = "modules"; /** * Listener for asynchronous indications of service availability */ interface PlatformServiceListener { public void onServiceAvailable(PlatformInit service); } /** * Listeners waiting for service availability */ private static List<PlatformServiceListener> serviceListeners = new ArrayList<PlatformServiceListener>(); /** * The singleton service */ private static PlatformInit theService; /******************* * public API *******************/ /** * Synchronously obtain the service instance, if it already exists. * This will not trigger the service being started; only use in contexts * where it is known that the service will already have been started. * @return the service instance if available */ public static synchronized PlatformInit getService() { if(theService == null || theService.initialised) return theService; return null; } /** * Get the service instance, registering a callback for the case that * the service is not currently avaiable. If not available, the service * will be started. * @param ctx a context to use to start the service if required. * @param listener a listener to call with the service instance, in the case that * the service was not available already. If the service was available at the time * the call was made, then the instance will be returned directly and the listener * will NOT be called. * @return the service instance if available; or null otherwise */ public static PlatformInit getService(Context ctx, PlatformServiceListener listener) { PlatformInit foundService = null; /* synchronously check, and add listener if necessary */ synchronized(PlatformInit.class) { if(theService == null || !theService.initialised) { if(listener != null) serviceListeners.add(listener); } else { foundService = theService; } } /* start service if necessary */ if(theService == null) { Config.init(ctx); ctx.startService(new Intent(ctx, PlatformInit.class)); } /* return synchronously obtained instance */ return foundService; } public static boolean onInit(final Context context, final Runnable handler) { if(getService() != null) return true; if(handler == null) return (getService(context, null) != null); class Waiter implements PlatformServiceListener { private ProgressDialog progressDialog; private boolean blocked; private void onBlock() { blocked = true; if(context instanceof Activity) { ((Activity)context).runOnUiThread(new Runnable() { @Override public void run() { /* put up progress dialog until the service is available */ progressDialog = new ProgressDialog(context); progressDialog.setCancelable(true); progressDialog.setMessage(context.getString(R.string.initialising_runtime)); progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); progressDialog.setIndeterminate(true); progressDialog.show(); } }); } } private void onUnblock() { if(context instanceof Activity) { ((Activity)context).runOnUiThread(new Runnable() { @Override public void run() { if(progressDialog != null) { progressDialog.dismiss(); progressDialog = null; } if(blocked) handler.run(); } }); return; } if(blocked) handler.run(); } @Override public void onServiceAvailable(PlatformInit service) { onUnblock(); } } Waiter waiter = new Waiter(); getService(context, waiter); boolean result; synchronized(PlatformInit.class) { result = (getService() != null); if(!result) waiter.onBlock(); } return result; } /******************* * lifecycle *******************/ @Override public int onStartCommand(Intent intent, int flags, int startId) { final boolean force = (intent.getAction() == ACTION_POSTINSTALL); /* synchronously set ourselves as the singleton instance, and notify * and pending listeners */ synchronized(PlatformInit.class) { if(theService == null) { theService = this; (new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { installModuleDependencies(force); synchronized(PlatformInit.class) { if(serviceListeners != null) { for(PlatformServiceListener listener : serviceListeners) listener.onServiceAvailable(theService); serviceListeners = null; } } return null; }}).execute(); } } return START_NOT_STICKY; } @Override public void onCreate() { super.onCreate(); Config.init(this); startService(new Intent(this, PzpService.class)); } /******************* * private state *******************/ private boolean initialised; /** * Install all module dependencies located in assets/modules * @param ctx the service context * @param force force update, even if module is already present */ private void installModuleDependencies(boolean force) { if(initialised && !force) return; AssetManager mgr = getAssets(); try { String[] modules = mgr.list(MODULE_PATH); if (modules != null) { for(String module : modules) { Log.v(TAG, "Checking module: " + module); checkModule(module, force); sendBroadcast(new Intent("org.webinos.android.app.wrt.ui.PROGRESS")); } } } catch (IOException e) { Log.v(TAG, "Unable to get assets in " + MODULE_PATH); } initialised = true; /* broadcast intent to indicate we're finished */ Intent postInstallIntent = new Intent(ACTION_POSTINSTALL_COMPLETE); postInstallIntent.setData(Uri.parse("package://org.webinos.android.app")); sendBroadcast(postInstallIntent); } private void checkModule(String asset, boolean force) { ModuleType modType = ModuleUtils.guessModuleType(asset); String module = ModuleUtils.guessModuleName(asset, modType); File installLocation = ModuleUtils.getModuleFile(module, modType); if(installLocation.exists()) { File pkg = new File(getPackageResourcePath()); if(force || pkg.exists() && installLocation.lastModified() < pkg.lastModified()) { Log.v(TAG, "Module already installed, removing: " + module); ModuleUtils.uninstall(module); } else { Log.v(TAG, "Module already installed, ignoring: " + module); return; } } Log.v(TAG, "Installing module from package: " + module); ModuleUtils.install(this, module, AssetUtils.ASSET_URI + MODULE_PATH + '/' + asset); } @Override public IBinder onBind(Intent arg0) { // TODO Auto-generated method stub return null; } }