Java tutorial
package com.jogden.spunkycharts; /* Copyright (C) 2014 Jonathon Ogden < jeog.dev@gmail.com > This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 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 General Public License along with this program. If not, see http://www.gnu.org/licenses. */ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserFactory; import com.jogden.spunkycharts.ApplicationPreferences.ChartPreferencesSetup; import com.jogden.spunkycharts.animations.BaseSelectAnimationA; import com.jogden.spunkycharts.data.DataContentService; import com.jogden.spunkycharts.data.DataContentService.DataClientInterface; import com.jogden.spunkycharts.misc.Pair; import android.app.Activity; import android.app.Application; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import android.support.v4.content.LocalBroadcastManager; import android.view.View; public class MainApplication extends Application { static { /* our native lib */ System.loadLibrary("SpunkyCharts"); } /* a wrapper to interface popular findViewById method */ public interface ViewFindable { public View findViewById(int id); } /* PRICE AXIS SCALE/INCREMENTS NATIVE SUB-ROUTINE: * determines what increment values fit 'best. (see SpunkyCharts.cpp) */ public static native float[] IncrementRegressNative(float adjRangeDiff, int maxNodes); @SuppressWarnings("serial") public static class AppNotReadyForDataException extends IllegalStateException { public AppNotReadyForDataException(String msg) { super(msg); } } @SuppressWarnings("serial") static public class InvalidChartManifestException extends XmlPullParserException { public InvalidChartManifestException(String msg) { super(msg); } } @SuppressWarnings("serial") public static final List<Class<?>> availablePanels = new ArrayList<Class<?>>() { { add(DockingPanelActivity.L2.class); add(DockingPanelActivity.L1.class); add(DockingPanelActivity.class); add(DockingPanelActivity.R1.class); add(DockingPanelActivity.R2.class); } }; public static final int TMOVE_THRSHLD = 10; public static final int WIRELESS_SETTINGS_REQ = 101; public static final int UPDATE_PREFS_REQ = 102; private static MainApplication thisApp = null; private static LocalBroadcastManager ourBroadcastMngr = null; private static DataContentService ourDataService = null; private static ServiceConnection ourDataServiceConnection = null; public static volatile boolean readyForData = false; public static final Object currentDockingPanelMonitor = new Object(); /* docking panel currently 'active' (onStart, onStop) */ public static volatile Activity currentDockingPanel = null; public static Class<? extends DockingPanelActivity> currentDockingPanelActivity = null; /* all panels, active or in back stack */ public static final List<DockingPanelActivity> dockingPanels = new ArrayList<DockingPanelActivity>(); /* available chart type names and Fragment objects */ public static final Map<String, Class<? extends BaseChartFragmentA>> metaChartTypes = new LinkedHashMap<String, Class<? extends BaseChartFragmentA>>(); /* available chart selection animation names and Animation objects */ public static final Map<String, Class<? extends BaseSelectAnimationA>> metaSelectAnimTypes = new LinkedHashMap<String, Class<? extends BaseSelectAnimationA>>(); /* available data client names and implementations * currently we're just using the last added to this mapping */ public static final Map<String, Class<? extends DataClientInterface>> metaDataClients = new LinkedHashMap<String, Class<? extends DataClientInterface>>(); public static LocalBroadcastManager getLocalBroadcastManager() { return ourBroadcastMngr; } public static MainApplication getInstance() { return thisApp; } public static DataContentService getDataService() { return ourDataService; } public static void setDataService(DataContentService service) { ourDataService = service; } public static boolean isDataServiceRunning() { return ourDataService != null; } public static void connectToDataService() { thisApp.bindService(new Intent(thisApp, DataContentService.class), ourDataServiceConnection = new ServiceConnection() { public void onServiceConnected(ComponentName name, IBinder binder) { ourDataService = ((DataContentService.OurBinder) binder).getService(); DataContentService.connect(); } public void onServiceDisconnected(ComponentName name) { ourDataService = null; } }, Context.BIND_AUTO_CREATE); thisApp.startService(new Intent(thisApp, DataContentService.class)); } public static void disconnectFromDataService() { if (ourDataService != null) { if (ourDataServiceConnection != null) thisApp.unbindService(ourDataServiceConnection); else throw new IllegalStateException( "Failed to disconnect from data service, " + "ourDataServiceConnection is null."); } } @Override public final void onCreate() { super.onCreate(); thisApp = this; /* DO NOT REFERENCE ApplicationPreferences BEFORE HERE- */ ourBroadcastMngr = LocalBroadcastManager.getInstance(this); try { /* check chart manifest */ _loadSpunkyManifest(); } catch (Exception e) { e.printStackTrace(); } } /* sub for extracting from chart_manifest.xml */ @SuppressWarnings("unchecked") private final void _loadSpunkyManifest() throws XmlPullParserException, ClassNotFoundException, InstantiationException, IllegalAccessException, IOException { XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); XmlPullParser parser = factory.newPullParser(); InputStream manifestStrm = getResources().openRawResource(R.raw.spunky_manifest); parser.setInput(manifestStrm, null); String name = null; String layName = null; String fragName = null; String prefsName = null; String className = null; Class<BaseChartFragmentA> fragCls = null; Class<ChartPreferencesSetup> prefCls = null; Class<BaseSelectAnimationA> animCls = null; Class<DataClientInterface> dataCls = null; Integer layId = 0; boolean validInner = false; int eType = parser.getEventType(); while (eType != XmlPullParser.END_DOCUMENT) { if (eType == XmlPullParser.START_TAG) { if (parser.getName().equals("SpunkyManifest")) validInner = true; name = null; fragName = null; prefsName = null; className = null; fragCls = null; prefCls = null; animCls = null; dataCls = null; layId = 0; if (validInner && parser.getName().equals("ChartType")) { if ((name = parser.getAttributeValue(null, "name")) == null) throw new InvalidChartManifestException("ChartType does not have a 'chartName' attr"); if ((fragName = parser.getAttributeValue(null, "fragName")) == null) throw new InvalidChartManifestException( "ChartType [" + name + "] does not have a 'fragName' attr"); else { try { fragCls = (Class<BaseChartFragmentA>) Class.forName(fragName); } catch (Exception e) { throw new InvalidChartManifestException( "fragName attr [ " + fragName + " ] is not a valid fully " + "qualifed name or derived class of BaseChartFragmentA"); } } if ((prefsName = parser.getAttributeValue(null, "prefsName")) == null) throw new InvalidChartManifestException( "ChartType [" + name + "] does not have a 'prefsName' attr "); else { try { prefCls = (Class<ChartPreferencesSetup>) Class.forName(prefsName); } catch (Exception e) { throw new InvalidChartManifestException("prefsName attr [ " + prefsName + " ] is not a valid " + "fully qualifed name or implementation of " + "ApplicationPreferences.ChartPreferencesSetup"); } } if ((layName = parser.getAttributeValue(null, "layout")) == null) throw new InvalidChartManifestException( "ChartType [ " + name + " ] does not have a 'layout' attr "); else { try { layId = getResources().getIdentifier( this.getPackageName() + ":" + layName.replace("@", ""), null, null); if (layId == 0) throw new Exception(); } catch (Exception e) { throw new InvalidChartManifestException( "layout attr [" + layName + "] is not a valid layout " + "resource in the " + this.getPackageName() + " package"); } } } else if (validInner && parser.getName().equals("DataClient")) { if ((name = parser.getAttributeValue(null, "name")) == null) throw new InvalidChartManifestException("DataClient does not have a 'name' attr"); if ((className = parser.getAttributeValue(null, "className")) == null) throw new InvalidChartManifestException( "DataClient [" + name + "] " + "does not have a 'className' attr "); else { try { dataCls = (Class<DataClientInterface>) Class.forName(className); } catch (Exception e) { throw new InvalidChartManifestException( "className attr [" + className + "] is not a valid fully " + "qualifed name or implementation of DataClientInterface"); } } } else if (validInner && parser.getName().equals("ChartSelectAnimation")) { if ((name = parser.getAttributeValue(null, "name")) == null) throw new InvalidChartManifestException("ChartSelectAnimation does not have a 'name' attr"); if ((className = parser.getAttributeValue(null, "className")) == null) throw new InvalidChartManifestException( "ChartSelectAnimation [" + name + "] " + "does not have a 'className' attr "); else { try { animCls = (Class<BaseSelectAnimationA>) Class.forName(className); } catch (Exception e) { throw new InvalidChartManifestException( "className attr [" + className + "] is not a valid fully " + "qualifed name or derived class of BaseSelectAnimationA"); } } } } else if (eType == XmlPullParser.END_TAG) { if (parser.getName().equals("SpunkyManifest")) validInner = false; if (validInner && parser.getName().equals("ChartType")) { metaChartTypes.put(name, fragCls); ApplicationPreferences.metaChartPrefs.put(name, new Pair<ChartPreferencesSetup, Integer>(prefCls.newInstance(), layId)); } else if (validInner && parser.getName().equals("ChartSelectAnimation")) { metaSelectAnimTypes.put(name, animCls); // // } else if (validInner && parser.getName().equals("DataClient")) { metaDataClients.put(name, dataCls); /* use the last provided (for now) */ DataContentService.setDataClientInterface(dataCls.newInstance()); // // } else if (!validInner) { int metaFrags = metaChartTypes.size(); int metaPrefs = ApplicationPreferences.metaChartPrefs.size(); int metaPrefLayIds = ApplicationPreferences.metaChartPrefs.size(); if (metaFrags != metaPrefs) throw new InvalidChartManifestException( "inconsistent fragment and preference entries (count)"); else if (metaPrefs != metaPrefLayIds) throw new InvalidChartManifestException( "inconsistent preference and preference " + "layout entries (count)"); else if (metaFrags == 0) throw new InvalidChartManifestException("Spunky Charts requires at least one chart type."); if (metaSelectAnimTypes.size() < 1) throw new InvalidChartManifestException( "Spunky Charts requires at least one Select Animation."); } } eType = parser.next(); } } }