Android Open Source - geoar-app Plugin Loader






From Project

Back to project page geoar-app.

License

The source code is released under:

Apache License

If you think the Android project geoar-app listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/**
 * Copyright 2012 52North Initiative for Geospatial Open Source Software GmbH
 *//from   w ww  .  j  a  v a 2 s.co  m
 * 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.
 */
package org.n52.geoar.newdata;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.n52.geoar.GeoARApplication;
import org.n52.geoar.ar.view.IntroController;
import org.n52.geoar.newdata.CheckList.OnCheckedChangedListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.util.Base64;

import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.PrecisionModel;

public class PluginLoader {

  /**
   * Class to hold information about a plugin, usually obtained from its
   * plugin descriptor file. At least an identifier is required to load a
   * plugin.
   * 
   */
  public static class PluginInfo {
    public PluginInfo(File pluginFile, String name, String description,
        Long version, String identifier, String publisher) {
      this.name = name;
      this.description = description;
      this.version = version;
      this.pluginFile = pluginFile;
      this.identifier = identifier;
      this.publisher = publisher;
    }

    File pluginFile; // Path to the plugin
    String name;
    String description;
    Long version;
    String identifier;
    String publisher;
  }

  private static final FilenameFilter PLUGIN_FILENAME_FILTER = new FilenameFilter() {
    @Override
    public boolean accept(File dir, String fileName) {
      return fileName.endsWith(".apk") || fileName.endsWith(".zip")
          || fileName.endsWith(".jar");
    }
  };
  private static final String PLUGIN_STATE_PREF = "selected_plugins";
  private static final int PLUGIN_STATE_VERSION = 4;
  private static final File PLUGIN_DIRECTORY_PATH = GeoARApplication.applicationContext
      .getExternalFilesDir(null);
  // Pattern captures the plugin version string
  private static final Pattern pluginVersionPattern = Pattern
      .compile("-(\\d+(?:\\.\\d+)*)[.-]");
  // Pattern captures the plugin name, ignoring the optional version and
  // filename ending
  private static final Pattern pluginNamePattern = Pattern
      .compile("^((?:.(?!-\\d+\\.))+.).*\\.[^.]+$");
  private static CheckList<InstalledPluginHolder> mInstalledPlugins = new CheckList<InstalledPluginHolder>(
      InstalledPluginHolder.class);
  private static List<DataSourceHolder> mDataSources = new ArrayList<DataSourceHolder>();
  private static boolean mReloadingPlugins;
  // Listener to update the list of currently available data sources
  private static OnCheckedChangedListener<InstalledPluginHolder> pluginCheckedChangedListener = new OnCheckedChangedListener<InstalledPluginHolder>() {
    @Override
    public void onCheckedChanged(InstalledPluginHolder item,
        boolean newState) {
      for (DataSourceHolder dataSource : item.getDataSources()) {
        if (newState == true) {
          addDataSource(dataSource);
        } else {
          removeDataSource(dataSource);
        }
      }

      if (newState && !mReloadingPlugins) {
        item.postConstruct();
      }
    }
  };
  private static DefaultHttpClient mHttpClient;
  private static GeometryFactory mGeometryFactory;

  private static final Logger LOG = LoggerFactory
      .getLogger(PluginLoader.class);

  static {
    mInstalledPlugins
        .addOnCheckedChangeListener(pluginCheckedChangedListener);
    mReloadingPlugins = true;
    loadPlugins();
    restoreState();
    mReloadingPlugins = false;
  }

  /**
   * Returns a thread safe {@link DefaultHttpClient} instance to be reused
   * among different parts of the application
   * 
   * @return
   */
  public static DefaultHttpClient getSharedHttpClient() {
    if (mHttpClient == null) {
      SchemeRegistry registry = new SchemeRegistry();
      registry.register(new Scheme("http", PlainSocketFactory
          .getSocketFactory(), 80));
      registry.register(new Scheme("https", SSLSocketFactory
          .getSocketFactory(), 443));

      HttpParams httpParameters = new BasicHttpParams();
      HttpConnectionParams.setSoTimeout(httpParameters, 60000);
      HttpConnectionParams.setConnectionTimeout(httpParameters, 20000);
      ClientConnectionManager cm = new ThreadSafeClientConnManager(
          httpParameters, registry);
      mHttpClient = new DefaultHttpClient(cm, httpParameters);
    }

    return mHttpClient;
  }
  
