├── .gitignore ├── LICENSE ├── MobileWeather ├── .gitignore ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── sdl │ │ │ └── mobileweather │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── ic_launcher-web.png │ │ ├── java │ │ │ └── com │ │ │ │ └── sdl │ │ │ │ └── mobileweather │ │ │ │ ├── activity │ │ │ │ └── MainActivity.java │ │ │ │ ├── adapter │ │ │ │ └── ForecastListAdapter.java │ │ │ │ ├── artifact │ │ │ │ ├── Const.java │ │ │ │ ├── GPSLocation.java │ │ │ │ └── WeatherLocation.java │ │ │ │ ├── connection │ │ │ │ └── HttpConnection.java │ │ │ │ ├── fragments │ │ │ │ ├── AlertsFragment.java │ │ │ │ ├── BaseFragment.java │ │ │ │ ├── ConditionsFragment.java │ │ │ │ └── ForecastFragment.java │ │ │ │ ├── localization │ │ │ │ └── LocalizationUtil.java │ │ │ │ ├── location │ │ │ │ ├── LocationJsonProcessor.java │ │ │ │ ├── PlayServicesConnectionChecker.java │ │ │ │ └── WeatherLocationServices.java │ │ │ │ ├── openweathermap │ │ │ │ ├── OpenWeatherMapLocationJsonProcessor.java │ │ │ │ ├── OpenWeatherMapService.java │ │ │ │ └── OpenWeatherMapWeatherJsonProcessor.java │ │ │ │ ├── processor │ │ │ │ ├── ImageProcessor.java │ │ │ │ └── JsonProcessor.java │ │ │ │ ├── smartdevicelink │ │ │ │ ├── SdlActivity.java │ │ │ │ ├── SdlApplication.java │ │ │ │ ├── SdlReceiver.java │ │ │ │ ├── SdlRouterService.java │ │ │ │ └── SdlService.java │ │ │ │ └── weather │ │ │ │ ├── AbbreviationDictionary.java │ │ │ │ ├── Forecast.java │ │ │ │ ├── InfoType.java │ │ │ │ ├── RoadConditions.java │ │ │ │ ├── UnitConverter.java │ │ │ │ ├── WeatherAlarmManager.java │ │ │ │ ├── WeatherAlert.java │ │ │ │ ├── WeatherAlertType.java │ │ │ │ ├── WeatherConditions.java │ │ │ │ ├── WeatherDataManager.java │ │ │ │ ├── WeatherJsonProcessor.java │ │ │ │ ├── WeatherService.java │ │ │ │ └── WeatherUpdateWakefulReceiver.java │ │ └── res │ │ │ ├── drawable-hdpi │ │ │ ├── chancesnow.png │ │ │ ├── chancesnow_50.png │ │ │ ├── chancestorm.png │ │ │ ├── chancestorm_50.png │ │ │ ├── chancethunderstorm.png │ │ │ ├── chancethunderstorm_50.png │ │ │ ├── clearday.png │ │ │ ├── clearday_50.png │ │ │ ├── clearnight.png │ │ │ ├── clearnight_50.png │ │ │ ├── cloudy.png │ │ │ ├── cloudy_50.png │ │ │ ├── dusk.png │ │ │ ├── dusk_50.png │ │ │ ├── fog.png │ │ │ ├── fog_50.png │ │ │ ├── fogcloudy.png │ │ │ ├── fogcloudy_50.png │ │ │ ├── fognight.png │ │ │ ├── fognight_50.png │ │ │ ├── ford_oval.png │ │ │ ├── hazy.png │ │ │ ├── hazy_50.png │ │ │ ├── ic_drawer.png │ │ │ ├── icon_license.xml │ │ │ ├── lightrain.png │ │ │ ├── lightrain_50.png │ │ │ ├── lightsnow.png │ │ │ ├── lightsnow_50.png │ │ │ ├── partlycloudyday.png │ │ │ ├── partlycloudyday_50.png │ │ │ ├── partlycloudynight.png │ │ │ ├── partlycloudynight_50.png │ │ │ ├── partlysunny.png │ │ │ ├── partlysunny_50.png │ │ │ ├── powered.png │ │ │ ├── rain.png │ │ │ ├── rain_50.png │ │ │ ├── sleet.png │ │ │ ├── sleet_50.png │ │ │ ├── snow.png │ │ │ ├── snow_50.png │ │ │ ├── snowflake.png │ │ │ ├── snowflake_50.png │ │ │ ├── storm.png │ │ │ ├── storm_50.png │ │ │ ├── sunrise.png │ │ │ ├── sunrise_50.png │ │ │ ├── thunderstorm.png │ │ │ ├── thunderstorm_50.png │ │ │ ├── windy.png │ │ │ ├── windy_50.png │ │ │ ├── windycloudy.png │ │ │ ├── windycloudy_50.png │ │ │ ├── windyrain.png │ │ │ ├── windyrain_50.png │ │ │ └── wunderground.png │ │ │ ├── drawable-mdpi │ │ │ ├── ic_add_location.png │ │ │ ├── ic_current_location.png │ │ │ └── ic_settings.png │ │ │ ├── drawable-xhdpi │ │ │ ├── ic_add_location.png │ │ │ ├── ic_current_location.png │ │ │ └── ic_settings.png │ │ │ ├── drawable-xxhdpi │ │ │ ├── ic_add_location.png │ │ │ ├── ic_current_location.png │ │ │ ├── ic_settings.png │ │ │ └── icon.png │ │ │ ├── drawable │ │ │ ├── ic_01d.png │ │ │ ├── ic_01n.png │ │ │ ├── ic_02d.png │ │ │ ├── ic_02n.png │ │ │ ├── ic_03d.png │ │ │ ├── ic_03n.png │ │ │ ├── ic_04d.png │ │ │ ├── ic_04n.png │ │ │ ├── ic_09d.png │ │ │ ├── ic_09n.png │ │ │ ├── ic_10d.png │ │ │ ├── ic_10n.png │ │ │ ├── ic_11d.png │ │ │ ├── ic_11n.png │ │ │ ├── ic_13d.png │ │ │ ├── ic_13n.png │ │ │ ├── ic_50d.png │ │ │ ├── ic_50n.png │ │ │ └── ic_none.png │ │ │ ├── layout │ │ │ ├── activity_main.xml │ │ │ ├── drawer_list_item.xml │ │ │ ├── forecast_list_item.xml │ │ │ ├── fragment_alerts.xml │ │ │ ├── fragment_base.xml │ │ │ ├── fragment_conditions.xml │ │ │ ├── fragment_forecast.xml │ │ │ └── lockscreen.xml │ │ │ ├── menu │ │ │ ├── alerts.xml │ │ │ └── forecast.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_round.png │ │ │ ├── values-de │ │ │ └── strings.xml │ │ │ ├── values-es-rES │ │ │ └── strings.xml │ │ │ ├── values-es-rMX │ │ │ └── strings.xml │ │ │ ├── values-large │ │ │ └── dimens.xml │ │ │ ├── values-pt-rBR │ │ │ └── strings.xml │ │ │ ├── values-pt-rPT │ │ │ └── strings.xml │ │ │ ├── values-sw600dp │ │ │ └── dimens.xml │ │ │ ├── values-sw720dp-land │ │ │ └── dimens.xml │ │ │ ├── values │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── ic_launcher_background.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ │ └── xml │ │ │ └── weather_abrv.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── sdl │ │ └── mobileweather │ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | ompiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | 23 | # Logs and databases # 24 | ###################### 25 | *.log 26 | *.sql 27 | *.sqlite 28 | 29 | # OS generated files # 30 | ###################### 31 | .DS_Store 32 | .DS_Store? 33 | ._* 34 | .Spotlight-V100 35 | .Trashes 36 | Icon? 37 | ehthumbs.db 38 | Thumbs.db 39 | 40 | ########Android Auto Generate########## 41 | bin/ 42 | gen/ 43 | /bin 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 - 2018 SmartDeviceLink Consortium, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of SmartDeviceLink Consortium, Inc. nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /MobileWeather/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/libraries 5 | /.idea/modules.xml 6 | /.idea/workspace.xml 7 | /.idea/misc.xml 8 | /.idea/jarRepositories.xml 9 | /.idea/gradle.xml 10 | /.idea/compiler.xml 11 | /.idea/vcs.xml 12 | /.idea/caches/build_file_checksums.ser 13 | .DS_Store 14 | /build 15 | /captures 16 | .externalNativeBuild 17 | -------------------------------------------------------------------------------- /MobileWeather/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /MobileWeather/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 34 5 | defaultConfig { 6 | applicationId "com.sdl.mobileweather" 7 | minSdkVersion 16 8 | targetSdkVersion 34 9 | versionCode 28 10 | versionName "1.7.15" 11 | testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | flavorDimensions "default" 20 | productFlavors{ 21 | multi_sec_off { 22 | buildConfigField 'String', 'TRANSPORT', '"MULTI"' 23 | buildConfigField 'String', 'SECURITY', '"OFF"' 24 | } 25 | tcp { 26 | buildConfigField 'String', 'TRANSPORT', '"TCP"' 27 | buildConfigField 'String', 'SECURITY', '"OFF"' 28 | } 29 | } 30 | lintOptions { 31 | disable 'GoogleAppIndexingWarning' 32 | } 33 | } 34 | 35 | dependencies { 36 | implementation fileTree(dir: 'libs', include: ['*.jar']) 37 | testImplementation 'junit:junit:4.12' 38 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 39 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' 40 | implementation 'com.smartdevicelink:sdl_android:5.7.0' 41 | implementation 'net.hockeyapp.android:HockeySDK:5.1.0' 42 | implementation 'com.google.android.gms:play-services-location:16.0.0' 43 | } 44 | -------------------------------------------------------------------------------- /MobileWeather/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | -keep class com.smartdevicelink.** { *; } 16 | -keep class com.livio.** { *; } 17 | 18 | # Uncomment this to preserve the line number information for 19 | # debugging stack traces. 20 | #-keepattributes SourceFile,LineNumberTable 21 | 22 | # If you keep the line number information, uncomment this to 23 | # hide the original source file name. 24 | #-renamesourcefileattribute SourceFile 25 | -------------------------------------------------------------------------------- /MobileWeather/app/src/androidTest/java/com/sdl/mobileweather/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather; 2 | 3 | import android.content.Context; 4 | import androidx.test.platform.app.InstrumentationRegistry; 5 | import androidx.test.ext.junit.runners.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | import com.sdl.mobileweather.openweathermap.OpenWeatherMapService; 13 | 14 | /** 15 | * Instrumented test, which will execute on an Android device. 16 | * 17 | * @see Testing documentation 18 | */ 19 | @RunWith(AndroidJUnit4.class) 20 | public class ExampleInstrumentedTest { 21 | @Test 22 | public void useAppContext() { 23 | // Context of the app under test. 24 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 25 | 26 | assertEquals("com.sdl.mobileweather", appContext.getPackageName()); 27 | } 28 | 29 | 30 | @Test 31 | public void openWeatherAPIRequestSuccessful() { 32 | OpenWeatherMapService openWeatherMapService = new OpenWeatherMapService(); 33 | assertEquals(OpenWeatherMapService.REQUEST_SUCCESS, openWeatherMapService.getRequestStatus()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 11 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 41 | 42 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 59 | 60 | 61 | 64 | 65 | 66 | 67 | 68 | 71 | 72 | 73 | 78 | 79 | 83 | 84 | 85 | 86 | 89 | 90 | 91 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/adapter/ForecastListAdapter.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.adapter; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.view.Gravity; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.view.ViewGroup.LayoutParams; 10 | import android.widget.ArrayAdapter; 11 | import android.widget.ImageView; 12 | import android.widget.LinearLayout; 13 | import android.widget.TextView; 14 | 15 | import com.sdl.mobileweather.R; 16 | import com.sdl.mobileweather.processor.ImageProcessor; 17 | import com.sdl.mobileweather.weather.Forecast; 18 | import com.sdl.mobileweather.weather.UnitConverter; 19 | import com.sdl.mobileweather.weather.WeatherDataManager; 20 | 21 | import java.text.SimpleDateFormat; 22 | import java.util.Arrays; 23 | import java.util.Calendar; 24 | import java.util.Locale; 25 | 26 | public class ForecastListAdapter extends ArrayAdapter { 27 | private final Context mContext; 28 | private final Forecast[] mForecast; 29 | private WeatherDataManager mDataManager; 30 | private boolean[] mForecastType; 31 | private LayoutInflater mInflater; 32 | private Activity parentActivity; 33 | 34 | public ForecastListAdapter(Context context, Forecast[] forecast, Activity parentActivity) { 35 | super(context, R.layout.forecast_list_item, forecast); 36 | this.mContext = context; 37 | this.mForecast = forecast; 38 | this.mDataManager = WeatherDataManager.getInstance(); 39 | this.mForecastType = new boolean[forecast.length]; 40 | this.parentActivity = parentActivity; 41 | Arrays.fill(this.mForecastType, false); 42 | mInflater = ((Activity) this.mContext).getLayoutInflater(); 43 | } 44 | 45 | public void toggleForecastType(int position) { 46 | if (position < this.mForecast.length) { 47 | this.mForecastType[position] = !this.mForecastType[position]; 48 | } 49 | } 50 | 51 | @Override 52 | public View getView(int position, View view, ViewGroup parent) { 53 | View rowView; 54 | 55 | if (!this.mForecastType[position]) { 56 | rowView = mInflater.inflate(R.layout.forecast_list_item, null, true); 57 | ImageView forecastImageView = (ImageView) rowView.findViewById(R.id.forecastImageView); 58 | TextView shortDayTextView = (TextView) rowView.findViewById(R.id.forecastShortDayTextView); 59 | TextView precipTextView = (TextView) rowView.findViewById(R.id.forecastPrecip); 60 | TextView lowTempTextView = (TextView) rowView.findViewById(R.id.forecastLowTemp); 61 | TextView highTempTextView = (TextView) rowView.findViewById(R.id.forecastHighTemp); 62 | 63 | String units = null; 64 | if (mDataManager != null) { 65 | units = mDataManager.getUnits(); 66 | } 67 | 68 | forecastImageView.setImageBitmap(null); 69 | shortDayTextView.setText(""); 70 | precipTextView.setText(""); 71 | lowTempTextView.setText(""); 72 | highTempTextView.setText(""); 73 | 74 | if (position < this.mForecast.length) { 75 | Forecast day = this.mForecast[position]; 76 | 77 | if ((mContext.getResources().getString(R.string.conditions_loading)).equals(day.conditionTitle)) { 78 | LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 1.0f); 79 | precipTextView.setLayoutParams(params); 80 | precipTextView.setGravity(Gravity.CENTER | Gravity.CENTER); 81 | precipTextView.setText(day.conditionTitle); 82 | forecastImageView.setImageBitmap(null); 83 | } 84 | else if((day.conditionId != null) && 85 | (day.precipitationChance != null) && 86 | (day.highTemperature != null) && 87 | (day.lowTemperature != null) && 88 | (day.date != null)) { 89 | String conditionId = day.conditionId; 90 | Integer precip = day.precipitationChance; 91 | Float highTemperature = day.highTemperature; 92 | Float lowTemperature = day.lowTemperature; 93 | Calendar forecastDate = day.date; 94 | 95 | SimpleDateFormat dateFormat = new SimpleDateFormat(mContext.getResources().getString(R.string.weather_forecast_day_simpleDateFormat), Locale.getDefault()); 96 | String dateString = dateFormat.format(forecastDate.getTime().getTime()*1000); 97 | String precipChance = null; 98 | String highTemp = null; 99 | String lowTemp = null; 100 | 101 | if ((mContext.getResources().getString(R.string.units_imperial)).equalsIgnoreCase(units) || units == null) { 102 | if (highTemperature != null) 103 | highTemperature = Float.valueOf(UnitConverter.convertTemperatureToImperial(highTemperature.floatValue())); 104 | if (lowTemperature != null) 105 | lowTemperature = Float.valueOf(UnitConverter.convertTemperatureToImperial(lowTemperature.floatValue())); 106 | } 107 | if (precip != null) { 108 | precipChance = String.format(Locale.getDefault(), "%d%%", precip.intValue()); 109 | } 110 | else { 111 | precipChance = " "; 112 | } 113 | if (highTemperature != null) 114 | highTemp = String.format(Locale.getDefault(), mContext.getResources().getString(R.string.weather_forecast_high_temp) + "%.0f\u00B0", highTemperature); 115 | if (lowTemperature != null) 116 | lowTemp = String.format(Locale.getDefault(), mContext.getResources().getString(R.string.weather_forecast_low_temp) + "%.0f\u00B0", lowTemperature); 117 | 118 | shortDayTextView.setText(dateString); 119 | precipTextView.setText(precipChance); 120 | lowTempTextView.setText(lowTemp); 121 | highTempTextView.setText(highTemp); 122 | if (conditionId != null) 123 | ImageProcessor.setConditionsImage(forecastImageView, conditionId); 124 | } 125 | } 126 | 127 | } 128 | else { 129 | rowView = mInflater.inflate(R.layout.forecast_list_item, null, true); 130 | LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 1.0f); 131 | TextView precipTextView = (TextView) rowView.findViewById(R.id.forecastPrecip); 132 | ImageView forecastImageView = (ImageView) rowView.findViewById(R.id.forecastImageView); 133 | precipTextView.setLayoutParams(params); 134 | precipTextView.setGravity(Gravity.CENTER | Gravity.CENTER); 135 | precipTextView.setText(mContext.getResources().getString(R.string.weather_forecast_hourly)); 136 | forecastImageView.setImageBitmap(null); 137 | } 138 | return rowView; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/artifact/Const.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.artifact; 2 | 3 | public class Const { 4 | public static final String PREFS_NAME = "MobileWeatherPrefs"; 5 | } 6 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/artifact/GPSLocation.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.artifact; 2 | 3 | public class GPSLocation { 4 | public String latitude; 5 | public String longitude; 6 | 7 | } 8 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/artifact/WeatherLocation.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.artifact; 2 | 3 | public class WeatherLocation { 4 | public String state; 5 | public String city; 6 | public String zipCode; 7 | public String airportCode; 8 | public GPSLocation gpsLocation; 9 | } 10 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/connection/HttpConnection.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.connection; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.DataOutputStream; 5 | import java.io.IOException; 6 | import java.io.InputStreamReader; 7 | import java.net.HttpURLConnection; 8 | import java.net.ProtocolException; 9 | import java.net.URL; 10 | import java.util.Arrays; 11 | import java.util.List; 12 | import java.util.Locale; 13 | 14 | public class HttpConnection { 15 | 16 | public enum RequestMethod { GET, PUT, POST, DELETE }; 17 | private static final Integer[] httpSuccessCodes = { 200, 201, 202, 203, 204, 205, 206, 207, 208, 226}; 18 | 19 | /** 20 | * Returns the result of an HTTP request. 21 | * 22 | * @param url the request URL 23 | * @param requestMethod the type of HTTP request to perform 24 | * @param dataToSend the data to send to the server 25 | * @return the result of the request 26 | */ 27 | public String sendRequest(URL url, RequestMethod requestMethod, String dataToSend, String contentType) { 28 | HttpURLConnection httpConnection = null; 29 | 30 | // Open the URL, send the request and any data, and read the result into a string 31 | try { 32 | // Open the connection 33 | httpConnection = (HttpURLConnection) url.openConnection(); 34 | return performRequest(httpConnection, requestMethod, dataToSend, contentType); 35 | } 36 | catch (ProtocolException e) { 37 | e.printStackTrace(); 38 | } 39 | catch (IOException e) { 40 | e.printStackTrace(); 41 | } finally { 42 | if(httpConnection != null) 43 | httpConnection.disconnect(); 44 | } 45 | 46 | return null; 47 | } 48 | 49 | /** 50 | * Performs an HTTP request. 51 | * @param httpConnection the HTTP connection. 52 | * @param requestMethod the HTTP RequestMethod. 53 | * @param dataToSend the data to send with the request, if any. 54 | * @param contentType the content type of the request. 55 | * @return the result of the request as a String. 56 | */ 57 | private String performRequest(HttpURLConnection httpConnection, RequestMethod requestMethod, String dataToSend, String contentType) { 58 | BufferedReader httpBufferedReader = null; 59 | String currentLine = null; 60 | DataOutputStream postDataWriter = null; 61 | String httpData = ""; 62 | 63 | // Open the URL, send the request and any data, and read the result into a string 64 | try { 65 | httpConnection.setRequestMethod(requestMethod.name()); 66 | httpConnection.setDoInput(true); 67 | 68 | // For certain methods, we need to send data 69 | if ((requestMethod == RequestMethod.PUT || requestMethod == RequestMethod.POST) && (dataToSend != null)) { 70 | httpConnection.setDoOutput(true); 71 | httpConnection.setUseCaches(false); 72 | if (contentType != null) { 73 | httpConnection.setRequestProperty("Content-Type", contentType); 74 | } 75 | else { 76 | httpConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); 77 | } 78 | httpConnection.setRequestProperty("Content-Language", "en-US"); 79 | httpConnection.setRequestProperty("Content-Length", "" + Integer.toString(dataToSend.getBytes().length)); 80 | 81 | postDataWriter = new DataOutputStream(httpConnection.getOutputStream()); 82 | postDataWriter.writeBytes(dataToSend); 83 | postDataWriter.flush(); 84 | postDataWriter.close(); 85 | } 86 | else { 87 | httpConnection.setDoOutput(false); 88 | } 89 | 90 | // Get the HTTP response code 91 | int responseCode = httpConnection.getResponseCode(); 92 | 93 | List codes = Arrays.asList(httpSuccessCodes); 94 | // If it doesn't equal 'OK' or we performed a DELETE, return the code and message 95 | if (!codes.contains(Integer.valueOf(responseCode)) || requestMethod == RequestMethod.DELETE) { 96 | httpData = String.format(Locale.getDefault(), "STATUS=%d:%s", responseCode, httpConnection.getResponseMessage()); 97 | } 98 | else { 99 | // Get any output from the site 100 | httpBufferedReader = new BufferedReader(new InputStreamReader(httpConnection.getInputStream())); 101 | while ((currentLine = httpBufferedReader.readLine()) != null) { 102 | httpData += currentLine; 103 | } 104 | } 105 | } 106 | catch (ProtocolException e) { 107 | e.printStackTrace(); 108 | } 109 | catch (IOException e) { 110 | e.printStackTrace(); 111 | } 112 | catch (Exception e) { 113 | e.printStackTrace(); 114 | } 115 | finally { 116 | if(httpConnection != null) 117 | httpConnection.disconnect(); 118 | } 119 | 120 | return httpData; 121 | } 122 | 123 | /** 124 | * Checks for an HTTP error message from the sendRequest() method. 125 | * @param statusString the results of the request. 126 | * @return the HTTP error message, if any occurred. 127 | */ 128 | public String getErrorMessage(String statusString) { 129 | if (statusString != null && statusString.startsWith("STATUS=")) { 130 | int separator = statusString.indexOf(":"); 131 | if (separator > 0 && statusString.length() > separator) { 132 | return statusString.substring(separator + 1); 133 | } 134 | } 135 | return null; 136 | } 137 | 138 | /** 139 | * Converts an HTTP error message from the sendRequest() method 140 | * into the HTTP status code. 141 | * 142 | * @param statusString the results from the request. 143 | * @return the HTTP status code. 144 | */ 145 | public int getStatusCode(String statusString) { 146 | if (statusString != null && statusString.startsWith("STATUS=")) { 147 | int separator = statusString.indexOf(":"); 148 | if (separator > 0) { 149 | try { 150 | return Integer.parseInt(statusString.substring(7, separator)); 151 | } 152 | catch (NumberFormatException e) { 153 | e.printStackTrace(); 154 | } 155 | } 156 | } else if (statusString == null){ 157 | // a null statusString may indicate a network connection error. 158 | return -2; 159 | } 160 | return -1; 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/fragments/AlertsFragment.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.fragments; 2 | 3 | 4 | import com.sdl.mobileweather.R; 5 | 6 | import android.os.Bundle; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.TextView; 11 | 12 | public class AlertsFragment extends BaseFragment { 13 | 14 | public static final String TEMP_TEXT = "ALERTS"; 15 | 16 | @Override 17 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 18 | View v = inflater.inflate(R.layout.fragment_alerts, null); 19 | TextView tv = (TextView) v.findViewById(R.id.alerts_msg); 20 | tv.setText(TEMP_TEXT); 21 | return v; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/fragments/BaseFragment.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.fragments; 2 | 3 | 4 | import com.sdl.mobileweather.R; 5 | 6 | import android.app.Fragment; 7 | import android.os.Bundle; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | 12 | 13 | public class BaseFragment extends Fragment { 14 | 15 | @Override 16 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 17 | View v = inflater.inflate(R.layout.fragment_base, null); 18 | return v; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/fragments/ConditionsFragment.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.fragments; 2 | 3 | 4 | import java.util.Locale; 5 | 6 | import android.os.Bundle; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.ImageView; 11 | import android.widget.TextView; 12 | 13 | import com.sdl.mobileweather.R; 14 | import com.sdl.mobileweather.artifact.WeatherLocation; 15 | import com.sdl.mobileweather.processor.ImageProcessor; 16 | import com.sdl.mobileweather.weather.UnitConverter; 17 | import com.sdl.mobileweather.weather.WeatherConditions; 18 | import com.sdl.mobileweather.weather.WeatherDataManager; 19 | 20 | 21 | public class ConditionsFragment extends BaseFragment { 22 | 23 | private ImageView mConditionsIconView; 24 | private TextView mConditionsTextView; 25 | private TextView mCurrentTempView; 26 | private TextView mAdjustedTempView; 27 | private TextView mWindSpeedView; 28 | private TextView mHumidityView; 29 | private TextView mLocationTextView; 30 | private WeatherDataManager mDataManager; 31 | 32 | @Override 33 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 34 | View v = inflater.inflate(R.layout.fragment_conditions, null); 35 | mConditionsIconView = (ImageView) v.findViewById(R.id.conditionsIcon); 36 | mConditionsTextView = (TextView) v.findViewById(R.id.conditionsText); 37 | mCurrentTempView = (TextView) v.findViewById(R.id.currentTemp); 38 | mAdjustedTempView = (TextView) v.findViewById(R.id.adjustedTemp); 39 | mWindSpeedView = (TextView) v.findViewById(R.id.windSpeed); 40 | mHumidityView = (TextView) v.findViewById(R.id.humidity); 41 | 42 | 43 | 44 | 45 | mLocationTextView = (TextView) v.findViewById(R.id.conditionsLocationTextView); 46 | 47 | mDataManager = WeatherDataManager.getInstance(); 48 | if (mDataManager != null) { 49 | WeatherConditions conditions = mDataManager.getWeatherConditions(); 50 | String units = mDataManager.getUnits(); 51 | WeatherLocation location = mDataManager.getCurrentLocation(); 52 | if (conditions != null) { 53 | setConditions(conditions, units); 54 | } 55 | if (location != null) { 56 | setLocation(location); 57 | } 58 | } 59 | 60 | return v; 61 | } 62 | 63 | public void updateLocation() { 64 | if (mDataManager != null) { 65 | WeatherLocation location = mDataManager.getCurrentLocation(); 66 | if (location != null) { 67 | setLocation(location); 68 | } 69 | } 70 | } 71 | 72 | 73 | 74 | public void setLocation(WeatherLocation location) { 75 | if (location != null) { 76 | String locationtext = ""; 77 | if (mLocationTextView != null){ 78 | if (location.city != null){ 79 | locationtext = location.city; 80 | } 81 | if (location.state != null){ 82 | if (location.city != null){ 83 | locationtext += ", " + location.state; 84 | } 85 | else{ 86 | locationtext = location.state; 87 | } 88 | } 89 | mLocationTextView.setText(locationtext); 90 | } 91 | } 92 | } 93 | 94 | public void updateConditions() { 95 | if (mDataManager != null) { 96 | WeatherConditions conditions = mDataManager.getWeatherConditions(); 97 | String units = mDataManager.getUnits(); 98 | if (conditions != null) { 99 | setConditions(conditions, units); 100 | } 101 | } 102 | } 103 | 104 | /** 105 | * Store the current conditions in the fragment 106 | * @param conditions 107 | */ 108 | public void setConditions(WeatherConditions conditions, String units) { 109 | if (conditions != null) { 110 | Float temperature = conditions.temperature; 111 | Float windSpeed = conditions.windSpeed; 112 | Float humidity = conditions.humidity; 113 | Float adjustedTemperature = conditions.feelsLikeTemperature; 114 | String temp = null; 115 | String wind = null; 116 | String humid = null; 117 | String adjTemp = null; 118 | String tempUnits = null; 119 | String speedUnits = null; 120 | 121 | if ((getResources().getString(R.string.units_imperial)).equalsIgnoreCase(units) || units == null) { 122 | if (temperature != null) 123 | temperature = Float.valueOf(UnitConverter.convertTemperatureToImperial(temperature.floatValue())); 124 | if (windSpeed != null) 125 | windSpeed = Float.valueOf(UnitConverter.convertSpeedToImperial(windSpeed.floatValue())); 126 | if (adjustedTemperature != null) 127 | adjustedTemperature = Float.valueOf(UnitConverter.convertTemperatureToImperial(adjustedTemperature.floatValue())); 128 | 129 | tempUnits = getResources().getString(R.string.units_imp_temp_short); 130 | speedUnits = getResources().getString(R.string.units_imp_speed_short); 131 | }else { 132 | tempUnits = getResources().getString(R.string.units_metric_temp_short); 133 | speedUnits = getResources().getString(R.string.units_metric_speed_short); 134 | } 135 | if (temperature != null) 136 | temp = String.format(Locale.getDefault(), "%.0f \u00B0 %s", temperature.floatValue(), tempUnits); 137 | if (windSpeed != null) 138 | wind = String.format(Locale.getDefault(), getResources().getString(R.string.conditions_windspeed) + ": %.0f %s", windSpeed.floatValue(), speedUnits); 139 | if (humidity != null) 140 | humid = String.format(Locale.getDefault(), getResources().getString(R.string.conditions_humidity) + ": %.0f %%", humidity.floatValue()); 141 | if (adjustedTemperature != null) 142 | adjTemp = String.format(Locale.getDefault(), getResources().getString(R.string.conditions_feelslike) + ": %.0f \u00B0 %s", adjustedTemperature.floatValue(), tempUnits); 143 | 144 | mCurrentTempView.setText(temp); 145 | mConditionsTextView.setText(conditions.conditionTitle); 146 | mAdjustedTempView.setText(adjTemp); 147 | mWindSpeedView.setText(wind); 148 | mHumidityView.setText(humid); 149 | 150 | if (conditions.conditionId != null) 151 | ImageProcessor.setConditionsImage(mConditionsIconView, conditions.conditionId); 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/fragments/ForecastFragment.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.fragments; 2 | 3 | 4 | import com.sdl.mobileweather.R; 5 | import com.sdl.mobileweather.adapter.ForecastListAdapter; 6 | import com.sdl.mobileweather.artifact.WeatherLocation; 7 | import com.sdl.mobileweather.weather.Forecast; 8 | import com.sdl.mobileweather.weather.WeatherDataManager; 9 | 10 | import android.app.Activity; 11 | import android.os.Bundle; 12 | import android.view.LayoutInflater; 13 | import android.view.View; 14 | import android.view.ViewGroup; 15 | import android.widget.AdapterView; 16 | import android.widget.ListView; 17 | import android.widget.TextView; 18 | 19 | public class ForecastFragment extends BaseFragment { 20 | private View mFragmentView; 21 | private TextView mLocationTextView; 22 | private ListView mForecastListView; 23 | private WeatherDataManager mDataManager; 24 | private ForecastListAdapter mAdapter; 25 | 26 | @Override 27 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 28 | mFragmentView = inflater.inflate(R.layout.fragment_forecast, null); 29 | mLocationTextView = (TextView) mFragmentView.findViewById(R.id.forecastLocationTextView); 30 | mForecastListView = (ListView) mFragmentView.findViewById(R.id.forecastListView); 31 | 32 | mDataManager = WeatherDataManager.getInstance(); 33 | if (mDataManager != null) { 34 | Forecast[] forecast = mDataManager.getForecast(); 35 | String units = mDataManager.getUnits(); 36 | WeatherLocation location = mDataManager.getCurrentLocation(); 37 | if (location != null) { 38 | setLocation(location); 39 | } 40 | if (forecast != null) { 41 | setForecast(forecast, units); 42 | } 43 | else { 44 | Forecast loadingForecast = new Forecast(); 45 | loadingForecast.conditionTitle = getResources().getString(R.string.forecast_loading); 46 | Forecast[] loading = { loadingForecast }; 47 | setForecast(loading, units); 48 | } 49 | } 50 | 51 | return mFragmentView; 52 | } 53 | 54 | public void updateLocation() { 55 | if (mDataManager != null) { 56 | WeatherLocation location = mDataManager.getCurrentLocation(); 57 | if (location != null) { 58 | setLocation(location); 59 | } 60 | } 61 | } 62 | 63 | public void setLocation(WeatherLocation location) { 64 | if (location != null) { 65 | String locationtext = ""; 66 | if (mLocationTextView != null){ 67 | if (location.city != null){ 68 | locationtext = location.city; 69 | } 70 | if (location.state != null){ 71 | if (location.city != null){ 72 | locationtext += ", " + location.state; 73 | } 74 | else{ 75 | locationtext = location.state; 76 | } 77 | } 78 | mLocationTextView.setText(locationtext); 79 | } 80 | } 81 | } 82 | 83 | 84 | public void updateForecast() { 85 | if (mDataManager != null) { 86 | Forecast[] forecast = mDataManager.getForecast(); 87 | String units = mDataManager.getUnits(); 88 | if (forecast != null) { 89 | setForecast(forecast, units); 90 | } 91 | } 92 | } 93 | 94 | /** 95 | * Store the current forecast in the fragment 96 | * @param forecast 97 | */ 98 | public void setForecast(Forecast[] forecast, String units) { 99 | if (forecast != null) { 100 | Activity context = getActivity(); 101 | mAdapter = new ForecastListAdapter(context, forecast, context); 102 | mForecastListView.setAdapter(mAdapter); 103 | mForecastListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 104 | @Override 105 | public void onItemClick(AdapterView parent, View view, int position, long id) { 106 | mAdapter.toggleForecastType(position); 107 | mAdapter.notifyDataSetChanged(); 108 | } 109 | }); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/localization/LocalizationUtil.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.localization; 2 | 3 | import java.util.Locale; 4 | 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.content.res.Configuration; 8 | 9 | import com.sdl.mobileweather.R; 10 | import com.sdl.mobileweather.openweathermap.OpenWeatherMapService; 11 | import com.smartdevicelink.proxy.rpc.enums.Language; 12 | //import com.sdl.mobileweather.forecastio.ForecastIoService; 13 | import com.sdl.mobileweather.smartdevicelink.SdlApplication; 14 | import com.sdl.mobileweather.weather.WeatherDataManager; 15 | import com.sdl.mobileweather.weather.WeatherUpdateWakefulReceiver; 16 | 17 | public class LocalizationUtil { 18 | private static LocalizationUtil instance; 19 | // variables to save the original and adjusted Locales 20 | private String mSavedLocaleCountry = "US"; 21 | private String mSavedLocaleLanguage = "en"; 22 | private String mAdjustedLocaleCountry = "US"; 23 | private String mAdjustedLocaleLanguage = "en"; 24 | 25 | private final Object mSavedLocaleCountryLock = new Object(); 26 | private final Object mSavedLocaleLanguageLock = new Object(); 27 | private final Object mAdjustedLocaleCountryLock = new Object(); 28 | private final Object mAdjustedLocaleLanguageLock = new Object(); 29 | 30 | static{ 31 | instance = null; 32 | } 33 | 34 | private static synchronized void setInstance(LocalizationUtil loc){ 35 | instance = loc; 36 | } 37 | 38 | public static synchronized LocalizationUtil getInstance(){ 39 | return instance; 40 | } 41 | 42 | public LocalizationUtil(){ 43 | LocalizationUtil.setInstance(this); 44 | } 45 | 46 | 47 | public String getLocaleCountry(){ 48 | synchronized (mSavedLocaleCountryLock){ 49 | return mSavedLocaleCountry; 50 | } 51 | } 52 | 53 | public void setLocaleCountry(String country){ 54 | synchronized (mSavedLocaleCountryLock) { 55 | this.mSavedLocaleCountry = country; 56 | } 57 | } 58 | 59 | public String getLocaleLanguage(){ 60 | synchronized (mSavedLocaleLanguageLock){ 61 | return mSavedLocaleLanguage; 62 | } 63 | } 64 | 65 | public void setLocaleLanguage(String language){ 66 | synchronized (mSavedLocaleLanguageLock){ 67 | this.mSavedLocaleLanguage = language; 68 | } 69 | } 70 | 71 | public String getAdjustedLocaleCountry(){ 72 | synchronized (mAdjustedLocaleCountryLock){ 73 | return mAdjustedLocaleCountry; 74 | } 75 | } 76 | 77 | public void setAdjustedLocaleCountry(String country){ 78 | synchronized (mAdjustedLocaleCountryLock){ 79 | this.mAdjustedLocaleCountry = country; 80 | } 81 | } 82 | 83 | public String getAdjustedLocaleLanguage(){ 84 | synchronized (mAdjustedLocaleLanguageLock){ 85 | return mAdjustedLocaleLanguage; 86 | } 87 | } 88 | 89 | public void setAdjustedLocaleLanguage(String language){ 90 | synchronized (mAdjustedLocaleLanguageLock){ 91 | this.mAdjustedLocaleLanguage = language; 92 | } 93 | } 94 | 95 | 96 | 97 | public void determineLocale(Language sync_language) { 98 | // derive locales for device, get language (VR+TTS) that is used currently by SYNC voice engine(VR+TTS) 99 | mSavedLocaleCountry = (Locale.getDefault()).getCountry(); 100 | mSavedLocaleLanguage = (Locale.getDefault()).getLanguage(); 101 | 102 | switch(sync_language){ 103 | /* 104 | * Map codes for the representation of languages according to ISO 639-1 Code 105 | * Map codes for the representation of countries according to ISO 3166-2 Code 106 | */ 107 | 108 | case EN_GB: 109 | mAdjustedLocaleLanguage = "en"; 110 | mAdjustedLocaleCountry = "GB"; 111 | break; 112 | case EN_US: 113 | mAdjustedLocaleLanguage ="en"; 114 | mAdjustedLocaleCountry = "US"; 115 | break; 116 | case DE_DE: 117 | mAdjustedLocaleLanguage = "de"; 118 | mAdjustedLocaleCountry = "DE"; 119 | break; 120 | case PT_BR: 121 | mAdjustedLocaleLanguage = "pt"; 122 | mAdjustedLocaleCountry = "BR"; 123 | break; 124 | case PT_PT: 125 | mAdjustedLocaleLanguage = "pt"; 126 | mAdjustedLocaleCountry = "PT"; 127 | break; 128 | case ES_ES: 129 | mAdjustedLocaleLanguage = "es"; 130 | mAdjustedLocaleCountry = "ES"; 131 | break; 132 | case ES_MX: 133 | mAdjustedLocaleLanguage = "es"; 134 | mAdjustedLocaleCountry = "MX"; 135 | break; 136 | /* fall through for the countries localization is not yet available */ 137 | case AR_SA:/* 138 | mAdjustedLocaleLanguage = "ar"; 139 | mAdjustedLocaleCountry = "SA"; 140 | break;*/ 141 | case CS_CZ:/* 142 | mAdjustedLocaleLanguage = "cs"; 143 | mAdjustedLocaleCountry = "CZ"; 144 | break;*/ 145 | case DA_DK:/* 146 | mAdjustedLocaleLanguage = "da"; 147 | mAdjustedLocaleCountry = "DK"; 148 | break;*/ 149 | case EN_AU:/* 150 | mAdjustedLocaleLanguage = "en"; 151 | mAdjustedLocaleCountry = "AU"; 152 | break;*/ 153 | case FR_CA:/* 154 | mAdjustedLocaleLanguage = "fr"; 155 | mAdjustedLocaleCountry = "CA"; 156 | break;*/ 157 | case FR_FR:/* 158 | mAdjustedLocaleLanguage = "fr"; 159 | mAdjustedLocaleCountry = "FR"; 160 | break;*/ 161 | case IT_IT:/* 162 | mAdjustedLocaleLanguage = "it"; 163 | mAdjustedLocaleCountry = "IT"; 164 | break;*/ 165 | case JA_JP:/* 166 | mAdjustedLocaleLanguage = "ja"; 167 | mAdjustedLocaleCountry = "JP" ; 168 | break;*/ 169 | case KO_KR:/* 170 | mAdjustedLocaleLanguage = "ko"; 171 | mAdjustedLocaleCountry = "KR"; 172 | break;*/ 173 | case NL_NL:/* 174 | mAdjustedLocaleLanguage = "nl"; 175 | mAdjustedLocaleCountry = "NL"; 176 | break;*/ 177 | case NO_NO:/* 178 | mAdjustedLocaleLanguage = "no"; 179 | mAdjustedLocaleCountry = "NO"; 180 | break;*/ 181 | case PL_PL:/* 182 | mAdjustedLocaleLanguage = "pl"; 183 | mAdjustedLocaleCountry = "PL"; 184 | break;*/ 185 | case RU_RU:/* 186 | mAdjustedLocaleLanguage = "ru"; 187 | mAdjustedLocaleCountry = "RU"; 188 | break;*/ 189 | case SV_SE:/* 190 | mAdjustedLocaleLanguage = "sv"; 191 | mAdjustedLocaleCountry = "SE"; 192 | break;*/ 193 | case TR_TR:/* 194 | mAdjustedLocaleLanguage = "tr"; 195 | mAdjustedLocaleCountry = "TR"; 196 | break;*/ 197 | case ZH_CN:/* 198 | mAdjustedLocaleLanguage = "zh"; 199 | mAdjustedLocaleCountry = "CN"; 200 | break;*/ 201 | case ZH_TW:/* 202 | mAdjustedLocaleLanguage = "zh"; 203 | mAdjustedLocaleCountry = "TW"; 204 | break;*/ 205 | default: 206 | mAdjustedLocaleLanguage = "en"; 207 | mAdjustedLocaleCountry = "US"; 208 | break; 209 | } 210 | return; 211 | } 212 | 213 | public void changeLocale(String language, String country, Context context){ 214 | // change locale to enforce usage of localization resource 215 | WeatherDataManager mDataManager; 216 | Configuration config = context.getResources().getConfiguration(); 217 | Locale locale = new Locale(language, country); 218 | Locale.setDefault(locale); 219 | Configuration conf = new Configuration(config); 220 | conf.locale = locale; 221 | context.getResources().updateConfiguration(conf, context.getResources().getDisplayMetrics()); 222 | mDataManager = WeatherDataManager.getInstance(); 223 | if(mDataManager != null){ 224 | mDataManager.setUnits(context.getResources().getString(R.string.units_default)); 225 | } 226 | Context mAppContext = SdlApplication.getInstance().getApplicationContext(); 227 | Intent mUpdateIntent = new Intent(mAppContext, WeatherUpdateWakefulReceiver.class); 228 | //mUpdateIntent.putExtra("weather_update_service", ForecastIoService.class.getName()); 229 | mUpdateIntent.putExtra("weather_update_service", OpenWeatherMapService.class.getName()); 230 | mAppContext.sendBroadcast(mUpdateIntent); 231 | return; 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/location/LocationJsonProcessor.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.location; 2 | 3 | import org.json.JSONObject; 4 | 5 | import com.sdl.mobileweather.artifact.WeatherLocation; 6 | 7 | public interface LocationJsonProcessor { 8 | 9 | public WeatherLocation getLocation(JSONObject location); 10 | } 11 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/location/PlayServicesConnectionChecker.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.location; 2 | 3 | import android.util.Log; 4 | 5 | import com.sdl.mobileweather.smartdevicelink.SdlApplication; 6 | import com.google.android.gms.common.ConnectionResult; 7 | import com.google.android.gms.common.GooglePlayServicesUtil; 8 | 9 | public class PlayServicesConnectionChecker { 10 | 11 | public static boolean servicesConnected() { 12 | // Check that Google Play services is available 13 | int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(SdlApplication.getInstance()); 14 | // If Google Play services is available 15 | if (ConnectionResult.SUCCESS == resultCode) { 16 | Log.d(SdlApplication.TAG, "Google Play services is available."); 17 | return true; 18 | // Google Play services was not available for some reason 19 | } else { 20 | Log.e(SdlApplication.TAG, "Google Play services are not available!"); 21 | } 22 | return false; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/openweathermap/OpenWeatherMapLocationJsonProcessor.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.openweathermap; 2 | 3 | import com.sdl.mobileweather.artifact.GPSLocation; 4 | import com.sdl.mobileweather.artifact.WeatherLocation; 5 | import com.sdl.mobileweather.location.LocationJsonProcessor; 6 | 7 | import org.json.JSONException; 8 | import org.json.JSONObject; 9 | 10 | //TODO - Convert WUnderground specific code to OpenWeatherMap specific code 11 | public class OpenWeatherMapLocationJsonProcessor implements LocationJsonProcessor { 12 | private static final String LOCATION = "location"; 13 | private static final String STATE = "state"; 14 | private static final String CITY = "city"; 15 | private static final String LATITUDE = "lat"; 16 | private static final String LONGITUDE = "lon"; 17 | private static final String ZIPCODE = "zip"; 18 | 19 | @Override 20 | public WeatherLocation getLocation(JSONObject jsonRoot) { 21 | WeatherLocation location = null; 22 | 23 | if (jsonRoot != null) { 24 | location = new WeatherLocation(); 25 | try { 26 | JSONObject jsonLocation = jsonRoot.getJSONObject(LOCATION); 27 | location.state = jsonLocation.getString(STATE); 28 | location.city = jsonLocation.getString(CITY); 29 | location.zipCode = jsonLocation.getString(ZIPCODE); 30 | location.gpsLocation = new GPSLocation(); 31 | location.gpsLocation.latitude = jsonLocation.getString(LATITUDE); 32 | location.gpsLocation.longitude = jsonLocation.getString(LONGITUDE); 33 | } catch (JSONException e) { 34 | // TODO Auto-generated catch block 35 | e.printStackTrace(); 36 | } 37 | } 38 | return location; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/openweathermap/OpenWeatherMapService.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.openweathermap; 2 | 3 | import android.content.Intent; 4 | import android.net.Uri; 5 | import android.util.Log; 6 | 7 | import androidx.localbroadcastmanager.content.LocalBroadcastManager; 8 | 9 | import com.sdl.mobileweather.artifact.GPSLocation; 10 | import com.sdl.mobileweather.artifact.WeatherLocation; 11 | import com.sdl.mobileweather.connection.HttpConnection; 12 | import com.sdl.mobileweather.processor.JsonProcessor; 13 | import com.sdl.mobileweather.smartdevicelink.SdlApplication; 14 | import com.sdl.mobileweather.weather.Forecast; 15 | import com.sdl.mobileweather.weather.WeatherAlert; 16 | import com.sdl.mobileweather.weather.WeatherConditions; 17 | import com.sdl.mobileweather.weather.WeatherService; 18 | 19 | import org.json.JSONObject; 20 | 21 | import java.net.MalformedURLException; 22 | import java.net.URL; 23 | 24 | public class OpenWeatherMapService extends WeatherService { 25 | 26 | /** 27 | * API key used for OpenWeatherMap API 28 | */ 29 | private static final String API_KEY = "INSERT YOUR OWN API KEY"; 30 | private static final String URI_SCHEME = "https"; 31 | /** 32 | * Base request URL for OpenWeatherMap 33 | */ 34 | private static final String BASE_URL = "api.openweathermap.org"; 35 | private static final String DATA_PATH = "data"; 36 | /** 37 | * A number value in the suggested API call to OpenWeatherMap 38 | *

