Compare commits

...

61 commits
V1.1 ... main

Author SHA1 Message Date
woheller69
10aced4a5d V2.7
SDK 35
Android 15
2025-07-01 10:01:17 +02:00
woheller69
02b78a99c9 fix potential rare crash 2025-05-23 06:27:05 +02:00
woheller69
10f7c2f9f5 Chinese translation by @ifox6677 2025-05-05 16:36:42 +02:00
woheller69
09eef2bee4 V2.6
Add produced / remaining today
Remove dependenciesInfo block
2025-04-29 07:26:35 +02:00
woheller69
8479bcd741 Add produced / remaining today
Remove dependenciesInfo block
2025-04-29 07:11:45 +02:00
woheller69
972de5739d update Readme 2025-01-22 21:20:46 +01:00
woheller69
786e9c27a5 update Readme 2025-01-15 10:28:32 +01:00
woheller69
9ab548b9ee V2.5
fixes #24
2024-07-17 15:41:31 +02:00
woheller69
5337da4e67 processing simplified (processNewWeekForecasts removed)
add option to specify inverter as central inverter and apply limits when summarizing in adapter
#24
2024-07-13 06:59:04 +02:00
woheller69
2974b4b156 -reset cumulated values at midnight, unless in debug mode
-bugfix for weekforcast because values are for preceding hour
fixes #23
2024-07-01 09:30:31 +02:00
woheller69
71e9cbe08d update Readme 2024-05-27 09:18:32 +02:00
woheller69
786635b7b8 Merge remote-tracking branch 'origin/main' 2024-05-27 09:16:51 +02:00
woheller69
87905209b6 update for Android 14 (SDK 34) 2024-05-27 09:16:29 +02:00
stefanosalvador
a4b936ac79
Italian translation (#15)
Co-authored-by: Stefano Salvador <stefano.salvador@protezionecivile.fvg.it>
2024-01-20 09:43:43 +01:00
woheller69
0d53115e68 allow rotation in AddLocation 2023-10-12 10:39:32 +02:00
woheller69
5039510970 Close keyboard after 3s 2023-09-27 13:48:46 +02:00
woheller69
117adab1c6 Update for V2.2
Remove unused stuff
2023-09-22 08:49:11 +02:00
woheller69
836376d7dd Add option to run own servers for APIs 2023-09-21 21:30:12 +02:00
woheller69
5b43313882 Update V2.1 2023-09-15 07:40:47 +02:00
woheller69
62321e0e4f Close dialog in case of recreation 2023-09-10 22:24:26 +02:00
woheller69
d586517236 Turkish translation
Extract string resource
2023-09-07 07:06:06 +02:00
woheller69
84224c863f improved shading calculation (6 steps per hour) 2023-09-02 07:52:48 +02:00
woheller69
975065f84f improve icon 2023-08-01 18:23:02 +02:00
woheller69
39cb0d9b65 fix backup/restore for Android 13
Update for V2.0
2023-07-31 11:46:57 +02:00
woheller69
66c12873d8 improve icon 2023-07-31 08:35:47 +02:00
woheller69
c9da1f7ed2 update Readme 2023-07-22 09:32:36 +02:00
woheller69
69ec75f776 update Readme 2023-07-22 08:48:47 +02:00
woheller69
83b53b7801 update fastlane for V1.9 2023-07-10 14:09:34 +02:00
woheller69
364abea7be Add support for monochrome icon 2023-07-10 14:07:47 +02:00
woheller69
7d208dd712 modify strings 2023-06-27 08:37:07 +02:00
woheller69
eb5043d4f2 show current sun position also in edit location dialog 2023-06-26 12:20:14 +02:00
woheller69
7ba379a8f9 update strings 2023-06-26 08:34:53 +02:00
woheller69
62324ecc76 Upgrade for Android 13
Support Dark Mode in Help Activity for Android 10+
Slightly improve contrast in Dark Mode
2023-06-19 21:43:38 +02:00
woheller69
1a05a6348d refactor calcDiffuseEfficiency to SolarPowerPlant.java 2023-06-15 14:07:19 +02:00
woheller69
23caf89c8e update for V1.8
Bugfix
Show date in course of day header
2023-06-13 08:17:51 +02:00
woheller69
4752f96609 update description 2023-05-25 17:00:43 +02:00
woheller69
1d9c95f08f update description 2023-05-25 16:46:05 +02:00
woheller69
c7919f1e7a Update for V1.7
Minor other changes
2023-05-22 09:04:00 +02:00
woheller69
28aed88cea add cumulated energy to course of day 2023-05-16 08:49:28 +02:00
woheller69
428d59a2a5 add attribution for Open-Meteo Geolocation search 2023-05-08 10:06:06 +02:00
woheller69
5b8461c8a3 Open instructions at first start #8
Disable scrolling in Viewpager2
Layout improvements
2023-05-07 10:39:22 +02:00
woheller69
7db77d345f Shorten details in location list
update instructions
Update fastlane for V1.6
2023-05-05 11:49:22 +02:00
woheller69
a1e8b410e5 update Readme and license 2023-05-03 13:38:25 +02:00
woheller69
757caeffcb add compass 2023-05-03 11:42:31 +02:00
woheller69
84752cee9c add compass 2023-05-03 11:33:28 +02:00
woheller69
07fb5f0b9e Take reflections into account 2023-05-02 12:14:34 +02:00
woheller69
6fcdccfa4f Update V1.5 2023-05-02 08:37:13 +02:00
woheller69
b8b37deacf Improve code in WeekWeatherAdapter
Improve isOnline check #4
Offer UNDO operation when location is deleted #5
Use cells peak power if cell area or efficiency = 0
2023-04-26 09:46:50 +02:00
woheller69
6492fc241f add F-Droid button 2023-04-19 10:07:33 +02:00
woheller69
7880b66a05 fix bug: show sum also in chart 2023-04-19 07:36:59 +02:00
woheller69
2ae9a055eb fix issue:refresh button not rotating
new feature: show sum
new feature: clone location
update Readme and help
2023-04-18 16:54:51 +02:00
woheller69
38295c65e1 update fastlane 2023-04-17 14:32:13 +02:00
woheller69
6537132813 bugfix backup/restore for Android R+ 2023-04-17 11:54:06 +02:00
woheller69
7a2edcf814 add backup/restore 2023-04-17 10:53:33 +02:00
woheller69
67027dc8e6 improve documentation and strings 2023-04-17 08:35:58 +02:00
woheller69
fb19d50123 improve documentation and strings 2023-04-17 08:32:38 +02:00
woheller69
b6838569ab limit number of y-axis labels 2023-04-16 20:55:01 +02:00
woheller69
3314f624da fix y-axis labels 2023-04-16 20:05:11 +02:00
woheller69
a3734b9d36 remove unnecessary API calls 2023-04-16 20:04:35 +02:00
woheller69
f1a79e41e1 change some stuff from float to double 2023-04-13 17:11:29 +02:00
woheller69
a1f14681fd Estimate panel temperature from ambient temperature and radiation
Additional parameter: temperature coefficient
Adapt efficiency according to these values
Bugfix input filter
Prepare V1.2
2023-04-13 08:52:54 +02:00
125 changed files with 2384 additions and 771 deletions

100
README.md
View file

@ -1,59 +1,98 @@
<pre>Send a coffee to woheller69@t-online.de <pre>Send a coffee to
<a href= "https://www.paypal.com/signin"><img align="left" src="https://www.paypalobjects.com/webstatic/de_DE/i/de-pp-logo-150px.png"></a></pre> woheller69@t-online.de
<a href= "https://www.paypal.com/signin"><img align="left" src="https://www.paypalobjects.com/webstatic/de_DE/i/de-pp-logo-150px.png"></a>
Or via this link (with fees)
<a href="https://www.paypal.com/donate?hosted_button_id=XVXQ54LBLZ4AA"><img align="left" src="https://img.shields.io/badge/Donate%20with%20Debit%20or%20Credit%20Card-002991?style=plastic"></a></pre>
| **RadarWeather** | **Gas Prices** | **Smart Eggtimer** |
|:---:|:---:|:---:|
| [<img src="https://github.com/woheller69/weather/blob/main/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.weather/) | [<img src="https://github.com/woheller69/spritpreise/blob/main/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.spritpreise/) | [<img src="https://github.com/woheller69/eggtimer/blob/main/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.eggtimer/) |
| **Bubble** | **hEARtest** | **GPS Cockpit** |
| [<img src="https://github.com/woheller69/Level/blob/master/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.level/) | [<img src="https://github.com/woheller69/audiometry/blob/new/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.audiometry/) | [<img src="https://github.com/woheller69/gpscockpit/blob/master/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.gpscockpit/) |
| **Audio Analyzer** | **LavSeeker** | **TimeLapseCam** |
| [<img src="https://github.com/woheller69/audio-analyzer-for-android/blob/master/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.audio_analyzer_for_android/) |[<img src="https://github.com/woheller69/lavatories/blob/master/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.lavatories/) | [<img src="https://github.com/woheller69/TimeLapseCamera/blob/master/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.TimeLapseCam/) |
| **Arity** | **Cirrus** | **solXpect** |
| [<img src="https://github.com/woheller69/arity/blob/master/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.arity/) | [<img src="https://github.com/woheller69/omweather/blob/master/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.omweather/) | [<img src="https://github.com/woheller69/solXpect/blob/main/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.solxpect/) |
| **gptAssist** | **dumpSeeker** | **huggingAssist** |
| [<img src="https://github.com/woheller69/gptassist/blob/master/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.gptassist/) | [<img src="https://github.com/woheller69/dumpseeker/blob/main/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.dumpseeker/) | [<img src="https://github.com/woheller69/huggingassist/blob/master/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.hugassist/) |
| **FREE Browser** | **whoBIRD** | **PeakOrama** |
| [<img src="https://github.com/woheller69/browser/blob/newmaster/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.browser/) | [<img src="https://github.com/woheller69/whoBIRD/blob/master/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.whobird/) | [<img src="https://github.com/woheller69/PeakOrama/blob/master/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.PeakOrama/) |
| **Whisper** | **Seamless** | |
| [<img src="https://github.com/woheller69/whisperIME/blob/master/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.whisper/) | [<img src="https://github.com/woheller69/seamless/blob/master/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.seemless/) | |
# solXpect # solXpect
solXpect forecasts the output of your solar power plant solXpect is an app that forecasts the output of your solar power plant by using direct and diffuse radiation data from Open-Meteo.com, calculating the position of the sun, and projecting the radiation on your solar panel.
It shows the estimated energy production for the next 16 days, with hourly values calculated for the preceding hour. As an example, if there are 150 Wh shown at 11:00 this means you can expect 150 Wh between 10:00 and 11:00 from your photovoltaic system.
This app takes direct and diffuse radiation data from Open-Meteo.com, calculates the position The values starting with Σ show the cumulated energy since midnight of the first day.
of the sun and projects the radiation on your solar panel. To use solXpect, you simply enter your latitude and longitude coordinates, as well as the azimuth and tilt of your solar panel.
It shows the estimated energy production for the next hours and up to 16 days. You also enter information about the peak power, efficiency, temperature coefficient, and area of your solar panel, as well as the maximum power and efficiency of your inverter.
The hourly values are calculated for the preceding hour. So if there are 150 Wh shown at 11:00 this means you can expect 150 Wh between 10:00 and 11:00. Additionally, solXpect allows you to define shading on your solar panels by specifying the minimum elevation of the sun necessary for the sun to hit the solar panels, as well as the percentage of shading for elevations below this value.
If you have multiple solar panels with the same latitude and longitude but pointing in different directions, you can define them as separate locations and use the 'show sum' feature to summarize their output.
Overall, solXpect is a powerful tool for optimizing the use of your own energy and reduce your energy costs.
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/01.png" width="150"/> <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/02.png" width="150"/> <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/03.png" width="150"/> <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/04.png" width="150"/> <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/01.png" width="150"/> <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/02.png" width="150"/> <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/03.png" width="150"/> <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/04.png" width="150"/>
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" height="75">](https://f-droid.org/de/packages/org.woheller69.solxpect/)
## Parameters ## Parameters
#### Name
Define a name for the location.
If you have several modules pointing in different directions at the same location you can activate "showSum" mode in settings.
In this case you should define your location names as 'myPV | part1', 'myPV | part2', etc. In 'show sum' mode the location is then shown as 'myPV' and '|' is taken as delimiter.
#### Latitude [°] #### Latitude [°]
Latitude specifies the northsouth position of your solar power plant. It ranges from 90° at the south pole to 90° at the north pole. Enter the north-south position of your solar power plant, ranging from -90° at the south pole to 90° at the north pole.
#### Longitude [°] #### Longitude [°]
Longitude specifies the eastwest position of your solar power plant. The prime meridian defines 0° longitude. Positive longitudes are east of the prime meridian, negative ones are west. Enter the east-west position of your solar power plant, with 0° defined as the prime meridian. Positive longitudes are east of the prime meridian, negative ones are west.
#### Azimuth [°] #### Azimuth [°]
Azimuth is the horizontal direction of your solar power plant. 0° equals North, 90° equals East, 180° equals South, 270° equals West. Specify the horizontal direction of your solar power plant, with 0° corresponding to North, 90° to East, 180° to South, and 270° to West.
#### Tilt [°] #### Tilt [°]
Tilt is the vertical direction of your solar power plant. 0° means it points up towards the the sky, 90° means it has a vertical orientation and points towards the horizon. Specify the vertical direction of your solar power plant, with 0° pointing upwards towards the sky and 90° being a vertical orientation pointing towards the horizon.
#### Cells max power [W] #### Cells peak power [W]
Maximum power your solar cells can deliver. Enter the maximum power your solar cells (total of all cells) can deliver. At the moment this value is only used if a value of 0 is specified for cells efficiency or cell area.
In this case it is assumed that the cells peak power is given at an irradiance of 1000W/sqm.
#### Cells efficiency [%] #### Cells efficiency [%]
Portion of energy in the form of sunlight that can be converted into electricity by the solar cell. Specify the portion of energy in the form of sunlight that can be converted into electricity by the solar cell.
#### Temperature coefficient [%/K] #### Temperature coefficient [%/K]
Dependence of the cell power on temperature (usually in the range of -0.4%/K). Enter the dependence of the cell power on temperature, usually in the range of -0.4%/K. Cell temperature is estimated from ambient temperature and total irradiance.
Cell temperature is estimated from ambient temperature and total irradiance.
#### Cell area [m<sup>2</sup>] #### Cell area [m<sup>2</sup>]
Size of your solar panel. Enter the size of your solar panels (total of all cells).
#### Diffuse radiation efficiency [%] #### Diffuse radiation efficiency [%]
Efficiency of your solar power plant for diffuse radiation. When pointing up it should be around 100%, when pointing to the horizon it may be around 50%. Specify the efficiency of your solar power plant for diffuse radiation. When pointing up, it should be around 100%, but when pointing towards the horizon, it may be 50% or less, depending on the environment.
Also depends on reflections etc. You probably need to optimize this parameter.
#### Albedo [0..1]
Specify the average albedo for your environment to take reflections into account. The value ranges from 0 (all radiation is absorbed) to 1 (all radiation is reflected).
Examples: Fresh snow: 0.8, green gras: 0.25, asphalt: 0.1
You probably need to optimize this parameter.
#### Inverter efficiency [%]
Enter the efficiency of your inverter.
#### Inverter power [W] #### Inverter power [W]
Maximum power of your inverter. If it is lower than the maximum power of your panels the output power of your system will be limited by this parameter. Specify the maximum power of your inverter. If it is lower than the maximum power of your panels, the output power of your system will be limited by this parameter.
#### Inverter efficiency [%] #### Central inverter
Efficiency of your inverter. Select this checkbox to apply the inverter limit to the entire system. When in 'show sum' mode, the power limits of all inverters with this checkbox enabled will be aggregated and used as the system-wide power limit.
#### Shading #### Shading
In this section you can define the shading on your solar panels. In this section you can define the shading on your solar panels.
For each azimuth angle range, you can specify the minimum elevation of the sun that is necessary for the sun to hit the solar panels. For each azimuth angle range, you can specify the minimum elevation of the sun that is necessary for the sun to hit the solar panels.
For elevations below this value you can set the percentage of shading. For example, a building will reduce radiation by 100%, a tree maybe only by 60%. For elevations below this value you can set the percentage of shading. For example, a building will reduce radiation by 100%, a tree maybe only by 60%.
You can use the sun icon button in the main window to get information about the current azimuth and elevation of the sun to determine at what elevation the sun gets above buildings or trees.
## License ## License
@ -71,6 +110,9 @@ The app uses:
- AutoSuggestTextViewAPICall (https://github.com/Truiton/AutoSuggestTextViewAPICall) which is licensed under <a href='https://github.com/Truiton/AutoSuggestTextViewAPICall/blob/master/LICENSE'>Apache License Version 2.0</a> - AutoSuggestTextViewAPICall (https://github.com/Truiton/AutoSuggestTextViewAPICall) which is licensed under <a href='https://github.com/Truiton/AutoSuggestTextViewAPICall/blob/master/LICENSE'>Apache License Version 2.0</a>
- Map data from OpenStreetMap, licensed under the Open Data Commons Open Database License (ODbL) by the OpenStreetMap Foundation (OSMF) (https://www.openstreetmap.org/copyright) - Map data from OpenStreetMap, licensed under the Open Data Commons Open Database License (ODbL) by the OpenStreetMap Foundation (OSMF) (https://www.openstreetmap.org/copyright)
- Solar positioning library (https://github.com/klausbrunner/solarpositioning) which is licensed under <a href='https://github.com/klausbrunner/solarpositioning/blob/master/LICENSE.txt'>MIT License</a> - Solar positioning library (https://github.com/klausbrunner/solarpositioning) which is licensed under <a href='https://github.com/klausbrunner/solarpositioning/blob/master/LICENSE.txt'>MIT License</a>
- Zip4j (https://github.com/srikanth-lingala/zip4j) which is licensed under Apache License Version 2.0
- CompassView (https://github.com/kix2902/CompassView) which is published under Apache License 2.0
## Contributing ## Contributing
If you find a bug, please open an issue in the Github repository, assuming one does not already exist. If you find a bug, please open an issue in the Github repository, assuming one does not already exist.
@ -79,15 +121,3 @@ If you find a bug, please open an issue in the Github repository, assuming one d
- Make your description as precise as possible. - Make your description as precise as possible.
If you know the solution to a bug please report it in the corresponding issue and if possible modify the code and create a pull request. If you know the solution to a bug please report it in the corresponding issue and if possible modify the code and create a pull request.
## Try my other apps
| **RadarWeather** | **Gas Prices** | **Smart Eggtimer** |
|:---:|:---:|:---:|
| [<img src="https://github.com/woheller69/weather/blob/main/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.weather/)| [<img src="https://github.com/woheller69/spritpreise/blob/main/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.spritpreise/) | [<img src="https://github.com/woheller69/eggtimer/blob/main/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.eggtimer/) |
| **Level** | **hEARtest** | **GPS Cockpit** |
| [<img src="https://github.com/woheller69/Level/blob/master/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.level/) | [<img src="https://github.com/woheller69/audiometry/blob/new/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.audiometry/) | [<img src="https://github.com/woheller69/gpscockpit/blob/master/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.gpscockpit/) |
| **Audio Analyzer** | **LavSeeker** | **TimeLapseCam** |
| [<img src="https://github.com/woheller69/audio-analyzer-for-android/blob/master/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.audio_analyzer_for_android/) |[<img src="https://github.com/woheller69/lavatories/blob/master/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.lavatories/) | [<img src="https://github.com/woheller69/TimeLapseCamera/blob/master/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.TimeLapseCam/) |
| **Arity** | **omWeather** | **solXpect** |
| [<img src="https://github.com/woheller69/arity/blob/master/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.arity/) | [<img src="https://github.com/woheller69/omweather/blob/master/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.omweather/) | [<img src="https://github.com/woheller69/solXpect/blob/main/fastlane/metadata/android/en-US/images/icon.png" width="50">](https://f-droid.org/packages/org.woheller69.solxpect/) |

View file

@ -2,20 +2,31 @@ apply plugin: 'com.android.application'
android { android {
compileSdkVersion 32 lintOptions {
disable 'MissingTranslation'
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
} }
dependenciesInfo {
// Disable including dependency metadata when building APKs
includeInApk = false
// Disable including dependency metadata when building Android App Bundles
includeInBundle = false
}
compileSdk 35
android.buildFeatures.buildConfig true
namespace 'org.woheller69.weather'
defaultConfig { defaultConfig {
applicationId "org.woheller69.solxpect" applicationId "org.woheller69.solxpect"
minSdkVersion 26 minSdkVersion 26
targetSdkVersion 31 targetSdk 35
versionCode 11 versionCode 27
versionName "1.1" versionName "2.7"
buildConfigField "String", "BASE_URL", "\"https://api.open-meteo.com/v1/\"" buildConfigField "String", "BASE_URL", "\"https://api.open-meteo.com/v1/\""
buildConfigField "String", "TILES_URL","\"https://tile.openstreetmap.org/\""
buildConfigField "String", "GEOCODING_URL","\"https://geocoding-api.open-meteo.com/\""
buildConfigField "String", "GITHUB_URL","\"https://github.com/woheller69/solxpect/\"" buildConfigField "String", "GITHUB_URL","\"https://github.com/woheller69/solxpect/\""
} }
@ -31,13 +42,14 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.diogobernardino:williamchart:2.2' implementation 'com.diogobernardino:williamchart:2.2'
implementation 'net.e175.klaus:solarpositioning:0.1.10' implementation 'net.e175.klaus:solarpositioning:0.1.10'
implementation 'androidx.preference:preference:1.2.0' implementation 'androidx.preference:preference:1.2.1'
implementation 'androidx.appcompat:appcompat:1.5.1' implementation 'androidx.appcompat:appcompat:1.7.1'
implementation 'com.google.android.material:material:1.6.1' implementation 'com.google.android.material:material:1.12.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'com.android.volley:volley:1.2.1' implementation 'com.android.volley:volley:1.2.1'
implementation 'androidx.recyclerview:recyclerview:1.2.1' implementation 'androidx.recyclerview:recyclerview:1.4.0'
implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.cardview:cardview:1.0.0'
implementation "androidx.lifecycle:lifecycle-viewmodel:2.5.1" //needed due to duplicate class error implementation 'net.lingala.zip4j:zip4j:2.9.1'
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1" //needed due to duplicate class error implementation 'com.github.woheller69:CompassView:948f3db329'
implementation 'androidx.webkit:webkit:1.5.0'
} }

View file

@ -1,14 +1,15 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android">
package="org.woheller69.weather">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application <application
android:allowBackup="true" android:allowBackup="true"
android:requestLegacyExternalStorage="true"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:label="@string/app_name" android:label="@string/app_name"
android:supportsRtl="true" android:supportsRtl="true"
android:fontFamily = "sans-serif-light" android:fontFamily = "sans-serif-light"
@ -56,6 +57,16 @@
android:value="org.woheller69.weather.activities.ForecastCityActivity" /> android:value="org.woheller69.weather.activities.ForecastCityActivity" />
</activity> </activity>
<activity
android:name="org.woheller69.weather.activities.BackupRestoreActivity"
android:label="@string/activity_backuprestore"
android:parentActivityName="org.woheller69.weather.activities.ForecastCityActivity"
android:theme="@style/AppTheme.NoActionBar">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.woheller69.weather.activities.ForecastCityActivity" />
</activity>
<activity <activity
android:name="org.woheller69.weather.activities.SettingsActivity" android:name="org.woheller69.weather.activities.SettingsActivity"
android:label="@string/activity_settings_title" android:label="@string/activity_settings_title"

View file

@ -2,49 +2,68 @@
<body> <body>
<h2>Übersicht</h2> <h2>Übersicht</h2>
Diese App nimmt direkte und diffuse Strahlungsdaten von Open-Meteo.com, berechnet die Position Die Android-App solXpect prognostiziert den Ertrag Ihrer Photovoltaik-Anlage. Sie lädt direkte und diffuse Strahlungsdaten von Open-Meteo.com, berechnet die Position der Sonne und projiziert die Strahlung auf Ihr Solarpanel.
der Sonne und projiziert die Strahlung auf Ihr Solarpanel. Anschließend wird die geschätzte Energieproduktion für die nächsten Stunden (bis zu 16 Tage) dargestellt, wobei stündliche Werte für die vorangegangene Stunde gelten. Wenn zum Beispiel um 11:00 Uhr 150 Wh angezeigt werden, bedeutet dies, dass Sie zwischen 10:00 und 11:00 Uhr 150 Wh erwarten können.
Es zeigt die geschätzte Energieproduktion für die nächsten Stunden und bis zu 16 Tage an. Die mit &#8721 beginnenden Werte zeigen die kumulierte Energie seit Mitternacht des ersten Tages.
Die Stundenwerte werden für die vorangegangene Stunde berechnet. Um solXpect zu verwenden, geben Sie einfach Ihre Breiten- und Längenkoordinaten sowie den Azimut und die Neigung Ihres Solarpanels ein.
Wenn also um 11:00 Uhr 150 Wh angezeigt werden, bedeutet dies, dass Sie zwischen 10:00 und 11:00 Uhr mit 150 Wh rechnen können. Sie geben auch Informationen über die Spitzenleistung, Effizienz, Temperaturkoeffizienten und Fläche Ihres Solarpanels sowie die maximale Leistung und Effizienz Ihres Wechselrichters an.
solXpect ermöglicht es Ihnen auch, Verschattungen auf Ihren Solarzellen zu definieren, indem Sie die minimale Sonnenhöhe angeben, die erforderlich ist, damit die Sonne auf die Solarzellen trifft, sowie den prozentualen Schatten für Höhen unter diesem Wert.
<h2>Parameter</h2> <h2>Parameter</h2>
<h3>Name</h3>
Definieren Sie einen Namen für den Standort.
Wenn Sie mehrere Solarpaneele mit denselben Breiten- und Längenkoordinaten, aber in unterschiedliche Richtungen ausgerichtet haben, können Sie sie als separate Standorte definieren und die Funktion "Summe anzeigen" verwenden, um ihre Leistung zusammenzufassen.
In diesem Fall sollten Sie Ihre Standortnamen als "myPV | part1", "myPV | part2" usw. definieren. Im "Summe anzeigen"-Modus wird der Standort dann als "myPV" angezeigt und "|" wird in diesem Fall als Trennzeichen verwendet.
<h3>Breitengrad [°]</h3> <h3>Breitengrad [°]</h3>
Der Breitengrad gibt die Nord-Süd-Position Ihres Solarkraftwerks an. Er reicht von 90° am Südpol bis 90° am Nordpol. Geben Sie die Nord-Süd-Position Ihrer Solaranlage an, die von -90° am Südpol bis 90° am Nordpol reicht.
<h3>Längengrad [°]</h3> <h3>Längengrad [°]</h3>
Der Längengrad gibt die Ost-West-Position Ihres Solarkraftwerks an. Der Nullmeridian definiert 0° Länge. Positive Längen liegen östlich des Nullmeridians, negative westlich. Geben Sie die Ost-West-Position Ihrer Solaranlage an, wobei 0° als Nullmeridian definiert ist. Positive Längengrade liegen östlich des Nullmeridians, negative westlich.
<h3>Azimut [°]</h3> <h3>Azimut [°]</h3>
Azimut ist die horizontale Richtung Ihres Solarkraftwerks. 0° entspricht Norden, 90° entspricht Osten, 180° entspricht Süden, 270° entspricht Westen. Legen Sie die horizontale Ausrichtung Ihrer Solaranlage fest, wobei 0° Norden entspricht, 90° Osten, 180° Süden und 270° Westen.
<h3>Neigung [°]</h3> <h3>Neigung [°]</h3>
Neigung ist die vertikale Richtung Ihres Solarkraftwerks. 0° bedeutet, dass es zum Himmel zeigt, 90° bedeutet, dass es vertikal ausgerichtet ist und zum Horizont zeigt. Legen Sie die vertikale Ausrichtung Ihrer Solaranlage fest, wobei 0° nach oben zum Himmel und 90° zum Horizont zeigt.
<h3>Zellen max. Leistung [W]</h3> <h3>Peakleistung der Zellen [W]</h3>
Maximale Leistung, die Ihre Solarzellen liefern können. Geben Sie die maximale Leistung an, die Ihre Solarzellen liefern können (Gesamtleistung aller Zellen). Derzeit wird dieser Wert nur verwendet, wenn für die Zelleneffizienz oder die Zellfläche ein Wert von 0 angegeben ist.
In diesem Fall wird angenommen, dass die Nennleistung der Zellen bei einer Bestrahlungsstärke von 1000 W/m² vorliegt.
<h3>Zelleneffizienz [%]</h3> <h3>Wirkungsgrad der Zellen [%]</h3>
Energieanteil in Form von Sonnenlicht, der von der Solarzelle in Strom umgewandelt werden kann. Geben Sie den Anteil der Energie in Form von Sonnenlicht an, der von der Solarzelle in Strom umgewandelt werden kann.
<h3>Zellenfläche [m<sup>2</sup>]</h3> <h3>Temperaturkoeffizient [%/K]</h3>
Größe der aktiven Fläche Ihres Solarpanels. Geben Sie die Abhängigkeit der Zellleistung von der Temperatur an, normalerweise im Bereich von -0,4%/K. Die Zelltemperatur wird aus der Umgebungstemperatur und der Gesamtstrahlung geschätzt.
<h3>Diffuse Strahlungseffizienz [%]</h3> <h3>Fläche der Zellen [m<sup>2</sup>]</h3>
Wirkungsgrad Ihres Solarkraftwerks bei diffuser Strahlung. Bei der Ausrichtung nach oben sollte er etwa 100 % betragen, bei der Ausrichtung zum Horizont etwa 50 %. Geben Sie die Größe Ihrer Solarmodule (Gesamtfläche aller Zellen) an.
Dies ist auch abhängig von Reflexionen etc.
<h3>Effizienz diffuse Strahlung [%]</h3>
Geben Sie die Effizienz Ihrer Solaranlage für diffuse Strahlung an. Wenn sie nach oben gerichtet ist, sollte sie etwa 100% betragen, aber wenn sie in Richtung Horizont gerichtet ist, beträgt Sie 50% oder weniger - abhängig von der Umgebung.
Sie müssen diesen Parameter wahrscheinlich optimieren.
<h3>Albedo [0..1]</h3>
Geben Sie die durchschnittliche Albedo für Ihre Umgebung an, um Reflexionen zu berücksichtigen. Der Wertebereich geht von 0 (alle Strahlung wird absorbiert) bis 1 (alle Strahlung wird reflektiert).
Beispiele: Frischer Schnee: 0.8, grünes Gras: 0.25, Asphalt: 0.1
Sie müssen diesen Parameter wahrscheinlich optimieren.
<h3>Wechselrichtereffizienz [%]</h3>
Geben Sie den Wirkungsgrad Ihres Wechselrichters ein.
<h3>Wechselrichterleistung [W]</h3> <h3>Wechselrichterleistung [W]</h3>
Maximale Leistung Ihres Wechselrichters. Wenn sie niedriger als die maximale Leistung Ihrer Panels ist, wird die Ausgangsleistung Ihres Systems durch diesen Parameter begrenzt. Geben Sie die maximale Leistung Ihres Wechselrichters an. Wenn diese geringer ist als die maximale Leistung Ihrer Module, wird die Ausgangsleistung Ihres Systems durch diesen Parameter begrenzt.
<h3>Wirkungsgrad des Wechselrichters [%]</h3> <h3>Zentralwechselrichter</h3>
Wirkungsgrad Ihres Wechselrichters. Wählen Sie dieses Kontrollkästchen, um die Wechselrichter-Begrenzung auf das gesamte System anzuwenden. Im "Summe anzeigen"-Modus, werden die Leistungsbegrenzungen aller Wechselrichter mit gesetztem Kontrollkästchen aggregiert und als systemweite Leistungsbegrenzung verwendet.
<h3>Abschattung</h3> <h3>Abschattung</h3>
In diesem Abschnitt können Sie die Abschattung Ihrer Solarmodule definieren. In diesem Abschnitt können Sie die Beschattung Ihrer Solarmodule definieren.
Für jeden Azimutwinkelbereich können Sie die minimale Elevation der Sonne angeben, die erforderlich ist, damit die Sonne auf die Solarmodule trifft. Für jeden Azimutwinkelbereich können Sie die minimale Sonnenhöhe angeben, die erforderlich ist, damit die Sonne auf die Solarmodule trifft.
Für Elevationen unter diesem Wert können Sie den Prozentsatz der Abschattung festlegen. Beispielsweise reduziert ein Gebäude die Strahlung um 100 %, ein Baum vielleicht nur um 60 %. Für Höhen unter diesem Wert können Sie den Prozentsatz der Beschattung festlegen. Zum Beispiel reduziert ein Gebäude die Strahlung um 100 %, ein Baum vielleicht nur um 60 %.
Sie können die Schaltfläche mit dem Sonnen-Symbol im Hauptfenster verwenden, um Informationen über den aktuellen Azimut und die Höhe der Sonne zu erhalten und zu bestimmen, auf welcher Höhe die Sonne über Gebäuden oder Bäumen steht.
</body> </body>
</html> </html>

View file

@ -2,49 +2,68 @@
<body> <body>
<h2>Overview</h2> <h2>Overview</h2>
This app takes direct and diffuse radiation data from Open-Meteo.com, calculates the position This app forecasts the output of your solar power plant by using direct and diffuse radiation data from Open-Meteo.com, calculating the position of the sun, and projecting the radiation on your solar panel.
of the sun and projects the radiation on your solar panel. It shows the estimated energy production for the next 16 days, with hourly values calculated for the preceding hour. As an example, if there are 150 Wh shown at 11:00 this means you can expect 150 Wh between 10:00 and 11:00.
It shows the estimated energy production for the next hours and up to 16 days. The values starting with &#8721 show the cumulated energy since midnight of the first day.
The hourly values are calculated for the preceding hour. To use solXpect, you simply enter your latitude and longitude coordinates, as well as the azimuth and tilt of your solar panel.
So if there are 150 Wh shown at 11:00 this means you can expect 150 Wh between 10:00 and 11:00. You also enter information about the peak power, efficiency, temperature coefficient, and area of your solar panel, as well as the maximum power and efficiency of your inverter.
Additionally, solXpect allows you to define shading on your solar panels by specifying the minimum elevation of the sun necessary for the sun to hit the solar panels, as well as the percentage of shading for elevations below this value.
<h2>Parameters</h2> <h2>Parameters</h2>
<h3>Name</h3>
Define a name for the location.
If you have multiple solar panels with the same latitude and longitude but pointing in different directions, you can define them as separate locations and use the 'show sum' feature to summarize their output.
In this case you should define your location names as 'myPV | part1', 'myPV | part2', etc. In 'show sum' mode the location is then shown as 'myPV' and '|' is taken as delimiter in this case.
<h3>Latitude [°]</h3> <h3>Latitude [°]</h3>
Latitude specifies the northsouth position of your solar power plant. It ranges from 90° at the south pole to 90° at the north pole. Enter the north-south position of your solar power plant, ranging from -90° at the south pole to 90° at the north pole.
<h3>Longitude [°]</h3> <h3>Longitude [°]</h3>
Longitude specifies the eastwest position of your solar power plant. The prime meridian defines 0° longitude. Positive longitudes are east of the prime meridian, negative ones are west. Enter the east-west position of your solar power plant, with 0° defined as the prime meridian. Positive longitudes are east of the prime meridian, negative ones are west.
<h3>Azimuth [°]</h3> <h3>Azimuth [°]</h3>
Azimuth is the horizontal direction of your solar power plant. 0° equals North, 90° equals East, 180° equals South, 270° equals West. Specify the horizontal direction of your solar power plant, with 0° corresponding to North, 90° to East, 180° to South, and 270° to West.
<h3>Tilt [°]</h3> <h3>Tilt [°]</h3>
Tilt is the vertical direction of your solar power plant. 0° means it points up towards the the sky, 90° means it has a vertical orientation and points towards the horizon. Specify the vertical direction of your solar power plant, with 0° pointing upwards towards the sky and 90° being a vertical orientation pointing towards the horizon.
<h3>Cells max power [W]</h3> <h3>Cells peak power [W]</h3>
Maximum power your solar cells can deliver. Enter the maximum power your solar cells (total of all cells) can deliver. At the moment this value is only used if a value of 0 is specified for cells efficiency or cell area.
In this case it is assumed that the cells peak power is given at an irradiance of 1000W/sqm.
<h3>Cells efficiency [%]</h3> <h3>Cells efficiency [%]</h3>
Portion of energy in the form of sunlight that can be converted into electricity by the solar cell. Specify the portion of energy in the form of sunlight that can be converted into electricity by the solar cell.
<h3>Temperature coefficient [%/K]</h3>
Enter the dependence of the cell power on temperature, usually in the range of -0.4%/K. Cell temperature is estimated from ambient temperature and total irradiance.
<h3>Cell area [m<sup>2</sup>]</h3> <h3>Cell area [m<sup>2</sup>]</h3>
Size of the active area your solar panel. Enter the size of your solar panels (total of all cells).
<h3>Diffuse radiation efficiency [%]</h3> <h3>Diffuse radiation efficiency [%]</h3>
Efficiency of your solar power plant for diffuse radiation. When pointing up it should be around 100%, when pointing to the horizon it may be around 50%. Specify the efficiency of your solar power plant for diffuse radiation. When pointing up, it should be around 100%, but when pointing towards the horizon, it may be 50% or less, depending on the environment.
Also depends on reflections etc. You probably need to optimize this parameter.
<h3>Inverter power [W]</h3> <h3>Albedo [0..1]</h3>
Maximum power of your inverter. If it is lower than the maximum power of your panels the output power of your system will be limited by this parameter. Specify the average albedo for your environment to take reflections into account. The value ranges from 0 (all radiation is absorbed) to 1 (all radiation is reflected).
Examples: Fresh snow: 0.8, green gras: 0.25, asphalt: 0.1
You probably need to optimize this parameter.
<h3>Inverter efficiency [%]</h3> <h3>Inverter efficiency [%]</h3>
Efficiency of your inverter. Enter the efficiency of your inverter.
<h3>Inverter power [W]</h3>
Specify the maximum power of your inverter. If it is lower than the maximum power of your panels, the output power of your system will be limited by this parameter.
<h3>Central Inverter</h3>
Select this checkbox to apply the inverter limit to the entire system. When in 'show sum' mode, the power limits of all inverters with this checkbox enabled will be aggregated and used as the system-wide power limit.
<h3>Shading</h3> <h3>Shading</h3>
In this section you can define the shading on your solar panels. In this section you can define the shading on your solar panels.
For each azimuth angle range, you can specify the minimum elevation of the sun that is necessary for the sun to hit the solar panels. For each azimuth angle range, you can specify the minimum elevation of the sun that is necessary for the sun to hit the solar panels.
For elevations below this value you can set the percentage of shading. For example, a building will reduce radiation by 100%, a tree maybe only by 60%. For elevations below this value you can set the percentage of shading. For example, a building will reduce radiation by 100%, a tree maybe only by 60%.
You can use the sun icon button in the main window to get information about the current azimuth and elevation of the sun to determine at what elevation the sun gets above buildings or trees.
</body> </body>
</html> </html>

View file

@ -0,0 +1,66 @@
<html>
<body>
<h2>Introduzione</h2>
Questa app fa previsioni sulla produzione del tuo impianto fotovoltaico usando i dati di radiazione luminosa diretta e indiretta prodotti da Open-Meteo.com, calcolando la posizione del sole e proiettando la radiazione sul tuo pannello solare.
Viene mostrata la stima di produzione del tuo impianto fotovoltaico per i prossimi 16 giorni, con valori orari calcolati per lora precedente. Ad esempio: se alle 11:00 viene mostrato il dato 150 Wh significa che devi aspettarti 150 Wh tra le 10:00 e le 11:00.
I valori con il simbolo &#8721 si riferiscono allenergia accumulata dalla mezzanotte del primo giorno.
Per usare solXpect devi conoscere alcuni parametri del tuo impianto: la latitudine, la longitudine, lazimuth (orientamento misurato in gradi rispetto alla direzione del nord), linclinazione, la potenza di picco e la potenza massima dellinverter.
Per migliorare la stima puoi anche personalizzare altri dati ad esempio lefficienza di conversione dellinverter, lefficienza dei pannelli, il coefficiente di temperatura e larea dellimpianto.
Infine solXpect gestisce anche gli eventuali ombreggiamenti del tuo impianto, infatti è possibile specificare lelevazione minima del sole necessaria per illuminare i pannelli e la percentuale di ombreggiamento per le elevazioni minori di questo valore.
<h2>Parametri</h2>
<h3>Nome</h3>
Scegli un nome per limpianto.
Se hai pannelli dello stesso impianto con direzione, inclinazione o ombreggiamento diversi puoi inserirli come impianti diversi e poi usare la funzione 'Mostra totale' per mostrare la produzione complessiva.
In questo caso devi scegliere i nomi degli impianti come "mioFV | sezione 1", "mioFV | sezione 2", ... 'Mostra totale' utilizzerà "mioFV" come nome dellimpianto in quanto il carattere '|' viene considerato come separatore tra le diverse parti.
<h3>Latitudine [°]</h3>
Indica la coordinata nord-sud del tuo impianto, può variare tra i valori -90° (polo sud) e 90° (polo nord).
<h3>Longitudine [°]</h3>
Indica La coordinata est-ovest del tuo impianto, 0° è il meridiano di Greenwich, valori positivi indicato posizioni a est di tale meridiano, negativi lovest.
<h3>Azimuth [°]</h3>
Indica come è orientato sul piano orizzontale il tuo impianto, 0° indicano il Nord, 90° lEst, 180° il Sud e 270° lOvest.
<h3>Inclinazione [°]</h3>
Indica linclinazione sul piano verticale del tuo impianto, se limpianto punta direttamente il cielo sarà 0°, invece sarà 90° per pannelli verticali.
<h3>Potenza di picco [W]</h3>
Indica la massima potenza del tuo impianto, spesso nel progetto dellimpianto è indicata con lunità di misura Wp. Attualmente questo valore viene utilizzato solo se è il valore dellefficienza delle celle o dellarea è 0.
In questo caso si suppone che che le il valore di picco delle celle si raggiunga con unirradianza di 1000W/mq.
<h3>Efficienza dei pannelli [%]</h3>
Indica la porzione di energia luminosa che viene convertita in elettricità dai pannelli.
<h3>Coefficiente di temperatura [%/K]</h3>
Indica la dipendenza della potenza dei pannelli dalla temperatura, tipicamente è un valore attorno a -0.4%/K. La temperatura dei pannelli viene stimata in base alla temperatura ambientale e allirradianza totale.
<h3>Area dei pannelli [m<sup>2</sup>]</h3>
Indica larea totale del tuo impianto.
<h3>Efficienza della radiazione diffusa [%]</h3>
Indica la capacità di catturare la radiazione diffusa. Per impianti poco inclinati si avvicina al 100%, per impianti molto inclinati potrebbe essere del 50% o meno, in base allambiente circostante.
<h3>Albedo [0..1]</h3>
Indica la capacità dellambiente circostante il tuo impianto di riflettere la luce solare. I valori variano da 0 (tutta la luce viene assorbita), a 1 (tutta la radiazione viene riflessa).
Esempi: neve fresca: 0,8 ; erba verde: 0,25 ; asfalto: 0,1 .
<h3>Efficienza dellinverter [%]</h3>
Indica lefficienza di conversione dellinverter.
<h3>Potenza dellinverter [W]</h3>
Indica la potenza massima gestita dallinverter. Se è minore della potenza massima dei pannelli, la produzione dellimpianto verrà limitata da questo parametro.
<h3>Invertitore centrale</h3>
Seleziona questa casella di controllo per applicare il limite dell'invertitore all'intero sistema. Quando si è in modalità "mostra totale", i limiti di potenza di tutti gli invertitori con questa casella di controllo abilitata verranno aggregati e utilizzati come limite di potenza dell'intero sistema.
<h3>Ombreggiamenti</h3>
In questa sezione puoi definire gli ombreggiamenti dei pannelli.
Per ogni intervallo di azimuth è possibile specificare a quale altezza minima il sole illumina i pannelli.
Per altezze minori di questo valore è possibile impostare la percentuale di ombreggiamento. Ad esempio un edificio riduce la radiazione solare del 100%, un albero circa del 60%.
Per impostare questi valori è possibile utilizzare i dati correnti di azimuth e altezza del sole che vengono mostrati premendo licona con il simbolo del sole che si trova nella schermata principale.
</body>
</html>

View file

@ -0,0 +1,68 @@
<html>
<body>
<h2>Genel Bakış</h2>
Bu uygulama, Open-Meteo.com'dan doğrudan ve dağınık radyasyon verilerini kullanarak, güneşin konumunu hesaplayarak ve radyasyonu güneş panelinize yansıtarak güneş enerjisi santralinizin çıktısını tahmin eder.
Bir önceki saat için hesaplanan saatlik değerlerle birlikte önümüzdeki 16 gün için tahmini enerji üretimini gösterir. Örnek olarak, 11:00'de 150 Wh gösteriliyorsa bu, 10:00 ile 11:00 arasında 150 Wh bekleyebileceğiniz anlamına gelir.
&#8721 ile başlayan değerler ilk günün gece yarısından itibaren biriken enerjiyi göstermektedir.
solXpect'i kullanmak için enlem ve boylam koordinatlarınızın yanı sıra güneş panelinizin azimut ve eğimini girmeniz yeterlidir.
Ayrıca güneş panelinizin tepe gücü, verimliliği, sıcaklık katsayısı ve alanı ile invertörünüzün maksimum gücü ve verimliliği hakkındaki bilgileri de girebilirsiniz.
Öte yandan solXpect, güneşin güneş panellerine vurması için gereken minimum güneş yüksekliğini ve bu değerin altındaki yükseklikler için gölgeleme yüzdesini belirleyerek güneş panellerinizdeki gölgelemeyi tanımlamanıza olanak tanır.
<h2>Parametreler</h2>
<h3>İsim</h3>
Konum için bir ad tanımlayın.
Aynı enlem ve boylama sahip fakat farklı yönleri işaret eden birden fazla güneş paneliniz varsa, bunları ayrı konumlar olarak tanımlayabilir ve çıktılarını özetlemek için 'Toplamı göster' özelliğini kullanabilirsiniz.
Bu durumda konum adlarınızı 'PVkaydım | part1', 'PVkaydım | part2' vb. olarak tanımlamalısınız. 'Toplamı göster' modunda konum daha sonra 'PVkaydım' olarak gösterilir ve bu durumda '|' sınırlayıcı olarak alınır.
<h3>Enlem [°]</h3>
Güney kutbunda -90° ile kuzey kutbunda 90° arasında değişen güneş enerjisi santralinizin kuzey-güney konumunu girin.
<h3>Boylam [°]</h3>
Güneş enerjisi santralinizin doğu-batı konumunu girin, 0° ana meridyen olarak tanımlanır. Pozitif boylamlar ana meridyenin doğusunda, negatif olanlar ise batısındadır.
<h3>Azimut [°]</h3>
Güneş enerjisi santralinizin yatay yönünü 0° Kuzeye, 90° Doğuya, 180° Güneye ve 270° Batıya karşılık gelecek şekilde belirtin.
<h3>Eğim [°]</h3>
Güneş enerjisi santralinizin dikey yönünü belirtin; 0° gökyüzüne doğru yukarı bakacak ve 90° ufka doğru dikey bir yön gösterecek şekilde.
<h3>Hücrelerin tepe gücü [W]</h3>
Güneş hücrelerinizin (tüm hücrelerin toplamı) sağlayabileceği maksimum gücü girin. Şu anda bu değer yalnızca hücre verimliliği veya hücre alanı için 0 değeri belirtilmişse kullanılmaktadır.
Bu durumda, hücrelerin tepe gücünün 1000W/m2 ışınımda verildiği varsayılır.
<h3>Hücre verimliliği [%]</h3>
Güneş ışığı formundaki enerjinin güneş pili tarafından elektriğe dönüştürülebilecek kısmını belirtin.
<h3>Sıcaklık katsayısı [%/K]</h3>
Hücre gücünün sıcaklığa bağımlılığını girin, genellikle -%0,4/K aralığındadır. Hücre sıcaklığı, ortam sıcaklığı ve toplam ışınımdan tahmin edilir.
<h3>Hücre alanı [m<sup>2</sup>]</h3>
Güneş panellerinizin boyutunu girin. (tüm hücrelerin toplamı)
<h3>Dağınık radyasyon verimliliği [%]</h3>
Dağınık radyasyon için güneş enerjisi santralinizin verimliliğini belirtin. Yukarı bakarken %100 civarında olmalıdır, ancak ufka doğru bakarken ortama bağlı olarak %50 veya daha az olabilir.
Muhtemelen bu parametreyi optimize etmeniz gerekir.
<h3>Yansıtabilirlik [0..1]</h3>
Yansımaları hesaba katmak için ortamınız için ortalama albedoyu belirtin. Değer 0 (tüm radyasyon emilir) ile 1 (tüm radyasyon yansıtılır) arasında değişir.
Örnek: Taze kar: 0.8, yeşil ot: 0.25, asfalt: 0.1
Muhtemelen bu parametreyi optimize etmeniz gerekir.
<h3>İnvertör verimliliği [%]</h3>
İnvertörünüzün verimliliğini girin.
<h3>İnvertör gücü [W]</h3>
İnvertörünüzün maksimum gücünü belirtin. Panellerinizin maksimum gücünden daha düşükse, sisteminizin çıkış gücü bu parametre ile sınırlandırılacaktır.
<h3>Merkezi invertör</h3>
İnvertör limitini tüm sisteme uygulamak için bu onay kutusunu seçin. "Toplamı göster" modundayken, bu onay kutusu etkinleştirilmiş olan tüm invertörlerin güç limitleri toplanacak ve tüm sistemin güç limiti olarak kullanılacaktır.
<h3>Gölgelendirme</h3>
Bu bölümde güneş panellerinizdeki gölgelendirmeyi tanımlayabilirsiniz.
Her bir azimut açısı aralığı için, güneşin güneş panellerine çarpması için gerekli olan minimum güneş yüksekliğini belirleyebilirsiniz.
Bu değerin altındaki yükseklikler için gölgeleme yüzdesini ayarlayabilirsiniz. Örneğin, bir bina radyasyonu %100 oranında azaltırken, bir ağaç sadece %60 oranında azaltabilir.
Ana penceredeki güneş simgesi düğmesini kullanarak güneşin mevcut azimutu ve yüksekliği hakkında bilgi alabilir ve güneşin binaların veya ağaçların üzerinde hangi yükseklikte olduğunu belirleyebilirsiniz.
</body>
</html>

View file

@ -1,7 +0,0 @@
/*
Leaflet.TileLayer.ColorFilter
(c) 2018, Claudio T. Kawakani
A simple and lightweight Leaflet plugin to apply CSS filters on map tiles.
https://github.com/xtk93x/Leaflet.TileLayer.ColorFilter
*/
"use strict";L.TileLayer.ColorFilter=L.TileLayer.extend({intialize:function(t,i){L.TileLayer.prototype.initialize.call(this,t,i)},colorFilter:function(){var r=["blur:px","brightness:%","bright:brightness:%","bri:brightness:%","contrast:%","con:contrast:%","grayscale:%","gray:grayscale:%","hue-rotate:deg","hue:hue-rotate:deg","hue-rotation:hue-rotate:deg","invert:%","inv:invert:%","opacity:%","op:opacity:%","saturate:%","saturation:saturate:%","sat:saturate:%","sepia:%","sep:sepia:%"];return(this.options.filter?this.options.filter:[]).map(function(t){var i=t.toLowerCase().split(":");if(2===i.length){var e=r.find(function(t){return t.split(":")[0]===i[0]});if(e)return e=e.split(":"),i[1]+=/^\d+$/.test(i[1])?e[e.length-1]:"","".concat(e[e.length-2],"(").concat(i[1],")")}return""}).join(" ")},_initContainer:function(){L.TileLayer.prototype._initContainer.call(this);this._container.style.filter=this.colorFilter()},updateFilter:function(t){this.options.filter=t,this._container&&(this._container.style.filter=this.colorFilter())}}),L.tileLayer.colorFilter=function(t,i){return new L.TileLayer.ColorFilter(t,i)};

View file

@ -34,7 +34,7 @@ var map = L.map('mapid', {
}).setView([getUrlParameter('lat'), getUrlParameter('lon')], 10); //zoom factor 10 }).setView([getUrlParameter('lat'), getUrlParameter('lon')], 10); //zoom factor 10
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { L.tileLayer(getUrlParameter('tiles')+'{z}/{x}/{y}.png', {
attribution: 'Map data ©<a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' attribution: 'Map data ©<a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map); }).addTo(map);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Before After
Before After

View file

@ -0,0 +1,75 @@
package org.woheller69.weather;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class Backup {
public static final int PERMISSION_REQUEST_CODE = 123;
public static boolean checkPermissionStorage (Context context) {
int result = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE);
int result1 = ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
return true;
} else {
return result == PackageManager.PERMISSION_GRANTED && result1 == PackageManager.PERMISSION_GRANTED;
}
}
public static void requestPermission(Activity activity) {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setIcon(R.drawable.ic_warning_amber_black_24dp);
builder.setTitle(activity.getResources().getString(R.string.permission_required));
builder.setMessage(activity.getResources().getString(R.string.permission_message,activity.getResources().getString(R.string.app_name)));
builder.setPositiveButton(R.string.dialog_OK_button, (dialog, which) -> {
dialog.cancel();
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE);
});
builder.setNegativeButton(R.string.dialog_NO_button, (dialog, whichButton) -> dialog.cancel());
AlertDialog dialog = builder.create();
dialog.show();
}
public static void zipExtract(Context context, File targetDir, Uri zipFile) {
ZipEntry zipEntry;
int readLen;
byte[] readBuffer = new byte[4096];
try {
InputStream src = context.getContentResolver().openInputStream(zipFile);
try {
try (ZipInputStream zipInputStream = new ZipInputStream(src)) {
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
File extractedFile = new File(targetDir ,zipEntry.getName());
try (OutputStream outputStream = Files.newOutputStream(extractedFile.toPath())) {
while ((readLen = zipInputStream.read(readBuffer)) != -1) {
outputStream.write(readBuffer, 0, readLen);
}
}
}
}
} catch (IOException ioException) {
ioException.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}

View file

@ -9,20 +9,24 @@ import java.time.ZoneId;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
public class SolarPowerPlant { public class SolarPowerPlant {
double albedo;
double latitude; double latitude;
double longitude; double longitude;
double cellsMaxPower; double cellsMaxPower;
double cellsArea; double cellsArea;
double cellsEfficiency; double cellsEfficiency;
double cellsTempCoeff;
double diffuseEfficiency; double diffuseEfficiency;
double inverterPowerLimit; double inverterPowerLimit;
double inverterEfficiency; double inverterEfficiency;
boolean isCentralInverter;
double azimuthAngle; double azimuthAngle;
double tiltAngle; double tiltAngle;
private int[] shadingElevation; private final int[] shadingElevation;
private int[] shadingOpacity; private final int[] shadingOpacity;
public SolarPowerPlant(double latitude, double longitude, double cellsMaxPower, double cellsArea, double cellsEfficiency, double diffuseEfficiency, double inverterPowerLimit, double inverterEfficiency, double azimuthAngle, double tiltAngle, int[] shadingElevation, int[] shadingOpacity ) { public SolarPowerPlant(double latitude, double longitude, double cellsMaxPower, double cellsArea, double cellsEfficiency, double cellsTempCoeff, double diffuseEfficiency, double inverterPowerLimit, double inverterEfficiency,boolean isCentralInverter, double azimuthAngle, double tiltAngle, int[] shadingElevation, int[] shadingOpacity, double albedo ) {
this.albedo = albedo;
this.latitude = latitude; this.latitude = latitude;
this.longitude = longitude; this.longitude = longitude;
this.cellsMaxPower = cellsMaxPower; this.cellsMaxPower = cellsMaxPower;
@ -35,10 +39,12 @@ public class SolarPowerPlant {
this.tiltAngle = tiltAngle; this.tiltAngle = tiltAngle;
this.shadingElevation = shadingElevation; this.shadingElevation = shadingElevation;
this.shadingOpacity = shadingOpacity; this.shadingOpacity = shadingOpacity;
this.cellsTempCoeff = cellsTempCoeff / 100;
this.isCentralInverter = isCentralInverter;
} }
public float getPower(double solarPowerNormal, double solarPowerDiffuse, long epochTimeSeconds) { public float getPower(double solarPowerNormal, double solarPowerDiffuse, double shortwaveRadiation, long epochTimeSeconds, double ambientTemperature) {
Instant i = Instant.ofEpochSecond(epochTimeSeconds); //currentTimeMillis is in GMT Instant i = Instant.ofEpochSecond(epochTimeSeconds); //currentTimeMillis is in GMT
ZonedDateTime dateTime = ZonedDateTime.ofInstant(i, ZoneId.of("GMT")); ZonedDateTime dateTime = ZonedDateTime.ofInstant(i, ZoneId.of("GMT"));
@ -63,17 +69,69 @@ public class SolarPowerPlant {
efficiency = Math.max(0, efficiency); //scalar product is negative if sun points to back of module. set 0 in this case efficiency = Math.max(0, efficiency); //scalar product is negative if sun points to back of module. set 0 in this case
if (efficiency > 0) { if (efficiency > 0) {
//Calculate shading in 10 degree ranges, total 36 ranges double shFactor=0;
int shadingIndex = ((((int) Math.round((solarAzimuth + 5) / 10)) - 1) % 36 + 36) % 36; Instant sh;
if (shadingElevation[shadingIndex] > solarElevation) { ZonedDateTime shDateTime;
efficiency *= (double) (100 - shadingOpacity[shadingIndex])/100; AzimuthZenithAngle shPosition;
double shSolarAzimuth;
double shSolarElevation;
//Calculate shading in 6 intervals per hour -> 10min steps xx:05 / xx:15 / xx:25 / xx:35 / xx:45 / xx:55
int numSteps = 6;
long interval = 3600 / numSteps;
for (int j=0; j<numSteps;j++){
sh = Instant.ofEpochSecond(epochTimeSeconds-(numSteps-1)*interval/2+j*interval);
shDateTime = ZonedDateTime.ofInstant(sh, ZoneId.of("GMT")); //currentTimeMillis is in GMT
shPosition = Grena3.calculateSolarPosition(
shDateTime,
latitude,
longitude,
DeltaT.estimate(shDateTime.toLocalDate())); // delta T (s)
shSolarAzimuth = shPosition.getAzimuth();
shSolarElevation = 90 - shPosition.getZenithAngle();
//shading values are provided in 10 degree ranges -> total of 36 ranges
int shadingIndex = ((((int) Math.round((shSolarAzimuth + 5) / 10)) - 1) % 36 + 36) % 36;
if (shadingElevation[shadingIndex] > shSolarElevation) {
shFactor += (double) (100 - shadingOpacity[shadingIndex])/(numSteps*100);
} else {
shFactor += (double) 100/(numSteps*100); //numSteps iterations with no shading give 100%
}
} }
efficiency *= shFactor;
} }
} }
double dcPower = (solarPowerNormal * efficiency + solarPowerDiffuse * diffuseEfficiency )* cellsEfficiency * cellsArea;
double acPower = Math.min(dcPower * inverterEfficiency, inverterPowerLimit); double totalRadiationOnCell = solarPowerNormal * efficiency + solarPowerDiffuse * diffuseEfficiency + shortwaveRadiation * (0.5-0.5*Math.cos(tiltAngle/180*Math.PI)) * albedo; //flat plate equivalent of the solar irradiance
double cellTemperature = calcCellTemperature(ambientTemperature,totalRadiationOnCell);
double dcPower;
if (cellsEfficiency!=0 && cellsArea!=0){
dcPower = totalRadiationOnCell * (1+(cellTemperature - 25)*cellsTempCoeff) * cellsEfficiency * cellsArea;
} else { //assume cellMaxPower is defined at 1000W/sqm
dcPower = totalRadiationOnCell/1000 * (1+(cellTemperature - 25)*cellsTempCoeff) * cellsMaxPower;
}
double acPower;
if (!isCentralInverter)
acPower = Math.min(dcPower * inverterEfficiency, inverterPowerLimit);
else
acPower = dcPower * inverterEfficiency; //do limiting on system level
return (float) acPower; return (float) acPower;
} }
public static int calcDiffuseEfficiency(float tilt){
return (int) ( 50 + 50* Math.cos(tilt/180*Math.PI));
}
public static double calcCellTemperature(double ambientTemperature, double totalIrradiance){
//models from here: https://www.scielo.br/j/babt/a/FBq5Pmm4gSFqsfh3V8MxfGN/ Photovoltaic Cell Temperature Estimation for a Grid-Connect Photovoltaic Systems in Curitiba
//float cellTemperature = 30.006f + 0.0175f*(totalIrradiance-300f)+1.14f*(ambientTemperature-25f); //Lasnier and Ang Lasnier, F.; Ang, T. G. Photovoltaic engineering handbook, 1st ed.; IOP Publishing LTD: Lasnier, France, 1990; pp. 258.
//float cellTemperature = ambientTemperature + 0.028f*totalIrradiance-1f; //Schott Schott, T. Operation temperatures of PV modules. Photovoltaic solar energy conference 1985, pp. 392-396.
double cellTemperature = ambientTemperature + 0.0342f*totalIrradiance; //Ross model: https://www.researchgate.net/publication/275438802_Thermal_effects_of_the_extended_holographic_regions_for_holographic_planar_concentrator
//assuming "not so well cooled" : 0.0342
return cellTemperature;
}
} }

View file

@ -1,7 +1,9 @@
package org.woheller69.weather.activities; package org.woheller69.weather.activities;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.text.method.LinkMovementMethod; import android.text.method.LinkMovementMethod;
import android.view.WindowInsetsController;
import android.widget.TextView; import android.widget.TextView;
import org.woheller69.weather.BuildConfig; import org.woheller69.weather.BuildConfig;
@ -15,7 +17,12 @@ public class AboutActivity extends NavigationActivity {
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_about); setContentView(R.layout.activity_about);
overridePendingTransition(0, 0); if (Build.VERSION.SDK_INT > Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
getWindow().getInsetsController().setSystemBarsAppearance(
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS,
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
);
}
((TextView) findViewById(R.id.openmeteoURL)).setMovementMethod(LinkMovementMethod.getInstance()); ((TextView) findViewById(R.id.openmeteoURL)).setMovementMethod(LinkMovementMethod.getInstance());
((TextView) findViewById(R.id.githubURL)).setMovementMethod(LinkMovementMethod.getInstance()); ((TextView) findViewById(R.id.githubURL)).setMovementMethod(LinkMovementMethod.getInstance());

View file

@ -0,0 +1,122 @@
package org.woheller69.weather.activities;
import static android.os.Environment.DIRECTORY_DOCUMENTS;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.view.Gravity;
import android.view.View;
import android.view.WindowInsetsController;
import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import org.woheller69.weather.Backup;
import org.woheller69.weather.R;
import java.io.File;
import java.util.Objects;
public class BackupRestoreActivity extends NavigationActivity{
ActivityResultLauncher<Intent> mRestore;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_backuprestore);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
getWindow().getInsetsController().setSystemBarsAppearance(
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS,
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
);
}
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
mRestore = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
File intData = new File(Environment.getDataDirectory() + "//data//" + this.getPackageName());
if (result.getData()!=null && result.getData().getData()!=null) Backup.zipExtract(this, intData, result.getData().getData());
});
}
@Override
protected int getNavigationDrawerID() {
return R.id.nav_backuprestore;
}
public void performBackup(View view) {
File extStorage;
File intData;
intData = new File(Environment.getDataDirectory()+"//data//" + this.getPackageName() + "//databases//");
extStorage = Environment.getExternalStoragePublicDirectory(DIRECTORY_DOCUMENTS);
if (!extStorage.exists()) extStorage.mkdir();
String filesBackup = getResources().getString(R.string.app_name)+".zip";
final File dbBackup = new File(extStorage, filesBackup);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(getResources().getString(R.string.backup_database) +" -> " + dbBackup.toString());
builder.setPositiveButton(R.string.dialog_OK_button, (dialog, whichButton) -> {
if (!Backup.checkPermissionStorage(this)) {
Backup.requestPermission(this);
} else {
if (dbBackup.exists()){
if (!dbBackup.delete()){
Toast.makeText(this,getResources().getString(R.string.toast_delete), Toast.LENGTH_LONG).show();
}
}
try {
new ZipFile(dbBackup).addFolder(intData);
} catch (ZipException e) {
Toast.makeText(this,e.toString(), Toast.LENGTH_LONG).show();
}
}
});
builder.setNegativeButton(R.string.dialog_NO_button, (dialog, whichButton) -> dialog.cancel());
AlertDialog dialog = builder.create();
dialog.show();
Objects.requireNonNull(dialog.getWindow()).setGravity(Gravity.BOTTOM);
}
public void performRestore(View view) {
File extStorage;
File intData;
intData = new File(Environment.getDataDirectory() + "//data//" + this.getPackageName());
extStorage = Environment.getExternalStoragePublicDirectory(DIRECTORY_DOCUMENTS);
String filesBackup = getResources().getString(R.string.app_name)+".zip";
final File zipFileBackup = new File(extStorage, filesBackup);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(getResources().getString(R.string.restore_database_message));
builder.setPositiveButton(R.string.dialog_OK_button, (dialog, whichButton) -> {
if (!Backup.checkPermissionStorage(this)) {
Backup.requestPermission(this);
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.setType("application/zip");
mRestore.launch(intent);
} else {
Backup.zipExtract(this, intData, Uri.fromFile(zipFileBackup));
}
}
});
builder.setNegativeButton(R.string.dialog_NO_button, (dialog, whichButton) -> dialog.cancel());
AlertDialog dialog = builder.create();
dialog.show();
Objects.requireNonNull(dialog.getWindow()).setGravity(Gravity.BOTTOM);
}
}

View file

@ -3,6 +3,7 @@ package org.woheller69.weather.activities;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
@ -16,6 +17,7 @@ import androidx.viewpager2.widget.ViewPager2;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.WindowInsetsController;
import android.view.animation.Animation; import android.view.animation.Animation;
import android.view.animation.LinearInterpolator; import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation; import android.view.animation.RotateAnimation;
@ -48,6 +50,7 @@ public class ForecastCityActivity extends NavigationActivity implements IUpdatea
private ViewPager2 viewPager2; private ViewPager2 viewPager2;
private TabLayout tabLayout; private TabLayout tabLayout;
private TextView noCityText; private TextView noCityText;
private static Boolean isRefreshing = false;
Context context; Context context;
@Override @Override
@ -83,18 +86,6 @@ public class ForecastCityActivity extends NavigationActivity implements IUpdatea
if (pagerAdapter.getItemCount()>0) { //only if at least one city is watched if (pagerAdapter.getItemCount()>0) { //only if at least one city is watched
//if pagerAdapter has item with current cityId go there, otherwise use cityId from current item //if pagerAdapter has item with current cityId go there, otherwise use cityId from current item
if (pagerAdapter.getPosForCityID(cityId)==-1) cityId=pagerAdapter.getCityIDForPos(viewPager2.getCurrentItem()); if (pagerAdapter.getPosForCityID(cityId)==-1) cityId=pagerAdapter.getCityIDForPos(viewPager2.getCurrentItem());
GeneralData generalData = db.getGeneralDataByCityId(cityId);
long timestamp = generalData.getTimestamp();
long systemTime = System.currentTimeMillis() / 1000;
SharedPreferences prefManager = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
long updateInterval = (long) (Float.parseFloat(prefManager.getString("pref_updateInterval", "2")) * 60 * 60);
if (timestamp + updateInterval - systemTime <= 0) {
WeatherPagerAdapter.refreshSingleData(getApplicationContext(), true, cityId); //only update current tab at start
ForecastCityActivity.startRefreshAnimation();
}
if (viewPager2.getCurrentItem()!=pagerAdapter.getPosForCityID(cityId)) viewPager2.setCurrentItem(pagerAdapter.getPosForCityID(cityId),false); if (viewPager2.getCurrentItem()!=pagerAdapter.getPosForCityID(cityId)) viewPager2.setCurrentItem(pagerAdapter.getPosForCityID(cityId),false);
} }
} }
@ -104,7 +95,12 @@ public class ForecastCityActivity extends NavigationActivity implements IUpdatea
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
context=this; context=this;
setContentView(R.layout.activity_forecast_city); setContentView(R.layout.activity_forecast_city);
overridePendingTransition(0, 0); if (Build.VERSION.SDK_INT > Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
getWindow().getInsetsController().setSystemBarsAppearance(
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS,
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
);
}
initResources(); initResources();
@ -148,10 +144,16 @@ public class ForecastCityActivity extends NavigationActivity implements IUpdatea
} }
private void initResources() { private void initResources() {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
viewPager2 = findViewById(R.id.viewPager2); viewPager2 = findViewById(R.id.viewPager2);
reduceViewpager2DragSensitivity(viewPager2,2); viewPager2.setUserInputEnabled(false);
//reduceViewpager2DragSensitivity(viewPager2,2);
tabLayout = findViewById(R.id.tab_layout); tabLayout = findViewById(R.id.tab_layout);
pagerAdapter = new WeatherPagerAdapter(this, getSupportFragmentManager(),getLifecycle()); if (sharedPreferences.getBoolean("pref_summarize",false)){
pagerAdapter = new WeatherPagerAdapter(this, getSupportFragmentManager(),getLifecycle(),true);
} else {
pagerAdapter = new WeatherPagerAdapter(this, getSupportFragmentManager(),getLifecycle(),false);
}
noCityText = findViewById(R.id.noCitySelectedText); noCityText = findViewById(R.id.noCitySelectedText);
} }
@ -170,6 +172,7 @@ public class ForecastCityActivity extends NavigationActivity implements IUpdatea
refreshActionButton = menu.findItem(R.id.menu_refresh); refreshActionButton = menu.findItem(R.id.menu_refresh);
refreshActionButton.setActionView(R.layout.menu_refresh_action_view); refreshActionButton.setActionView(R.layout.menu_refresh_action_view);
refreshActionButton.getActionView().setOnClickListener(v -> m.performIdentifierAction(refreshActionButton.getItemId(), 0)); refreshActionButton.getActionView().setOnClickListener(v -> m.performIdentifierAction(refreshActionButton.getItemId(), 0));
if (isRefreshing) startRefreshAnimation();
return true; return true;
} }
@ -220,28 +223,24 @@ public class ForecastCityActivity extends NavigationActivity implements IUpdatea
@Override @Override
public void processNewGeneralData(GeneralData data) { public void processNewGeneralData(GeneralData data) {
if (refreshActionButton != null && refreshActionButton.getActionView() != null) { stopRefreshAnimation();
refreshActionButton.getActionView().clearAnimation();
}
} }
@Override @Override
public void processNewWeekForecasts(List<WeekForecast> forecasts) { public void processNewForecasts(List<HourlyForecast> hourlyForecasts, List<WeekForecast> weekForecasts) {
if (refreshActionButton != null && refreshActionButton.getActionView() != null) { stopRefreshAnimation();
refreshActionButton.getActionView().clearAnimation();
}
} }
@Override public static void stopRefreshAnimation(){
public void processNewForecasts(List<HourlyForecast> hourlyForecasts) {
if (refreshActionButton != null && refreshActionButton.getActionView() != null) { if (refreshActionButton != null && refreshActionButton.getActionView() != null) {
refreshActionButton.getActionView().clearAnimation(); refreshActionButton.getActionView().clearAnimation();
} }
isRefreshing = false;
} }
public static void startRefreshAnimation(){ public static void startRefreshAnimation(){
{ isRefreshing = true;
if(refreshActionButton !=null && refreshActionButton.getActionView() != null) { if(refreshActionButton !=null && refreshActionButton.getActionView() != null) {
RotateAnimation rotate = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); RotateAnimation rotate = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rotate.setDuration(500); rotate.setDuration(500);
rotate.setRepeatCount(5); rotate.setRepeatCount(5);
@ -259,6 +258,7 @@ public class ForecastCityActivity extends NavigationActivity implements IUpdatea
refreshActionButton.getActionView().setActivated(true); refreshActionButton.getActionView().setActivated(true);
refreshActionButton.getActionView().setEnabled(true); refreshActionButton.getActionView().setEnabled(true);
refreshActionButton.getActionView().setClickable(true); refreshActionButton.getActionView().setClickable(true);
isRefreshing = false;
} }
@Override @Override
@ -266,8 +266,7 @@ public class ForecastCityActivity extends NavigationActivity implements IUpdatea
} }
}); });
refreshActionButton.getActionView().startAnimation(rotate); refreshActionButton.getActionView().startAnimation(rotate);
} }
}
} }
//https://devdreamz.com/question/348298-how-to-modify-sensitivity-of-viewpager //https://devdreamz.com/question/348298-how-to-modify-sensitivity-of-viewpager