  public static GeometryFactory getGeometryFactory(){
      if(mGeometryFactory == null){
          mGeometryFactory = new GeometryFactory();
      }
      return mGeometryFactory;
  }

  /**
   * Restores the state of plugins from {@link SharedPreferences}. If an error
   * occurs, e.g. if a previously selected plugin got removed, this function
   * will quit silently.
   */
  private static void restoreState() {
    try {
      SharedPreferences preferences = GeoARApplication.applicationContext
          .getSharedPreferences(GeoARApplication.PREFERENCES_FILE,
              Context.MODE_PRIVATE);

      byte[] data = Base64.decode(
          preferences.getString(PLUGIN_STATE_PREF, ""),
          Base64.DEFAULT);
      PluginStateInputStream objectInputStream = new PluginStateInputStream(
          new ByteArrayInputStream(data));

      int stateVersion = objectInputStream.readInt();
      if (stateVersion != PLUGIN_STATE_VERSION) {
        // Do not read state if preferences contains old/invalid state
        // information
        return;
      }

      // Restore plugin state
      int count = objectInputStream.readInt();
      for (int i = 0; i < count; i++) {

        InstalledPluginHolder plugin = getPluginByIdentifier(objectInputStream
            .readUTF());
        if (plugin == null) {
          return;
        }
        try {
          plugin.restoreState(objectInputStream);
          plugin.postConstruct();
        } catch (IOException e) {
          LOG.warn("Exception while restoring state of plugin "
              + plugin.getName(), e);
        }
      }
      objectInputStream.close();
    } catch (Exception e) {
      LOG.error("Exception while restoring state ", e);
      // TODO
    }
  }

  /**
   * Saves the state of plugins to {@link SharedPreferences}.
   */
  public static void saveState() {
    try {
      ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
      ObjectOutputStream objectOutputStream = new ObjectOutputStream(
          outputStream);

      // store plugin state version
      objectOutputStream.writeInt(PLUGIN_STATE_VERSION);

      List<InstalledPluginHolder> checkedPlugins = mInstalledPlugins
          .getCheckedItems();
      objectOutputStream.writeInt(checkedPlugins.size());

      for (InstalledPluginHolder plugin : checkedPlugins) {
        objectOutputStream.writeUTF(plugin.getIdentifier());
        plugin.saveState(objectOutputStream);
      }

      SharedPreferences preferences = GeoARApplication.applicationContext
          .getSharedPreferences(GeoARApplication.PREFERENCES_FILE,
              Context.MODE_PRIVATE);
      Editor editor = preferences.edit();
      objectOutputStream.flush();
      editor.putString(PLUGIN_STATE_PREF, Base64.encodeToString(
          outputStream.toByteArray(), Base64.DEFAULT));
      editor.commit();
      objectOutputStream.close();
    } catch (IOException e) {
      e.printStackTrace();
      // TODO
    }
  }

  /**
   * Parses a version string to long. Assumes that each part of a version
   * string is < 100.
   * 
   * @param version
   *            Version string, e.g. "1.2.3"
   * @return long built by multiplying each version component by 100 to the
   *         power of its position from the back, i.e. "0.0.1" -> 1, "0.1.0"
   *         -> 100
   */
  private static long parseVersionNumber(String version) {
    String[] split = version.split("\\.");
    long versionNumber = 0;
    for (int i = 0; i < split.length; i++) {
      int num = Integer.parseInt(split[i]);
      if (num < 0 || num >= 100) {
        throw new NumberFormatException(
            "Unable to parse version number, each part may not exceed 100");
      }
      versionNumber += Math.pow(100, (split.length - 1) - i) * num;
    }
    return versionNumber;
  }