39 | * I believe this value in the API call represents the version number of the API. 40 | * It may need to change if OpenWeatherMap updates their API 41 | *

42 | * - Noah Stanford (noah@livio.io) 43 | */ 44 | private static final String VERSION = "2.5"; 45 | /** 46 | * This API call type provides current weather, minute forecast for 1 hour, hourly forecast for 48 hours, 47 | * daily forecast for 7 days, global weather alerts and historical data for 5 previous day for any location 48 | */ 49 | private static final String ONE_CALL_API_CALL_TYPE = "onecall"; 50 | private static final String LATITUDE_PARAMETER = "lat"; 51 | private static final String LONGITUDE_PARAMETER = "lon"; 52 | private static final String UNITS_PARAMETER = "units"; 53 | private static final String METRIC = "metric"; 54 | /** 55 | * This parameter is used to specify the API key for the API call 56 | */ 57 | private static final String APP_ID_PARAMETER = "appid"; 58 | /** 59 | * This is 0 because it's the first index and only one URL is used for weather data at a time 60 | */ 61 | private static final int ONLY_URL_INDEX = 0; 62 | public static final int REQUEST_SUCCESS = -1; 63 | public static final int REQUEST_FAILURE = -2; 64 | public static final String TAG = "MobileWeather"; 65 | 66 | 67 | public OpenWeatherMapService() { 68 | super(); 69 | mWeatherProcessor = new OpenWeatherMapWeatherJsonProcessor(); 70 | } 71 | 72 | @Override 73 | protected void updateWeatherData(URL... urls) { 74 | HttpConnection httpRequest = new HttpConnection(); 75 | JsonProcessor jsonProcessor = new JsonProcessor(); 76 | WeatherConditions conditions = null; 77 | Forecast[] forecast = null; 78 | Forecast[] hourlyForecast = null; 79 | WeatherAlert[] alerts = null; 80 | String httpResult = null; 81 | 82 | Log.d(TAG, "updateWeatherData"); 83 | if (urls.length == 1 && mDataManager != null && mWeatherProcessor != null) { 84 | LocalBroadcastManager lbManager = LocalBroadcastManager.getInstance(this); 85 | Log.d(TAG, "updateWeatherData - valid urls"); 86 | if (urls[ONLY_URL_INDEX] != null) { 87 | Log.d(TAG, urls[ONLY_URL_INDEX].toString()); 88 | Log.d(TAG, "updateWeatherData - getting json"); 89 | httpResult = httpRequest.sendRequest(urls[ONLY_URL_INDEX], HttpConnection.RequestMethod.GET, null, "application/json"); 90 | int statusCode = httpRequest.getStatusCode(httpResult); 91 | if (statusCode == REQUEST_SUCCESS) { 92 | Log.d(TAG, "updateWeatherData - parsing conditions json"); 93 | JSONObject jsonRoot = jsonProcessor.getJsonFromString(httpResult); 94 | if (jsonRoot != null) { 95 | Log.d(TAG, "updateWeatherData - parsing conditions"); 96 | conditions = mWeatherProcessor.getWeatherConditions(jsonRoot); 97 | mDataManager.setWeatherConditions(conditions); 98 | if (conditions != null) { 99 | Log.d(TAG, "updateWeatherData - new conditions"); 100 | Intent intent = new Intent("com.sdl.mobileweather.WeatherConditions"); 101 | lbManager.sendBroadcast(intent); 102 | } 103 | 104 | Log.d(TAG, "updateWeatherData - parsing forecast"); 105 | forecast = mWeatherProcessor.getForecast(jsonRoot); 106 | mDataManager.setForecast(forecast); 107 | if (forecast != null) { 108 | Log.d(TAG, "updateWeatherData - new forecast"); 109 | Intent intent = new Intent("com.sdl.mobileweather.Forecast"); 110 | lbManager.sendBroadcast(intent); 111 | } 112 | 113 | Log.d(TAG, "updateWeatherData - parsing hourly forecast"); 114 | hourlyForecast = mWeatherProcessor.getHourlyForecast(jsonRoot); 115 | mDataManager.setHourlyForecast(hourlyForecast); 116 | if (hourlyForecast != null) { 117 | Intent intent = new Intent("com.sdl.mobileweather.HourlyForecast"); 118 | lbManager.sendBroadcast(intent); 119 | } 120 | 121 | alerts = mWeatherProcessor.getAlerts(jsonRoot); 122 | mDataManager.setAlerts(alerts); 123 | if (alerts != null) { 124 | Log.d(TAG, "updateWeatherData - new Alerts"); 125 | Intent intent = new Intent("com.sdl.mobileweather.Alerts"); 126 | lbManager.sendBroadcast(intent); 127 | } 128 | } 129 | 130 | reportApiAvail(true); 131 | 132 | } else if (statusCode == REQUEST_FAILURE){ 133 | reportConnectionAvail(false); 134 | } else { 135 | reportApiAvail(false); 136 | } 137 | } 138 | 139 | 140 | WeatherLocation loc = mDataManager.getCurrentLocation(); 141 | if (loc != null) { 142 | mDataManager.setLastCity(loc.city); 143 | mDataManager.setLastState(loc.state); 144 | } 145 | } 146 | } 147 | 148 | /** 149 | * Creates service specific weather data URLs. 150 | */ 151 | @Override 152 | protected URL[] getURLs(WeatherLocation currentLocation) { 153 | URL oneCallURL = null; 154 | try { 155 | oneCallURL = buildOneCallURL(currentLocation); 156 | Log.d(SdlApplication.TAG, currentLocation.gpsLocation.latitude + "," + currentLocation.gpsLocation.longitude); 157 | } catch (MalformedURLException e) { 158 | e.printStackTrace(); 159 | } 160 | return new URL[] { oneCallURL }; 161 | } 162 | 163 | private URL buildOneCallURL(WeatherLocation currentLocation) throws MalformedURLException { 164 | Uri.Builder builder = new Uri.Builder(); 165 | builder.scheme(URI_SCHEME) 166 | .authority(BASE_URL) 167 | .appendPath(DATA_PATH) 168 | .appendPath(VERSION) 169 | .appendPath(ONE_CALL_API_CALL_TYPE) 170 | .appendQueryParameter(LATITUDE_PARAMETER, currentLocation.gpsLocation.latitude) 171 | .appendQueryParameter(LONGITUDE_PARAMETER, currentLocation.gpsLocation.longitude) 172 | .appendQueryParameter(UNITS_PARAMETER, METRIC) 173 | .appendQueryParameter(APP_ID_PARAMETER, API_KEY); 174 | 175 | Uri everythingUri = builder.build(); 176 | return new URL(everythingUri.toString()); 177 | } 178 | 179 | public int getRequestStatus() { 180 | String httpResult = null; 181 | HttpConnection httpRequest = new HttpConnection(); 182 | WeatherLocation location = new WeatherLocation(); 183 | location.gpsLocation = new GPSLocation(); 184 | location.gpsLocation.latitude = "0.0"; 185 | location.gpsLocation.longitude = "0.0"; 186 | 187 | URL[] urls = getURLs(location); 188 | httpResult = httpRequest.sendRequest(urls[ONLY_URL_INDEX], HttpConnection.RequestMethod.GET, null, "application/json"); 189 | return httpRequest.getStatusCode(httpResult); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/processor/ImageProcessor.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.processor; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.graphics.Bitmap; 6 | import android.graphics.BitmapFactory; 7 | import android.net.Uri; 8 | import android.widget.ImageView; 9 | 10 | import com.sdl.mobileweather.BuildConfig; 11 | import com.sdl.mobileweather.smartdevicelink.SdlApplication; 12 | 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | import java.net.URL; 16 | import java.nio.ByteBuffer; 17 | import java.util.Collections; 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | import java.util.concurrent.ExecutorService; 21 | import java.util.concurrent.Executors; 22 | 23 | import javax.net.ssl.HttpsURLConnection; 24 | 25 | public class ImageProcessor { 26 | private static final ExecutorService executorService = Executors.newSingleThreadExecutor(); 27 | private static final String IMAGE_DIR = "imageDir"; 28 | private static final String PNG_EXTENSION = ".png"; 29 | private static final int BITMAP_QUALITY = 100; 30 | private static final String TAG = "ImageProcessor"; 31 | private static final String RESOURCE_SCHEME = "android.resource://"; 32 | private static final String DRAWABLE_FOLDER = "drawable"; 33 | private static final String ICON_RESOURCE_TEMPLATE = "ic_%s"; 34 | 35 | private static final Map mConditionsImageMap = 36 | Collections.unmodifiableMap(new HashMap() { 37 | private static final long serialVersionUID = 1L; 38 | { 39 | put("sunny.gif", "clearday"); 40 | put("clear.gif", "clearday"); 41 | put("mostlysunny.gif", "partlycloudyday"); 42 | put("partlysunny.gif", "partlysunny"); 43 | put("hazy.gif", "hazy"); 44 | put("fog.gif", "fogcloudy"); 45 | put("partlycloudy.gif", "partlycloudyday"); 46 | put("chancetstorms.gif", "chancethunderstorm"); 47 | put("tstorms.gif", "thunderstorm"); 48 | put("chancerain.gif", "lightrain"); 49 | put("rain.gif", "rain"); 50 | put("chanceflurries.gif", "chancesnow"); 51 | put("flurries.gif", "lightsnow"); 52 | put("chancesnow.gif", "chancesnow"); 53 | put("snow.gif", "snow"); 54 | put("chancesleet.gif", "sleet"); 55 | put("sleet.gif", "sleet"); 56 | put("mostlycloudy.gif", "partlysunny"); 57 | put("cloudy.gif", "cloudy"); 58 | 59 | put("nt_sunny.gif", "clearday"); 60 | put("nt_clear.gif", "clearday"); 61 | put("nt_mostlysunny.gif", "partlycloudyday"); 62 | put("nt_partlysunny.gif", "partlysunny"); 63 | put("nt_hazy.gif", "hazy"); 64 | put("nt_fog.gif", "fogcloudy"); 65 | put("nt_partlycloudy.gif", "partlycloudyday"); 66 | put("nt_chancetstorms.gif", "chancethunderstorm"); 67 | put("nt_tstorms.gif", "thunderstorm"); 68 | put("nt_chancerain.gif", "lightrain"); 69 | put("nt_rain.gif", "rain"); 70 | put("nt_chanceflurries.gif", "chancesnow"); 71 | put("nt_flurries.gif", "lightsnow"); 72 | put("nt_chancesnow.gif", "chancesnow"); 73 | put("nt_snow.gif", "snow"); 74 | put("nt_chancesleet.gif", "sleet"); 75 | put("nt_sleet.gif", "sleet"); 76 | put("nt_mostlycloudy.gif", "partlysunny"); 77 | put("nt_cloudy.gif", "cloudy"); 78 | 79 | put("clear-day.gif", "clearday"); 80 | put("clear-night.gif", "clearnight"); 81 | //put("rain.gif", "rain"); 82 | //put("snow.gif", "snow"); 83 | //put("sleet.gif", "sleet"); 84 | put("wind.gif", "windycloudy"); 85 | //put("fog.gif", "fog"); 86 | //put("cloudy.gif", "cloudy"); 87 | put("partly-cloudy-day.gif", "partlycloudyday"); 88 | put("partly-cloudy-night.gif", "partlycloudynight"); 89 | }}); 90 | 91 | public static String getMappedConditionsImageName(String conditionsImage, boolean small) { 92 | String suffix = ""; 93 | if (small) { 94 | suffix = "_50"; 95 | } 96 | if (mConditionsImageMap.containsKey(conditionsImage)) { 97 | return mConditionsImageMap.get(conditionsImage); 98 | } 99 | else { 100 | return null; 101 | } 102 | } 103 | 104 | public static Bitmap getBitmapFromResources(String conditionId) { 105 | String imageName = String.format(ICON_RESOURCE_TEMPLATE, conditionId); 106 | Resources resources = SdlApplication.getInstance().getResources(); 107 | int resId = resources.getIdentifier(imageName, "drawable", "com.sdl.mobileweather"); 108 | return BitmapFactory.decodeResource(resources, resId); 109 | } 110 | 111 | public static byte[] getBytesFromURL(URL url) { 112 | byte[] bytes = null; 113 | HttpsURLConnection connection = null; 114 | try { 115 | connection = (HttpsURLConnection) url.openConnection(); 116 | connection.setDoInput(true); 117 | connection.connect(); 118 | InputStream inputStream = connection.getInputStream(); 119 | final Bitmap bitmap = BitmapFactory.decodeStream(inputStream); 120 | int size = bitmap.getHeight() * bitmap.getRowBytes(); 121 | ByteBuffer buffer = ByteBuffer.allocate(size); 122 | bitmap.copyPixelsToBuffer(buffer); 123 | bytes = buffer.array(); 124 | 125 | } catch (IOException e) { 126 | e.printStackTrace(); 127 | } 128 | return bytes; 129 | } 130 | 131 | public static Uri getImageUriFromURL(String imageName, URL url, Context context) { 132 | return Uri.parse(url.toString()); 133 | } 134 | 135 | public static String getFileFromURL(URL url) { 136 | if (url != null) { 137 | return url.getFile(); 138 | } 139 | else { 140 | return null; 141 | } 142 | } 143 | 144 | public static void setConditionsImage(ImageView imageView, String conditionId) { 145 | 146 | Bitmap bitmap = getBitmapFromResources(conditionId); 147 | 148 | imageView.setImageBitmap(bitmap); 149 | } 150 | 151 | public static Uri getWeatherIconUri(String iconId) { 152 | 153 | String iconFileString = String.format(ICON_RESOURCE_TEMPLATE, iconId); 154 | return Uri.parse(RESOURCE_SCHEME + BuildConfig.APPLICATION_ID + "/" + DRAWABLE_FOLDER + "/" + iconFileString); 155 | } 156 | } 157 | 158 | 159 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/processor/JsonProcessor.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.processor; 2 | 3 | import org.json.JSONException; 4 | import org.json.JSONObject; 5 | 6 | public class JsonProcessor { 7 | 8 | /** 9 | * Process a String containing JSON into a JSONObject. 10 | * @param data the JSON String 11 | * @return the root JSON Object. 12 | */ 13 | public JSONObject getJsonFromString(String data) { 14 | JSONObject jsonObject = null; 15 | 16 | try { 17 | jsonObject = new JSONObject(data); 18 | } catch (JSONException e) { 19 | // TODO Auto-generated catch block 20 | e.printStackTrace(); 21 | } 22 | 23 | return jsonObject; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/smartdevicelink/SdlActivity.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.smartdevicelink; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.util.Log; 6 | import android.view.Menu; 7 | import android.view.MenuItem; 8 | 9 | import com.sdl.mobileweather.weather.AbbreviationDictionary; 10 | 11 | public class SdlActivity extends Activity { 12 | private boolean activityOnTop; 13 | 14 | /** 15 | * Activity is moving to the foreground. 16 | * Set this activity as the current activity and that it is on top. 17 | * Update the lockscreen. 18 | */ 19 | @Override 20 | protected void onResume() { 21 | super.onResume(); 22 | SdlApplication.setCurrentActivity(this); 23 | activityOnTop = true; 24 | } 25 | 26 | /** 27 | * Activity becoming partially visible (obstructed by another). 28 | * Activity if no longer on top. 29 | */ 30 | @Override 31 | protected void onPause() { 32 | activityOnTop = false; 33 | // Notify App that activity is leaving forground 34 | if (SdlApplication.getCurrentActivity() == this) { 35 | SdlApplication.setCurrentActivity(null); 36 | }else{ 37 | if(SdlApplication.getCurrentActivity() == null){ 38 | Log.v(SdlApplication.TAG, "Current activity already null."); 39 | }else{ 40 | Log.v(SdlApplication.TAG, "This activity is not on top."); 41 | } 42 | } 43 | super.onPause(); 44 | } 45 | 46 | @Override 47 | protected void onCreate(Bundle savedInstanceState) { 48 | super.onCreate(savedInstanceState); 49 | if(!AbbreviationDictionary.isPrepared()) 50 | AbbreviationDictionary.loadDictionary(this); 51 | } 52 | 53 | /** 54 | * Activity is no longer visible on the screen. 55 | */ 56 | @Override 57 | protected void onStop() { 58 | // Stop services if no other weather activity has taken foreground 59 | SdlApplication app = SdlApplication.getInstance(); 60 | if (app != null) { 61 | app.stopServices(); 62 | }else{ 63 | Log.d(SdlApplication.TAG, "onStop app==null"); 64 | } 65 | super.onStop(); 66 | } 67 | 68 | @Override 69 | protected void onDestroy() { 70 | super.onDestroy(); 71 | } 72 | 73 | @Override 74 | public boolean onCreateOptionsMenu(Menu menu) { 75 | return true; 76 | } 77 | 78 | @Override 79 | public boolean onOptionsItemSelected(MenuItem item) { 80 | return true; 81 | } 82 | 83 | public boolean isActivityonTop(){ 84 | return activityOnTop; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/smartdevicelink/SdlApplication.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.smartdevicelink; 2 | 3 | import android.app.Activity; 4 | import android.app.AlertDialog; 5 | import android.app.Application; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.content.pm.PackageManager.NameNotFoundException; 9 | import android.content.res.Configuration; 10 | import android.util.Log; 11 | 12 | import com.sdl.mobileweather.R; 13 | //import com.sdl.mobileweather.forecastio.ForecastIoService; 14 | import com.sdl.mobileweather.localization.LocalizationUtil; 15 | import com.sdl.mobileweather.location.PlayServicesConnectionChecker; 16 | import com.sdl.mobileweather.location.WeatherLocationServices; 17 | import com.sdl.mobileweather.openweathermap.OpenWeatherMapService; 18 | import com.sdl.mobileweather.weather.WeatherAlarmManager; 19 | import com.sdl.mobileweather.weather.WeatherDataManager; 20 | import com.sdl.mobileweather.weather.WeatherUpdateWakefulReceiver; 21 | import com.sdl.mobileweather.BuildConfig; 22 | import com.smartdevicelink.managers.SdlManager; 23 | 24 | import java.util.Locale; 25 | 26 | 27 | public class SdlApplication extends Application { 28 | 29 | public static final String TAG = "MobileWeather"; 30 | private static SdlApplication instance; 31 | private static Activity currentUIActivity; 32 | private WeatherLocationServices mLocationServices; 33 | private WeatherDataManager mDataManager; 34 | private WeatherAlarmManager mWeatherAlarm; 35 | private LocalizationUtil mLocalizationUtil; 36 | 37 | 38 | static { 39 | instance = null; 40 | } 41 | 42 | private static synchronized void setInstance(SdlApplication app) { 43 | instance = app; 44 | } 45 | 46 | public static synchronized SdlApplication getInstance() { 47 | return instance; 48 | } 49 | 50 | public static synchronized void setCurrentActivity(Activity act) { 51 | currentUIActivity = act; 52 | } 53 | 54 | public static synchronized Activity getCurrentActivity() { 55 | return currentUIActivity; 56 | } 57 | 58 | @Override 59 | public void onCreate() { 60 | super.onCreate(); 61 | SdlApplication.setInstance(this); 62 | mDataManager = new WeatherDataManager(); 63 | // TODO: Fix magic number assignment of update interval 64 | mDataManager.setUpdateInterval(5); 65 | mDataManager.setUnits(getResources().getString(R.string.units_default)); 66 | mLocalizationUtil = new LocalizationUtil(); 67 | //mWeatherAlarm = new WeatherAlarmManager(ForecastIoService.class); 68 | mWeatherAlarm = new WeatherAlarmManager(OpenWeatherMapService.class); 69 | mLocationServices = null; 70 | if (PlayServicesConnectionChecker.servicesConnected()) { 71 | mLocationServices = new WeatherLocationServices(); 72 | } 73 | } 74 | 75 | public void onConfigurationChanged(Configuration newConfig) { 76 | super.onConfigurationChanged(newConfig); 77 | Log.d(TAG, "onConfigurationChanged received"); 78 | Context mAppContext = SdlApplication.getInstance().getApplicationContext(); 79 | Intent mUpdateIntent = new Intent(mAppContext, WeatherUpdateWakefulReceiver.class); 80 | //mUpdateIntent.putExtra("weather_update_service", ForecastIoService.class.getName()); 81 | mUpdateIntent.putExtra("weather_update_service", OpenWeatherMapService.class.getName()); 82 | mAppContext.sendBroadcast(mUpdateIntent); 83 | if (mDataManager != null) { 84 | mDataManager.setUnits(getResources().getString(R.string.units_default)); 85 | } 86 | mLocalizationUtil.setLocaleCountry((Locale.getDefault()).getCountry()); 87 | mLocalizationUtil.setLocaleLanguage((Locale.getDefault()).getLanguage()); 88 | } 89 | 90 | 91 | @Override 92 | public void onLowMemory() { 93 | super.onLowMemory(); 94 | } 95 | 96 | public void startSdlProxyService() { 97 | Log.i(SdlApplication.TAG, "Starting SmartDeviceLink service"); 98 | if (BuildConfig.TRANSPORT.equals("MULTI")) { 99 | Log.i(TAG, "startSdlProxyService: Multiplex Selected"); 100 | SdlReceiver.queryForConnectedService(this); 101 | } else if (BuildConfig.TRANSPORT.equals("TCP")) { 102 | Log.i(TAG, "startSdlProxyService: TCP Selected"); 103 | Intent proxyIntent = new Intent(this, SdlService.class); 104 | startService(proxyIntent); 105 | } 106 | } 107 | 108 | // Recycle the proxy 109 | public void endSdlProxyInstance() { 110 | SdlService serviceInstance = SdlService.getInstance(); 111 | if (serviceInstance != null){ 112 | SdlManager sdlManagerInstance = serviceInstance.getSdlManager(); 113 | // if proxy exists, reset it 114 | if(sdlManagerInstance != null){ 115 | serviceInstance.reset(); 116 | // if proxy == null create proxy 117 | } else { 118 | serviceInstance.startProxy(); 119 | } 120 | } 121 | } 122 | 123 | public void startWeatherUpdates() { 124 | Log.i(SdlApplication.TAG, "Starting weather updates"); 125 | mWeatherAlarm.startPendingLocation(); 126 | } 127 | 128 | public void endWeatherUpdates() { 129 | Log.i(SdlApplication.TAG, "Stopping weather updates"); 130 | mWeatherAlarm.stop(); 131 | } 132 | 133 | public void startLocationServices() { 134 | Log.i(SdlApplication.TAG, "Starting location service"); 135 | if (mLocationServices != null) { 136 | mLocationServices.start(); 137 | } 138 | } 139 | 140 | public void endLocationServices() { 141 | Log.i(SdlApplication.TAG, "Stopping location service"); 142 | if (mLocationServices != null) { 143 | mLocationServices.stop(); 144 | } 145 | } 146 | 147 | public void startServices() { 148 | startSdlProxyService(); 149 | startLocationServices(); 150 | startWeatherUpdates(); 151 | } 152 | 153 | public void stopServices() { 154 | boolean noUIActivity; 155 | boolean noApplinkService; 156 | 157 | noUIActivity = (currentUIActivity == null); 158 | noApplinkService = (SdlService.getInstance() == null); 159 | 160 | Log.d(TAG, "Attempting to stop services"); 161 | if(noUIActivity && noApplinkService){ 162 | Log.d(TAG, "Stopping services"); 163 | endWeatherUpdates(); 164 | endLocationServices(); 165 | return; 166 | } 167 | Log.d(TAG, "Not stopping services"); 168 | } 169 | 170 | public void stopServices(boolean appLinkStopping) { 171 | if(appLinkStopping){ 172 | if(currentUIActivity == null){ 173 | Log.d(TAG, "Attempt force stop services"); 174 | endWeatherUpdates(); 175 | endLocationServices(); 176 | } else { 177 | Log.d(TAG, "Application in foreground on phone. Not stopping loc + weath services"); 178 | } 179 | } else { 180 | stopServices(); 181 | } 182 | } 183 | 184 | public void showAppVersion(Context context) { 185 | 186 | String appMessage = getResources().getString(R.string.mobileweather_ver_not_available); 187 | String proxyMessage = getResources().getString(R.string.proxy_ver_not_available); 188 | SdlService serviceInstance = SdlService.getInstance(); 189 | try { 190 | appMessage = getResources().getString(R.string.mobileweather_ver) + 191 | getPackageManager().getPackageInfo(getPackageName(), 0).versionName; 192 | } catch (NameNotFoundException e) { 193 | Log.d(SdlApplication.TAG, "Can't get package info", e); 194 | } 195 | 196 | proxyMessage = getResources().getString(R.string.proxy_ver) + BuildConfig.VERSION_NAME; 197 | new AlertDialog.Builder(context).setTitle((getResources().getString(R.string.app_ver))) 198 | .setMessage(appMessage + "\r\n" + proxyMessage) 199 | .setNeutralButton(android.R.string.ok, null).create().show(); 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/smartdevicelink/SdlReceiver.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.smartdevicelink; 2 | 3 | import android.app.PendingIntent; 4 | import android.bluetooth.BluetoothDevice; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.os.Build; 8 | import android.util.Log; 9 | 10 | import com.smartdevicelink.transport.SdlBroadcastReceiver; 11 | import com.smartdevicelink.transport.SdlRouterService; 12 | import com.smartdevicelink.transport.TransportConstants; 13 | import com.smartdevicelink.util.AndroidTools; 14 | 15 | public class SdlReceiver extends SdlBroadcastReceiver { 16 | 17 | 18 | @Override 19 | public void onReceive(Context context, Intent intent) { 20 | // Start services on BT connection 21 | if (intent.getAction().compareTo(BluetoothDevice.ACTION_ACL_CONNECTED) == 0) { 22 | Log.i(SdlApplication.TAG, "ACL Connect"); 23 | SdlApplication app = SdlApplication.getInstance(); 24 | if (app != null) { 25 | Log.i(SdlApplication.TAG, "Starting services"); 26 | app.startLocationServices(); 27 | app.startWeatherUpdates(); 28 | } 29 | } 30 | // Stop services on BT disconnection 31 | else if (intent.getAction().compareTo(BluetoothDevice.ACTION_ACL_DISCONNECTED) == 0) { 32 | Log.i(SdlApplication.TAG, "ACL Disconnect"); 33 | SdlApplication app = SdlApplication.getInstance(); 34 | if (app != null) { 35 | if (SdlApplication.getCurrentActivity() == null) { 36 | app.stopServices(); 37 | } 38 | } 39 | } 40 | 41 | super.onReceive(context, intent); 42 | } 43 | 44 | 45 | @Override 46 | public void onSdlEnabled(Context context, Intent intent) { 47 | //Use the provided intent but set the class to the SdlService 48 | intent.setClass(context, SdlService.class); 49 | 50 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { 51 | if (intent.getParcelableExtra(TransportConstants.PENDING_INTENT_EXTRA) != null) { 52 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { 53 | if (!AndroidTools.hasForegroundServiceTypePermission(context)) { 54 | return; 55 | } 56 | } 57 | PendingIntent pendingIntent = (PendingIntent) intent.getParcelableExtra(TransportConstants.PENDING_INTENT_EXTRA); 58 | try { 59 | //Here we are allowing the RouterService that is in the Foreground to start the SdlService on our behalf 60 | pendingIntent.send(context, 0, intent); 61 | } catch (PendingIntent.CanceledException e) { 62 | e.printStackTrace(); 63 | } 64 | } 65 | } else { 66 | // SdlService needs to be foregrounded in Android O and above 67 | // This will prevent apps in the background from crashing when they try to start SdlService 68 | // Because Android O doesn't allow background apps to start background services 69 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 70 | context.startForegroundService(intent); 71 | } else { 72 | context.startService(intent); 73 | } 74 | } 75 | } 76 | 77 | 78 | @Override 79 | public Class defineLocalSdlRouterClass() { 80 | //Return a local copy of the SdlRouterService located in your project 81 | return com.sdl.mobileweather.smartdevicelink.SdlRouterService.class; 82 | } 83 | 84 | } -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/smartdevicelink/SdlRouterService.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.smartdevicelink; 2 | 3 | public class SdlRouterService extends com.smartdevicelink.transport.SdlRouterService { 4 | 5 | 6 | } -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/weather/AbbreviationDictionary.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.weather; 2 | 3 | import java.util.HashMap; 4 | 5 | import org.xmlpull.v1.XmlPullParser; 6 | 7 | import android.content.Context; 8 | import android.content.res.XmlResourceParser; 9 | import android.util.Log; 10 | 11 | import com.sdl.mobileweather.R; 12 | import com.sdl.mobileweather.smartdevicelink.SdlApplication; 13 | 14 | public class AbbreviationDictionary { 15 | 16 | private static HashMap mAbrvDictionary = null; 17 | private static Object mDictionaryLock = new Object(); 18 | 19 | public static String lookUp(String term){ 20 | synchronized (mDictionaryLock) { 21 | return mAbrvDictionary.get(term); 22 | } 23 | } 24 | 25 | public static boolean isPrepared(){ 26 | synchronized (mDictionaryLock) { 27 | return (mAbrvDictionary != null); 28 | } 29 | } 30 | 31 | public static boolean loadDictionary(Context context){ 32 | synchronized (mDictionaryLock) { 33 | XmlResourceParser parser = context.getResources().getXml(R.xml.weather_abrv); 34 | mAbrvDictionary = new HashMap(); 35 | String key = null; 36 | String value = null; 37 | try { 38 | int eventType = parser.getEventType(); 39 | 40 | while (eventType != XmlPullParser.END_DOCUMENT) { 41 | if (eventType == XmlPullParser.START_DOCUMENT) { 42 | 43 | Log.i(SdlApplication.TAG, 44 | "START_DOCUMENT: Loading abbreviation XML file"); 45 | } else if (eventType == XmlPullParser.START_TAG) { 46 | if (parser.getName().equals("entry")) { 47 | Log.d(SdlApplication.TAG, "START_TAG"); 48 | key = parser.getAttributeValue(null, "key"); 49 | if (key == null) { 50 | parser.close(); 51 | break; 52 | } 53 | } 54 | } else if (eventType == XmlPullParser.TEXT) { 55 | if (key != null) { 56 | Log.d(SdlApplication.TAG, "TEXT"); 57 | value = parser.getText(); 58 | } 59 | 60 | } else if (eventType == XmlPullParser.END_TAG) { 61 | if (parser.getName().equals("entry")) { 62 | mAbrvDictionary.put(key, value); 63 | Log.v(SdlApplication.TAG, 64 | String.format("END_TAG: %s, %s", key, value)); 65 | key = null; 66 | value = null; 67 | } 68 | } 69 | eventType = parser.next(); 70 | } 71 | } catch (Exception e) { 72 | e.printStackTrace(); 73 | Log.d(SdlApplication.TAG, 74 | "Loading exception: mAbrvDictionary=null"); 75 | mAbrvDictionary = null; 76 | return false; 77 | } 78 | return true; 79 | } 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/weather/Forecast.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.weather; 2 | 3 | import java.util.Calendar; 4 | 5 | public class Forecast { 6 | public Calendar date; 7 | public String conditionTitle; 8 | public String conditionId; 9 | public Float temperature = (float)0; 10 | public Float highTemperature = (float)0; 11 | public Float lowTemperature = (float)0; 12 | public Float humidity = (float)0; 13 | public Float windSpeed = (float)0; 14 | public Float precipitation = (float)0; 15 | public Integer precipitationChance = 0; 16 | public Float snow = (float)0; 17 | } 18 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/weather/InfoType.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.weather; 2 | 3 | /** 4 | * Different weather information types that can be displayed. 5 | */ 6 | public enum InfoType { 7 | WEATHER_CONDITIONS, HOURLY_FORECAST, STANDARD_FORECAST, DAILY_FORECAST, ALERTS, ROAD_CONDITIONS, NONE, WELCOME_SCREEN; 8 | } 9 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/weather/RoadConditions.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.weather; 2 | 3 | public enum RoadConditions { 4 | WET, ICE, FOG, WIND, LOW_VISIBILITY, OK 5 | } 6 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/weather/UnitConverter.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.weather; 2 | 3 | public final class UnitConverter { 4 | 5 | private UnitConverter(){ 6 | // Constructor is private to prevent instantiation of static 7 | // method provider 8 | } 9 | 10 | /** 11 | * Convert length from mm to inches. 12 | * @param length 13 | * @return 14 | */ 15 | public static float convertLengthToImperial(float length) { 16 | return (float) (length * 0.0393701); 17 | } 18 | 19 | /** 20 | * Convert speed from kph to mph 21 | * @param speed 22 | * @return 23 | */ 24 | public static float convertSpeedToImperial(float speed) { 25 | return (float) (speed * 0.621371); 26 | } 27 | 28 | /** 29 | * Convert from Celsius to Fahrenheit. 30 | * @param temperature the temperature in Celsius. 31 | * @return temperature temperature in Fahrenheit. 32 | */ 33 | public static float convertTemperatureToImperial(float temperature) { 34 | return (float) ((temperature * 9 / 5) + 32); 35 | } 36 | 37 | /** 38 | * Convert length from inches to mm. 39 | * @param length 40 | * @return 41 | */ 42 | public static float convertLengthToMetric(float length) { 43 | return (float) (length / 0.0393701); 44 | } 45 | 46 | /** 47 | * Convert speed from mph to kph 48 | * @param speed 49 | * @return 50 | */ 51 | public static float convertSpeedToMetric(float speed) { 52 | return (float) (speed / 0.621371); 53 | } 54 | 55 | /** 56 | * Convert from Fahrenheit to Celsius. 57 | * @param temperature the temperature in Fahrenheit. 58 | * @return temperature temperature in Celsius. 59 | */ 60 | public static float convertTemperatureToMetric(float temperature) { 61 | return (float) ((temperature - 32) * 5 / 9); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/weather/WeatherAlarmManager.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.weather; 2 | 3 | import android.os.Build; 4 | import java.util.Calendar; 5 | 6 | import android.app.AlarmManager; 7 | import android.app.PendingIntent; 8 | import android.content.BroadcastReceiver; 9 | import android.content.Context; 10 | import android.content.Intent; 11 | import android.content.IntentFilter; 12 | import android.os.Bundle; 13 | import androidx.localbroadcastmanager.content.LocalBroadcastManager; 14 | import android.util.Log; 15 | 16 | import com.sdl.mobileweather.artifact.WeatherLocation; 17 | import com.sdl.mobileweather.smartdevicelink.SdlApplication; 18 | 19 | public class WeatherAlarmManager { 20 | 21 | private static int PENDING_INTENT_ID = 100000; 22 | 23 | private boolean mAlarmRunning = false; 24 | private int mUpdateInterval = 5; // The number of minutes between weather updates 25 | private AlarmManager mAlarmManager = null; // Alarm manager to manage recurring updates 26 | private PendingIntent mAlarmIntent = null; // Intent used to trigger an update 27 | private boolean mFirstLocationUpdate = true; // Used to trigger a manual weather update on the first location update 28 | private WeatherDataManager mDataManager = null; 29 | private Context mAppContext = null; 30 | private Intent mUpdateIntent = null; 31 | 32 | /** 33 | * Receiver 34 | */ 35 | private final BroadcastReceiver mChangeUpdateIntervalReceiver = new BroadcastReceiver() { 36 | 37 | @Override 38 | public void onReceive(Context context, Intent intent) { 39 | Log.d(SdlApplication.TAG, "UpdateIntervalReceiver"); 40 | if (mAlarmRunning && mDataManager != null) { 41 | mUpdateInterval = mDataManager.getUpdateInterval(); 42 | restartAlarm(context); 43 | } 44 | } 45 | }; 46 | 47 | /** 48 | * Receiver 49 | */ 50 | private final BroadcastReceiver mChangeLocationReceiver = new BroadcastReceiver() { 51 | 52 | @Override 53 | public void onReceive(Context context, Intent intent) { 54 | WeatherLocation currentLocation = null; 55 | String lastCity = null; 56 | String lastState = null; 57 | Log.d("MobileWeather", "WeatherAlarmManager - ChangeLocationReceiver"); 58 | if (mDataManager != null) { 59 | currentLocation = mDataManager.getCurrentLocation(); 60 | lastCity = mDataManager.getLastCity(); 61 | lastState = mDataManager.getLastState(); 62 | } 63 | if (mFirstLocationUpdate) { 64 | Log.d("MobileWeather", "WeatherAlarmManager - first location - starting weather update alarm"); 65 | restartAlarm(context); 66 | mFirstLocationUpdate = false; 67 | mAppContext.sendBroadcast(mUpdateIntent); 68 | } 69 | else if (currentLocation != null && currentLocation.city != null) { 70 | if((!currentLocation.city.equals(lastCity)) || 71 | ((currentLocation.state != null) && (!currentLocation.state.equals(lastState)))) { 72 | Log.d("MobileWeather", "city/state change - weather update"); 73 | mAppContext.sendBroadcast(mUpdateIntent); 74 | } 75 | } 76 | } 77 | }; 78 | 79 | /** 80 | * Receiver 81 | */ 82 | private final BroadcastReceiver mRoamingStatusReceiver = new BroadcastReceiver() { 83 | 84 | @Override 85 | public void onReceive(Context context, Intent intent) { 86 | Bundle bundle = intent.getExtras(); 87 | if (bundle != null) { 88 | boolean roaming = bundle.getBoolean("roaming"); 89 | if (roaming) { 90 | if (mAlarmManager != null && mAlarmIntent != null) { 91 | mAlarmManager.cancel(mAlarmIntent); 92 | mAlarmIntent = null; 93 | mAlarmRunning = false; 94 | } 95 | } 96 | else { 97 | Log.d("MobileWeather", "WeatherAlarmManager - restarting alarm"); 98 | restartAlarm(context); 99 | } 100 | } 101 | } 102 | }; 103 | 104 | public WeatherAlarmManager(Class weatherService) { 105 | Log.d(SdlApplication.TAG, "WeatherAlarmManager - constructed"); 106 | mAlarmRunning = false; 107 | mDataManager = WeatherDataManager.getInstance(); 108 | mAppContext = SdlApplication.getInstance().getApplicationContext(); 109 | if (mDataManager != null) { 110 | mUpdateInterval = mDataManager.getUpdateInterval(); 111 | } 112 | String weatherServiceName = weatherService.getName(); 113 | mUpdateIntent = new Intent(mAppContext, WeatherUpdateWakefulReceiver.class); 114 | mUpdateIntent.putExtra(WeatherUpdateWakefulReceiver.WEATHER_UPDATE_SERVICE, weatherServiceName); 115 | } 116 | 117 | public void startPendingLocation() { 118 | Log.d(SdlApplication.TAG, "WeatherAlarmManager - registering local receivers"); 119 | LocalBroadcastManager lbManager = LocalBroadcastManager.getInstance(mAppContext); 120 | lbManager.registerReceiver(mChangeUpdateIntervalReceiver, new IntentFilter("com.sdl.mobileweather.ChangeUpdateInterval")); 121 | lbManager.registerReceiver(mChangeLocationReceiver, new IntentFilter("com.sdl.mobileweather.Location")); 122 | lbManager.registerReceiver(mRoamingStatusReceiver, new IntentFilter("com.sdl.mobileweather.RoamingStatus")); 123 | } 124 | 125 | public void stop() { 126 | Log.d(SdlApplication.TAG, "WeatherAlarmManager - unregistering local receivers"); 127 | cancelUpdates(); 128 | try { 129 | LocalBroadcastManager lbManager = LocalBroadcastManager.getInstance(mAppContext); 130 | lbManager.unregisterReceiver(mChangeUpdateIntervalReceiver); 131 | lbManager.unregisterReceiver(mChangeLocationReceiver); 132 | lbManager.unregisterReceiver(mRoamingStatusReceiver); 133 | 134 | } catch (IllegalArgumentException e) { 135 | e.printStackTrace(); 136 | } 137 | } 138 | 139 | /** 140 | * Stop and cancel any update alarms, and start/restart the timer. 141 | * This is used for periodic weather updates. 142 | */ 143 | private void restartAlarm(Context context) { 144 | Log.d(SdlApplication.TAG, "WeatherAlarmManager - restartAlarm"); 145 | if (mAlarmManager == null) { 146 | mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 147 | } 148 | 149 | if (mAlarmManager != null && mAlarmIntent != null) { 150 | mAlarmManager.cancel(mAlarmIntent); 151 | } 152 | 153 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { 154 | mAlarmIntent = PendingIntent.getBroadcast(context, PENDING_INTENT_ID, mUpdateIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); 155 | } 156 | else { 157 | mAlarmIntent = PendingIntent.getBroadcast(context, PENDING_INTENT_ID, mUpdateIntent, PendingIntent.FLAG_UPDATE_CURRENT); 158 | } 159 | 160 | Calendar cal = Calendar.getInstance(); 161 | cal.setTimeInMillis(System.currentTimeMillis()); 162 | cal.add(Calendar.MINUTE, mUpdateInterval); 163 | Log.d(SdlApplication.TAG, "restartAlarm mUpdateInterval = " + mUpdateInterval); 164 | 165 | mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), (mUpdateInterval*60*1000), mAlarmIntent); 166 | mAlarmRunning = true; 167 | } 168 | 169 | /** 170 | * Stop and cancel any update alarm. 171 | */ 172 | private void cancelUpdates() { 173 | Log.i(SdlApplication.TAG, "Canceling periodic weather updates.."); 174 | if (mAlarmManager != null && mAlarmIntent != null) { 175 | mAlarmManager.cancel(mAlarmIntent); 176 | mAlarmRunning = false; 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/weather/WeatherAlert.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.weather; 2 | 3 | import java.util.Calendar; 4 | 5 | public class WeatherAlert { 6 | public WeatherAlertType type; 7 | public String description; 8 | public Calendar dateIssued; 9 | public Calendar dateExpires; 10 | public String message; 11 | } 12 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/weather/WeatherAlertType.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.weather; 2 | 3 | public enum WeatherAlertType { 4 | HUR, TOR, TOW, WRN, SEW, WIN, FLO, WAT, WND, SVR, HEA, FOG, SPE, FIR, VOL, HWW, REC, REP, PUB; 5 | } 6 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/weather/WeatherConditions.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.weather; 2 | 3 | public class WeatherConditions { 4 | public String conditionTitle; 5 | public String conditionId; 6 | public Float temperature; 7 | public Float humidity; 8 | public Float windSpeed; 9 | public Float windSpeedGust; 10 | public Float visibility; 11 | public Float feelsLikeTemperature; 12 | public Float precipitation; 13 | } 14 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/weather/WeatherDataManager.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.weather; 2 | 3 | import com.sdl.mobileweather.artifact.WeatherLocation; 4 | 5 | public class WeatherDataManager { 6 | 7 | private static WeatherDataManager instance; 8 | 9 | private WeatherLocation mCurrentLocation = null; // Stores the current location for weather 10 | private String mUnits = null; // Stores the current units 11 | private int mUpdateInterval = 0; // Stores the update interval 12 | private WeatherConditions mWeatherConditions = null; // Stores the current weather conditions 13 | private RoadConditions mRoadConditions = null; // Stores the current road conditions 14 | private WeatherAlert[] mAlerts = null; // Stores the current weather alerts 15 | private Forecast[] mForecast = null; // Stores the current forecast 16 | private Forecast[] mHourlyForecast = null; // Stores the current hourly forecast 17 | private String mLastCity = null; // Stores the city of the last weather update 18 | private String mLastState = null; // Stores the state of the last weather update 19 | 20 | // Locks used for thread safety 21 | private final Object mCurrentLocationLock = new Object(); 22 | private final Object mUnitsLock = new Object(); 23 | private final Object mUpdateIntervalLock = new Object(); 24 | private final Object mWeatherConditionsLock = new Object(); 25 | private final Object mRoadConditionsLock = new Object(); 26 | private final Object mAlertsLock = new Object(); 27 | private final Object mForecastLock = new Object(); 28 | private final Object mHourlyForecastLock = new Object(); 29 | private final Object mLastCityLock = new Object(); 30 | private final Object mLastStateLock = new Object(); 31 | 32 | // Error state booleans 33 | private boolean mLocationAvailable = false; 34 | private boolean mAPIAvailable = false; 35 | private boolean mNetworkAvailable = false; 36 | 37 | private final Object mErrorLock = new Object(); 38 | 39 | static { 40 | instance = null; 41 | } 42 | 43 | private static synchronized void setInstance(WeatherDataManager man) { 44 | instance = man; 45 | } 46 | 47 | public static synchronized WeatherDataManager getInstance() { 48 | return instance; 49 | } 50 | 51 | public WeatherDataManager() { 52 | WeatherDataManager.setInstance(this); 53 | } 54 | 55 | public WeatherLocation getCurrentLocation() { 56 | synchronized (mCurrentLocationLock) { 57 | return mCurrentLocation; 58 | } 59 | } 60 | 61 | public void setCurrentLocation(WeatherLocation location) { 62 | synchronized (mCurrentLocationLock) { 63 | this.mCurrentLocation = location; 64 | } 65 | } 66 | 67 | public WeatherConditions getWeatherConditions() { 68 | synchronized (mWeatherConditionsLock) { 69 | return mWeatherConditions; 70 | } 71 | } 72 | 73 | public void setWeatherConditions(WeatherConditions conditions) { 74 | synchronized (mWeatherConditionsLock) { 75 | this.mWeatherConditions = conditions; 76 | } 77 | } 78 | 79 | public RoadConditions getRoadConditions() { 80 | synchronized (mRoadConditionsLock) { 81 | return mRoadConditions; 82 | } 83 | } 84 | 85 | public void setRoadConditions(RoadConditions roadConditions) { 86 | synchronized (mRoadConditionsLock) { 87 | this.mRoadConditions = roadConditions; 88 | } 89 | } 90 | 91 | public WeatherAlert[] getAlerts() { 92 | synchronized (mAlertsLock) { 93 | return mAlerts; 94 | } 95 | } 96 | 97 | public void setAlerts(WeatherAlert[] alerts) { 98 | synchronized (mAlertsLock) { 99 | this.mAlerts = alerts; 100 | } 101 | } 102 | 103 | public Forecast[] getForecast() { 104 | synchronized (mForecastLock) { 105 | return mForecast; 106 | } 107 | } 108 | 109 | public void setForecast(Forecast[] forecast) { 110 | synchronized (mForecastLock) { 111 | this.mForecast = forecast; 112 | } 113 | } 114 | 115 | public Forecast[] getHourlyForecast() { 116 | synchronized (mHourlyForecastLock) { 117 | return mHourlyForecast; 118 | } 119 | } 120 | 121 | public void setHourlyForecast(Forecast[] hourlyForecast) { 122 | synchronized (mHourlyForecastLock) { 123 | this.mHourlyForecast = hourlyForecast; 124 | } 125 | } 126 | 127 | public String getUnits() { 128 | synchronized (mUnitsLock) { 129 | return mUnits; 130 | } 131 | } 132 | 133 | public void setUnits(String units) { 134 | synchronized (mUnitsLock) { 135 | this.mUnits = units; 136 | } 137 | } 138 | 139 | public int getUpdateInterval() { 140 | synchronized (mUpdateIntervalLock) { 141 | return mUpdateInterval; 142 | } 143 | } 144 | 145 | public void setUpdateInterval(int mUpdateInterval) { 146 | synchronized (mUpdateIntervalLock) { 147 | this.mUpdateInterval = mUpdateInterval; 148 | } 149 | } 150 | 151 | public String getLastCity() { 152 | synchronized (mLastCityLock) { 153 | return mLastCity; 154 | } 155 | } 156 | 157 | public void setLastCity(String city) { 158 | synchronized (mLastCityLock) { 159 | this.mLastCity = city; 160 | } 161 | } 162 | 163 | public String getLastState() { 164 | synchronized (mLastStateLock) { 165 | return mLastState; 166 | } 167 | } 168 | 169 | public void setLastState(String state) { 170 | synchronized (mLastStateLock) { 171 | this.mLastState = state; 172 | } 173 | } 174 | 175 | public boolean isLocationAvailable() { 176 | synchronized(mErrorLock){ 177 | return mLocationAvailable; 178 | } 179 | } 180 | 181 | public void setLocationAvailable(boolean available){ 182 | synchronized(mErrorLock){ 183 | mLocationAvailable = available; 184 | if(available){ 185 | mNetworkAvailable = true; 186 | } 187 | } 188 | } 189 | 190 | public boolean isAPIAvailable() { 191 | synchronized (mErrorLock) { 192 | return mAPIAvailable; 193 | } 194 | } 195 | 196 | public void setAPIAvailable(boolean available) { 197 | synchronized (mErrorLock) { 198 | mAPIAvailable = available; 199 | if(available){ 200 | mNetworkAvailable = true; 201 | } 202 | } 203 | } 204 | 205 | public boolean isNetworkAvailable(){ 206 | synchronized(mErrorLock){ 207 | return mNetworkAvailable; 208 | } 209 | } 210 | 211 | public void setNetworkAvailable(boolean available){ 212 | synchronized (mErrorLock){ 213 | mNetworkAvailable = available; 214 | } 215 | } 216 | 217 | public boolean isInErrorState(){ 218 | synchronized(mErrorLock){ 219 | return !(mNetworkAvailable && mAPIAvailable && mLocationAvailable); 220 | } 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/weather/WeatherJsonProcessor.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.weather; 2 | 3 | import org.json.JSONObject; 4 | 5 | public interface WeatherJsonProcessor { 6 | 7 | public Forecast[] getForecast(JSONObject forecastJson); 8 | public Forecast[] getHourlyForecast(JSONObject forecastJson); 9 | public WeatherConditions getWeatherConditions(JSONObject conditionsJson); 10 | public WeatherAlert[] getAlerts(JSONObject alertsJson); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/weather/WeatherService.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.weather; 2 | 3 | import java.net.URL; 4 | 5 | import android.app.IntentService; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.net.ConnectivityManager; 9 | import android.net.NetworkInfo; 10 | import androidx.localbroadcastmanager.content.LocalBroadcastManager; 11 | import android.util.Log; 12 | 13 | import com.sdl.mobileweather.artifact.WeatherLocation; 14 | 15 | public abstract class WeatherService extends IntentService { 16 | 17 | protected boolean mFirstLocationUpdate = true; // Used to trigger a manual weather update on the first location update 18 | protected WeatherDataManager mDataManager = null; 19 | protected WeatherJsonProcessor mWeatherProcessor = null; 20 | 21 | protected abstract URL[] getURLs(WeatherLocation currentLocation); 22 | 23 | protected abstract void updateWeatherData(URL... urls); 24 | 25 | public WeatherService() { 26 | super("WeatherService"); 27 | } 28 | 29 | @Override 30 | protected void onHandleIntent(Intent intent) { 31 | 32 | Log.d("MobileWeather", "WeatherService - Intent recieved"); 33 | WeatherLocation currentLocation = null; 34 | mDataManager = WeatherDataManager.getInstance(); 35 | 36 | if (mDataManager != null) { 37 | currentLocation = mDataManager.getCurrentLocation(); 38 | } 39 | 40 | if (currentLocation != null) { 41 | Log.d("MobileWeather", "WeatherService - valid location"); 42 | URL[] weatherURLs = getURLs(currentLocation); 43 | updateWeatherData(weatherURLs); 44 | } 45 | 46 | WeatherUpdateWakefulReceiver.completeWakefulIntent(intent); 47 | } 48 | 49 | protected void reportConnectionAvail(boolean avail){ 50 | if(mDataManager != null){ 51 | if(mDataManager.isNetworkAvailable() != avail){ 52 | ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); 53 | NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); 54 | boolean isConnected = activeNetwork != null && 55 | activeNetwork.isConnectedOrConnecting(); 56 | mDataManager.setNetworkAvailable(isConnected); 57 | if(!isConnected){ 58 | Intent intent = new Intent("com.sdl.mobileweather.ErrorUpdate"); 59 | LocalBroadcastManager.getInstance(this).sendBroadcast(intent); 60 | } 61 | } 62 | } 63 | } 64 | 65 | protected void reportApiAvail(boolean avail){ 66 | if (mDataManager != null){ 67 | if(avail != mDataManager.isAPIAvailable()){ 68 | mDataManager.setAPIAvailable(avail); 69 | Intent intent = new Intent("com.sdl.mobileweather.ErrorUpdate"); 70 | LocalBroadcastManager.getInstance(this).sendBroadcast(intent); 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/java/com/sdl/mobileweather/weather/WeatherUpdateWakefulReceiver.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather.weather; 2 | 3 | import com.sdl.mobileweather.smartdevicelink.SdlApplication; 4 | 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import androidx.legacy.content.WakefulBroadcastReceiver; 8 | import android.util.Log; 9 | 10 | public class WeatherUpdateWakefulReceiver extends WakefulBroadcastReceiver { 11 | 12 | protected final static String WEATHER_UPDATE_SERVICE = "weather_update_service"; 13 | 14 | @Override 15 | public void onReceive(Context context, Intent intent) { 16 | String className = intent.getStringExtra(WEATHER_UPDATE_SERVICE); 17 | Intent service; 18 | if(className != null){ 19 | try { 20 | service = new Intent(context, Class.forName(className)); 21 | startWakefulService(context, service); 22 | } catch (ClassNotFoundException e) { 23 | Log.d(SdlApplication.TAG, "WakefulBroadcastReceiver - invalid class name"); 24 | e.printStackTrace(); 25 | } 26 | }else{ 27 | Log.d(SdlApplication.TAG, "WakefulBroadcastReceiver - no service specified"); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/chancesnow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/chancesnow.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/chancesnow_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/chancesnow_50.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/chancestorm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/chancestorm.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/chancestorm_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/chancestorm_50.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/chancethunderstorm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/chancethunderstorm.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/chancethunderstorm_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/chancethunderstorm_50.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/clearday.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/clearday.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/clearday_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/clearday_50.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/clearnight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/clearnight.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/clearnight_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/clearnight_50.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/cloudy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/cloudy.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/cloudy_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/cloudy_50.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/dusk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/dusk.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/dusk_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/dusk_50.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/fog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/fog.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/fog_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/fog_50.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/fogcloudy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/fogcloudy.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/fogcloudy_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/fogcloudy_50.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/fognight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/fognight.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/fognight_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/fognight_50.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/ford_oval.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/ford_oval.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/hazy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/hazy.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/hazy_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/hazy_50.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/ic_drawer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/ic_drawer.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/icon_license.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/lightrain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/lightrain.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/lightrain_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/lightrain_50.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/lightsnow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/lightsnow.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/lightsnow_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/lightsnow_50.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/partlycloudyday.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/partlycloudyday.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/partlycloudyday_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/partlycloudyday_50.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/partlycloudynight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/partlycloudynight.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/partlycloudynight_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/partlycloudynight_50.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/partlysunny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/partlysunny.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/partlysunny_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/partlysunny_50.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/powered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/powered.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/rain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/rain.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/rain_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/rain_50.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/sleet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/sleet.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/sleet_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/sleet_50.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/snow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/snow.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/snow_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/snow_50.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/snowflake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/snowflake.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/snowflake_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/snowflake_50.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/storm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/storm.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/storm_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/storm_50.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/sunrise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/sunrise.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/sunrise_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/sunrise_50.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/thunderstorm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/thunderstorm.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/thunderstorm_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/thunderstorm_50.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/windy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/windy.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/windy_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/windy_50.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/windycloudy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/windycloudy.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/windycloudy_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/windycloudy_50.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/windyrain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/windyrain.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/windyrain_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/windyrain_50.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-hdpi/wunderground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-hdpi/wunderground.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-mdpi/ic_add_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-mdpi/ic_add_location.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-mdpi/ic_current_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-mdpi/ic_current_location.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-mdpi/ic_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-mdpi/ic_settings.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-xhdpi/ic_add_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-xhdpi/ic_add_location.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-xhdpi/ic_current_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-xhdpi/ic_current_location.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-xhdpi/ic_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-xhdpi/ic_settings.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-xxhdpi/ic_add_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-xxhdpi/ic_add_location.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-xxhdpi/ic_current_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-xxhdpi/ic_current_location.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-xxhdpi/ic_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-xxhdpi/ic_settings.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable-xxhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable-xxhdpi/icon.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable/ic_01d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable/ic_01d.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable/ic_01n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable/ic_01n.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable/ic_02d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable/ic_02d.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable/ic_02n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable/ic_02n.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable/ic_03d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable/ic_03d.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable/ic_03n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable/ic_03n.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable/ic_04d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable/ic_04d.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable/ic_04n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable/ic_04n.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable/ic_09d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable/ic_09d.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable/ic_09n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable/ic_09n.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable/ic_10d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable/ic_10d.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable/ic_10n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable/ic_10n.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable/ic_11d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable/ic_11d.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable/ic_11n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable/ic_11n.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable/ic_13d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable/ic_13d.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable/ic_13n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable/ic_13n.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable/ic_50d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable/ic_50d.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable/ic_50n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable/ic_50n.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/drawable/ic_none.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/drawable/ic_none.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 15 | 16 | 17 | 26 | 27 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/layout/drawer_list_item.xml: -------------------------------------------------------------------------------- 1 | 12 | 13 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/layout/forecast_list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 18 | 19 | 29 | 30 | 39 | 40 | 48 | 49 | 58 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/layout/fragment_alerts.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/layout/fragment_base.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/layout/fragment_conditions.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 18 | 19 | 29 | 30 | 41 | 42 | 52 | 53 | 63 | 64 | 74 | 75 | 88 | 89 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/layout/fragment_forecast.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 17 | 18 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/layout/lockscreen.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 21 | 22 | 33 | 34 | 35 | 36 | 45 | 46 | 52 | 53 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/menu/alerts.xml: -------------------------------------------------------------------------------- 1 |

2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/menu/forecast.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartDeviceLink-Examples/example_weather_app_android/707d279565e73cb9637863954c1b067571c6ad28/MobileWeather/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/values-es-rES/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MobileWeather 5 | Salir 6 | SDL 7 | Ford 8 | Subte 9 | Configurar 10 | MobileWeather 11 | Alertas 12 | Condición Climática 13 | Pronóstico 14 | Abrir Navegador 15 | Cerrar Navegador 16 | "Localizando…" 17 | --- 18 | "Cargando…" 19 | Velocidad del Viento 20 | Humedad 21 | Se Siente Como 22 | "Localizando…" 23 | "Cargando…" 24 | "Información de la versión del Proxy no disponible" 25 | "Información de la versión de MobileWeather no disponible" 26 | "Información de la versión no disponible" 27 | "Versión de Proxy: " 28 | "Versión de App:" 29 | "Versión de MobileWeather: " 30 | metric 31 | 32 | 33 | 34 | 35 | @string/drawer_item_current_location 36 | @string/drawer_item_add_location 37 | @string/drawer_item_settings 38 | @string/drawer_item_update_weather 39 | @string/drawer_item_reset_sync 40 | @string/drawer_item_about 41 | 42 | 43 | 44 | "Ubicación Actual" 45 | "Agregar Ubicación" 46 | "Configurar" 47 | "Actualizar el Clima" 48 | "Resetear el Sync" 49 | "Acerca De" 50 | 51 | 52 | "Unidades Imperiales" 53 | "F" 54 | "MPH" 55 | "Millas por Hora" 56 | "pulgadas" 57 | "Unidades Métricas" 58 | "C" 59 | "KPH" 60 | "Kilómetros por Hora" 61 | "milímetros" 62 | 63 | 64 | "Actual" 65 | "Condicion Actual" 66 | 76 | "daily" 77 | "daily forecast" 78 | "Cambiar Unidade de Medición" 79 | "Unidades" 80 | "Hora a Hora" 81 | "Pronóstico por hora" 82 | "Alertas" 83 | "back" 84 | "today" 85 | "tomorrow" 86 | "Jetzt" 87 | 88 | 89 | "Condición Actual" 90 | "Pronóstico de 3 días" 91 | "Pronóstico de 10 días" 92 | "Daily Forecast" 93 | "Cambiar Unidade de Medición" 94 | "Pronóstico por hora" 95 | "Alertas" 96 | "Back" 97 | 98 | 99 | "Unidades Imperiales" 100 | "Unidades Imperiales" 101 | "Unidades Métricas" 102 | "Unidades Métricas" 103 | 104 | 105 | "Elija la Unidad: Métrica o Imperial" 106 | "Unidades" 107 | 108 | 109 | "No" 110 | "Alertas" 111 | "" 112 | "" 113 | "No se registran alertas por el momoento" 114 | 115 | "hh:mm a" 116 | "%s. Expira en %s ,," 117 | "Expira en %s" 118 | 119 | 120 | 121 | 122 | "Condiciones" 123 | "No Disponible" 124 | "" 125 | "" 126 | "Condición Actual no disponible" 127 | 128 | 129 | "Pronóstico" 130 | "No Disponible" 131 | "" 132 | "" 133 | "Pronóstico No Disponible" 134 | 135 | 136 | "Sin Red" 137 | "Conexión" 138 | "" 139 | "" 140 | "Conexión de Red no disponible" 141 | 142 | 143 | "Servicio de Localización" 144 | "No Disponible" 145 | "" 146 | "" 147 | "Servicio de Localización no disponible" 148 | 149 | 150 | "API de Clima" 151 | "No Disponible" 152 | "" 153 | "" 154 | "API de Clima no disponible" 155 | 156 | 157 | Actualmente %1$s y hace %2$.0f grados con una humedad de %3$.0f porciento y una velocidad del viento de %4$.0f %5$s 158 | Actualmente %1$s y hace menos %2$.0f grados con una humedad de %3$.0f porciento y una velocidad del viento de %4$.0f %5$s 159 | Cambio en las precipitaciones de %1$d y 160 | En %1$s hará %2$s con %3$s %4$s ,, 161 | %1$s hará %2$s con %3$s %4$s ,, 162 | Temperatura de menos %1$.0f ,, 163 | Temperatura de %1$.0f ,, 164 | Una máxima de menos %1$.0f ,, 165 | Una máxima de %1$.0f ,, 166 | Parcialmente 167 | Mayormente 168 | Ligero 169 | Pesado 170 | 171 | 172 | "dd/MM" 173 | "h a" 174 | "E dd/MM" 175 | "EEEE" 176 | "E" 177 | "M: " 178 | "m: " 179 | "Hora a Hora" 180 | 181 | 182 | "Condicióna Actual, tres días, diez días, cambiar unidad, hora a hora, alerta o presione uno para repetir la información del clima" 183 | "Ayuda de Mobile Weather" 184 | 185 | 186 | "Bienvenido a" 187 | "MobileWeather" 188 | "" 189 | "" 190 | "Bienvenido a MobileWeather" 191 | 192 | 193 | "Actual" 194 | "3 días" 195 | 196 | "Daily" 197 | "Hora a Hora" 198 | "Alertas" 199 | "sb6" 200 | "sb7" 201 | "sb8" 202 | 203 | 204 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/values-es-rMX/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MobileWeather 5 | Salir 6 | SDL 7 | Ford 8 | Subte 9 | Configurar 10 | MobileWeather 11 | Alertas 12 | Condición Climática 13 | Pronóstico 14 | Abrir Navegador 15 | Cerrar Navegador 16 | "Localizando…" 17 | --- 18 | "Cargando…" 19 | Velocidad del Viento 20 | Humedad 21 | Se Siente Como 22 | "Localizando…" 23 | "Cargando…" 24 | "Información de la versión del Proxy no disponible" 25 | "Información de la versión de MobileWeather no disponible" 26 | "Información de la versión no disponible" 27 | "Versión de Proxy: " 28 | "Versión de App:" 29 | "Versión de MobileWeather: " 30 | metric 31 | 32 | 33 | 34 | 35 | @string/drawer_item_current_location 36 | @string/drawer_item_add_location 37 | @string/drawer_item_settings 38 | @string/drawer_item_update_weather 39 | @string/drawer_item_reset_sync 40 | @string/drawer_item_about 41 | 42 | 43 | 44 | "Ubicación Actual" 45 | "Agregar Ubicación" 46 | "Configurar" 47 | "Actualizar el Clima" 48 | "Resetear el Sync" 49 | "Acerca De" 50 | 51 | 52 | "Unidades Imperiales" 53 | "F" 54 | "MPH" 55 | "Millas por Hora" 56 | "pulgadas" 57 | "Unidades Métricas" 58 | "C" 59 | "KPH" 60 | "Kilómetros por Hora" 61 | "milímetros" 62 | 63 | 64 | "Actual" 65 | "Condicion Actual" 66 | 76 | "daily" 77 | "daily forecast" 78 | "Cambiar Unidade de Medición" 79 | "Unidades" 80 | "Hora a Hora" 81 | "Pronóstico por hora" 82 | "Alertas" 83 | "back" 84 | "today" 85 | "tomorrow" 86 | "now" 87 | 88 | 89 | "Condición Actual" 90 | "Pronóstico de 3 días" 91 | "Pronóstico de 10 días" 92 | "Daily Forecast" 93 | "Cambiar Unidade de Medición" 94 | "Pronóstico por hora" 95 | "Alertas" 96 | "Back" 97 | 98 | 99 | "Unidades Imperiales" 100 | "Unidades Imperiales" 101 | "Unidades Métricas" 102 | "Unidades Métricas" 103 | 104 | 105 | "Elija la Unidad: Métrica o Imperial" 106 | "Unidades" 107 | 108 | 109 | "No" 110 | "Alertas" 111 | "" 112 | "" 113 | "No se registran alertas por el momoento" 114 | 115 | "hh:mm a" 116 | "%s. Expira en %s ,," 117 | "Expira en %s" 118 | 119 | 120 | 121 | 122 | "Condiciones" 123 | "No Disponible" 124 | "" 125 | "" 126 | "Condición Actual no disponible" 127 | 128 | 129 | "Pronóstico" 130 | "No Disponible" 131 | "" 132 | "" 133 | "Pronóstico No Disponible" 134 | 135 | 136 | "Sin Red" 137 | "Conexión" 138 | "" 139 | "" 140 | "Conexión de Red no disponible" 141 | 142 | 143 | "Servicio de Localización" 144 | "No Disponible" 145 | "" 146 | "" 147 | "Servicio de Localización no disponible" 148 | 149 | 150 | "API de Clima" 151 | "No Disponible" 152 | "" 153 | "" 154 | "API de Clima no disponible" 155 | 156 | 157 | Actualmente %1$s y hace %2$.0f grados con una humedad de %3$.0f porciento y una velocidad del viento de %4$.0f %5$s 158 | Actualmente %1$s y hace menos %2$.0f grados con una humedad de %3$.0f porciento y una velocidad del viento de %4$.0f %5$s 159 | Cambio en las precipitaciones de %1$d y 160 | En %1$s hará %2$s con %3$s %4$s ,, 161 | %1$s hará %2$s con %3$s %4$s ,, 162 | Temperatura de menos %1$.0f ,, 163 | Temperatura de %1$.0f ,, 164 | Una máxima de menos %1$.0f ,, 165 | Una máxima de %1$.0f ,, 166 | Parcialmente 167 | Mayormente 168 | Ligero 169 | Pesado 170 | 171 | 172 | "dd/MM" 173 | "h a" 174 | "E dd/MM" 175 | "EEEE" 176 | "E" 177 | "M: " 178 | "m: " 179 | "Hora a Hora" 180 | 181 | 182 | "Condicióna Actual, tres días, diez días, cambiar unidad, hora a hora, alerta o presione uno para repetir la información del clima" 183 | "Ayuda de Mobile Weather" 184 | 185 | 186 | "Bienvenido a" 187 | "MobileWeather" 188 | "" 189 | "" 190 | "Bienvenido a MobileWeather" 191 | 192 | 193 | "Actual" 194 | "3 días" 195 | 196 | "Daily" 197 | "Hora a Hora" 198 | "Alertas" 199 | "sb6" 200 | "sb7" 201 | "sb8" 202 | 203 | 204 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/values-large/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8dp 4 | 16dp 5 | 16dp 6 | 7 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/values-pt-rBR/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MobileWeather 5 | Sair 6 | SDL 7 | Ford 8 | WUnderground 9 | Opções 10 | MobileWeather 11 | Alertas 12 | Condições 13 | Previsão 14 | Abrir navegador 15 | Fechar Navegador 16 | "Obtendo Local.." 17 | --- 18 | "Carregando.." 19 | Velocidade do vento 20 | Umidade 21 | Sensação 22 | "Obtendo Local.." 23 | "Carregando.." 24 | "Versão do Proxy não disponível" 25 | "Versão do MobileWeather não disponível" 26 | "Versão não disponível" 27 | "Versão do Proxy: " 28 | "Versão do App" 29 | "Versão do MobileWeather: " 30 | metric 31 | 32 | 33 | 34 | @string/drawer_item_current_location 35 | @string/drawer_item_add_location 36 | @string/drawer_item_settings 37 | @string/drawer_item_update_weather 38 | @string/drawer_item_reset_sync 39 | @string/drawer_item_about 40 | 41 | 42 | 43 | "Local atual" 44 | "Adicionar local" 45 | "Opções" 46 | "Atualizar Tempo" 47 | "Reiniciar Sync" 48 | "Sobre" 49 | 50 | 51 | "Imperial" 52 | "F" 53 | "MPH" 54 | "milhas por hora" 55 | "polegadas" 56 | "Métrico" 57 | "C" 58 | "km/h" 59 | "kilometros por hora" 60 | "milímetros" 61 | 62 | 63 | "Atual" 64 | "Condições atuais" 65 | 75 | "daily" 76 | "daily forecast" 77 | "Mudar unidades" 78 | "Unidades" 79 | "De hora em hora" 80 | "Previsão horária" 81 | "Alertas" 82 | "back" 83 | "today" 84 | "tomorrow" 85 | "now" 86 | 87 | 88 | "Condições atuais" 89 | "Previsão para 3-dias" 90 | "Previsão para 10-dias" 91 | "Daily Forecast" 92 | "Mudar unidades" 93 | "Previsão horária" 94 | "Alertas" 95 | 96 | 97 | 98 | "Imperial" 99 | "Imperial" 100 | "Métrico" 101 | "Métrico" 102 | 103 | 104 | "Escolha a unidade em métrico ou imperial" 105 | "Unidades" 106 | 107 | 108 | "Não" 109 | "Alertas" 110 | "" 111 | "" 112 | "Nenhum alerta neste período" 113 | 114 | "hh:mm a" 115 | "%s .Expira em %s ,," 116 | "exp %s" 117 | 118 | 119 | "Condições" 120 | "Não disponíveis" 121 | "" 122 | "" 123 | "Condições atuais não disponíveis" 124 | 125 | 126 | "Previsão" 127 | "Não disponível" 128 | "" 129 | "" 130 | "Previsão não disponível" 131 | 132 | 133 | "Rede indponível" 134 | "Conexão" 135 | "" 136 | "" 137 | "Conexão de rede não disponível" 138 | 139 | 140 | "Serviço local" 141 | "Não disponível" 142 | "" 143 | "" 144 | "Serviço local não disponível" 145 | 146 | 147 | "API de clima" 148 | "Não disponível" 149 | "" 150 | "" 151 | "API de clima não disponível" 152 | 153 | 154 | Tempo atual de %1$s e %2$.0f graus com %3$.0f de humidade percentual, e velocidade do vento de %4$.0f %5$s 155 | Temperatura atual negativa de %1$s e %2$.0f graus com %3$.0f de humidade percentual, e velocidade do vento de %4$.0f %5$s 156 | %1$d porcento de chance de precipitação e 157 | Em %1$s será(ão) %2$s com %3$s %4$s ,, 158 | %1$s será(ão) %2$s com %3$s %4$s ,, 159 | temperatura negativa de %1$.0f,, 160 | temperatura de %1$.0f ,, 161 | uma diminuição de %1$.0f,, 162 | um aumento de %1$.0f ,, 163 | Parcialmente 164 | Maior parte 165 | Leve 166 | Pesado 167 | 168 | 169 | "MM/dd" 170 | "h a" 171 | "E MM/dd" 172 | "EEEE" 173 | "E" 174 | "H: " 175 | "L: " 176 | "De hora em hora" 177 | 178 | 179 | "condições atuais, três dias, dez dias, mudar unidades, De hora em hora, ou alertas, ou pressione um para repetir as informações do tempo" 180 | "ajuda do Mobile Weather" 181 | 182 | 183 | "Bem vindo ao" 184 | "MobileWeather" 185 | "" 186 | "" 187 | "Bem vindo ao Mobile Weather" 188 | 189 | 190 | "Atual" 191 | "3-dias" 192 | 193 | "Daily" 194 | "De hora em hora" 195 | "Alertas" 196 | "sb6" 197 | "sb7" 198 | "sb8" 199 | 200 | 201 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/values-pt-rPT/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MobileWeather 5 | Sair 6 | SDL 7 | Ford 8 | WUnderground 9 | Opções 10 | MobileWeather 11 | Alertas 12 | Condições 13 | Previsão 14 | Abrir navegador 15 | Fechar Navegador 16 | "Obtendo Local.." 17 | --- 18 | "Carregando.." 19 | Velocidade do vento 20 | Umidade 21 | Sensação 22 | "Obtendo Local.." 23 | "Carregando.." 24 | "Versão do Proxy não disponível" 25 | "Versão do MobileWeather não disponível" 26 | "Versão não disponível" 27 | "Versão do Proxy: " 28 | "Versão do App" 29 | "Versão do MobileWeather: " 30 | metric 31 | 32 | 33 | 34 | @string/drawer_item_current_location 35 | @string/drawer_item_add_location 36 | @string/drawer_item_settings 37 | @string/drawer_item_update_weather 38 | @string/drawer_item_reset_sync 39 | @string/drawer_item_about 40 | 41 | 42 | 43 | "Local atual" 44 | "Adicionar local" 45 | "Opções" 46 | "Atualizar Tempo" 47 | "Reiniciar Sync" 48 | "Sobre" 49 | 50 | 51 | "Imperial" 52 | "F" 53 | "MPH" 54 | "milhas por hora" 55 | "polegadas" 56 | "Métrico" 57 | "C" 58 | "km/h" 59 | "kilometros por hora" 60 | "milímetros" 61 | 62 | 63 | "Atual" 64 | "Condições atuais" 65 | 75 | "daily" 76 | "daily forecast" 77 | "Mudar unidades" 78 | "Unidades" 79 | "De hora em hora" 80 | "Previsão horária" 81 | "Alertas" 82 | "back" 83 | "today" 84 | "tomorrow" 85 | "now" 86 | 87 | 88 | "Condições atuais" 89 | "Previsão para 3-dias" 90 | "Previsão para 10-dias" 91 | "Daily Forecast" 92 | "Mudar unidades" 93 | "Previsão horária" 94 | "Alertas" 95 | "Back" 96 | 97 | 98 | 99 | "Imperial" 100 | "Imperial" 101 | "Métrico" 102 | "Métrico" 103 | 104 | 105 | "Escolha a unidade em métrico ou imperial" 106 | "Unidades" 107 | 108 | 109 | "Não" 110 | "Alertas" 111 | "" 112 | "" 113 | "Nenhum alerta neste período" 114 | 115 | "hh:mm a" 116 | "%s .Expira em %s ,," 117 | "exp %s" 118 | 119 | 120 | "Condições" 121 | "Não disponíveis" 122 | "" 123 | "" 124 | "Condições atuais não disponíveis" 125 | 126 | 127 | "Previsão" 128 | "Não disponível" 129 | "" 130 | "" 131 | "Previsão não disponível" 132 | 133 | 134 | "Rede indponível" 135 | "Conexão" 136 | "" 137 | "" 138 | "Conexão de rede não disponível" 139 | 140 | 141 | "Serviço local" 142 | "Não disponível" 143 | "" 144 | "" 145 | "Serviço local não disponível" 146 | 147 | 148 | "API de clima" 149 | "Não disponível" 150 | "" 151 | "" 152 | "API de clima não disponível" 153 | 154 | 155 | Tempo atual de %1$s e %2$.0f graus com %3$.0f de humidade percentual, e velocidade do vento de %4$.0f %5$s 156 | Temperatura atual negativa de %1$s e %2$.0f graus com %3$.0f de humidade percentual, e velocidade do vento de %4$.0f %5$s 157 | %1$d porcento de chance de precipitação e 158 | Em %1$s será(ão) %2$s com %3$s %4$s ,, 159 | %1$s será(ão) %2$s com %3$s %4$s ,, 160 | temperatura negativa de %1$.0f,, 161 | temperatura de %1$.0f ,, 162 | uma diminuição de %1$.0f,, 163 | um aumento de %1$.0f ,, 164 | Parcialmente 165 | Maior parte 166 | Leve 167 | Pesado 168 | 169 | 170 | "MM/dd" 171 | "h a" 172 | "E MM/dd" 173 | "EEEE" 174 | "E" 175 | "H: " 176 | "L: " 177 | "De hora em hora" 178 | 179 | 180 | "condições atuais, três dias, dez dias, mudar unidades, De hora em hora, ou alertas, ou pressione um para repetir as informações do tempo" 181 | "ajuda do Mobile Weather" 182 | 183 | 184 | "Bem vindo ao" 185 | "MobileWeather" 186 | "" 187 | "" 188 | "Bem vindo ao Mobile Weather" 189 | 190 | 191 | "Atual" 192 | "3-dias" 193 | 194 | "Daily" 195 | "De hora em hora" 196 | "Alertas" 197 | "sb6" 198 | "sb7" 199 | "sb8" 200 | 201 | 202 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/values-sw600dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/values-sw720dp-land/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 128dp 8 | 9 | 10 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | #fff3f3f3 8 | #212121 9 | #39609A 10 | #F4F4F7 11 | #F4F4F7 12 | 13 | 14 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8dp 5 | 8dp 6 | 16dp 7 | 24dp 8 | 16dp 9 | 16dp 10 | 11 | 12 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #39609A 4 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 11 | 12 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /MobileWeather/app/src/main/res/xml/weather_abrv.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | ADV 4 | AVL 5 | DNG 6 | CLD 7 | CLD 8 | BLZ 9 | DRZL 10 | FLD 11 | FNL 12 | FNLC 13 | HVY 14 | LT 15 | MST 16 | OVRC 17 | PRT 18 | SVR 19 | SNW 20 | SP 21 | ST 22 | STM 23 | STR 24 | T 25 | T-STM 26 | TOR 27 | WRN 28 | WTCH 29 | WTHR 30 | WNTR 31 | -------------------------------------------------------------------------------- /MobileWeather/app/src/test/java/com/sdl/mobileweather/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.sdl.mobileweather; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /MobileWeather/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | repositories { 6 | google() 7 | mavenCentral() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:7.4.2' 11 | 12 | 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | google() 21 | mavenCentral() 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /MobileWeather/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | android.enableJetifier=true 10 | android.useAndroidX=true 11 | org.gradle.jvmargs=-Xmx1536m 12 | # When configured, Gradle will run in incubating parallel mode. 13 | # This option should only be used with decoupled projects. More details, visit 14 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 15 | # org.gradle.parallel=true 16 | -------------------------------------------------------------------------------- /MobileWeather/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Apr 12 09:43:03 EDT 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip 7 | -------------------------------------------------------------------------------- /MobileWeather/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /MobileWeather/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /MobileWeather/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android Mobile Weather Tutorial 2 | 3 | This tutorial is designed to show you how to include the SmartDeviceLink SDK in your app and to help you get your app’s features onto the vehicle’s infotainment head unit. 4 | 5 | Please visit the [wiki](https://github.com/smartdevicelink/sdl_mobileweather_tutorial_android/wiki) of this repository to get the full tutorial. 6 | 7 | ### Weather API Key 8 | 9 | 1. Sign up and get your own [OpenWeather API Key](https://home.openweathermap.org/api_keys). 10 | 2. Set the value of `API_KEY`in `OpenWeatherMapService.java` to your API key 11 | --------------------------------------------------------------------------------