View file

@ -1,11 +1,15 @@
package org.woheller69.weather.activities; package org.woheller69.weather.activities;
import android.content.res.AssetManager; import android.content.res.AssetManager;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.view.WindowInsetsController;
import android.webkit.WebView; import android.webkit.WebView;
import org.woheller69.weather.R; import org.woheller69.weather.R;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.webkit.WebSettingsCompat;
import androidx.webkit.WebViewFeature;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
@ -17,8 +21,21 @@ public class HelpActivity extends NavigationActivity{
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_help); setContentView(R.layout.activity_help);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
getWindow().getInsetsController().setSystemBarsAppearance(
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS,
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
);
}
WebView view = findViewById(R.id.help); WebView view = findViewById(R.id.help);
if(WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
WebSettingsCompat.setAlgorithmicDarkeningAllowed(view.getSettings(), true);
}
}
String language = getResources().getConfiguration().getLocales().get(0).getLanguage(); String language = getResources().getConfiguration().getLocales().get(0).getLanguage();
String filename = "help-"+language+".html"; String filename = "help-"+language+".html";

View file

@ -1,8 +1,10 @@
package org.woheller69.weather.activities; package org.woheller69.weather.activities;
import android.content.Context; import android.content.Context;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.redinput.compassview.CompassView;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
@ -16,22 +18,33 @@ import android.text.TextWatcher;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.WindowInsetsController;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.CheckBox;
import android.widget.EditText; import android.widget.EditText;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import net.e175.klaus.solarpositioning.AzimuthZenithAngle;
import net.e175.klaus.solarpositioning.DeltaT;
import net.e175.klaus.solarpositioning.Grena3;
import org.woheller69.weather.R; import org.woheller69.weather.R;
import org.woheller69.weather.SolarPowerPlant;
import org.woheller69.weather.database.City; import org.woheller69.weather.database.City;
import org.woheller69.weather.database.CityToWatch; import org.woheller69.weather.database.CityToWatch;
import org.woheller69.weather.database.SQLiteHelper; import org.woheller69.weather.database.SQLiteHelper;
import org.woheller69.weather.dialogs.AddLocationDialogOmGeocodingAPI; import org.woheller69.weather.dialogs.AddLocationDialogOmGeocodingAPI;
import org.woheller69.weather.ui.Help.InputFilterMinMax; import org.woheller69.weather.ui.Help.InputFilterMinMax;
import org.woheller69.weather.ui.Help.StringFormatUtils;
import org.woheller69.weather.ui.RecycleList.RecyclerItemClickListener; import org.woheller69.weather.ui.RecycleList.RecyclerItemClickListener;
import org.woheller69.weather.ui.RecycleList.RecyclerOverviewListAdapter; import org.woheller69.weather.ui.RecycleList.RecyclerOverviewListAdapter;
import org.woheller69.weather.ui.RecycleList.SimpleItemTouchHelperCallback; import org.woheller69.weather.ui.RecycleList.SimpleItemTouchHelperCallback;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
@ -51,7 +64,13 @@ public class ManageLocationsActivity extends NavigationActivity {
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_manage_locations); setContentView(R.layout.activity_manage_locations);
overridePendingTransition(0, 0); if (Build.VERSION.SDK_INT > Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
getWindow().getInsetsController().setSystemBarsAppearance(
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS,
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
);
}
context=this; context=this;
database = SQLiteHelper.getInstance(getApplicationContext()); database = SQLiteHelper.getInstance(getApplicationContext());
@ -183,31 +202,67 @@ public class ManageLocationsActivity extends NavigationActivity {
EditText editLongitude = (EditText) dialogView.findViewById(R.id.EditLocation_Lon); EditText editLongitude = (EditText) dialogView.findViewById(R.id.EditLocation_Lon);
EditText editCity = (EditText) dialogView.findViewById(R.id.EditLocation_Name); EditText editCity = (EditText) dialogView.findViewById(R.id.EditLocation_Name);
EditText editAzimuth = (EditText) dialogView.findViewById(R.id.EditLocation_Azimuth); EditText editAzimuth = (EditText) dialogView.findViewById(R.id.EditLocation_Azimuth);
CompassView editCompass = (CompassView) dialogView.findViewById(R.id.EditLocation_Compass);
EditText editTilt = (EditText) dialogView.findViewById(R.id.EditLocation_Tilt); EditText editTilt = (EditText) dialogView.findViewById(R.id.EditLocation_Tilt);
EditText editCellsMaxPower = (EditText) dialogView.findViewById(R.id.EditLocation_Cell_Max_Power); EditText editCellsMaxPower = (EditText) dialogView.findViewById(R.id.EditLocation_Cell_Max_Power);
EditText editCellsArea = (EditText) dialogView.findViewById(R.id.EditLocation_Cells_Area); EditText editCellsArea = (EditText) dialogView.findViewById(R.id.EditLocation_Cells_Area);
EditText editCellsEfficiency = (EditText) dialogView.findViewById(R.id.EditLocation_Cell_Efficiency); EditText editCellsEfficiency = (EditText) dialogView.findViewById(R.id.EditLocation_Cell_Efficiency);
EditText editCellsTempCoeff = (EditText) dialogView.findViewById(R.id.EditLocation_Cell_Temp_Coeff);
EditText editDiffuseEfficiency = (EditText) dialogView.findViewById(R.id.EditLocation_Diffuse_Efficiency); EditText editDiffuseEfficiency = (EditText) dialogView.findViewById(R.id.EditLocation_Diffuse_Efficiency);
EditText editAlbedo = (EditText) dialogView.findViewById(R.id.EditLocation_Albedo);
EditText editInverterPowerLimit = (EditText) dialogView.findViewById(R.id.EditLocation_Inverter_Power_Limit); EditText editInverterPowerLimit = (EditText) dialogView.findViewById(R.id.EditLocation_Inverter_Power_Limit);
EditText editInverterEfficiency = (EditText) dialogView.findViewById(R.id.EditLocation_Inverter_Efficiency); EditText editInverterEfficiency = (EditText) dialogView.findViewById(R.id.EditLocation_Inverter_Efficiency);
CheckBox editIsCentralInverter = (CheckBox) dialogView.findViewById(R.id.EditLocation_Central_Inverter);
TextView currentAzimuth = (TextView) dialogView.findViewById(R.id.edit_current_azi_ele);
Long time = System.currentTimeMillis()/1000;
ZonedDateTime dateTime = ZonedDateTime.ofInstant(Instant.ofEpochSecond(time), ZoneId.of("GMT"));
AzimuthZenithAngle position = Grena3.calculateSolarPosition(
dateTime,
city.getLatitude(),
city.getLongitude(),
DeltaT.estimate(dateTime.toLocalDate()));
String solarAzimuth = StringFormatUtils.formatDecimal((float) position.getAzimuth(),"");
String solarElevation = StringFormatUtils.formatDecimal((float) (90 - position.getZenithAngle()),"");
currentAzimuth.setText(getString(R.string.edit_location_hint_azimuth)+": "+solarAzimuth + " " + getString(R.string.action_sun_elevation) + ": " + solarElevation);
editCity.setText(city.getCityName()); editCity.setText(city.getCityName());
editLatitude.setText(Float.toString(city.getLatitude())); editLatitude.setText(Float.toString(city.getLatitude()));
editLatitude.setFilters(new InputFilter[]{ new InputFilterMinMax(-90, 90)}); editLatitude.setFilters(new InputFilter[]{ new InputFilterMinMax(-90, 90)});
editLongitude.setText(Float.toString(city.getLongitude())); editLongitude.setText(Float.toString(city.getLongitude()));
editLongitude.setFilters(new InputFilter[]{ new InputFilterMinMax(-180, 180)}); editLongitude.setFilters(new InputFilter[]{ new InputFilterMinMax(-180, 180)});
editCompass.setDegrees(city.getAzimuthAngle());
editCompass.setOnCompassDragListener(degrees -> { editAzimuth.setText(Float.toString(Math.round(degrees))); });
editAzimuth.setText(Float.toString(city.getAzimuthAngle())); editAzimuth.setText(Float.toString(city.getAzimuthAngle()));
editAzimuth.setFilters(new InputFilter[]{ new InputFilterMinMax(0, 360)}); editAzimuth.setFilters(new InputFilter[]{ new InputFilterMinMax(0, 360)});
editAzimuth.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { }
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { }
@Override
public void afterTextChanged(Editable editable) {
float azimuth = Float.parseFloat(!editAzimuth.getText().toString().isEmpty() ? editAzimuth.getText().toString() : "0");
editCompass.setDegrees(azimuth);
}
});
editTilt.setText(Float.toString(city.getTiltAngle())); editTilt.setText(Float.toString(city.getTiltAngle()));
editTilt.setFilters(new InputFilter[]{ new InputFilterMinMax(0, 90)}); editTilt.setFilters(new InputFilter[]{ new InputFilterMinMax(0, 90)});
editCellsMaxPower.setText(Float.toString(city.getCellsMaxPower())); editCellsMaxPower.setText(Float.toString(city.getCellsMaxPower()));
editCellsArea.setText(Float.toString(city.getCellsArea())); editCellsArea.setText(Float.toString(city.getCellsArea()));
editCellsEfficiency.setText(Float.toString(city.getCellsEfficiency())); editCellsEfficiency.setText(Float.toString(city.getCellsEfficiency()));
editCellsEfficiency.setFilters(new InputFilter[]{ new InputFilterMinMax(0, 100)}); editCellsEfficiency.setFilters(new InputFilter[]{ new InputFilterMinMax(0, 100)});
editCellsTempCoeff.setText(Float.toString(city.getCellsTempCoeff()));
editCellsTempCoeff.setFilters(new InputFilter[]{ new InputFilterMinMax(-100, 100)});
editDiffuseEfficiency.setText(Float.toString(city.getDiffuseEfficiency())); editDiffuseEfficiency.setText(Float.toString(city.getDiffuseEfficiency()));
editDiffuseEfficiency.setFilters(new InputFilter[]{ new InputFilterMinMax(0, 100)}); editDiffuseEfficiency.setFilters(new InputFilter[]{ new InputFilterMinMax(0, 100)});
editAlbedo.setText(Float.toString(city.getAlbedo()));
editAlbedo.setFilters(new InputFilter[]{ new InputFilterMinMax(0,1)});
editInverterPowerLimit.setText(Float.toString(city.getInverterPowerLimit())); editInverterPowerLimit.setText(Float.toString(city.getInverterPowerLimit()));
editInverterEfficiency.setText(Float.toString(city.getInverterEfficiency())); editInverterEfficiency.setText(Float.toString(city.getInverterEfficiency()));
editIsCentralInverter.setChecked(city.isCentralInverter());
editInverterEfficiency.setFilters(new InputFilter[]{ new InputFilterMinMax(0, 100)}); editInverterEfficiency.setFilters(new InputFilter[]{ new InputFilterMinMax(0, 100)});
editTilt.addTextChangedListener(new TextWatcher() { editTilt.addTextChangedListener(new TextWatcher() {
@Override @Override
@ -218,7 +273,7 @@ public class ManageLocationsActivity extends NavigationActivity {
@Override @Override
public void afterTextChanged(Editable editable) { public void afterTextChanged(Editable editable) {
float tilt = Float.parseFloat(!editTilt.getText().toString().isEmpty() ? editTilt.getText().toString() : "0"); float tilt = Float.parseFloat(!editTilt.getText().toString().isEmpty() ? editTilt.getText().toString() : "0");
int diffuseEfficiency = (int) (100-50 * tilt/90); int diffuseEfficiency = SolarPowerPlant.calcDiffuseEfficiency(tilt);
editDiffuseEfficiency.setText(Float.toString((float) diffuseEfficiency)); editDiffuseEfficiency.setText(Float.toString((float) diffuseEfficiency));
} }
}); });
@ -237,14 +292,53 @@ public class ManageLocationsActivity extends NavigationActivity {
Float.parseFloat(editCellsMaxPower.getText().toString().isEmpty() ? "0" : editCellsMaxPower.getText().toString()), Float.parseFloat(editCellsMaxPower.getText().toString().isEmpty() ? "0" : editCellsMaxPower.getText().toString()),
Float.parseFloat(editCellsArea.getText().toString().isEmpty() ? "0" : editCellsArea.getText().toString()), Float.parseFloat(editCellsArea.getText().toString().isEmpty() ? "0" : editCellsArea.getText().toString()),
Float.parseFloat(editCellsEfficiency.getText().toString().isEmpty() ? "0" : editCellsEfficiency.getText().toString()), Float.parseFloat(editCellsEfficiency.getText().toString().isEmpty() ? "0" : editCellsEfficiency.getText().toString()),
Float.parseFloat(editCellsTempCoeff.getText().toString().isEmpty() ? "0" : editCellsTempCoeff.getText().toString()),
Float.parseFloat(editDiffuseEfficiency.getText().toString().isEmpty() ? "0" : editDiffuseEfficiency.getText().toString()), Float.parseFloat(editDiffuseEfficiency.getText().toString().isEmpty() ? "0" : editDiffuseEfficiency.getText().toString()),
Float.parseFloat(editAlbedo.getText().toString().isEmpty() ? "0" : editAlbedo.getText().toString()),
Float.parseFloat(editInverterPowerLimit.getText().toString().isEmpty() ? "0" : editInverterPowerLimit.getText().toString()), Float.parseFloat(editInverterPowerLimit.getText().toString().isEmpty() ? "0" : editInverterPowerLimit.getText().toString()),
Float.parseFloat(editInverterEfficiency.getText().toString().isEmpty() ? "0" : editInverterEfficiency.getText().toString()), Float.parseFloat(editInverterEfficiency.getText().toString().isEmpty() ? "0" : editInverterEfficiency.getText().toString()),
editIsCentralInverter.isChecked(),
shadingElevation, shadingElevation,
shadingOpacity shadingOpacity
); );
}); });
alert.setNegativeButton(getString(R.string.dialog_add_close_button), (dialog, whichButton) -> { alert.setNegativeButton(getString(android.R.string.cancel), (dialog, whichButton) -> {
});
alert.setNeutralButton(getString(R.string.dialog_add_clone_button), (dialog, whichButton) -> {
CityToWatch cloneCity = new CityToWatch(
database.getMaxRank() + 1,
-1,
0, city.getLongitude(),city.getLatitude(),
city.getCityName()
);
long id=database.addCityToWatch(cloneCity);
cloneCity.setId((int) id);
cloneCity.setCityId((int) id); //use id also instead of city id as unique identifier
cities.add(cloneCity);
adapter.notifyDataSetChanged();
for (int i = 0; i < shadingElevation.length ; i++) {
shadingElevation[i]= Integer.parseInt(elevationViews[i].getText().toString().isEmpty() ? "0" : elevationViews[i].getText().toString());
shadingOpacity[i]= Integer.parseInt(opacityViews[i].getText().toString().isEmpty() ? "0" : opacityViews[i].getText().toString());
}
adapter.updateCity(cloneCity, String.valueOf(editCity.getText()),
Float.parseFloat(editLatitude.getText().toString().isEmpty() ? "0" : editLatitude.getText().toString()),
Float.parseFloat(editLongitude.getText().toString().isEmpty() ? "0" : editLongitude.getText().toString()),
Float.parseFloat(editAzimuth.getText().toString().isEmpty() ? "0" : editAzimuth.getText().toString()),
Float.parseFloat(editTilt.getText().toString().isEmpty() ? "0" : editTilt.getText().toString()),
Float.parseFloat(editCellsMaxPower.getText().toString().isEmpty() ? "0" : editCellsMaxPower.getText().toString()),
Float.parseFloat(editCellsArea.getText().toString().isEmpty() ? "0" : editCellsArea.getText().toString()),
Float.parseFloat(editCellsEfficiency.getText().toString().isEmpty() ? "0" : editCellsEfficiency.getText().toString()),
Float.parseFloat(editCellsTempCoeff.getText().toString().isEmpty() ? "0" : editCellsTempCoeff.getText().toString()),
Float.parseFloat(editDiffuseEfficiency.getText().toString().isEmpty() ? "0" : editDiffuseEfficiency.getText().toString()),
Float.parseFloat(editAlbedo.getText().toString().isEmpty() ? "0" : editAlbedo.getText().toString()),
Float.parseFloat(editInverterPowerLimit.getText().toString().isEmpty() ? "0" : editInverterPowerLimit.getText().toString()),
Float.parseFloat(editInverterEfficiency.getText().toString().isEmpty() ? "0" : editInverterEfficiency.getText().toString()),
editIsCentralInverter.isChecked(),
shadingElevation,
shadingOpacity
);
}); });
alert.show(); alert.show();
@ -279,7 +373,7 @@ public class ManageLocationsActivity extends NavigationActivity {
return new CityToWatch( return new CityToWatch(
database.getMaxRank() + 1, database.getMaxRank() + 1,
-1, -1,
selectedCity.getCityId(), selectedCity.getLongitude(),selectedCity.getLatitude(), 0, selectedCity.getLongitude(),selectedCity.getLatitude(),
selectedCity.getCityName() selectedCity.getCityName()
); );
} }

View file

@ -3,6 +3,7 @@ package org.woheller69.weather.activities;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
@ -25,6 +26,7 @@ import android.view.MenuItem;
import org.woheller69.weather.BuildConfig; import org.woheller69.weather.BuildConfig;
import org.woheller69.weather.R; import org.woheller69.weather.R;
import org.woheller69.weather.database.SQLiteHelper;
import org.woheller69.weather.preferences.AppPreferencesManager; import org.woheller69.weather.preferences.AppPreferencesManager;
import static java.lang.Boolean.TRUE; import static java.lang.Boolean.TRUE;
@ -79,7 +81,6 @@ public class NavigationActivity extends AppCompatActivity implements OnNavigatio
} }
overridePendingTransition(0, 0);
} }
@Override @Override
@ -169,6 +170,12 @@ public class NavigationActivity extends AppCompatActivity implements OnNavigatio
}else if(itemId==R.id.nav_help) { }else if(itemId==R.id.nav_help) {
intent = new Intent(this, HelpActivity.class); intent = new Intent(this, HelpActivity.class);
startActivity(intent); startActivity(intent);
}else if(itemId==R.id.nav_backuprestore) {
SQLiteHelper dbhelper = SQLiteHelper.getInstance(this); //create a database if it does not yet exist
SQLiteDatabase database = dbhelper.getWritableDatabase();
database.close();
intent = new Intent(this, BackupRestoreActivity.class);
startActivity(intent);
}else if (itemId==R.id.star_on_github){ }else if (itemId==R.id.star_on_github){
startActivity(new Intent(Intent.ACTION_VIEW, startActivity(new Intent(Intent.ACTION_VIEW,
Uri.parse(BuildConfig.GITHUB_URL))); Uri.parse(BuildConfig.GITHUB_URL)));

View file

@ -4,6 +4,7 @@ package org.woheller69.weather.activities;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.view.WindowInsetsController;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
@ -12,6 +13,7 @@ import androidx.preference.PreferenceManager;
import androidx.preference.SeekBarPreference; import androidx.preference.SeekBarPreference;
import org.woheller69.weather.R; import org.woheller69.weather.R;
import org.woheller69.weather.database.SQLiteHelper;
public class SettingsActivity extends NavigationActivity implements SharedPreferences.OnSharedPreferenceChangeListener{ public class SettingsActivity extends NavigationActivity implements SharedPreferences.OnSharedPreferenceChangeListener{
@ -39,8 +41,12 @@ public class SettingsActivity extends NavigationActivity implements SharedPrefer
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings); setContentView(R.layout.activity_settings);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
overridePendingTransition(0, 0); getWindow().getInsetsController().setSystemBarsAppearance(
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS,
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
);
}
} }
@Override @Override
@ -80,6 +86,11 @@ public class SettingsActivity extends NavigationActivity implements SharedPrefer
if (key.equals("pref_number_days")){ if (key.equals("pref_number_days")){
SeekBarPreference numberDays = findPreference("pref_number_days"); SeekBarPreference numberDays = findPreference("pref_number_days");
if (numberDays.getValue()<3) numberDays.setValue(3); if (numberDays.getValue()<3) numberDays.setValue(3);
} else if (key.equals("pref_summarize")){
if (sharedPreferences.getBoolean("pref_summarize",false)) {
SQLiteHelper database = SQLiteHelper.getInstance(getActivity());
database.deleteAllForecasts();
}
} }
} }
} }

View file

@ -17,11 +17,14 @@ public class CityToWatch {
private float cellsMaxPower; private float cellsMaxPower;
private float cellsArea; private float cellsArea;
private float cellsEfficiency; private float cellsEfficiency;
private float cellsTempCoeff;
private float diffuseEfficiency; private float diffuseEfficiency;
private float inverterPowerLimit; private float inverterPowerLimit;
private float inverterEfficiency; private float inverterEfficiency;
private float azimuthAngle; private float azimuthAngle;
private float tiltAngle; private float tiltAngle;
private float albedo;
private boolean isCentralInverter;
private int rank; private int rank;
private int[] shadingElevation = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; private int[] shadingElevation = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
private int[] shadingOpacity = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; private int[] shadingOpacity = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
@ -36,14 +39,16 @@ public class CityToWatch {
this.id = id; this.id = id;
this.cityId = cityId; this.cityId = cityId;
this.cityName = cityName; this.cityName = cityName;
this.cellsMaxPower=650; this.cellsMaxPower = 650.0f;
this.cellsArea=3.18f; this.cellsArea = 3.18f;
this.cellsEfficiency=19.3f; this.cellsEfficiency = 19.3f;
this.diffuseEfficiency=40; this.cellsTempCoeff = -0.4f;
this.inverterPowerLimit =600; this.diffuseEfficiency = 40.0f;
this.inverterEfficiency =95; this.inverterPowerLimit = 600.0f;
this.azimuthAngle=170; this.inverterEfficiency = 95.0f;
this.tiltAngle =90; this.azimuthAngle = 170.0f;
this.tiltAngle = 90.0f;
this.albedo = 0f;
} }
@ -143,6 +148,11 @@ public class CityToWatch {
this.inverterPowerLimit = inverterPowerLimit; this.inverterPowerLimit = inverterPowerLimit;
} }
public void setIsCentralInverter(boolean isCentralInverter) {
this.isCentralInverter = isCentralInverter;
}
public boolean isCentralInverter() {return isCentralInverter;}
public void setDiffuseEfficiency(float diffuseEfficiency) { public void setDiffuseEfficiency(float diffuseEfficiency) {
this.diffuseEfficiency = diffuseEfficiency; this.diffuseEfficiency = diffuseEfficiency;
} }
@ -182,4 +192,16 @@ public class CityToWatch {
public String getShadingOpacityString() { public String getShadingOpacityString() {
return Arrays.toString(shadingOpacity).replaceAll("\\[|\\]|\\s", ""); return Arrays.toString(shadingOpacity).replaceAll("\\[|\\]|\\s", "");
} }
public float getCellsTempCoeff() {
return cellsTempCoeff;
}
public void setCellsTempCoeff(float cellsTempCoeff) {
this.cellsTempCoeff = cellsTempCoeff;
}
public float getAlbedo() { return this.albedo; }
public void setAlbedo (float albedo) { this.albedo = albedo; }
} }

View file

@ -15,7 +15,9 @@ public class HourlyForecast {
private int weatherID; private int weatherID;
private float directRadiationNormal; private float directRadiationNormal;
private float diffuseRadiation; private float diffuseRadiation;
private float shortwaveRadiation;
private float power; private float power;
private float energyCum;
private String city_name; private String city_name;
@ -104,4 +106,12 @@ public class HourlyForecast {
public void setDiffuseRadiation(float diffuseRadiation) { this.diffuseRadiation = diffuseRadiation; } public void setDiffuseRadiation(float diffuseRadiation) { this.diffuseRadiation = diffuseRadiation; }
public void setPower(float power) { this.power = power; } public void setPower(float power) { this.power = power; }
public void setShortwaveRadiation(float shortwaveRadiation) { this.shortwaveRadiation = shortwaveRadiation; }
public float getShortwaveRadiation() { return this.shortwaveRadiation; }
public float getEnergyCum() { return this.energyCum; }
public void setEnergyCum(float energy_cum) { this.energyCum = energy_cum;}
} }

View file

@ -20,7 +20,7 @@ import static androidx.core.app.JobIntentService.enqueueWork;
*/ */
public class SQLiteHelper extends SQLiteOpenHelper { public class SQLiteHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 1; private static final int DATABASE_VERSION = 4;
private Context context; private Context context;
private List<City> allCities = new ArrayList<>(); private List<City> allCities = new ArrayList<>();
@ -53,6 +53,9 @@ public class SQLiteHelper extends SQLiteOpenHelper {
private static final String CITIES_TO_WATCH_TILT_ANGLE = "tilt_angle"; private static final String CITIES_TO_WATCH_TILT_ANGLE = "tilt_angle";
private static final String CITIES_TO_WATCH_SHADING_ELEVATION = "shading_elevation"; private static final String CITIES_TO_WATCH_SHADING_ELEVATION = "shading_elevation";
private static final String CITIES_TO_WATCH_SHADING_OPACITY = "shading_opacity"; private static final String CITIES_TO_WATCH_SHADING_OPACITY = "shading_opacity";
private static final String CITIES_TO_WATCH_CELLS_TEMP_COEFF = "cells_temp_coeff";
private static final String CITIES_TO_WATCH_ALBEDO = "albedo";
private static final String CITIES_TO_WATCH_IS_CENTRAL_INVERTER = "is_central_inverter";
//Names of columns in TABLE_FORECAST //Names of columns in TABLE_FORECAST
private static final String FORECAST_ID = "forecast_id"; private static final String FORECAST_ID = "forecast_id";
@ -135,7 +138,10 @@ public class SQLiteHelper extends SQLiteOpenHelper {
CITIES_TO_WATCH_AZIMUTH_ANGLE + " REAL NOT NULL," + CITIES_TO_WATCH_AZIMUTH_ANGLE + " REAL NOT NULL," +
CITIES_TO_WATCH_TILT_ANGLE + " REAL NOT NULL," + CITIES_TO_WATCH_TILT_ANGLE + " REAL NOT NULL," +
CITIES_TO_WATCH_SHADING_ELEVATION + " VARCHAR(255) NOT NULL," + CITIES_TO_WATCH_SHADING_ELEVATION + " VARCHAR(255) NOT NULL," +
CITIES_TO_WATCH_SHADING_OPACITY + " VARCHAR(255) NOT NULL)"; CITIES_TO_WATCH_SHADING_OPACITY + " VARCHAR(255) NOT NULL," +
CITIES_TO_WATCH_CELLS_TEMP_COEFF + " REAL NOT NULL," +
CITIES_TO_WATCH_ALBEDO + " REAL NOT NULL," +
CITIES_TO_WATCH_IS_CENTRAL_INVERTER + " INTEGER)";
public static SQLiteHelper getInstance(Context context) { public static SQLiteHelper getInstance(Context context) {
if (instance == null && context != null) { if (instance == null && context != null) {
@ -160,6 +166,16 @@ public class SQLiteHelper extends SQLiteOpenHelper {
@Override @Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
switch(oldVersion) {
case 1:
db.execSQL("ALTER TABLE "+TABLE_CITIES_TO_WATCH+" ADD COLUMN "+CITIES_TO_WATCH_CELLS_TEMP_COEFF+" REAL DEFAULT 0");
// we want both updates, so no break statement here...
case 2:
db.execSQL("ALTER TABLE "+TABLE_CITIES_TO_WATCH+" ADD COLUMN "+CITIES_TO_WATCH_ALBEDO+" REAL DEFAULT 0");
case 3:
db.execSQL("ALTER TABLE "+TABLE_CITIES_TO_WATCH+" ADD COLUMN "+CITIES_TO_WATCH_IS_CENTRAL_INVERTER+" INTEGER DEFAULT 0");
}
} }
@ -185,6 +201,9 @@ public class SQLiteHelper extends SQLiteOpenHelper {
values.put(CITIES_TO_WATCH_TILT_ANGLE,city.getTiltAngle()); values.put(CITIES_TO_WATCH_TILT_ANGLE,city.getTiltAngle());
values.put(CITIES_TO_WATCH_SHADING_ELEVATION,city.getShadingElevationString()); values.put(CITIES_TO_WATCH_SHADING_ELEVATION,city.getShadingElevationString());
values.put(CITIES_TO_WATCH_SHADING_OPACITY,city.getShadingOpacityString()); values.put(CITIES_TO_WATCH_SHADING_OPACITY,city.getShadingOpacityString());
values.put(CITIES_TO_WATCH_CELLS_TEMP_COEFF,city.getCellsTempCoeff());
values.put(CITIES_TO_WATCH_ALBEDO,city.getAlbedo());
values.put(CITIES_TO_WATCH_IS_CENTRAL_INVERTER,city.isCentralInverter() ? 1 : 0);
long id=database.insert(TABLE_CITIES_TO_WATCH, null, values); long id=database.insert(TABLE_CITIES_TO_WATCH, null, values);
@ -218,6 +237,9 @@ public class SQLiteHelper extends SQLiteOpenHelper {
", " + CITIES_TO_WATCH_TILT_ANGLE + ", " + CITIES_TO_WATCH_TILT_ANGLE +
", " + CITIES_TO_WATCH_SHADING_ELEVATION + ", " + CITIES_TO_WATCH_SHADING_ELEVATION +
", " + CITIES_TO_WATCH_SHADING_OPACITY + ", " + CITIES_TO_WATCH_SHADING_OPACITY +
", " + CITIES_TO_WATCH_CELLS_TEMP_COEFF +
", " + CITIES_TO_WATCH_ALBEDO +
", " + CITIES_TO_WATCH_IS_CENTRAL_INVERTER +
", " + CITIES_TO_WATCH_COLUMN_RANK + ", " + CITIES_TO_WATCH_COLUMN_RANK +
" FROM " + TABLE_CITIES_TO_WATCH + " FROM " + TABLE_CITIES_TO_WATCH +
" WHERE " + CITIES_TO_WATCH_CITY_ID + " = ?", arguments); " WHERE " + CITIES_TO_WATCH_CITY_ID + " = ?", arguments);
@ -240,7 +262,10 @@ public class SQLiteHelper extends SQLiteOpenHelper {
cityToWatch.setTiltAngle(Float.parseFloat(cursor.getString(12))); cityToWatch.setTiltAngle(Float.parseFloat(cursor.getString(12)));
cityToWatch.setShadingElevation(cursor.getString(13)); cityToWatch.setShadingElevation(cursor.getString(13));
cityToWatch.setShadingOpacity(cursor.getString(14)); cityToWatch.setShadingOpacity(cursor.getString(14));
cityToWatch.setRank(Integer.parseInt(cursor.getString(15))); cityToWatch.setCellsTempCoeff(Float.parseFloat(cursor.getString(15)));
cityToWatch.setAlbedo(Float.parseFloat(cursor.getString(16)));
cityToWatch.setIsCentralInverter(Integer.parseInt(cursor.getString(17)) == 1);
cityToWatch.setRank(Integer.parseInt(cursor.getString(18)));
cursor.close(); cursor.close();
} }
@ -271,6 +296,9 @@ public class SQLiteHelper extends SQLiteOpenHelper {
", " + CITIES_TO_WATCH_TILT_ANGLE + ", " + CITIES_TO_WATCH_TILT_ANGLE +
", " + CITIES_TO_WATCH_SHADING_ELEVATION + ", " + CITIES_TO_WATCH_SHADING_ELEVATION +
", " + CITIES_TO_WATCH_SHADING_OPACITY + ", " + CITIES_TO_WATCH_SHADING_OPACITY +
", " + CITIES_TO_WATCH_CELLS_TEMP_COEFF +
", " + CITIES_TO_WATCH_ALBEDO +
", " + CITIES_TO_WATCH_IS_CENTRAL_INVERTER +
", " + CITIES_TO_WATCH_COLUMN_RANK + ", " + CITIES_TO_WATCH_COLUMN_RANK +
" FROM " + TABLE_CITIES_TO_WATCH " FROM " + TABLE_CITIES_TO_WATCH
, new String[]{}); , new String[]{});
@ -295,7 +323,10 @@ public class SQLiteHelper extends SQLiteOpenHelper {
cityToWatch.setTiltAngle(Float.parseFloat(cursor.getString(12))); cityToWatch.setTiltAngle(Float.parseFloat(cursor.getString(12)));
cityToWatch.setShadingElevation(cursor.getString(13)); cityToWatch.setShadingElevation(cursor.getString(13));
cityToWatch.setShadingOpacity(cursor.getString(14)); cityToWatch.setShadingOpacity(cursor.getString(14));
cityToWatch.setRank(Integer.parseInt(cursor.getString(15))); cityToWatch.setCellsTempCoeff(Float.parseFloat(cursor.getString(15)));
cityToWatch.setAlbedo(Float.parseFloat(cursor.getString(16)));
cityToWatch.setIsCentralInverter(Integer.parseInt(cursor.getString(17)) == 1);
cityToWatch.setRank(Integer.parseInt(cursor.getString(18)));
cityToWatchList.add(cityToWatch); cityToWatchList.add(cityToWatch);
} while (cursor.moveToNext()); } while (cursor.moveToNext());
@ -325,6 +356,9 @@ public class SQLiteHelper extends SQLiteOpenHelper {
values.put(CITIES_TO_WATCH_TILT_ANGLE,cityToWatch.getTiltAngle()); values.put(CITIES_TO_WATCH_TILT_ANGLE,cityToWatch.getTiltAngle());
values.put(CITIES_TO_WATCH_SHADING_ELEVATION,cityToWatch.getShadingElevationString()); values.put(CITIES_TO_WATCH_SHADING_ELEVATION,cityToWatch.getShadingElevationString());
values.put(CITIES_TO_WATCH_SHADING_OPACITY,cityToWatch.getShadingOpacityString()); values.put(CITIES_TO_WATCH_SHADING_OPACITY,cityToWatch.getShadingOpacityString());
values.put(CITIES_TO_WATCH_CELLS_TEMP_COEFF,cityToWatch.getCellsTempCoeff());
values.put(CITIES_TO_WATCH_ALBEDO,cityToWatch.getAlbedo());
values.put(CITIES_TO_WATCH_IS_CENTRAL_INVERTER,cityToWatch.isCentralInverter() ? 1 : 0);
database.update(TABLE_CITIES_TO_WATCH, values, CITIES_TO_WATCH_ID + " = ?", database.update(TABLE_CITIES_TO_WATCH, values, CITIES_TO_WATCH_ID + " = ?",
new String[]{String.valueOf(cityToWatch.getId())}); new String[]{String.valueOf(cityToWatch.getId())});
@ -389,6 +423,17 @@ public class SQLiteHelper extends SQLiteOpenHelper {
} }
public synchronized void deleteAllForecasts(){
SQLiteDatabase database = this.getWritableDatabase();
database.delete(TABLE_GENERAL_DATA, COLUMN_CITY_ID + " <> ?",
new String[]{Integer.toString(-1)});
database.delete(TABLE_HOURLY_FORECAST, FORECAST_CITY_ID + " <> ?",
new String[]{Integer.toString(-1)});
database.delete(TABLE_WEEKFORECAST, WEEKFORECAST_CITY_ID + " <> ?",
new String[]{Integer.toString(-1)});
database.close();
}
public synchronized List<HourlyForecast> getForecastsByCityId(int cityId) { public synchronized List<HourlyForecast> getForecastsByCityId(int cityId) {
SQLiteDatabase database = this.getWritableDatabase(); SQLiteDatabase database = this.getWritableDatabase();

View file

@ -6,6 +6,7 @@ import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.res.Resources; import android.content.res.Resources;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
@ -24,8 +25,10 @@ import android.widget.AutoCompleteTextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.os.ConfigurationCompat; import androidx.core.os.ConfigurationCompat;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import androidx.preference.PreferenceManager;
import com.android.volley.Response; import com.android.volley.Response;
import com.android.volley.VolleyError; import com.android.volley.VolleyError;
@ -53,47 +56,73 @@ public class AddLocationDialogOmGeocodingAPI extends DialogFragment {
Activity activity; Activity activity;
View rootView; View rootView;
SQLiteHelper database; SQLiteHelper database;
private WebView webview;
private AutoCompleteTextView autoCompleteTextView; private AutoCompleteTextView autoCompleteTextView;
City selectedCity; City selectedCity;
private static final int TRIGGER_AUTO_COMPLETE = 100; private static final int TRIGGER_AUTO_COMPLETE = 100;
private static final long AUTO_COMPLETE_DELAY = 300; private static final long AUTO_COMPLETE_DELAY = 300;
private static final int TRIGGER_HIDE_KEYBOARD = 200;
private static final long HIDE_KEYBOARD_DELAY = 3000;
private Handler handler; private Handler handler;
private AutoSuggestAdapter autoSuggestAdapter; private AutoSuggestAdapter autoSuggestAdapter;
String url="https://geocoding-api.open-meteo.com/v1/search?name="; String urlSuffix="v1/search?name=";
String lang="default"; String url="";
String lang="en";
public AddLocationDialogOmGeocodingAPI() {
setRetainInstance(true);
}
@Override @Override
public void onAttach(@NonNull Context context) { public void onAttach(@NonNull Context context) {
super.onAttach(context); super.onAttach(context);
if (context instanceof Activity){ if (context instanceof Activity){
this.activity=(Activity) context; this.activity=(Activity) context;
} }
} }
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) dismiss();
}
@Override
public void onResume() {
super.onResume();
handler.removeMessages(TRIGGER_HIDE_KEYBOARD);
if(selectedCity != null && webview != null) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(activity);
String osmTiles = sp.getString("pref_OsmTiles_URL", BuildConfig.TILES_URL);
webview.loadUrl("file:///android_asset/map.html?lat=" + selectedCity.getLatitude() + "&lon=" + selectedCity.getLongitude() + "&tiles=" + osmTiles);
}
}
@NonNull @NonNull
@SuppressLint("SetJavaScriptEnabled") @SuppressLint("SetJavaScriptEnabled")
@Override @Override
public Dialog onCreateDialog(Bundle savedInstanceState) { public Dialog onCreateDialog(Bundle savedInstanceState) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(activity);
url = sp.getString("pref_OMGEO_URL",BuildConfig.GEOCODING_URL);
url = url + urlSuffix;
Locale locale = ConfigurationCompat.getLocales(Resources.getSystem().getConfiguration()).get(0); Locale locale = ConfigurationCompat.getLocales(Resources.getSystem().getConfiguration()).get(0);
lang=locale.getLanguage(); if (locale != null) lang=locale.getLanguage();
LayoutInflater inflater = getActivity().getLayoutInflater(); LayoutInflater inflater = activity.getLayoutInflater();
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); AlertDialog.Builder builder = new AlertDialog.Builder(activity);
View view = inflater.inflate(R.layout.dialog_add_location, null); View view = inflater.inflate(R.layout.dialog_add_location, null);
rootView = view; rootView = view;
builder.setView(view); builder.setView(view);
builder.setTitle(getActivity().getString(R.string.dialog_add_label)); builder.setTitle(activity.getString(R.string.dialog_add_label));
this.database = SQLiteHelper.getInstance(getActivity()); this.database = SQLiteHelper.getInstance(activity);
final WebView webview= rootView.findViewById(R.id.webViewAddLocation); webview = rootView.findViewById(R.id.webViewAddLocation);
webview.getSettings().setJavaScriptEnabled(true); webview.getSettings().setJavaScriptEnabled(true);
webview.getSettings().setUserAgentString(BuildConfig.APPLICATION_ID+"/"+BuildConfig.VERSION_NAME); webview.getSettings().setUserAgentString(BuildConfig.APPLICATION_ID+"/"+BuildConfig.VERSION_NAME);
webview.setBackgroundColor(0x00000000); webview.setBackgroundColor(0x00000000);
@ -115,10 +144,11 @@ public class AddLocationDialogOmGeocodingAPI extends DialogFragment {
int position, long id) { int position, long id) {
selectedCity=autoSuggestAdapter.getObject(position); selectedCity=autoSuggestAdapter.getObject(position);
//Hide keyboard to have more space //Hide keyboard to have more space
final InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); final InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(rootView.getWindowToken(), 0); imm.hideSoftInputFromWindow(rootView.getWindowToken(), 0);
//Show city on map //Show city on map
webview.loadUrl("file:///android_asset/map.html?lat=" + selectedCity.getLatitude() + "&lon=" + selectedCity.getLongitude()); String osmTiles = sp.getString("pref_OsmTiles_URL", BuildConfig.TILES_URL);
webview.loadUrl("file:///android_asset/map.html?lat=" + selectedCity.getLatitude() + "&lon=" + selectedCity.getLongitude() + "&tiles=" + osmTiles);
} }
}); });
@ -133,8 +163,9 @@ public class AddLocationDialogOmGeocodingAPI extends DialogFragment {
public void onTextChanged(CharSequence s, int start, int before, public void onTextChanged(CharSequence s, int start, int before,
int count) { int count) {
handler.removeMessages(TRIGGER_AUTO_COMPLETE); handler.removeMessages(TRIGGER_AUTO_COMPLETE);
handler.sendEmptyMessageDelayed(TRIGGER_AUTO_COMPLETE, handler.sendEmptyMessageDelayed(TRIGGER_AUTO_COMPLETE, AUTO_COMPLETE_DELAY);
AUTO_COMPLETE_DELAY); handler.removeMessages(TRIGGER_HIDE_KEYBOARD);
handler.sendEmptyMessageDelayed(TRIGGER_HIDE_KEYBOARD, HIDE_KEYBOARD_DELAY);
} }
@Override @Override
@ -143,23 +174,24 @@ public class AddLocationDialogOmGeocodingAPI extends DialogFragment {
} }
}); });
handler = new Handler(Looper.getMainLooper(), new Handler.Callback() { handler = new Handler(Looper.getMainLooper(), msg -> {
@Override if (msg.what == TRIGGER_AUTO_COMPLETE) {
public boolean handleMessage(Message msg) { if (!TextUtils.isEmpty(autoCompleteTextView.getText())) {
if (msg.what == TRIGGER_AUTO_COMPLETE) { try {
if (!TextUtils.isEmpty(autoCompleteTextView.getText())) { makeApiCall(URLEncoder.encode(autoCompleteTextView.getText().toString(), StandardCharsets.UTF_8.name()));
try { } catch (UnsupportedEncodingException e) {
makeApiCall(URLEncoder.encode(autoCompleteTextView.getText().toString(), StandardCharsets.UTF_8.name())); e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} }
} }
return false; } else if (msg.what == TRIGGER_HIDE_KEYBOARD) {
//Hide keyboard to show entries behind the keyboard
final InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(rootView.getWindowToken(), 0);
} }
return false;
}); });
builder.setPositiveButton(getActivity().getString(R.string.dialog_add_add_button), new DialogInterface.OnClickListener() { builder.setPositiveButton(activity.getString(R.string.dialog_add_add_button), new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
@ -167,7 +199,7 @@ public class AddLocationDialogOmGeocodingAPI extends DialogFragment {
} }
}); });
builder.setNegativeButton(getActivity().getString(R.string.dialog_add_close_button), null); builder.setNegativeButton(activity.getString(android.R.string.cancel), null);
return builder.create(); return builder.create();

View file

@ -14,6 +14,7 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.Window; import android.view.Window;
import android.view.WindowInsetsController;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.Button; import android.widget.Button;
@ -22,7 +23,7 @@ import android.widget.TextView;
import org.woheller69.weather.R; import org.woheller69.weather.R;
import org.woheller69.weather.activities.ForecastCityActivity; import org.woheller69.weather.activities.ForecastCityActivity;
import org.woheller69.weather.activities.SettingsActivity; import org.woheller69.weather.activities.HelpActivity;
/** /**
@ -46,7 +47,12 @@ public class TutorialActivity extends AppCompatActivity {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tutorial); setContentView(R.layout.activity_tutorial);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
getWindow().getInsetsController().setSystemBarsAppearance(
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS,
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
);
}
viewPager = (ViewPager) findViewById(R.id.view_pager); viewPager = (ViewPager) findViewById(R.id.view_pager);
dotsLayout = (LinearLayout) findViewById(R.id.layoutDots); dotsLayout = (LinearLayout) findViewById(R.id.layoutDots);
btnNext = (Button) findViewById(R.id.btn_next); btnNext = (Button) findViewById(R.id.btn_next);
@ -62,8 +68,6 @@ public class TutorialActivity extends AppCompatActivity {
// adding bottom dots // adding bottom dots
addBottomDots(0); addBottomDots(0);
// making notification bar transparent
changeStatusBarColor();
myViewPagerAdapter = new MyViewPagerAdapter(); myViewPagerAdapter = new MyViewPagerAdapter();
viewPager.setAdapter(myViewPagerAdapter); viewPager.setAdapter(myViewPagerAdapter);
@ -118,7 +122,7 @@ public class TutorialActivity extends AppCompatActivity {
} }
private void launchSettings() { private void launchSettings() {
startActivity(new Intent(TutorialActivity.this, SettingsActivity.class)); startActivity(new Intent(TutorialActivity.this, HelpActivity.class));
finish(); finish();
} }
@ -158,17 +162,6 @@ public class TutorialActivity extends AppCompatActivity {
} }
}; };
/**
* Making notification bar transparent
*/
private void changeStatusBarColor() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
}
}
/** /**
* View pager adapter * View pager adapter
*/ */

View file

