From df7c5e6e3fe99385f8093c8fd12fa13397745117 Mon Sep 17 00:00:00 2001 From: Stepan Date: Mon, 23 Jun 2025 23:56:50 +0200 Subject: [PATCH] init --- .gitignore | 15 ++ .idea/.gitignore | 3 + .idea/.name | 1 + .idea/AndroidProjectSystem.xml | 6 + .idea/compiler.xml | 6 + .idea/deploymentTargetSelector.xml | 10 + .idea/gradle.xml | 18 ++ .idea/migrations.xml | 10 + .idea/misc.xml | 10 + .idea/runConfigurations.xml | 17 ++ app/.gitignore | 1 + app/build.gradle.kts | 44 ++++ app/moodle/AQActivity.java | 191 +++++++++++++++++ app/moodle/activity_aqactivity.xml | 83 ++++++++ app/moodle/air_quality_files.zip | Bin 0 -> 2887 bytes app/proguard-rules.pro | 21 ++ .../airquality/ExampleInstrumentedTest.java | 26 +++ app/src/main/AndroidManifest.xml | 37 ++++ app/src/main/assets/forecast_sample_40.json | 159 ++++++++++++++ .../com/example/airquality/AQActivity.java | 191 +++++++++++++++++ .../example/airquality/DetailsActivity.java | 20 ++ .../com/example/airquality/MainActivity.java | 194 ++++++++++++++++++ .../airquality/PreferencesManager.java | 36 ++++ .../example/airquality/SettingsActivity.java | 82 ++++++++ .../com/example/airquality/Weather3h.java | 57 +++++ .../example/airquality/Weather3hAdapter.java | 54 +++++ .../example/airquality/Weather3hParser.java | 44 ++++ .../res/drawable/ic_launcher_background.xml | 170 +++++++++++++++ .../res/drawable/ic_launcher_foreground.xml | 30 +++ app/src/main/res/drawable/ic_weather.png | Bin 0 -> 14750 bytes .../main/res/drawable/spinner_background.xml | 18 ++ .../res/drawable/weather_clear_symbolic.xml | 5 + .../main/res/layout/activity_aqactivity.xml | 83 ++++++++ app/src/main/res/layout/activity_details.xml | 23 +++ app/src/main/res/layout/activity_main.xml | 54 +++++ app/src/main/res/layout/activity_settings.xml | 123 +++++++++++ app/src/main/res/layout/item_weather.xml | 27 +++ app/src/main/res/layout/item_weather3h.xml | 17 ++ app/src/main/res/menu/menu_main.xml | 17 ++ app/src/main/res/menu/popup_menu.xml | 18 ++ .../main/res/mipmap-anydpi/ic_launcher.xml | 6 + .../res/mipmap-anydpi/ic_launcher_round.xml | 6 + app/src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes app/src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes app/src/main/res/values-night/themes.xml | 7 + app/src/main/res/values/colors.xml | 12 ++ app/src/main/res/values/strings.xml | 5 + app/src/main/res/values/themes.xml | 9 + app/src/main/res/xml/backup_rules.xml | 13 ++ .../main/res/xml/data_extraction_rules.xml | 19 ++ .../example/airquality/ExampleUnitTest.java | 17 ++ build.gradle.kts | 4 + gradle.properties | 22 ++ gradle/libs.versions.toml | 22 ++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59203 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 185 +++++++++++++++++ gradlew.bat | 89 ++++++++ readme_assets/air_pollution.png | Bin 0 -> 14617 bytes readme_assets/air_pollution_2.png | Bin 0 -> 17104 bytes readme_assets/dokumentacija.docx | Bin 0 -> 61005 bytes readme_assets/menu.png | Bin 0 -> 3188 bytes settings.gradle.kts | 24 +++ 71 files changed, 2367 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/.name create mode 100644 .idea/AndroidProjectSystem.xml create mode 100644 .idea/compiler.xml create mode 100644 .idea/deploymentTargetSelector.xml create mode 100644 .idea/gradle.xml create mode 100644 .idea/migrations.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/runConfigurations.xml create mode 100644 app/.gitignore create mode 100644 app/build.gradle.kts create mode 100644 app/moodle/AQActivity.java create mode 100644 app/moodle/activity_aqactivity.xml create mode 100644 app/moodle/air_quality_files.zip create mode 100644 app/proguard-rules.pro create mode 100644 app/src/androidTest/java/com/example/airquality/ExampleInstrumentedTest.java create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/assets/forecast_sample_40.json create mode 100644 app/src/main/java/com/example/airquality/AQActivity.java create mode 100644 app/src/main/java/com/example/airquality/DetailsActivity.java create mode 100644 app/src/main/java/com/example/airquality/MainActivity.java create mode 100644 app/src/main/java/com/example/airquality/PreferencesManager.java create mode 100644 app/src/main/java/com/example/airquality/SettingsActivity.java create mode 100644 app/src/main/java/com/example/airquality/Weather3h.java create mode 100644 app/src/main/java/com/example/airquality/Weather3hAdapter.java create mode 100644 app/src/main/java/com/example/airquality/Weather3hParser.java create mode 100644 app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 app/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 app/src/main/res/drawable/ic_weather.png create mode 100644 app/src/main/res/drawable/spinner_background.xml create mode 100644 app/src/main/res/drawable/weather_clear_symbolic.xml create mode 100644 app/src/main/res/layout/activity_aqactivity.xml create mode 100644 app/src/main/res/layout/activity_details.xml create mode 100644 app/src/main/res/layout/activity_main.xml create mode 100644 app/src/main/res/layout/activity_settings.xml create mode 100644 app/src/main/res/layout/item_weather.xml create mode 100644 app/src/main/res/layout/item_weather3h.xml create mode 100644 app/src/main/res/menu/menu_main.xml create mode 100644 app/src/main/res/menu/popup_menu.xml create mode 100644 app/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 app/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/values-night/themes.xml create mode 100644 app/src/main/res/values/colors.xml create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/main/res/values/themes.xml create mode 100644 app/src/main/res/xml/backup_rules.xml create mode 100644 app/src/main/res/xml/data_extraction_rules.xml create mode 100644 app/src/test/java/com/example/airquality/ExampleUnitTest.java create mode 100644 build.gradle.kts create mode 100644 gradle.properties create mode 100644 gradle/libs.versions.toml create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 readme_assets/air_pollution.png create mode 100644 readme_assets/air_pollution_2.png create mode 100644 readme_assets/dokumentacija.docx create mode 100644 readme_assets/menu.png create mode 100644 settings.gradle.kts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..32e05af --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +Air Quality \ No newline at end of file diff --git a/.idea/AndroidProjectSystem.xml b/.idea/AndroidProjectSystem.xml new file mode 100644 index 0000000..4a53bee --- /dev/null +++ b/.idea/AndroidProjectSystem.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..b86273d --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..b268ef3 --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..97f0a8e --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..74dd639 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..16660f1 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..e0fd9f8 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,44 @@ +plugins { + alias(libs.plugins.android.application) +} + +android { + namespace = "com.example.airquality" + compileSdk = 35 + + defaultConfig { + applicationId = "com.example.airquality" + minSdk = 26 + targetSdk = 35 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation(libs.appcompat) + implementation(libs.material) + implementation(libs.activity) + implementation(libs.constraintlayout) + testImplementation(libs.junit) + androidTestImplementation(libs.ext.junit) + androidTestImplementation(libs.espresso.core) + implementation("com.android.volley:volley:1.2.1") +} \ No newline at end of file diff --git a/app/moodle/AQActivity.java b/app/moodle/AQActivity.java new file mode 100644 index 0000000..e80b2e9 --- /dev/null +++ b/app/moodle/AQActivity.java @@ -0,0 +1,191 @@ +package com.example.airquality; + +import android.os.Bundle; + +import android.content.Intent; +import android.os.Bundle; +import android.view.MenuInflater; +import android.view.View; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.activity.EdgeToEdge; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; + +import com.android.volley.Request; +import com.android.volley.RequestQueue; +import com.android.volley.Response; +import com.android.volley.VolleyError; +import com.android.volley.toolbox.JsonObjectRequest; +import com.android.volley.toolbox.Volley; + +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.Map; + +public class AQActivity extends AppCompatActivity { + private static final Map cityCoordinates = new HashMap<>(); + static { + cityCoordinates.put("Subotica", new double[]{46.1, 19.6667}); + cityCoordinates.put("Novi Sad", new double[]{45.2517, 19.8369}); + cityCoordinates.put("Beograd", new double[]{44.804, 20.4651}); + cityCoordinates.put("San Francisko", new double[]{37.7749, -122.4194}); + cityCoordinates.put("Sidnej", new double[]{-33.8679, 151.2073}); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + setContentView(R.layout.activity_aqactivity); + Toolbar toolbar = findViewById(R.id.toolbar); + + // Using toolbar as ActionBar + setSupportActionBar(toolbar); + + // Display application icon in the toolbar + if (getSupportActionBar() != null) { + getSupportActionBar().setDisplayShowHomeEnabled(true); + getSupportActionBar().setLogo(R.drawable.weather_clear_symbolic); + getSupportActionBar().setDisplayUseLogoEnabled(true); + } + + loadAQ(); + } + @Override + protected void onResume() { + super.onResume(); + loadAQ(); + } + + public boolean onCreateOptionsMenu(android.view.Menu menu) { + MenuInflater inflater = getMenuInflater(); + + inflater.inflate(R.menu.menu_main, menu); + return super.onCreateOptionsMenu(menu); + } + + + @Override + public boolean onOptionsItemSelected(android.view.MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_aq: + Intent intentAQ = new Intent(this, AQActivity.class); + startActivity(intentAQ); + return true; + case R.id.menu_settings: + Intent intent = new Intent(this, SettingsActivity.class); + startActivity(intent); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + private String getAQIDescription(int aqi) { + switch (aqi) { + case 1: return "Good"; + case 2: return "Fair"; + case 3: return "Moderate"; + case 4: return "Poor"; + case 5: return "Very Poor"; + default: return "Unknown"; + } + } + public void loadAQ(){ + String city = PreferencesManager.getCity(this); + double[] coords = cityCoordinates.get(city); + if (coords == null) { + Toast.makeText(this, "Unknown city: " + city, Toast.LENGTH_SHORT).show(); + return; + } + String apiKey = getString(R.string.weather_api_key); + String url = String.format( + "https://api.openweathermap.org/data/2.5/air_pollution?lat=%.6f&lon=%.6f&appid=%s", + coords[0], coords[1], apiKey + ); + + RequestQueue queue = Volley.newRequestQueue(this); + JsonObjectRequest request = new JsonObjectRequest( + Request.Method.GET, + url, + null, + new Response.Listener() { + @Override + public void onResponse(JSONObject response) { + try { + // Get JSON fields + JSONObject listItem = response.getJSONArray("list").getJSONObject(0); + int aqi = listItem.getJSONObject("main").getInt("aqi"); + + View rootLayout = findViewById(R.id.main); + + int colorResId; + switch (aqi) { + case 1: + colorResId = R.color.aqi_good; + break; + case 2: + colorResId = R.color.aqi_fair; + break; + case 3: + colorResId = R.color.aqi_moderate; + break; + case 4: + colorResId = R.color.aqi_poor; + break; + case 5: + colorResId = R.color.aqi_very_poor; + break; + default: + colorResId = android.R.color.background_light; + } + + rootLayout.setBackgroundColor(getResources().getColor(colorResId)); + + JSONObject components = listItem.getJSONObject("components"); + double pm25 = components.getDouble("pm2_5"); + double pm10 = components.getDouble("pm10"); + double o3 = components.getDouble("o3"); + double co = components.getDouble("co"); + double no2 = components.getDouble("no2"); + + + TextView tvCity = findViewById(R.id.tvCity); + TextView tvAQ = findViewById(R.id.tvAQ); + TextView tvComponents = findViewById(R.id.tvComponents); + + + String city = PreferencesManager.getCity(AQActivity.this); + + + tvCity.setText("City: " + city); + tvAQ.setText("AQI Index: " + aqi + " (" + getAQIDescription(aqi) + ")"); + tvComponents.setText( + String.format("PM2.5: %.2f µg/m³\nPM10: %.2f µg/m³\nO₃: %.2f ppb\nCO: %.2f ppm\nNO₂: %.2f ppb", + pm25, pm10, o3, co, no2) + ); + + } catch (Exception e) { + e.printStackTrace(); + Toast.makeText(AQActivity.this, "Failed to parse air data", Toast.LENGTH_SHORT).show(); + } + } + }, + new Response.ErrorListener() { + @Override + public void onErrorResponse(VolleyError error) { + Toast.makeText(AQActivity.this, "Failed to load air pollution data", Toast.LENGTH_SHORT).show(); + error.printStackTrace(); + } + } + ); + + queue.add(request); + + } +} \ No newline at end of file diff --git a/app/moodle/activity_aqactivity.xml b/app/moodle/activity_aqactivity.xml new file mode 100644 index 0000000..4f2bbb4 --- /dev/null +++ b/app/moodle/activity_aqactivity.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/moodle/air_quality_files.zip b/app/moodle/air_quality_files.zip new file mode 100644 index 0000000000000000000000000000000000000000..534ec2f16d454480adaa7b3155a36606d9d6a2d7 GIT binary patch literal 2887 zcmZ{mXH*ji7KM{gf>I+y1eB7{n;_CbsRBVE^n@aWIzU2C3`j4ED1<6V6D0H|RZ0M9 zN)=Fwf>cKW2uhs@BCNB!vuDqo-S>Xn`_6gi{<=Rtq%j>m7XSoc0&tbi*ydDp4cr9* z01#FHfExe+U|fPc37$d0ju`*HtSk|S-Lk$#%8}!uP;HPj6wq@DF()pNbIa}kS>8Td zHZ#D?hGK4Z<2P#V>SSk;7Eab_)3lTX!=CVbQubX)USHubm`+-cHI|3_=E>J(ZIj3J z<=_(7Evfn}QiYRFU|T9ZQ* zdg!^r^`*sO)BIq~>v2mzFAtgs4JtlD;s@YlEqC&{@QfOi zOtVg(U4GQZMe!>ta|hY_3w_dN!o56h;^cz72Lj*h`m6&N{fV$Wv!txfdYpIpBxZ*Q}K3n)tpujje_Wd6p$DfOfC0~h0S&4P#dz!$(ZhDVh#7nNLmN$ji>ZWj_ zEFRR=stFY{-Muv>T8o;bSya$pv)gFcRup?7yRQ> z*1lH9Ypr3+rHR|5uF&KCvGrA!?VzETW!OXRI{U_zJ#tHoLGdHiJ5Ej+lwps+ zcn@{T>_~eiGkLk&gEt%wbj?kvX-{iU^odWr)FzCaR6b=~P^_q>x~--Ph+tG1c;zZ)y-g&|;?&<+7}N*v)d3FI5Ht||2WQ3I!xiMN!=ic-?20V9FbBu1Ff z19^sVL2-22j&TUtx{{$c+K66jwu5DU9MCf6a<@Uk?E zxS|H@6wBNi;2tTupf=)C ze?1)GQ%zPVTjzj^U^0G62Tyt1? zv9I)nIUlswUoJ}4Y?mPp7I6|t{v%JYK_W&ko*L0r^wBD zd?s?cHfC>zQ3f6k3yg-ZAAT1(Q|ckSmLU-mBo%+7lPdUH3=^LudyZXeDJ;EJ&T^l1 zKbW~2%H+$Il&E7QJV4UC5lWwI%4kl+zSWWiyB;l*N(X@D*Qh-V{6@M#@fRElP<3La zTU+@z9;L8Q+Rg^3#FKHX&mdqWXT__CE5p<`{60#^J^6*Xxw)=HA)9BE)j)REcsrf- z73&wRwNd;{H2zu!wUUdW9gp61lL+dple7JK9X^3FBaw3Z_8hCkW$5MTCfd$PIMts{ z6WeUt@-FlvnnZ1V6fOzU#J<=m>Il>FBIL9*wJ(}P`n=fxl)0x0?Fsax^xH?$U}lN7|jZjED;Tr-TA>7z!8k8(C=-$^Nsu>AC^IhTO~&N6#05F=-S*GSVcAE z8+Ej*2I0zzCs=$ARrgv2l+?miElUQ|HYv5jSe0;KyA&#n?@jz`Uia*q5!!CTsO759 z(9xaVjfzbhd#rSr3^%(Q0EGtowY#jwiRDSuPpRC)ONy$v&*|U68ycou3k|{i zeze4fb|_f@#gx6C(9e}0DNABaw6EwaSM+aRijNxFC9lbx1r_RYB$=3M@(6t$9dz85qo;CA)&{)Ol+5q#%ZD!=<77x zPwg@1IIo2yi?z;d#6ejB1Bu>Jx;aVDUE`haT?gSo2J-`aih?+yH@hHH9_3Z(uXAaJhI*Gv@Q~q8NI$v0~GHl_bqQ_h*LU*?)BO@tAy&Zyt0$hjw}f@k}=s0 z<{b=PigIiQrjk4P?cb%$7ZF7nOW;o z6MTnz-4Op1@qNs=SKI!pfy#+`RkgAQ(pI1V z+@g5H@ic#Pr1Rt;r9;*G>-pDjP7ilIG|tl(k*Xr{PqM>YYom@u;5O+?_{;=4*06X! zKYFXg1*Cyf`+2f#$h?l)MBM0LqHdEX#KC3>$>MM|MidNMFBW8Lnh8FbPpVpSWu52j z1z-Ih9S?#Dwrf9#vQ&1#Ej*pS13kVtUZ95<$60@dHTf6KytY~V(5it;} zhc&n?r6^im$u#zzOs!mi+gzEt{!ROtq`RH5#CH6T%SdA&=sMm12i9K<@XsMf{V9Jj q>)-J8zeD~PMf@uQ03b&_|HZ<8V+y1(1LL0$pkEvPOKZ>mdHn-$Izx2; literal 0 HcmV?d00001 diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/androidTest/java/com/example/airquality/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/example/airquality/ExampleInstrumentedTest.java new file mode 100644 index 0000000..213f02f --- /dev/null +++ b/app/src/androidTest/java/com/example/airquality/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.example.airquality; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("com.example.airquality", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8d1082b --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/assets/forecast_sample_40.json b/app/src/main/assets/forecast_sample_40.json new file mode 100644 index 0000000..fd89808 --- /dev/null +++ b/app/src/main/assets/forecast_sample_40.json @@ -0,0 +1,159 @@ +{ + "cod": "200", + "message": 0, + "cnt": 8, + "list": [ + { + "dt": 1718325600, + "main": { + "temp": 298.15, + "pressure": 1013, + "humidity": 65 + }, + "weather": [ + { + "description": "clear sky", + "icon": "01d" + } + ], + "wind": { + "speed": 1.5, + "deg": 90 + }, + "dt_txt": "2025-06-14 00:00:00" + }, + { + "dt": 1718336400, + "main": { + "temp": 297.10, + "pressure": 1012, + "humidity": 68 + }, + "weather": [ + { + "description": "few clouds", + "icon": "02n" + } + ], + "wind": { + "speed": 1.8, + "deg": 100 + }, + "dt_txt": "2025-06-14 03:00:00" + }, + { + "dt": 1718347200, + "main": { + "temp": 296.00, + "pressure": 1011, + "humidity": 70 + }, + "weather": [ + { + "description": "scattered clouds", + "icon": "03n" + } + ], + "wind": { + "speed": 2.0, + "deg": 110 + }, + "dt_txt": "2025-06-14 06:00:00" + }, + { + "dt": 1718358000, + "main": { + "temp": 299.50, + "pressure": 1012, + "humidity": 60 + }, + "weather": [ + { + "description": "broken clouds", + "icon": "04d" + } + ], + "wind": { + "speed": 2.5, + "deg": 120 + }, + "dt_txt": "2025-06-14 09:00:00" + }, + { + "dt": 1718368800, + "main": { + "temp": 302.20, + "pressure": 1013, + "humidity": 50 + }, + "weather": [ + { + "description": "light rain", + "icon": "10d" + } + ], + "wind": { + "speed": 2.8, + "deg": 135 + }, + "dt_txt": "2025-06-14 12:00:00" + }, + { + "dt": 1718379600, + "main": { + "temp": 303.10, + "pressure": 1012, + "humidity": 48 + }, + "weather": [ + { + "description": "light rain", + "icon": "10d" + } + ], + "wind": { + "speed": 3.0, + "deg": 150 + }, + "dt_txt": "2025-06-14 15:00:00" + }, + { + "dt": 1718390400, + "main": { + "temp": 300.00, + "pressure": 1011, + "humidity": 55 + }, + "weather": [ + { + "description": "few clouds", + "icon": "02d" + } + ], + "wind": { + "speed": 2.2, + "deg": 160 + }, + "dt_txt": "2025-06-14 18:00:00" + }, + { + "dt": 1718401200, + "main": { + "temp": 298.50, + "pressure": 1010, + "humidity": 60 + }, + "weather": [ + { + "description": "clear sky", + "icon": "01n" + } + ], + "wind": { + "speed": 1.7, + "deg": 180 + }, + "dt_txt": "2025-06-14 21:00:00" + } + ] +} diff --git a/app/src/main/java/com/example/airquality/AQActivity.java b/app/src/main/java/com/example/airquality/AQActivity.java new file mode 100644 index 0000000..e80b2e9 --- /dev/null +++ b/app/src/main/java/com/example/airquality/AQActivity.java @@ -0,0 +1,191 @@ +package com.example.airquality; + +import android.os.Bundle; + +import android.content.Intent; +import android.os.Bundle; +import android.view.MenuInflater; +import android.view.View; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.activity.EdgeToEdge; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; + +import com.android.volley.Request; +import com.android.volley.RequestQueue; +import com.android.volley.Response; +import com.android.volley.VolleyError; +import com.android.volley.toolbox.JsonObjectRequest; +import com.android.volley.toolbox.Volley; + +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.Map; + +public class AQActivity extends AppCompatActivity { + private static final Map cityCoordinates = new HashMap<>(); + static { + cityCoordinates.put("Subotica", new double[]{46.1, 19.6667}); + cityCoordinates.put("Novi Sad", new double[]{45.2517, 19.8369}); + cityCoordinates.put("Beograd", new double[]{44.804, 20.4651}); + cityCoordinates.put("San Francisko", new double[]{37.7749, -122.4194}); + cityCoordinates.put("Sidnej", new double[]{-33.8679, 151.2073}); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + setContentView(R.layout.activity_aqactivity); + Toolbar toolbar = findViewById(R.id.toolbar); + + // Using toolbar as ActionBar + setSupportActionBar(toolbar); + + // Display application icon in the toolbar + if (getSupportActionBar() != null) { + getSupportActionBar().setDisplayShowHomeEnabled(true); + getSupportActionBar().setLogo(R.drawable.weather_clear_symbolic); + getSupportActionBar().setDisplayUseLogoEnabled(true); + } + + loadAQ(); + } + @Override + protected void onResume() { + super.onResume(); + loadAQ(); + } + + public boolean onCreateOptionsMenu(android.view.Menu menu) { + MenuInflater inflater = getMenuInflater(); + + inflater.inflate(R.menu.menu_main, menu); + return super.onCreateOptionsMenu(menu); + } + + + @Override + public boolean onOptionsItemSelected(android.view.MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_aq: + Intent intentAQ = new Intent(this, AQActivity.class); + startActivity(intentAQ); + return true; + case R.id.menu_settings: + Intent intent = new Intent(this, SettingsActivity.class); + startActivity(intent); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + private String getAQIDescription(int aqi) { + switch (aqi) { + case 1: return "Good"; + case 2: return "Fair"; + case 3: return "Moderate"; + case 4: return "Poor"; + case 5: return "Very Poor"; + default: return "Unknown"; + } + } + public void loadAQ(){ + String city = PreferencesManager.getCity(this); + double[] coords = cityCoordinates.get(city); + if (coords == null) { + Toast.makeText(this, "Unknown city: " + city, Toast.LENGTH_SHORT).show(); + return; + } + String apiKey = getString(R.string.weather_api_key); + String url = String.format( + "https://api.openweathermap.org/data/2.5/air_pollution?lat=%.6f&lon=%.6f&appid=%s", + coords[0], coords[1], apiKey + ); + + RequestQueue queue = Volley.newRequestQueue(this); + JsonObjectRequest request = new JsonObjectRequest( + Request.Method.GET, + url, + null, + new Response.Listener() { + @Override + public void onResponse(JSONObject response) { + try { + // Get JSON fields + JSONObject listItem = response.getJSONArray("list").getJSONObject(0); + int aqi = listItem.getJSONObject("main").getInt("aqi"); + + View rootLayout = findViewById(R.id.main); + + int colorResId; + switch (aqi) { + case 1: + colorResId = R.color.aqi_good; + break; + case 2: + colorResId = R.color.aqi_fair; + break; + case 3: + colorResId = R.color.aqi_moderate; + break; + case 4: + colorResId = R.color.aqi_poor; + break; + case 5: + colorResId = R.color.aqi_very_poor; + break; + default: + colorResId = android.R.color.background_light; + } + + rootLayout.setBackgroundColor(getResources().getColor(colorResId)); + + JSONObject components = listItem.getJSONObject("components"); + double pm25 = components.getDouble("pm2_5"); + double pm10 = components.getDouble("pm10"); + double o3 = components.getDouble("o3"); + double co = components.getDouble("co"); + double no2 = components.getDouble("no2"); + + + TextView tvCity = findViewById(R.id.tvCity); + TextView tvAQ = findViewById(R.id.tvAQ); + TextView tvComponents = findViewById(R.id.tvComponents); + + + String city = PreferencesManager.getCity(AQActivity.this); + + + tvCity.setText("City: " + city); + tvAQ.setText("AQI Index: " + aqi + " (" + getAQIDescription(aqi) + ")"); + tvComponents.setText( + String.format("PM2.5: %.2f µg/m³\nPM10: %.2f µg/m³\nO₃: %.2f ppb\nCO: %.2f ppm\nNO₂: %.2f ppb", + pm25, pm10, o3, co, no2) + ); + + } catch (Exception e) { + e.printStackTrace(); + Toast.makeText(AQActivity.this, "Failed to parse air data", Toast.LENGTH_SHORT).show(); + } + } + }, + new Response.ErrorListener() { + @Override + public void onErrorResponse(VolleyError error) { + Toast.makeText(AQActivity.this, "Failed to load air pollution data", Toast.LENGTH_SHORT).show(); + error.printStackTrace(); + } + } + ); + + queue.add(request); + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/airquality/DetailsActivity.java b/app/src/main/java/com/example/airquality/DetailsActivity.java new file mode 100644 index 0000000..71cf85a --- /dev/null +++ b/app/src/main/java/com/example/airquality/DetailsActivity.java @@ -0,0 +1,20 @@ +package com.example.airquality; + +import android.os.Bundle; + +import androidx.activity.EdgeToEdge; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; + +public class DetailsActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + setContentView(R.layout.activity_details); + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/airquality/MainActivity.java b/app/src/main/java/com/example/airquality/MainActivity.java new file mode 100644 index 0000000..2b1acd3 --- /dev/null +++ b/app/src/main/java/com/example/airquality/MainActivity.java @@ -0,0 +1,194 @@ +package com.example.airquality; + +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import android.view.MenuInflater; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.activity.EdgeToEdge; +import androidx.appcompat.app.AppCompatActivity; + +import androidx.appcompat.widget.Toolbar; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; + +import com.android.volley.Request; +import com.android.volley.RequestQueue; +import com.android.volley.Response; +import com.android.volley.VolleyError; +import com.android.volley.toolbox.JsonObjectRequest; +import com.android.volley.toolbox.Volley; +import com.google.android.material.appbar.MaterialToolbar; + +import org.json.JSONObject; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class MainActivity extends AppCompatActivity { + List prognoza; + + private ListView listView; + + private static final Map cityCoordinates = new HashMap<>(); + static { + cityCoordinates.put("Subotica", new double[]{46.1, 19.6667}); + cityCoordinates.put("Novi Sad", new double[]{45.2517, 19.8369}); + cityCoordinates.put("Beograd", new double[]{44.804, 20.4651}); + cityCoordinates.put("San Francisko", new double[]{37.7749, -122.4194}); + cityCoordinates.put("Sidnej", new double[]{-33.8679, 151.2073}); + } + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + setContentView(R.layout.activity_main); + + listView=findViewById(R.id.listView); + + // Assigning ID of the toolbar to a variable + Toolbar toolbar = findViewById(R.id.toolbar); + + // Using toolbar as ActionBar + setSupportActionBar(toolbar); + + // Display application icon in the toolbar + if (getSupportActionBar() != null) { + getSupportActionBar().setDisplayShowHomeEnabled(true); + getSupportActionBar().setLogo(R.drawable.weather_clear_symbolic); + getSupportActionBar().setDisplayUseLogoEnabled(true); + } + + loadForecast(); + } + +// private void loadForecast() { +// String city = PreferencesManager.getCity(this); +// int days = PreferencesManager.getDays(this); +// String units = PreferencesManager.getUnits(this); +// +// Log.d("SETTINGS", "City: " + city + ", Days: " + days + ", Units: " + units); +// +// try { +// // Učitavanje lokalnog JSON fajla +// InputStream is = getAssets().open("forecast_sample_40.json"); +// int size = is.available(); +// byte[] buffer = new byte[size]; +// is.read(buffer); +// is.close(); +// +// String jsonString = new String(buffer, "UTF-8"); +// JSONObject jsonObject = new JSONObject(jsonString); +// +// // Računamo maksimalan broj vremenskih tačaka +// int maxPoints = days * 8; // jer ima 8 tačaka dnevno na svakih 3h +// +// // Parsiramo podatke +// List prognoza = Weather3hParser.parse(jsonObject, maxPoints); +// +// // Povezujemo adapter sa ListView-om +// Weather3hAdapter adapter = new Weather3hAdapter(this, prognoza); +// ListView listView = findViewById(R.id.listView); +// listView.setAdapter(adapter); +// +// } catch (Exception e) { +// e.printStackTrace(); +// } +// } + + + public boolean onCreateOptionsMenu(android.view.Menu menu) { + MenuInflater inflater = getMenuInflater(); + + inflater.inflate(R.menu.menu_main, menu); + return super.onCreateOptionsMenu(menu); + } + + + @Override + public boolean onOptionsItemSelected(android.view.MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_aq: + Intent intentAQ = new Intent(this, AQActivity.class); + startActivity(intentAQ); + return true; + case R.id.menu_settings: + Intent intent = new Intent(this, SettingsActivity.class); + startActivity(intent); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + protected void onResume() { + super.onResume(); + loadForecast(); + } + + + + + private void loadForecast() { + String city = PreferencesManager.getCity(this); + int days = PreferencesManager.getDays(this); + String units = PreferencesManager.getUnits(this); + + double[] coords = cityCoordinates.get(city); + if (coords == null) { + Toast.makeText(this, "Unknown city: " + city, Toast.LENGTH_SHORT).show(); + return; + } + + String apiKey = getString(R.string.weather_api_key); + String url = String.format( + "https://api.openweathermap.org/data/2.5/forecast?lat=%f&lon=%f&units=%s&appid=%s", + coords[0], coords[1], units, apiKey + ); + + RequestQueue queue = Volley.newRequestQueue(this); + + JsonObjectRequest request = new JsonObjectRequest( + Request.Method.GET, + url, + null, + new Response.Listener() { + @Override + public void onResponse(JSONObject response) { + prognoza = Weather3hParser.parse(response, days);; + Weather3hAdapter adapter = new Weather3hAdapter(MainActivity.this, prognoza); + ListView listView = findViewById(R.id.listView); + listView.setAdapter(adapter); + } + }, + new Response.ErrorListener() { + @Override + public void onErrorResponse(VolleyError error) { + error.printStackTrace(); + Toast.makeText(MainActivity.this, "Failed to load weather data", Toast.LENGTH_SHORT).show(); + } + } + ); + + queue.add(request); + } + + + + + + + +} + + diff --git a/app/src/main/java/com/example/airquality/PreferencesManager.java b/app/src/main/java/com/example/airquality/PreferencesManager.java new file mode 100644 index 0000000..f9838a0 --- /dev/null +++ b/app/src/main/java/com/example/airquality/PreferencesManager.java @@ -0,0 +1,36 @@ +package com.example.airquality; + +import android.content.Context; +import android.content.SharedPreferences; + +public class PreferencesManager { + + private static final String PREFS_NAME = "MyPrefs"; + private static final String KEY_CITY = "city"; + private static final String KEY_DAYS = "days"; + private static final String KEY_UNITS = "units"; + + public static void savePreferences(Context context, String city, int days, String units) { + SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = prefs.edit(); + editor.putString(KEY_CITY, city); + editor.putInt(KEY_DAYS, days); + editor.putString(KEY_UNITS, units); + editor.apply(); + } + + public static String getCity(Context context) { + SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); + return prefs.getString(KEY_CITY, "Subotica"); // default: Subotica + } + + public static int getDays(Context context) { + SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); + return prefs.getInt(KEY_DAYS, 7); // default: 7 dana + } + + public static String getUnits(Context context) { + SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); + return prefs.getString(KEY_UNITS, "metric"); // default: metric + } +} diff --git a/app/src/main/java/com/example/airquality/SettingsActivity.java b/app/src/main/java/com/example/airquality/SettingsActivity.java new file mode 100644 index 0000000..fc2686b --- /dev/null +++ b/app/src/main/java/com/example/airquality/SettingsActivity.java @@ -0,0 +1,82 @@ +package com.example.airquality; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.RadioGroup; +import android.widget.Spinner; + +import androidx.activity.EdgeToEdge; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; + +public class SettingsActivity extends AppCompatActivity { + Spinner spinnerCity; + RadioGroup radioGroupDays, radioGroupUnits; + CheckBox checkBoxRemember; + Button buttonSave; + + String[] cities = {"Subotica", "Novi Sad", "Beograd", "San Francisko", "Sidnej"}; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_settings); + + spinnerCity = findViewById(R.id.spinnerCity); + radioGroupDays = findViewById(R.id.radioGroupDays); + radioGroupUnits = findViewById(R.id.radioGroupUnits); + checkBoxRemember = findViewById(R.id.checkBoxRemember); + buttonSave = findViewById(R.id.buttonSave); + + // Podesi spinner + ArrayAdapter adapter = new ArrayAdapter<>(this, + android.R.layout.simple_spinner_item, cities); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinnerCity.setAdapter(adapter); + + buttonSave.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + String selectedCity = spinnerCity.getSelectedItem().toString(); + + int days = 7; // podrazumevano + if (radioGroupDays.getCheckedRadioButtonId() == R.id.radio3days) { + days = 3; + } else if (radioGroupDays.getCheckedRadioButtonId() == R.id.radio5days) { + days = 5; + } + + String units = "metric"; + if (radioGroupUnits.getCheckedRadioButtonId() == R.id.radioImperial) { + units = "imperial"; + } + + boolean remember = checkBoxRemember.isChecked(); + + if (remember) { + SharedPreferences prefs = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE); + SharedPreferences.Editor editor = prefs.edit(); + editor.putString("city", selectedCity); + editor.putInt("days", days); + editor.putString("units", units); + editor.apply(); + } + + Intent resultIntent = new Intent(); + resultIntent.putExtra("city", selectedCity); + resultIntent.putExtra("days", days); + resultIntent.putExtra("units", units); + setResult(RESULT_OK, resultIntent); + finish(); + } + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/airquality/Weather3h.java b/app/src/main/java/com/example/airquality/Weather3h.java new file mode 100644 index 0000000..72f1c21 --- /dev/null +++ b/app/src/main/java/com/example/airquality/Weather3h.java @@ -0,0 +1,57 @@ +package com.example.airquality; + + + public class Weather3h { + private String dateTime; + private double temp; + private int pressure; + private int humidity; + private double windSpeed; + private int windDeg; + private String description; + private String icon; + + public Weather3h(String dateTime, double temp, int pressure, int humidity, + double windSpeed, int windDeg, String description, String icon) { + this.dateTime = dateTime; + this.temp = temp; + this.pressure = pressure; + this.humidity = humidity; + this.windSpeed = windSpeed; + this.windDeg = windDeg; + this.description = description; + this.icon = icon; + } + + public String getDateTime() { + return dateTime; + } + + public double getTemp() { + return temp; + } + + public int getPressure() { + return pressure; + } + + public int getHumidity() { + return humidity; + } + + public double getWindSpeed() { + return windSpeed; + } + + public int getWindDeg() { + return windDeg; + } + + public String getDescription() { + return description; + } + + public String getIcon() { + return icon; + } + } diff --git a/app/src/main/java/com/example/airquality/Weather3hAdapter.java b/app/src/main/java/com/example/airquality/Weather3hAdapter.java new file mode 100644 index 0000000..2e63882 --- /dev/null +++ b/app/src/main/java/com/example/airquality/Weather3hAdapter.java @@ -0,0 +1,54 @@ +package com.example.airquality; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.TextView; + +import java.util.List; + +public class Weather3hAdapter extends BaseAdapter { + private Context context; + private List weatherList; + private LayoutInflater inflater; + + public Weather3hAdapter(Context context, List weatherList) { + this.context = context; + this.weatherList = weatherList; + this.inflater = LayoutInflater.from(context); + } + + @Override + public int getCount() { + return weatherList.size(); + } + + @Override + public Object getItem(int position) { + return weatherList.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View rowView = inflater.inflate(R.layout.item_weather3h, parent, false); + + TextView txtDate = rowView.findViewById(R.id.txtDate); + TextView txtTemp = rowView.findViewById(R.id.txtTemp); + TextView txtDesc = rowView.findViewById(R.id.txtDesc); + + Weather3h item = weatherList.get(position); + + txtDate.setText(item.getDateTime()); + txtTemp.setText(String.format("%.1f°C", item.getTemp())); + txtDesc.setText(item.getDescription()); + + return rowView; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/airquality/Weather3hParser.java b/app/src/main/java/com/example/airquality/Weather3hParser.java new file mode 100644 index 0000000..14446da --- /dev/null +++ b/app/src/main/java/com/example/airquality/Weather3hParser.java @@ -0,0 +1,44 @@ +package com.example.airquality; + +import org.json.JSONArray; +import org.json.JSONObject; +import java.util.ArrayList; +import java.util.List; + +public class Weather3hParser { + public static List parse(JSONObject jsonObject, int maxPoints) { + List forecastList = new ArrayList<>(); + + try { + JSONArray listArray = jsonObject.getJSONArray("list"); + + for (int i = 0; i < listArray.length() && i < maxPoints; i++) { + JSONObject item = listArray.getJSONObject(i); + + String dateTime = item.getString("dt_txt"); + + JSONObject main = item.getJSONObject("main"); + double temp = main.getDouble("temp"); + int pressure = main.getInt("pressure"); + int humidity = main.getInt("humidity"); + + JSONObject weather = item.getJSONArray("weather").getJSONObject(0); + String description = weather.getString("description"); + String icon = weather.getString("icon"); + + JSONObject wind = item.getJSONObject("wind"); + double windSpeed = wind.getDouble("speed"); + int windDeg = wind.getInt("deg"); + + Weather3h weather3h = new Weather3h(dateTime, temp, pressure, humidity, windSpeed, windDeg, description, icon); + forecastList.add(weather3h); + } + + } catch (Exception e) { + e.printStackTrace(); + } + + return forecastList; + } +} + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_weather.png b/app/src/main/res/drawable/ic_weather.png new file mode 100644 index 0000000000000000000000000000000000000000..558b3cbbadd9e54da516f4a7c8cee193f5718957 GIT binary patch literal 14750 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 + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/weather_clear_symbolic.xml b/app/src/main/res/drawable/weather_clear_symbolic.xml new file mode 100644 index 0000000..3870715 --- /dev/null +++ b/app/src/main/res/drawable/weather_clear_symbolic.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/layout/activity_aqactivity.xml b/app/src/main/res/layout/activity_aqactivity.xml new file mode 100644 index 0000000..4f2bbb4 --- /dev/null +++ b/app/src/main/res/layout/activity_aqactivity.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_details.xml b/app/src/main/res/layout/activity_details.xml new file mode 100644 index 0000000..2b0d73f --- /dev/null +++ b/app/src/main/res/layout/activity_details.xml @@ -0,0 +1,23 @@ + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..bdacf39 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml new file mode 100644 index 0000000..e19598d --- /dev/null +++ b/app/src/main/res/layout/activity_settings.xml @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +