diff --git a/AerisDemo/AerisDemo.iml b/AerisDemo/AerisDemo.iml index 4199dcbc..99677132 100644 --- a/AerisDemo/AerisDemo.iml +++ b/AerisDemo/AerisDemo.iml @@ -102,9 +102,9 @@ - + @@ -125,7 +125,7 @@ - + diff --git a/AerisDemo/build.gradle b/AerisDemo/build.gradle index d6e673a0..e9eae12b 100644 --- a/AerisDemo/build.gradle +++ b/AerisDemo/build.gradle @@ -7,9 +7,9 @@ android { defaultConfig { minSdkVersion 19 applicationId "com.example.demoaerisproject" - versionCode 9 + versionCode 10 targetSdkVersion 27 - versionName "2.5.0" + versionName "2.6.0" //multiDexEnabled true signingConfigs { config { @@ -59,7 +59,7 @@ buildscript { mavenCentral() google() jcenter() -// maven { url maven_staging_url } + maven { url maven_staging_url } } dependencies { classpath 'com.android.tools.build:gradle:3.1.4' @@ -70,11 +70,11 @@ repositories { mavenCentral() google() jcenter() -// maven { url maven_staging_url } + maven { url maven_staging_url } } dependencies { testImplementation 'junit:junit:4.12' - implementation ('com.aerisweather:aeris-maps-lib:2.5.0@aar') { + implementation ('com.aerisweather:aeris-maps-lib:2.6.0@aar') { transitive true } androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { diff --git a/AerisDemo/gradle.properties b/AerisDemo/gradle.properties index 5275bafd..30739d99 100644 --- a/AerisDemo/gradle.properties +++ b/AerisDemo/gradle.properties @@ -1,4 +1,4 @@ org.gradle.jvmargs=-Xmx2048M -maven_staging_url=https://oss.sonatype.org/content/repositories/comaerisweather-1586 \ No newline at end of file +maven_staging_url=https://oss.sonatype.org/content/repositories/comaerisweather-1613 diff --git a/AerisDemo/res/layout/listview_pollutant.xml b/AerisDemo/res/layout/listview_pollutant.xml new file mode 100644 index 00000000..a646b631 --- /dev/null +++ b/AerisDemo/res/layout/listview_pollutant.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AerisDemo/res/layout/view_airquality.xml b/AerisDemo/res/layout/view_airquality.xml new file mode 100644 index 00000000..8f88477f --- /dev/null +++ b/AerisDemo/res/layout/view_airquality.xml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AerisDemo/res/values/nav_menu_strings.xml b/AerisDemo/res/values/nav_menu_strings.xml index 46f3a34f..2872dfa7 100644 --- a/AerisDemo/res/values/nav_menu_strings.xml +++ b/AerisDemo/res/values/nav_menu_strings.xml @@ -13,6 +13,8 @@ Weekend Forecast Interactive Maps Sun/Moon + Air Quality + diff --git a/AerisDemo/src/com/example/demoaerisproject/DrawerActivity.java b/AerisDemo/src/com/example/demoaerisproject/DrawerActivity.java index 5c2201e8..18480f68 100644 --- a/AerisDemo/src/com/example/demoaerisproject/DrawerActivity.java +++ b/AerisDemo/src/com/example/demoaerisproject/DrawerActivity.java @@ -34,6 +34,7 @@ import com.example.db.MyPlacesDb.PlacesColumns; import com.example.db.MyPlacesSubject; import com.example.db.MyPlacesSubject.MyPlacesObserver; +import com.example.fragment.AirQualityFragment; import com.example.fragment.ExtForecastFragment; import com.example.fragment.HeadlessFragment; import com.example.fragment.MyMapFragment; @@ -275,6 +276,11 @@ public void displayView(int position) fragment = new CustomSunmoonFragment(); break; } + case 7: + { + fragment = new AirQualityFragment(); + break; + } default: break; } diff --git a/AerisDemo/src/com/example/fragment/AirQualityFragment.java b/AerisDemo/src/com/example/fragment/AirQualityFragment.java new file mode 100644 index 00000000..f5761f2e --- /dev/null +++ b/AerisDemo/src/com/example/fragment/AirQualityFragment.java @@ -0,0 +1,84 @@ +package com.example.fragment; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ListView; + +import com.aerisweather.aeris.communication.EndpointType; +import com.aerisweather.aeris.model.AerisDataJSON; +import com.aerisweather.aeris.model.AerisResponse; +import com.aerisweather.aeris.response.AirQualityResponse; +import com.example.demoaerisproject.R; +import com.example.listview.AirQualityAdapter; + +import java.util.ArrayList; +import java.util.List; + + +public class AirQualityFragment extends AerisFragment +{ + ListView listView; + AirQualityAdapter adapter; + private List aqResponses; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) + { + //use the existing generic listview fragment we use for the extended forecast and nearby obs + View rootView = inflater.inflate(R.layout.fragment_extforecast, container, false); + listView = (ListView) rootView; + return rootView; + } + + @Override + public void onResult(EndpointType endpoint, AerisResponse response) + { + if (listView == null) + { + return; + } + if (endpoint == EndpointType.AIR_QUALITY) + { + if (response.isSuccessfulWithResponses()) + { + aqResponses = new ArrayList(); + + for (AerisDataJSON data : response.getListOfResponse()) + { + aqResponses.add(new AirQualityResponse(data)); + } + + if (adapter == null) + { + adapter = new AirQualityAdapter(aqResponses, getActivity()); + listView.setAdapter(adapter); + } + else + { + adapter.notifyDataSetChanged(); + } + } + else + { + this.handleError(response.getError()); + } + } + } + + @Override + void performRequest() { + headlessFragment.performAirQuality(this); + } + + @Override + EndpointType getEndpointType() { + return EndpointType.AIR_QUALITY; + } + + @Override + String getKey() { + return HeadlessFragment.AIR_QUALITY; + } +} diff --git a/AerisDemo/src/com/example/fragment/HeadlessFragment.java b/AerisDemo/src/com/example/fragment/HeadlessFragment.java index 38a48ddd..454e58e9 100644 --- a/AerisDemo/src/com/example/fragment/HeadlessFragment.java +++ b/AerisDemo/src/com/example/fragment/HeadlessFragment.java @@ -46,6 +46,7 @@ public interface HeadlessObserver { protected static final String EXT_FORECAST = "extended_forecasts"; protected static final String NEARBY_OBS = "nearby_observations"; protected static final String OVERVIEW = "weather_overview"; + protected static final String AIR_QUALITY = "air_quality"; private int currentFragment = 0; private static final long TEN_MINUTES = 1000 * 60 * 10; @@ -53,15 +54,16 @@ public interface HeadlessObserver { private Map timeMap = new HashMap(); private static List observers = new ArrayList(); - public static HeadlessFragment getFragment(Activity activity) { + public static HeadlessFragment getFragment(Activity activity) + { // create headless fragment FragmentManager fragmentManager = activity.getFragmentManager(); - HeadlessFragment fragment = (HeadlessFragment) fragmentManager - .findFragmentByTag("Headless"); - if (fragment == null) { + HeadlessFragment fragment = (HeadlessFragment) fragmentManager.findFragmentByTag("Headless"); + + if (fragment == null) + { fragment = new HeadlessFragment(); - fragmentManager.beginTransaction().add(fragment, "Headless") - .commit(); + fragmentManager.beginTransaction().add(fragment, "Headless").commit(); } return fragment; } @@ -74,57 +76,70 @@ public static void removeObserver(HeadlessObserver observer) { observers.remove(observer); } - public void storeResponse(String key, Object object) { + public void storeResponse(String key, Object object) + { map.put(key, object); timeMap.put(key, System.currentTimeMillis()); } - public Object getResponse(String key) { - if (timeMap.get(key) == null) { + public Object getResponse(String key) + { + if (timeMap.get(key) == null) + { return null; - } else { - if (System.currentTimeMillis() - timeMap.get(key) > TEN_MINUTES) { + } + else + { + if (System.currentTimeMillis() - timeMap.get(key) > TEN_MINUTES) + { return null; - } else { + } + else + { return map.get(key); } } - } @Override - public void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) + { super.onCreate(savedInstanceState); - // The heart and mind of headless fragment is below line. It will keep - // the fragment alive during configuration change when activities and - // //subsequent fragments are "put to death" and recreated + // The heart and mind of headless fragment is below line. It will keep the fragment alive during configuration + // change when activities and subsequent fragments are "put to death" and recreated setRetainInstance(true); } - public void performWeatherOverview(AerisProgressListener listener) { + public void performWeatherOverview(AerisProgressListener listener) + { BatchBuilder builder = new BatchBuilder(); builder.addGlobalParameter(this.getPlaceParameter()); builder.addEndpoint(new Endpoint(EndpointType.OBSERVATIONS, Action.CLOSEST).addParameters(FieldsParameter.initWith("ob"))); + builder.addEndpoint(new Endpoint(EndpointType.PLACES, Action.CLOSEST) .addParameters(FieldsParameter.initWith("place"))); + builder.addEndpoint(new Endpoint(EndpointType.FORECASTS, Action.CLOSEST) .addParameters(FieldsParameter.initWith( ForecastsFields.WEATHER_PRIMARY, ForecastsFields.MAX_TEMP_F, ForecastsFields.ICON, ForecastsFields.DATETIME_ISO, ForecastsFields.MIN_TEMP_F))); + AerisRequest request = builder.build(); BatchCommunicationTask task = new BatchCommunicationTask(getActivity(), this, request); + if (listener != null) task.withProgress(listener); + task.execute(); } - public void performNearbyObs(AerisProgressListener listener) { - + public void performNearbyObs(AerisProgressListener listener) + { AerisRequest request = new AerisRequest(new Endpoint( EndpointType.OBSERVATIONS), Action.CLOSEST, getPlaceParameter(), FieldsParameter.initWith( @@ -132,15 +147,18 @@ public void performNearbyObs(AerisProgressListener listener) { ObservationFields.ICON, ObservationFields.WEATHER_SHORT, Fields.PLACE, ObservationFields.DATETIME), new LimitParameter(10)); - AerisCommunicationTask task = new AerisCommunicationTask(getActivity(), - this, request); - if (listener != null) { + + AerisCommunicationTask task = new AerisCommunicationTask(getActivity(), this, request); + + if (listener != null) + { task.withProgress(listener); } task.execute(); } - public void performExtForecast(AerisProgressListener listener) { + public void performExtForecast(AerisProgressListener listener) + { AerisRequest request = new AerisRequest(new Endpoint( EndpointType.FORECASTS), Action.CLOSEST, getPlaceParameter(), FieldsParameter.initWith(Fields.INTERVAL, @@ -149,83 +167,123 @@ public void performExtForecast(AerisProgressListener listener) { ForecastsFields.DATETIME_ISO, ForecastsFields.MIN_TEMP_F), new FilterParameter("7"), new LimitParameter(10)); - AerisCommunicationTask task = new AerisCommunicationTask(getActivity(), - this, request); - if (listener != null) { + + AerisCommunicationTask task = new AerisCommunicationTask(getActivity(), this, request); + + if (listener != null) + { task.withProgress(listener); } task.execute(); } - public void performWeekendForecast(AerisProgressListener listener) { + public void performWeekendForecast(AerisProgressListener listener) + { AerisRequest request = new AerisRequest(new Endpoint( EndpointType.FORECASTS), Action.CLOSEST, getPlaceParameter(), new FilterParameter("daynight"), new FromParameter("friday"), new ToParameter("+3days")); - AerisCommunicationTask task = new AerisCommunicationTask(getActivity(), - this, request); + + AerisCommunicationTask task = new AerisCommunicationTask(getActivity(), this, request); task.withProgress(listener); task.execute(); } - public void performDetailedObservation(AerisProgressListener listener) { + public void performDetailedObservation(AerisProgressListener listener) + { BatchBuilder builder = new BatchBuilder(); builder.addGlobalParameter(getPlaceParameter()); + builder.addEndpoint(new Endpoint(EndpointType.OBSERVATIONS, Action.CLOSEST).addParameters(FieldsParameter.initWith("ob"))); + builder.addEndpoint(new Endpoint(EndpointType.PLACES, Action.CLOSEST) .addParameters(FieldsParameter.initWith("place"))); + builder.addEndpoint(new Endpoint(EndpointType.FORECASTS, Action.CLOSEST) .addParameters(new FilterParameter("daynight"), new PLimitParameter(2))); + builder.addEndpoint(new Endpoint(EndpointType.FORECASTS, Action.CLOSEST) .addParameters(new FilterParameter("3hr"), new PLimitParameter( 8), FieldsParameter.initWith(ForecastsFields.TEMP_F, ForecastsFields.TEMP_C, ForecastsFields.ICON, ForecastsFields.DATETIME_ISO, Fields.INTERVAL))); + AerisRequest request = builder.build(); - BatchCommunicationTask task = new BatchCommunicationTask(getActivity(), - this, request); - if (listener != null) { + BatchCommunicationTask task = new BatchCommunicationTask(getActivity(), this, request); + + if (listener != null) + { task.withProgress(listener); } task.execute(); } + public void performAirQuality(AerisProgressListener listener) + { + AerisRequest request = new AerisRequest(new Endpoint( + EndpointType.AIR_QUALITY), + Action.CLOSEST, + getPlaceParameter(), + new LimitParameter(10)); + + AerisCommunicationTask task = new AerisCommunicationTask(getActivity(), this, request); + + if (listener != null) + { + task.withProgress(listener); + } + + task.execute(); + } + public void performCall(AerisRequest request, AerisCallback callback, - BatchCallback batchCallback, AerisProgressListener listener) { - if (callback != null) { - AerisCommunicationTask task = new AerisCommunicationTask( - getActivity(), callback, request); + BatchCallback batchCallback, AerisProgressListener listener) + { + if (callback != null) + { + AerisCommunicationTask task = new AerisCommunicationTask(getActivity(), callback, request); - if (listener != null) { + if (listener != null) + { task.withProgress(listener); } + task.execute(); - } else if (batchCallback != null) { - BatchCommunicationTask task = new BatchCommunicationTask( - getActivity(), batchCallback, request); - if (listener != null) { + } + else if (batchCallback != null) + { + BatchCommunicationTask task = new BatchCommunicationTask(getActivity(), batchCallback, request); + + if (listener != null) + { task.withProgress(listener); } task.execute(); } - } - private void notifyObservers() { - for (HeadlessObserver observer : observers) { + private void notifyObservers() + { + for (HeadlessObserver observer : observers) + { observer.notifyDataChanged(); } } @Override - public void onBatchResponse(AerisBatchResponse response) { - if (response.isSuccessful() && response.getError() == null) { - if (response.responses.size() == 3) { + public void onBatchResponse(AerisBatchResponse response) + { + if (response.isSuccessful() && response.getError() == null) + { + if (response.responses.size() == 3) + { storeResponse(OVERVIEW, response); - } else if (response.responses.size() == 4) { + } + else if (response.responses.size() == 4) + { storeResponse(DETAILED_OBSERVATION, response); } } @@ -252,6 +310,10 @@ else if (endpoint == EndpointType.OBSERVATIONS) { storeResponse(NEARBY_OBS, response); } + else if (endpoint == EndpointType.AIR_QUALITY) + { + storeResponse(AIR_QUALITY, response); + } notifyObservers(); } } @@ -264,19 +326,22 @@ public void setCurrentFragment(int currentFragment) { this.currentFragment = currentFragment; } - public void clearStored() { + public void clearStored() + { map.clear(); timeMap.clear(); } - private PlaceParameter getPlaceParameter() { + private PlaceParameter getPlaceParameter() + { MyPlacesDb db = new MyPlacesDb(getActivity()); PlaceParameter place = db.getMyPlaceParameter(); db.close(); - if (place == null) { + + if (place == null) + { place = new PlaceParameter(getActivity()); } return place; } - } diff --git a/AerisDemo/src/com/example/fragment/MyMapFragment.java b/AerisDemo/src/com/example/fragment/MyMapFragment.java index bafc04de..c17c5ee4 100644 --- a/AerisDemo/src/com/example/fragment/MyMapFragment.java +++ b/AerisDemo/src/com/example/fragment/MyMapFragment.java @@ -255,6 +255,15 @@ private void initMap() AerisAmpLayer customAmpLayer = new AerisAmpLayer("pressure-msl-nam", "pressure-msl-nam", 80); aerisAmp.setLayer(customAmpLayer); */ + /** + * SETTING LAYER MODIFIERS + */ + /* + AerisAmpLayer statesAmpLayer = aerisAmp.getLayerFromId("states"); + AerisAmpLayer.Modifier stateModifier = statesAmpLayer.getLayerModifier("States Outlines"); + stateModifier.setModifierOption("outlines", true); + aerisAmp.setLayer(statesAmpLayer); + */ if (aerisAmp.getActiveMapLayers().size() < 1) { @@ -265,6 +274,11 @@ private void initMap() //point data layer(s) m_aerisMapView.addLayer(m_mapOptions.getPointData()); + /** + * SAMPLE: TROPICAL CYCLONES POINT DATA + */ +// m_aerisMapView.addLayer(AerisPointData.TROPICAL_CYCLONES); + //polygon layer(s) /** * SAMPLE: DAY TWO CONVECTIVE @@ -276,7 +290,14 @@ private void initMap() withCustomParameter("from", "today")); m_aerisMapView.addLayer(aerisPolygonData); */ - m_aerisMapView.addLayer(m_mapOptions.getPolygonData()); + + /** + * SAMPLE: TROPICAL CYCLONES ERROR CONES + */ + AerisPolygonData aerisPolygonData = AerisPolygonData.TROPICAL_CYCLONE_ERROR_CONES; + m_aerisMapView.addLayer(aerisPolygonData); + +// m_aerisMapView.addLayer(m_mapOptions.getPolygonData()); //get a new marker option object MarkerOptions markerOptions = new MarkerOptions(); @@ -354,7 +375,7 @@ public void onResume() m_mapOptions.getMapPreferences(getActivity()); m_aerisMapView.getMap().setMapType(m_mapOptions.getMapType()); - + m_aerisMapView.addLayer(m_mapOptions.getAerisAMP()); m_aerisMapView.addLayer(m_mapOptions.getPointData()); m_aerisMapView.addLayer(m_mapOptions.getPolygonData()); diff --git a/AerisDemo/src/com/example/listview/AirQualityAdapter.java b/AerisDemo/src/com/example/listview/AirQualityAdapter.java new file mode 100644 index 00000000..5e9c6048 --- /dev/null +++ b/AerisDemo/src/com/example/listview/AirQualityAdapter.java @@ -0,0 +1,24 @@ +package com.example.listview; + +import android.app.Activity; + +import com.aerisweather.aeris.response.AirQualityResponse; + +import java.util.List; + +public class AirQualityAdapter extends ListAdapter +{ + Activity m_activity; + + public AirQualityAdapter(List items, Activity activity) + { + super(items, activity, 0); + m_activity = activity; + } + + @Override + public AdapterHolder getHolder() { + return new AirQualityItemHolder(m_activity); + } + +} diff --git a/AerisDemo/src/com/example/listview/AirQualityItemHolder.java b/AerisDemo/src/com/example/listview/AirQualityItemHolder.java new file mode 100644 index 00000000..bf55b02a --- /dev/null +++ b/AerisDemo/src/com/example/listview/AirQualityItemHolder.java @@ -0,0 +1,87 @@ +package com.example.listview; + +import android.app.Activity; +import android.graphics.Color; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.TextView; + +import com.aerisweather.aeris.model.Pollutant; +import com.aerisweather.aeris.response.AirQualityResponse; +import com.aerisweather.aeris.util.FileUtil; +import com.aerisweather.aeris.util.WeatherUtil; +import com.example.demoaerisproject.R; +import com.example.view.PollutantView; + +public class AirQualityItemHolder implements AdapterHolder +{ + TextView m_place; + TextView m_time; + TextView m_aqi; + TextView m_category; + ImageView m_aqiIcon; + TextView m_method; + TextView m_dominant; + LinearLayout m_llPollutants; + + Activity m_activity; + + AirQualityItemHolder(Activity activity) + { + m_activity = activity; + } + + @Override + public View inflateview(LayoutInflater mInflater) + { + View v = mInflater.inflate(R.layout.view_airquality, null, false); + m_place = (TextView) v.findViewById(R.id.tvPlace); + m_time = (TextView) v.findViewById(R.id.tvTime); + m_aqi = (TextView) v.findViewById(R.id.tvAirQuality); + m_category = (TextView) v.findViewById(R.id.tvCategory); + m_aqiIcon = (ImageView) v.findViewById(R.id.ivAirQualityIcon); + m_method = (TextView) v.findViewById(R.id.tvMethod); + m_dominant = (TextView) v.findViewById(R.id.tvDominant); + m_llPollutants = (LinearLayout) v.findViewById(R.id.llPollutants); + return v; + } + + @Override + public void populateView(AirQualityResponse response, int position) + { + m_place.setText(WeatherUtil.capitalize(response.getPlace().name)); + + m_time.setText(WeatherUtil.getFormatFromISO(response.getPeriods().get(0).dateTimeISO, "h:mm aa")); + + String sAQI = String.valueOf(response.getPeriods().get(0).aqi); + m_aqi.setText(sAQI); + + m_category.setText(WeatherUtil.capitalize(response.getPeriods().get(0).category)); + +// m_aqiIcon.setImageResource(FileUtil.getDrawableByName(t.getObservation().icon, weatherIcon.getContext())); + String aqiColorString = "#" + response.getPeriods().get(0).color; + int aqiColor = Color.parseColor(aqiColorString); + m_aqiIcon.setBackgroundColor(aqiColor); + + m_method.setText("Method of Calculation: " + WeatherUtil.capitalize(response.getPeriods().get(0).method)); + + m_dominant.setText("Dominant Pollutant: " + WeatherUtil.capitalize(response.getPeriods().get(0).dominant)); + + m_llPollutants.removeAllViews(); + for (Pollutant pollutant: response.getPeriods().get(0).pollutants) + { + PollutantView pollutantView = new PollutantView(m_activity, null); + pollutantView.setText( + WeatherUtil.capitalize(pollutant.name), + String.valueOf(pollutant.aqi), + WeatherUtil.capitalize(pollutant.category), + String.valueOf(pollutant.valuePPB), + String.valueOf(pollutant.valueUGM3)); + m_llPollutants.addView(pollutantView); + } + } + +} diff --git a/AerisDemo/src/com/example/view/PollutantView.java b/AerisDemo/src/com/example/view/PollutantView.java new file mode 100644 index 00000000..90e2c15a --- /dev/null +++ b/AerisDemo/src/com/example/view/PollutantView.java @@ -0,0 +1,39 @@ +package com.example.view; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.example.demoaerisproject.R; + +public class PollutantView extends LinearLayout +{ + private TextView m_name; + private TextView m_aqi; + private TextView m_category; + private TextView m_ppb; + private TextView m_ugm3; + + public PollutantView(Context context, AttributeSet set) + { + super(context, set); + LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.listview_pollutant, this, true); + m_name = (TextView) this.findViewById(R.id.tvPollutantName); + m_aqi = (TextView) this.findViewById(R.id.tvPollutantValue); + m_category = (TextView) this.findViewById(R.id.tvPollutantCategory); + m_ppb = (TextView) this.findViewById(R.id.tvPollutantPPB_Value); + m_ugm3 = (TextView) this.findViewById(R.id.tvPollutantUGM3Value); + } + + public void setText(String name, String aqi, String category, String ppb, String umg3) + { + m_name.setText(name); + m_aqi.setText(aqi); + m_category.setText(category); + m_ppb.setText(ppb); + m_ugm3.setText(umg3); + } +}