@ -20,6 +20,11 @@ import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.URL; import java.net.URL;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/** /**
* This class provides the functionality to fetch forecast data for a given city as a background * This class provides the functionality to fetch forecast data for a given city as a background
@ -54,7 +59,7 @@ public class UpdateDataService extends JobIntentService {
@Override @Override
protected void onHandleWork(Intent intent) { protected void onHandleWork(Intent intent) {
if (!isOnline()) { if (!isOnline(2000)) {
Handler h = new Handler(getApplicationContext().getMainLooper()); Handler h = new Handler(getApplicationContext().getMainLooper());
h.post(new Runnable() { h.post(new Runnable() {
@Override @Override
@ -116,14 +121,22 @@ public class UpdateDataService extends JobIntentService {
} }
} }
private boolean isOnline() { private boolean isOnline(int timeOut) { //https://stackoverflow.com/questions/9570237/android-check-internet-connection
InetAddress inetAddress = null;
try { try {
URL url = new URL(BuildConfig.BASE_URL); Future<InetAddress> future = Executors.newSingleThreadExecutor().submit(() -> {
InetAddress inetAddress = InetAddress.getByName(url.getHost()); try {
return inetAddress.isReachable(2000); URL url = new URL(BuildConfig.BASE_URL);
} catch (IOException | IllegalArgumentException e) { return InetAddress.getByName(url.getHost());
return false; } catch ( IOException e) {
return null;
}
});
inetAddress = future.get(timeOut, TimeUnit.MILLISECONDS);
future.cancel(true);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
} }
return inetAddress!=null && !inetAddress.toString().isEmpty();
} }
private void handleUpdateForecastAction(Intent intent) { private void handleUpdateForecastAction(Intent intent) {

View file

@ -5,9 +5,9 @@ import android.text.Spanned;
public class InputFilterMinMax implements InputFilter { public class InputFilterMinMax implements InputFilter {
private int min, max; private float min, max;
public InputFilterMinMax(int min, int max) { public InputFilterMinMax(float min, float max) {
this.min = min; this.min = min;
this.max = max; this.max = max;
} }
@ -15,17 +15,30 @@ public class InputFilterMinMax implements InputFilter {
@Override @Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
String oldString = dest.toString(); try {
String insertString = source.toString(); String oldString = dest.toString();
String newString = new StringBuilder(oldString).insert(dstart,insertString).toString(); String insertString = source.toString();
float input = Float.parseFloat(newString); String newString = oldString.substring(0, dstart) + oldString.substring(dend);
if (isInRange(min, max, input)) newString = newString.substring(0, dstart) + insertString + newString.substring(dstart);
return null; float input = Float.parseFloat(newString);
else
return ""; if (isInRange(min, max, input)) {
return null;
} else {
if (source.equals("") && dest.toString().length() != 1) {
//backspace was clicked, do not accept that change, unless user is deleting the last char
return dest.subSequence(dstart, dend);
} else {
return "";
}
}
} catch (NumberFormatException e) {
e.printStackTrace();
}
return "";
} }
private boolean isInRange(int a, int b, float c) { private boolean isInRange(float a, float b, float c) {
return b > a ? c >= a && c <= b : c >= b && c <= a; return b > a ? c >= a && c <= b : c >= b && c <= a;
} }

View file

@ -2,14 +2,12 @@ package org.woheller69.weather.ui.Help;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.graphics.drawable.Drawable;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import androidx.core.content.res.ResourcesCompat;
import org.woheller69.weather.R; import org.woheller69.weather.R;
import org.woheller69.weather.preferences.AppPreferencesManager;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.text.DateFormat;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Calendar; import java.util.Calendar;
@ -28,6 +26,12 @@ public final class StringFormatUtils {
return removeMinusIfZerosOnly(decimalFormat.format(decimal)); return removeMinusIfZerosOnly(decimalFormat.format(decimal));
} }
public static String formatEnergyCum(Context context, float energyCum) {
if (energyCum < 10000.0f) return formatInt(energyCum, context.getString(R.string.units_Wh));
else if (energyCum < 100000.0f) return formatDecimal(energyCum/1000,context.getString(R.string.units_kWh));
else return formatInt(energyCum/1000,context.getString(R.string.units_kWh));
}
public static String formatInt(float decimal) { public static String formatInt(float decimal) {
intFormat.setRoundingMode(RoundingMode.HALF_UP); intFormat.setRoundingMode(RoundingMode.HALF_UP);
return removeMinusIfZerosOnly(intFormat.format(decimal)); return removeMinusIfZerosOnly(intFormat.format(decimal));
@ -54,6 +58,12 @@ public final class StringFormatUtils {
return df.format(time); return df.format(time);
} }
public static String formatDate(long time) {
java.text.DateFormat df = java.text.DateFormat.getDateInstance(DateFormat.SHORT);
df.setTimeZone(TimeZone.getTimeZone("GMT"));
return df.format(time);
}
public static Integer getDayShort(int day){ public static Integer getDayShort(int day){
switch(day) { switch(day) {

View file

@ -2,7 +2,6 @@ package org.woheller69.weather.ui.RecycleList;
import android.content.Context; import android.content.Context;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.core.content.res.ResourcesCompat;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -13,6 +12,7 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import com.db.chart.Tools; import com.db.chart.Tools;
import com.db.chart.model.BarSet; import com.db.chart.model.BarSet;
@ -21,12 +21,12 @@ import com.db.chart.view.AxisController;
import com.db.chart.view.BarChartView; import com.db.chart.view.BarChartView;
import org.woheller69.weather.R; import org.woheller69.weather.R;
import org.woheller69.weather.database.CityToWatch;
import org.woheller69.weather.database.GeneralData; import org.woheller69.weather.database.GeneralData;
import org.woheller69.weather.database.HourlyForecast; import org.woheller69.weather.database.HourlyForecast;
import org.woheller69.weather.database.SQLiteHelper; import org.woheller69.weather.database.SQLiteHelper;
import org.woheller69.weather.database.WeekForecast; import org.woheller69.weather.database.WeekForecast;
import org.woheller69.weather.ui.Help.StringFormatUtils; import org.woheller69.weather.ui.Help.StringFormatUtils;
import org.woheller69.weather.ui.UiResourceProvider;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
@ -37,7 +37,9 @@ public class CityWeatherAdapter extends RecyclerView.Adapter<CityWeatherAdapter.
private int[] dataSetTypes; private int[] dataSetTypes;
private List<HourlyForecast> courseDayList; private List<HourlyForecast> courseDayList;
private float[][] forecastData; private List<WeekForecast> weekForecastList;
private float producedToday;
private float remainingToday;
private Context context; private Context context;
private ViewGroup mParent; private ViewGroup mParent;
@ -63,60 +65,93 @@ public class CityWeatherAdapter extends RecyclerView.Adapter<CityWeatherAdapter.
List<HourlyForecast> hourlyForecasts = database.getForecastsByCityId(generalDataList.getCity_id()); List<HourlyForecast> hourlyForecasts = database.getForecastsByCityId(generalDataList.getCity_id());
List<WeekForecast> weekforecasts = database.getWeekForecastsByCityId(generalDataList.getCity_id()); List<WeekForecast> weekforecasts = database.getWeekForecastsByCityId(generalDataList.getCity_id());
updateForecastData(hourlyForecasts); updateForecastData(hourlyForecasts, weekforecasts);
updateWeekForecastData(weekforecasts);
} }
// function update 3-hour or 1-hour forecast list // function update 3-hour or 1-hour forecast list
public void updateForecastData(List<HourlyForecast> hourlyForecasts) { public void updateForecastData(List<HourlyForecast> hourlyForecasts, List<WeekForecast> weekForecasts) {
if (hourlyForecasts.isEmpty() || weekForecasts.isEmpty()) return;
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
courseDayList = new ArrayList<>(); int cityId = hourlyForecasts.get(0).getCity_id();
SQLiteHelper dbHelper = SQLiteHelper.getInstance(context.getApplicationContext());
CityToWatch requestedCity = dbHelper.getCityToWatch(cityId);
for (HourlyForecast f : hourlyForecasts) { Float centralInverterLimit = requestedCity.isCentralInverter() ? requestedCity.getInverterPowerLimit() : 0;
if (sp.getBoolean("pref_debug",false)) {
courseDayList.add(f); if (sp.getBoolean("pref_summarize",false)){
} else { ArrayList<Integer> CityIDList = new ArrayList<Integer>();
if (f.getForecastTime() >= System.currentTimeMillis()) { hourlyForecasts = dbHelper.getForecastsByCityId(cityId); //get fresh values from database to make sure we do add new values to sum values from last update
courseDayList.add(f); List<CityToWatch> citiesToWatch = dbHelper.getAllCitiesToWatch();
for (int i = 0; i < citiesToWatch.size(); i++) {
CityToWatch city = citiesToWatch.get(i);
if (city.getCityId()!=requestedCity.getCityId() && city.getLatitude() == requestedCity.getLatitude() && city.getLongitude() == requestedCity.getLongitude()) {
CityIDList.add(city.getCityId());
if (city.isCentralInverter()) centralInverterLimit += city.getInverterPowerLimit();
}
}
if (CityIDList.size()>0){
for (int c=0; c<CityIDList.size();c++) {
int iteratorCityId = CityIDList.get(c);
List<HourlyForecast> hfc = dbHelper.getForecastsByCityId(iteratorCityId);
if (hfc.size()!=hourlyForecasts.size()) break; //maybe something went wrong during update or city is not yet updated
for (int i=0;i<hfc.size();i++){
hourlyForecasts.get(i).setPower(hourlyForecasts.get(i).getPower()+hfc.get(i).getPower());
} }
} }
} }
notifyDataSetChanged();
}
// function for week forecast list
public void updateWeekForecastData(List<WeekForecast> forecasts) {
if (forecasts.isEmpty()) return;
int cityId = forecasts.get(0).getCity_id();
SQLiteHelper dbHelper = SQLiteHelper.getInstance(context.getApplicationContext());
int zonemilliseconds = dbHelper.getGeneralDataByCityId(cityId).getTimeZoneSeconds() * 1000;
//temp max 0, temp min 1, humidity 2, pressure 3, precipitation 4, wind 5, wind direction 6, uv_index 7, forecast time 8, weather ID 9, number of FCs for day 10
forecastData = new float[forecasts.size()][11];
for (int i=0;i<forecasts.size();i++){
forecastData[i][0]=0;
forecastData[i][1]=0;
forecastData[i][2]=0;
forecastData[i][3]=0;
forecastData[i][4]=forecasts.get(i).getEnergyDay();
forecastData[i][5]=0;
forecastData[i][6]=0;
forecastData[i][7]=0;
forecastData[i][8]=forecasts.get(i).getForecastTime()+zonemilliseconds;
forecastData[i][9]=forecasts.get(i).getWeatherID();
forecastData[i][10]=1;
} }
courseDayList = new ArrayList<>();
float energyCumulated=0;
boolean isDebugMode = sp.getBoolean("pref_debug", false);
int stepCounter = 0; // Counter to track the number of steps taken in the loop
for (HourlyForecast f : hourlyForecasts) {
if (centralInverterLimit>0) f.setPower(Math.min(f.getPower(),centralInverterLimit)); //apply central inverter limit if there is one
float power = f.getPower();
if (stepCounter > 0) energyCumulated += power; //Ignore first value because power values are for preceding hour
f.setEnergyCum(energyCumulated);
// In debug mode show all values, otherwise only future values
if (isDebugMode || f.getForecastTime() >= System.currentTimeMillis()) {
courseDayList.add(f);
}
if (f.getForecastTime() < System.currentTimeMillis() && stepCounter <= 24) producedToday = f.getEnergyCum();
if (f.getForecastTime() > System.currentTimeMillis() && f.getForecastTime() < System.currentTimeMillis() + 3600000 && stepCounter <= 24) {
long millisRemainingThisHour = f.getForecastTime() - System.currentTimeMillis();
long millisSoFarThisHour = 3600000 - millisRemainingThisHour;
producedToday = producedToday + (f.getEnergyCum()-producedToday) * millisSoFarThisHour/3600000;
}
stepCounter++;
// if not in debug mode: Reset energyCumulated after every 24 hours if next step is 01:00 am because values are for previous hour
if (!isDebugMode && stepCounter % 24 == 1) {
energyCumulated = 0;
}
}
//Now calculate weekForecasts from hourlyForecasts
for (WeekForecast weekForecast: weekForecasts){
float totalEnergy = 0;
long timeNoon = weekForecast.getForecastTime();
for (HourlyForecast hourlyForecast: hourlyForecasts){
if ((hourlyForecast.getForecastTime()>=timeNoon-11*3600*1000L) && (hourlyForecast.getForecastTime()< timeNoon + 13*3600*1000L)){ //values are for preceding hour!
totalEnergy+=hourlyForecast.getPower();
}
}
weekForecast.setEnergyDay(totalEnergy/1000);
}
remainingToday = weekForecasts.get(0).getEnergyDay()*1000 - producedToday;
weekForecastList = weekForecasts;
notifyDataSetChanged(); notifyDataSetChanged();
} }
static class ViewHolder extends RecyclerView.ViewHolder { static class ViewHolder extends RecyclerView.ViewHolder {
ViewHolder(View v) { ViewHolder(View v) {
super(v); super(v);
@ -124,15 +159,17 @@ public class CityWeatherAdapter extends RecyclerView.Adapter<CityWeatherAdapter.
} }
public class OverViewHolder extends ViewHolder { public class OverViewHolder extends ViewHolder {
TextView temperature; TextView produced;
TextView remaining;
TextView updatetime; TextView updatetime;
TextView sun; TextView sun;
OverViewHolder(View v) { OverViewHolder(View v) {
super(v); super(v);
this.temperature = v.findViewById(R.id.card_overview_temperature);
this.sun=v.findViewById(R.id.card_overview_sunrise_sunset); this.sun=v.findViewById(R.id.card_overview_sunrise_sunset);
this.updatetime=v.findViewById(R.id.card_overview_update_time); this.updatetime=v.findViewById(R.id.card_overview_update_time);
this.produced=v.findViewById(R.id.card_overview_produced_today);
this.remaining=v.findViewById(R.id.card_overview_remaining_today);
} }
} }
@ -181,13 +218,13 @@ public class CityWeatherAdapter extends RecyclerView.Adapter<CityWeatherAdapter.
public class ChartViewHolder extends ViewHolder { public class ChartViewHolder extends ViewHolder {
TextView precipitationunit; TextView energyUnit;
BarChartView barChartView; BarChartView barChartView;
ChartViewHolder(View v) { ChartViewHolder(View v) {
super(v); super(v);
this.barChartView = v.findViewById(R.id.graph_precipitation); this.barChartView = v.findViewById(R.id.graph_energy);
this.precipitationunit=v.findViewById(R.id.graph_precipitationunit); this.energyUnit =v.findViewById(R.id.graph_energyunit);
} }
} }
@ -250,6 +287,8 @@ public class CityWeatherAdapter extends RecyclerView.Adapter<CityWeatherAdapter.
holder.updatetime.setText("("+StringFormatUtils.formatTimeWithoutZone(context, updateTime)+")"); holder.updatetime.setText("("+StringFormatUtils.formatTimeWithoutZone(context, updateTime)+")");
holder.produced.setText(StringFormatUtils.formatEnergyCum(context, producedToday));
holder.remaining.setText(StringFormatUtils.formatEnergyCum(context, remainingToday));
} else if (viewHolder.getItemViewType() == WEEK) { } else if (viewHolder.getItemViewType() == WEEK) {
@ -258,7 +297,7 @@ public class CityWeatherAdapter extends RecyclerView.Adapter<CityWeatherAdapter.
holder.recyclerView.setLayoutManager(layoutManager); holder.recyclerView.setLayoutManager(layoutManager);
final WeekWeatherAdapter adapter = new WeekWeatherAdapter(context, forecastData, generalDataList.getCity_id()); final WeekWeatherAdapter adapter = new WeekWeatherAdapter(context, weekForecastList, generalDataList.getCity_id());
holder.recyclerView.setAdapter(adapter); holder.recyclerView.setAdapter(adapter);
holder.recyclerView.setFocusable(false); holder.recyclerView.setFocusable(false);
@ -272,6 +311,7 @@ public class CityWeatherAdapter extends RecyclerView.Adapter<CityWeatherAdapter.
new RecyclerItemClickListener(context, holder.recyclerView, new RecyclerItemClickListener.OnItemClickListener() { new RecyclerItemClickListener(context, holder.recyclerView, new RecyclerItemClickListener.OnItemClickListener() {
@Override @Override
public void onItemClick(View view, int position) { public void onItemClick(View view, int position) {
if (position == -1) return;
SQLiteHelper database = SQLiteHelper.getInstance(context.getApplicationContext()); SQLiteHelper database = SQLiteHelper.getInstance(context.getApplicationContext());
List<WeekForecast> weekforecasts = database.getWeekForecastsByCityId(generalDataList.getCity_id()); List<WeekForecast> weekforecasts = database.getWeekForecastsByCityId(generalDataList.getCity_id());
long time = weekforecasts.get(position).getForecastTime(); //time of clicked week item long time = weekforecasts.get(position).getForecastTime(); //time of clicked week item
@ -302,20 +342,11 @@ public class CityWeatherAdapter extends RecyclerView.Adapter<CityWeatherAdapter.
mCourseOfDay.getLayoutManager().scrollToPosition(i); mCourseOfDay.getLayoutManager().scrollToPosition(i);
} }
highlightSelected(view);
} }
} }
} }
private void highlightSelected(View view) {
for (int j=0;j<courseDayList.size();j++){ //reset all items
if (holder.recyclerView.getLayoutManager().getChildAt(j)!=null){
holder.recyclerView.getLayoutManager().getChildAt(j).setBackground(ResourcesCompat.getDrawable(context.getResources(),R.drawable.rounded_transparent,null));
}
}
view.setBackground(ResourcesCompat.getDrawable(context.getResources(),R.drawable.rounded_highlight,null)); //highlight selected item
}
public void onLongItemClick(View view, int position) { public void onLongItemClick(View view, int position) {
@ -335,52 +366,59 @@ public class CityWeatherAdapter extends RecyclerView.Adapter<CityWeatherAdapter.
} else if (viewHolder.getItemViewType() == CHART) { } else if (viewHolder.getItemViewType() == CHART) {
ChartViewHolder holder = (ChartViewHolder) viewHolder; ChartViewHolder holder = (ChartViewHolder) viewHolder;
SQLiteHelper database = SQLiteHelper.getInstance(context.getApplicationContext()); if(weekForecastList.isEmpty()) return;
List<WeekForecast> weekforecasts = database.getWeekForecastsByCityId(generalDataList.getCity_id());
if (weekforecasts.isEmpty()) { float energyMax=0;
return;
}
float pmax=0; BarSet energyDataset = new BarSet();
BarSet precipitationDataset = new BarSet();
Calendar c = Calendar.getInstance(); Calendar c = Calendar.getInstance();
c.setTimeZone(TimeZone.getTimeZone("GMT")); c.setTimeZone(TimeZone.getTimeZone("GMT"));
int zonemilliseconds = generalDataList.getTimeZoneSeconds()*1000;
for (int i=0 ; i< weekforecasts.size();i++) { for (int i = 0 ; i < weekForecastList.size(); i++) {
c.setTimeInMillis(weekforecasts.get(i).getForecastTime()+zonemilliseconds); c.setTimeInMillis(weekForecastList.get(i).getLocalForecastTime(context));
int day = c.get(Calendar.DAY_OF_WEEK); int day = c.get(Calendar.DAY_OF_WEEK);
float precip=weekforecasts.get(i).getEnergyDay(); float energyDay=weekForecastList.get(i).getEnergyDay();
String dayString = context.getResources().getString(StringFormatUtils.getDayShort(day)); String dayString = context.getResources().getString(StringFormatUtils.getDayShort(day));
if (weekforecasts.size()>8) dayString=dayString.substring(0,1); //use first character only if more than 8 days to avoid overlapping text if (weekForecastList.size()>8) dayString=dayString.substring(0,1); //use first character only if more than 8 days to avoid overlapping text
precipitationDataset.addBar(dayString, precip); energyDataset.addBar(dayString, energyDay);
if (precip>pmax) pmax=precip; if (energyDay>energyMax) energyMax=energyDay;
} }
ArrayList<ChartSet> precipitation = new ArrayList<>(); //Calculate step size. Target: 4 <= steps <= 7, but step size must integer >= 1
precipitation.add(precipitationDataset); int stepSize = 1;
int numSteps;
precipitationDataset.setColor(ContextCompat.getColor(context,R.color.yellow)); do {
precipitationDataset.setAlpha(0.8f); // make precipitation bars transparent numSteps = (int) (energyMax / stepSize);
if (numSteps > 10) stepSize *=10;
else if (numSteps >= 8) stepSize *=2;
else if (numSteps < 4) stepSize /=2;
} while (numSteps >= 8 || numSteps < 4 && stepSize>0);
holder.barChartView.addData(precipitation); if (stepSize<1) stepSize=1; //Step size must be integer, min 1
ArrayList<ChartSet> energyData = new ArrayList<>();
energyData.add(energyDataset);
energyDataset.setColor(ContextCompat.getColor(context,R.color.yellow));
energyDataset.setAlpha(0.8f); // make energyData bars transparent
holder.barChartView.addData(energyData);
holder.barChartView.setBarSpacing(10); holder.barChartView.setBarSpacing(10);
holder.barChartView.setStep(stepSize);
holder.barChartView.setXAxis(false); holder.barChartView.setXAxis(false);
holder.barChartView.setYAxis(false); holder.barChartView.setYAxis(false);
holder.barChartView.setYLabels(AxisController.LabelPosition.INSIDE); //no labels for precipitation holder.barChartView.setYLabels(AxisController.LabelPosition.OUTSIDE);
holder.barChartView.setLabelsColor(ContextCompat.getColor(context,R.color.colorPrimaryDark)); //transparent color, make labels invisible holder.barChartView.setLabelsColor(ContextCompat.getColor(context,R.color.colorPrimaryDark)); //transparent color, make labels invisible
holder.barChartView.setAxisColor(ContextCompat.getColor(context,R.color.colorPrimaryDark)); holder.barChartView.setAxisColor(ContextCompat.getColor(context,R.color.colorPrimaryDark));
holder.barChartView.setFontSize((int) Tools.fromDpToPx(17)); holder.barChartView.setFontSize((int) Tools.fromDpToPx(17));
holder.barChartView.setBorderSpacing(Tools.fromDpToPx(30));
holder.barChartView.show(); holder.barChartView.show();
holder.precipitationunit.setText(" " + context.getResources().getString(R.string.units_kWh)+" "); holder.energyUnit.setText(" " + context.getResources().getString(R.string.units_kWh)+" ");
} }
//No update for error needed //No update for error needed
} }

View file

@ -42,7 +42,7 @@ public class CourseOfDayAdapter extends RecyclerView.Adapter<CourseOfDayAdapter.
this.courseOfDayList = courseOfDayList; this.courseOfDayList = courseOfDayList;
this.recyclerViewHeader=recyclerViewHeader; this.recyclerViewHeader=recyclerViewHeader;
this.recyclerView=recyclerView; this.recyclerView=recyclerView;
if (courseOfDayList.size()!=0 && courseOfDayList.get(0)!=null) { if (courseOfDayList!=null && courseOfDayList.size()!=0 && courseOfDayList.get(0)!=null) {
this.courseOfDayHeaderDate = new Date(courseOfDayList.get(0).getLocalForecastTime(context)); this.courseOfDayHeaderDate = new Date(courseOfDayList.get(0).getLocalForecastTime(context));
}else this.courseOfDayHeaderDate=new Date(); //fallback if no data available }else this.courseOfDayHeaderDate=new Date(); //fallback if no data available
} }
@ -98,21 +98,16 @@ public class CourseOfDayAdapter extends RecyclerView.Adapter<CourseOfDayAdapter.
if (sp.getBoolean("pref_debug",false)) { if (sp.getBoolean("pref_debug",false)) {
holder.diffuseRadiation.setVisibility(View.VISIBLE); holder.diffuseRadiation.setVisibility(View.VISIBLE);
holder.directRadiationNormal.setVisibility(View.VISIBLE); holder.directRadiationNormal.setVisibility(View.VISIBLE);
holder.energyCum.setVisibility(View.VISIBLE);
} else { } else {
holder.diffuseRadiation.setVisibility(View.GONE); holder.diffuseRadiation.setVisibility(View.GONE);
holder.directRadiationNormal.setVisibility(View.GONE); holder.directRadiationNormal.setVisibility(View.GONE);
holder.energyCum.setVisibility(View.GONE);
} }
holder.time.setText(StringFormatUtils.formatTimeWithoutZone(context, courseOfDayList.get(position).getLocalForecastTime(context))); holder.time.setText(StringFormatUtils.formatTimeWithoutZone(context, courseOfDayList.get(position).getLocalForecastTime(context)));
holder.directRadiationNormal.setText(StringFormatUtils.formatInt(courseOfDayList.get(position).getDirectRadiationNormal()," W/qm")); holder.directRadiationNormal.setText(StringFormatUtils.formatInt(courseOfDayList.get(position).getDirectRadiationNormal()," W/qm"));
holder.diffuseRadiation.setText(StringFormatUtils.formatInt(courseOfDayList.get(position).getDiffuseRadiation()," W/qm")); holder.diffuseRadiation.setText(StringFormatUtils.formatInt(courseOfDayList.get(position).getDiffuseRadiation()," W/qm"));
holder.power.setText(StringFormatUtils.formatInt(courseOfDayList.get(position).getPower()," "+ context.getString(R.string.units_Wh))); holder.power.setText(StringFormatUtils.formatInt(courseOfDayList.get(position).getPower(),context.getString(R.string.units_Wh)));
float energyCumulated=0; holder.energyCum.setText("\u03a3\u200a"+StringFormatUtils.formatEnergyCum(context, courseOfDayList.get(position).getEnergyCum()));
for (int i=0; i<=position;i++)
energyCumulated+=courseOfDayList.get(i).getPower();
holder.energyCum.setText(StringFormatUtils.formatInt(energyCumulated," "+ context.getString(R.string.units_Wh)));
updateRecyclerViewHeader(); //update header according to date in first visible item on the left updateRecyclerViewHeader(); //update header according to date in first visible item on the left
@ -133,7 +128,7 @@ public class CourseOfDayAdapter extends RecyclerView.Adapter<CourseOfDayAdapter.
HeaderTime.setTimeInMillis(courseOfDayList.get(visiblePosition).getLocalForecastTime(context)); HeaderTime.setTimeInMillis(courseOfDayList.get(visiblePosition).getLocalForecastTime(context));
int headerday = HeaderTime.get(Calendar.DAY_OF_WEEK); int headerday = HeaderTime.get(Calendar.DAY_OF_WEEK);
headerday = StringFormatUtils.getDayLong(headerday); headerday = StringFormatUtils.getDayLong(headerday);
recyclerViewHeader.setText(context.getResources().getString(headerday)); recyclerViewHeader.setText(context.getResources().getString(headerday) + " (" + StringFormatUtils.formatDate(courseOfDayList.get(visiblePosition).getLocalForecastTime(context)) + ")");
courseOfDayHeaderDate=HeaderTime.getTime(); courseOfDayHeaderDate=HeaderTime.getTime();
@ -146,7 +141,8 @@ public class CourseOfDayAdapter extends RecyclerView.Adapter<CourseOfDayAdapter.
@Override @Override
public int getItemCount() { public int getItemCount() {
return courseOfDayList.size(); if (courseOfDayList==null) return 0;
else return courseOfDayList.size();
} }
class CourseOfDayViewHolder extends RecyclerView.ViewHolder { class CourseOfDayViewHolder extends RecyclerView.ViewHolder {

View file

@ -23,7 +23,6 @@ public class ItemViewHolder extends RecyclerView.ViewHolder {
public TextView cellsMaxPower; public TextView cellsMaxPower;
public TextView cellsArea; public TextView cellsArea;
public TextView cellsEfficiency; public TextView cellsEfficiency;
public TextView diffuseEfficiency;
public TextView inverterPowerLimit; public TextView inverterPowerLimit;
public TextView inverterEfficiency; public TextView inverterEfficiency;
@ -41,7 +40,6 @@ public class ItemViewHolder extends RecyclerView.ViewHolder {
this.cellsMaxPower = (TextView) itemView.findViewById(R.id.city_cells_max_power); this.cellsMaxPower = (TextView) itemView.findViewById(R.id.city_cells_max_power);
this.cellsArea = (TextView) itemView.findViewById(R.id.city_cells_area); this.cellsArea = (TextView) itemView.findViewById(R.id.city_cells_area);
this.cellsEfficiency = (TextView) itemView.findViewById(R.id.city_cells_efficiency); this.cellsEfficiency = (TextView) itemView.findViewById(R.id.city_cells_efficiency);
this.diffuseEfficiency = (TextView) itemView.findViewById(R.id.city_diffuse_efficiency);
this.inverterPowerLimit = (TextView) itemView.findViewById(R.id.city_inverter_power_limit); this.inverterPowerLimit = (TextView) itemView.findViewById(R.id.city_inverter_power_limit);
this.inverterEfficiency = (TextView) itemView.findViewById(R.id.city_inverter_efficiency); this.inverterEfficiency = (TextView) itemView.findViewById(R.id.city_inverter_efficiency);

View file

@ -8,6 +8,8 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import com.google.android.material.snackbar.Snackbar;
import org.woheller69.weather.R; import org.woheller69.weather.R;
import org.woheller69.weather.database.CityToWatch; import org.woheller69.weather.database.CityToWatch;
import org.woheller69.weather.database.SQLiteHelper; import org.woheller69.weather.database.SQLiteHelper;
@ -28,6 +30,7 @@ public class RecyclerOverviewListAdapter extends RecyclerView.Adapter<ItemViewHo
*/ */
private Context context; private Context context;
private final List<CityToWatch> cities; private final List<CityToWatch> cities;
private RecyclerView rv;
SQLiteHelper database; SQLiteHelper database;
@ -49,6 +52,7 @@ public class RecyclerOverviewListAdapter extends RecyclerView.Adapter<ItemViewHo
@Override @Override
public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_city_list, parent, false); View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_city_list, parent, false);
rv = (RecyclerView) parent;
return new ItemViewHolder(view); return new ItemViewHolder(view);
} }
@ -64,7 +68,6 @@ public class RecyclerOverviewListAdapter extends RecyclerView.Adapter<ItemViewHo
holder.cellsMaxPower.setText(context.getString(R.string.edit_location_hint_cells_max_power) +": "+ cities.get(position).getCellsMaxPower()); holder.cellsMaxPower.setText(context.getString(R.string.edit_location_hint_cells_max_power) +": "+ cities.get(position).getCellsMaxPower());
holder.cellsEfficiency.setText(context.getString(R.string.edit_location_hint_cells_efficiency) +": "+ cities.get(position).getCellsEfficiency()); holder.cellsEfficiency.setText(context.getString(R.string.edit_location_hint_cells_efficiency) +": "+ cities.get(position).getCellsEfficiency());
holder.cellsArea.setText(context.getString(R.string.edit_location_hint_cells_area) +": "+ cities.get(position).getCellsArea()); holder.cellsArea.setText(context.getString(R.string.edit_location_hint_cells_area) +": "+ cities.get(position).getCellsArea());
holder.diffuseEfficiency.setText(context.getString(R.string.edit_location_hint_diffuse_efficiency) +": "+ cities.get(position).getDiffuseEfficiency());
holder.inverterPowerLimit.setText(context.getString(R.string.edit_location_hint_inverter_power_limit) +": "+ cities.get(position).getInverterPowerLimit()); holder.inverterPowerLimit.setText(context.getString(R.string.edit_location_hint_inverter_power_limit) +": "+ cities.get(position).getInverterPowerLimit());
holder.inverterEfficiency.setText(context.getString(R.string.edit_location_hint_inverter_efficiency) +": "+ cities.get(position).getInverterEfficiency()); holder.inverterEfficiency.setText(context.getString(R.string.edit_location_hint_inverter_efficiency) +": "+ cities.get(position).getInverterEfficiency());
@ -89,6 +92,17 @@ public class RecyclerOverviewListAdapter extends RecyclerView.Adapter<ItemViewHo
database.deleteCityToWatch(city); database.deleteCityToWatch(city);
cities.remove(position); cities.remove(position);
notifyItemRemoved(position); notifyItemRemoved(position);
Snackbar.make(rv,context.getString(R.string.itemRemoved,city.getCityName()),Snackbar.LENGTH_LONG).setAction(context.getString(R.string.undo), new View.OnClickListener() {
@Override
public void onClick(View view) {
long id=database.addCityToWatch(city);
city.setId((int) id);
city.setCityId((int) id); //use id also instead of city id as unique identifier
cities.add(position,city);
notifyItemInserted(position);
}
}).show();
} }
/** /**
@ -114,7 +128,7 @@ public class RecyclerOverviewListAdapter extends RecyclerView.Adapter<ItemViewHo
public CityToWatch getCitytoWatch(int position){ public CityToWatch getCitytoWatch(int position){
return cities.get(position); return cities.get(position);
} }
public void updateCity(CityToWatch cityToWatch, String cityName, float latitude, float longitude, float azimuth, float tilt, float cellsMaxPower, float cellsArea, float cellsEfficiency, float diffuseEfficiency, float inverterPowerLimit, float inverterEfficiency, int[] shadingElevation, int[] shadingOpacity) { public void updateCity(CityToWatch cityToWatch, String cityName, float latitude, float longitude, float azimuth, float tilt, float cellsMaxPower, float cellsArea, float cellsEfficiency, float cellsTempCoeff, float diffuseEfficiency, float albedo, float inverterPowerLimit, float inverterEfficiency, boolean isCentralInverter, int[] shadingElevation, int[] shadingOpacity) {
cityToWatch.setCityName(cityName); cityToWatch.setCityName(cityName);
cityToWatch.setLatitude(latitude); cityToWatch.setLatitude(latitude);
cityToWatch.setLongitude(longitude); cityToWatch.setLongitude(longitude);
@ -123,9 +137,12 @@ public class RecyclerOverviewListAdapter extends RecyclerView.Adapter<ItemViewHo
cityToWatch.setCellsMaxPower(cellsMaxPower); cityToWatch.setCellsMaxPower(cellsMaxPower);
cityToWatch.setCellsArea(cellsArea); cityToWatch.setCellsArea(cellsArea);
cityToWatch.setCellsEfficiency(cellsEfficiency); cityToWatch.setCellsEfficiency(cellsEfficiency);
cityToWatch.setCellsTempCoeff(cellsTempCoeff);
cityToWatch.setDiffuseEfficiency(diffuseEfficiency); cityToWatch.setDiffuseEfficiency(diffuseEfficiency);
cityToWatch.setAlbedo(albedo);
cityToWatch.setInverterPowerLimit(inverterPowerLimit); cityToWatch.setInverterPowerLimit(inverterPowerLimit);
cityToWatch.setInverterEfficiency(inverterEfficiency); cityToWatch.setInverterEfficiency(inverterEfficiency);
cityToWatch.setIsCentralInverter(isCentralInverter);
cityToWatch.setShadingElevation(shadingElevation); cityToWatch.setShadingElevation(shadingElevation);
cityToWatch.setShadingOpacity(shadingOpacity); cityToWatch.setShadingOpacity(shadingOpacity);
database.updateCityToWatch(cityToWatch); database.updateCityToWatch(cityToWatch);

View file

@ -14,11 +14,13 @@ import android.widget.TextView;
import org.woheller69.weather.R; import org.woheller69.weather.R;
import org.woheller69.weather.database.GeneralData; import org.woheller69.weather.database.GeneralData;
import org.woheller69.weather.database.SQLiteHelper; import org.woheller69.weather.database.SQLiteHelper;
import org.woheller69.weather.database.WeekForecast;
import org.woheller69.weather.ui.Help.StringFormatUtils; import org.woheller69.weather.ui.Help.StringFormatUtils;
import org.woheller69.weather.ui.UiResourceProvider; import org.woheller69.weather.ui.UiResourceProvider;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.List;
import java.util.TimeZone; import java.util.TimeZone;
/** /**
@ -28,16 +30,16 @@ import java.util.TimeZone;
public class WeekWeatherAdapter extends RecyclerView.Adapter<WeekWeatherAdapter.WeekForecastViewHolder> { public class WeekWeatherAdapter extends RecyclerView.Adapter<WeekWeatherAdapter.WeekForecastViewHolder> {
private Context context; private Context context;
private float[][] forecastData; private List<WeekForecast> weekForecastList;
private int cityID; private int cityID;
private Date courseOfDayHeaderDate; private Date courseOfDayHeaderDate;
WeekWeatherAdapter(Context context, float[][] forecastData, int cityID) { WeekWeatherAdapter(Context context, List<WeekForecast> weekForecastList, int cityID) {
this.context = context; this.context = context;
this.cityID = cityID; this.cityID = cityID;
this.forecastData = forecastData; this.weekForecastList = weekForecastList;
if (forecastData!=null && forecastData.length!=0 && forecastData[0]!=null) { if (weekForecastList!=null && !weekForecastList.isEmpty()) {
this.courseOfDayHeaderDate = new Date((long) forecastData[0][8]); //init with date of first weekday this.courseOfDayHeaderDate = new Date(weekForecastList.get(0).getLocalForecastTime(context)); //init with date of first weekday
} else this.courseOfDayHeaderDate = new Date(); //fallback if no data available } else this.courseOfDayHeaderDate = new Date(); //fallback if no data available
} }
@ -65,15 +67,14 @@ public class WeekWeatherAdapter extends RecyclerView.Adapter<WeekWeatherAdapter.
@Override @Override
public void onBindViewHolder(WeekForecastViewHolder holder, int position) { public void onBindViewHolder(WeekForecastViewHolder holder, int position) {
float[] dayValues = forecastData[position]; WeekForecast weekForecast = weekForecastList.get(position);
if (dayValues.length!=11) return; //Fixes app crash if forecastData not yet ready.
SQLiteHelper dbHelper = SQLiteHelper.getInstance(context); SQLiteHelper dbHelper = SQLiteHelper.getInstance(context);
GeneralData generalData = dbHelper.getGeneralDataByCityId(cityID); GeneralData generalData = dbHelper.getGeneralDataByCityId(cityID);
Calendar forecastTime = Calendar.getInstance(); Calendar forecastTime = Calendar.getInstance();
forecastTime.setTimeZone(TimeZone.getTimeZone("GMT")); forecastTime.setTimeZone(TimeZone.getTimeZone("GMT"));
forecastTime.setTimeInMillis((long) dayValues[8]); forecastTime.setTimeInMillis(weekForecast.getLocalForecastTime(context));
boolean isDay; boolean isDay;
@ -87,15 +88,15 @@ public class WeekWeatherAdapter extends RecyclerView.Adapter<WeekWeatherAdapter.
isDay = true; isDay = true;
} }
setIcon((int) dayValues[9], holder.weather, isDay); setIcon(weekForecast.getWeatherID(), holder.weather, isDay);
if (dayValues[4] == 0) if (weekForecast.getEnergyDay() == 0)
holder.power.setText("-"); holder.power.setText("-");
else else
holder.power.setText(StringFormatUtils.formatDecimal(dayValues[4], context.getString(R.string.units_kWh))); holder.power.setText(StringFormatUtils.formatDecimal(weekForecast.getEnergyDay(), context.getString(R.string.units_kWh)));
Calendar c = Calendar.getInstance(); Calendar c = Calendar.getInstance();
c.setTimeZone(TimeZone.getTimeZone("GMT")); c.setTimeZone(TimeZone.getTimeZone("GMT"));
c.setTimeInMillis((long) dayValues[8]); c.setTimeInMillis(weekForecast.getLocalForecastTime(context));
int day = c.get(Calendar.DAY_OF_WEEK); int day = c.get(Calendar.DAY_OF_WEEK);
holder.day.setText(StringFormatUtils.getDayShort(day)); holder.day.setText(StringFormatUtils.getDayShort(day));
@ -113,8 +114,8 @@ public class WeekWeatherAdapter extends RecyclerView.Adapter<WeekWeatherAdapter.
@Override @Override
public int getItemCount() { public int getItemCount() {
if (forecastData!=null) if (weekForecastList!=null && !weekForecastList.isEmpty())
return forecastData.length; return weekForecastList.size();
else else
return 0; return 0;
} }

View file

@ -3,6 +3,7 @@ package org.woheller69.weather.ui;
import static org.woheller69.weather.ui.RecycleList.CityWeatherAdapter.CHART; import static org.woheller69.weather.ui.RecycleList.CityWeatherAdapter.CHART;
import static org.woheller69.weather.ui.RecycleList.CityWeatherAdapter.DAY; import static org.woheller69.weather.ui.RecycleList.CityWeatherAdapter.DAY;
import static org.woheller69.weather.ui.RecycleList.CityWeatherAdapter.EMPTY; import static org.woheller69.weather.ui.RecycleList.CityWeatherAdapter.EMPTY;
import static org.woheller69.weather.ui.RecycleList.CityWeatherAdapter.OVERVIEW;
import static org.woheller69.weather.ui.RecycleList.CityWeatherAdapter.WEEK; import static org.woheller69.weather.ui.RecycleList.CityWeatherAdapter.WEEK;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
@ -38,7 +39,7 @@ public class WeatherCityFragment extends Fragment implements IUpdateableCityUI {
private int mCityId = -1; private int mCityId = -1;
private int[] mDataSetTypes = new int[]{}; private int[] mDataSetTypes = new int[]{};
private static int[] mFull = {DAY, WEEK, CHART}; //TODO Make dynamic from Settings private static int[] mFull = {OVERVIEW, DAY, WEEK, CHART}; //TODO Make dynamic from Settings
private static int[] mEmpty = {EMPTY}; private static int[] mEmpty = {EMPTY};
private CityWeatherAdapter mAdapter; private CityWeatherAdapter mAdapter;
@ -128,19 +129,10 @@ public class WeatherCityFragment extends Fragment implements IUpdateableCityUI {
} }
@Override @Override
public void processNewForecasts(List<HourlyForecast> hourlyForecasts) { public void processNewForecasts(List<HourlyForecast> hourlyForecasts, List<WeekForecast> weekForecasts) {
if (hourlyForecasts != null && hourlyForecasts.size() > 0 && hourlyForecasts.get(0).getCity_id() == mCityId) { if (hourlyForecasts != null && hourlyForecasts.size() > 0 && hourlyForecasts.get(0).getCity_id() == mCityId) {
if (mAdapter != null) { if (mAdapter != null) {
mAdapter.updateForecastData(hourlyForecasts); mAdapter.updateForecastData(hourlyForecasts, weekForecasts);
}
}
}
@Override
public void processNewWeekForecasts(List<WeekForecast> forecasts) {
if (forecasts != null && forecasts.size() > 0 && forecasts.get(0).getCity_id() == mCityId) {
if (mAdapter != null) {
mAdapter.updateWeekForecastData(forecasts);
} }
} }
} }

View file

@ -12,7 +12,6 @@ import java.util.List;
public interface IUpdateableCityUI { public interface IUpdateableCityUI {
void processNewGeneralData(GeneralData data); void processNewGeneralData(GeneralData data);
void processNewForecasts(List<HourlyForecast> hourlyForecasts); void processNewForecasts(List<HourlyForecast> hourlyForecasts, List<WeekForecast> weekForecasts);
void processNewWeekForecasts(List<WeekForecast> forecasts);
} }

View file

@ -31,17 +31,11 @@ public class ViewUpdater {
} }
} }
public static void updateWeekForecasts(List<WeekForecast> forecasts) {
ArrayList<IUpdateableCityUI> subcopy = new ArrayList<>(subscribers);
for (IUpdateableCityUI sub : subcopy) {
sub.processNewWeekForecasts(forecasts);
}
}
public static void updateForecasts(List<HourlyForecast> hourlyForecasts) { public static void updateForecasts(List<HourlyForecast> hourlyForecasts, List<WeekForecast> weekForecasts) {
ArrayList<IUpdateableCityUI> subcopy = new ArrayList<>(subscribers); ArrayList<IUpdateableCityUI> subcopy = new ArrayList<>(subscribers);
for (IUpdateableCityUI sub : subcopy) { for (IUpdateableCityUI sub : subcopy) {
sub.processNewForecasts(hourlyForecasts); sub.processNewForecasts(hourlyForecasts, weekForecasts);
} }
} }
} }

View file

@ -17,6 +17,7 @@ import org.woheller69.weather.services.UpdateDataService;
import org.woheller69.weather.ui.WeatherCityFragment; import org.woheller69.weather.ui.WeatherCityFragment;
import org.woheller69.weather.ui.updater.IUpdateableCityUI; import org.woheller69.weather.ui.updater.IUpdateableCityUI;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -35,18 +36,41 @@ public class WeatherPagerAdapter extends FragmentStateAdapter implements IUpdate
private List<CityToWatch> cities; private List<CityToWatch> cities;
private boolean showSum;
public WeatherPagerAdapter(Context context, @NonNull FragmentManager supportFragmentManager, @NonNull Lifecycle lifecycle) { public WeatherPagerAdapter(Context context, @NonNull FragmentManager supportFragmentManager, @NonNull Lifecycle lifecycle, Boolean showSum) {
super(supportFragmentManager,lifecycle); super(supportFragmentManager,lifecycle);
this.mContext = context; this.mContext = context;
this.database = SQLiteHelper.getInstance(context); this.database = SQLiteHelper.getInstance(context);
this.showSum = showSum;
loadCities(); loadCities();
} }
public void loadCities() { public void loadCities() {
this.cities = database.getAllCitiesToWatch(); List<CityToWatch> allCities = database.getAllCitiesToWatch();
Collections.sort(cities, (o1, o2) -> o1.getRank() - o2.getRank()); Collections.sort(allCities, (o1, o2) -> o1.getRank() - o2.getRank());
if (showSum && !allCities.isEmpty()) {
List<CityToWatch> citiesFiltered = new ArrayList<>();
citiesFiltered.add(allCities.get(0)); //always add first element
if (allCities.size() > 0) { //if there is more than one element
for (int i = 1; i < allCities.size(); i++) {
boolean alreadyHasLatLon = false;
for (int j = 0; j < citiesFiltered.size(); j++) {
if (citiesFiltered.get(j).getLatitude() == allCities.get(i).getLatitude() && citiesFiltered.get(j).getLongitude() == allCities.get(i).getLongitude()) {
alreadyHasLatLon = true;
}
}
if (!alreadyHasLatLon) {
citiesFiltered.add(allCities.get(i));
}
}
}
this.cities = citiesFiltered;
} else {
this.cities = allCities;
}
} }
@NonNull @NonNull
@ -64,7 +88,19 @@ public class WeatherPagerAdapter extends FragmentStateAdapter implements IUpdate
} }
public CharSequence getPageTitle(int position) { public CharSequence getPageTitle(int position) {
return cities.get(position).getCityName(); String title;
if (showSum){
String name = cities.get(position).getCityName();
if (name.contains("|")){
String[] name2 = name.split("\\|");
title = name2[0];
} else {
title = cities.get(position).getCityName();
}
} else {
title = cities.get(position).getCityName();
}
return title;
} }
public static void refreshSingleData(Context context, Boolean asap, int cityId) { public static void refreshSingleData(Context context, Boolean asap, int cityId) {
@ -82,14 +118,10 @@ public class WeatherPagerAdapter extends FragmentStateAdapter implements IUpdate
} }
@Override @Override
public void processNewForecasts(List<HourlyForecast> hourlyForecasts) { public void processNewForecasts(List<HourlyForecast> hourlyForecasts, List<WeekForecast> weekForecasts) {
//empty because Fragments are subscribers themselves //empty because Fragments are subscribers themselves
} }
@Override
public void processNewWeekForecasts(List<WeekForecast> forecasts) {
//empty because Fragments are subscribers themselves
}
public int getCityIDForPos(int pos) { public int getCityIDForPos(int pos) {
CityToWatch city = cities.get(pos); CityToWatch city = cities.get(pos);
@ -106,14 +138,4 @@ public class WeatherPagerAdapter extends FragmentStateAdapter implements IUpdate
return -1; //item not found return -1; //item not found
} }
public float getLatForPos(int pos) {
CityToWatch city = cities.get(pos);
return city.getLatitude();
}
public float getLonForPos(int pos) {
CityToWatch city = cities.get(pos);
return city.getLongitude();
}
} }

View file

@ -66,25 +66,30 @@ public class OMDataExtractor implements IDataExtractor {
JSONObject jsonData = new JSONObject(data); JSONObject jsonData = new JSONObject(data);
JSONArray timeArray = jsonData.getJSONArray("time"); JSONArray timeArray = jsonData.getJSONArray("time");
JSONArray weathercodeArray = jsonData.has("weathercode") ? jsonData.getJSONArray("weathercode") : null; JSONArray weathercodeArray = jsonData.has("weathercode") ? jsonData.getJSONArray("weathercode") : null;
JSONArray tempArray = jsonData.has("temperature_2m") ? jsonData.getJSONArray("temperature_2m") : null;
JSONArray directRadiationArray = jsonData.has("direct_normal_irradiance") ? jsonData.getJSONArray("direct_normal_irradiance") : null; JSONArray directRadiationArray = jsonData.has("direct_normal_irradiance") ? jsonData.getJSONArray("direct_normal_irradiance") : null;
JSONArray diffuseRadiationArray = jsonData.has("diffuse_radiation") ? jsonData.getJSONArray("diffuse_radiation") : null; JSONArray diffuseRadiationArray = jsonData.has("diffuse_radiation") ? jsonData.getJSONArray("diffuse_radiation") : null;
JSONArray shortwaveRadiationArray = jsonData.has("shortwave_radiation") ? jsonData.getJSONArray("shortwave_radiation") : null;
//TODO get Data for power plant from city to Watch //TODO get Data for power plant from city to Watch
SQLiteHelper dbhelper = SQLiteHelper.getInstance(context); SQLiteHelper dbhelper = SQLiteHelper.getInstance(context);
CityToWatch city = dbhelper.getCityToWatch(cityID); CityToWatch city = dbhelper.getCityToWatch(cityID);
SolarPowerPlant spp = new SolarPowerPlant(city.getLatitude(), city.getLongitude(), city.getCellsMaxPower(), city.getCellsArea(), city.getCellsEfficiency(),city.getDiffuseEfficiency(), city.getInverterPowerLimit(), city.getInverterEfficiency(), city.getAzimuthAngle(), city.getTiltAngle(), city.getShadingElevation(), city.getShadingOpacity()); SolarPowerPlant spp = new SolarPowerPlant(city.getLatitude(), city.getLongitude(), city.getCellsMaxPower(), city.getCellsArea(), city.getCellsEfficiency(), city.getCellsTempCoeff(), city.getDiffuseEfficiency(), city.getInverterPowerLimit(), city.getInverterEfficiency(), city.isCentralInverter(), city.getAzimuthAngle(), city.getTiltAngle(), city.getShadingElevation(), city.getShadingOpacity(), city.getAlbedo());
IApiToDatabaseConversion conversion = new OMToDatabaseConversion(); IApiToDatabaseConversion conversion = new OMToDatabaseConversion();
double ambientTemperature = 25.0;
for (int i = 0; i < timeArray.length(); i++) { for (int i = 0; i < timeArray.length(); i++) {
HourlyForecast hourlyForecast = new HourlyForecast(); HourlyForecast hourlyForecast = new HourlyForecast();
hourlyForecast.setTimestamp(System.currentTimeMillis() / 1000); hourlyForecast.setTimestamp(System.currentTimeMillis() / 1000);
if (timeArray!=null && !timeArray.isNull(i)) hourlyForecast.setForecastTime(timeArray.getLong(i)*1000L); if (timeArray!=null && !timeArray.isNull(i)) hourlyForecast.setForecastTime(timeArray.getLong(i)*1000L);
if (tempArray != null && !tempArray.isNull(i)) ambientTemperature = tempArray.getDouble(i);
if (weathercodeArray!=null && !weathercodeArray.isNull(i)) hourlyForecast.setWeatherID(conversion.convertWeatherCategory(weathercodeArray.getString(i))); if (weathercodeArray!=null && !weathercodeArray.isNull(i)) hourlyForecast.setWeatherID(conversion.convertWeatherCategory(weathercodeArray.getString(i)));
if (directRadiationArray!=null && !directRadiationArray.isNull(i)) hourlyForecast.setDirectRadiationNormal((float) directRadiationArray.getDouble(i)); if (directRadiationArray!=null && !directRadiationArray.isNull(i)) hourlyForecast.setDirectRadiationNormal((float) directRadiationArray.getDouble(i));
if (diffuseRadiationArray!=null && !diffuseRadiationArray.isNull(i)) hourlyForecast.setDiffuseRadiation((float) diffuseRadiationArray.getDouble(i)); if (diffuseRadiationArray!=null && !diffuseRadiationArray.isNull(i)) hourlyForecast.setDiffuseRadiation((float) diffuseRadiationArray.getDouble(i));
hourlyForecast.setPower(spp.getPower(hourlyForecast.getDirectRadiationNormal(),hourlyForecast.getDiffuseRadiation(), timeArray.getLong(i)-1800)); //use solar position 1/2h earlier for calculation of average power in preceding hour if (shortwaveRadiationArray!=null && !shortwaveRadiationArray.isNull(i)) hourlyForecast.setShortwaveRadiation((float) shortwaveRadiationArray.getDouble(i));
hourlyForecast.setPower(spp.getPower(hourlyForecast.getDirectRadiationNormal(), hourlyForecast.getDiffuseRadiation(), hourlyForecast.getShortwaveRadiation(), timeArray.getLong(i)-1800, ambientTemperature)); //use solar position 1/2h earlier for calculation of average power in preceding hour
hourlyForecasts.add(hourlyForecast); hourlyForecasts.add(hourlyForecast);
} }
return hourlyForecasts; return hourlyForecasts;

View file

@ -19,13 +19,12 @@ import java.util.List;
public class OMHttpRequest { public class OMHttpRequest {
protected String getUrlForQueryingOMweatherAPI(Context context, float lat, float lon) { protected String getUrlForQueryingOMweatherAPI(Context context, float lat, float lon) {
AppPreferencesManager prefManager =
new AppPreferencesManager(PreferenceManager.getDefaultSharedPreferences(context));
SharedPreferences sharedPreferences=PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences sharedPreferences=PreferenceManager.getDefaultSharedPreferences(context);
return String.format( return String.format(
"%sforecast?latitude=%s&longitude=%s&forecast_days=%s&hourly=diffuse_radiation,direct_normal_irradiance,weathercode&daily=weathercode,sunrise,sunset,&timeformat=unixtime&timezone=auto", "%sforecast?latitude=%s&longitude=%s&forecast_days=%s&hourly=temperature_2m,diffuse_radiation,direct_normal_irradiance,shortwave_radiation,weathercode&daily=weathercode,sunrise,sunset,&timeformat=unixtime&timezone=auto",
BuildConfig.BASE_URL, sharedPreferences.getString("pref_OM_URL", BuildConfig.BASE_URL),
lat, lat,
lon, lon,
sharedPreferences.getInt("pref_number_days",7) sharedPreferences.getInt("pref_number_days",7)

View file

@ -1,15 +1,18 @@
package org.woheller69.weather.weather_api.open_meteo; package org.woheller69.weather.weather_api.open_meteo;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences;
import android.os.Handler; import android.os.Handler;
import android.widget.Toast; import android.widget.Toast;
import androidx.preference.PreferenceManager;
import com.android.volley.VolleyError; import com.android.volley.VolleyError;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.woheller69.weather.R; import org.woheller69.weather.R;
import org.woheller69.weather.activities.NavigationActivity; import org.woheller69.weather.activities.NavigationActivity;
import org.woheller69.weather.database.CityToWatch;
import org.woheller69.weather.database.GeneralData; import org.woheller69.weather.database.GeneralData;
import org.woheller69.weather.database.HourlyForecast; import org.woheller69.weather.database.HourlyForecast;
import org.woheller69.weather.database.WeekForecast; import org.woheller69.weather.database.WeekForecast;
@ -62,66 +65,87 @@ public class ProcessOMweatherAPIRequest implements IProcessHttpRequest {
public void processSuccessScenario(String response, int cityId) { public void processSuccessScenario(String response, int cityId) {
IDataExtractor extractor = new OMDataExtractor(context); IDataExtractor extractor = new OMDataExtractor(context);
try {
JSONObject json = new JSONObject(response);
//Extract daily weather
dbHelper.deleteWeekForecastsByCityId(cityId);
List<WeekForecast> weekforecasts = new ArrayList<>();
weekforecasts = extractor.extractWeekForecast(json.getString("daily"));
if (weekforecasts!=null && !weekforecasts.isEmpty()){ ArrayList<Integer> CityIDList = new ArrayList<Integer>();
for (WeekForecast weekForecast: weekforecasts){
weekForecast.setCity_id(cityId); SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
if (sharedPreferences.getBoolean("pref_summarize",false)){
List<CityToWatch> citiesToWatch = dbHelper.getAllCitiesToWatch();
CityToWatch requestedCity = dbHelper.getCityToWatch(cityId);
for (int i = 0; i < citiesToWatch.size(); i++) {
CityToWatch city = citiesToWatch.get(i);
if (city.getCityId()!=requestedCity.getCityId() && city.getLatitude() == requestedCity.getLatitude() && city.getLongitude() == requestedCity.getLongitude()) {
CityIDList.add(city.getCityId());
} }
} else {
final String ERROR_MSG = context.getResources().getString(R.string.error_convert_to_json);
if (NavigationActivity.isVisible)
Toast.makeText(context, ERROR_MSG, Toast.LENGTH_LONG).show();
return;
} }
}
//Extract current weather CityIDList.add(cityId); //add requested city at end of list. Call Viewupdater if last (requested) city is updated
GeneralData generalData = new GeneralData();
generalData.setTimestamp(System.currentTimeMillis() / 1000);
generalData.setCity_id(cityId);
generalData.setTimeSunrise(weekforecasts.get(0).getTimeSunrise());
generalData.setTimeSunset(weekforecasts.get(0).getTimeSunset());
generalData.setTimeZoneSeconds(json.getInt("utc_offset_seconds"));
GeneralData current = dbHelper.getGeneralDataByCityId(cityId);
if (current != null && current.getCity_id() == cityId) {
dbHelper.updateGeneralData(generalData);
} else {
dbHelper.addGeneralData(generalData);
}
//Extract hourly weather for (int c=0; c<CityIDList.size();c++) {
dbHelper.deleteForecastsByCityId(cityId); cityId = CityIDList.get(c);
List<HourlyForecast> hourlyforecasts = new ArrayList<>();
hourlyforecasts = extractor.extractHourlyForecast(json.getString("hourly"), cityId);
if (hourlyforecasts!=null && !hourlyforecasts.isEmpty()){ try {
for (HourlyForecast hourlyForecast: hourlyforecasts){ JSONObject json = new JSONObject(response);
hourlyForecast.setCity_id(cityId);
//Extract daily weather
dbHelper.deleteWeekForecastsByCityId(cityId);
List<WeekForecast> weekforecasts = new ArrayList<>();
weekforecasts = extractor.extractWeekForecast(json.getString("daily"));
if (weekforecasts != null && !weekforecasts.isEmpty()) {
for (WeekForecast weekForecast : weekforecasts) {
weekForecast.setCity_id(cityId);
}
} else {
final String ERROR_MSG = context.getResources().getString(R.string.error_convert_to_json);
if (NavigationActivity.isVisible)
Toast.makeText(context, ERROR_MSG, Toast.LENGTH_LONG).show();
return;
} }
} else {
final String ERROR_MSG = context.getResources().getString(R.string.error_convert_to_json); //Extract current weather
if (NavigationActivity.isVisible) GeneralData generalData = new GeneralData();
Toast.makeText(context, ERROR_MSG, Toast.LENGTH_LONG).show(); generalData.setTimestamp(System.currentTimeMillis() / 1000);
return; generalData.setCity_id(cityId);
generalData.setTimeSunrise(weekforecasts.get(0).getTimeSunrise());
generalData.setTimeSunset(weekforecasts.get(0).getTimeSunset());
generalData.setTimeZoneSeconds(json.getInt("utc_offset_seconds"));
GeneralData current = dbHelper.getGeneralDataByCityId(cityId);
if (current != null && current.getCity_id() == cityId) {
dbHelper.updateGeneralData(generalData);
} else {
dbHelper.addGeneralData(generalData);
}
//Extract hourly weather
dbHelper.deleteForecastsByCityId(cityId);
List<HourlyForecast> hourlyforecasts = new ArrayList<>();
hourlyforecasts = extractor.extractHourlyForecast(json.getString("hourly"), cityId);
if (hourlyforecasts != null && !hourlyforecasts.isEmpty()) {
for (HourlyForecast hourlyForecast : hourlyforecasts) {
hourlyForecast.setCity_id(cityId);
}
} else {
final String ERROR_MSG = context.getResources().getString(R.string.error_convert_to_json);
if (NavigationActivity.isVisible)
Toast.makeText(context, ERROR_MSG, Toast.LENGTH_LONG).show();
return;
}
dbHelper.addForecasts(hourlyforecasts);
weekforecasts = reanalyzeWeekIDs(weekforecasts, hourlyforecasts);
dbHelper.addWeekForecasts(weekforecasts);
if (c == CityIDList.size()-1) ViewUpdater.updateGeneralDataData(generalData); // Call Viewupdater if last (requested) city is updated
if (c == CityIDList.size()-1) ViewUpdater.updateForecasts(hourlyforecasts, weekforecasts);
} catch (JSONException e) {
e.printStackTrace();
} }
dbHelper.addForecasts(hourlyforecasts);
weekforecasts = reanalyzeWeekIDs(weekforecasts, hourlyforecasts);
dbHelper.addWeekForecasts(weekforecasts);
ViewUpdater.updateGeneralDataData(generalData);
ViewUpdater.updateWeekForecasts(weekforecasts);
ViewUpdater.updateForecasts(hourlyforecasts);
} catch (JSONException e) {
e.printStackTrace();
} }
} }
@ -169,17 +193,6 @@ public class ProcessOMweatherAPIRequest implements IProcessHttpRequest {
} }
} }
for (WeekForecast weekForecast: weekforecasts){
float totalEnergy = 0;
Long timeNoon = weekForecast.getForecastTime();
for (HourlyForecast hourlyForecast: hourlyforecasts){
if ((hourlyForecast.getForecastTime()>=timeNoon-12*3600*1000L) && (hourlyForecast.getForecastTime()< timeNoon + 12*3600*1000L)){
totalEnergy+=hourlyForecast.getPower();
}
}
weekForecast.setEnergyDay(totalEnergy/1000);
}
return weekforecasts; return weekforecasts;
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Before After
Before After

View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<solid android:color="@color/middlegrey" />
<corners android:radius="6dp" />
</shape>
</item>
</selector>

View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<solid android:color="@color/blue" />
<corners android:radius="6dp" />
</shape>
</item>
</selector>

View file

@ -13,14 +13,14 @@
android:strokeColor="#00000000" android:strokeColor="#00000000"
android:fillType="evenOdd"/> android:fillType="evenOdd"/>
<path <path
android:pathData="M3409,9504L1600,6178 4257,5943 6067,9270 3409,9504Z" android:pathData="M3410,9505L1600,6178 4258,5943 6067,9270 3410,9505Z"
android:strokeLineJoin="round" android:strokeLineJoin="round"
android:strokeWidth="28.222" android:strokeWidth="28.222"
android:fillColor="#192072" android:fillColor="#192072"
android:strokeColor="#00000000" android:strokeColor="#00000000"
android:fillType="evenOdd"/> android:fillType="evenOdd"/>
<path <path
android:pathData="M6331,9246L4521,5920 7178,5686 8987,9012 6331,9246Z" android:pathData="M6331,9246L4521,5920 7178,5686 8988,9012 6331,9246Z"
android:strokeLineJoin="round" android:strokeLineJoin="round"
android:strokeWidth="28.222" android:strokeWidth="28.222"
android:fillColor="#192072" android:fillColor="#192072"
@ -34,21 +34,21 @@
android:strokeColor="#00000000" android:strokeColor="#00000000"
android:fillType="evenOdd"/> android:fillType="evenOdd"/>
<path <path
android:pathData="M5241,13000L3431,9674 6088,9439 7897,12766 5241,13000Z" android:pathData="M5351,13078L3541,9752 6198,9517 8008,12844 5351,13078Z"
android:strokeLineJoin="round" android:strokeLineJoin="round"
android:strokeWidth="28.222" android:strokeWidth="28.222"
android:fillColor="#192072" android:fillColor="#192072"
android:strokeColor="#00000000" android:strokeColor="#00000000"
android:fillType="evenOdd"/> android:fillType="evenOdd"/>
<path <path
android:pathData="M8270,12810L6460,9484 9117,9250 10926,12576 8270,12810Z" android:pathData="M8270,12811L6460,9484 9116,9250 10926,12575 8270,12811Z"
android:strokeLineJoin="round" android:strokeLineJoin="round"
android:strokeWidth="28.222" android:strokeWidth="28.222"
android:fillColor="#192072" android:fillColor="#192072"
android:strokeColor="#00000000" android:strokeColor="#00000000"
android:fillType="evenOdd"/> android:fillType="evenOdd"/>
<path <path
android:pathData="M11193,12552L9384,9226 12040,8991 13850,12318 11193,12552Z" android:pathData="M11193,12553L9384,9226 12040,8991 13850,12318 11193,12553Z"
android:strokeLineJoin="round" android:strokeLineJoin="round"
android:strokeWidth="28.222" android:strokeWidth="28.222"
android:fillColor="#192072" android:fillColor="#192072"
@ -61,6 +61,13 @@
android:fillColor="#FFD550" android:fillColor="#FFD550"
android:strokeColor="#00000000" android:strokeColor="#00000000"
android:fillType="evenOdd"/> android:fillType="evenOdd"/>
<path
android:pathData="M6295,6466L7622,6937 7622,5994 6295,6466Z"
android:strokeLineJoin="round"
android:strokeWidth="212"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:fillType="evenOdd"/>
<path <path
android:pathData="M7251,8776L8523,8171 7856,7504 7251,8776Z" android:pathData="M7251,8776L8523,8171 7856,7504 7251,8776Z"
android:strokeLineJoin="round" android:strokeLineJoin="round"
@ -68,6 +75,13 @@
android:fillColor="#FFD550" android:fillColor="#FFD550"
android:strokeColor="#00000000" android:strokeColor="#00000000"
android:fillType="evenOdd"/> android:fillType="evenOdd"/>
<path
android:pathData="M7251,8776L8523,8171 7856,7504 7251,8776Z"
android:strokeLineJoin="round"
android:strokeWidth="212"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:fillType="evenOdd"/>
<path <path
android:pathData="M9562,9733L10033,8405 9090,8405 9562,9733Z" android:pathData="M9562,9733L10033,8405 9090,8405 9562,9733Z"
android:strokeLineJoin="round" android:strokeLineJoin="round"
@ -75,6 +89,13 @@
android:fillColor="#FFD550" android:fillColor="#FFD550"
android:strokeColor="#00000000" android:strokeColor="#00000000"
android:fillType="evenOdd"/> android:fillType="evenOdd"/>
<path
android:pathData="M9562,9733L10033,8405 9090,8405 9562,9733Z"
android:strokeLineJoin="round"
android:strokeWidth="212"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:fillType="evenOdd"/>
<path <path
android:pathData="M11872,8776L11267,7504 10600,8171 11872,8776Z" android:pathData="M11872,8776L11267,7504 10600,8171 11872,8776Z"
android:strokeLineJoin="round" android:strokeLineJoin="round"
@ -82,6 +103,13 @@
android:fillColor="#FFD550" android:fillColor="#FFD550"
android:strokeColor="#00000000" android:strokeColor="#00000000"
android:fillType="evenOdd"/> android:fillType="evenOdd"/>
<path
android:pathData="M11872,8776L11267,7504 10600,8171 11872,8776Z"
android:strokeLineJoin="round"
android:strokeWidth="212"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:fillType="evenOdd"/>
<path <path
android:pathData="M12829,6466L11501,5994 11501,6937 12829,6466Z" android:pathData="M12829,6466L11501,5994 11501,6937 12829,6466Z"
android:strokeLineJoin="round" android:strokeLineJoin="round"
@ -89,6 +117,13 @@
android:fillColor="#FFD550" android:fillColor="#FFD550"
android:strokeColor="#00000000" android:strokeColor="#00000000"
android:fillType="evenOdd"/> android:fillType="evenOdd"/>
<path
android:pathData="M12829,6466L11501,5994 11501,6937 12829,6466Z"
android:strokeLineJoin="round"
android:strokeWidth="212"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:fillType="evenOdd"/>
<path <path
android:pathData="M11872,4155L10600,4760 11267,5427 11872,4155Z" android:pathData="M11872,4155L10600,4760 11267,5427 11872,4155Z"
android:strokeLineJoin="round" android:strokeLineJoin="round"
@ -96,6 +131,13 @@
android:fillColor="#FFD550" android:fillColor="#FFD550"
android:strokeColor="#00000000" android:strokeColor="#00000000"
android:fillType="evenOdd"/> android:fillType="evenOdd"/>
<path
android:pathData="M11872,4155L10600,4760 11267,5427 11872,4155Z"
android:strokeLineJoin="round"
android:strokeWidth="212"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:fillType="evenOdd"/>
<path <path
android:pathData="M9562,3199L9090,4526 10033,4526 9562,3199Z" android:pathData="M9562,3199L9090,4526 10033,4526 9562,3199Z"
android:strokeLineJoin="round" android:strokeLineJoin="round"
@ -103,6 +145,13 @@
android:fillColor="#FFD550" android:fillColor="#FFD550"
android:strokeColor="#00000000" android:strokeColor="#00000000"
android:fillType="evenOdd"/> android:fillType="evenOdd"/>
<path
android:pathData="M9562,3199L9090,4526 10033,4526 9562,3199Z"
android:strokeLineJoin="round"
android:strokeWidth="212"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:fillType="evenOdd"/>
<path <path
android:pathData="M7251,4155L7856,5427 8523,4760 7251,4155Z" android:pathData="M7251,4155L7856,5427 8523,4760 7251,4155Z"
android:strokeLineJoin="round" android:strokeLineJoin="round"
@ -110,6 +159,13 @@
android:fillColor="#FFD550" android:fillColor="#FFD550"
android:strokeColor="#00000000" android:strokeColor="#00000000"
android:fillType="evenOdd"/> android:fillType="evenOdd"/>
<path
android:pathData="M7251,4155L7856,5427 8523,4760 7251,4155Z"
android:strokeLineJoin="round"
android:strokeWidth="212"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:fillType="evenOdd"/>
<path <path
android:pathData="M11196,6466C11196,6753 11120,7034 10977,7283 10833,7531 10627,7737 10379,7881 10130,8024 9849,8100 9562,8100 9275,8100 8994,8024 8745,7881 8497,7737 8291,7531 8147,7283 8004,7034 7929,6753 7929,6466 7929,6179 8004,5898 8147,5649 8291,5401 8497,5195 8745,5051 8994,4908 9275,4833 9562,4833 9849,4832 10130,4908 10379,5051 10627,5195 10833,5401 10977,5649 11120,5898 11196,6179 11196,6466L11196,6466Z" android:pathData="M11196,6466C11196,6753 11120,7034 10977,7283 10833,7531 10627,7737 10379,7881 10130,8024 9849,8100 9562,8100 9275,8100 8994,8024 8745,7881 8497,7737 8291,7531 8147,7283 8004,7034 7929,6753 7929,6466 7929,6179 8004,5898 8147,5649 8291,5401 8497,5195 8745,5051 8994,4908 9275,4833 9562,4833 9849,4832 10130,4908 10379,5051 10627,5195 10833,5401 10977,5649 11120,5898 11196,6179 11196,6466L11196,6466Z"
android:strokeLineJoin="round" android:strokeLineJoin="round"
@ -117,5 +173,12 @@
android:fillColor="#FFD550" android:fillColor="#FFD550"
android:strokeColor="#00000000" android:strokeColor="#00000000"
android:fillType="evenOdd"/> android:fillType="evenOdd"/>
<path
android:pathData="M11196,6466C11196,6753 11120,7034 10977,7283 10833,7531 10627,7737 10379,7881 10130,8024 9849,8100 9562,8100 9275,8100 8994,8024 8745,7881 8497,7737 8291,7531 8147,7283 8004,7034 7929,6753 7929,6466 7929,6179 8004,5898 8147,5649 8291,5401 8497,5195 8745,5051 8994,4908 9275,4833 9562,4833 9849,4832 10130,4908 10379,5051 10627,5195 10833,5401 10977,5649 11120,5898 11196,6179 11196,6466L11196,6466Z"
android:strokeLineJoin="round"
android:strokeWidth="212"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:fillType="evenOdd"/>
</group> </group>
</vector> </vector>

View file

@ -0,0 +1,48 @@
<vector android:height="108dp" android:viewportHeight="16001"
android:viewportWidth="16001" android:width="108dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillType="evenOdd"
android:pathData="M8000,16000L0,16000 0,0 16000,0 16000,16000 8000,16000Z"
android:strokeColor="#00000000" android:strokeLineJoin="round" android:strokeWidth="28.222"/>
<path android:fillColor="#00000000" android:fillType="evenOdd"
android:pathData="M7318,11319L6502,11400 5276,9110 6909,8949 8135,11239 7318,11319Z"
android:strokeColor="#192072" android:strokeLineJoin="round" android:strokeWidth="159"/>
<path android:fillColor="#00000000" android:fillType="evenOdd"
android:pathData="M9294,11116L8477,11196 7250,8905 8884,8745 10111,11036 9294,11116Z"
android:strokeColor="#192072" android:strokeLineJoin="round" android:strokeWidth="159"/>
<path android:fillColor="#00000000" android:fillType="evenOdd"
android:pathData="M11267,10936L10451,11017 9225,8727 10858,8566 12084,10856 11267,10936Z"
android:strokeColor="#192072" android:strokeLineJoin="round" android:strokeWidth="159"/>
<path android:fillColor="#00000000" android:fillType="evenOdd"
android:pathData="M5958,8734L5141,8814 3914,6523 5547,6363 6775,8654 5958,8734Z"
android:strokeColor="#192072" android:strokeLineJoin="round" android:strokeWidth="159"/>
<path android:fillColor="#00000000" android:fillType="evenOdd"
android:pathData="M7933,8529L7116,8610 5889,6319 7523,6158 8750,8449 7933,8529Z"
android:strokeColor="#192072" android:strokeLineJoin="round" android:strokeWidth="159"/>
<path android:fillColor="#00000000" android:fillType="evenOdd"
android:pathData="M7006,7084L8012,7442 8012,6726 7006,7084Z"
android:strokeColor="#192072" android:strokeLineJoin="round" android:strokeWidth="282"/>
<path android:fillColor="#00000000" android:fillType="evenOdd"
android:pathData="M7730,8837C8052,8684 8373,8530 8695,8378L8189,7872C8037,8194 7883,8515 7730,8837Z"
android:strokeColor="#192072" android:strokeLineJoin="round" android:strokeWidth="282"/>
<path android:fillColor="#00000000" android:fillType="evenOdd"
android:pathData="M9483,9562C9602,9227 9721,8890 9841,8555L9125,8555C9244,8890 9364,9227 9483,9562Z"
android:strokeColor="#192072" android:strokeLineJoin="round" android:strokeWidth="282"/>
<path android:fillColor="#00000000" android:fillType="evenOdd"
android:pathData="M11237,8837C11084,8515 10930,8194 10778,7872L10272,8378C10594,8530 10915,8684 11237,8837Z"
android:strokeColor="#192072" android:strokeLineJoin="round" android:strokeWidth="282"/>
<path android:fillColor="#00000000" android:fillType="evenOdd"
android:pathData="M11962,7084C11627,6965 11290,6846 10955,6726L10955,7442C11290,7322 11627,7203 11962,7084Z"
android:strokeColor="#192072" android:strokeLineJoin="round" android:strokeWidth="282"/>
<path android:fillColor="#00000000" android:fillType="evenOdd"
android:pathData="M11237,5331C10915,5484 10594,5638 10272,5790L10778,6296C10930,5974 11084,5653 11237,5331Z"
android:strokeColor="#192072" android:strokeLineJoin="round" android:strokeWidth="282"/>
<path android:fillColor="#00000000" android:fillType="evenOdd"
android:pathData="M9483,4606L9125,5612 9841,5612 9483,4606Z"
android:strokeColor="#192072" android:strokeLineJoin="round" android:strokeWidth="282"/>
<path android:fillColor="#00000000" android:fillType="evenOdd"
android:pathData="M7730,5331C7883,5653 8037,5974 8189,6296L8695,5790C8373,5638 8052,5484 7730,5331Z"
android:strokeColor="#192072" android:strokeLineJoin="round" android:strokeWidth="282"/>
<path android:fillColor="#00000000" android:fillType="evenOdd"
android:pathData="M10723,7084C10723,7301 10666,7515 10557,7703 10449,7892 10292,8049 10104,8157 9915,8266 9701,8323 9484,8323 9267,8323 9053,8266 8865,8157 8676,8049 8520,7892 8411,7703 8302,7515 8245,7301 8245,7084 8245,6867 8302,6653 8411,6465 8520,6276 8676,6120 8865,6011 9053,5902 9267,5845 9484,5845 9701,5845 9915,5902 10104,6011 10292,6120 10449,6276 10557,6465 10666,6653 10723,6867 10723,7084Z"
android:strokeColor="#192072" android:strokeLineJoin="round" android:strokeWidth="282"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#FF000000"
android:pathData="M480,560q-33,0 -56.5,-23.5T400,480q0,-33 23.5,-56.5T480,400q33,0 56.5,23.5T560,480q0,33 -23.5,56.5T480,560ZM480,840q-139,0 -241,-91.5T122,520h82q14,104 92.5,172T480,760q117,0 198.5,-81.5T760,480q0,-117 -81.5,-198.5T480,200q-69,0 -129,32t-101,88h110v80L120,400L120,160h80v94q51,-64 124.5,-99T480,120q75,0 140.5,28.5t114,77q48.5,48.5 77,114T840,480q0,75 -28.5,140.5t-77,114q-48.5,48.5 -114,77T480,840Z"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M440,200L440,40h80v160h-80ZM706,310 L651,255 763,140 819,197 706,310ZM760,520v-80h160v80L760,520ZM440,920L440,760h80v160h-80ZM254,308 L140,197l57,-56 113,113 -56,54ZM762,820L651,705l54,-54 114,110 -57,59ZM40,520v-80h160v80L40,520ZM197,820 L141,763 253,651 282,678 311,706 197,820ZM480,720q-100,0 -170,-70t-70,-170q0,-100 70,-170t170,-70q100,0 170,70t70,170q0,100 -70,170t-170,70ZM480,640q66,0 113,-47t47,-113q0,-66 -47,-113t-113,-47q-66,0 -113,47t-47,113q0,66 47,113t113,47ZM480,480Z"/>
</vector>

View file

@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,5.99L19.53,19H4.47L12,5.99M12,2L1,21h22L12,2L12,2z"
android:fillColor="@color/orange"/>
<path
android:pathData="M13,16l-2,0l0,2l2,0z"
android:fillColor="@color/orange"/>
<path
android:pathData="M13,10l-2,0l0,5l2,0z"
android:fillColor="@color/orange"/>
</vector>

View file

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M6.76,4.84l-1.8,-1.79 -1.41,1.41 1.79,1.79 1.42,-1.41zM4,10.5L1,10.5v2h3v-2zM13,0.55h-2L11,3.5h2L13,0.55zM20.45,4.46l-1.41,-1.41 -1.79,1.79 1.41,1.41 1.79,-1.79zM17.24,18.16l1.79,1.8 1.41,-1.41 -1.8,-1.79 -1.4,1.4zM20,10.5v2h3v-2h-3zM12,5.5c-3.31,0 -6,2.69 -6,6s2.69,6 6,6 6,-2.69 6,-6 -2.69,-6 -6,-6zM11,22.45h2L13,19.5h-2v2.95zM3.55,18.54l1.41,1.41 1.79,-1.8 -1.41,-1.41 -1.79,1.8z"/>
</vector>

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"> <shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#AAFFFFFF" /> <solid android:color="@color/lightgrey" />
<padding <padding
android:left="3dp" android:left="3dp"

View file

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#AAFFFFFF" />
<stroke android:width="0dp"
android:color="#AAFFFFFF"
/>
<padding
android:bottom="-3dp"
android:top="-3dp"
android:left="5dp"
android:right="5dp" />
<corners android:radius="7dp" />
</shape>

View file

@ -14,7 +14,6 @@
android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin" android:paddingTop="@dimen/activity_vertical_margin"
android:layout_marginTop="53dp"
android:weightSum="1"> android:weightSum="1">
<TextView <TextView

View file

@ -5,7 +5,6 @@
android:id="@+id/drawer_layout" android:id="@+id/drawer_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start"> tools:openDrawer="start">
<androidx.coordinatorlayout.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout
@ -13,10 +12,15 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:fitsSystemWindows="true" android:fitsSystemWindows="true"
tools:context=".activities.AboutActivity"> tools:context=".activities.AboutActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/toolbar" /> <include layout="@layout/toolbar" />
<include layout="@layout/about" /> <include layout="@layout/about" />
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:openDrawer="start">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".activities.BackupRestoreActivity">
<include layout="@layout/toolbar" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:orientation="vertical">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/backup_database"
android:layout_marginBottom="20dp"
android:onClick="performBackup"
/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/restore_database"
android:onClick="performRestore"
/>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="@layout/nav_header_main"
app:menu="@menu/activity_main_drawer" />
</androidx.drawerlayout.widget.DrawerLayout>

View file

@ -5,7 +5,6 @@
android:id="@+id/drawer_layout" android:id="@+id/drawer_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start"> tools:openDrawer="start">
<androidx.coordinatorlayout.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout

View file

@ -5,7 +5,6 @@
android:id="@+id/drawer_layout" android:id="@+id/drawer_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start"> tools:openDrawer="start">
<androidx.coordinatorlayout.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout
@ -14,14 +13,20 @@
android:fitsSystemWindows="true" android:fitsSystemWindows="true"
tools:context=".activities.HelpActivity"> tools:context=".activities.HelpActivity">
<include layout="@layout/toolbar" /> <LinearLayout
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/help"
android:layout_marginTop="53dp"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="fill_parent" /> android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/toolbar" />
<WebView
android:id="@+id/help"
android:padding="10dp"
android:layout_width="match_parent"
android:layout_height="fill_parent" />
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.navigation.NavigationView <com.google.android.material.navigation.NavigationView

View file

@ -5,7 +5,6 @@
android:id="@+id/drawer_layout" android:id="@+id/drawer_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start"> tools:openDrawer="start">
<include <include

View file

@ -1,40 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.RainViewerActivity"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/backgroundBlue"
android:orientation="horizontal">
<ImageButton
android:id="@+id/rainviewer_prev"
android:layout_width="wrap_content"
android:layout_weight="1"
android:src="@drawable/ic_skip_previous_24px"
android:layout_height="wrap_content" />
<ImageButton
android:id="@+id/rainviewer_startstop"
android:layout_width="wrap_content"
android:layout_weight="3"
android:src="@drawable/ic_playpause"
android:layout_height="wrap_content" />
<ImageButton
android:id="@+id/rainviewer_next"
android:layout_width="wrap_content"
android:layout_weight="1"
android:src="@drawable/ic_skip_next_24px"
android:layout_height="wrap_content" />
</LinearLayout>
<WebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="fill_parent" />
</LinearLayout>

View file

@ -5,7 +5,6 @@
android:id="@+id/drawer_layout" android:id="@+id/drawer_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start"> tools:openDrawer="start">
<androidx.coordinatorlayout.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout

View file

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:fitsSystemWindows="true">
<androidx.viewpager.widget.ViewPager <androidx.viewpager.widget.ViewPager

View file

@ -27,7 +27,7 @@
android:textStyle="bold" /> android:textStyle="bold" />
<TextView <TextView
android:id="@+id/graph_precipitationunit" android:id="@+id/graph_energyunit"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
@ -48,11 +48,12 @@
android:background="@color/white" /> android:background="@color/white" />
<com.db.chart.view.BarChartView <com.db.chart.view.BarChartView
android:id="@+id/graph_precipitation" android:id="@+id/graph_energy"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="200dp" android:layout_height="200dp"
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:layout_marginStart="10dp"
android:layout_below="@+id/spacer" android:layout_below="@+id/spacer"
chart:chart_axisBorderSpacing="8dp" /> chart:chart_axisBorderSpacing="8dp" />
</RelativeLayout> </RelativeLayout>

View file

@ -17,7 +17,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="10dp" android:layout_margin="10dp"
android:gravity="center" android:gravity="center"
android:text="@string/card_day_heading"
android:textAllCaps="true" android:textAllCaps="true"
android:textColor="@color/colorPrimaryDark" android:textColor="@color/colorPrimaryDark"
android:textStyle="bold" /> android:textStyle="bold" />

View file

@ -20,7 +20,6 @@
android:textAllCaps="true" android:textAllCaps="true"
android:textColor="@color/colorPrimaryDark" android:textColor="@color/colorPrimaryDark"
android:textStyle="bold" android:textStyle="bold"
android:text="@string/card_details_heading"
android:id="@+id/card_details_title"/> android:id="@+id/card_details_title"/>
<View <View
@ -44,7 +43,6 @@
android:layout_column="0" android:layout_column="0"
android:layout_marginBottom="5dp" android:layout_marginBottom="5dp"
android:layout_row="0" android:layout_row="0"
android:text="@string/card_details_rain60min"
android:textAppearance="?android:attr/textAppearanceMedium" android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="@color/colorPrimaryDark" /> android:textColor="@color/colorPrimaryDark" />
@ -79,7 +77,6 @@
android:layout_column="0" android:layout_column="0"
android:layout_marginBottom="5dp" android:layout_marginBottom="5dp"
android:layout_row="2" android:layout_row="2"
android:text="@string/card_details_humidity"
android:textAppearance="?android:attr/textAppearanceMedium" android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="@color/colorPrimaryDark" /> android:textColor="@color/colorPrimaryDark" />
@ -101,7 +98,6 @@
android:layout_column="0" android:layout_column="0"
android:layout_marginBottom="5dp" android:layout_marginBottom="5dp"
android:layout_row="3" android:layout_row="3"
android:text="@string/card_details_pressure"
android:textAppearance="?android:attr/textAppearanceMedium" android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="@color/colorPrimaryDark" /> android:textColor="@color/colorPrimaryDark" />
@ -123,7 +119,6 @@
android:layout_column="0" android:layout_column="0"
android:layout_marginBottom="5dp" android:layout_marginBottom="5dp"
android:layout_row="4" android:layout_row="4"
android:text="@string/card_details_wind_speed"
android:textAppearance="?android:attr/textAppearanceMedium" android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="@color/colorPrimaryDark" /> android:textColor="@color/colorPrimaryDark" />

View file

@ -5,40 +5,38 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="@dimen/card_margin"> android:layout_margin="@dimen/card_margin">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#fafafa"
android:layout_marginTop="5dp"
android:layout_marginStart="7dp"
android:textSize="12dp"
android:text="Weather data by Open-Meteo.com" />
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:layout_gravity="center_vertical"> android:layout_gravity="center_vertical"
android:background="@color/backgroundBlue">
<TextView <TextView
android:id="@+id/card_overview_temperature" android:id="@+id/card_overview_header"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true" android:layout_marginStart="10dp"
android:layout_centerVertical="true" android:layout_marginTop="10dp"
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:background="@drawable/rounded_corner" android:layout_marginBottom="10dp"
android:paddingLeft="7dp" android:gravity="center"
android:paddingRight="7dp" android:text="@string/card_today_heading"
android:textAllCaps="true"
android:textColor="@color/colorPrimaryDark" android:textColor="@color/colorPrimaryDark"
android:textSize="45dp" /> android:textStyle="bold" />
<View
android:id="@+id/card_overview_header_spacer"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_below="@id/card_overview_header"
android:background="@color/white" />
<TextView <TextView
android:id="@+id/card_overview_update_time" android:id="@+id/card_overview_update_time"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentTop="true" android:layout_alignBaseline="@id/card_overview_header"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:paddingLeft="7dp" android:paddingLeft="7dp"
@ -46,18 +44,77 @@
android:textColor="#fafafa" android:textColor="#fafafa"
android:textSize="18dp" /> android:textSize="18dp" />
<TextView
android:id="@+id/card_overview_sunrise_sunset"
android:layout_alignBaseline="@id/card_overview_remaining_today"
android:layout_alignParentEnd="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15dp"
android:textColor="@color/colorPrimaryDark"
android:layout_marginEnd="10dp"
android:paddingRight="7dp"
android:paddingLeft="7dp" />
<TextView
android:id="@+id/card_overview_credits"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/card_overview_sunrise_sunset"
android:layout_centerHorizontal="true"
android:textColor="#fafafa"
android:layout_marginStart="7dp"
android:textSize="12dp"
android:text="Weather data by Open-Meteo.com" />
<TextView
android:id="@+id/card_overview_produced_today_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/card_overview_header_spacer"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="@color/colorPrimaryDark"
android:layout_marginTop="5dp"
android:layout_marginStart="7dp"
android:text="@string/card_today_produced" />
<TextView
android:id="@+id/card_overview_produced_today"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/card_overview_header_spacer"
android:layout_toEndOf="@id/card_overview_produced_today_header"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="@color/colorPrimaryDark"
android:layout_marginTop="5dp"
android:layout_marginStart="7dp"
android:text="1500 Wh" />
<TextView
android:id="@+id/card_overview_remaining_today_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/card_overview_produced_today"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="@color/colorPrimaryDark"
android:layout_marginTop="5dp"
android:layout_marginStart="7dp"
android:text="@string/card_today_remaining" />
<TextView
android:id="@+id/card_overview_remaining_today"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/card_overview_produced_today"
android:layout_toEndOf="@id/card_overview_remaining_today_header"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="@color/colorPrimaryDark"
android:layout_marginTop="5dp"
android:layout_marginStart="7dp"
android:text="1500 Wh" />
</RelativeLayout> </RelativeLayout>
<TextView
android:id="@+id/card_overview_sunrise_sunset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:textSize="15dp"
android:textColor="@color/colorPrimaryDark"
android:background="@drawable/rounded_corner"
android:layout_marginEnd="10dp"
android:layout_marginBottom="5dp"
android:paddingRight="7dp"
android:paddingLeft="7dp" />
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>

View file

@ -17,10 +17,15 @@
android:imeOptions="actionDone" android:imeOptions="actionDone"
android:maxLines="1" android:maxLines="1"
android:inputType="text" /> android:inputType="text" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#aad3df"
android:gravity="center"
android:text="Search powered by by Open-Meteo.com"/>
<WebView <WebView
android:id="@+id/webViewAddLocation" android:id="@+id/webViewAddLocation"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="200dp" /> android:layout_height="200dp" />
</LinearLayout> </LinearLayout>

View file

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ScrollView <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -15,6 +15,7 @@
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textStyle="bold"
android:hint="@string/edit_location_hint_name"/> android:hint="@string/edit_location_hint_name"/>
<androidx.appcompat.widget.AppCompatEditText <androidx.appcompat.widget.AppCompatEditText
android:id="@+id/EditLocation_Name" android:id="@+id/EditLocation_Name"
@ -24,6 +25,7 @@
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textStyle="bold"
android:hint="@string/edit_location_hint_latitude"/> android:hint="@string/edit_location_hint_latitude"/>
<androidx.appcompat.widget.AppCompatEditText <androidx.appcompat.widget.AppCompatEditText
android:id="@+id/EditLocation_Lat" android:id="@+id/EditLocation_Lat"
@ -34,6 +36,7 @@
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textStyle="bold"
android:hint="@string/edit_location_hint_longitude"/> android:hint="@string/edit_location_hint_longitude"/>
<androidx.appcompat.widget.AppCompatEditText <androidx.appcompat.widget.AppCompatEditText
android:id="@+id/EditLocation_Lon" android:id="@+id/EditLocation_Lon"
@ -44,7 +47,22 @@
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textStyle="bold"
android:hint="@string/edit_location_hint_azimuth"/> android:hint="@string/edit_location_hint_azimuth"/>
<com.redinput.compassview.CompassView
android:id="@+id/EditLocation_Compass"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
app:backgroundColor="#FFFFFF"
app:showMarker="true"
app:markerColor="@color/colorAccent"
app:lineColor="#000000"
app:textColor="#000000"
app:textSize="12sp"
app:rangeDegrees="180.0"/>
<androidx.appcompat.widget.AppCompatEditText <androidx.appcompat.widget.AppCompatEditText
android:id="@+id/EditLocation_Azimuth" android:id="@+id/EditLocation_Azimuth"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -54,6 +72,7 @@
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textStyle="bold"
android:hint="@string/edit_location_hint_tilt"/> android:hint="@string/edit_location_hint_tilt"/>
<androidx.appcompat.widget.AppCompatEditText <androidx.appcompat.widget.AppCompatEditText
android:id="@+id/EditLocation_Tilt" android:id="@+id/EditLocation_Tilt"
@ -64,6 +83,7 @@
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textStyle="bold"
android:hint="@string/edit_location_hint_cells_max_power"/> android:hint="@string/edit_location_hint_cells_max_power"/>
<androidx.appcompat.widget.AppCompatEditText <androidx.appcompat.widget.AppCompatEditText
android:id="@+id/EditLocation_Cell_Max_Power" android:id="@+id/EditLocation_Cell_Max_Power"
@ -74,6 +94,7 @@
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textStyle="bold"
android:hint="@string/edit_location_hint_cells_area"/> android:hint="@string/edit_location_hint_cells_area"/>
<androidx.appcompat.widget.AppCompatEditText <androidx.appcompat.widget.AppCompatEditText
android:id="@+id/EditLocation_Cells_Area" android:id="@+id/EditLocation_Cells_Area"
@ -84,6 +105,7 @@
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textStyle="bold"
android:hint="@string/edit_location_hint_cells_efficiency"/> android:hint="@string/edit_location_hint_cells_efficiency"/>
<androidx.appcompat.widget.AppCompatEditText <androidx.appcompat.widget.AppCompatEditText
android:id="@+id/EditLocation_Cell_Efficiency" android:id="@+id/EditLocation_Cell_Efficiency"
@ -94,6 +116,18 @@
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textStyle="bold"
android:hint="@string/edit_location_hint_cells_temp_coeff"/>
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/EditLocation_Cell_Temp_Coeff"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="numberDecimal|numberSigned"
android:hint="@string/edit_location_hint_cells_temp_coeff"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textStyle="bold"
android:hint="@string/edit_location_hint_diffuse_efficiency"/> android:hint="@string/edit_location_hint_diffuse_efficiency"/>
<androidx.appcompat.widget.AppCompatEditText <androidx.appcompat.widget.AppCompatEditText
android:id="@+id/EditLocation_Diffuse_Efficiency" android:id="@+id/EditLocation_Diffuse_Efficiency"
@ -104,6 +138,29 @@
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textStyle="bold"
android:hint="@string/edit_location_hint_albedo"/>
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/EditLocation_Albedo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="numberDecimal"
android:hint="@string/edit_location_hint_albedo"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textStyle="bold"
android:hint="@string/edit_location_hint_inverter_efficiency"/>
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/EditLocation_Inverter_Efficiency"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="numberDecimal"
android:hint="@string/edit_location_hint_inverter_efficiency"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textStyle="bold"
android:hint="@string/edit_location_hint_inverter_power_limit"/> android:hint="@string/edit_location_hint_inverter_power_limit"/>
<androidx.appcompat.widget.AppCompatEditText <androidx.appcompat.widget.AppCompatEditText
android:id="@+id/EditLocation_Inverter_Power_Limit" android:id="@+id/EditLocation_Inverter_Power_Limit"
@ -114,14 +171,12 @@
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/edit_location_hint_inverter_efficiency"/> android:textStyle="bold"
<androidx.appcompat.widget.AppCompatEditText android:hint="@string/edit_location_hint_central_inverter"/>
android:id="@+id/EditLocation_Inverter_Efficiency" <CheckBox
android:id="@+id/EditLocation_Central_Inverter"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content" />
android:inputType="numberDecimal"
android:hint="@string/edit_location_hint_inverter_efficiency"/>
<LinearLayout <LinearLayout
android:id="@+id/edit_Location_shading" android:id="@+id/edit_Location_shading"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -131,9 +186,22 @@
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textStyle="bold"
android:text="@string/edit_location_shading_heading" android:text="@string/edit_location_shading_heading"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:layout_marginBottom="10dp"/> android:layout_marginBottom="10dp"/>
<TextView
android:paddingLeft="5px"
android:text="@string/action_sun_position"
android:textStyle="bold"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/edit_current_azi_ele"
android:paddingLeft="5px"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"/>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View file

@ -89,13 +89,12 @@
android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textAppearance="@style/TextAppearance.AppCompat.Medium"
tools:visibility="visible" /> tools:visibility="visible" />
<TextView <TextView
android:id="@+id/city_diffuse_efficiency" android:id="@+id/city_inverter_efficiency"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:text="@string/edit_location_hint_diffuse_efficiency" android:text="@string/edit_location_hint_inverter_efficiency"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textAppearance="@style/TextAppearance.AppCompat.Medium"
tools:visibility="visible" /> tools:visibility="visible" />
@ -108,14 +107,6 @@
android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textAppearance="@style/TextAppearance.AppCompat.Medium"
tools:visibility="visible" /> tools:visibility="visible" />
<TextView
android:id="@+id/city_inverter_efficiency"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/edit_location_hint_inverter_efficiency"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
tools:visibility="visible" />
</LinearLayout> </LinearLayout>

View file

@ -57,7 +57,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="3dp" android:layout_marginBottom="3dp"
android:text="xy Wh" android:text="\u03a3\u200axy Wh"
android:textAppearance="?android:attr/textAppearanceMedium" android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="@color/colorPrimaryDark" /> android:textColor="@color/colorPrimaryDark" />

View file

@ -5,7 +5,7 @@
android:id="@+id/menu_sun_position" android:id="@+id/menu_sun_position"
android:orderInCategory="80" android:orderInCategory="80"
android:visible="true" android:visible="true"
android:icon="@drawable/ic_wb_sunny_24px" android:icon="@drawable/ic_sunny_24px"
app:showAsAction="always" app:showAsAction="always"
android:title="@string/action_sun_position" /> android:title="@string/action_sun_position" />
<item <item

View file

@ -25,6 +25,10 @@
android:id="@+id/nav_help" android:id="@+id/nav_help"
android:icon="@drawable/ic_help_24px" android:icon="@drawable/ic_help_24px"
android:title="@string/activity_help" /> android:title="@string/activity_help" />
<item
android:id="@+id/nav_backuprestore"
android:icon="@drawable/ic_settings_backup_restore_24px"
android:title="@string/activity_backuprestore" />
<item <item
android:id="@+id/nav_about" android:id="@+id/nav_about"
android:icon="@drawable/ic_info_24px" android:icon="@drawable/ic_info_24px"
@ -32,6 +36,6 @@
<item <item
android:id="@+id/star_on_github" android:id="@+id/star_on_github"
android:icon="@drawable/baseline_star_rate_24" android:icon="@drawable/baseline_star_rate_24"
android:title="Star on GitHub" /> android:title="@string/github" />
</group> </group>
</menu> </menu>

View file

@ -2,4 +2,5 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/> <background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/> <foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
</adaptive-icon> </adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Before After
Before After

View file

@ -1,49 +1,33 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name">solXpect</string>
<string name="about">Über</string> <string name="about">Über</string>
<string name="about_privacy_heading">Privatsphäre-Informationen</string> <string name="about_privacy_heading">Privatsphäre-Informationen</string>
<string name="about_more_info">Mehr Informationen können gefunden werden auf:</string> <string name="about_more_info">Mehr Informationen können gefunden werden auf:</string>
<string name="about_license_text">Diese App ist abgeleitet von Privacy Friendly Weather, entwickelt von der Forschungsgruppe SECUSO. Quelltext lizenziert unter GPLv3. Die App benutzt Icons von Google Material Design Icons, lizenziert unter Apache License Version 2.0, die Leaflet Bibliothek, lizenziert unter 2-clause BSD License, AutoSuggestTextViewAPICall, lizenziert unter der Apache License Version 2.0, Solarpositioning (net.e175.klaus:solarpositioning), lizenziert unter MIT Lizenz und die WilliamChart Bibliothek (com.db.chart), lizenziert unter der Apache License Version 2.0</string> <string name="about_license_text">Diese App ist abgeleitet von Privacy Friendly Weather, entwickelt von der Forschungsgruppe SECUSO. Quelltext lizenziert unter GPLv3. Die App benutzt Icons von Google Material Design Icons, lizenziert unter Apache License Version 2.0, die Leaflet Bibliothek, lizenziert unter 2-clause BSD License, AutoSuggestTextViewAPICall, lizenziert unter der Apache License Version 2.0, Solarpositioning (net.e175.klaus:solarpositioning), lizenziert unter MIT Lizenz, Zip4j, lizenziert unter Apache License Version 2.0, CompassView (https://github.com/kix2902/CompassView), lizenziert unter Apache License Version 2.0 und die WilliamChart Bibliothek (com.db.chart), lizenziert unter der Apache License Version 2.0</string>
<string name="about_license">Lizenz</string> <string name="about_license">Lizenz</string>
<string name="about_privacy_answer">solXpect benötigt nur Zugang zum Internet. Es werden weder persönliche Daten gesammelt, noch wird Werbung angezeigt.</string> <string name="about_privacy_answer">solXpect benötigt nur Zugang zum Internet. Es werden weder persönliche Daten gesammelt, noch wird Werbung angezeigt.</string>
<string name="about_where_from">Woher kommen die Wetterinformationen?</string> <string name="about_where_from">Woher kommen die Wetterinformationen?</string>
<string name="about_where_from_answer">Die Wetterinformationen stammen von</string> <string name="about_where_from_answer">Die Wetterinformationen stammen von</string>
<string name="activity_about">Über</string> <string name="activity_about">Über</string>
<string name="version_number">Version</string>
<string name="activity_weather">Vorhersage</string> <string name="activity_weather">Vorhersage</string>
<string name="activity_manage">Orte verwalten</string> <string name="activity_manage">Orte verwalten</string>
<string name="activity_settings">Einstellungen</string> <string name="activity_settings">Einstellungen</string>
<string name="action_refresh">Aktualisieren</string> <string name="action_refresh">Aktualisieren</string>
<string name="activity_settings_title">Einstellungen</string> <string name="activity_settings_title">Einstellungen</string>
<string name="card_details_heading">Jetzt</string>
<string name="card_details_humidity">Luftfeuchte:</string>
<string name="card_details_pressure">Luftdruck:</string>
<string name="card_details_wind_speed">Wind:</string>
<string name="card_day_heading">Tagesverlauf</string>
<string name="card_week_heading">Woche</string> <string name="card_week_heading">Woche</string>
<string name="error_convert_to_json">Die erhaltenen Wetterdaten entsprachen nicht dem erwarteten Format.</string> <string name="error_convert_to_json">Die erhaltenen Wetterdaten entsprachen nicht dem erwarteten Format.</string>
<string name="error_no_internet">Das Gerät ist nicht mit dem Internet verbunden!</string> <string name="error_no_internet">Das Gerät ist nicht mit dem Internet verbunden!</string>
<string name="error_fetch_forecast">Es ist ein Fehler beim Abrufen der Wettervorhersage aufgetreten, bitte versuchen Sie es erneut!</string> <string name="error_fetch_forecast">Es ist ein Fehler beim Abrufen der Wettervorhersage aufgetreten, bitte versuchen Sie es erneut!</string>
<string name="error_no_city_selected">Sie haben keinen Ort ausgewählt. Wählen Sie \"Orte verwalten\" in der Navigation, um einen Ort auszuwählen.</string> <string name="error_no_city_selected">Sie haben keinen Ort ausgewählt. Wählen Sie \"Orte verwalten\" in der Navigation, um einen Ort auszuwählen.</string>
<string name="dialog_add_add_button">Hinzufügen</string> <string name="dialog_add_add_button">Hinzufügen</string>
<string name="dialog_add_close_button">Schließen</string>
<string name="dialog_add_label">Ort, der hinzugefügt werden soll:</string> <string name="dialog_add_label">Ort, der hinzugefügt werden soll:</string>
<string name="dialog_add_no_city_found">Es wurde kein Ort gefunden, der der Eingabe entspricht. Es wird empfohlen, einen Eintrag aus dem Dropdown-Menu zu wählen.</string> <string name="dialog_add_no_city_found">Es wurde kein Ort gefunden, der der Eingabe entspricht. Es wird empfohlen, einen Eintrag aus dem Dropdown-Menu zu wählen.</string>
<string name="navigation_drawer_close">Navigationsleiste schließen</string> <string name="navigation_drawer_close">Navigationsleiste schließen</string>
<string name="settings_title_units">Maßeinheiten</string>
<string name="settings_title_temperature">Temperaturen</string>
<string name="settings_title_distance">Entfernungen</string>
<string name="settings_celsius">Celsius</string>
<string name="settings_fahrenheit">Fahrenheit</string>
<string name="settings_kilometers">Kilometer</string>
<string name="settings_miles">Meilen</string>
<string name="settings_summary_distance">Die Einheit, die zur Anzeige von Entfernungen verwendet wird.</string>
<string name="settings_summary_temperature">Die Einheit, die zur Anzeige von Temperaturen verwendet wird.</string>
<string name="settings_interval_summary">Setzen Sie das Interval der automatischen Updates</string> <string name="settings_interval_summary">Setzen Sie das Interval der automatischen Updates</string>
<string name="settings_intervals">Intervalle</string> <string name="settings_intervals">Intervalle</string>
<string name="settings_update_interval">Update Intervall</string> <string name="settings_update_interval">Update Intervall</string>
<string name="settings_title_display_options">Anzeigeoptionen</string> <string name="settings_title_display_options">Anzeigeoptionen</string>
<string name="navigation_drawer_open">Navigationsleiste öffnen</string> <string name="navigation_drawer_open">Navigationsleiste öffnen</string>
<string name="abbreviation_friday">Fr.</string> <string name="abbreviation_friday">Fr.</string>
<string name="abbreviation_monday">Mo.</string> <string name="abbreviation_monday">Mo.</string>
@ -62,7 +46,6 @@
<string name="long_press_text">Halten und ziehen Sie, um die Orte zu sortieren.</string> <string name="long_press_text">Halten und ziehen Sie, um die Orte zu sortieren.</string>
<string name="swipe_to_delete">Wischen Sie zum Löschen</string> <string name="swipe_to_delete">Wischen Sie zum Löschen</string>
<string name="infoProvider">Die Wetterinformationen stammen von Open-Meteo.com</string> <string name="infoProvider">Die Wetterinformationen stammen von Open-Meteo.com</string>
<string name="monday">Montag</string> <string name="monday">Montag</string>
<string name="tuesday">Dienstag</string> <string name="tuesday">Dienstag</string>
<string name="wednesday">Mittwoch</string> <string name="wednesday">Mittwoch</string>
@ -71,34 +54,17 @@
<string name="saturday">Samstag</string> <string name="saturday">Samstag</string>
<string name="sunday">Sonntag</string> <string name="sunday">Sonntag</string>
<string name="chart">Diagramm</string> <string name="chart">Diagramm</string>
<string name="units_rh">%rh</string>
<string name="units_kWh">kWh</string> <string name="units_kWh">kWh</string>
<string name="units_km">km</string> <string name="dialog_edit_change_button">Speichern</string>
<string name="units_hPa">hPa</string>
<string name="units_Bft">Bft</string>
<string name="units_mm_h">mm/h</string>
<string name="units_km_h">km/h</string>
<string name="units_mph">mph</string>
<string name="card_details_rain60min">☔ 60min:</string>
<string name="error_no_rain60min_data">keine Daten</string>
<string name="dialog_edit_change_button">Ändern</string>
<string name="edit_location_hint_name">Name</string> <string name="edit_location_hint_name">Name</string>
<string name="settings_search">Suche</string> <string name="settings_search">Suche</string>
<string name="settings_temp_decimal">Temperatur mit einer Dezimalstelle anzeigen</string>
<string name="settings_darkmode">Android 10+ Dark Mode</string> <string name="settings_darkmode">Android 10+ Dark Mode</string>
<string name="settings_position">Position</string>
<string name="settings_GPS_summary">Erlaubt die Nutzung der aktuellen GPS Position im ersten TAB und im Widget für das aktuelle Wetter. Das Update der Position wird automatisch vom Widget ausgelöst.</string>
<string name="settings_GPS_position">GPS Nutzung erlauben</string>
<string name="error_no_position">Keine Position verfügbar</string>
<string name="settings_time24h">24-Stunden-Format</string> <string name="settings_time24h">24-Stunden-Format</string>
<string name="summary_time24h">Systemeinstellung überschreiben</string> <string name="summary_time24h">Systemeinstellung überschreiben</string>
<string name="settings_wind">Beaufort-Skala benutzen</string>
<string name="dialog_OK_button">OK</string> <string name="dialog_OK_button">OK</string>
<string name="dialog_NO_button">Nein</string> <string name="dialog_NO_button">Nein</string>
<string name="dialog_Later_button">Vielleicht später</string> <string name="dialog_Later_button">Vielleicht später</string>
<string name="dialog_StarOnGitHub">Mögen Sie diese App? Bitte vergeben Sie einen Stern auf GitHub und spendieren Sie dem Entwickler einen Kaffee über PayPal.</string> <string name="dialog_StarOnGitHub">Mögen Sie diese App? Bitte vergeben Sie einen Stern auf GitHub und spendieren Sie dem Entwickler einen Kaffee über PayPal.</string>
<string name="settings_show_pressure">Luftdruck anzeigen</string>
<string name="settings_GPS_manual">Nur manuell aktualisieren</string>
<string name="settings_interval_quarter">15 min</string> <string name="settings_interval_quarter">15 min</string>
<string name="settings_interval_half">30 min</string> <string name="settings_interval_half">30 min</string>
<string name="settings_interval_one">1 h</string> <string name="settings_interval_one">1 h</string>
@ -106,24 +72,18 @@
<string name="settings_interval_six">6 h</string> <string name="settings_interval_six">6 h</string>
<string name="settings_interval_twelve">12 h</string> <string name="settings_interval_twelve">12 h</string>
<string name="settings_interval_twentyfour">24 h</string> <string name="settings_interval_twentyfour">24 h</string>
<string name="rationale_background_location">Bitte erlauben Sie auch die GPS Nutzung im Hintergrund für die Verwendung im Widget</string>
<string name="error_no_gps">Bitte GPS einschalten</string>
<string name="slide3_heading">Open-Meteo</string> <string name="slide3_heading">Open-Meteo</string>
<string name="summary_reanalyze">Verbessern der Wochensymbole durch Analyse der stündlichen Vorhersagen zwischen Sonnenaufgang und Sonnenuntergang (experimentell).</string>
<string name="settings_reanalyze">Wochensymbole</string>
<string name="settings_precipitation">Niederschlag</string>
<string name="summary_precipitation">Schneemenge anstelle der entsprechenden Wassermenge verwenden.</string>
<string name="settings_forecast_days">Anzahl der prognostizierten Tage</string> <string name="settings_forecast_days">Anzahl der prognostizierten Tage</string>
<string name="edit_location_hint_latitude">Breitengrad [°]</string> <string name="edit_location_hint_latitude">Breitengrad [°]</string>
<string name="edit_location_hint_longitude">Längengrad [°]</string> <string name="edit_location_hint_longitude">Längengrad [°]</string>
<string name="edit_location_hint_azimuth">Azimut [°]</string> <string name="edit_location_hint_azimuth">Azimut [°]</string>
<string name="edit_location_hint_tilt">Neigung [°]</string> <string name="edit_location_hint_tilt">Neigung [°]</string>
<string name="edit_location_hint_cells_max_power">Maximalleistung Zelle [W]</string> <string name="edit_location_hint_cells_max_power">Peakleistung der Zellen [W]</string>
<string name="edit_location_hint_cells_efficiency">Wirkungsgrad Zelle [%]</string> <string name="edit_location_hint_cells_efficiency">Wirkungsgrad der Zellen [%]</string>
<string name="edit_location_hint_cells_area">Fläche Zelle [m\u00b2]</string> <string name="edit_location_hint_cells_area">Fläche der Zellen [m\u00b2]</string>
<string name="edit_location_hint_diffuse_efficiency">Effizienz diffuse Strahlung [%]</string> <string name="edit_location_hint_diffuse_efficiency">Effizienz diffuse Strahlung [%]</string>
<string name="edit_location_hint_inverter_power_limit">Leistung Wechselrichter [W]</string> <string name="edit_location_hint_inverter_power_limit">Wechselrichterleistung [W]</string>
<string name="edit_location_hint_inverter_efficiency">Effizienz Wechselrichter [%]</string> <string name="edit_location_hint_inverter_efficiency">Wechselrichtereffizienz [%]</string>
<string name="units_Wh">Wh</string> <string name="units_Wh">Wh</string>
<string name="edit_location_title">Ort bearbeiten</string> <string name="edit_location_title">Ort bearbeiten</string>
<string name="edit_location_shading_azimuth_heading">Azimut Bereich [°]</string> <string name="edit_location_shading_azimuth_heading">Azimut Bereich [°]</string>
@ -131,7 +91,26 @@
<string name="edit_location_shading_opacity_heading">Abschattung unterhalb dieser Elevation [%]</string> <string name="edit_location_shading_opacity_heading">Abschattung unterhalb dieser Elevation [%]</string>
<string name="edit_location_shading_heading">Abschattung</string> <string name="edit_location_shading_heading">Abschattung</string>
<string name="activity_help">Anleitung</string> <string name="activity_help">Anleitung</string>
<string name="action_sun_position">Sonnenposition</string> <string name="action_sun_position">Aktuelle Sonnenposition</string>
<string name="action_sun_elevation">Elevation [°]</string> <string name="action_sun_elevation">Elevation [°]</string>
<string name="edit_location_hint_cells_temp_coeff">Temperaturkoeffizient [%/K]</string>
<string name="activity_backuprestore">Sichern/Wiederherstellen</string>
<string name="backup_database">Datenbank sichern</string>
<string name="restore_database">Datenbank wiederherstellen</string>
<string name="permission_required">Berechtigung erforderlich</string>
<string name="permission_message">%s benötigt Zugriff auf den externen Speicher. Bitte die Berechtigung erteilen und erneut versuchen.</string>
<string name="toast_delete">Bitte die Datei zuerst löschen und erneut versuchen.</string>
<string name="restore_database_message">Daten aus dem Dokumentenordner wiederherstellen. Falls nötig, Ordner \'Documents\' öffnen und solXpect.zip auswählen!</string>
<string name="settings_summarize">Summe anzeigen</string>
<string name="summary_summarize">Summiere die Werte aller Module mit gleichem Längen- und Breitengrad.</string>
<string name="dialog_add_clone_button">Klonen</string>
<string name="itemRemoved">ENTFERNT:\u0020\u0020%s</string>
<string name="undo">WIEDERHERSTELLEN</string>
<string name="edit_location_hint_albedo">Albedo [0..1]</string>
<string name="settings_server_urls">Server URLs</string>
<string name="settings_server_summary">Nur ändern, wenn Sie Ihre eigenen Server betreiben wollen</string>
<string name="edit_location_hint_central_inverter">Zentralwechselrichter</string>
<string name="card_today_heading">Heute</string>
<string name="card_today_produced">Produziert:</string>
<string name="card_today_remaining">Verbleibend:</string>
</resources> </resources>

View file

@ -0,0 +1,117 @@
<resources>
<string name="activity_about">Info</string>
<string name="activity_settings">Impostazioni</string>
<string name="activity_weather">Previsione</string>
<string name="activity_manage">Gestisci luoghi</string>
<string name="action_refresh">Ricarica</string>
<string name="navigation_drawer_open">Apri pannello di navigazione</string>
<string name="navigation_drawer_close">Chiudi pannello di navigazione</string>
<string name="next">Prossimo</string>
<string name="okay">OK</string>
<string name="slide1_heading">Benvenuto!</string>
<string name="slide1_text">solXpect prevede la produzione del tuo impianto fotovoltaico</string>
<string name="slide2_heading" translatable="false">Github</string>
<string name="slide2_text">Il codice sorgente di questa app è disponibile su GitHub. Ulteriori dettagli si trovano nella pagina Info.</string>
<string name="card_week_heading">Settimana</string>
<string name="card_error_heading">Errore nel recuperare i dati meteo</string>
<string name="card_error_content">Prova a aggiornare!</string>
<string name="about_privacy_heading">Privacy Info</string>
<string name="about">Info</string>
<string name="version_number">Versione</string>
<string name="about_license">Licenza</string>
<string name="about_license_text">Questa applicazione è derivata da Privacy Friendly Weather, sviluppata dal gruppo di ricerca SECUSO. Il codice sorgente è rilasciato con licenza GPLv3. L\u0027app usa icone da Google Material Design Icons distruite con licenza Apache License Version 2.0, inoltre usa le seguenti librerie: Leaflet con licenza 2-clause BSD License, AutoSuggestTextViewAPICall con licenza Apache License Version 2.0, Solarpositioning (net.e175.klaus:solarpositioning) con licenza MIT License, Zip4j (https://github.com/srikanth-lingala/zip4j) con licenza Apache License Version 2.0, CompassView (https://github.com/kix2902/CompassView) con licenza Apache License Version 2.0, and WilliamChart library (com.db.chart) con licenza Apache License Version 2.0</string>
<string name="about_more_info">Altre informazioni si possono trovare su:</string>
<string name="about_github" translatable="false"><a href="https://github.com/woheller69/solxpect">Github-Repo</a></string>
<string name="about_openmeteo" translatable="false"><a href="https://open-meteo.com">Open-Meteo \n(Attribution 4.0 International CC BY 4.0)</a></string>
<string name="activity_settings_title">Impostazioni</string>
<string name="abbreviation_monday">Lu.</string>
<string name="abbreviation_tuesday">Ma.</string>
<string name="abbreviation_wednesday">Me.</string>
<string name="abbreviation_thursday">Gio.</string>
<string name="abbreviation_friday">Ve.</string>
<string name="abbreviation_saturday">Sa.</string>
<string name="abbreviation_sunday">Do.</string>
<string name="dialog_add_label">Inserisci il luogo da aggiungere:</string>
<string name="dialog_add_no_city_found">Nessun luogo corrisponde al testo immesso. Si raccommanda di selezionare uno degli elementi dalla lista.</string>
<string name="dialog_add_add_button">Aggiungi</string>
<string name="error_convert_to_json">I dati meteo scaricati hanno un formato errato.</string>
<string name="error_no_internet">Il dispositivo non è collegato a internet</string>
<string name="error_fetch_forecast">Errore nell\u0027aggiornare le previsioni, riprova!</string>
<string name="error_no_city_selected">Nessun luogo selezionato. Per scegliere un luog va su \"Gestisci luoghi\".</string>
<string name="settings_interval_quarter">15 min</string>
<string name="settings_interval_half">30 min</string>
<string name="settings_interval_one">1 h</string>
<string name="settings_interval_two">2 h</string>
<string name="settings_interval_six">6 h</string>
<string name="settings_interval_twelve">12 h</string>
<string name="settings_interval_twentyfour">24 h</string>
<string name="settings_intervals">Intervalli</string>
<string name="settings_update_interval">Aggiorna intervalli</string>
<string name="settings_interval_summary">Imposta l\u0027intervallo di tempo per l\u0027aggionamento automatico</string>
<string name="about_privacy_answer">solXpect usa solo il permisso \"Internet\" per scaricare i dati meteo. Non include nessun meccanismo di tracciamento o pubblicitario.</string>
<string name="about_where_from">Da dove vengono scaricati i dati meteo?</string>
<string name="about_where_from_answer">I dati meteo vengono scaricati da</string>
<string name="long_press_text">Tenere premuto e trascinare per ordinare.</string>
<string name="swipe_to_delete">Passare il dito per eliminare</string>
<string name="settings_title_display_options">Opzioni di visualizzazione</string>
<string name="infoProvider">I dati meteo sono scaricati da Open-Meteo.com</string>
<string name="monday">Lunedì</string>
<string name="tuesday">Martedì</string>
<string name="wednesday">Mercoledì</string>
<string name="thursday">Giovedì</string>
<string name="friday">Venerdì</string>
<string name="saturday">Sabato</string>
<string name="sunday">Domenica</string>
<string name="chart">Grafico</string>
<string name="units_kWh">kWh</string>
<string name="dialog_edit_change_button">Salva</string>
<string name="edit_location_hint_name">Nome</string>
<string name="settings_search">Cerca</string>
<string name="settings_darkmode">Modalità scura per Android 10+</string>
<string name="settings_time24h">Ore in formato 24h</string>
<string name="summary_time24h">Sovrascrivi le impostazioni di sistema</string>
<string name="dialog_OK_button">OK</string>
<string name="dialog_NO_button">No</string>
<string name="dialog_Later_button">Forse dopo</string>
<string name="dialog_StarOnGitHub">Ti piace questa app? Per favore assegnale una stella su GitHub e regala un caffè allo sviluppatore con PayPal.</string>
<string name="slide3_heading">Open-Meteo</string>
<string name="settings_forecast_days">Numero di giorni nelle previsioni</string>
<string name="edit_location_hint_latitude">Latitudine [°]</string>
<string name="edit_location_hint_longitude">Longitudine [°]</string>
<string name="edit_location_hint_azimuth">Azimuth [°]</string>
<string name="edit_location_hint_tilt">Inclinazione [°]</string>
<string name="edit_location_hint_cells_max_power">Potenza di picco delle celle [W]</string>
<string name="edit_location_hint_cells_efficiency">Efficienza delle celle [%]</string>
<string name="edit_location_hint_cells_area">Area delle celle [m\u00b2]</string>
<string name="edit_location_hint_diffuse_efficiency">Efficienza da radiazione diffusa [%]</string>
<string name="edit_location_hint_inverter_power_limit">Potenza dell\u0027inverter [W]</string>
<string name="edit_location_hint_inverter_efficiency">Efficienza dell\u0027inverter [%]</string>
<string name="units_Wh">Wh</string>
<string name="edit_location_title">Modifica luogo</string>
<string name="edit_location_shading_azimuth_heading">Intervallo di Azimuth [°]</string>
<string name="edit_location_shading_solar_elevation_heading">Elevazione solare min. [°]</string>
<string name="edit_location_shading_opacity_heading">Ombra al di sotto di questa elevazione [%]</string>
<string name="edit_location_shading_heading">Ombreggiamento</string>
<string name="activity_help">Instruzioni</string>
<string name="action_sun_position">Posizione attuale del sole</string>
<string name="action_sun_elevation">Elevazione [°]</string>
<string name="edit_location_hint_cells_temp_coeff">Coefficiente di temperatura [%/K]</string>
<string name="activity_backuprestore">Backup/Ripristino</string>
<string name="backup_database">Backup database</string>
<string name="restore_database">Ripristina database</string>
<string name="permission_required">Permessi richiesti</string>
<string name="permission_message">%s deve accedere allo spazio di archiviazione esterno. Accetta i permessi e riprova.</string>
<string name="toast_delete">Cancella il file e riprova</string>
<string name="restore_database_message">Ripristina i dati dalla cartella Documenti. Apri la cartella \'Documenti\' e seleziona solXpect.zip se rchiesto!</string>
<string name="settings_summarize">Mostra somma</string>
<string name="summary_summarize">Somma i valori dei moduli con la stessa latitudine and longitudine.</string>
<string name="dialog_add_clone_button">Clona</string>
<string name="itemRemoved">RIMOSSO:\u0020\u0020%s</string>
<string name="undo">ANNULLA</string>
<string name="edit_location_hint_albedo">Albedo [0..1]</string>
<string name="github" translatable="false">GitHub</string>
<string name="settings_server_urls">Server URLs</string>
<string name="settings_server_summary">Cambia solo se vuoi ospitare un tuo server</string>
<string name="edit_location_hint_central_inverter">Invertitore centrale</string>
</resources>

View file

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<color name="colorStatusBar">#181818</color>
<color name="colorPrimary">#181818</color> <color name="colorPrimary">#181818</color>
<color name="colorPrimaryDark">#666666</color> <color name="colorPrimaryDark">#777777</color>
<color name="colorAccent">#707070</color> <color name="colorAccent">#a0a0a0</color>
<color name="colorListItem">#FFFFFF</color> <color name="colorListItem">#FFFFFF</color>
<color name="middlegrey">#A0A0A0</color> <color name="middlegrey">#707070</color>
<color name="lightgrey">#BBFFFFFF</color>
<color name="backgroundHighlight">#2A2A2A</color> <color name="backgroundHighlight">#2A2A2A</color>
<color name="white">#303030</color> <color name="white">#303030</color>
<color name="red">#d01530</color> <color name="red">#d01530</color>
@ -17,7 +17,7 @@
<color name="blue">#0274b2</color> <color name="blue">#0274b2</color>
<color name="lightblue">#1382d4</color> <color name="lightblue">#1382d4</color>
<color name="midblue">#1b5cd9</color> <color name="midblue">#1b5cd9</color>
<color name="backgroundBlue">#181818</color> <color name="backgroundBlue">#080808</color>
<color name="widgetBackground">#DD181818</color> <color name="widgetBackground">#DD181818</color>
</resources> </resources>

View file

@ -0,0 +1,112 @@
<resources>
<string name="activity_about">Hakkında</string>
<string name="activity_settings">Ayarlar</string>
<string name="activity_weather">Tahmin</string>
<string name="activity_manage">Konumu yönet</string>
<string name="action_refresh">Yenile</string>
<string name="navigation_drawer_open">Navigasyon bölümünü aç</string>
<string name="navigation_drawer_close">Navigasyon bölümünü kapat</string>
<string name="next">Sıradaki</string>
<string name="okay">Tamam</string>
<string name="slide1_heading">Hoşgeldin!</string>
<string name="slide1_text">solXpect güneş enerjisi santralinizin üretimini tahmin eder</string>
<string name="slide2_text">Bu uygulamanın kaynak kodu GitHub\'da mevcuttur. Daha fazla açıklama için Hakkında Sayfası\'na bir göz atın.</string>
<string name="card_week_heading">Hafta</string>
<string name="card_error_heading">Hava durumu verisini getirirken hata oluştu</string>
<string name="card_error_content">Lütfen güncellemeyi deneyin!</string>
<string name="about_privacy_heading">Gizlilik Bilgisi</string>
<string name="about">Hakkında</string>
<string name="version_number">Sürüm</string>
<string name="about_license">Lisans</string>
<string name="about_license_text">Bu uygulama, SECUSO araştırma grubu tarafından geliştirilen Privacy Friendly Weather\'dan türetilmiştir. Kaynak kodu GPLv3 altında lisanslanmıştır. Uygulama, Apache Lisansı Sürüm 2.0 altında lisanslanan Google Material Design Icons, 2 maddelik BSD Lisansı altında lisanslanan Leaflet kütüphanesi, Apache Lisansı Sürüm 2.0 altında lisanslanan AutoSuggestTextViewAPICall, MIT Lisansı altında lisanslanan Solarpositioning (net.e175.klaus:solarpositioning), Zip4j (https://github. com/srikanth-lingala/zip4j) Apache Lisansı Sürüm 2.0 altında, CompassView (https://github.com/kix2902/CompassView) Apache Lisansı Sürüm 2.0 altında ve WilliamChart kütüphanesi (com.db.chart) Apache Lisansı Sürüm 2.0 altında lisanslanmıştır.</string>
<string name="about_more_info">Daha fazla bilgi için:</string>
<string name="activity_settings_title">Ayarlar</string>
<string name="abbreviation_monday">Pzt.</string>
<string name="abbreviation_tuesday">Sal.</string>
<string name="abbreviation_wednesday">Çrş.</string>
<string name="abbreviation_thursday">Perş.</string>
<string name="abbreviation_friday">Cum.</string>
<string name="abbreviation_saturday">Cts.</string>
<string name="abbreviation_sunday">Paz.</string>
<string name="dialog_add_label">Eklemek için konum girin:</string>
<string name="dialog_add_no_city_found">Girdiğiniz bilgilerle eşleşen bir konum bulunamadı. listedeki öğelerden birini seçebilirsiniz. </string>
<string name="dialog_add_add_button">Ekle</string>
<string name="error_convert_to_json">Alınan hava durumu verileri iyi biçimlendirilmemiştir. </string>
<string name="error_no_internet">Cihazınız internete bağlı değil</string>
<string name="error_fetch_forecast">Tahmini güncellerken bir hata oluştu, tekrar deneyin!</string>
<string name="error_no_city_selected">Seçili bir konum yok. Bir konum seçmek için \"Konumları yönet\" bölümüne gidin.</string>
<string name="settings_interval_quarter">15 dk</string>
<string name="settings_interval_half">30 dk</string>
<string name="settings_interval_one">1 sa</string>
<string name="settings_interval_two">2 sa</string>
<string name="settings_interval_six">6 sa</string>
<string name="settings_interval_twelve">12 sa</string>
<string name="settings_interval_twentyfour">24 sa</string>
<string name="settings_intervals">Aralıklar</string>
<string name="settings_update_interval">Aralığı güncelle</string>
<string name="settings_interval_summary">Otomatik güncellemelerin aralığını ayarla</string>
<string name="about_privacy_answer">solXpect hava durumu verilerini almak için yalnızca \"İnternet\" iznini kullanır. Ayrıca herhangi bir izleme mekanizması veya reklam içermez.</string>
<string name="about_where_from">Hava durumu bilgileri nereden geliyor?</string>
<string name="about_where_from_answer">Hava durumu bilgilerinin alındığı yer</string>
<string name="long_press_text">Sıralamak için basılı tutun ve sürükleyin.</string>
<string name="swipe_to_delete">Silmek için kaydırın</string>
<string name="settings_title_display_options">Ekran seçenekleri</string>
<string name="infoProvider">Hava durumu bilgileri Open-Meteo.com\'dan çekilir</string>
<string name="monday">Pazartesi</string>
<string name="tuesday">Salı</string>
<string name="wednesday">Çarşamba</string>
<string name="thursday">Perşembe</string>
<string name="friday">Cuma</string>
<string name="saturday">Cumartesi</string>
<string name="sunday">Pazar</string>
<string name="chart">Grafik</string>
<string name="units_kWh">kWsa</string>
<string name="dialog_edit_change_button">Kaydet</string>
<string name="edit_location_hint_name">İsim</string>
<string name="settings_search">Ara</string>
<string name="settings_darkmode">Android 10+ Koyu Mod</string>
<string name="settings_time24h">24 saat formatı</string>
<string name="summary_time24h">Sistem ayarını geçersiz kıl</string>
<string name="dialog_OK_button">TAMAM</string>
<string name="dialog_NO_button">Hayır</string>
<string name="dialog_Later_button">Belki sonra</string>
<string name="dialog_StarOnGitHub">Bu uygulamayı beğendiniz mi? Lütfen GitHub\'da yıldız verin ve geliştiriciye PayPal ile bir kahve ısmarlayın.</string>
<string name="slide3_heading">Open-Meteo</string>
<string name="settings_forecast_days">Tahmin edilen gün sayısı</string>
<string name="edit_location_hint_latitude">Enlem [°]</string>
<string name="edit_location_hint_longitude">Boylam [°]</string>
<string name="edit_location_hint_azimuth">Azimut [°]</string>
<string name="edit_location_hint_tilt">Eğim [°]</string>
<string name="edit_location_hint_cells_max_power">Hücrelerin tepe gücü [W]</string>
<string name="edit_location_hint_cells_efficiency">Hücre verimliliği [%]</string>
<string name="edit_location_hint_cells_area">Hücre bölgesi [m\u00b2]</string>
<string name="edit_location_hint_diffuse_efficiency">Dağınık radyasyon verimliliği [%]</string>
<string name="edit_location_hint_inverter_power_limit">İnvertör gücü [W]</string>
<string name="edit_location_hint_inverter_efficiency">İnvertör verimliliği [%]</string>
<string name="units_Wh">Wh</string>
<string name="edit_location_title">Konumu düzenle</string>
<string name="edit_location_shading_azimuth_heading">Azimut menzili [°]</string>
<string name="edit_location_shading_solar_elevation_heading">Min. güneş yüksekliği [°]</string>
<string name="edit_location_shading_opacity_heading">Bu yüksekliğin altında gölgelendirme [%]</string>
<string name="edit_location_shading_heading">Gölgelendirme</string>
<string name="activity_help">Yönergeler</string>
<string name="action_sun_position">Güncel güneş konumu</string>
<string name="action_sun_elevation">Yükseklik [°]</string>
<string name="edit_location_hint_cells_temp_coeff">Sıcaklık katsayısı [%/K]</string>
<string name="activity_backuprestore">Yedekle/Geri Yükle</string>
<string name="backup_database">Yedekleme veritabanı</string>
<string name="restore_database">Geri Yükleme veritabanı</string>
<string name="permission_required">İzİn gerekli</string>
<string name="permission_message">%s harici depolama alanına erişmesi gerekiyor. Lütfen izni kabul edin ve tekrar deneyin.</string>
<string name="toast_delete">Lütfen dosyayı silin ve tekrar deneyin</string>
<string name="restore_database_message">Belgeler klasöründen Verileri Geri Yükleyin. Eğer gerekirse \'Belgeler\' klasörünü açın ve solXpect.zip dosyasını seçin!</string>
<string name="settings_summarize">Toplamı göster</string>
<string name="summary_summarize">Aynı enlem ve boylamdaki modüllerin değerlerini özetleyin.</string>
<string name="dialog_add_clone_button">Klon</string>
<string name="itemRemoved">\u0020\u0020%s KALDIRILDI</string>
<string name="undo">GERİ AL</string>
<string name="edit_location_hint_albedo">Albedo [0..1]</string>
<string name="settings_server_urls">Sunucu URL\'leri</string>
<string name="settings_server_summary">Yalnızca kendi sunucularınızı barındırmak istiyorsanız değiştirin</string>
<string name="edit_location_hint_central_inverter">Merkezi invertör</string>
</resources>

View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--
Refer to App Widget Documentation for margin information
http://developer.android.com/guide/topics/appwidgets/index.html#CreatingLayout
-->
</resources>

View file

@ -1,14 +0,0 @@
<resources>
<style name="AppTheme" parent="Theme.AppCompat.DayNight.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:statusBarColor">@color/colorStatusBar</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
</style>
</resources>

View file

@ -1,6 +0,0 @@
<resources>
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
(such as screen margins) for screens with more than 820dp of available width. This
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
<dimen name="activity_horizontal_margin">64dp</dimen>
</resources>

View file

@ -0,0 +1,115 @@
<resources>
<string name="app_name">发电预测</string>
<string name="activity_about">关于</string>
<string name="activity_settings">设置</string>
<string name="activity_weather">天气预报</string>
<string name="activity_manage">管理位置</string>
<string name="action_refresh">刷新</string>
<string name="navigation_drawer_open">打开导航抽屉</string>
<string name="navigation_drawer_close">关闭导航抽屉</string>
<string name="next">下一步</string>
<string name="okay">确定</string>
<string name="slide1_heading">欢迎!</string>
<string name="slide1_text">太阳能预测可预测您的太阳能发电站输出</string>
<string name="slide2_text">本应用的源代码可在GitHub获取。更多说明请查看关于页面。</string>
<string name="card_week_heading">本周</string>
<string name="card_error_heading">获取天气数据错误</string>
<string name="card_error_content">请尝试更新!</string>
<string name="about_privacy_heading">隐私信息</string>
<string name="about">关于</string>
<string name="version_number">版本号</string>
<string name="about_license">许可证</string>
<string name="about_license_text">本应用衍生自SECUSO研究小组开发的Privacy Friendly Weather。源代码采用GPLv3许可。应用使用的图标来自Google Material Design IconsApache 2.0许可、Leaflet库BSD 2条款许可、AutoSuggestTextViewAPICallApache 2.0许可、SolarpositioningMIT许可、Zip4jApache 2.0许可、CompassViewApache 2.0许可和WilliamChart库Apache 2.0许可)</string>
<string name="about_more_info">更多信息请访问:</string>
<string name="activity_settings_title">设置</string>
<string name="abbreviation_monday">周一</string>
<string name="abbreviation_tuesday">周二</string>
<string name="abbreviation_wednesday">周三</string>
<string name="abbreviation_thursday">周四</string>
<string name="abbreviation_friday">周五</string>
<string name="abbreviation_saturday">周六</string>
<string name="abbreviation_sunday">周日</string>
<string name="dialog_add_label">输入要添加的位置:</string>
<string name="dialog_add_no_city_found">未找到匹配您输入的位置。建议从下拉列表中选择项目。</string>
<string name="dialog_add_add_button">添加</string>
<string name="error_convert_to_json">获取的天气数据格式不正确。</string>
<string name="error_no_internet">您的设备未连接到互联网</string>
<string name="error_fetch_forecast">更新预报时出错,请重试!</string>
<string name="error_no_city_selected">未选择位置。请前往\"管理位置\"进行选择。</string>
<string name="settings_interval_quarter">15分钟</string>
<string name="settings_interval_half">30分钟</string>
<string name="settings_interval_one">1小时</string>
<string name="settings_interval_two">2小时</string>
<string name="settings_interval_six">6小时</string>
<string name="settings_interval_twelve">12小时</string>
<string name="settings_interval_twentyfour">24小时</string>
<string name="settings_intervals">间隔</string>
<string name="settings_update_interval">更新间隔</string>
<string name="settings_interval_summary">设置自动更新的间隔时间</string>
<string name="about_privacy_answer">太阳能预测仅使用\"互联网\"权限获取天气数据。不包含任何跟踪机制或广告。</string>
<string name="about_where_from">天气信息来自哪里?</string>
<string name="about_where_from_answer">天气信息获取自</string>
<string name="long_press_text">长按并拖动可排序。</string>
<string name="swipe_to_delete">滑动删除</string>
<string name="settings_title_display_options">显示选项</string>
<string name="infoProvider">天气信息获取自Open-Meteo.com</string>
<string name="monday">星期一</string>
<string name="tuesday">星期二</string>
<string name="wednesday">星期三</string>
<string name="thursday">星期四</string>
<string name="friday">星期五</string>
<string name="saturday">星期六</string>
<string name="sunday">星期日</string>
<string name="chart">图表</string>
<string name="units_kWh">千瓦时</string>
<string name="dialog_edit_change_button">保存</string>
<string name="edit_location_hint_name">名称</string>
<string name="settings_search">搜索</string>
<string name="settings_darkmode">Android 10+深色模式</string>
<string name="settings_time24h">24小时制</string>
<string name="summary_time24h">覆盖系统设置</string>
<string name="dialog_OK_button">确定</string>
<string name="dialog_NO_button"></string>
<string name="dialog_Later_button">稍后再说</string>
<string name="dialog_StarOnGitHub">喜欢这个应用吗请在GitHub上给个星标并通过PayPal请开发者喝杯咖啡。</string>
<string name="slide3_heading">Open-Meteo</string>
<string name="settings_forecast_days">预报天数</string>
<string name="edit_location_hint_latitude">纬度[°]</string>
<string name="edit_location_hint_longitude">经度[°]</string>
<string name="edit_location_hint_azimuth">方位角[°]</string>
<string name="edit_location_hint_tilt">倾斜角[°]</string>
<string name="edit_location_hint_cells_max_power">电池峰值功率[W]</string>
<string name="edit_location_hint_cells_efficiency">电池效率[%]</string>
<string name="edit_location_hint_cells_area">电池面积[m²]</string>
<string name="edit_location_hint_diffuse_efficiency">漫射辐射效率[%]</string>
<string name="edit_location_hint_inverter_power_limit">逆变器功率[W]</string>
<string name="edit_location_hint_inverter_efficiency">逆变器效率[%]</string>
<string name="units_Wh">瓦时</string>
<string name="edit_location_title">编辑位置</string>
<string name="edit_location_shading_azimuth_heading">方位角范围[°]</string>
<string name="edit_location_shading_solar_elevation_heading">最小太阳高度角[°]</string>
<string name="edit_location_shading_opacity_heading">此高度以下的遮阳度[%]</string>
<string name="edit_location_shading_heading">遮阳</string>
<string name="activity_help">使用说明</string>
<string name="action_sun_position">当前太阳位置</string>
<string name="action_sun_elevation">高度角[°]</string>
<string name="edit_location_hint_cells_temp_coeff">温度系数[%/K]</string>
<string name="activity_backuprestore">备份/恢复</string>
<string name="backup_database">备份数据库</string>
<string name="restore_database">恢复数据库</string>
<string name="permission_required">需要权限</string>
<string name="permission_message">%s需要访问外部存储。请授予权限后重试。</string>
<string name="toast_delete">请删除文件后重试</string>
<string name="settings_summarize">显示总和</string>
<string name="summary_summarize">汇总相同经纬度模块的值。</string>
<string name="dialog_add_clone_button">克隆</string>
<string name="itemRemoved">已删除:%s</string>
<string name="undo">撤销</string>
<string name="edit_location_hint_albedo">反照率[0..1]</string>
<string name="settings_server_urls">服务器URL</string>
<string name="settings_server_summary">仅在您想托管自己的服务器时更改</string>
<string name="edit_location_hint_central_inverter">中央逆变器</string>
<string name="card_today_heading">今日</string>
<string name="card_today_produced">已发电:</string>
<string name="card_today_remaining">剩余:</string>
</resources>

View file

@ -1,24 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string-array name="temperatureUnitsArray">
<item>@string/settings_celsius</item>
<item>@string/settings_fahrenheit</item>
</string-array>
<string-array name="temperatureUnitsValues">
<item>1</item>
<item>2</item>
</string-array>
<string-array name="distanceUnitsArray">
<item>@string/settings_kilometers</item>
<item>@string/settings_miles</item>
</string-array>
<string-array name="distanceUnitsValues">
<item>1</item>
<item>2</item>
</string-array>
<string-array name="refreshIntervalArray"> <string-array name="refreshIntervalArray">
<item>@string/settings_interval_quarter</item> <item>@string/settings_interval_quarter</item>
<item>@string/settings_interval_half</item> <item>@string/settings_interval_half</item>

View file

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<color name="colorStatusBar">#024265</color>
<color name="colorPrimary">#024265</color> <color name="colorPrimary">#024265</color>
<color name="colorPrimaryDark">#024265</color> <color name="colorPrimaryDark">#024265</color>
<color name="colorAccent">#0274b2</color> <color name="colorAccent">#0274b2</color>
<color name="colorListItem">#024265</color> <color name="colorListItem">#024265</color>
<color name="backgroundHighlight">#5fa1d2</color> <color name="backgroundHighlight">#5fa1d2</color>
<color name="middlegrey">#A8A8A8</color> <color name="middlegrey">#A8A8A8</color>
<color name="lightgrey">#AAFFFFFF</color>
<color name="white">#fafafa</color> <color name="white">#fafafa</color>
<color name="red">#d01530</color> <color name="red">#d01530</color>
<color name="lightred">#fa7972</color> <color name="lightred">#fa7972</color>

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<color name="ic_launcher_background">#aba4a4</color> <color name="ic_launcher_background">#ABA4A4</color>
</resources> </resources>

Some files were not shown because too many files have changed in this diff Show more