  /**
   * Extracts and parses the geoar-plugin.xml plugin-descriptor to create and
   * fill a {@link PluginInfo} instance.
   * 
   * @param pluginFile
   * @return
   */
  private static PluginInfo readPluginInfoFromPlugin(File pluginFile) {
    try {
      ZipFile zipFile = new ZipFile(pluginFile);
      ZipEntry pluginDescriptorEntry = zipFile
          .getEntry("geoar-plugin.xml");
      if (pluginDescriptorEntry == null) {
        return null;
      }

      Document document = DocumentBuilderFactory.newInstance()
          .newDocumentBuilder()
          .parse(zipFile.getInputStream(pluginDescriptorEntry));
      // Find name
      String name = null;
      NodeList nodeList = document.getElementsByTagName("name");
      if (nodeList != null && nodeList.getLength() >= 1) {
        name = nodeList.item(0).getTextContent();
      } else {
        LOG.warn("Plugin Descriptor for " + pluginFile.getName()
            + " does not specify a name");
      }

      // Find publisher
      String publisher = null;
      nodeList = document.getElementsByTagName("publisher");
      if (nodeList != null && nodeList.getLength() >= 1) {
        publisher = nodeList.item(0).getTextContent();
      } else {
        LOG.warn("Plugin Descriptor for " + pluginFile.getName()
            + " does not specify a publisher");
      }

      // Find description
      String description = null;
      nodeList = document.getElementsByTagName("description");
      if (nodeList != null && nodeList.getLength() >= 1) {
        description = nodeList.item(0).getTextContent();
      } else {
        LOG.warn("Plugin Descriptor for " + pluginFile.getName()
            + " does not specify a description");
      }

      // Find identifier
      String identifier = null;
      nodeList = document.getElementsByTagName("identifier");
      if (nodeList != null && nodeList.getLength() >= 1) {
        identifier = nodeList.item(0).getTextContent();
      } else {
        LOG.warn("Plugin Descriptor for " + pluginFile.getName()
            + " does not specify an identifier");
      }

      // Find version
      Long version = null;
      nodeList = document.getElementsByTagName("version");
      if (nodeList != null && nodeList.getLength() >= 1) {
        String versionString = "-" + nodeList.item(0).getTextContent();

        Matcher matcher = pluginVersionPattern.matcher(versionString);
        if (matcher.find() && matcher.group(1) != null) {
          try {
            version = parseVersionNumber(matcher.group(1));
          } catch (NumberFormatException e) {
            LOG.error("Plugin filename version invalid: "
                + matcher.group(1));
          }
        }
      } else {
        LOG.warn("Plugin Descriptor for " + pluginFile.getName()
            + " does not specify a version");
      }

      if (identifier == null) {
        identifier = name;
      }

      return new PluginInfo(pluginFile, name, description, version,
          identifier, publisher);
    } catch (SAXException e) {
      e.printStackTrace();
    } catch (ParserConfigurationException e) {
      e.printStackTrace();
    } catch (ZipException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
    return null;
  }

  /**
   * Reads {@link PluginInfo} from the path of a plugin. This will only
   * extract a name and version information
   * 
   * @param pluginFile
   * @return
   */
  private static PluginInfo readPluginInfoFromFilename(File pluginFile) {
    String pluginFileName = pluginFile.getName();

    Matcher matcher = pluginNamePattern.matcher(pluginFileName);
    if (!matcher.matches()) {
      LOG.error("Plugin filename invalid: " + pluginFileName);
      return null;
    }

    String name = matcher.group(1);

    Long version = null;
    matcher = pluginVersionPattern.matcher(pluginFileName);
    if (matcher.find() && matcher.group(1) != null) {
      try {
        version = parseVersionNumber(matcher.group(1));
      } catch (NumberFormatException e) {
        LOG.error("Plugin filename version invalid: "
            + matcher.group(1));
      }
    }

    return new PluginInfo(pluginFile, name, null, version, name, null);
  }

  /**
   * Loads all plugins from SD card, compares version strings for plugins with
   * same names and loads the most recent plugin.
   * 
   * The plugin name is the filename without its ending and without optional
   * version string. A version string is introduced by a hyphen, following
   * dot-separated numbers, e.g. "-1.2.3"
   */
  private static void loadPlugins() {
    String[] apksInDirectory = PLUGIN_DIRECTORY_PATH
        .list(PLUGIN_FILENAME_FILTER);

    if (apksInDirectory == null || apksInDirectory.length == 0){
      IntroController.startIntro(!mInstalledPlugins.isEmpty());
      return;
    }
      

    // Map to store all plugins with their versions for loading only the
    // newest ones
    HashMap<String, PluginInfo> pluginVersionMap = new HashMap<String, PluginInfo>();

    for (String pluginFileName : apksInDirectory) {

      PluginInfo pluginInfo = readPluginInfoFromPlugin(new File(
          PLUGIN_DIRECTORY_PATH, pluginFileName));
      if (pluginInfo == null) {
        LOG.info("Plugin " + pluginFileName
            + " has no plugin descriptor");
        pluginInfo = readPluginInfoFromFilename(new File(
            PLUGIN_DIRECTORY_PATH, pluginFileName));
      }

      if (pluginInfo.identifier == null) {
        LOG.error("Plugin "
            + pluginFileName
            + " has an invalid plugin descriptor and an invalid filename. Plugin excluded from loading.");
        continue;
      }
      if (pluginInfo.version == null) {
        pluginInfo.version = -1L; // Set unknown version to version -1
      }

      PluginInfo pluginInfoMapping = pluginVersionMap
          .get(pluginInfo.identifier);
      if (pluginInfoMapping == null
          || pluginInfoMapping.version < pluginInfo.version) {
        // Plugin not yet known or newer
        pluginVersionMap.put(pluginInfo.identifier, pluginInfo);
      }
    }

    for (PluginInfo pluginInfo : pluginVersionMap.values()) {
      InstalledPluginHolder pluginHolder = new InstalledPluginHolder(
          pluginInfo);
      mInstalledPlugins.add(pluginHolder);
    }
  }

  /**
   * Reloads all plugins. The current state of the plugins are restored
   * afterwards.
   */
  public static void reloadPlugins() {
    mReloadingPlugins = true;
    saveState();
    mInstalledPlugins.clear();
    mDataSources.clear();
    loadPlugins();
    restoreState();
    mReloadingPlugins = false;
  }

  public static CheckList<InstalledPluginHolder> getInstalledPlugins() {
    return mInstalledPlugins;
  }

  private static void addDataSource(DataSourceHolder dataSource) {
    if (!mDataSources.contains(dataSource))
      mDataSources.add(dataSource);
  }

  private static void removeDataSource(DataSourceHolder dataSource) {
    mDataSources.remove(dataSource);
  }

  /**
   * Returns all {@link DataSourceHolder} of currently activated plugins.
   * 
   * @return
   */
  public static List<DataSourceHolder> getDataSources() {
    return mDataSources;
  }
  
  public static boolean hasDataSources(){
    return mInstalledPlugins == null ? false : mInstalledPlugins.size() > 0;
  }

  public static InstalledPluginHolder getPluginByIdentifier(String identifier) {
    for (InstalledPluginHolder plugin : mInstalledPlugins) {
      if (plugin.getIdentifier().equals(identifier)) {
        return plugin;
      }
    }
    return null;
  }

}




Java Source Code List

.DataSourcesOverlay.java
.VisualizationOverlayItem.java
org.n52.geoar.AboutDialog.java
org.n52.geoar.DataSourceListAdapter.java
org.n52.geoar.GeoARActivity.java
org.n52.geoar.GeoARApplication.java
org.n52.geoar.ar.view.ARFragment.java
org.n52.geoar.ar.view.ARObject.java
org.n52.geoar.ar.view.ARView.java
org.n52.geoar.ar.view.DataSourceVisualizationHandler.java
org.n52.geoar.ar.view.IntroController.java
org.n52.geoar.ar.view.IntroViewer.java
org.n52.geoar.ar.view.gl.ARSurfaceViewRenderer.java
org.n52.geoar.ar.view.gl.ARSurfaceView.java
org.n52.geoar.ar.view.gl.GLESCamera.java
org.n52.geoar.ar.view.gl.MultisampleConfigs.java
org.n52.geoar.ar.view.gl.SurfaceTopology.java
org.n52.geoar.ar.view.overlay.ARCanvasSurfaceView.java
org.n52.geoar.ar.view.overlay.GUIDrawable.java
org.n52.geoar.ar.view.overlay.Radar.java
org.n52.geoar.exception.UnsupportedGeometryType.java
org.n52.geoar.map.view.DataSourceOverlayHandler.java
org.n52.geoar.map.view.GeoARMapView.java
org.n52.geoar.map.view.MapActivityContext.java
org.n52.geoar.map.view.MapFragment.java
org.n52.geoar.map.view.overlay.DataSourceOverlay.java
org.n52.geoar.map.view.overlay.DataSourcePointOverlay.java
org.n52.geoar.map.view.overlay.DataSourcePolygonOverlay.java
org.n52.geoar.map.view.overlay.DataSourcePolylineOverlay.java
org.n52.geoar.map.view.overlay.DataSourcesOverlay.java
org.n52.geoar.map.view.overlay.OverlayType.java
org.n52.geoar.map.view.overlay.PointOverlayType.java
org.n52.geoar.map.view.overlay.PolygonOverlayType.java
org.n52.geoar.map.view.overlay.PolylineOverlayType.java
org.n52.geoar.newdata.CheckList.java
org.n52.geoar.newdata.DataCache.java
org.n52.geoar.newdata.DataSourceHolder.java
org.n52.geoar.newdata.DataSourceInstanceHolder.java
org.n52.geoar.newdata.DataSourceInstanceSettingsDialogActivity.java
org.n52.geoar.newdata.InstalledPluginHolder.java
org.n52.geoar.newdata.PluginActivityContext.java
org.n52.geoar.newdata.PluginContext.java
org.n52.geoar.newdata.PluginDialogFragment.java
org.n52.geoar.newdata.PluginDownloadHolder.java
org.n52.geoar.newdata.PluginDownloader.java
org.n52.geoar.newdata.PluginFragment.java
org.n52.geoar.newdata.PluginGridAdapter.java
org.n52.geoar.newdata.PluginHolder.java
org.n52.geoar.newdata.PluginLoader.java
org.n52.geoar.newdata.PluginLogger.java
org.n52.geoar.newdata.PluginStateInputStream.java
org.n52.geoar.newdata.Tile.java
org.n52.geoar.settings.DateTimeSettingsViewField.java
org.n52.geoar.settings.DateUtils.java
org.n52.geoar.settings.NumberSettingsViewField.java
org.n52.geoar.settings.SettingsException.java
org.n52.geoar.settings.SettingsHelper.java
org.n52.geoar.settings.SettingsViewField.java
org.n52.geoar.settings.SettingsView.java
org.n52.geoar.settings.SpinnerSettingsViewField.java
org.n52.geoar.settings.StringSettingsViewField.java
org.n52.geoar.tracking.camera.CameraView.java
org.n52.geoar.tracking.camera.RealityCamera.java
org.n52.geoar.tracking.location.AdaptiveLowPassSensorBuffer.java
org.n52.geoar.tracking.location.LocationHandler.java
org.n52.geoar.tracking.location.LowPassSensorBuffer.java
org.n52.geoar.tracking.location.MeanSensorBuffer.java
org.n52.geoar.tracking.location.SensorBuffer.java
org.n52.geoar.view.InfoView.java
org.n52.geoar.view.geoar.CalibrationControlView.java
org.n52.geoar.view.geoar.Settings.java
org.n52.geoar.view.geoar.gl.mode.BilligerColorShader.java
org.n52.geoar.view.geoar.gl.mode.BilligerLightShader.java
org.n52.geoar.view.geoar.gl.mode.BilligerTextureShader.java
org.n52.geoar.view.geoar.gl.mode.BoundingBox.java
org.n52.geoar.view.geoar.gl.mode.FeatureShader.java
org.n52.geoar.view.geoar.gl.mode.PhongFeatureShader.java
org.n52.geoar.view.geoar.gl.mode.RenderFeature2.java
org.n52.geoar.view.geoar.gl.mode.Spatial.java
org.n52.geoar.view.geoar.gl.mode.TextureFeatureShader.java
org.n52.geoar.view.geoar.gl.mode.Texture.java
org.n52.geoar.view.geoar.gl.mode.features.CubeFeature2.java
org.n52.geoar.view.geoar.gl.mode.features.FlatCircleFeature.java
org.n52.geoar.view.geoar.gl.mode.features.HeightMapFeature.java
org.n52.geoar.view.geoar.gl.mode.features.NewGridFeature.java
org.n52.geoar.view.geoar.gl.mode.features.ReferencedGridFeature.java
org.n52.geoar.view.geoar.gl.mode.features.SphereFeature.java
org.n52.geoar.view.geoar.gl.mode.features.TriangleFeature.java