mirror of
https://github.com/woheller69/solxpect.git
synced 2026-01-31 06:25:15 +01:00
first version solarCast
This commit is contained in:
commit
da720ba6dc
471 changed files with 10774 additions and 0 deletions
|
|
@ -0,0 +1,68 @@
|
|||
package org.woheller69.weather;
|
||||
|
||||
import net.e175.klaus.solarpositioning.AzimuthZenithAngle;
|
||||
import net.e175.klaus.solarpositioning.DeltaT;
|
||||
import net.e175.klaus.solarpositioning.Grena3;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
public class SolarPowerPlant {
|
||||
double latitude;
|
||||
double longitude;
|
||||
double cellsMaxPower;
|
||||
double cellsArea;
|
||||
double cellsEfficiency;
|
||||
double diffuseEfficiency;
|
||||
double converterPowerLimit;
|
||||
double converterEfficiency;
|
||||
double azimuthAngle;
|
||||
double elevationAngle;
|
||||
|
||||
public SolarPowerPlant(double latitude, double longitude, double cellsMaxPower, double cellsArea, double cellsEfficiency, double diffuseEfficiency, double converterPowerLimit, double converterEfficiency, double azimuthAngle, double elevationAngle) {
|
||||
this.latitude = latitude;
|
||||
this.longitude = longitude;
|
||||
this.cellsMaxPower = cellsMaxPower;
|
||||
this.cellsArea = cellsArea;
|
||||
this.cellsEfficiency = cellsEfficiency / 100;
|
||||
this.diffuseEfficiency = diffuseEfficiency / 100;
|
||||
this.converterPowerLimit = converterPowerLimit;
|
||||
this.converterEfficiency = converterEfficiency / 100;
|
||||
this.azimuthAngle = azimuthAngle;
|
||||
this.elevationAngle = elevationAngle;
|
||||
|
||||
}
|
||||
|
||||
public float getPower(double solarPowerNormal, double solarPowerDiffuse, long epochTimeSeconds) {
|
||||
Instant i = Instant.ofEpochSecond(epochTimeSeconds); //currentTimeMillis is in GMT
|
||||
ZonedDateTime dateTime = ZonedDateTime.ofInstant(i, ZoneId.of("GMT"));
|
||||
|
||||
AzimuthZenithAngle position = Grena3.calculateSolarPosition(
|
||||
dateTime,
|
||||
latitude,
|
||||
longitude,
|
||||
DeltaT.estimate(dateTime.toLocalDate())); // delta T (s)
|
||||
|
||||
double solarAzimuth = position.getAzimuth();
|
||||
double solarElevation = 90 - position.getZenithAngle();
|
||||
|
||||
Double[] directionSun = {Math.sin(solarAzimuth / 180 * Math.PI) * Math.cos(solarElevation / 180 * Math.PI), Math.cos(solarAzimuth / 180 * Math.PI) * Math.cos(solarElevation / 180 * Math.PI), Math.sin(solarElevation / 180 * Math.PI)};
|
||||
Double[] normalPanel = {Math.sin(azimuthAngle / 180 * Math.PI) * Math.cos((90 - elevationAngle) / 180 * Math.PI), Math.cos(azimuthAngle / 180 * Math.PI) * Math.cos((90 - elevationAngle) / 180 * Math.PI), Math.sin((90 - elevationAngle) / 180 * Math.PI)};
|
||||
|
||||
double efficiency = 0; //calculate scalar product of sunDirection and normalPanel vectors
|
||||
for (int j = 0; j < directionSun.length; j++) {
|
||||
efficiency += directionSun[j] * normalPanel[j];
|
||||
}
|
||||
|
||||
efficiency = Math.max(0,efficiency); //scalar product is negative if sun points to back of module. set 0 in this case
|
||||
|
||||
//TODO solarPowerDiffuse ignored so far
|
||||
|
||||
double dcPower = (solarPowerNormal * efficiency + solarPowerDiffuse * diffuseEfficiency )* cellsEfficiency * cellsArea;
|
||||
|
||||
double acPower = Math.min(dcPower * converterEfficiency, converterPowerLimit);
|
||||
|
||||
return (float) acPower;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package org.woheller69.weather.activities;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.woheller69.weather.BuildConfig;
|
||||
import org.woheller69.weather.R;
|
||||
|
||||
/**
|
||||
* Created by yonjuni on 15.06.16.
|
||||
*/
|
||||
public class AboutActivity extends NavigationActivity {
|
||||
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_about);
|
||||
overridePendingTransition(0, 0);
|
||||
|
||||
((TextView) findViewById(R.id.openmeteoURL)).setMovementMethod(LinkMovementMethod.getInstance());
|
||||
((TextView) findViewById(R.id.githubURL)).setMovementMethod(LinkMovementMethod.getInstance());
|
||||
((TextView) findViewById(R.id.textFieldVersionName)).setText(BuildConfig.VERSION_NAME);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getNavigationDrawerID() {
|
||||
return R.id.nav_about;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,274 @@
|
|||
package org.woheller69.weather.activities;
|
||||
|
||||
import static java.lang.Boolean.TRUE;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.location.LocationListener;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
import com.google.android.material.tabs.TabLayoutMediator;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.viewpager2.widget.ViewPager2;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.animation.AlphaAnimation;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
import android.view.animation.RotateAnimation;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.woheller69.weather.R;
|
||||
import org.woheller69.weather.database.CityToWatch;
|
||||
import org.woheller69.weather.database.CurrentWeatherData;
|
||||
import org.woheller69.weather.database.HourlyForecast;
|
||||
import org.woheller69.weather.database.SQLiteHelper;
|
||||
import org.woheller69.weather.database.WeekForecast;
|
||||
import org.woheller69.weather.ui.updater.IUpdateableCityUI;
|
||||
import org.woheller69.weather.ui.updater.ViewUpdater;
|
||||
import org.woheller69.weather.ui.viewPager.WeatherPagerAdapter;
|
||||
import static org.woheller69.weather.database.SQLiteHelper.getWidgetCityID;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class ForecastCityActivity extends NavigationActivity implements IUpdateableCityUI {
|
||||
private WeatherPagerAdapter pagerAdapter;
|
||||
private static MenuItem refreshActionButton;
|
||||
|
||||
private int cityId = -1;
|
||||
private ViewPager2 viewPager2;
|
||||
private TabLayout tabLayout;
|
||||
private TextView noCityText;
|
||||
Context context;
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
||||
ViewUpdater.removeSubscriber(this);
|
||||
ViewUpdater.removeSubscriber(pagerAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
SQLiteHelper db = SQLiteHelper.getInstance(this);
|
||||
if (db.getAllCitiesToWatch().isEmpty()) {
|
||||
// no cities selected.. don't show the viewPager - rather show a text that tells the user that no city was selected
|
||||
viewPager2.setVisibility(View.GONE);
|
||||
noCityText.setVisibility(View.VISIBLE);
|
||||
|
||||
} else {
|
||||
noCityText.setVisibility(View.GONE);
|
||||
viewPager2.setVisibility(View.VISIBLE);
|
||||
pagerAdapter.loadCities();
|
||||
viewPager2.setAdapter(pagerAdapter);
|
||||
TabLayoutMediator tabLayoutMediator = new TabLayoutMediator(tabLayout, viewPager2,false,false, (tab, position) -> tab.setText(pagerAdapter.getPageTitle(position)));
|
||||
tabLayoutMediator.attach();
|
||||
}
|
||||
|
||||
ViewUpdater.addSubscriber(this);
|
||||
ViewUpdater.addSubscriber(pagerAdapter);
|
||||
|
||||
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.getPosForCityID(cityId)==-1) cityId=pagerAdapter.getCityIDForPos(viewPager2.getCurrentItem());
|
||||
CurrentWeatherData currentWeather = db.getCurrentWeatherByCityId(cityId);
|
||||
|
||||
long timestamp = currentWeather.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);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
context=this;
|
||||
setContentView(R.layout.activity_forecast_city);
|
||||
overridePendingTransition(0, 0);
|
||||
|
||||
initResources();
|
||||
|
||||
viewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
super.onPageSelected(position);
|
||||
//Update current tab if outside update interval, show animation
|
||||
SharedPreferences prefManager = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
SQLiteHelper database = SQLiteHelper.getInstance(getApplicationContext().getApplicationContext());
|
||||
CurrentWeatherData currentWeather = database.getCurrentWeatherByCityId(pagerAdapter.getCityIDForPos(position));
|
||||
|
||||
long timestamp = currentWeather.getTimestamp();
|
||||
long systemTime = System.currentTimeMillis() / 1000;
|
||||
long updateInterval = (long) (Float.parseFloat(prefManager.getString("pref_updateInterval", "2")) * 60 * 60);
|
||||
|
||||
if (timestamp + updateInterval - systemTime <= 0) {
|
||||
WeatherPagerAdapter.refreshSingleData(getApplicationContext(),true, pagerAdapter.getCityIDForPos(position));
|
||||
ForecastCityActivity.startRefreshAnimation();
|
||||
|
||||
}
|
||||
//post method needed to avoid Illegal State Exception: Cannot call this method in a scroll callback.
|
||||
viewPager2.post(() -> {
|
||||
pagerAdapter.notifyItemChanged(position); //fix crash with StaggeredGridLayoutManager when moving back and forth between items
|
||||
});
|
||||
cityId=pagerAdapter.getCityIDForPos(viewPager2.getCurrentItem()); //save current cityId for next resume
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
setIntent(intent);
|
||||
if (intent.hasExtra("cityId")) {
|
||||
cityId = intent.getIntExtra("cityId",-1);
|
||||
if (pagerAdapter.getItemCount()>0) viewPager2.setCurrentItem(pagerAdapter.getPosForCityID(cityId),false);
|
||||
}
|
||||
}
|
||||
|
||||
private void initResources() {
|
||||
viewPager2 = findViewById(R.id.viewPager2);
|
||||
reduceViewpager2DragSensitivity(viewPager2,2);
|
||||
tabLayout = findViewById(R.id.tab_layout);
|
||||
pagerAdapter = new WeatherPagerAdapter(this, getSupportFragmentManager(),getLifecycle());
|
||||
noCityText = findViewById(R.id.noCitySelectedText);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getNavigationDrawerID() {
|
||||
return R.id.nav_weather;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
// Inflate the menu; this adds items to the action bar if it is present.
|
||||
getMenuInflater().inflate(R.menu.activity_forecast_city, menu);
|
||||
|
||||
final Menu m = menu;
|
||||
|
||||
refreshActionButton = menu.findItem(R.id.menu_refresh);
|
||||
refreshActionButton.setActionView(R.layout.menu_refresh_action_view);
|
||||
refreshActionButton.getActionView().setOnClickListener(v -> m.performIdentifierAction(refreshActionButton.getItemId(), 0));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
// Handle action bar item clicks here. The action bar will
|
||||
// automatically handle clicks on the Home/Up button, so long
|
||||
// as you specify a parent activity in AndroidManifest.xml.
|
||||
int id = item.getItemId();
|
||||
SQLiteHelper db = SQLiteHelper.getInstance(this);
|
||||
|
||||
if (id==R.id.menu_refresh){
|
||||
if (!db.getAllCitiesToWatch().isEmpty()) { //only if at least one city is watched, otherwise crash
|
||||
WeatherPagerAdapter.refreshSingleData(getApplicationContext(),true, pagerAdapter.getCityIDForPos(viewPager2.getCurrentItem()));
|
||||
ForecastCityActivity.startRefreshAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostResume() {
|
||||
super.onPostResume();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNewCurrentWeatherData(CurrentWeatherData data) {
|
||||
if (refreshActionButton != null && refreshActionButton.getActionView() != null) {
|
||||
refreshActionButton.getActionView().clearAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNewWeekForecasts(List<WeekForecast> forecasts) {
|
||||
if (refreshActionButton != null && refreshActionButton.getActionView() != null) {
|
||||
refreshActionButton.getActionView().clearAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNewForecasts(List<HourlyForecast> hourlyForecasts) {
|
||||
if (refreshActionButton != null && refreshActionButton.getActionView() != null) {
|
||||
refreshActionButton.getActionView().clearAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
public static void startRefreshAnimation(){
|
||||
{
|
||||
if(refreshActionButton !=null && refreshActionButton.getActionView() != null) {
|
||||
RotateAnimation rotate = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
|
||||
rotate.setDuration(500);
|
||||
rotate.setRepeatCount(5);
|
||||
rotate.setInterpolator(new LinearInterpolator());
|
||||
rotate.setAnimationListener(new Animation.AnimationListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
refreshActionButton.getActionView().setActivated(false);
|
||||
refreshActionButton.getActionView().setEnabled(false);
|
||||
refreshActionButton.getActionView().setClickable(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
refreshActionButton.getActionView().setActivated(true);
|
||||
refreshActionButton.getActionView().setEnabled(true);
|
||||
refreshActionButton.getActionView().setClickable(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
}
|
||||
});
|
||||
refreshActionButton.getActionView().startAnimation(rotate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//https://devdreamz.com/question/348298-how-to-modify-sensitivity-of-viewpager
|
||||
private void reduceViewpager2DragSensitivity(ViewPager2 viewPager, int sensitivity) {
|
||||
try {
|
||||
Field ff = ViewPager2.class.getDeclaredField("mRecyclerView") ;
|
||||
ff.setAccessible(true);
|
||||
RecyclerView recyclerView = (RecyclerView) ff.get(viewPager);
|
||||
Field touchSlopField = RecyclerView.class.getDeclaredField("mTouchSlop") ;
|
||||
touchSlopField.setAccessible(true);
|
||||
int touchSlop = (int) touchSlopField.get(recyclerView);
|
||||
touchSlopField.set(recyclerView,touchSlop*sensitivity);
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
package org.woheller69.weather.activities;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.woheller69.weather.R;
|
||||
import org.woheller69.weather.database.City;
|
||||
import org.woheller69.weather.database.CityToWatch;
|
||||
import org.woheller69.weather.database.SQLiteHelper;
|
||||
import org.woheller69.weather.dialogs.AddLocationDialogOmGeocodingAPI;
|
||||
import org.woheller69.weather.ui.RecycleList.RecyclerItemClickListener;
|
||||
import org.woheller69.weather.ui.RecycleList.RecyclerOverviewListAdapter;
|
||||
import org.woheller69.weather.ui.RecycleList.SimpleItemTouchHelperCallback;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
//in-App: where cities get added & sorted
|
||||
public class ManageLocationsActivity extends NavigationActivity {
|
||||
|
||||
private SQLiteHelper database;
|
||||
|
||||
private ItemTouchHelper.Callback callback;
|
||||
private ItemTouchHelper touchHelper;
|
||||
RecyclerOverviewListAdapter adapter;
|
||||
List<CityToWatch> cities;
|
||||
Context context;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_manage_locations);
|
||||
overridePendingTransition(0, 0);
|
||||
context=this;
|
||||
database = SQLiteHelper.getInstance(getApplicationContext());
|
||||
|
||||
|
||||
try {
|
||||
cities = database.getAllCitiesToWatch();
|
||||
Collections.sort(cities, new Comparator<CityToWatch>() {
|
||||
@Override
|
||||
public int compare(CityToWatch o1, CityToWatch o2) {
|
||||
return o1.getRank() - o2.getRank();
|
||||
}
|
||||
|
||||
});
|
||||
} catch (NullPointerException e) {
|
||||
e.printStackTrace();
|
||||
Toast toast = Toast.makeText(getBaseContext(), "No cities in DB", Toast.LENGTH_SHORT);
|
||||
toast.show();
|
||||
}
|
||||
|
||||
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.list_view_cities);
|
||||
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
|
||||
recyclerView.setLayoutManager(linearLayoutManager);
|
||||
|
||||
recyclerView.addOnItemTouchListener(
|
||||
new RecyclerItemClickListener(getBaseContext(), recyclerView, new RecyclerItemClickListener.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(View view, int position) {
|
||||
AlertDialog.Builder alert = new AlertDialog.Builder(context);
|
||||
|
||||
LayoutInflater inflater = getLayoutInflater();
|
||||
View dialogView = inflater.inflate(R.layout.dialog_edit_location, null);
|
||||
|
||||
alert.setTitle(getString(R.string.edit_location_title));
|
||||
alert.setView(dialogView);
|
||||
EditText editLatitude = (EditText) dialogView.findViewById(R.id.EditLocation_Lat);
|
||||
EditText editLongitude = (EditText) dialogView.findViewById(R.id.EditLocation_Lon);
|
||||
EditText editCity = (EditText) dialogView.findViewById(R.id.EditLocation_Name);
|
||||
EditText editAzimuth = (EditText) dialogView.findViewById(R.id.EditLocation_Azimuth);
|
||||
EditText editElevation = (EditText) dialogView.findViewById(R.id.EditLocation_Elevation);
|
||||
EditText editCellsMaxPower = (EditText) dialogView.findViewById(R.id.EditLocation_Cell_Max_Power);
|
||||
EditText editCellsArea = (EditText) dialogView.findViewById(R.id.EditLocation_Cells_Area);
|
||||
EditText editCellsEfficiency = (EditText) dialogView.findViewById(R.id.EditLocation_Cell_Efficiency);
|
||||
EditText editDiffuseEfficiency = (EditText) dialogView.findViewById(R.id.EditLocation_Diffuse_Efficiency);
|
||||
EditText editConverterPowerLimit = (EditText) dialogView.findViewById(R.id.EditLocation_Converter_Power_Limit);
|
||||
EditText editConverterEfficiency = (EditText) dialogView.findViewById(R.id.EditLocation_Converter_Efficiency);
|
||||
|
||||
editCity.setText(adapter.getCitytoWatch(position).getCityName());
|
||||
editLatitude.setText(Float.toString(adapter.getCitytoWatch(position).getLatitude()));
|
||||
editLongitude.setText(Float.toString(adapter.getCitytoWatch(position).getLongitude()));
|
||||
editAzimuth.setText(Float.toString(adapter.getCitytoWatch(position).getAzimuthAngle()));
|
||||
editElevation.setText(Float.toString(adapter.getCitytoWatch(position).getElevationAngle()));
|
||||
editCellsMaxPower.setText(Float.toString(adapter.getCitytoWatch(position).getCellsMaxPower()));
|
||||
editCellsArea.setText(Float.toString(adapter.getCitytoWatch(position).getCellsArea()));
|
||||
editCellsEfficiency.setText(Float.toString(adapter.getCitytoWatch(position).getCellsEfficiency()));
|
||||
editDiffuseEfficiency.setText(Float.toString(adapter.getCitytoWatch(position).getDiffuseEfficiency()));
|
||||
editConverterPowerLimit.setText(Float.toString(adapter.getCitytoWatch(position).getConverterPowerLimit()));
|
||||
editConverterEfficiency.setText(Float.toString(adapter.getCitytoWatch(position).getConverterEfficiency()));
|
||||
|
||||
alert.setPositiveButton(getString(R.string.dialog_edit_change_button), (dialog, whichButton) -> {
|
||||
adapter.updateCity(position, String.valueOf(editCity.getText()),
|
||||
Float.parseFloat(editLatitude.getText().toString()),
|
||||
Float.parseFloat(editLongitude.getText().toString()),
|
||||
Float.parseFloat(editAzimuth.getText().toString()),
|
||||
Float.parseFloat(editElevation.getText().toString()),
|
||||
Float.parseFloat(editCellsMaxPower.getText().toString()),
|
||||
Float.parseFloat(editCellsArea.getText().toString()),
|
||||
Float.parseFloat(editCellsEfficiency.getText().toString()),
|
||||
Float.parseFloat(editDiffuseEfficiency.getText().toString()),
|
||||
Float.parseFloat(editConverterPowerLimit.getText().toString()),
|
||||
Float.parseFloat(editConverterEfficiency.getText().toString())
|
||||
);
|
||||
});
|
||||
alert.setNegativeButton(getString(R.string.dialog_add_close_button), (dialog, whichButton) -> {
|
||||
});
|
||||
|
||||
alert.show();
|
||||
}
|
||||
|
||||
public void onLongItemClick(View view, int position) {
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
);
|
||||
|
||||
adapter = new RecyclerOverviewListAdapter(getApplicationContext(), cities);
|
||||
recyclerView.setAdapter(adapter);
|
||||
recyclerView.setFocusable(false);
|
||||
|
||||
callback = new SimpleItemTouchHelperCallback(adapter);
|
||||
touchHelper = new ItemTouchHelper(callback);
|
||||
touchHelper.attachToRecyclerView(recyclerView);
|
||||
|
||||
FloatingActionButton addFab1 = (FloatingActionButton) findViewById(R.id.fabAddLocation);
|
||||
|
||||
if (addFab1 != null) {
|
||||
|
||||
addFab1.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
FragmentManager fragmentManager = getSupportFragmentManager();
|
||||
AddLocationDialogOmGeocodingAPI addLocationDialog = new AddLocationDialogOmGeocodingAPI();
|
||||
addLocationDialog.show(fragmentManager, "AddLocationDialog");
|
||||
getSupportFragmentManager().executePendingTransactions();
|
||||
addLocationDialog.getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getNavigationDrawerID() {
|
||||
return R.id.nav_manage;
|
||||
}
|
||||
|
||||
public void addCityToList(City city) {
|
||||
CityToWatch newCity=convertCityToWatched(city);
|
||||
long id=database.addCityToWatch(newCity);
|
||||
newCity.setId((int) id);
|
||||
newCity.setCityId((int) id); //use id also instead of city id as unique identifier
|
||||
cities.add(newCity);
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
private CityToWatch convertCityToWatched(City selectedCity) {
|
||||
|
||||
return new CityToWatch(
|
||||
database.getMaxRank() + 1,
|
||||
-1,
|
||||
selectedCity.getCityId(), selectedCity.getLongitude(),selectedCity.getLatitude(),
|
||||
selectedCity.getCityName()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,228 @@
|
|||
package org.woheller69.weather.activities;
|
||||
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import com.google.android.material.navigation.NavigationView;
|
||||
import com.google.android.material.navigation.NavigationView.OnNavigationItemSelectedListener;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.core.app.TaskStackBuilder;
|
||||
import androidx.core.view.GravityCompat;
|
||||
import androidx.drawerlayout.widget.DrawerLayout;
|
||||
import androidx.appcompat.app.ActionBarDrawerToggle;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
import android.os.Looper;
|
||||
import android.view.MenuItem;
|
||||
|
||||
|
||||
import org.woheller69.weather.BuildConfig;
|
||||
import org.woheller69.weather.R;
|
||||
import org.woheller69.weather.preferences.AppPreferencesManager;
|
||||
|
||||
import static java.lang.Boolean.TRUE;
|
||||
|
||||
/**
|
||||
* Created by Chris on 04.07.2016.
|
||||
*/
|
||||
public class NavigationActivity extends AppCompatActivity implements OnNavigationItemSelectedListener {
|
||||
|
||||
// delay to launch nav drawer item, to allow close animation to play
|
||||
static final int NAVDRAWER_LAUNCH_DELAY = 250;
|
||||
public static boolean isVisible = false;
|
||||
|
||||
// Navigation drawer:
|
||||
private DrawerLayout mDrawerLayout;
|
||||
private NavigationView mNavigationView;
|
||||
|
||||
// Helper
|
||||
private Handler mHandler;
|
||||
protected SharedPreferences mSharedPreferences;
|
||||
protected AppPreferencesManager prefManager;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
prefManager = new AppPreferencesManager(PreferenceManager.getDefaultSharedPreferences(this));
|
||||
if (prefManager.showStarDialog(this)) {
|
||||
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
|
||||
alertDialogBuilder.setMessage(R.string.dialog_StarOnGitHub);
|
||||
alertDialogBuilder.setPositiveButton(getString(R.string.dialog_OK_button), new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(BuildConfig.GITHUB_URL)));
|
||||
prefManager = new AppPreferencesManager(PreferenceManager.getDefaultSharedPreferences(getApplicationContext()));
|
||||
prefManager.setAskForStar(false);
|
||||
}
|
||||
});
|
||||
alertDialogBuilder.setNegativeButton(getString(R.string.dialog_NO_button), new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
prefManager = new AppPreferencesManager(PreferenceManager.getDefaultSharedPreferences(getApplicationContext()));
|
||||
prefManager.setAskForStar(false);
|
||||
}
|
||||
});
|
||||
alertDialogBuilder.setNeutralButton(getString(R.string.dialog_Later_button), null);
|
||||
|
||||
AlertDialog alertDialog = alertDialogBuilder.create();
|
||||
alertDialog.show();
|
||||
|
||||
}
|
||||
|
||||
overridePendingTransition(0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
|
||||
if (drawer.isDrawerOpen(GravityCompat.START)) {
|
||||
drawer.closeDrawer(GravityCompat.START);
|
||||
} else {
|
||||
if (getNavigationDrawerID()!=R.id.nav_weather)
|
||||
{
|
||||
Intent intent = new Intent(this, ForecastCityActivity.class);
|
||||
startActivity(intent);
|
||||
}else{
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_MAIN);
|
||||
intent.addCategory(Intent.CATEGORY_HOME);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected int getNavigationDrawerID() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onNavigationItemSelected(MenuItem item) {
|
||||
final int itemId = item.getItemId();
|
||||
|
||||
return goToNavigationItem(itemId);
|
||||
}
|
||||
|
||||
protected boolean goToNavigationItem(final int itemId) {
|
||||
|
||||
if (itemId == getNavigationDrawerID()) {
|
||||
// just close drawer because we are already in this activity
|
||||
mDrawerLayout.closeDrawer(GravityCompat.START);
|
||||
return true;
|
||||
}
|
||||
|
||||
// delay transition so the drawer can close
|
||||
mHandler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
callDrawerItem(itemId);
|
||||
}
|
||||
}, NAVDRAWER_LAUNCH_DELAY);
|
||||
|
||||
mDrawerLayout.closeDrawer(GravityCompat.START);
|
||||
|
||||
selectNavigationItem(itemId);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// set active navigation item
|
||||
private void selectNavigationItem(int itemId) {
|
||||
for (int i = 0; i < mNavigationView.getMenu().size(); i++) {
|
||||
boolean b = itemId == mNavigationView.getMenu().getItem(i).getItemId();
|
||||
mNavigationView.getMenu().getItem(i).setChecked(b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables back navigation for activities that are launched from the NavBar. See
|
||||
* {@code AndroidManifest.xml} to find out the parent activity names for each activity.
|
||||
*
|
||||
* @param intent
|
||||
*/
|
||||
private void createBackStack(Intent intent) {
|
||||
TaskStackBuilder builder = TaskStackBuilder.create(this);
|
||||
builder.addNextIntentWithParentStack(intent);
|
||||
builder.startActivities();
|
||||
}
|
||||
|
||||
private void callDrawerItem(final int itemId) {
|
||||
|
||||
Intent intent;
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
if(sharedPreferences.getBoolean("pref_DarkMode", false)==TRUE) {
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
|
||||
}else {
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
|
||||
}
|
||||
|
||||
if (itemId==R.id.nav_weather) {
|
||||
intent = new Intent(this, ForecastCityActivity.class);
|
||||
startActivity(intent);
|
||||
}else if (itemId==R.id.nav_manage){
|
||||
intent = new Intent(this, ManageLocationsActivity.class);
|
||||
startActivity(intent);
|
||||
}else if (itemId==R.id.nav_about) {
|
||||
intent = new Intent(this, AboutActivity.class);
|
||||
createBackStack(intent);
|
||||
}else if(itemId==R.id.nav_settings) {
|
||||
intent = new Intent(this, SettingsActivity.class);
|
||||
createBackStack(intent);
|
||||
}else if (itemId==R.id.star_on_github){
|
||||
startActivity(new Intent(Intent.ACTION_VIEW,
|
||||
Uri.parse(BuildConfig.GITHUB_URL)));
|
||||
prefManager = new AppPreferencesManager(PreferenceManager.getDefaultSharedPreferences(getApplicationContext()));
|
||||
prefManager.setAskForStar(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostCreate(Bundle savedInstanceState) {
|
||||
super.onPostCreate(savedInstanceState);
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
if (getSupportActionBar() == null) {
|
||||
setSupportActionBar(toolbar);
|
||||
}
|
||||
|
||||
mDrawerLayout = findViewById(R.id.drawer_layout);
|
||||
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
|
||||
this, mDrawerLayout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
|
||||
mDrawerLayout.addDrawerListener(toggle);
|
||||
toggle.syncState();
|
||||
|
||||
mNavigationView = findViewById(R.id.nav_view);
|
||||
mNavigationView.setNavigationItemSelectedListener(this);
|
||||
|
||||
selectNavigationItem(getNavigationDrawerID());
|
||||
|
||||
}
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
isVisible=true;
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
if(sharedPreferences.getBoolean("pref_DarkMode", false)==TRUE) {
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
|
||||
}else {
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
isVisible=false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
package org.woheller69.weather.activities;
|
||||
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.preference.SeekBarPreference;
|
||||
|
||||
import org.woheller69.weather.R;
|
||||
|
||||
public class SettingsActivity extends NavigationActivity implements SharedPreferences.OnSharedPreferenceChangeListener{
|
||||
|
||||
@Override
|
||||
protected void onRestart() {
|
||||
super.onRestart();
|
||||
|
||||
recreate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
PreferenceManager.getDefaultSharedPreferences(this.getApplicationContext()).registerOnSharedPreferenceChangeListener(this);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_settings);
|
||||
|
||||
overridePendingTransition(0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getNavigationDrawerID() {
|
||||
return R.id.nav_settings;
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.Q)
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This fragment shows general preferences only. It is used when the
|
||||
* activity is showing a two-pane settings UI.
|
||||
*/
|
||||
public static class GeneralPreferenceFragment extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
setPreferencesFromResource(R.xml.pref_general, rootKey);
|
||||
}
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
|
||||
super.onPause();
|
||||
}
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
if (key.equals("pref_number_days")){
|
||||
SeekBarPreference numberDays = findPreference("pref_number_days");
|
||||
if (numberDays.getValue()<3) numberDays.setValue(3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package org.woheller69.weather.activities;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import org.woheller69.weather.firststart.TutorialActivity;
|
||||
import org.woheller69.weather.preferences.AppPreferencesManager;
|
||||
|
||||
/**
|
||||
* Created by yonjuni on 24.10.16.
|
||||
*/
|
||||
|
||||
public class SplashActivity extends AppCompatActivity {
|
||||
private AppPreferencesManager prefManager;
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
prefManager = new AppPreferencesManager(PreferenceManager.getDefaultSharedPreferences(this));
|
||||
if (prefManager.isFirstTimeLaunch(this)){ //First time got to TutorialActivity
|
||||
Intent mainIntent = new Intent(SplashActivity.this, TutorialActivity.class);
|
||||
SplashActivity.this.startActivity(mainIntent);
|
||||
} else { //otherwise directly start ForecastCityActivity
|
||||
Intent mainIntent = new Intent(SplashActivity.this, ForecastCityActivity.class);
|
||||
SplashActivity.this.startActivity(mainIntent);
|
||||
}
|
||||
SplashActivity.this.finish();
|
||||
}
|
||||
|
||||
}
|
||||
76
app/src/main/java/org/woheller69/weather/database/City.java
Normal file
76
app/src/main/java/org/woheller69/weather/database/City.java
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
package org.woheller69.weather.database;
|
||||
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Created by yonjuni on 04.01.17.
|
||||
* data object for city
|
||||
* <p>
|
||||
* Structure taken from the old orm package from previous versions of this app.
|
||||
*/
|
||||
|
||||
public class City {
|
||||
|
||||
private int cityId;
|
||||
private String cityName;
|
||||
private String countryCode;
|
||||
private float lon;
|
||||
private float lat;
|
||||
|
||||
public City() {
|
||||
}
|
||||
|
||||
public City(int cityId, String cityName, String countryCode, float lon, float lat) {
|
||||
this.cityId = cityId;
|
||||
this.cityName = cityName;
|
||||
this.countryCode = countryCode;
|
||||
this.lon = lon;
|
||||
this.lat = lat;
|
||||
}
|
||||
|
||||
public int getCityId() {
|
||||
return cityId;
|
||||
}
|
||||
|
||||
public void setCityId(int cityId) {
|
||||
this.cityId = cityId;
|
||||
}
|
||||
|
||||
public String getCityName() {
|
||||
return cityName;
|
||||
}
|
||||
|
||||
public void setCityName(String cityName) {
|
||||
this.cityName = cityName;
|
||||
}
|
||||
|
||||
public String getCountryCode() {
|
||||
return countryCode;
|
||||
}
|
||||
|
||||
public void setCountryCode(String countryCode) {
|
||||
this.countryCode = countryCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(Locale.getDefault(),"%s, %s (%.2f / %.2f)", cityName, countryCode, lat, lon);
|
||||
}
|
||||
|
||||
public void setLatitude(float latitude) {
|
||||
lat = latitude;
|
||||
}
|
||||
|
||||
public float getLatitude() {
|
||||
return lat;
|
||||
}
|
||||
|
||||
public float getLongitude() {
|
||||
return lon;
|
||||
}
|
||||
|
||||
public void setLongitude(float lon) {
|
||||
this.lon = lon;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
package org.woheller69.weather.database;
|
||||
|
||||
/**
|
||||
* This class is the database model for the cities to watch. 'Cities to watch' means the locations
|
||||
* for which a user would like to see the weather for. This includes those locations that will be
|
||||
* deleted after app close (non-persistent locations).
|
||||
*/
|
||||
public class CityToWatch {
|
||||
|
||||
private int id;
|
||||
private int cityId;
|
||||
private String cityName;
|
||||
private float lon;
|
||||
private float lat;
|
||||
private float cellsMaxPower;
|
||||
private float cellsArea;
|
||||
private float cellsEfficiency;
|
||||
private float diffuseEfficiency;
|
||||
private float converterPowerLimit;
|
||||
private float converterEfficiency;
|
||||
private float azimuthAngle;
|
||||
private float elevationAngle;
|
||||
private int rank;
|
||||
|
||||
public CityToWatch() {
|
||||
}
|
||||
|
||||
public CityToWatch(int rank, int id, int cityId, float lon, float lat, String cityName) {
|
||||
this.rank = rank;
|
||||
this.lon = lon;
|
||||
this.lat = lat;
|
||||
this.id = id;
|
||||
this.cityId = cityId;
|
||||
this.cityName = cityName;
|
||||
this.cellsMaxPower=650;
|
||||
this.cellsArea=3.18f;
|
||||
this.cellsEfficiency=19.3f;
|
||||
this.diffuseEfficiency=40;
|
||||
this.converterPowerLimit=600;
|
||||
this.converterEfficiency=95;
|
||||
this.azimuthAngle=170;
|
||||
this.elevationAngle=90;
|
||||
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getCityId() {
|
||||
return cityId;
|
||||
}
|
||||
|
||||
public void setCityId(int cityId) {
|
||||
this.cityId = cityId;
|
||||
}
|
||||
|
||||
public String getCityName() {
|
||||
return cityName;
|
||||
}
|
||||
|
||||
public void setCityName(String cityName) {
|
||||
this.cityName = cityName;
|
||||
}
|
||||
|
||||
public int getRank() {
|
||||
return rank;
|
||||
}
|
||||
|
||||
public void setRank(int rank) {
|
||||
this.rank = rank;
|
||||
}
|
||||
|
||||
public void setLongitude(float lon) { this.lon = lon; }
|
||||
|
||||
public float getLongitude() { return lon; }
|
||||
|
||||
public float getLatitude() { return lat; }
|
||||
|
||||
public void setLatitude(float lat) { this.lat = lat; }
|
||||
|
||||
public float getCellsMaxPower() {
|
||||
return cellsMaxPower;
|
||||
}
|
||||
|
||||
public float getCellsArea() {
|
||||
return cellsArea;
|
||||
}
|
||||
|
||||
public float getAzimuthAngle() {
|
||||
return azimuthAngle;
|
||||
}
|
||||
|
||||
public float getCellsEfficiency() {
|
||||
return cellsEfficiency;
|
||||
}
|
||||
|
||||
public float getDiffuseEfficiency() {
|
||||
return diffuseEfficiency;
|
||||
}
|
||||
|
||||
public float getConverterEfficiency() {
|
||||
return converterEfficiency;
|
||||
}
|
||||
|
||||
public float getConverterPowerLimit() {
|
||||
return converterPowerLimit;
|
||||
}
|
||||
|
||||
public float getElevationAngle() {
|
||||
return elevationAngle;
|
||||
}
|
||||
|
||||
public void setCellsMaxPower(float cellsMaxPower) {
|
||||
this.cellsMaxPower = cellsMaxPower;
|
||||
}
|
||||
|
||||
public void setCellsArea(float cellsArea) {
|
||||
this.cellsArea = cellsArea;
|
||||
}
|
||||
|
||||
public void setAzimuthAngle(float azimuthAngle) {
|
||||
this.azimuthAngle = azimuthAngle;
|
||||
}
|
||||
|
||||
public void setCellsEfficiency(float cellsEfficiency) {
|
||||
this.cellsEfficiency = cellsEfficiency;
|
||||
}
|
||||
|
||||
public void setConverterEfficiency(float converterEfficiency) {
|
||||
this.converterEfficiency = converterEfficiency;
|
||||
}
|
||||
|
||||
public void setConverterPowerLimit(float converterPowerLimit) {
|
||||
this.converterPowerLimit = converterPowerLimit;
|
||||
}
|
||||
|
||||
public void setDiffuseEfficiency(float diffuseEfficiency) {
|
||||
this.diffuseEfficiency = diffuseEfficiency;
|
||||
}
|
||||
|
||||
public void setElevationAngle(float elevationAngle) {
|
||||
this.elevationAngle = elevationAngle;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
package org.woheller69.weather.database;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* This class represents the database model for current weather data of cities.
|
||||
*/
|
||||
|
||||
public class CurrentWeatherData {
|
||||
|
||||
private int id;
|
||||
private int city_id;
|
||||
private long timestamp;
|
||||
private int weatherID;
|
||||
private float temperatureCurrent;
|
||||
private float humidity;
|
||||
private float pressure;
|
||||
private float windSpeed;
|
||||
private float windDirection;
|
||||
private float cloudiness;
|
||||
private long timeSunrise;
|
||||
private long timeSunset;
|
||||
private int timeZoneSeconds;
|
||||
private String Rain60min;
|
||||
|
||||
private String city_name;
|
||||
|
||||
public CurrentWeatherData() {
|
||||
this.city_id = Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
public CurrentWeatherData(int id, int city_id, long timestamp, int weatherID, float temperatureCurrent, float temperatureMin, float temperatureMax, float humidity, float pressure, float windSpeed, float windDirection, float cloudiness, long timeSunrise, long timeSunset, int timeZoneSeconds) {
|
||||
this.id = id;
|
||||
this.city_id = city_id;
|
||||
this.timestamp = timestamp;
|
||||
this.weatherID = weatherID;
|
||||
this.temperatureCurrent = temperatureCurrent;
|
||||
this.humidity = humidity;
|
||||
this.pressure = pressure;
|
||||
this.windSpeed = windSpeed;
|
||||
this.windDirection = windDirection;
|
||||
this.cloudiness = cloudiness;
|
||||
this.timeSunrise = timeSunrise;
|
||||
this.timeSunset = timeSunset;
|
||||
this.timeZoneSeconds = timeZoneSeconds;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getCity_id() {
|
||||
return city_id;
|
||||
}
|
||||
|
||||
public void setCity_id(int city_id) {
|
||||
this.city_id = city_id;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public int getWeatherID() {
|
||||
return weatherID;
|
||||
}
|
||||
|
||||
public void setWeatherID(int weatherID) {
|
||||
this.weatherID = weatherID;
|
||||
}
|
||||
|
||||
public float getTemperatureCurrent() {
|
||||
return temperatureCurrent;
|
||||
}
|
||||
|
||||
public void setTemperatureCurrent(float temperatureCurrent) {
|
||||
this.temperatureCurrent = temperatureCurrent;
|
||||
}
|
||||
|
||||
public float getHumidity() {
|
||||
return humidity;
|
||||
}
|
||||
|
||||
public void setHumidity(float humidity) {
|
||||
this.humidity = humidity;
|
||||
}
|
||||
|
||||
public float getPressure() {
|
||||
return pressure;
|
||||
}
|
||||
|
||||
public void setPressure(float pressure) {
|
||||
this.pressure = pressure;
|
||||
}
|
||||
|
||||
public float getWindSpeed() {
|
||||
return windSpeed;
|
||||
}
|
||||
|
||||
public void setWindSpeed(float windSpeed) {
|
||||
this.windSpeed = windSpeed;
|
||||
}
|
||||
|
||||
public float getWindDirection() {
|
||||
return windDirection;
|
||||
}
|
||||
|
||||
public void setWindDirection(float windDirection) {
|
||||
this.windDirection = windDirection;
|
||||
}
|
||||
|
||||
public float getCloudiness() {
|
||||
return cloudiness;
|
||||
}
|
||||
|
||||
public void setCloudiness(float cloudiness) {
|
||||
this.cloudiness = cloudiness;
|
||||
}
|
||||
|
||||
public boolean isDay(Context context){
|
||||
Calendar timeStamp = Calendar.getInstance();
|
||||
timeStamp.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
timeStamp.setTimeInMillis((timestamp+timeZoneSeconds)*1000);
|
||||
SQLiteHelper dbHelper = SQLiteHelper.getInstance(context);
|
||||
if (timeSunrise==0 || timeSunset==0){
|
||||
if ((dbHelper.getCityToWatch(city_id).getLatitude())>0){ //northern hemisphere
|
||||
return timeStamp.get(Calendar.DAY_OF_YEAR) >= 80 && timeStamp.get(Calendar.DAY_OF_YEAR) <= 265; //from March 21 to September 22 (incl)
|
||||
}else{ //southern hemisphere
|
||||
return timeStamp.get(Calendar.DAY_OF_YEAR) < 80 || timeStamp.get(Calendar.DAY_OF_YEAR) > 265;
|
||||
}
|
||||
}else {
|
||||
return timestamp > timeSunrise && timestamp < timeSunset;
|
||||
}
|
||||
}
|
||||
|
||||
public long getTimeSunrise() { return timeSunrise; }
|
||||
|
||||
public void setTimeSunrise(long timeSunrise) {
|
||||
this.timeSunrise = timeSunrise;
|
||||
}
|
||||
|
||||
public long getTimeSunset() {
|
||||
return timeSunset;
|
||||
}
|
||||
|
||||
public void setTimeSunset(long timeSunset) {
|
||||
this.timeSunset = timeSunset;
|
||||
}
|
||||
|
||||
public String getCity_name() {
|
||||
return city_name;
|
||||
}
|
||||
|
||||
public void setCity_name(String city_name) {
|
||||
this.city_name = city_name;
|
||||
}
|
||||
|
||||
public int getTimeZoneSeconds() {
|
||||
return timeZoneSeconds;
|
||||
}
|
||||
|
||||
public void setTimeZoneSeconds(int timeZoneSeconds) {
|
||||
this.timeZoneSeconds = timeZoneSeconds;
|
||||
}
|
||||
|
||||
public String getRain60min() {
|
||||
return Rain60min;
|
||||
}
|
||||
|
||||
public void setRain60min(String Rain60min) {
|
||||
this.Rain60min = Rain60min;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
package org.woheller69.weather.database;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* This class is the database model for the forecasts table.
|
||||
*/
|
||||
public class HourlyForecast {
|
||||
|
||||
public static final float NO_RAIN_VALUE = 0;
|
||||
private int id;
|
||||
private int city_id;
|
||||
private long timestamp;
|
||||
private long forecastFor;
|
||||
private int weatherID;
|
||||
private float directRadiationNormal;
|
||||
private float diffuseRadiation;
|
||||
private float power;
|
||||
private String city_name;
|
||||
|
||||
|
||||
public HourlyForecast() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the ID of the record (which uniquely identifies the record).
|
||||
*/
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the date and time for the forecast.
|
||||
*/
|
||||
public long getForecastTime() {
|
||||
return forecastFor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the local time for the forecast in UTC epoch
|
||||
*/
|
||||
public long getLocalForecastTime(Context context) {
|
||||
SQLiteHelper dbhelper = SQLiteHelper.getInstance(context);
|
||||
int timezoneseconds = dbhelper.getCurrentWeatherByCityId(city_id).getTimeZoneSeconds();
|
||||
return forecastFor + timezoneseconds * 1000L;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param forecastFor The point of time for the forecast.
|
||||
*/
|
||||
public void setForecastTime(long forecastFor) {
|
||||
this.forecastFor = forecastFor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the point of time when the data was inserted into the database in Unix, UTC.
|
||||
*/
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param timestamp The point of time to set when the data was inserted into the database in
|
||||
* Unix, UTC.
|
||||
*/
|
||||
public void setTimestamp(long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public int getCity_id() {
|
||||
return city_id;
|
||||
}
|
||||
|
||||
public void setCity_id(int city_id) {
|
||||
this.city_id = city_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the weather condition ID.
|
||||
*/
|
||||
public int getWeatherID() {
|
||||
return weatherID;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param weatherID The weather condition ID to set.
|
||||
*/
|
||||
public void setWeatherID(int weatherID) {
|
||||
this.weatherID = weatherID;
|
||||
}
|
||||
|
||||
public float getDiffuseRadiation() { return diffuseRadiation; }
|
||||
|
||||
public float getDirectRadiationNormal() { return directRadiationNormal; }
|
||||
|
||||
public float getPower() { return power; }
|
||||
|
||||
public void setDirectRadiationNormal(float directRadiationNormal) { this.directRadiationNormal = directRadiationNormal; }
|
||||
|
||||
public void setDiffuseRadiation(float diffuseRadiation) { this.diffuseRadiation = diffuseRadiation; }
|
||||
|
||||
public void setPower(float power) { this.power = power; }
|
||||
}
|
||||
|
|
@ -0,0 +1,665 @@
|
|||
package org.woheller69.weather.database;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static androidx.core.app.JobIntentService.enqueueWork;
|
||||
|
||||
/**
|
||||
* @author Karola Marky, Christopher Beckmann
|
||||
* @version 1.0
|
||||
* @since 25.01.2018
|
||||
* created 02.01.2017
|
||||
*/
|
||||
public class SQLiteHelper extends SQLiteOpenHelper {
|
||||
|
||||
private static final int DATABASE_VERSION = 1;
|
||||
private Context context;
|
||||
|
||||
private List<City> allCities = new ArrayList<>();
|
||||
|
||||
private static SQLiteHelper instance = null;
|
||||
|
||||
private static final String DATABASE_NAME = "SQLITE.db";
|
||||
|
||||
//Names of tables in the database
|
||||
private static final String TABLE_CITIES_TO_WATCH = "CITIES_TO_WATCH";
|
||||
private static final String TABLE_HOURLY_FORECAST = "FORECASTS";
|
||||
private static final String TABLE_WEEKFORECAST = "WEEKFORECASTS";
|
||||
private static final String TABLE_CURRENT_WEATHER = "CURRENT_WEATHER";
|
||||
|
||||
|
||||
//Names of columns in TABLE_CITIES_TO_WATCH
|
||||
private static final String CITIES_TO_WATCH_ID = "cities_to_watch_id";
|
||||
private static final String CITIES_TO_WATCH_CITY_ID = "city_id";
|
||||
private static final String CITIES_TO_WATCH_COLUMN_RANK = "rank";
|
||||
private static final String CITIES_TO_WATCH_NAME = "city_name";
|
||||
private static final String CITIES_TO_WATCH_LONGITUDE = "longitude";
|
||||
private static final String CITIES_TO_WATCH_LATITUDE = "latitude";
|
||||
private static final String CITIES_TO_WATCH_CELLS_MAX_POWER = "cells_max_pPower";
|
||||
private static final String CITIES_TO_WATCH_CELLS_AREA = "cells_area";
|
||||
private static final String CITIES_TO_WATCH_CELLS_EFFICIENCY = "cells_efficiency";
|
||||
private static final String CITIES_TO_WATCH_DIFFUSE_EFFICIENCY = "diffuse_efficiency";
|
||||
private static final String CITIES_TO_WATCH_CONVERTER_POWER_LIMIT = "converter_power_limit";
|
||||
private static final String CITIES_TO_WATCH_CONVERTER_EFFICIENCY = "converter_efficiency";
|
||||
private static final String CITIES_TO_WATCH_AZIMUTH_ANGLE = "azimuth_angle";
|
||||
private static final String CITIES_TO_WATCH_ELEVATION_ANGLE = "elevation_angle";
|
||||
|
||||
//Names of columns in TABLE_FORECAST
|
||||
private static final String FORECAST_ID = "forecast_id";
|
||||
private static final String FORECAST_CITY_ID = "city_id";
|
||||
private static final String FORECAST_COLUMN_TIME_MEASUREMENT = "time_of_measurement";
|
||||
private static final String FORECAST_COLUMN_FORECAST_FOR = "forecast_for";
|
||||
private static final String FORECAST_COLUMN_WEATHER_ID = "weather_id";
|
||||
private static final String FORECAST_COLUMN_DIRECT_RADIATION_NORMAL = "direct_radiation_normal";
|
||||
private static final String FORECAST_COLUMN_DIFFUSE_RADIATION = "diffuse_radiation";
|
||||
private static final String FORECAST_COLUMN_POWER = "power";
|
||||
|
||||
|
||||
//Names of columns in TABLE_WEEKFORECAST
|
||||
private static final String WEEKFORECAST_ID = "forecast_id";
|
||||
private static final String WEEKFORECAST_CITY_ID = "city_id";
|
||||
private static final String WEEKFORECAST_COLUMN_TIME_MEASUREMENT = "time_of_measurement";
|
||||
private static final String WEEKFORECAST_COLUMN_FORECAST_FOR = "forecast_for";
|
||||
private static final String WEEKFORECAST_COLUMN_WEATHER_ID = "weather_id";
|
||||
private static final String WEEKFORECAST_COLUMN_TEMPERATURE_CURRENT = "temperature_current";
|
||||
private static final String WEEKFORECAST_COLUMN_TEMPERATURE_MIN = "temperature_min";
|
||||
private static final String WEEKFORECAST_COLUMN_TEMPERATURE_MAX = "temperature_max";
|
||||
private static final String WEEKFORECAST_COLUMN_HUMIDITY = "humidity";
|
||||
private static final String WEEKFORECAST_COLUMN_PRESSURE = "pressure";
|
||||
private static final String WEEKFORECAST_COLUMN_PRECIPITATION = "precipitation";
|
||||
private static final String WEEKFORECAST_COLUMN_WIND_SPEED = "wind_speed";
|
||||
private static final String WEEKFORECAST_COLUMN_WIND_DIRECTION = "wind_direction";
|
||||
private static final String WEEKFORECAST_COLUMN_UV_INDEX = "uv_index";
|
||||
private static final String WEEKFORECAST_COLUMN_TIME_SUNRISE = "time_sunrise";
|
||||
private static final String WEEKFORECAST_COLUMN_TIME_SUNSET = "time_sunset";
|
||||
|
||||
|
||||
//Names of columns in TABLE_CURRENT_WEATHER
|
||||
private static final String CURRENT_WEATHER_ID = "current_weather_id";
|
||||
private static final String CURRENT_WEATHER_CITY_ID = "city_id";
|
||||
private static final String COLUMN_TIME_MEASUREMENT = "time_of_measurement";
|
||||
private static final String COLUMN_WEATHER_ID = "weather_id";
|
||||
private static final String COLUMN_TEMPERATURE_CURRENT = "temperature_current";
|
||||
private static final String COLUMN_HUMIDITY = "humidity";
|
||||
private static final String COLUMN_PRESSURE = "pressure";
|
||||
private static final String COLUMN_WIND_SPEED = "wind_speed";
|
||||
private static final String COLUMN_WIND_DIRECTION = "wind_direction";
|
||||
private static final String COLUMN_CLOUDINESS = "cloudiness";
|
||||
private static final String COLUMN_TIME_SUNRISE = "time_sunrise";
|
||||
private static final String COLUMN_TIME_SUNSET = "time_sunset";
|
||||
private static final String COLUMN_TIMEZONE_SECONDS = "timezone_seconds";
|
||||
private static final String COLUMN_RAIN60MIN = "Rain60min";
|
||||
|
||||
/**
|
||||
* Create Table statements for all tables
|
||||
*/
|
||||
private static final String CREATE_CURRENT_WEATHER = "CREATE TABLE " + TABLE_CURRENT_WEATHER +
|
||||
"(" +
|
||||
CURRENT_WEATHER_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
|
||||
CURRENT_WEATHER_CITY_ID + " INTEGER," +
|
||||
COLUMN_TIME_MEASUREMENT + " LONG NOT NULL," +
|
||||
COLUMN_WEATHER_ID + " INTEGER," +
|
||||
COLUMN_TEMPERATURE_CURRENT + " REAL," +
|
||||
COLUMN_HUMIDITY + " REAL," +
|
||||
COLUMN_PRESSURE + " REAL," +
|
||||
COLUMN_WIND_SPEED + " REAL," +
|
||||
COLUMN_WIND_DIRECTION + " REAL," +
|
||||
COLUMN_CLOUDINESS + " REAL," +
|
||||
COLUMN_TIME_SUNRISE + " LONG NOT NULL," +
|
||||
COLUMN_TIME_SUNSET + " LONG NOT NULL," +
|
||||
COLUMN_TIMEZONE_SECONDS + " INTEGER," +
|
||||
COLUMN_RAIN60MIN + " VARCHAR(25) NOT NULL) ;";
|
||||
|
||||
|
||||
private static final String CREATE_TABLE_FORECASTS = "CREATE TABLE " + TABLE_HOURLY_FORECAST +
|
||||
"(" +
|
||||
FORECAST_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
|
||||
FORECAST_CITY_ID + " INTEGER," +
|
||||
FORECAST_COLUMN_TIME_MEASUREMENT + " LONG NOT NULL," +
|
||||
FORECAST_COLUMN_FORECAST_FOR + " VARCHAR(200) NOT NULL," +
|
||||
FORECAST_COLUMN_WEATHER_ID + " INTEGER," +
|
||||
FORECAST_COLUMN_DIRECT_RADIATION_NORMAL + " REAL," +
|
||||
FORECAST_COLUMN_DIFFUSE_RADIATION + " REAL," +
|
||||
FORECAST_COLUMN_POWER + " REAL)";
|
||||
|
||||
private static final String CREATE_TABLE_WEEKFORECASTS = "CREATE TABLE " + TABLE_WEEKFORECAST +
|
||||
"(" +
|
||||
WEEKFORECAST_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
|
||||
WEEKFORECAST_CITY_ID + " INTEGER," +
|
||||
WEEKFORECAST_COLUMN_TIME_MEASUREMENT + " LONG NOT NULL," +
|
||||
WEEKFORECAST_COLUMN_FORECAST_FOR + " VARCHAR(200) NOT NULL," +
|
||||
WEEKFORECAST_COLUMN_WEATHER_ID + " INTEGER," +
|
||||
WEEKFORECAST_COLUMN_TEMPERATURE_CURRENT + " REAL," +
|
||||
WEEKFORECAST_COLUMN_TEMPERATURE_MIN + " REAL," +
|
||||
WEEKFORECAST_COLUMN_TEMPERATURE_MAX + " REAL," +
|
||||
WEEKFORECAST_COLUMN_HUMIDITY + " REAL," +
|
||||
WEEKFORECAST_COLUMN_PRESSURE + " REAL," +
|
||||
WEEKFORECAST_COLUMN_PRECIPITATION + " REAL," +
|
||||
WEEKFORECAST_COLUMN_WIND_SPEED + " REAL," +
|
||||
WEEKFORECAST_COLUMN_WIND_DIRECTION + " REAL," +
|
||||
WEEKFORECAST_COLUMN_UV_INDEX + " REAL," +
|
||||
WEEKFORECAST_COLUMN_TIME_SUNRISE + " LONG NOT NULL," +
|
||||
WEEKFORECAST_COLUMN_TIME_SUNSET + " LONG NOT NULL)";
|
||||
|
||||
private static final String CREATE_TABLE_CITIES_TO_WATCH = "CREATE TABLE " + TABLE_CITIES_TO_WATCH +
|
||||
"(" +
|
||||
CITIES_TO_WATCH_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
|
||||
CITIES_TO_WATCH_CITY_ID + " INTEGER," +
|
||||
CITIES_TO_WATCH_COLUMN_RANK + " INTEGER," +
|
||||
CITIES_TO_WATCH_NAME + " VARCHAR(100) NOT NULL," +
|
||||
CITIES_TO_WATCH_LONGITUDE + " REAL NOT NULL," +
|
||||
CITIES_TO_WATCH_LATITUDE + " REAL NOT NULL," +
|
||||
CITIES_TO_WATCH_CELLS_MAX_POWER + " REAL NOT NULL," +
|
||||
CITIES_TO_WATCH_CELLS_AREA + " REAL NOT NULL," +
|
||||
CITIES_TO_WATCH_CELLS_EFFICIENCY + " REAL NOT NULL," +
|
||||
CITIES_TO_WATCH_DIFFUSE_EFFICIENCY + " REAL NOT NULL," +
|
||||
CITIES_TO_WATCH_CONVERTER_POWER_LIMIT + " REAL NOT NULL," +
|
||||
CITIES_TO_WATCH_CONVERTER_EFFICIENCY + " REAL NOT NULL," +
|
||||
CITIES_TO_WATCH_AZIMUTH_ANGLE + " REAL NOT NULL," +
|
||||
CITIES_TO_WATCH_ELEVATION_ANGLE + " REAL NOT NULL)";
|
||||
|
||||
public static SQLiteHelper getInstance(Context context) {
|
||||
if (instance == null && context != null) {
|
||||
instance = new SQLiteHelper(context.getApplicationContext());
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private SQLiteHelper(Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
this.context = context.getApplicationContext();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
db.execSQL(CREATE_TABLE_CITIES_TO_WATCH);
|
||||
db.execSQL(CREATE_CURRENT_WEATHER);
|
||||
db.execSQL(CREATE_TABLE_FORECASTS);
|
||||
db.execSQL(CREATE_TABLE_WEEKFORECASTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Methods for TABLE_CITIES_TO_WATCH
|
||||
*/
|
||||
public synchronized long addCityToWatch(CityToWatch city) {
|
||||
SQLiteDatabase database = this.getWritableDatabase();
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(CITIES_TO_WATCH_CITY_ID, city.getCityId());
|
||||
values.put(CITIES_TO_WATCH_COLUMN_RANK, city.getRank());
|
||||
values.put(CITIES_TO_WATCH_NAME,city.getCityName());
|
||||
values.put(CITIES_TO_WATCH_LATITUDE,city.getLatitude());
|
||||
values.put(CITIES_TO_WATCH_LONGITUDE,city.getLongitude());
|
||||
values.put(CITIES_TO_WATCH_CELLS_MAX_POWER,city.getCellsMaxPower());
|
||||
values.put(CITIES_TO_WATCH_CELLS_AREA,city.getCellsArea());
|
||||
values.put(CITIES_TO_WATCH_CELLS_EFFICIENCY,city.getCellsEfficiency());
|
||||
values.put(CITIES_TO_WATCH_DIFFUSE_EFFICIENCY,city.getDiffuseEfficiency());
|
||||
values.put(CITIES_TO_WATCH_CONVERTER_POWER_LIMIT,city.getConverterPowerLimit());
|
||||
values.put(CITIES_TO_WATCH_CONVERTER_EFFICIENCY,city.getConverterEfficiency());
|
||||
values.put(CITIES_TO_WATCH_AZIMUTH_ANGLE,city.getAzimuthAngle());
|
||||
values.put(CITIES_TO_WATCH_ELEVATION_ANGLE,city.getElevationAngle());
|
||||
|
||||
long id=database.insert(TABLE_CITIES_TO_WATCH, null, values);
|
||||
|
||||
//use id also instead of city id as unique identifier
|
||||
values.put(CITIES_TO_WATCH_CITY_ID,id);
|
||||
database.update(TABLE_CITIES_TO_WATCH, values, CITIES_TO_WATCH_ID + " = ?",
|
||||
new String[]{String.valueOf(id)});
|
||||
|
||||
database.close();
|
||||
return id;
|
||||
}
|
||||
|
||||
public synchronized CityToWatch getCityToWatch(int id) {
|
||||
SQLiteDatabase database = this.getWritableDatabase();
|
||||
|
||||
String[] arguments = {String.valueOf(id)};
|
||||
|
||||
Cursor cursor = database.rawQuery(
|
||||
"SELECT " + CITIES_TO_WATCH_ID +
|
||||
", " + CITIES_TO_WATCH_CITY_ID +
|
||||
", " + CITIES_TO_WATCH_NAME +
|
||||
", " + CITIES_TO_WATCH_LONGITUDE +
|
||||
", " + CITIES_TO_WATCH_LATITUDE +
|
||||
", " + CITIES_TO_WATCH_CELLS_MAX_POWER +
|
||||
", " + CITIES_TO_WATCH_CELLS_AREA +
|
||||
", " + CITIES_TO_WATCH_CELLS_EFFICIENCY +
|
||||
", " + CITIES_TO_WATCH_DIFFUSE_EFFICIENCY +
|
||||
", " + CITIES_TO_WATCH_CONVERTER_POWER_LIMIT +
|
||||
", " + CITIES_TO_WATCH_CONVERTER_EFFICIENCY +
|
||||
", " + CITIES_TO_WATCH_AZIMUTH_ANGLE +
|
||||
", " + CITIES_TO_WATCH_ELEVATION_ANGLE +
|
||||
", " + CITIES_TO_WATCH_COLUMN_RANK +
|
||||
" FROM " + TABLE_CITIES_TO_WATCH +
|
||||
" WHERE " + CITIES_TO_WATCH_CITY_ID + " = ?", arguments);
|
||||
|
||||
CityToWatch cityToWatch = new CityToWatch();
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
cityToWatch.setId(Integer.parseInt(cursor.getString(0)));
|
||||
cityToWatch.setCityId(Integer.parseInt(cursor.getString(1)));
|
||||
cityToWatch.setCityName(cursor.getString(2));
|
||||
cityToWatch.setLongitude(Float.parseFloat(cursor.getString(3)));
|
||||
cityToWatch.setLatitude(Float.parseFloat(cursor.getString(4)));
|
||||
cityToWatch.setCellsMaxPower(Float.parseFloat(cursor.getString(5)));
|
||||
cityToWatch.setCellsArea(Float.parseFloat(cursor.getString(6)));
|
||||
cityToWatch.setCellsEfficiency(Float.parseFloat(cursor.getString(7)));
|
||||
cityToWatch.setDiffuseEfficiency(Float.parseFloat(cursor.getString(8)));
|
||||
cityToWatch.setConverterPowerLimit(Float.parseFloat(cursor.getString(9)));
|
||||
cityToWatch.setConverterEfficiency(Float.parseFloat(cursor.getString(10)));
|
||||
cityToWatch.setAzimuthAngle(Float.parseFloat(cursor.getString(11)));
|
||||
cityToWatch.setElevationAngle(Float.parseFloat(cursor.getString(12)));
|
||||
cityToWatch.setRank(Integer.parseInt(cursor.getString(13)));
|
||||
|
||||
cursor.close();
|
||||
}
|
||||
database.close();
|
||||
return cityToWatch;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public synchronized List<CityToWatch> getAllCitiesToWatch() {
|
||||
List<CityToWatch> cityToWatchList = new ArrayList<>();
|
||||
|
||||
SQLiteDatabase database = this.getWritableDatabase();
|
||||
|
||||
Cursor cursor = database.rawQuery(
|
||||
"SELECT " + CITIES_TO_WATCH_ID +
|
||||
", " + CITIES_TO_WATCH_CITY_ID +
|
||||
", " + CITIES_TO_WATCH_NAME +
|
||||
", " + CITIES_TO_WATCH_LONGITUDE +
|
||||
", " + CITIES_TO_WATCH_LATITUDE +
|
||||
", " + CITIES_TO_WATCH_CELLS_MAX_POWER +
|
||||
", " + CITIES_TO_WATCH_CELLS_AREA +
|
||||
", " + CITIES_TO_WATCH_CELLS_EFFICIENCY +
|
||||
", " + CITIES_TO_WATCH_DIFFUSE_EFFICIENCY +
|
||||
", " + CITIES_TO_WATCH_CONVERTER_POWER_LIMIT +
|
||||
", " + CITIES_TO_WATCH_CONVERTER_EFFICIENCY +
|
||||
", " + CITIES_TO_WATCH_AZIMUTH_ANGLE +
|
||||
", " + CITIES_TO_WATCH_ELEVATION_ANGLE +
|
||||
", " + CITIES_TO_WATCH_COLUMN_RANK +
|
||||
" FROM " + TABLE_CITIES_TO_WATCH
|
||||
, new String[]{});
|
||||
|
||||
CityToWatch cityToWatch;
|
||||
|
||||
if (cursor.moveToFirst()) {
|
||||
do {
|
||||
cityToWatch = new CityToWatch();
|
||||
cityToWatch.setId(Integer.parseInt(cursor.getString(0)));
|
||||
cityToWatch.setCityId(Integer.parseInt(cursor.getString(1)));
|
||||
cityToWatch.setCityName(cursor.getString(2));
|
||||
cityToWatch.setLongitude(Float.parseFloat(cursor.getString(3)));
|
||||
cityToWatch.setLatitude(Float.parseFloat(cursor.getString(4)));
|
||||
cityToWatch.setCellsMaxPower(Float.parseFloat(cursor.getString(5)));
|
||||
cityToWatch.setCellsArea(Float.parseFloat(cursor.getString(6)));
|
||||
cityToWatch.setCellsEfficiency(Float.parseFloat(cursor.getString(7)));
|
||||
cityToWatch.setDiffuseEfficiency(Float.parseFloat(cursor.getString(8)));
|
||||
cityToWatch.setConverterPowerLimit(Float.parseFloat(cursor.getString(9)));
|
||||
cityToWatch.setConverterEfficiency(Float.parseFloat(cursor.getString(10)));
|
||||
cityToWatch.setAzimuthAngle(Float.parseFloat(cursor.getString(11)));
|
||||
cityToWatch.setElevationAngle(Float.parseFloat(cursor.getString(12)));
|
||||
cityToWatch.setRank(Integer.parseInt(cursor.getString(13)));
|
||||
|
||||
cityToWatchList.add(cityToWatch);
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
|
||||
cursor.close();
|
||||
database.close();
|
||||
return cityToWatchList;
|
||||
}
|
||||
|
||||
public synchronized void updateCityToWatch(CityToWatch cityToWatch) {
|
||||
SQLiteDatabase database = this.getWritableDatabase();
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(CITIES_TO_WATCH_CITY_ID, cityToWatch.getCityId());
|
||||
values.put(CITIES_TO_WATCH_COLUMN_RANK, cityToWatch.getRank());
|
||||
values.put(CITIES_TO_WATCH_NAME,cityToWatch.getCityName());
|
||||
values.put(CITIES_TO_WATCH_LATITUDE,cityToWatch.getLatitude());
|
||||
values.put(CITIES_TO_WATCH_LONGITUDE,cityToWatch.getLongitude());
|
||||
values.put(CITIES_TO_WATCH_CELLS_MAX_POWER,cityToWatch.getCellsMaxPower());
|
||||
values.put(CITIES_TO_WATCH_CELLS_AREA,cityToWatch.getCellsArea());
|
||||
values.put(CITIES_TO_WATCH_CELLS_EFFICIENCY,cityToWatch.getCellsEfficiency());
|
||||
values.put(CITIES_TO_WATCH_DIFFUSE_EFFICIENCY,cityToWatch.getDiffuseEfficiency());
|
||||
values.put(CITIES_TO_WATCH_CONVERTER_POWER_LIMIT,cityToWatch.getConverterPowerLimit());
|
||||
values.put(CITIES_TO_WATCH_CONVERTER_EFFICIENCY,cityToWatch.getConverterEfficiency());
|
||||
values.put(CITIES_TO_WATCH_AZIMUTH_ANGLE,cityToWatch.getAzimuthAngle());
|
||||
values.put(CITIES_TO_WATCH_ELEVATION_ANGLE,cityToWatch.getElevationAngle());
|
||||
|
||||
database.update(TABLE_CITIES_TO_WATCH, values, CITIES_TO_WATCH_ID + " = ?",
|
||||
new String[]{String.valueOf(cityToWatch.getId())});
|
||||
database.close();
|
||||
}
|
||||
|
||||
public synchronized void deleteCityToWatch(CityToWatch cityToWatch) {
|
||||
|
||||
//First delete all weather data for city which is deleted
|
||||
deleteCurrentWeatherByCityId(cityToWatch.getCityId());
|
||||
deleteForecastsByCityId(cityToWatch.getCityId());
|
||||
deleteWeekForecastsByCityId(cityToWatch.getCityId());
|
||||
|
||||
//Now remove city from CITIES_TO_WATCH
|
||||
SQLiteDatabase database = this.getWritableDatabase();
|
||||
database.delete(TABLE_CITIES_TO_WATCH, CITIES_TO_WATCH_ID + " = ?",
|
||||
new String[]{Integer.toString(cityToWatch.getId())});
|
||||
database.close();
|
||||
}
|
||||
|
||||
public synchronized int getWatchedCitiesCount() {
|
||||
SQLiteDatabase database = this.getWritableDatabase();
|
||||
long count = DatabaseUtils.queryNumEntries(database, TABLE_CITIES_TO_WATCH);
|
||||
database.close();
|
||||
return (int) count;
|
||||
}
|
||||
|
||||
public int getMaxRank() {
|
||||
List<CityToWatch> cities = getAllCitiesToWatch();
|
||||
int maxRank = 0;
|
||||
for (CityToWatch ctw : cities) {
|
||||
if (ctw.getRank() > maxRank) maxRank = ctw.getRank();
|
||||
}
|
||||
return maxRank;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Methods for TABLE_FORECAST
|
||||
*/
|
||||
public synchronized void addForecasts(List<HourlyForecast> hourlyForecasts) {
|
||||
SQLiteDatabase database = this.getWritableDatabase();
|
||||
for (HourlyForecast hourlyForecast: hourlyForecasts) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(FORECAST_CITY_ID, hourlyForecast.getCity_id());
|
||||
values.put(FORECAST_COLUMN_TIME_MEASUREMENT, hourlyForecast.getTimestamp());
|
||||
values.put(FORECAST_COLUMN_FORECAST_FOR, hourlyForecast.getForecastTime());
|
||||
values.put(FORECAST_COLUMN_WEATHER_ID, hourlyForecast.getWeatherID());
|
||||
values.put(FORECAST_COLUMN_DIRECT_RADIATION_NORMAL, hourlyForecast.getDirectRadiationNormal());
|
||||
values.put(FORECAST_COLUMN_DIFFUSE_RADIATION, hourlyForecast.getDiffuseRadiation());
|
||||
values.put(FORECAST_COLUMN_POWER, hourlyForecast.getPower());
|
||||
database.insert(TABLE_HOURLY_FORECAST, null, values);
|
||||
}
|
||||
database.close();
|
||||
}
|
||||
|
||||
public synchronized void deleteForecastsByCityId(int cityId) {
|
||||
SQLiteDatabase database = this.getWritableDatabase();
|
||||
database.delete(TABLE_HOURLY_FORECAST, FORECAST_CITY_ID + " = ?",
|
||||
new String[]{Integer.toString(cityId)});
|
||||
database.close();
|
||||
}
|
||||
|
||||
|
||||
public synchronized List<HourlyForecast> getForecastsByCityId(int cityId) {
|
||||
SQLiteDatabase database = this.getWritableDatabase();
|
||||
|
||||
Cursor cursor = database.query(TABLE_HOURLY_FORECAST,
|
||||
new String[]{FORECAST_ID,
|
||||
FORECAST_CITY_ID,
|
||||
FORECAST_COLUMN_TIME_MEASUREMENT,
|
||||
FORECAST_COLUMN_FORECAST_FOR,
|
||||
FORECAST_COLUMN_WEATHER_ID,
|
||||
FORECAST_COLUMN_DIRECT_RADIATION_NORMAL,
|
||||
FORECAST_COLUMN_DIFFUSE_RADIATION,
|
||||
FORECAST_COLUMN_POWER}
|
||||
, FORECAST_CITY_ID + "=?",
|
||||
new String[]{String.valueOf(cityId)}, null, null, null, null);
|
||||
|
||||
List<HourlyForecast> list = new ArrayList<>();
|
||||
HourlyForecast hourlyForecast;
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
hourlyForecast = new HourlyForecast();
|
||||
hourlyForecast.setId(Integer.parseInt(cursor.getString(0)));
|
||||
hourlyForecast.setCity_id(Integer.parseInt(cursor.getString(1)));
|
||||
hourlyForecast.setTimestamp(Long.parseLong(cursor.getString(2)));
|
||||
hourlyForecast.setForecastTime(Long.parseLong(cursor.getString(3)));
|
||||
hourlyForecast.setWeatherID(Integer.parseInt(cursor.getString(4)));
|
||||
hourlyForecast.setDirectRadiationNormal(Float.parseFloat(cursor.getString(5)));
|
||||
hourlyForecast.setDiffuseRadiation(Float.parseFloat(cursor.getString(6)));
|
||||
hourlyForecast.setPower(Float.parseFloat(cursor.getString(7)));
|
||||
list.add(hourlyForecast);
|
||||
} while (cursor.moveToNext());
|
||||
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Methods for TABLE_WEEKFORECAST
|
||||
*/
|
||||
public synchronized void addWeekForecasts(List<WeekForecast> weekForecasts) {
|
||||
SQLiteDatabase database = this.getWritableDatabase();
|
||||
for (WeekForecast weekForecast: weekForecasts) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(WEEKFORECAST_CITY_ID, weekForecast.getCity_id());
|
||||
values.put(WEEKFORECAST_COLUMN_TIME_MEASUREMENT, weekForecast.getTimestamp());
|
||||
values.put(WEEKFORECAST_COLUMN_FORECAST_FOR, weekForecast.getForecastTime());
|
||||
values.put(WEEKFORECAST_COLUMN_WEATHER_ID, weekForecast.getWeatherID());
|
||||
values.put(WEEKFORECAST_COLUMN_TEMPERATURE_CURRENT, weekForecast.getTemperature());
|
||||
values.put(WEEKFORECAST_COLUMN_TEMPERATURE_MIN, weekForecast.getMinTemperature());
|
||||
values.put(WEEKFORECAST_COLUMN_TEMPERATURE_MAX, weekForecast.getMaxTemperature());
|
||||
values.put(WEEKFORECAST_COLUMN_HUMIDITY, weekForecast.getHumidity());
|
||||
values.put(WEEKFORECAST_COLUMN_PRESSURE, weekForecast.getPressure());
|
||||
values.put(WEEKFORECAST_COLUMN_PRECIPITATION, weekForecast.getPrecipitation());
|
||||
values.put(WEEKFORECAST_COLUMN_WIND_SPEED, weekForecast.getWind_speed());
|
||||
values.put(WEEKFORECAST_COLUMN_WIND_DIRECTION, weekForecast.getWind_direction());
|
||||
values.put(WEEKFORECAST_COLUMN_UV_INDEX, weekForecast.getUv_index());
|
||||
values.put(WEEKFORECAST_COLUMN_TIME_SUNRISE, weekForecast.getTimeSunrise());
|
||||
values.put(WEEKFORECAST_COLUMN_TIME_SUNSET, weekForecast.getTimeSunset());
|
||||
database.insert(TABLE_WEEKFORECAST, null, values);
|
||||
}
|
||||
database.close();
|
||||
}
|
||||
|
||||
public synchronized void deleteWeekForecastsByCityId(int cityId) {
|
||||
SQLiteDatabase database = this.getWritableDatabase();
|
||||
database.delete(TABLE_WEEKFORECAST, WEEKFORECAST_CITY_ID + " = ?",
|
||||
new String[]{Integer.toString(cityId)});
|
||||
database.close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public synchronized List<WeekForecast> getWeekForecastsByCityId(int cityId) {
|
||||
SQLiteDatabase database = this.getWritableDatabase();
|
||||
|
||||
Cursor cursor = database.query(TABLE_WEEKFORECAST,
|
||||
new String[]{WEEKFORECAST_ID,
|
||||
WEEKFORECAST_CITY_ID,
|
||||
WEEKFORECAST_COLUMN_TIME_MEASUREMENT,
|
||||
WEEKFORECAST_COLUMN_FORECAST_FOR,
|
||||
WEEKFORECAST_COLUMN_WEATHER_ID,
|
||||
WEEKFORECAST_COLUMN_TEMPERATURE_CURRENT,
|
||||
WEEKFORECAST_COLUMN_TEMPERATURE_MIN,
|
||||
WEEKFORECAST_COLUMN_TEMPERATURE_MAX,
|
||||
WEEKFORECAST_COLUMN_HUMIDITY,
|
||||
WEEKFORECAST_COLUMN_PRESSURE,
|
||||
WEEKFORECAST_COLUMN_PRECIPITATION,
|
||||
WEEKFORECAST_COLUMN_WIND_SPEED,
|
||||
WEEKFORECAST_COLUMN_WIND_DIRECTION,
|
||||
WEEKFORECAST_COLUMN_UV_INDEX,
|
||||
WEEKFORECAST_COLUMN_TIME_SUNRISE,
|
||||
WEEKFORECAST_COLUMN_TIME_SUNSET}
|
||||
, WEEKFORECAST_CITY_ID + "=?",
|
||||
new String[]{String.valueOf(cityId)}, null, null, null, null);
|
||||
|
||||
List<WeekForecast> list = new ArrayList<>();
|
||||
WeekForecast weekForecast;
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
weekForecast = new WeekForecast();
|
||||
weekForecast.setId(Integer.parseInt(cursor.getString(0)));
|
||||
weekForecast.setCity_id(Integer.parseInt(cursor.getString(1)));
|
||||
weekForecast.setTimestamp(Long.parseLong(cursor.getString(2)));
|
||||
weekForecast.setForecastTime(Long.parseLong(cursor.getString(3)));
|
||||
weekForecast.setWeatherID(Integer.parseInt(cursor.getString(4)));
|
||||
weekForecast.setTemperature(Float.parseFloat(cursor.getString(5)));
|
||||
weekForecast.setMinTemperature(Float.parseFloat(cursor.getString(6)));
|
||||
weekForecast.setMaxTemperature(Float.parseFloat(cursor.getString(7)));
|
||||
weekForecast.setHumidity(Float.parseFloat(cursor.getString(8)));
|
||||
weekForecast.setPressure(Float.parseFloat(cursor.getString(9)));
|
||||
weekForecast.setPrecipitation(Float.parseFloat(cursor.getString(10)));
|
||||
weekForecast.setWind_speed(Float.parseFloat(cursor.getString(11)));
|
||||
weekForecast.setWind_direction(Float.parseFloat(cursor.getString(12)));
|
||||
weekForecast.setUv_index(Float.parseFloat(cursor.getString(13)));
|
||||
weekForecast.setTimeSunrise(Long.parseLong(cursor.getString(14)));
|
||||
weekForecast.setTimeSunset(Long.parseLong(cursor.getString(15)));
|
||||
list.add(weekForecast);
|
||||
} while (cursor.moveToNext());
|
||||
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Methods for TABLE_CURRENT_WEATHER
|
||||
*/
|
||||
public synchronized void addCurrentWeather(CurrentWeatherData currentWeather) {
|
||||
SQLiteDatabase database = this.getWritableDatabase();
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(CURRENT_WEATHER_CITY_ID, currentWeather.getCity_id());
|
||||
values.put(COLUMN_TIME_MEASUREMENT, currentWeather.getTimestamp());
|
||||
values.put(COLUMN_WEATHER_ID, currentWeather.getWeatherID());
|
||||
values.put(COLUMN_TEMPERATURE_CURRENT, currentWeather.getTemperatureCurrent());
|
||||
values.put(COLUMN_HUMIDITY, currentWeather.getHumidity());
|
||||
values.put(COLUMN_PRESSURE, currentWeather.getPressure());
|
||||
values.put(COLUMN_WIND_SPEED, currentWeather.getWindSpeed());
|
||||
values.put(COLUMN_WIND_DIRECTION, currentWeather.getWindDirection());
|
||||
values.put(COLUMN_CLOUDINESS, currentWeather.getCloudiness());
|
||||
values.put(COLUMN_TIME_SUNRISE, currentWeather.getTimeSunrise());
|
||||
values.put(COLUMN_TIME_SUNSET, currentWeather.getTimeSunset());
|
||||
values.put(COLUMN_TIMEZONE_SECONDS, currentWeather.getTimeZoneSeconds());
|
||||
values.put(COLUMN_RAIN60MIN, currentWeather.getRain60min());
|
||||
|
||||
|
||||
database.insert(TABLE_CURRENT_WEATHER, null, values);
|
||||
database.close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public synchronized CurrentWeatherData getCurrentWeatherByCityId(int cityId) {
|
||||
SQLiteDatabase database = this.getReadableDatabase();
|
||||
|
||||
Cursor cursor = database.query(TABLE_CURRENT_WEATHER,
|
||||
new String[]{CURRENT_WEATHER_ID,
|
||||
CURRENT_WEATHER_CITY_ID,
|
||||
COLUMN_TIME_MEASUREMENT,
|
||||
COLUMN_WEATHER_ID,
|
||||
COLUMN_TEMPERATURE_CURRENT,
|
||||
COLUMN_HUMIDITY,
|
||||
COLUMN_PRESSURE,
|
||||
COLUMN_WIND_SPEED,
|
||||
COLUMN_WIND_DIRECTION,
|
||||
COLUMN_CLOUDINESS,
|
||||
COLUMN_TIME_SUNRISE,
|
||||
COLUMN_TIME_SUNSET,
|
||||
COLUMN_TIMEZONE_SECONDS,
|
||||
COLUMN_RAIN60MIN},
|
||||
CURRENT_WEATHER_CITY_ID + " = ?",
|
||||
new String[]{String.valueOf(cityId)}, null, null, null, null);
|
||||
|
||||
CurrentWeatherData currentWeather = new CurrentWeatherData();
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
currentWeather.setId(Integer.parseInt(cursor.getString(0)));
|
||||
currentWeather.setCity_id(Integer.parseInt(cursor.getString(1)));
|
||||
currentWeather.setTimestamp(Long.parseLong(cursor.getString(2)));
|
||||
currentWeather.setWeatherID(Integer.parseInt(cursor.getString(3)));
|
||||
currentWeather.setTemperatureCurrent(Float.parseFloat(cursor.getString(4)));
|
||||
currentWeather.setHumidity(Float.parseFloat(cursor.getString(5)));
|
||||
currentWeather.setPressure(Float.parseFloat(cursor.getString(6)));
|
||||
currentWeather.setWindSpeed(Float.parseFloat(cursor.getString(7)));
|
||||
currentWeather.setWindDirection(Float.parseFloat(cursor.getString(8)));
|
||||
currentWeather.setCloudiness(Float.parseFloat(cursor.getString(9)));
|
||||
currentWeather.setTimeSunrise(Long.parseLong(cursor.getString(10)));
|
||||
currentWeather.setTimeSunset(Long.parseLong(cursor.getString(11)));
|
||||
currentWeather.setTimeZoneSeconds(Integer.parseInt(cursor.getString(12)));
|
||||
currentWeather.setRain60min(cursor.getString(13));
|
||||
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return currentWeather;
|
||||
}
|
||||
|
||||
public synchronized void updateCurrentWeather(CurrentWeatherData currentWeather) {
|
||||
SQLiteDatabase database = this.getWritableDatabase();
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(CURRENT_WEATHER_CITY_ID, currentWeather.getCity_id());
|
||||
values.put(COLUMN_TIME_MEASUREMENT, currentWeather.getTimestamp());
|
||||
values.put(COLUMN_WEATHER_ID, currentWeather.getWeatherID());
|
||||
values.put(COLUMN_TEMPERATURE_CURRENT, currentWeather.getTemperatureCurrent());
|
||||
values.put(COLUMN_HUMIDITY, currentWeather.getHumidity());
|
||||
values.put(COLUMN_PRESSURE, currentWeather.getPressure());
|
||||
values.put(COLUMN_WIND_SPEED, currentWeather.getWindSpeed());
|
||||
values.put(COLUMN_WIND_DIRECTION, currentWeather.getWindDirection());
|
||||
values.put(COLUMN_CLOUDINESS, currentWeather.getCloudiness());
|
||||
values.put(COLUMN_TIME_SUNRISE, currentWeather.getTimeSunrise());
|
||||
values.put(COLUMN_TIME_SUNSET, currentWeather.getTimeSunset());
|
||||
values.put(COLUMN_TIMEZONE_SECONDS, currentWeather.getTimeZoneSeconds());
|
||||
values.put(COLUMN_RAIN60MIN, currentWeather.getRain60min());
|
||||
|
||||
database.update(TABLE_CURRENT_WEATHER, values, CURRENT_WEATHER_CITY_ID + " = ?",
|
||||
new String[]{String.valueOf(currentWeather.getCity_id())});
|
||||
}
|
||||
|
||||
public synchronized void deleteCurrentWeather(CurrentWeatherData currentWeather) {
|
||||
SQLiteDatabase database = this.getWritableDatabase();
|
||||
database.delete(TABLE_CURRENT_WEATHER, CURRENT_WEATHER_ID + " = ?",
|
||||
new String[]{Integer.toString(currentWeather.getId())});
|
||||
database.close();
|
||||
}
|
||||
|
||||
public synchronized void deleteCurrentWeatherByCityId(int cityId) {
|
||||
SQLiteDatabase database = this.getWritableDatabase();
|
||||
database.delete(TABLE_CURRENT_WEATHER, CURRENT_WEATHER_CITY_ID + " = ?",
|
||||
new String[]{Integer.toString(cityId)});
|
||||
database.close();
|
||||
}
|
||||
|
||||
public static int getWidgetCityID(Context context) {
|
||||
SQLiteHelper db = SQLiteHelper.getInstance(context);
|
||||
int cityID=0;
|
||||
List<CityToWatch> cities = db.getAllCitiesToWatch();
|
||||
int rank=cities.get(0).getRank();
|
||||
for (int i = 0; i < cities.size(); i++) { //find cityID for first city to watch = lowest Rank
|
||||
CityToWatch city = cities.get(i);
|
||||
//Log.d("debugtag",Integer.toString(city.getRank()));
|
||||
if (city.getRank() <= rank ){
|
||||
rank=city.getRank();
|
||||
cityID = city.getCityId();
|
||||
}
|
||||
}
|
||||
return cityID;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
package org.woheller69.weather.database;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* This class is the database model for the forecasts table.
|
||||
*/
|
||||
public class WeekForecast {
|
||||
|
||||
public static final float NO_RAIN_VALUE = 0;
|
||||
private int id;
|
||||
private int city_id;
|
||||
private long timestamp;
|
||||
private long forecastFor;
|
||||
private int weatherID;
|
||||
private float temperature;
|
||||
private float temperature_min;
|
||||
private float temperature_max;
|
||||
private float humidity;
|
||||
private float pressure;
|
||||
private float precipitation;
|
||||
private float wind_speed;
|
||||
private float wind_direction;
|
||||
private float uv_index;
|
||||
private long timeSunrise;
|
||||
private long timeSunset;
|
||||
|
||||
public WeekForecast() {
|
||||
}
|
||||
|
||||
public WeekForecast(int id, int city_id, long timestamp, long forecastFor, int weatherID, float temperature, float temperature_min, float temperature_max, float humidity, float pressure, float precipitation, float wind_speed, float wind_direction, float uv_index) {
|
||||
this.id = id;
|
||||
this.city_id = city_id;
|
||||
this.timestamp = timestamp;
|
||||
this.forecastFor = forecastFor;
|
||||
this.weatherID = weatherID;
|
||||
this.temperature = temperature;
|
||||
this.temperature_min = temperature_min;
|
||||
this.temperature_max = temperature_max;
|
||||
this.humidity = humidity;
|
||||
this.pressure = pressure;
|
||||
this.precipitation=precipitation;
|
||||
this.wind_speed=wind_speed;
|
||||
this.wind_direction=wind_direction;
|
||||
this.uv_index=uv_index;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Returns the ID of the record (which uniquely identifies the record).
|
||||
*/
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the date and time for the forecast.
|
||||
*/
|
||||
public long getForecastTime() {
|
||||
return forecastFor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the local time for the forecast in UTC epoch
|
||||
*/
|
||||
public long getLocalForecastTime(Context context) {
|
||||
SQLiteHelper dbhelper = SQLiteHelper.getInstance(context);
|
||||
int timezoneseconds = dbhelper.getCurrentWeatherByCityId(city_id).getTimeZoneSeconds();
|
||||
return forecastFor + timezoneseconds * 1000L;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param forecastFor The point of time for the forecast.
|
||||
*/
|
||||
public void setForecastTime(long forecastFor) {
|
||||
this.forecastFor = forecastFor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the point of time when the data was inserted into the database in Unix, UTC.
|
||||
*/
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param timestamp The point of time to set when the data was inserted into the database in
|
||||
* Unix, UTC.
|
||||
*/
|
||||
public void setTimestamp(long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public int getCity_id() {
|
||||
return city_id;
|
||||
}
|
||||
|
||||
public void setCity_id(int city_id) {
|
||||
this.city_id = city_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the weather condition ID.
|
||||
*/
|
||||
public int getWeatherID() {
|
||||
return weatherID;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param weatherID The weather condition ID to set.
|
||||
*/
|
||||
public void setWeatherID(int weatherID) {
|
||||
this.weatherID = weatherID;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the current temperature in Celsius.
|
||||
*/
|
||||
public float getTemperature() {
|
||||
return temperature;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param temperature The current temperature to set in Celsius.
|
||||
*/
|
||||
public void setTemperature(float temperature) {
|
||||
this.temperature = temperature;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the min temperature in Celsius.
|
||||
*/
|
||||
public float getMinTemperature() {
|
||||
return temperature_min;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param temperature_min The min temperature to set in Celsius.
|
||||
*/
|
||||
public void setMinTemperature(float temperature_min) {
|
||||
this.temperature_min = temperature_min;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the max temperature in Celsius.
|
||||
*/
|
||||
public float getMaxTemperature() {
|
||||
return temperature_max;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param temperature_max The max temperature to set in Celsius.
|
||||
*/
|
||||
public void setMaxTemperature(float temperature_max) {
|
||||
this.temperature_max = temperature_max;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Returns the humidity value in percent.
|
||||
*/
|
||||
public float getHumidity() {
|
||||
return humidity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param humidity The humidity value in percent to set.
|
||||
*/
|
||||
public void setHumidity(float humidity) {
|
||||
this.humidity = humidity;
|
||||
}
|
||||
|
||||
public float getPressure() { return pressure;}
|
||||
public void setPressure(float pressure) {this.pressure=pressure;}
|
||||
|
||||
public float getPrecipitation() {return precipitation; }
|
||||
public void setPrecipitation(float precipitation) {this.precipitation=precipitation;}
|
||||
|
||||
public float getWind_speed() { return wind_speed;}
|
||||
public void setWind_speed(float wind_speed) {this.wind_speed=wind_speed;}
|
||||
|
||||
public float getWind_direction() { return wind_direction;}
|
||||
public void setWind_direction(float wind_direction) {this.wind_direction=wind_direction;}
|
||||
|
||||
public float getUv_index() { return uv_index; }
|
||||
public void setUv_index(float uv_index) {this.uv_index=uv_index;}
|
||||
|
||||
public long getTimeSunrise() { return timeSunrise; }
|
||||
|
||||
public void setTimeSunrise(long timeSunrise) {
|
||||
this.timeSunrise = timeSunrise;
|
||||
}
|
||||
|
||||
public long getTimeSunset() {
|
||||
return timeSunset;
|
||||
}
|
||||
|
||||
public void setTimeSunset(long timeSunset) {
|
||||
this.timeSunset = timeSunset;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,263 @@
|
|||
package org.woheller69.weather.dialogs;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.webkit.WebView;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AutoCompleteTextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.os.ConfigurationCompat;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
|
||||
import com.android.volley.Response;
|
||||
import com.android.volley.VolleyError;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.woheller69.weather.BuildConfig;
|
||||
import org.woheller69.weather.R;
|
||||
import org.woheller69.weather.activities.ManageLocationsActivity;
|
||||
import org.woheller69.weather.database.City;
|
||||
import org.woheller69.weather.database.SQLiteHelper;
|
||||
import org.woheller69.weather.ui.util.geocodingApiCall;
|
||||
import org.woheller69.weather.ui.util.AutoSuggestAdapter;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
|
||||
public class AddLocationDialogOmGeocodingAPI extends DialogFragment {
|
||||
|
||||
Activity activity;
|
||||
View rootView;
|
||||
SQLiteHelper database;
|
||||
|
||||
private AutoCompleteTextView autoCompleteTextView;
|
||||
City selectedCity;
|
||||
|
||||
private static final int TRIGGER_AUTO_COMPLETE = 100;
|
||||
private static final long AUTO_COMPLETE_DELAY = 300;
|
||||
private Handler handler;
|
||||
private AutoSuggestAdapter autoSuggestAdapter;
|
||||
String url="https://geocoding-api.open-meteo.com/v1/search?name=";
|
||||
String lang="default";
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof Activity){
|
||||
this.activity=(Activity) context;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
|
||||
Locale locale = ConfigurationCompat.getLocales(Resources.getSystem().getConfiguration()).get(0);
|
||||
lang=locale.getLanguage();
|
||||
|
||||
LayoutInflater inflater = getActivity().getLayoutInflater();
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
View view = inflater.inflate(R.layout.dialog_add_location, null);
|
||||
|
||||
rootView = view;
|
||||
|
||||
builder.setView(view);
|
||||
builder.setTitle(getActivity().getString(R.string.dialog_add_label));
|
||||
|
||||
this.database = SQLiteHelper.getInstance(getActivity());
|
||||
|
||||
|
||||
final WebView webview= rootView.findViewById(R.id.webViewAddLocation);
|
||||
webview.getSettings().setJavaScriptEnabled(true);
|
||||
webview.getSettings().setUserAgentString(BuildConfig.APPLICATION_ID+"/"+BuildConfig.VERSION_NAME);
|
||||
webview.setBackgroundColor(0x00000000);
|
||||
webview.setBackgroundResource(R.drawable.map_back);
|
||||
|
||||
autoCompleteTextView = (AutoCompleteTextView) rootView.findViewById(R.id.autoCompleteTvAddDialog);
|
||||
|
||||
//Setting up the adapter for AutoSuggest
|
||||
autoSuggestAdapter = new AutoSuggestAdapter(requireContext(),
|
||||
R.layout.list_item_autocomplete);
|
||||
autoCompleteTextView.setThreshold(2);
|
||||
autoCompleteTextView.setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
||||
autoCompleteTextView.setAdapter(autoSuggestAdapter);
|
||||
|
||||
autoCompleteTextView.setOnItemClickListener(
|
||||
new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view,
|
||||
int position, long id) {
|
||||
selectedCity=autoSuggestAdapter.getObject(position);
|
||||
//Hide keyboard to have more space
|
||||
final InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(rootView.getWindowToken(), 0);
|
||||
//Show city on map
|
||||
webview.loadUrl("file:///android_asset/map.html?lat=" + selectedCity.getLatitude() + "&lon=" + selectedCity.getLongitude());
|
||||
}
|
||||
});
|
||||
|
||||
autoCompleteTextView.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int
|
||||
count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before,
|
||||
int count) {
|
||||
handler.removeMessages(TRIGGER_AUTO_COMPLETE);
|
||||
handler.sendEmptyMessageDelayed(TRIGGER_AUTO_COMPLETE,
|
||||
AUTO_COMPLETE_DELAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
handler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
|
||||
@Override
|
||||
public boolean handleMessage(Message msg) {
|
||||
if (msg.what == TRIGGER_AUTO_COMPLETE) {
|
||||
if (!TextUtils.isEmpty(autoCompleteTextView.getText())) {
|
||||
try {
|
||||
makeApiCall(URLEncoder.encode(autoCompleteTextView.getText().toString(), StandardCharsets.UTF_8.name()));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
builder.setPositiveButton(getActivity().getString(R.string.dialog_add_add_button), new DialogInterface.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
performDone();
|
||||
}
|
||||
});
|
||||
|
||||
builder.setNegativeButton(getActivity().getString(R.string.dialog_add_close_button), null);
|
||||
|
||||
return builder.create();
|
||||
|
||||
}
|
||||
private void makeApiCall(String text) {
|
||||
geocodingApiCall.make(getContext(), text, url,lang, new Response.Listener<String>() {
|
||||
@Override
|
||||
public void onResponse(String response) {
|
||||
//parsing logic, please change it as per your requirement
|
||||
List<String> stringList = new ArrayList<>();
|
||||
List<City> cityList = new ArrayList<>();
|
||||
try {
|
||||
JSONObject responseObject = new JSONObject(response);
|
||||
|
||||
JSONArray array = responseObject.getJSONArray("results");
|
||||
for (int i = 0; i < array.length(); i++) {
|
||||
City city =new City();
|
||||
String citystring="";
|
||||
JSONObject jsonFeatures = array.getJSONObject(i);
|
||||
String name="";
|
||||
if (jsonFeatures.has("name")) {
|
||||
name=jsonFeatures.getString("name");
|
||||
citystring=citystring+name;
|
||||
}
|
||||
|
||||
String countrycode="";
|
||||
if (jsonFeatures.has("country_code")) {
|
||||
countrycode=jsonFeatures.getString("country_code");
|
||||
citystring=citystring+", "+countrycode;
|
||||
}
|
||||
String admin1="";
|
||||
if (jsonFeatures.has("admin1")) {
|
||||
admin1=jsonFeatures.getString("admin1");
|
||||
citystring=citystring+", "+admin1;
|
||||
}
|
||||
|
||||
String admin2="";
|
||||
if (jsonFeatures.has("admin2")) {
|
||||
admin2=jsonFeatures.getString("admin2");
|
||||
citystring=citystring+", "+admin2;
|
||||
}
|
||||
|
||||
String admin3="";
|
||||
if (jsonFeatures.has("admin3")) {
|
||||
admin3=jsonFeatures.getString("admin3");
|
||||
citystring=citystring+", "+admin3;
|
||||
}
|
||||
|
||||
String admin4="";
|
||||
if (jsonFeatures.has("admin4")) {
|
||||
admin4=jsonFeatures.getString("admin4");
|
||||
citystring=citystring+", "+admin4;
|
||||
}
|
||||
|
||||
city.setCityName(name);
|
||||
city.setCountryCode(countrycode);
|
||||
city.setLatitude((float) jsonFeatures.getDouble("latitude"));
|
||||
city.setLongitude((float) jsonFeatures.getDouble("longitude"));
|
||||
cityList.add(city);
|
||||
stringList.add(citystring);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
//IMPORTANT: set data here and notify
|
||||
autoSuggestAdapter.setData(stringList,cityList);
|
||||
autoSuggestAdapter.notifyDataSetChanged();
|
||||
}
|
||||
}, new Response.ErrorListener() {
|
||||
@Override
|
||||
public void onErrorResponse(VolleyError error) {
|
||||
Handler h = new Handler(activity.getMainLooper());
|
||||
h.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(activity, error.toString(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void performDone() {
|
||||
if (selectedCity == null) {
|
||||
Toast.makeText(activity, R.string.dialog_add_no_city_found, Toast.LENGTH_SHORT).show();
|
||||
}else {
|
||||
((ManageLocationsActivity) activity).addCityToList(selectedCity);
|
||||
dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
package org.woheller69.weather.firststart;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.woheller69.weather.R;
|
||||
import org.woheller69.weather.activities.ForecastCityActivity;
|
||||
import org.woheller69.weather.activities.SettingsActivity;
|
||||
|
||||
|
||||
/**
|
||||
* Class structure taken from tutorial at http://www.androidhive.info/2016/05/android-build-intro-slider-app/
|
||||
*
|
||||
* @author Karola Marky
|
||||
* @version 20161214
|
||||
*/
|
||||
|
||||
public class TutorialActivity extends AppCompatActivity {
|
||||
|
||||
private ViewPager viewPager;
|
||||
private MyViewPagerAdapter myViewPagerAdapter;
|
||||
private LinearLayout dotsLayout;
|
||||
private TextView[] dots;
|
||||
private int[] layouts;
|
||||
private Button btnNext;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_tutorial);
|
||||
|
||||
viewPager = (ViewPager) findViewById(R.id.view_pager);
|
||||
dotsLayout = (LinearLayout) findViewById(R.id.layoutDots);
|
||||
btnNext = (Button) findViewById(R.id.btn_next);
|
||||
|
||||
|
||||
// layouts of all welcome sliders
|
||||
// add few more layouts if you want
|
||||
layouts = new int[]{
|
||||
R.layout.tutorial_slide1,
|
||||
R.layout.tutorial_slide2,
|
||||
R.layout.tutorial_slide3};
|
||||
|
||||
// adding bottom dots
|
||||
addBottomDots(0);
|
||||
|
||||
// making notification bar transparent
|
||||
changeStatusBarColor();
|
||||
|
||||
myViewPagerAdapter = new MyViewPagerAdapter();
|
||||
viewPager.setAdapter(myViewPagerAdapter);
|
||||
viewPager.addOnPageChangeListener(viewPagerPageChangeListener);
|
||||
|
||||
|
||||
|
||||
btnNext.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// checking for last page
|
||||
// if last page home screen will be launched
|
||||
int current = getItem(+1);
|
||||
if (current < layouts.length) {
|
||||
// move to next screen
|
||||
viewPager.setCurrentItem(current);
|
||||
} else {
|
||||
launchSettings();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void addBottomDots(int currentPage) {
|
||||
dots = new TextView[layouts.length];
|
||||
|
||||
int[] colorsActive = getResources().getIntArray(R.array.array_dot_active);
|
||||
int[] colorsInactive = getResources().getIntArray(R.array.array_dot_inactive);
|
||||
|
||||
dotsLayout.removeAllViews();
|
||||
for (int i = 0; i < dots.length; i++) {
|
||||
dots[i] = new TextView(this);
|
||||
dots[i].setText("\u2022");
|
||||
dots[i].setTextSize(35);
|
||||
dots[i].setTextColor(colorsInactive[currentPage]);
|
||||
dotsLayout.addView(dots[i]);
|
||||
}
|
||||
|
||||
if (dots.length > 0)
|
||||
dots[currentPage].setTextColor(colorsActive[currentPage]);
|
||||
}
|
||||
|
||||
private int getItem(int i) {
|
||||
return viewPager.getCurrentItem() + i;
|
||||
}
|
||||
|
||||
private void launchHomeScreen() {
|
||||
startActivity(new Intent(TutorialActivity.this, ForecastCityActivity.class));
|
||||
finish();
|
||||
}
|
||||
|
||||
private void launchSettings() {
|
||||
startActivity(new Intent(TutorialActivity.this, SettingsActivity.class));
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
viewPagerPageChangeListener.onPageSelected(viewPager.getCurrentItem());
|
||||
}
|
||||
|
||||
|
||||
// viewpager change listener
|
||||
ViewPager.OnPageChangeListener viewPagerPageChangeListener = new ViewPager.OnPageChangeListener() {
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
addBottomDots(position);
|
||||
|
||||
// changing the next button text 'NEXT' / 'GOT IT'
|
||||
if (position == layouts.length - 1) {
|
||||
// last page. make button text to GOT IT
|
||||
btnNext.setText(getString(R.string.okay));
|
||||
} else {
|
||||
// still pages are left
|
||||
btnNext.setText(getString(R.string.next));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrolled(int arg0, float arg1, int arg2) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int arg0) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public class MyViewPagerAdapter extends PagerAdapter {
|
||||
private LayoutInflater layoutInflater;
|
||||
|
||||
public MyViewPagerAdapter() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object instantiateItem(ViewGroup container, int position) {
|
||||
layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
|
||||
final View view = layoutInflater.inflate(layouts[position], container, false);
|
||||
|
||||
container.addView(view);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return layouts.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isViewFromObject(View view, Object obj) {
|
||||
return view == obj;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void destroyItem(ViewGroup container, int position, Object object) {
|
||||
View view = (View) object;
|
||||
container.removeView(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package org.woheller69.weather.http;
|
||||
|
||||
/**
|
||||
* A list of all the possible HTTP request types (there are more, for sure, but for this project
|
||||
* the four below are definitely sufficient).
|
||||
*/
|
||||
public enum HttpRequestType {
|
||||
POST,
|
||||
GET,
|
||||
PUT,
|
||||
DELETE
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package org.woheller69.weather.http;
|
||||
|
||||
import org.woheller69.weather.weather_api.IProcessHttpRequest;
|
||||
|
||||
/**
|
||||
* This interface defines the template for making HTTP request. Furthermore, it provides a generic
|
||||
* way for handling the responses.
|
||||
*/
|
||||
public interface IHttpRequest {
|
||||
|
||||
/**
|
||||
* Makes an HTTP request and processes the response.
|
||||
*
|
||||
* @param URL The target of the HTTP request.
|
||||
* @param method Which method to use for the HTTP request (e.g. GET or POST)
|
||||
* @param requestProcessor This object with its implemented methods processSuccessScenario and
|
||||
* processFailScenario defines how to handle the response in the success
|
||||
* and error case respectively.
|
||||
*/
|
||||
void make(final String URL, HttpRequestType method, IProcessHttpRequest requestProcessor);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
package org.woheller69.weather.http;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.volley.Request;
|
||||
import com.android.volley.RequestQueue;
|
||||
import com.android.volley.Response;
|
||||
import com.android.volley.VolleyError;
|
||||
import com.android.volley.toolbox.HurlStack;
|
||||
import com.android.volley.toolbox.StringRequest;
|
||||
import com.android.volley.toolbox.Volley;
|
||||
|
||||
import org.woheller69.weather.BuildConfig;
|
||||
import org.woheller69.weather.weather_api.IProcessHttpRequest;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
/**
|
||||
* This class implements the IHttpRequest interface. It provides HTTP requests by using Volley.
|
||||
* See: https://developer.android.com/training/volley/simple.html
|
||||
*/
|
||||
public class VolleyHttpRequest implements IHttpRequest {
|
||||
|
||||
private Context context;
|
||||
private int cityId;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param context Volley needs a context "for creating the cache dir".
|
||||
* @see Volley#newRequestQueue(Context)
|
||||
*/
|
||||
public VolleyHttpRequest(Context context, int cityId) {
|
||||
|
||||
this.context = context;
|
||||
this.cityId = cityId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IHttpRequest#make(String, HttpRequestType, IProcessHttpRequest)
|
||||
*/
|
||||
@Override
|
||||
public void make(String URL, HttpRequestType method, final IProcessHttpRequest requestProcessor) {
|
||||
RequestQueue queue = Volley.newRequestQueue(context);
|
||||
|
||||
// Set the request method
|
||||
int requestMethod;
|
||||
switch (method) {
|
||||
case POST:
|
||||
requestMethod = Request.Method.POST;
|
||||
break;
|
||||
case GET:
|
||||
requestMethod = Request.Method.GET;
|
||||
break;
|
||||
case PUT:
|
||||
requestMethod = Request.Method.PUT;
|
||||
break;
|
||||
case DELETE:
|
||||
requestMethod = Request.Method.DELETE;
|
||||
break;
|
||||
default:
|
||||
requestMethod = Request.Method.GET;
|
||||
}
|
||||
|
||||
// Execute the request and handle the response
|
||||
StringRequest stringRequest = new StringRequest(requestMethod, URL,
|
||||
new Response.Listener<String>() {
|
||||
@Override
|
||||
public void onResponse(String response) {
|
||||
requestProcessor.processSuccessScenario(response,cityId);
|
||||
}
|
||||
},
|
||||
new Response.ErrorListener() {
|
||||
@Override
|
||||
public void onErrorResponse(VolleyError error) {
|
||||
requestProcessor.processFailScenario(error);
|
||||
}
|
||||
}
|
||||
){
|
||||
@Override
|
||||
public Map<String, String> getHeaders() { //from https://stackoverflow.com/questions/17049473/how-to-set-custom-header-in-volley-request
|
||||
Map<String, String> params = new HashMap<String, String>();
|
||||
params.put("User-Agent", BuildConfig.APPLICATION_ID + "/" + BuildConfig.VERSION_NAME);
|
||||
return params;
|
||||
}
|
||||
};
|
||||
|
||||
queue.add(stringRequest);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
package org.woheller69.weather.preferences;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import org.woheller69.weather.BuildConfig;
|
||||
import org.woheller69.weather.R;
|
||||
|
||||
/**
|
||||
* This class provides access and methods for relevant preferences.
|
||||
*/
|
||||
public class AppPreferencesManager {
|
||||
|
||||
|
||||
/**
|
||||
* Member variables
|
||||
*/
|
||||
SharedPreferences preferences;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param preferences Source for the preferences to use.
|
||||
*/
|
||||
public AppPreferencesManager(SharedPreferences preferences) {
|
||||
this.preferences = preferences;
|
||||
}
|
||||
|
||||
public boolean isFirstTimeLaunch(Context context) {
|
||||
boolean result = preferences.getBoolean("firstLaunch", true);
|
||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
editor.putBoolean("firstLaunch",false);
|
||||
editor.apply();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method converts a given temperature value into the unit that was set in the preferences.
|
||||
*
|
||||
* @param temperature The temperature to convert into the unit that is set in the preferences.
|
||||
* Make sure to pass a value in celsius.
|
||||
* @return Returns the converted temperature.
|
||||
**
|
||||
*/
|
||||
public float convertTemperatureFromCelsius(float temperature) {
|
||||
// 1 = Celsius (fallback), 2 = Fahrenheit
|
||||
int prefValue = Integer.parseInt(preferences.getString("temperatureUnit", "1"));
|
||||
if (prefValue == 1) {
|
||||
return temperature;
|
||||
} else {
|
||||
return (((temperature * 9) / 5) + 32);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method converts a given distance value into the unit that was set in the preferences.
|
||||
*
|
||||
* @param kilometers The kilometers to convert into the unit that is set in the preferences.
|
||||
* Make sure to pass a value in kilometers.
|
||||
* @return Returns the converted distance.
|
||||
*/
|
||||
public float convertDistanceFromKilometers(float kilometers) {
|
||||
// 1 = kilometers, 2 = miles
|
||||
int prefValue = Integer.parseInt(preferences.getString("distanceUnit", "1"));
|
||||
if (prefValue == 1) {
|
||||
return kilometers;
|
||||
} else {
|
||||
return convertKmInMiles(kilometers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns true if kilometers was set as distance unit in the preferences else false.
|
||||
*/
|
||||
public boolean isDistanceUnitKilometers() {
|
||||
int prefValue = Integer.parseInt(preferences.getString("distanceUnit", "0"));
|
||||
return (prefValue == 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns true if miles was set as distance unit in the preferences else false.
|
||||
*/
|
||||
public boolean isDistanceUnitMiles() {
|
||||
int prefValue = Integer.parseInt(preferences.getString("distanceUnit", "0"));
|
||||
return (prefValue == 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a kilometer value in miles.
|
||||
*
|
||||
* @param km The value to convert to miles.
|
||||
* @return Returns the converted value.
|
||||
*/
|
||||
public float convertKmInMiles(float km) {
|
||||
// TODO: Is this the right class for the function???
|
||||
return (float) (km / 1.609344);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a miles value in kilometers.
|
||||
*
|
||||
* @param miles The value to convert to kilometers.
|
||||
* @return Returns the converted value.
|
||||
*/
|
||||
public float convertMilesInKm(float miles) {
|
||||
// TODO: Is this the right class for the function???
|
||||
return (float) (miles * 1.609344);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns "°C" in case Celsius is set and "°F" if Fahrenheit was selected.
|
||||
*/
|
||||
public String getTemperatureUnit() {
|
||||
int prefValue = Integer.parseInt(preferences.getString("temperatureUnit", "1"));
|
||||
if (prefValue == 1) {
|
||||
return "°C";
|
||||
} else {
|
||||
return "°F";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns "km" in case kilometer is set and "mi" if miles was selected.
|
||||
* @param applicationContext
|
||||
*/
|
||||
public String getDistanceUnit(Context applicationContext) {
|
||||
int prefValue = Integer.parseInt(preferences.getString("distanceUnit", "1"));
|
||||
if (prefValue == 1) {
|
||||
return applicationContext.getString(R.string.units_km);
|
||||
} else {
|
||||
return "mi";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean showStarDialog(Context context) {
|
||||
int versionCode = preferences.getInt("versionCode",BuildConfig.VERSION_CODE);
|
||||
boolean askForStar=preferences.getBoolean("askForStar",true);
|
||||
|
||||
if (!isFirstTimeLaunch(context) && BuildConfig.VERSION_CODE>versionCode && askForStar){ //not at first start, only after upgrade and only if use has not yet given a star or has declined
|
||||
SharedPreferences.Editor editor = preferences.edit();
|
||||
editor.putInt("versionCode", BuildConfig.VERSION_CODE);
|
||||
editor.apply();
|
||||
return true;
|
||||
} else {
|
||||
SharedPreferences.Editor editor = preferences.edit();
|
||||
editor.putInt("versionCode", BuildConfig.VERSION_CODE);
|
||||
editor.apply();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void setAskForStar(boolean askForStar){
|
||||
SharedPreferences.Editor editor = preferences.edit();
|
||||
editor.putBoolean("askForStar", askForStar);
|
||||
editor.apply();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
package org.woheller69.weather.services;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Handler;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.core.app.JobIntentService;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.woheller69.weather.BuildConfig;
|
||||
import org.woheller69.weather.R;
|
||||
import org.woheller69.weather.activities.NavigationActivity;
|
||||
import org.woheller69.weather.database.CityToWatch;
|
||||
import org.woheller69.weather.database.HourlyForecast;
|
||||
import org.woheller69.weather.database.SQLiteHelper;
|
||||
import org.woheller69.weather.weather_api.IHttpRequestForWeatherAPI;
|
||||
import org.woheller69.weather.weather_api.open_meteo.OMHttpRequestForWeatherAPI;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This class provides the functionality to fetch forecast data for a given city as a background
|
||||
* task.
|
||||
*/
|
||||
public class UpdateDataService extends JobIntentService {
|
||||
|
||||
public static final String UPDATE_FORECAST_ACTION = "org.woheller69.weather.services.UpdateDataService.UPDATE_FORECAST_ACTION";
|
||||
public static final String UPDATE_ALL_ACTION = "org.woheller69.weather.services.UpdateDataService.UPDATE_ALL_ACTION";
|
||||
public static final String UPDATE_SINGLE_ACTION = "org.woheller69.weather.services.UpdateDataService.UPDATE_SINGLE_ACTION";
|
||||
|
||||
public static final String CITY_ID = "cityId";
|
||||
public static final String SKIP_UPDATE_INTERVAL = "skipUpdateInterval";
|
||||
private static final long MIN_UPDATE_INTERVAL=20;
|
||||
|
||||
private SQLiteHelper dbHelper;
|
||||
private SharedPreferences prefManager;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public UpdateDataService() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
dbHelper = SQLiteHelper.getInstance(getApplicationContext());
|
||||
prefManager = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleWork(Intent intent) {
|
||||
if (!isOnline()) {
|
||||
Handler h = new Handler(getApplicationContext().getMainLooper());
|
||||
h.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (NavigationActivity.isVisible) Toast.makeText(getApplicationContext(), getResources().getString(R.string.error_no_internet), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (intent != null) {
|
||||
if (UPDATE_ALL_ACTION.equals(intent.getAction())) handleUpdateAll(intent);
|
||||
else if (UPDATE_FORECAST_ACTION.equals(intent.getAction()))
|
||||
handleUpdateForecastAction(intent);
|
||||
else if (UPDATE_SINGLE_ACTION.equals(intent.getAction())) handleUpdateSingle(intent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Be careful, with using this. It can cause many calls to the API, because it wants to update everything if the update interval allows it.
|
||||
*
|
||||
* @param intent contains necessary parameters for the service work
|
||||
*/
|
||||
private void handleUpdateAll(Intent intent) {
|
||||
List<CityToWatch> cities = dbHelper.getAllCitiesToWatch();
|
||||
for (CityToWatch c : cities) {
|
||||
handleUpdateForecastAction(intent, c.getCityId(),c.getLatitude(),c.getLongitude());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleUpdateSingle(Intent intent) {
|
||||
int cityId = intent.getIntExtra("cityId",-1);
|
||||
CityToWatch city = dbHelper.getCityToWatch(cityId);
|
||||
handleUpdateForecastAction(intent, cityId, city.getLatitude(), city.getLongitude());
|
||||
}
|
||||
|
||||
private void handleUpdateForecastAction(Intent intent, int cityId, float lat, float lon) {
|
||||
boolean skipUpdateInterval = intent.getBooleanExtra(SKIP_UPDATE_INTERVAL, false);
|
||||
|
||||
long timestamp = 0;
|
||||
long systemTime = System.currentTimeMillis() / 1000;
|
||||
long updateInterval = (long) (Float.parseFloat(prefManager.getString("pref_updateInterval", "2")) * 60 * 60);
|
||||
|
||||
List<HourlyForecast> hourlyForecasts = dbHelper.getForecastsByCityId(cityId);
|
||||
if (hourlyForecasts.size() > 0) { // check timestamp of the current forecasts
|
||||
timestamp = hourlyForecasts.get(0).getTimestamp();
|
||||
}
|
||||
|
||||
if (skipUpdateInterval) {
|
||||
// check timestamp of the current forecasts
|
||||
if ((timestamp+MIN_UPDATE_INTERVAL-systemTime)>0) skipUpdateInterval=false; //even if skipUpdateInterval is true, never update if less than MIN_UPDATE_INTERVAL s
|
||||
}
|
||||
|
||||
// Update if update forced or if a certain time has passed
|
||||
if (skipUpdateInterval || timestamp + updateInterval - systemTime <= 0) {
|
||||
IHttpRequestForWeatherAPI forecastOneCallRequest = new OMHttpRequestForWeatherAPI(getApplicationContext());
|
||||
forecastOneCallRequest.perform(lat,lon, cityId);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isOnline() {
|
||||
try {
|
||||
URL url = new URL(BuildConfig.BASE_URL);
|
||||
InetAddress inetAddress = InetAddress.getByName(url.getHost());
|
||||
return inetAddress.isReachable(2000);
|
||||
} catch (IOException | IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleUpdateForecastAction(Intent intent) {
|
||||
int cityId = intent.getIntExtra(CITY_ID, -1);
|
||||
float lat =0;
|
||||
float lon =0;
|
||||
//get lat lon for cityID
|
||||
List<CityToWatch> citiesToWatch = dbHelper.getAllCitiesToWatch();
|
||||
for (int i = 0; i < citiesToWatch.size(); i++) {
|
||||
CityToWatch city = citiesToWatch.get(i);
|
||||
if (city.getCityId() == cityId) {
|
||||
lat = city.getLatitude();
|
||||
lon = city.getLongitude();
|
||||
break;
|
||||
}
|
||||
}
|
||||
handleUpdateForecastAction(intent, cityId, lat, lon);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,278 @@
|
|||
package org.woheller69.weather.ui.Help;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
import org.woheller69.weather.R;
|
||||
import org.woheller69.weather.preferences.AppPreferencesManager;
|
||||
|
||||
import java.math.RoundingMode;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import static java.lang.Boolean.TRUE;
|
||||
|
||||
public final class StringFormatUtils {
|
||||
|
||||
private static final DecimalFormat decimalFormat = new DecimalFormat("0.0");
|
||||
private static final DecimalFormat intFormat = new DecimalFormat("0");
|
||||
|
||||
public static String formatDecimal(float decimal) {
|
||||
decimalFormat.setRoundingMode(RoundingMode.HALF_UP);
|
||||
return removeMinusIfZerosOnly(decimalFormat.format(decimal));
|
||||
}
|
||||
|
||||
public static String formatInt(float decimal) {
|
||||
intFormat.setRoundingMode(RoundingMode.HALF_UP);
|
||||
return removeMinusIfZerosOnly(intFormat.format(decimal));
|
||||
}
|
||||
|
||||
public static String formatInt(float decimal, String appendix) {
|
||||
return String.format("%s\u200a%s", removeMinusIfZerosOnly(formatInt(decimal)), appendix); //\u200a adds tiny space
|
||||
}
|
||||
|
||||
public static String formatDecimal(float decimal, String appendix) {
|
||||
return String.format("%s\u200a%s", removeMinusIfZerosOnly(formatDecimal(decimal)), appendix);
|
||||
}
|
||||
|
||||
public static String formatTimeWithoutZone(Context context, long time) {
|
||||
SharedPreferences sharedPreferences= PreferenceManager.getDefaultSharedPreferences(context);
|
||||
SimpleDateFormat df;
|
||||
if (android.text.format.DateFormat.is24HourFormat(context) || sharedPreferences.getBoolean("pref_TimeFormat", true)==TRUE){
|
||||
df = new SimpleDateFormat("HH:mm", Locale.getDefault());
|
||||
df.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
}else {
|
||||
df = new SimpleDateFormat("hh:mm aa", Locale.getDefault());
|
||||
df.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
}
|
||||
return df.format(time);
|
||||
}
|
||||
|
||||
public static String formatWindSpeed(Context context, float wind_speed) {
|
||||
SharedPreferences sharedPreferences= PreferenceManager.getDefaultSharedPreferences(context);
|
||||
if (sharedPreferences.getBoolean("pref_WindFormat",true)==TRUE) {
|
||||
if (wind_speed < 0.3) {
|
||||
return formatInt(0, context.getString(R.string.units_Bft)); // Calm
|
||||
} else if (wind_speed < 1.5) {
|
||||
return formatInt(1, context.getString(R.string.units_Bft)); // Light air
|
||||
} else if (wind_speed < 3.3) {
|
||||
return formatInt(2, context.getString(R.string.units_Bft)); // Light breeze
|
||||
} else if (wind_speed < 5.5) {
|
||||
return formatInt(3, context.getString(R.string.units_Bft)); // Gentle breeze
|
||||
} else if (wind_speed < 7.9) {
|
||||
return formatInt(4, context.getString(R.string.units_Bft)); // Moderate breeze
|
||||
} else if (wind_speed < 10.7) {
|
||||
return formatInt(5, context.getString(R.string.units_Bft)); // Fresh breeze
|
||||
} else if (wind_speed < 13.8) {
|
||||
return formatInt(6, context.getString(R.string.units_Bft)); // Strong breeze
|
||||
} else if (wind_speed < 17.1) {
|
||||
return formatInt(7, context.getString(R.string.units_Bft)); // High wind
|
||||
} else if (wind_speed < 20.7) {
|
||||
return formatInt(8, context.getString(R.string.units_Bft)); // Gale
|
||||
} else if (wind_speed < 24.4) {
|
||||
return formatInt(9, context.getString(R.string.units_Bft)); // Strong gale
|
||||
} else if (wind_speed < 28.4) {
|
||||
return formatInt(10, context.getString(R.string.units_Bft)); // Storm
|
||||
} else if (wind_speed < 32.6) {
|
||||
return formatInt(11, context.getString(R.string.units_Bft)); // Violent storm
|
||||
} else {
|
||||
return formatInt(12, context.getString(R.string.units_Bft)); // Hurricane
|
||||
}
|
||||
}else{
|
||||
if (sharedPreferences.getString("distanceUnit", "0").equals("1")) { //distanceUnit km
|
||||
return formatInt((float) (wind_speed*3.6),context.getString(R.string.units_km_h));
|
||||
}else return formatInt((float) (wind_speed*2.236),context.getString(R.string.units_mph));
|
||||
}
|
||||
}
|
||||
|
||||
public static Drawable colorWindSpeed(Context context, float wind_speed) {
|
||||
if (wind_speed < 0.3) {
|
||||
return ResourcesCompat.getDrawable(context.getResources(),R.drawable.rounded_transparent,null);
|
||||
} else if (wind_speed < 1.5) {
|
||||
return ResourcesCompat.getDrawable(context.getResources(),R.drawable.rounded_transparent,null);
|
||||
} else if (wind_speed < 3.3) {
|
||||
return ResourcesCompat.getDrawable(context.getResources(),R.drawable.rounded_transparent,null);
|
||||
} else if (wind_speed < 5.5) {
|
||||
return ResourcesCompat.getDrawable(context.getResources(),R.drawable.rounded_transparent,null);
|
||||
} else if (wind_speed < 7.9) {
|
||||
return ResourcesCompat.getDrawable(context.getResources(),R.drawable.rounded_transparent,null);
|
||||
} else if (wind_speed < 10.7) {
|
||||
return ResourcesCompat.getDrawable(context.getResources(),R.drawable.rounded_yellow,null);
|
||||
} else if (wind_speed < 13.8) {
|
||||
return ResourcesCompat.getDrawable(context.getResources(),R.drawable.rounded_yellow,null);
|
||||
} else if (wind_speed < 17.1) {
|
||||
return ResourcesCompat.getDrawable(context.getResources(),R.drawable.rounded_yellow,null);
|
||||
} else if (wind_speed < 20.7) {
|
||||
return ResourcesCompat.getDrawable(context.getResources(),R.drawable.rounded_orange,null);
|
||||
} else if (wind_speed < 24.4) {
|
||||
return ResourcesCompat.getDrawable(context.getResources(),R.drawable.rounded_orange,null);
|
||||
} else if (wind_speed < 28.4) {
|
||||
return ResourcesCompat.getDrawable(context.getResources(),R.drawable.rounded_lightred,null);
|
||||
} else if (wind_speed < 32.6) {
|
||||
return ResourcesCompat.getDrawable(context.getResources(),R.drawable.rounded_lightred,null);
|
||||
} else {
|
||||
return ResourcesCompat.getDrawable(context.getResources(),R.drawable.rounded_red,null);
|
||||
}
|
||||
}
|
||||
|
||||
public static int colorWindSpeedWidget(float wind_speed) {
|
||||
if (wind_speed < 0.3) {
|
||||
return R.drawable.ic_wind_empty;
|
||||
} else if (wind_speed < 1.5) {
|
||||
return R.drawable.ic_wind_empty;
|
||||
} else if (wind_speed < 3.3) {
|
||||
return R.drawable.ic_wind_empty;
|
||||
} else if (wind_speed < 5.5) {
|
||||
return R.drawable.ic_wind_empty;
|
||||
} else if (wind_speed < 7.9) {
|
||||
return R.drawable.ic_wind_empty;
|
||||
} else if (wind_speed < 10.7) {
|
||||
return R.drawable.ic_wind_yellow;
|
||||
} else if (wind_speed < 13.8) {
|
||||
return R.drawable.ic_wind_yellow;
|
||||
} else if (wind_speed < 17.1) {
|
||||
return R.drawable.ic_wind_yellow;
|
||||
} else if (wind_speed < 20.7) {
|
||||
return R.drawable.ic_wind_orange;
|
||||
} else if (wind_speed < 24.4) {
|
||||
return R.drawable.ic_wind_orange;
|
||||
} else if (wind_speed < 28.4) {
|
||||
return R.drawable.ic_wind_lightred;
|
||||
} else if (wind_speed < 32.6) {
|
||||
return R.drawable.ic_wind_lightred;
|
||||
} else {
|
||||
return R.drawable.ic_wind_lightred;
|
||||
}
|
||||
}
|
||||
|
||||
public static Drawable colorUVindex(Context context, int uvindex) {
|
||||
if (uvindex <=2) {
|
||||
return ResourcesCompat.getDrawable(context.getResources(),R.drawable.rounded_transparent,null);
|
||||
} else if (uvindex <= 5) {
|
||||
return ResourcesCompat.getDrawable(context.getResources(),R.drawable.rounded_yellow,null);
|
||||
} else if (uvindex <= 7) {
|
||||
return ResourcesCompat.getDrawable(context.getResources(),R.drawable.rounded_orange,null);
|
||||
} else if (uvindex <= 10) {
|
||||
return ResourcesCompat.getDrawable(context.getResources(),R.drawable.rounded_lightred,null);
|
||||
} else {
|
||||
return ResourcesCompat.getDrawable(context.getResources(),R.drawable.rounded_violet,null);
|
||||
}
|
||||
}
|
||||
|
||||
public static Integer widgetColorWindSpeed(Context context, float wind_speed) {
|
||||
if (wind_speed < 0.3) {
|
||||
return R.drawable.rounded_grey;
|
||||
} else if (wind_speed < 1.5) {
|
||||
return R.drawable.rounded_grey;
|
||||
} else if (wind_speed < 3.3) {
|
||||
return R.drawable.rounded_grey;
|
||||
} else if (wind_speed < 5.5) {
|
||||
return R.drawable.rounded_grey;
|
||||
} else if (wind_speed < 7.9) {
|
||||
return R.drawable.rounded_grey;
|
||||
} else if (wind_speed < 10.7) {
|
||||
return R.drawable.rounded_yellow;
|
||||
} else if (wind_speed < 13.8) {
|
||||
return R.drawable.rounded_yellow;
|
||||
} else if (wind_speed < 17.1) {
|
||||
return R.drawable.rounded_yellow;
|
||||
} else if (wind_speed < 20.7) {
|
||||
return R.drawable.rounded_orange;
|
||||
} else if (wind_speed < 24.4) {
|
||||
return R.drawable.rounded_orange;
|
||||
} else if (wind_speed < 28.4) {
|
||||
return R.drawable.rounded_lightred;
|
||||
} else if (wind_speed < 32.6) {
|
||||
return R.drawable.rounded_lightred;
|
||||
} else {
|
||||
return R.drawable.rounded_red;
|
||||
}
|
||||
}
|
||||
|
||||
public static Integer widgetColorUVindex(Context context, int uvindex) {
|
||||
if (uvindex <=2) {
|
||||
return R.drawable.rounded_green;
|
||||
} else if (uvindex <= 5) {
|
||||
return R.drawable.rounded_yellow;
|
||||
} else if (uvindex <= 7) {
|
||||
return R.drawable.rounded_orange;
|
||||
} else if (uvindex <= 10) {
|
||||
return R.drawable.rounded_lightred;
|
||||
} else {
|
||||
return R.drawable.rounded_violet;
|
||||
}
|
||||
}
|
||||
|
||||
public static Integer getDayShort(int day){
|
||||
|
||||
switch(day) {
|
||||
case Calendar.MONDAY:
|
||||
day = R.string.abbreviation_monday;
|
||||
break;
|
||||
case Calendar.TUESDAY:
|
||||
day = R.string.abbreviation_tuesday;
|
||||
break;
|
||||
case Calendar.WEDNESDAY:
|
||||
day = R.string.abbreviation_wednesday;
|
||||
break;
|
||||
case Calendar.THURSDAY:
|
||||
day = R.string.abbreviation_thursday;
|
||||
break;
|
||||
case Calendar.FRIDAY:
|
||||
day = R.string.abbreviation_friday;
|
||||
break;
|
||||
case Calendar.SATURDAY:
|
||||
day = R.string.abbreviation_saturday;
|
||||
break;
|
||||
case Calendar.SUNDAY:
|
||||
day = R.string.abbreviation_sunday;
|
||||
break;
|
||||
default:
|
||||
day = R.string.abbreviation_monday;
|
||||
}
|
||||
return day;
|
||||
}
|
||||
|
||||
public static Integer getDayLong(int day){
|
||||
|
||||
switch(day) {
|
||||
case Calendar.MONDAY:
|
||||
day = R.string.monday;
|
||||
break;
|
||||
case Calendar.TUESDAY:
|
||||
day = R.string.tuesday;
|
||||
break;
|
||||
case Calendar.WEDNESDAY:
|
||||
day = R.string.wednesday;
|
||||
break;
|
||||
case Calendar.THURSDAY:
|
||||
day = R.string.thursday;
|
||||
break;
|
||||
case Calendar.FRIDAY:
|
||||
day = R.string.friday;
|
||||
break;
|
||||
case Calendar.SATURDAY:
|
||||
day = R.string.saturday;
|
||||
break;
|
||||
case Calendar.SUNDAY:
|
||||
day = R.string.sunday;
|
||||
break;
|
||||
default:
|
||||
day = R.string.monday;
|
||||
}
|
||||
return day;
|
||||
}
|
||||
|
||||
public static String removeMinusIfZerosOnly(String string){
|
||||
// It removes (replaces with "") the minus sign if it's followed by 0-n characters of "0.00000...",
|
||||
// so this will work for any similar result such as "-0", "-0." or "-0.000000000"
|
||||
// https://newbedev.com/negative-sign-in-case-of-zero-in-java
|
||||
return string.replaceAll("^-(?=0(\\.0*)?$)", "");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,415 @@
|
|||
package org.woheller69.weather.ui.RecycleList;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.db.chart.Tools;
|
||||
import com.db.chart.model.BarSet;
|
||||
import com.db.chart.model.ChartSet;
|
||||
import com.db.chart.model.LineSet;
|
||||
import com.db.chart.view.AxisController;
|
||||
import com.db.chart.view.BarChartView;
|
||||
|
||||
import org.woheller69.weather.R;
|
||||
import org.woheller69.weather.database.CurrentWeatherData;
|
||||
import org.woheller69.weather.database.HourlyForecast;
|
||||
import org.woheller69.weather.database.SQLiteHelper;
|
||||
import org.woheller69.weather.database.WeekForecast;
|
||||
import org.woheller69.weather.preferences.AppPreferencesManager;
|
||||
import org.woheller69.weather.ui.Help.StringFormatUtils;
|
||||
import org.woheller69.weather.ui.UiResourceProvider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class CityWeatherAdapter extends RecyclerView.Adapter<CityWeatherAdapter.ViewHolder> {
|
||||
private static final String TAG = "Forecast_Adapter";
|
||||
|
||||
private int[] dataSetTypes;
|
||||
private List<HourlyForecast> courseDayList;
|
||||
private float[][] forecastData;
|
||||
|
||||
private Context context;
|
||||
private ViewGroup mParent;
|
||||
private RecyclerView mCourseOfDay;
|
||||
private RecyclerView mWeekWeather;
|
||||
|
||||
private CurrentWeatherData currentWeatherDataList;
|
||||
|
||||
public static final int OVERVIEW = 0;
|
||||
public static final int DETAILS = 1;
|
||||
public static final int WEEK = 2;
|
||||
public static final int DAY = 3;
|
||||
public static final int CHART = 4;
|
||||
public static final int EMPTY = 5;
|
||||
|
||||
public CityWeatherAdapter(CurrentWeatherData currentWeatherDataList, int[] dataSetTypes, Context context) {
|
||||
this.currentWeatherDataList = currentWeatherDataList;
|
||||
this.dataSetTypes = dataSetTypes;
|
||||
this.context = context;
|
||||
|
||||
SQLiteHelper database = SQLiteHelper.getInstance(context.getApplicationContext());
|
||||
|
||||
List<HourlyForecast> hourlyForecasts = database.getForecastsByCityId(currentWeatherDataList.getCity_id());
|
||||
List<WeekForecast> weekforecasts = database.getWeekForecastsByCityId(currentWeatherDataList.getCity_id());
|
||||
|
||||
updateForecastData(hourlyForecasts);
|
||||
updateWeekForecastData(weekforecasts);
|
||||
|
||||
}
|
||||
|
||||
// function update 3-hour or 1-hour forecast list
|
||||
public void updateForecastData(List<HourlyForecast> hourlyForecasts) {
|
||||
|
||||
courseDayList = new ArrayList<>();
|
||||
|
||||
for (HourlyForecast f : hourlyForecasts) {
|
||||
if (f.getForecastTime() >= System.currentTimeMillis()) {
|
||||
courseDayList.add(f);
|
||||
}
|
||||
}
|
||||
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.getCurrentWeatherByCityId(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]=forecasts.get(i).getMaxTemperature();
|
||||
forecastData[i][1]=forecasts.get(i).getMinTemperature();
|
||||
forecastData[i][2]=forecasts.get(i).getHumidity();
|
||||
forecastData[i][3]=forecasts.get(i).getPressure();
|
||||
forecastData[i][4]=forecasts.get(i).getPrecipitation();
|
||||
forecastData[i][5]=forecasts.get(i).getWind_speed();
|
||||
forecastData[i][6]=forecasts.get(i).getWind_direction();
|
||||
forecastData[i][7]=forecasts.get(i).getUv_index();
|
||||
forecastData[i][8]=forecasts.get(i).getForecastTime()+zonemilliseconds;
|
||||
forecastData[i][9]=forecasts.get(i).getWeatherID();
|
||||
forecastData[i][10]=1;
|
||||
}
|
||||
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
|
||||
|
||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
ViewHolder(View v) {
|
||||
super(v);
|
||||
}
|
||||
}
|
||||
|
||||
public class OverViewHolder extends ViewHolder {
|
||||
TextView temperature;
|
||||
ImageView weather;
|
||||
ImageView windicon;
|
||||
TextView updatetime;
|
||||
TextView sun;
|
||||
|
||||
OverViewHolder(View v) {
|
||||
super(v);
|
||||
this.temperature = v.findViewById(R.id.card_overview_temperature);
|
||||
this.weather = v.findViewById(R.id.card_overview_weather_image);
|
||||
this.sun=v.findViewById(R.id.card_overview_sunrise_sunset);
|
||||
this.windicon=v.findViewById(R.id.card_overview_windicon);
|
||||
this.updatetime=v.findViewById(R.id.card_overview_update_time);
|
||||
}
|
||||
}
|
||||
|
||||
public class DetailViewHolder extends ViewHolder {
|
||||
TextView humidity;
|
||||
TextView pressure;
|
||||
TextView windspeed;
|
||||
TextView rain60min;
|
||||
TextView rain60minLegend;
|
||||
TextView time;
|
||||
ImageView winddirection;
|
||||
|
||||
DetailViewHolder(View v) {
|
||||
super(v);
|
||||
this.humidity = v.findViewById(R.id.card_details_humidity_value);
|
||||
this.pressure = v.findViewById(R.id.card_details_pressure_value);
|
||||
this.windspeed = v.findViewById(R.id.card_details_wind_speed_value);
|
||||
this.rain60min = v.findViewById(R.id.card_details_rain60min_value);
|
||||
this.rain60minLegend=v.findViewById(R.id.card_details_legend_rain60min);
|
||||
this.winddirection =v.findViewById((R.id.card_details_wind_direction_value));
|
||||
this.time=v.findViewById(R.id.card_details_title);
|
||||
}
|
||||
}
|
||||
|
||||
public class WeekViewHolder extends ViewHolder {
|
||||
RecyclerView recyclerView;
|
||||
|
||||
WeekViewHolder(View v) {
|
||||
super(v);
|
||||
recyclerView = v.findViewById(R.id.recycler_view_week);
|
||||
mWeekWeather=recyclerView;
|
||||
}
|
||||
}
|
||||
|
||||
public class DayViewHolder extends ViewHolder {
|
||||
RecyclerView recyclerView;
|
||||
TextView recyclerViewHeader;
|
||||
|
||||
DayViewHolder(View v) {
|
||||
super(v);
|
||||
recyclerView = v.findViewById(R.id.recycler_view_course_day);
|
||||
mCourseOfDay=recyclerView;
|
||||
recyclerViewHeader=v.findViewById(R.id.recycler_view_header);
|
||||
}
|
||||
}
|
||||
|
||||
public class ChartViewHolder extends ViewHolder {
|
||||
|
||||
TextView precipitationunit;
|
||||
BarChartView barChartView;
|
||||
|
||||
ChartViewHolder(View v) {
|
||||
super(v);
|
||||
this.barChartView = v.findViewById(R.id.graph_precipitation);
|
||||
this.precipitationunit=v.findViewById(R.id.graph_precipitationunit);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
|
||||
View v;
|
||||
mParent=viewGroup;
|
||||
if (viewType == OVERVIEW) {
|
||||
v = LayoutInflater.from(viewGroup.getContext())
|
||||
.inflate(R.layout.card_overview, viewGroup, false);
|
||||
|
||||
return new OverViewHolder(v);
|
||||
|
||||
} else if (viewType == DETAILS) {
|
||||
|
||||
v = LayoutInflater.from(viewGroup.getContext())
|
||||
.inflate(R.layout.card_details, viewGroup, false);
|
||||
return new DetailViewHolder(v);
|
||||
|
||||
} else if (viewType == WEEK) {
|
||||
|
||||
v = LayoutInflater.from(viewGroup.getContext())
|
||||
.inflate(R.layout.card_week, viewGroup, false);
|
||||
return new WeekViewHolder(v);
|
||||
|
||||
} else if (viewType == DAY) {
|
||||
|
||||
v = LayoutInflater.from(viewGroup.getContext())
|
||||
.inflate(R.layout.card_day, viewGroup, false);
|
||||
return new DayViewHolder(v);
|
||||
|
||||
} else if (viewType == CHART) {
|
||||
|
||||
v = LayoutInflater.from(viewGroup.getContext())
|
||||
.inflate(R.layout.card_chart, viewGroup, false);
|
||||
return new ChartViewHolder(v);
|
||||
} else {
|
||||
v = LayoutInflater.from(viewGroup.getContext())
|
||||
.inflate(R.layout.card_empty, viewGroup, false);
|
||||
return new ViewHolder(v);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
|
||||
|
||||
boolean isDay = currentWeatherDataList.isDay(context);
|
||||
|
||||
if (viewHolder.getItemViewType() == OVERVIEW) {
|
||||
OverViewHolder holder = (OverViewHolder) viewHolder;
|
||||
|
||||
//correct for timezone differences
|
||||
int zoneseconds = currentWeatherDataList.getTimeZoneSeconds();
|
||||
long riseTime = (currentWeatherDataList.getTimeSunrise() + zoneseconds) * 1000;
|
||||
long setTime = (currentWeatherDataList.getTimeSunset() + zoneseconds) * 1000;
|
||||
if (riseTime==zoneseconds*1000 || setTime==zoneseconds*1000) holder.sun.setText("\u2600\u25b2 --:--" + " \u25bc --:--" );
|
||||
else {
|
||||
holder.sun.setText("\u2600\u25b2 " + StringFormatUtils.formatTimeWithoutZone(context, riseTime) + " \u25bc " + StringFormatUtils.formatTimeWithoutZone(context, setTime));
|
||||
}
|
||||
holder.windicon.setImageResource(StringFormatUtils.colorWindSpeedWidget(currentWeatherDataList.getWindSpeed()));
|
||||
long time = currentWeatherDataList.getTimestamp();
|
||||
long updateTime = ((time + zoneseconds) * 1000);
|
||||
|
||||
holder.updatetime.setText("("+StringFormatUtils.formatTimeWithoutZone(context, updateTime)+")");
|
||||
|
||||
setImage(currentWeatherDataList.getWeatherID(), holder.weather, isDay);
|
||||
|
||||
|
||||
} else if (viewHolder.getItemViewType() == WEEK) {
|
||||
|
||||
final WeekViewHolder holder = (WeekViewHolder) viewHolder;
|
||||
LinearLayoutManager layoutManager = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
|
||||
holder.recyclerView.setLayoutManager(layoutManager);
|
||||
|
||||
|
||||
final WeekWeatherAdapter adapter = new WeekWeatherAdapter(context, forecastData,currentWeatherDataList.getCity_id());
|
||||
holder.recyclerView.setAdapter(adapter);
|
||||
holder.recyclerView.setFocusable(false);
|
||||
|
||||
if (mCourseOfDay!=null) { //otherwise crash if courseOfDay not visible
|
||||
CourseOfDayAdapter dayadapter = (CourseOfDayAdapter) mCourseOfDay.getAdapter();
|
||||
dayadapter.setWeekRecyclerView(holder.recyclerView); //provide CourseOfDayAdapter with reference to week recyclerview
|
||||
adapter.setCourseOfDayHeaderDate(dayadapter.getCourseOfDayHeaderDate()); //initialize WeekWeatherAdapter with current HeaderDate from CourseOfDayAdapter
|
||||
}
|
||||
|
||||
holder.recyclerView.addOnItemTouchListener(
|
||||
new RecyclerItemClickListener(context, holder.recyclerView, new RecyclerItemClickListener.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(View view, int position) {
|
||||
SQLiteHelper database = SQLiteHelper.getInstance(context.getApplicationContext());
|
||||
List<WeekForecast> weekforecasts = database.getWeekForecastsByCityId(currentWeatherDataList.getCity_id());
|
||||
long time = weekforecasts.get(position).getForecastTime(); //time of clicked week item
|
||||
time=time-6*3600000; //week item normally midday -> subtract 6h to get morning time
|
||||
|
||||
if (mCourseOfDay!=null){ //otherwise crash if courseOfDay not visible
|
||||
LinearLayoutManager llm = (LinearLayoutManager) mCourseOfDay.getLayoutManager();
|
||||
|
||||
assert llm != null;
|
||||
int num = llm.findLastVisibleItemPosition() - llm.findFirstVisibleItemPosition(); //get number of visible elements
|
||||
int i;
|
||||
|
||||
for (i = 0; i < courseDayList.size(); i++) {
|
||||
if (courseDayList.get(i).getForecastTime() > time) { //find first ForecastTime > time of clicked item
|
||||
Calendar HeaderTime = Calendar.getInstance();
|
||||
HeaderTime.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
HeaderTime.setTimeInMillis(courseDayList.get(i).getLocalForecastTime(context));
|
||||
adapter.setCourseOfDayHeaderDate(HeaderTime.getTime());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i < courseDayList.size()) { //only if element found
|
||||
if (i > llm.findFirstVisibleItemPosition()) { //if scroll right
|
||||
int min = Math.min(i + num, courseDayList.size()-1); //scroll to i+num so that requested element is on the left. Max scroll to courseDayList.size()
|
||||
mCourseOfDay.getLayoutManager().scrollToPosition(min);
|
||||
} else { //if scroll left
|
||||
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) {
|
||||
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
} else if (viewHolder.getItemViewType() == DAY) {
|
||||
|
||||
DayViewHolder holder = (DayViewHolder) viewHolder;
|
||||
LinearLayoutManager layoutManager = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
|
||||
holder.recyclerView.setLayoutManager(layoutManager);
|
||||
CourseOfDayAdapter adapter = new CourseOfDayAdapter(courseDayList, context,holder.recyclerViewHeader,holder.recyclerView);
|
||||
holder.recyclerView.setAdapter(adapter);
|
||||
holder.recyclerView.setFocusable(false);
|
||||
|
||||
} else if (viewHolder.getItemViewType() == CHART) {
|
||||
ChartViewHolder holder = (ChartViewHolder) viewHolder;
|
||||
|
||||
SQLiteHelper database = SQLiteHelper.getInstance(context.getApplicationContext());
|
||||
List<WeekForecast> weekforecasts = database.getWeekForecastsByCityId(currentWeatherDataList.getCity_id());
|
||||
|
||||
if (weekforecasts.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
float pmax=0;
|
||||
|
||||
BarSet precipitationDataset = new BarSet();
|
||||
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
int zonemilliseconds = currentWeatherDataList.getTimeZoneSeconds()*1000;
|
||||
|
||||
for (int i=0 ; i< weekforecasts.size();i++) {
|
||||
c.setTimeInMillis(weekforecasts.get(i).getForecastTime()+zonemilliseconds);
|
||||
int day = c.get(Calendar.DAY_OF_WEEK);
|
||||
float precip=weekforecasts.get(i).getPrecipitation();
|
||||
|
||||
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
|
||||
|
||||
precipitationDataset.addBar(dayString, precip);
|
||||
if (precip>pmax) pmax=precip;
|
||||
}
|
||||
|
||||
int step;
|
||||
|
||||
ArrayList<ChartSet> temperature = new ArrayList<>();
|
||||
|
||||
ArrayList<ChartSet> precipitation = new ArrayList<>();
|
||||
precipitation.add(precipitationDataset);
|
||||
|
||||
precipitationDataset.setColor(ContextCompat.getColor(context,R.color.yellow));
|
||||
precipitationDataset.setAlpha(0.8f); // make precipitation bars transparent
|
||||
|
||||
|
||||
step = (int) Math.ceil((Math.max(1,pmax))/4);
|
||||
holder.barChartView.addData(precipitation);
|
||||
holder.barChartView.setBarSpacing(10);
|
||||
holder.barChartView.setXAxis(false);
|
||||
holder.barChartView.setYAxis(false);
|
||||
holder.barChartView.setYLabels(AxisController.LabelPosition.INSIDE); //no labels for precipitation
|
||||
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.setFontSize((int) Tools.fromDpToPx(17));
|
||||
holder.barChartView.setBorderSpacing(Tools.fromDpToPx(30));
|
||||
|
||||
holder.barChartView.show();
|
||||
|
||||
holder.precipitationunit.setText(" " + context.getResources().getString(R.string.units_kWh)+" ");
|
||||
}
|
||||
//No update for error needed
|
||||
}
|
||||
|
||||
public void setImage(int value, ImageView imageView, boolean isDay) {
|
||||
imageView.setImageResource(UiResourceProvider.getImageResourceForWeatherCategory(value, isDay));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return dataSetTypes.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
return dataSetTypes[position];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
package org.woheller69.weather.ui.RecycleList;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.content.SharedPreferences;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.woheller69.weather.R;
|
||||
import org.woheller69.weather.database.CurrentWeatherData;
|
||||
import org.woheller69.weather.database.HourlyForecast;
|
||||
import org.woheller69.weather.database.SQLiteHelper;
|
||||
import org.woheller69.weather.ui.Help.StringFormatUtils;
|
||||
import org.woheller69.weather.ui.UiResourceProvider;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
//**
|
||||
// * Created by yonjuni on 02.01.17.
|
||||
// * Adapter for the horizontal listView for course of the day.
|
||||
// */import java.util.List;
|
||||
|
||||
public class CourseOfDayAdapter extends RecyclerView.Adapter<CourseOfDayAdapter.CourseOfDayViewHolder> {
|
||||
|
||||
private List<HourlyForecast> courseOfDayList;
|
||||
private Context context;
|
||||
private TextView recyclerViewHeader;
|
||||
private RecyclerView recyclerView;
|
||||
private RecyclerView weekRecyclerView;
|
||||
private Date courseOfDayHeaderDate;
|
||||
|
||||
CourseOfDayAdapter(List<HourlyForecast> courseOfDayList, Context context, TextView recyclerViewHeader, RecyclerView recyclerView) {
|
||||
this.context = context;
|
||||
this.courseOfDayList = courseOfDayList;
|
||||
this.recyclerViewHeader=recyclerViewHeader;
|
||||
this.recyclerView=recyclerView;
|
||||
if (courseOfDayList.size()!=0 && courseOfDayList.get(0)!=null) {
|
||||
this.courseOfDayHeaderDate = new Date(courseOfDayList.get(0).getLocalForecastTime(context));
|
||||
}else this.courseOfDayHeaderDate=new Date(); //fallback if no data available
|
||||
}
|
||||
|
||||
public void setWeekRecyclerView(RecyclerView weekRecyclerView){
|
||||
this.weekRecyclerView=weekRecyclerView;
|
||||
}
|
||||
|
||||
public Date getCourseOfDayHeaderDate(){
|
||||
return this.courseOfDayHeaderDate;
|
||||
}
|
||||
@Override
|
||||
public CourseOfDayViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_course_of_day, parent, false);
|
||||
return new CourseOfDayViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(CourseOfDayViewHolder holder, int position) {
|
||||
SQLiteHelper dbHelper = SQLiteHelper.getInstance(context);
|
||||
CurrentWeatherData currentWeather = dbHelper.getCurrentWeatherByCityId(courseOfDayList.get(position).getCity_id());
|
||||
|
||||
Calendar forecastTime = Calendar.getInstance();
|
||||
forecastTime.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
forecastTime.setTimeInMillis(courseOfDayList.get(position).getLocalForecastTime(context));
|
||||
|
||||
boolean isDay;
|
||||
if (currentWeather.getTimeSunrise()==0 || currentWeather.getTimeSunset()==0){
|
||||
if ((dbHelper.getCityToWatch(courseOfDayList.get(position).getCity_id()).getLatitude())>0){ //northern hemisphere
|
||||
isDay= forecastTime.get(Calendar.DAY_OF_YEAR) >= 80 && forecastTime.get(Calendar.DAY_OF_YEAR) <= 265; //from March 21 to September 22 (incl)
|
||||
}else{ //southern hemisphere
|
||||
isDay= forecastTime.get(Calendar.DAY_OF_YEAR) < 80 || forecastTime.get(Calendar.DAY_OF_YEAR) > 265;
|
||||
}
|
||||
}else {
|
||||
Calendar sunSetTime = Calendar.getInstance();
|
||||
sunSetTime.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
sunSetTime.setTimeInMillis(currentWeather.getTimeSunset() * 1000 + currentWeather.getTimeZoneSeconds() * 1000L);
|
||||
sunSetTime.set(Calendar.DAY_OF_YEAR, forecastTime.get(Calendar.DAY_OF_YEAR));
|
||||
sunSetTime.set(Calendar.YEAR, forecastTime.get(Calendar.YEAR));
|
||||
|
||||
|
||||
Calendar sunRiseTime = Calendar.getInstance();
|
||||
sunRiseTime.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
sunRiseTime.setTimeInMillis(currentWeather.getTimeSunrise() * 1000 + currentWeather.getTimeZoneSeconds() * 1000L);
|
||||
sunRiseTime.set(Calendar.DAY_OF_YEAR, forecastTime.get(Calendar.DAY_OF_YEAR));
|
||||
sunRiseTime.set(Calendar.YEAR, forecastTime.get(Calendar.YEAR));
|
||||
|
||||
isDay = forecastTime.after(sunRiseTime) && forecastTime.before(sunSetTime);
|
||||
}
|
||||
|
||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
if (sp.getBoolean("pref_debug",false)) {
|
||||
holder.diffuseRadiation.setVisibility(View.VISIBLE);
|
||||
holder.directRadiationNormal.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.diffuseRadiation.setVisibility(View.GONE);
|
||||
holder.directRadiationNormal.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
holder.time.setText(StringFormatUtils.formatTimeWithoutZone(context, courseOfDayList.get(position).getLocalForecastTime(context)));
|
||||
holder.directRadiationNormal.setText(StringFormatUtils.formatInt(courseOfDayList.get(position).getDirectRadiationNormal()," 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)));
|
||||
updateRecyclerViewHeader(); //update header according to date in first visible item on the left
|
||||
|
||||
setIcon(courseOfDayList.get(position).getWeatherID(), holder.weather, isDay);
|
||||
|
||||
|
||||
}
|
||||
|
||||
//update header according to date in first visible item on the left of recyclerview
|
||||
private void updateRecyclerViewHeader() {
|
||||
RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
|
||||
LinearLayoutManager llm = (LinearLayoutManager) manager;
|
||||
assert llm != null;
|
||||
int visiblePosition = llm.findFirstVisibleItemPosition();
|
||||
if (visiblePosition>-1) {
|
||||
Calendar HeaderTime = Calendar.getInstance();
|
||||
HeaderTime.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
HeaderTime.setTimeInMillis(courseOfDayList.get(visiblePosition).getLocalForecastTime(context));
|
||||
int headerday = HeaderTime.get(Calendar.DAY_OF_WEEK);
|
||||
headerday = StringFormatUtils.getDayLong(headerday);
|
||||
recyclerViewHeader.setText(context.getResources().getString(headerday));
|
||||
|
||||
courseOfDayHeaderDate=HeaderTime.getTime();
|
||||
|
||||
if (weekRecyclerView!=null){
|
||||
WeekWeatherAdapter weekadapter = (WeekWeatherAdapter) weekRecyclerView.getAdapter();
|
||||
weekadapter.setCourseOfDayHeaderDate(courseOfDayHeaderDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return courseOfDayList.size();
|
||||
}
|
||||
|
||||
class CourseOfDayViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView time;
|
||||
ImageView weather;
|
||||
TextView directRadiationNormal;
|
||||
TextView diffuseRadiation;
|
||||
TextView power;
|
||||
TextView wind_speed;
|
||||
|
||||
CourseOfDayViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
|
||||
time = itemView.findViewById(R.id.course_of_day_time);
|
||||
weather = itemView.findViewById(R.id.course_of_day_weather);
|
||||
directRadiationNormal = itemView.findViewById(R.id.course_of_day_direct);
|
||||
diffuseRadiation = itemView.findViewById(R.id.course_of_day_diffuse);
|
||||
power = itemView.findViewById(R.id.course_of_day_power);
|
||||
}
|
||||
}
|
||||
|
||||
public void setIcon(int value, ImageView imageView, boolean isDay) {
|
||||
imageView.setImageResource(UiResourceProvider.getIconResourceForWeatherCategory(value, isDay));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package org.woheller69.weather.ui.RecycleList;
|
||||
|
||||
/**
|
||||
* This interface defines the functionality that can be bound to touch events.
|
||||
* For the most part it has been taken from
|
||||
* https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-b9456d2b1aaf#.hmhbe8sku
|
||||
* as of 2016-08-03
|
||||
*/
|
||||
public interface ItemTouchHelperAdapter {
|
||||
|
||||
/**
|
||||
* This method removes an item from an adapter at the specified positin.
|
||||
*
|
||||
* @param position The position of the item to remove.
|
||||
*/
|
||||
void onItemDismiss(int position);
|
||||
|
||||
/**
|
||||
* This method is required to remove items from the list that is used to display the data
|
||||
* whenever an item is deleted by swiping.
|
||||
*
|
||||
* @param fromPosition The from position.
|
||||
* @param toPosition The to position.
|
||||
*/
|
||||
void onItemMove(int fromPosition, int toPosition);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package org.woheller69.weather.ui.RecycleList;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.woheller69.weather.R;
|
||||
|
||||
/**
|
||||
* This class holds instances of items that are to be displayed in the list.
|
||||
* The idea of this class has been taken from
|
||||
* https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-b9456d2b1aaf#.hmhbe8sku
|
||||
* as of 2016-08-03. Parts of the code were copied from that source.
|
||||
*/
|
||||
public class ItemViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
/**
|
||||
* Member variables
|
||||
*/
|
||||
public TextView cityName;
|
||||
public TextView elevationAngle;
|
||||
public TextView azimuthAngle;
|
||||
public TextView cellsMaxPower;
|
||||
public TextView cellsArea;
|
||||
public TextView cellsEfficiency;
|
||||
public TextView diffuseEfficiency;
|
||||
public TextView converterPowerLimit;
|
||||
public TextView converterEfficiency;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param itemView The view that contains the fields that are to be set for each list item.
|
||||
*/
|
||||
public ItemViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
this.cityName = (TextView) itemView.findViewById(R.id.city_overview_list_item_text);
|
||||
this.elevationAngle = (TextView) itemView.findViewById(R.id.city_elevation_angle);
|
||||
this.azimuthAngle = (TextView) itemView.findViewById(R.id.city_azimuth_angle);
|
||||
this.cellsMaxPower = (TextView) itemView.findViewById(R.id.city_cells_max_power);
|
||||
this.cellsArea = (TextView) itemView.findViewById(R.id.city_cells_area);
|
||||
this.cellsEfficiency = (TextView) itemView.findViewById(R.id.city_cells_efficiency);
|
||||
this.diffuseEfficiency = (TextView) itemView.findViewById(R.id.city_diffuse_efficiency);
|
||||
this.converterPowerLimit = (TextView) itemView.findViewById(R.id.city_converter_power_limit);
|
||||
this.converterEfficiency = (TextView) itemView.findViewById(R.id.city_converter_efficiency);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package org.woheller69.weather.ui.RecycleList;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
public class OnSwipeDownListener implements View.OnTouchListener {
|
||||
|
||||
private final GestureDetector gestureDetector;
|
||||
|
||||
public OnSwipeDownListener(Context context) {
|
||||
gestureDetector = new GestureDetector(context, new GestureListener());
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
public boolean onTouch(final View view, final MotionEvent motionEvent) {
|
||||
return gestureDetector.onTouchEvent(motionEvent);
|
||||
}
|
||||
|
||||
private final class GestureListener extends GestureDetector.SimpleOnGestureListener {
|
||||
|
||||
private static final int SWIPE_THRESHOLD = 120;
|
||||
private static final int SWIPE_VELOCITY_THRESHOLD = 120;
|
||||
|
||||
@Override
|
||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||
boolean result = false;
|
||||
try {
|
||||
float diffY = e2.getY() - e1.getY();
|
||||
if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
|
||||
if (diffY > 0) {
|
||||
onSwipeDown();
|
||||
}
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public void onSwipeDown() { }
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
package org.woheller69.weather.ui.RecycleList;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Provides the functionality to detect (long) touch events on RecyclerView items.
|
||||
* The code has been taken from
|
||||
* http://stackoverflow.com/questions/24471109/recyclerview-onclick (answer of H. Azizkhani) as of
|
||||
* 2016-08-04.
|
||||
*/
|
||||
|
||||
public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
|
||||
private OnItemClickListener mListener;
|
||||
|
||||
public interface OnItemClickListener {
|
||||
void onItemClick(View view, int position);
|
||||
|
||||
void onLongItemClick(View view, int position);
|
||||
|
||||
}
|
||||
|
||||
private GestureDetector mGestureDetector;
|
||||
|
||||
public RecyclerItemClickListener(Context context, final RecyclerView recyclerView, OnItemClickListener listener) {
|
||||
mListener = listener;
|
||||
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
|
||||
|
||||
@Override
|
||||
public boolean onSingleTapUp(MotionEvent e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLongPress(MotionEvent e) {
|
||||
View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
|
||||
if (child != null && mListener != null) {
|
||||
mListener.onLongItemClick(child, recyclerView.getChildAdapterPosition(child));
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
|
||||
View childView = view.findChildViewUnder(e.getX(), e.getY());
|
||||
if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
|
||||
mListener.onItemClick(childView, view.getChildAdapterPosition(childView));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
package org.woheller69.weather.ui.RecycleList;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
|
||||
import org.woheller69.weather.R;
|
||||
import org.woheller69.weather.database.CityToWatch;
|
||||
import org.woheller69.weather.database.SQLiteHelper;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is the adapter for the RecyclerList that is to be used for the overview of added locations.
|
||||
* For the most part, it has been taken from
|
||||
* https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-b9456d2b1aaf#.hmhbe8sku
|
||||
* as of 2016-08-03
|
||||
*/
|
||||
public class RecyclerOverviewListAdapter extends RecyclerView.Adapter<ItemViewHolder> implements ItemTouchHelperAdapter {
|
||||
|
||||
/**
|
||||
* Member variables
|
||||
*/
|
||||
private Context context;
|
||||
private final List<CityToWatch> cities;
|
||||
|
||||
SQLiteHelper database;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public RecyclerOverviewListAdapter(Context context, List<CityToWatch> cities) {
|
||||
this.context = context;
|
||||
this.cities = cities;
|
||||
this.database = SQLiteHelper.getInstance(context);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see RecyclerView.Adapter#onCreateViewHolder(ViewGroup, int)
|
||||
* Returns the template for a list item.
|
||||
*/
|
||||
@Override
|
||||
public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_city_list, parent, false);
|
||||
return new ItemViewHolder(view);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see RecyclerView.Adapter#onBindViewHolder(RecyclerView.ViewHolder, int)
|
||||
* Sets the content of items.
|
||||
*/
|
||||
@Override
|
||||
public void onBindViewHolder(ItemViewHolder holder, int position) {
|
||||
holder.cityName.setText(cities.get(position).getCityName());
|
||||
holder.azimuthAngle.setText(context.getString(R.string.edit_location_hint_azimuth) +": "+ cities.get(position).getAzimuthAngle());
|
||||
holder.elevationAngle.setText(context.getString(R.string.edit_location_hint_elevation) +": "+ cities.get(position).getElevationAngle());
|
||||
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.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.converterPowerLimit.setText(context.getString(R.string.edit_location_hint_converter_power_limit) +": "+ cities.get(position).getConverterPowerLimit());
|
||||
holder.converterEfficiency.setText(context.getString(R.string.edit_location_hint_cells_efficiency) +": "+ cities.get(position).getConverterEfficiency());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @see RecyclerView.Adapter#getItemCount()
|
||||
*/
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return cities.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ItemTouchHelperAdapter#onItemDismiss(int)
|
||||
* Removes an item from the list.
|
||||
*/
|
||||
@Override
|
||||
public void onItemDismiss(int position) {
|
||||
|
||||
CityToWatch city = cities.get(position);
|
||||
database.deleteCityToWatch(city);
|
||||
cities.remove(position);
|
||||
notifyItemRemoved(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ItemTouchHelperAdapter#onItemMove(int, int)
|
||||
*/
|
||||
@Override
|
||||
public void onItemMove(int fromPosition, int toPosition) {
|
||||
// For updating the database records
|
||||
CityToWatch fromCityToWatch = cities.get(fromPosition);
|
||||
int fromRank = fromCityToWatch.getRank();
|
||||
CityToWatch toCityToWatch = cities.get(toPosition);
|
||||
int toRank = toCityToWatch.getRank();
|
||||
|
||||
fromCityToWatch.setRank(toRank);
|
||||
toCityToWatch.setRank(fromRank);
|
||||
database.updateCityToWatch(fromCityToWatch);
|
||||
database.updateCityToWatch(toCityToWatch);
|
||||
Collections.swap(cities, fromPosition, toPosition);
|
||||
notifyItemMoved(fromPosition, toPosition);
|
||||
|
||||
}
|
||||
|
||||
public CityToWatch getCitytoWatch(int position){
|
||||
return cities.get(position);
|
||||
}
|
||||
public void updateCity(int position, String cityName, float latitude, float longitude, float azimuth, float elevation, float cellsMaxPower, float cellsArea, float cellsEfficiency, float diffuseEfficiency, float converterPowerLimit, float converterEfficiency) {
|
||||
CityToWatch cityToWatch = cities.get(position);
|
||||
cityToWatch.setCityName(cityName);
|
||||
cityToWatch.setLatitude(latitude);
|
||||
cityToWatch.setLongitude(longitude);
|
||||
cityToWatch.setAzimuthAngle(azimuth);
|
||||
cityToWatch.setElevationAngle(elevation);
|
||||
cityToWatch.setCellsMaxPower(cellsMaxPower);
|
||||
cityToWatch.setCellsArea(cellsArea);
|
||||
cityToWatch.setCellsEfficiency(cellsEfficiency);
|
||||
cityToWatch.setDiffuseEfficiency(diffuseEfficiency);
|
||||
cityToWatch.setConverterPowerLimit(converterPowerLimit);
|
||||
cityToWatch.setConverterEfficiency(converterEfficiency);
|
||||
database.updateCityToWatch(cityToWatch);
|
||||
notifyItemChanged(position);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package org.woheller69.weather.ui.RecycleList;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import org.woheller69.weather.R;
|
||||
|
||||
/**
|
||||
* This class defines a separator for recycler views. The code has been taken from N J's answer from
|
||||
* http://stackoverflow.com/questions/31242812/how-can-a-divider-line-be-added-in-an-android-recyclerview
|
||||
* as of 2016-09-04 and was slightly modified.
|
||||
*/
|
||||
public class SimpleDividerItemDecoration extends RecyclerView.ItemDecoration {
|
||||
|
||||
private Drawable mDivider;
|
||||
|
||||
public SimpleDividerItemDecoration(Context context) {
|
||||
mDivider = ContextCompat.getDrawable(context, R.drawable.recycle_view_line_divider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
|
||||
int left = parent.getPaddingLeft();
|
||||
int right = parent.getWidth() - parent.getPaddingRight();
|
||||
|
||||
int childCount = parent.getChildCount();
|
||||
for (int i = 0; i < childCount - 1; i++) {
|
||||
View child = parent.getChildAt(i);
|
||||
|
||||
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
|
||||
|
||||
int top = child.getBottom() + params.bottomMargin;
|
||||
Log.d("debug", "Top: " + top);
|
||||
int bottom = top + mDivider.getIntrinsicHeight();
|
||||
Log.d("debug", "Bottom: " + top);
|
||||
|
||||
mDivider.setBounds(left, top, right, bottom);
|
||||
mDivider.draw(c);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
package org.woheller69.weather.ui.RecycleList;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
|
||||
/**
|
||||
* To use the ItemTouchHelper we need to create an ItemTouchHelper.Callback which this class is.
|
||||
* For the most part it has been taken from
|
||||
* https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-b9456d2b1aaf#.hmhbe8sku
|
||||
* as of 2016-08-03
|
||||
*/
|
||||
public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
|
||||
|
||||
private final ItemTouchHelperAdapter adapter;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param adapter The adapter to bind the ItemTouchHelper to.
|
||||
*/
|
||||
public SimpleItemTouchHelperCallback(ItemTouchHelperAdapter adapter) {
|
||||
this.adapter = adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ItemTouchHelper.Callback#isLongPressDragEnabled()
|
||||
* As it is not supported, false will be returned.
|
||||
*/
|
||||
@Override
|
||||
public boolean isLongPressDragEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ItemTouchHelper.Callback#isItemViewSwipeEnabled()
|
||||
* As this feature is supported, true will be returned.
|
||||
*/
|
||||
@Override
|
||||
public boolean isItemViewSwipeEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see androidx.recyclerview.widget.ItemTouchHelper.Callback#getMovementFlags(RecyclerView, RecyclerView.ViewHolder)
|
||||
* Sets the swipe flags for start and end.
|
||||
*/
|
||||
@Override
|
||||
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
|
||||
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
|
||||
int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
|
||||
return makeMovementFlags(dragFlags, swipeFlags);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see androidx.recyclerview.widget.ItemTouchHelper.Callback#onMove(RecyclerView, RecyclerView.ViewHolder, RecyclerView.ViewHolder)
|
||||
*/
|
||||
@Override
|
||||
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
|
||||
adapter.onItemMove(viewHolder.getBindingAdapterPosition(), target.getBindingAdapterPosition());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see androidx.recyclerview.widget.ItemTouchHelper.Callback#onSwiped(RecyclerView.ViewHolder, int)
|
||||
* On swipe, the corresponding element is removed from the list.
|
||||
*/
|
||||
@Override
|
||||
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
|
||||
adapter.onItemDismiss(viewHolder.getBindingAdapterPosition());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
package org.woheller69.weather.ui.RecycleList;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.woheller69.weather.R;
|
||||
import org.woheller69.weather.database.CurrentWeatherData;
|
||||
import org.woheller69.weather.database.SQLiteHelper;
|
||||
import org.woheller69.weather.ui.Help.StringFormatUtils;
|
||||
import org.woheller69.weather.ui.UiResourceProvider;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* Created by yonjuni on 02.01.17.
|
||||
*/
|
||||
|
||||
public class WeekWeatherAdapter extends RecyclerView.Adapter<WeekWeatherAdapter.WeekForecastViewHolder> {
|
||||
|
||||
private Context context;
|
||||
private float[][] forecastData;
|
||||
private int cityID;
|
||||
private Date courseOfDayHeaderDate;
|
||||
|
||||
WeekWeatherAdapter(Context context, float[][] forecastData, int cityID) {
|
||||
this.context = context;
|
||||
this.cityID = cityID;
|
||||
this.forecastData = forecastData;
|
||||
if (forecastData!=null && forecastData.length!=0 && forecastData[0]!=null) {
|
||||
this.courseOfDayHeaderDate = new Date((long) forecastData[0][8]); //init with date of first weekday
|
||||
} else this.courseOfDayHeaderDate = new Date(); //fallback if no data available
|
||||
}
|
||||
|
||||
public void setCourseOfDayHeaderDate(Date courseOfDayHeaderDate){
|
||||
Date oldDate=this.courseOfDayHeaderDate;
|
||||
this.courseOfDayHeaderDate=courseOfDayHeaderDate;
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
|
||||
c.setTime(oldDate);
|
||||
int oldDay=c.get(Calendar.DAY_OF_MONTH);
|
||||
c.setTime(courseOfDayHeaderDate);
|
||||
int newDay=c.get(Calendar.DAY_OF_MONTH);
|
||||
if (newDay!=oldDay){ //Refresh viewholder only of day has changed
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public WeekForecastViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_week_forecast, parent, false);
|
||||
return new WeekForecastViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(WeekForecastViewHolder holder, int position) {
|
||||
float[] dayValues = forecastData[position];
|
||||
if (dayValues.length!=11) return; //Fixes app crash if forecastData not yet ready.
|
||||
|
||||
SQLiteHelper dbHelper = SQLiteHelper.getInstance(context);
|
||||
CurrentWeatherData currentWeather = dbHelper.getCurrentWeatherByCityId(cityID);
|
||||
|
||||
Calendar forecastTime = Calendar.getInstance();
|
||||
forecastTime.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
forecastTime.setTimeInMillis((long) dayValues[8]);
|
||||
|
||||
boolean isDay;
|
||||
|
||||
if (currentWeather.getTimeSunrise()==0 || currentWeather.getTimeSunset()==0) {
|
||||
if ((dbHelper.getCityToWatch(cityID).getLatitude()) > 0) { //northern hemisphere
|
||||
isDay = forecastTime.get(Calendar.DAY_OF_YEAR) >= 80 && forecastTime.get(Calendar.DAY_OF_YEAR) <= 265; //from March 21 to September 22 (incl)
|
||||
} else { //southern hemisphere
|
||||
isDay = forecastTime.get(Calendar.DAY_OF_YEAR) < 80 || forecastTime.get(Calendar.DAY_OF_YEAR) > 265;
|
||||
}
|
||||
} else {
|
||||
isDay = true;
|
||||
}
|
||||
|
||||
setIcon((int) dayValues[9], holder.weather, isDay);
|
||||
if (dayValues[4] == 0)
|
||||
holder.power.setText("-");
|
||||
else
|
||||
holder.power.setText(StringFormatUtils.formatDecimal(dayValues[4], context.getString(R.string.units_kWh)));
|
||||
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
c.setTimeInMillis((long) dayValues[8]);
|
||||
int day = c.get(Calendar.DAY_OF_WEEK);
|
||||
|
||||
holder.day.setText(StringFormatUtils.getDayShort(day));
|
||||
|
||||
day=c.get(Calendar.DAY_OF_MONTH);
|
||||
c.setTimeInMillis(courseOfDayHeaderDate.getTime());
|
||||
int dayheader=c.get(Calendar.DAY_OF_MONTH);
|
||||
if (dayheader==day) {
|
||||
holder.itemView.setBackground(ResourcesCompat.getDrawable(context.getResources(),R.drawable.rounded_highlight,null));
|
||||
}else{
|
||||
holder.itemView.setBackground(ResourcesCompat.getDrawable(context.getResources(),R.drawable.rounded_transparent,null));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
if (forecastData!=null)
|
||||
return forecastData.length;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
class WeekForecastViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
TextView day;
|
||||
ImageView weather;
|
||||
TextView power;
|
||||
|
||||
WeekForecastViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
|
||||
day = itemView.findViewById(R.id.week_forecast_day);
|
||||
weather = itemView.findViewById(R.id.week_forecast_weather);
|
||||
power = itemView.findViewById(R.id.week_forecast_power);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
|
||||
super.onAttachedToRecyclerView(recyclerView);
|
||||
}
|
||||
|
||||
public void setIcon(int value, ImageView imageView, boolean isDay) {
|
||||
imageView.setImageResource(UiResourceProvider.getIconResourceForWeatherCategory(value, isDay));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,304 @@
|
|||
package org.woheller69.weather.ui;
|
||||
|
||||
import org.woheller69.weather.R;
|
||||
import org.woheller69.weather.weather_api.IApiToDatabaseConversion.WeatherCategories;
|
||||
|
||||
/**
|
||||
* This static class provides image / icon resources for the UI.
|
||||
*/
|
||||
public class UiResourceProvider {
|
||||
|
||||
/**
|
||||
* Private constructor in order to make this class static.
|
||||
*/
|
||||
private UiResourceProvider() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param categoryNumber The category number. See IApiToDatabaseConversion#WeatherCategories
|
||||
* for details.
|
||||
* @param isDay True if TimeStamp between sunrise and sunset
|
||||
* @return Returns the icon (resource) that belongs to the given category number.
|
||||
*/
|
||||
public static int getIconResourceForWeatherCategory(int categoryNumber, boolean isDay) {
|
||||
if (categoryNumber == WeatherCategories.CLEAR_SKY.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.mipmap.wmo_icon_00d;
|
||||
} else {
|
||||
return R.mipmap.wmo_icon_00n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.FEW_CLOUDS.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.mipmap.wmo_icon_01d;
|
||||
} else {
|
||||
return R.mipmap.wmo_icon_01n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.SCATTERED_CLOUDS.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.mipmap.wmo_icon_02d;
|
||||
} else {
|
||||
return R.mipmap.wmo_icon_02n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.OVERCAST_CLOUDS.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.mipmap.wmo_icon_03d;
|
||||
} else {
|
||||
return R.mipmap.wmo_icon_03n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.MIST.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.mipmap.wmo_icon_45d;
|
||||
} else {
|
||||
return R.mipmap.wmo_icon_45n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.DRIZZLE_RAIN.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.mipmap.wmo_icon_53d;
|
||||
} else {
|
||||
return R.mipmap.wmo_icon_53n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.FREEZING_DRIZZLE_RAIN.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.mipmap.wmo_icon_57d;
|
||||
} else {
|
||||
return R.mipmap.wmo_icon_57n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.LIGHT_RAIN.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.mipmap.wmo_icon_61d;
|
||||
} else {
|
||||
return R.mipmap.wmo_icon_61n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.MODERATE_RAIN.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.mipmap.wmo_icon_63d;
|
||||
} else {
|
||||
return R.mipmap.wmo_icon_63n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.HEAVY_RAIN.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.mipmap.wmo_icon_65d;
|
||||
} else {
|
||||
return R.mipmap.wmo_icon_65n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.LIGHT_SHOWER_RAIN.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.mipmap.wmo_icon_80d;
|
||||
} else {
|
||||
return R.mipmap.wmo_icon_80n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.SHOWER_RAIN.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.mipmap.wmo_icon_81d;
|
||||
} else {
|
||||
return R.mipmap.wmo_icon_81n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.LIGHT_SNOW.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.mipmap.wmo_icon_71d;
|
||||
} else {
|
||||
return R.mipmap.wmo_icon_71n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.MODERATE_SNOW.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.mipmap.wmo_icon_73d;
|
||||
} else {
|
||||
return R.mipmap.wmo_icon_73n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.HEAVY_SNOW.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.mipmap.wmo_icon_75d;
|
||||
} else {
|
||||
return R.mipmap.wmo_icon_75n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.LIGHT_FREEZING_RAIN.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.mipmap.wmo_icon_66d;
|
||||
} else {
|
||||
return R.mipmap.wmo_icon_66n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.FREEZING_RAIN.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.mipmap.wmo_icon_67d;
|
||||
} else {
|
||||
return R.mipmap.wmo_icon_67n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.LIGHT_SHOWER_SNOW.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.mipmap.wmo_icon_85d;
|
||||
} else {
|
||||
return R.mipmap.wmo_icon_85n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.SHOWER_SNOW.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.mipmap.wmo_icon_86d;
|
||||
} else {
|
||||
return R.mipmap.wmo_icon_86n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.SHOWER_RAIN_SNOW.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.mipmap.wmo_icon_84d;
|
||||
} else {
|
||||
return R.mipmap.wmo_icon_84n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.THUNDERSTORM.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.mipmap.wmo_icon_95d;
|
||||
} else {
|
||||
return R.mipmap.wmo_icon_95n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.THUNDERSTORM_HAIL.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.mipmap.wmo_icon_96d;
|
||||
} else {
|
||||
return R.mipmap.wmo_icon_96n;
|
||||
}
|
||||
} else { //this should not occur
|
||||
return R.mipmap.wmo_icon_error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param categoryNumber The category number. See IApiToDatabaseConversion#WeatherCategories
|
||||
* for details.
|
||||
* @param isDay True if TimeStamp between sunrise and sunset
|
||||
* @return Returns the image resource that belongs to the given category number.
|
||||
*/
|
||||
public static int getImageResourceForWeatherCategory(int categoryNumber, boolean isDay) {
|
||||
if (categoryNumber == WeatherCategories.CLEAR_SKY.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.drawable.wmo_image_00d;
|
||||
} else {
|
||||
return R.drawable.wmo_image_00n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.FEW_CLOUDS.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.drawable.wmo_image_01d;
|
||||
} else {
|
||||
return R.drawable.wmo_image_01n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.SCATTERED_CLOUDS.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.drawable.wmo_image_02d;
|
||||
} else {
|
||||
return R.drawable.wmo_image_02n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.OVERCAST_CLOUDS.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.drawable.wmo_image_03d;
|
||||
} else {
|
||||
return R.drawable.wmo_image_03n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.MIST.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.drawable.wmo_image_45d;
|
||||
} else {
|
||||
return R.drawable.wmo_image_45n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.DRIZZLE_RAIN.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.drawable.wmo_image_53d;
|
||||
} else {
|
||||
return R.drawable.wmo_image_53n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.FREEZING_DRIZZLE_RAIN.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.drawable.wmo_image_57d;
|
||||
} else {
|
||||
return R.drawable.wmo_image_57n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.LIGHT_RAIN.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.drawable.wmo_image_61d;
|
||||
} else {
|
||||
return R.drawable.wmo_image_61n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.MODERATE_RAIN.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.drawable.wmo_image_63d;
|
||||
} else {
|
||||
return R.drawable.wmo_image_63n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.HEAVY_RAIN.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.drawable.wmo_image_65d;
|
||||
} else {
|
||||
return R.drawable.wmo_image_65n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.LIGHT_SHOWER_RAIN.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.drawable.wmo_image_80d;
|
||||
} else {
|
||||
return R.drawable.wmo_image_80n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.SHOWER_RAIN.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.drawable.wmo_image_81d;
|
||||
} else {
|
||||
return R.drawable.wmo_image_81n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.LIGHT_SNOW.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.drawable.wmo_image_71d;
|
||||
} else {
|
||||
return R.drawable.wmo_image_71n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.MODERATE_SNOW.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.drawable.wmo_image_73d;
|
||||
} else {
|
||||
return R.drawable.wmo_image_73n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.HEAVY_SNOW.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.drawable.wmo_image_75d;
|
||||
} else {
|
||||
return R.drawable.wmo_image_75n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.LIGHT_FREEZING_RAIN.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.drawable.wmo_image_66d;
|
||||
} else {
|
||||
return R.drawable.wmo_image_66n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.FREEZING_RAIN.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.drawable.wmo_image_67d;
|
||||
} else {
|
||||
return R.drawable.wmo_image_67n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.LIGHT_SHOWER_SNOW.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.drawable.wmo_image_85d;
|
||||
} else {
|
||||
return R.drawable.wmo_image_85n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.SHOWER_SNOW.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.drawable.wmo_image_86d;
|
||||
} else {
|
||||
return R.drawable.wmo_image_86n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.SHOWER_RAIN_SNOW.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.drawable.wmo_image_84d;
|
||||
} else {
|
||||
return R.drawable.wmo_image_84n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.THUNDERSTORM.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.drawable.wmo_image_95d;
|
||||
} else {
|
||||
return R.drawable.wmo_image_95n;
|
||||
}
|
||||
} else if (categoryNumber == WeatherCategories.THUNDERSTORM_HAIL.getNumVal()) {
|
||||
if (isDay) {
|
||||
return R.drawable.wmo_image_96d;
|
||||
} else {
|
||||
return R.drawable.wmo_image_96n;
|
||||
}
|
||||
} else { //this should not occur
|
||||
return R.drawable.wmo_image_error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
package org.woheller69.weather.ui;
|
||||
|
||||
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.DETAILS;
|
||||
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 android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.woheller69.weather.R;
|
||||
import org.woheller69.weather.activities.ForecastCityActivity;
|
||||
import org.woheller69.weather.database.CurrentWeatherData;
|
||||
import org.woheller69.weather.database.HourlyForecast;
|
||||
import org.woheller69.weather.database.SQLiteHelper;
|
||||
import org.woheller69.weather.database.WeekForecast;
|
||||
import org.woheller69.weather.ui.RecycleList.CityWeatherAdapter;
|
||||
import org.woheller69.weather.ui.RecycleList.OnSwipeDownListener;
|
||||
import org.woheller69.weather.ui.updater.IUpdateableCityUI;
|
||||
import org.woheller69.weather.ui.updater.ViewUpdater;
|
||||
import org.woheller69.weather.ui.viewPager.WeatherPagerAdapter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class WeatherCityFragment extends Fragment implements IUpdateableCityUI {
|
||||
private static final int MINGRIDWIDTH = 500;
|
||||
private int mCityId = -1;
|
||||
private int[] mDataSetTypes = new int[]{};
|
||||
private static int[] mFull = {DAY, WEEK, CHART}; //TODO Make dynamic from Settings
|
||||
private static int[] mEmpty = {EMPTY};
|
||||
private CityWeatherAdapter mAdapter;
|
||||
|
||||
private RecyclerView recyclerView;
|
||||
|
||||
public static WeatherCityFragment newInstance(Bundle args)
|
||||
{
|
||||
WeatherCityFragment weatherCityFragment = new WeatherCityFragment();
|
||||
weatherCityFragment.setArguments(args);
|
||||
return weatherCityFragment;
|
||||
}
|
||||
|
||||
public void setAdapter(CityWeatherAdapter adapter) {
|
||||
mAdapter = adapter;
|
||||
|
||||
if (recyclerView != null) {
|
||||
recyclerView.setAdapter(mAdapter);
|
||||
recyclerView.setFocusable(false);
|
||||
recyclerView.setLayoutManager(getLayoutManager(getContext())); //fixes problems with StaggeredGrid: After refreshing data only empty space shown below tab
|
||||
}
|
||||
}
|
||||
|
||||
public void loadData() {
|
||||
CurrentWeatherData currentWeatherData = SQLiteHelper.getInstance(getContext()).getCurrentWeatherByCityId(mCityId);
|
||||
if (currentWeatherData.getTimestamp()==0) mDataSetTypes=mEmpty; //show empty view if no data available yet
|
||||
else mDataSetTypes=mFull;
|
||||
mAdapter = new CityWeatherAdapter(currentWeatherData, mDataSetTypes, getContext());
|
||||
setAdapter(mAdapter);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
|
||||
ViewUpdater.addSubscriber(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
ViewUpdater.removeSubscriber(this);
|
||||
|
||||
super.onDetach();
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
final View v = inflater.inflate(R.layout.fragment_weather_forecast_city_overview, container, false);
|
||||
|
||||
recyclerView = v.findViewById(R.id.weatherForecastRecyclerView);
|
||||
recyclerView.setLayoutManager(getLayoutManager(getContext()));
|
||||
|
||||
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener(){
|
||||
@Override
|
||||
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
|
||||
super.onScrolled(recyclerView, dx, dy);
|
||||
if (!recyclerView.canScrollVertically(-1)){
|
||||
recyclerView.setOnTouchListener(new OnSwipeDownListener(getContext()) {
|
||||
public void onSwipeDown() {
|
||||
WeatherPagerAdapter.refreshSingleData(getContext(),true,mCityId);
|
||||
ForecastCityActivity.startRefreshAnimation();
|
||||
}
|
||||
});
|
||||
}else recyclerView.setOnTouchListener(null);
|
||||
}
|
||||
});
|
||||
|
||||
Bundle args = getArguments();
|
||||
mCityId = args.getInt("city_id");
|
||||
|
||||
loadData();
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
public RecyclerView.LayoutManager getLayoutManager(Context context) {
|
||||
return new LinearLayoutManager(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNewCurrentWeatherData(CurrentWeatherData data) {
|
||||
if (data != null && data.getCity_id() == mCityId) {
|
||||
mDataSetTypes= mFull;
|
||||
setAdapter(new CityWeatherAdapter(data, mDataSetTypes, getContext()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNewForecasts(List<HourlyForecast> hourlyForecasts) {
|
||||
if (hourlyForecasts != null && hourlyForecasts.size() > 0 && hourlyForecasts.get(0).getCity_id() == mCityId) {
|
||||
if (mAdapter != null) {
|
||||
mAdapter.updateForecastData(hourlyForecasts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package org.woheller69.weather.ui.updater;
|
||||
|
||||
import org.woheller69.weather.database.CurrentWeatherData;
|
||||
import org.woheller69.weather.database.HourlyForecast;
|
||||
import org.woheller69.weather.database.WeekForecast;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by chris on 24.01.2017.
|
||||
*/
|
||||
public interface IUpdateableCityUI {
|
||||
void processNewCurrentWeatherData(CurrentWeatherData data);
|
||||
|
||||
void processNewForecasts(List<HourlyForecast> hourlyForecasts);
|
||||
|
||||
void processNewWeekForecasts(List<WeekForecast> forecasts);
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package org.woheller69.weather.ui.updater;
|
||||
|
||||
import org.woheller69.weather.database.CurrentWeatherData;
|
||||
import org.woheller69.weather.database.HourlyForecast;
|
||||
import org.woheller69.weather.database.WeekForecast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by chris on 24.01.2017.
|
||||
*/
|
||||
|
||||
public class ViewUpdater {
|
||||
private static List<IUpdateableCityUI> subscribers = new ArrayList<>();
|
||||
|
||||
public static void addSubscriber(IUpdateableCityUI sub) {
|
||||
if (!subscribers.contains(sub)) {
|
||||
subscribers.add(sub);
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeSubscriber(IUpdateableCityUI sub) {
|
||||
subscribers.remove(sub);
|
||||
}
|
||||
|
||||
public static void updateCurrentWeatherData(CurrentWeatherData data) {
|
||||
ArrayList<IUpdateableCityUI> subcopy = new ArrayList<>(subscribers); //copy list needed as bugfix for concurrent modification exception
|
||||
for (IUpdateableCityUI sub : subcopy) {
|
||||
sub.processNewCurrentWeatherData(data);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
ArrayList<IUpdateableCityUI> subcopy = new ArrayList<>(subscribers);
|
||||
for (IUpdateableCityUI sub : subcopy) {
|
||||
sub.processNewForecasts(hourlyForecasts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
package org.woheller69.weather.ui.util;
|
||||
/*
|
||||
* Taken from https://github.com/Truiton/AutoSuggestTextViewAPICall
|
||||
* Modified by woheller69
|
||||
*/
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Filter;
|
||||
import android.widget.Filterable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.woheller69.weather.database.City;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AutoSuggestAdapter extends ArrayAdapter<String> implements Filterable {
|
||||
private final List<String> mlistData;
|
||||
private final List<City> mlistCity;
|
||||
|
||||
public AutoSuggestAdapter(@NonNull Context context, int resource) {
|
||||
super(context, resource);
|
||||
mlistData = new ArrayList<>();
|
||||
mlistCity = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void setData(List<String> list, List<City> cityList) {
|
||||
mlistData.clear();
|
||||
mlistCity.clear();
|
||||
mlistData.addAll(list);
|
||||
mlistCity.addAll(cityList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mlistData.size();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getItem(int position) {
|
||||
return mlistData.get(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to Return the full object directly from adapter.
|
||||
*
|
||||
* @param position
|
||||
* @return
|
||||
*/
|
||||
public City getObject(int position) {
|
||||
return mlistCity.get(position);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Filter getFilter() {
|
||||
Filter dataFilter = new Filter() {
|
||||
@Override
|
||||
protected FilterResults performFiltering(CharSequence constraint) {
|
||||
FilterResults filterResults = new FilterResults();
|
||||
if (constraint != null) {
|
||||
filterResults.values = mlistData;
|
||||
filterResults.count = mlistData.size();
|
||||
}
|
||||
return filterResults;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void publishResults(CharSequence constraint, FilterResults results) {
|
||||
if (results != null && (results.count > 0)) {
|
||||
notifyDataSetChanged();
|
||||
} else {
|
||||
notifyDataSetInvalidated();
|
||||
}
|
||||
}
|
||||
};
|
||||
return dataFilter;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package org.woheller69.weather.ui.util;
|
||||
|
||||
/**
|
||||
* Created by Thomas Glaser on 14.05.2017.
|
||||
*/
|
||||
|
||||
public interface MyConsumer<T> {
|
||||
void accept(T t);
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package org.woheller69.weather.ui.util;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.volley.Request;
|
||||
import com.android.volley.RequestQueue;
|
||||
import com.android.volley.Response;
|
||||
import com.android.volley.toolbox.StringRequest;
|
||||
import com.android.volley.toolbox.Volley;
|
||||
|
||||
/**
|
||||
* Created by MG on 04-03-2018.
|
||||
*
|
||||
* Taken from https://github.com/Truiton/AutoSuggestTextViewAPICall
|
||||
* Modified by woheller69
|
||||
*/
|
||||
|
||||
public class geocodingApiCall {
|
||||
private static geocodingApiCall mInstance;
|
||||
private RequestQueue mRequestQueue;
|
||||
private static Context mCtx;
|
||||
|
||||
public geocodingApiCall(Context ctx) {
|
||||
mCtx = ctx.getApplicationContext();
|
||||
mRequestQueue = getRequestQueue();
|
||||
}
|
||||
|
||||
public static synchronized geocodingApiCall getInstance(Context context) {
|
||||
if (mInstance == null) {
|
||||
mInstance = new geocodingApiCall(context);
|
||||
}
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
public RequestQueue getRequestQueue() {
|
||||
if (mRequestQueue == null) {
|
||||
mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
|
||||
}
|
||||
return mRequestQueue;
|
||||
}
|
||||
|
||||
public <T> void addToRequestQueue(Request<T> req) {
|
||||
getRequestQueue().add(req);
|
||||
}
|
||||
|
||||
public static void make(Context ctx, String query, String url, String lang, Response.Listener<String>
|
||||
listener, Response.ErrorListener errorListener) {
|
||||
url = url + query+"&language="+lang;
|
||||
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
|
||||
listener, errorListener);
|
||||
geocodingApiCall.getInstance(ctx).addToRequestQueue(stringRequest);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
package org.woheller69.weather.ui.viewPager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
|
||||
import org.woheller69.weather.database.CityToWatch;
|
||||
import org.woheller69.weather.database.CurrentWeatherData;
|
||||
import org.woheller69.weather.database.HourlyForecast;
|
||||
import org.woheller69.weather.database.SQLiteHelper;
|
||||
import org.woheller69.weather.database.WeekForecast;
|
||||
import org.woheller69.weather.services.UpdateDataService;
|
||||
import org.woheller69.weather.ui.WeatherCityFragment;
|
||||
import org.woheller69.weather.ui.updater.IUpdateableCityUI;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static androidx.core.app.JobIntentService.enqueueWork;
|
||||
import static org.woheller69.weather.services.UpdateDataService.SKIP_UPDATE_INTERVAL;
|
||||
|
||||
/**
|
||||
* Created by thomagglaser on 07.08.2017.
|
||||
*/
|
||||
|
||||
public class WeatherPagerAdapter extends FragmentStateAdapter implements IUpdateableCityUI {
|
||||
|
||||
private Context mContext;
|
||||
|
||||
private SQLiteHelper database;
|
||||
|
||||
private List<CityToWatch> cities;
|
||||
|
||||
|
||||
public WeatherPagerAdapter(Context context, @NonNull FragmentManager supportFragmentManager, @NonNull Lifecycle lifecycle) {
|
||||
super(supportFragmentManager,lifecycle);
|
||||
this.mContext = context;
|
||||
this.database = SQLiteHelper.getInstance(context);
|
||||
|
||||
loadCities();
|
||||
}
|
||||
|
||||
public void loadCities() {
|
||||
this.cities = database.getAllCitiesToWatch();
|
||||
Collections.sort(cities, (o1, o2) -> o1.getRank() - o2.getRank());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public WeatherCityFragment createFragment(int position) {
|
||||
Bundle args = new Bundle();
|
||||
args.putInt("city_id", cities.get(position).getCityId());
|
||||
|
||||
return WeatherCityFragment.newInstance(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return cities.size();
|
||||
}
|
||||
|
||||
public CharSequence getPageTitle(int position) {
|
||||
return cities.get(position).getCityName();
|
||||
}
|
||||
|
||||
public static void refreshSingleData(Context context, Boolean asap, int cityId) {
|
||||
Intent intent = new Intent(context, UpdateDataService.class);
|
||||
intent.setAction(UpdateDataService.UPDATE_SINGLE_ACTION);
|
||||
intent.putExtra(SKIP_UPDATE_INTERVAL, asap);
|
||||
intent.putExtra("cityId",cityId);
|
||||
enqueueWork(context, UpdateDataService.class, 0, intent);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void processNewCurrentWeatherData(CurrentWeatherData data) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNewForecasts(List<HourlyForecast> hourlyForecasts) {
|
||||
//empty because Fragments are subscribers themselves
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNewWeekForecasts(List<WeekForecast> forecasts) {
|
||||
//empty because Fragments are subscribers themselves
|
||||
}
|
||||
|
||||
public int getCityIDForPos(int pos) {
|
||||
CityToWatch city = cities.get(pos);
|
||||
return city.getCityId();
|
||||
}
|
||||
|
||||
public int getPosForCityID(int cityID) {
|
||||
for (int i = 0; i < cities.size(); i++) {
|
||||
CityToWatch city = cities.get(i);
|
||||
if (city.getCityId() == cityID) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package org.woheller69.weather.weather_api;
|
||||
|
||||
/**
|
||||
* This interface class defines a set of methods that guarantee that even the use of multiple APIs
|
||||
* result in the same data bases.
|
||||
*/
|
||||
public abstract class IApiToDatabaseConversion {
|
||||
|
||||
/**
|
||||
* This enum provides a list of all available weather categories and assigns them a numerical
|
||||
* value. Please note that is ordered from best weather (CLEAR_SKY) to worst weather
|
||||
* (THUNDERSTORM).
|
||||
*/
|
||||
public enum WeatherCategories {
|
||||
ERROR(-1),
|
||||
CLEAR_SKY(0),
|
||||
FEW_CLOUDS(1),
|
||||
SCATTERED_CLOUDS(2),
|
||||
OVERCAST_CLOUDS(3),
|
||||
MIST(45),
|
||||
DRIZZLE_RAIN(53),
|
||||
FREEZING_DRIZZLE_RAIN(57),
|
||||
LIGHT_RAIN(61),
|
||||
MODERATE_RAIN(63),
|
||||
HEAVY_RAIN(65),
|
||||
LIGHT_FREEZING_RAIN(66),
|
||||
FREEZING_RAIN(67),
|
||||
LIGHT_SNOW(71),
|
||||
MODERATE_SNOW(73),
|
||||
HEAVY_SNOW(75),
|
||||
LIGHT_SHOWER_RAIN(80),
|
||||
SHOWER_RAIN(81),
|
||||
SHOWER_RAIN_SNOW(84), //only used as icon in week forecasts
|
||||
LIGHT_SHOWER_SNOW(85),
|
||||
SHOWER_SNOW(86),
|
||||
THUNDERSTORM(95),
|
||||
THUNDERSTORM_HAIL(96);
|
||||
|
||||
private int numVal;
|
||||
|
||||
WeatherCategories(int numVal) {
|
||||
this.numVal = numVal;
|
||||
}
|
||||
|
||||
public int getNumVal() {
|
||||
return numVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Different APIs will use different representation for weather conditions / categories.
|
||||
* Internally, they will stored uniformly.
|
||||
*
|
||||
* @param category The category to convert into the internal representation.
|
||||
* @return Returns 10 for clear sky, 20 for (few) clouds, 30 for scattered cloud, 40 for broken
|
||||
* clouds, 50 for shower rain, 60 for rain, 70 for thunderstorm, 80 for snow, 90 for mist.
|
||||
*/
|
||||
public abstract int convertWeatherCategory(String category);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package org.woheller69.weather.weather_api;
|
||||
|
||||
import org.woheller69.weather.database.CurrentWeatherData;
|
||||
import org.woheller69.weather.database.HourlyForecast;
|
||||
import org.woheller69.weather.database.WeekForecast;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This interface defines the frame of the functionality to extractCurrentWeatherData weather information from which
|
||||
* is returned by some API.
|
||||
*/
|
||||
public interface IDataExtractor {
|
||||
|
||||
/**
|
||||
* @param data The data that contains the information to instantiate a CurrentWeatherData
|
||||
* object. In the easiest case this is the (HTTP) response of the One Call API.
|
||||
* @return Returns the extracted information as a CurrentWeatherData instance.
|
||||
*/
|
||||
CurrentWeatherData extractCurrentWeather(String data);
|
||||
|
||||
/**
|
||||
* @param data The data that contains the information to instantiate a Forecast object.
|
||||
* @return Returns the extracted weather forecast information. In case some error occurs, null
|
||||
* will be returned.
|
||||
*/
|
||||
List<WeekForecast> extractWeekForecast(String data);
|
||||
|
||||
/**
|
||||
* @param data The data that contains the information to instantiate a Forecast object.
|
||||
* @return Returns the extracted weather forecast information. In case some error occurs, null
|
||||
* will be returned.
|
||||
*/
|
||||
|
||||
List<HourlyForecast> extractHourlyForecast(String data, int cityID);
|
||||
|
||||
/**
|
||||
* @param data0, data1, data2, data3, data4 contain the information to retrieve the rain for a minute within the next 60min.
|
||||
* @return Returns a string with a rain drop in case of rain or a - in case of no rain
|
||||
*/
|
||||
String extractRain60min(String data0,String data1, String data2, String data3, String data4);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package org.woheller69.weather.weather_api;
|
||||
|
||||
/**
|
||||
* This generic interface is for making an HTTP request to some weather API, process the data and
|
||||
* finally trigger some mechanism to update the UI.
|
||||
*/
|
||||
public interface IHttpRequestForWeatherAPI {
|
||||
|
||||
/**
|
||||
* @param lat The latitude of the city to get the data for.
|
||||
* @param lon The longitude of the city to get the data for.
|
||||
*/
|
||||
void perform(float lat, float lon, int cityId);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package org.woheller69.weather.weather_api;
|
||||
|
||||
import com.android.volley.VolleyError;
|
||||
|
||||
/**
|
||||
* This interface is used for providing methods for processing success and failure scenarios of
|
||||
* HTTP requests. Implementations of this interface can work with any (weather) API.
|
||||
*/
|
||||
public interface IProcessHttpRequest {
|
||||
|
||||
/**
|
||||
* The method that will be executed in case of a successful HTTP request.
|
||||
*
|
||||
* @param response The response of the HTTP request.
|
||||
*/
|
||||
void processSuccessScenario(String response, int cityId);
|
||||
|
||||
/**
|
||||
* This method will be executed in case any error arose while executing the HTTP request.
|
||||
*
|
||||
* @param error The error that occurred while executing the HTTP request.
|
||||
*/
|
||||
void processFailScenario(VolleyError error);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
package org.woheller69.weather.weather_api.open_meteo;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.woheller69.weather.SolarPowerPlant;
|
||||
import org.woheller69.weather.database.CityToWatch;
|
||||
import org.woheller69.weather.database.CurrentWeatherData;
|
||||
import org.woheller69.weather.database.HourlyForecast;
|
||||
import org.woheller69.weather.database.SQLiteHelper;
|
||||
import org.woheller69.weather.database.WeekForecast;
|
||||
import org.woheller69.weather.weather_api.IApiToDatabaseConversion;
|
||||
import org.woheller69.weather.weather_api.IDataExtractor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is a concrete implementation for extracting weather data that was retrieved by
|
||||
* Open-Meteo.
|
||||
*/
|
||||
public class OMDataExtractor implements IDataExtractor {
|
||||
|
||||
private Context context;
|
||||
public OMDataExtractor(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CurrentWeatherData extractCurrentWeather(String data) {
|
||||
try {
|
||||
JSONObject jsonData = new JSONObject(data);
|
||||
CurrentWeatherData weatherData = new CurrentWeatherData();
|
||||
weatherData.setTimestamp(System.currentTimeMillis() / 1000);
|
||||
IApiToDatabaseConversion conversion = new OMToDatabaseConversion();
|
||||
if (jsonData.has("weathercode")) weatherData.setWeatherID(conversion.convertWeatherCategory(jsonData.getString("weathercode")));
|
||||
if (jsonData.has("temperature")) weatherData.setTemperatureCurrent((float) jsonData.getDouble("temperature"));
|
||||
if (jsonData.has("windspeed")) weatherData.setWindSpeed((float) jsonData.getDouble("windspeed"));
|
||||
if (jsonData.has("winddirection")) weatherData.setWindDirection((float) jsonData.getDouble("winddirection"));
|
||||
weatherData.setTimeSunrise(0L);
|
||||
weatherData.setTimeSunset(0L);
|
||||
weatherData.setHumidity(0);
|
||||
weatherData.setPressure(0);
|
||||
weatherData.setCloudiness(0);
|
||||
|
||||
return weatherData;
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<WeekForecast> extractWeekForecast(String data) {
|
||||
try {
|
||||
SharedPreferences prefManager = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
List<WeekForecast> weekforecasts = new ArrayList<>();
|
||||
JSONObject jsonData = new JSONObject(data);
|
||||
JSONArray timeArray = jsonData.getJSONArray("time");
|
||||
JSONArray weathercodeArray = jsonData.has("weathercode") ? jsonData.getJSONArray("weathercode") : null;
|
||||
JSONArray tempMaxArray = jsonData.has("temperature_2m_max") ? jsonData.getJSONArray("temperature_2m_max") : null;
|
||||
JSONArray tempMinArray = jsonData.has("temperature_2m_min") ? jsonData.getJSONArray("temperature_2m_min") : null;
|
||||
JSONArray sunriseArray = jsonData.has("sunrise") ? jsonData.getJSONArray("sunrise") : null;
|
||||
JSONArray sunsetArray = jsonData.has("sunset") ? jsonData.getJSONArray("sunset") : null;
|
||||
JSONArray uvIndexArray = jsonData.has("uv_index_max") ? jsonData.getJSONArray("uv_index_max") : null;
|
||||
JSONArray precipitationArray = jsonData.has("precipitation_sum") ? jsonData.getJSONArray("precipitation_sum") : null;
|
||||
JSONArray windSpeedArray = jsonData.has("windspeed_10m_max") ? jsonData.getJSONArray("windspeed_10m_max") : null;
|
||||
JSONArray snowfallArray = jsonData.has("snowfall_sum") ? jsonData.getJSONArray("snowfall_sum") : null;
|
||||
JSONArray showersArray = jsonData.has("showers_sum") ? jsonData.getJSONArray("showers_sum") : null;
|
||||
JSONArray rainArray = jsonData.has("rain_sum") ? jsonData.getJSONArray("rain_sum") : null;
|
||||
|
||||
IApiToDatabaseConversion conversion = new OMToDatabaseConversion();
|
||||
for (int i = 0; i < timeArray.length(); i++) {
|
||||
WeekForecast weekForecast = new WeekForecast();
|
||||
weekForecast.setTimestamp(System.currentTimeMillis() / 1000);
|
||||
if (!timeArray.isNull(i)) weekForecast.setForecastTime((timeArray.getLong(i)+12*3600)*1000L); //shift to midday
|
||||
if (!weathercodeArray.isNull(i)) weekForecast.setWeatherID(conversion.convertWeatherCategory(weathercodeArray.getString(i)));
|
||||
if (!tempMaxArray.isNull(i)) weekForecast.setMaxTemperature((float) tempMaxArray.getDouble(i));
|
||||
if (!tempMinArray.isNull(i)) weekForecast.setMinTemperature((float) tempMinArray.getDouble(i));
|
||||
if (!sunriseArray.isNull(i)) weekForecast.setTimeSunrise(sunriseArray.getLong(i));
|
||||
if (!sunsetArray.isNull(i)) weekForecast.setTimeSunset(sunsetArray.getLong(i));
|
||||
if (!uvIndexArray.isNull(i)) {
|
||||
weekForecast.setUv_index((float) uvIndexArray.getDouble(i));
|
||||
} else weekForecast.setUv_index(-1);
|
||||
if (!windSpeedArray.isNull(i)) weekForecast.setWind_speed((float) windSpeedArray.getDouble(i));
|
||||
weekforecasts.add(weekForecast);
|
||||
}
|
||||
return weekforecasts;
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IDataExtractor#extractHourlyForecast(String,int)
|
||||
*/
|
||||
@Override
|
||||
public List<HourlyForecast> extractHourlyForecast(String data, int cityID) {
|
||||
try {
|
||||
SharedPreferences prefManager = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
List<HourlyForecast> hourlyForecasts = new ArrayList<>();
|
||||
JSONObject jsonData = new JSONObject(data);
|
||||
JSONArray timeArray = jsonData.getJSONArray("time");
|
||||
JSONArray weathercodeArray = jsonData.has("weathercode") ? jsonData.getJSONArray("weathercode") : 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;
|
||||
|
||||
//TODO get Data for power plant from city to Watch
|
||||
|
||||
SQLiteHelper dbhelper = SQLiteHelper.getInstance(context);
|
||||
CityToWatch city = dbhelper.getCityToWatch(cityID);
|
||||
SolarPowerPlant spp = new SolarPowerPlant(city.getLatitude(), city.getLongitude(), city.getCellsMaxPower(), city.getCellsArea(), city.getCellsEfficiency(),city.getDiffuseEfficiency(), city.getConverterPowerLimit(), city.getConverterEfficiency(), city.getAzimuthAngle(), city.getElevationAngle());
|
||||
|
||||
|
||||
IApiToDatabaseConversion conversion = new OMToDatabaseConversion();
|
||||
for (int i = 0; i < timeArray.length(); i++) {
|
||||
HourlyForecast hourlyForecast = new HourlyForecast();
|
||||
hourlyForecast.setTimestamp(System.currentTimeMillis() / 1000);
|
||||
if (timeArray!=null) hourlyForecast.setForecastTime(timeArray.getLong(i)*1000L);
|
||||
if (weathercodeArray!=null) hourlyForecast.setWeatherID(conversion.convertWeatherCategory(weathercodeArray.getString(i)));
|
||||
if (directRadiationArray!=null) hourlyForecast.setDirectRadiationNormal((float) directRadiationArray.getDouble(i));
|
||||
if (diffuseRadiationArray!=null) 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
|
||||
hourlyForecasts.add(hourlyForecast);
|
||||
}
|
||||
return hourlyForecasts;
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see IDataExtractor#extractRain60min(String, String, String, String, String)
|
||||
*/
|
||||
@Override
|
||||
public String extractRain60min(String data0,String data1, String data2, String data3, String data4) {
|
||||
try {
|
||||
|
||||
String rain = "";
|
||||
JSONObject jsonData0 = new JSONObject(data0);
|
||||
JSONObject jsonData1 = new JSONObject(data1);
|
||||
JSONObject jsonData2 = new JSONObject(data2);
|
||||
JSONObject jsonData3 = new JSONObject(data3);
|
||||
JSONObject jsonData4 = new JSONObject(data4);
|
||||
double rain5min=jsonData0.getDouble("precipitation")+jsonData1.getDouble("precipitation")+jsonData2.getDouble("precipitation")+jsonData3.getDouble("precipitation")+jsonData4.getDouble("precipitation");
|
||||
if (rain5min==0){
|
||||
rain ="\u25a1";
|
||||
} else if (rain5min<2.5){ // very light rain equals <0.5mm/h (2.5 = 5 x 0.5)
|
||||
rain ="\u25a4";
|
||||
}else if (rain5min<12.5){ //light rain equals <2.5mm/h (12.5 = 5 x 2.5)
|
||||
rain ="\u25a6";
|
||||
} else{
|
||||
rain ="\u25a0";
|
||||
}
|
||||
|
||||
return rain;
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param data The data that contains the information to retrieve the ID of the city.
|
||||
* If data for a single city were requested, the response string can be
|
||||
* passed as an argument.
|
||||
* If data for multiple cities were requested, make sure to pass only one item
|
||||
* of the response list at a time!
|
||||
* @return Returns the ID of the city or Integer#MIN_VALUE in case the data is not well-formed
|
||||
* and the information could not be extracted.
|
||||
*/
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package org.woheller69.weather.weather_api.open_meteo;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.woheller69.weather.BuildConfig;
|
||||
import org.woheller69.weather.database.CityToWatch;
|
||||
import org.woheller69.weather.preferences.AppPreferencesManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class OMHttpRequest {
|
||||
|
||||
protected String getUrlForQueryingOMweatherAPI(Context context, float lat, float lon) {
|
||||
AppPreferencesManager prefManager =
|
||||
new AppPreferencesManager(PreferenceManager.getDefaultSharedPreferences(context));
|
||||
SharedPreferences sharedPreferences=PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
return String.format(
|
||||
"%sforecast?latitude=%s&longitude=%s&forecast_days=%s&hourly=diffuse_radiation,direct_normal_irradiance,weathercode&daily=weathercode,temperature_2m_max,temperature_2m_min,sunrise,sunset,uv_index_max,precipitation_sum,windspeed_10m_max¤t_weather=true&windspeed_unit=ms&timeformat=unixtime&timezone=auto",
|
||||
BuildConfig.BASE_URL,
|
||||
lat,
|
||||
lon,
|
||||
sharedPreferences.getInt("pref_number_days",7)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package org.woheller69.weather.weather_api.open_meteo;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.woheller69.weather.http.HttpRequestType;
|
||||
import org.woheller69.weather.http.IHttpRequest;
|
||||
import org.woheller69.weather.http.VolleyHttpRequest;
|
||||
import org.woheller69.weather.weather_api.IHttpRequestForWeatherAPI;
|
||||
|
||||
/**
|
||||
* This class provides the functionality for making and processing HTTP requests to Open-Meteo to retrieve the latest weather data for all stored cities.
|
||||
*/
|
||||
public class OMHttpRequestForWeatherAPI extends OMHttpRequest implements IHttpRequestForWeatherAPI {
|
||||
|
||||
/**
|
||||
* Member variables.
|
||||
*/
|
||||
private Context context;
|
||||
|
||||
/**
|
||||
* @param context The context to use.
|
||||
*/
|
||||
public OMHttpRequestForWeatherAPI(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void perform(float lat, float lon, int cityId) {
|
||||
IHttpRequest httpRequest = new VolleyHttpRequest(context, cityId);
|
||||
final String URL = getUrlForQueryingOMweatherAPI(context, lat, lon);
|
||||
httpRequest.make(URL, HttpRequestType.GET, new ProcessOMweatherAPIRequest(context));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
package org.woheller69.weather.weather_api.open_meteo;
|
||||
|
||||
import org.woheller69.weather.weather_api.IApiToDatabaseConversion;
|
||||
|
||||
public class OMToDatabaseConversion extends IApiToDatabaseConversion {
|
||||
|
||||
@Override
|
||||
public int convertWeatherCategory(String category) {
|
||||
int value = Integer.parseInt(category);
|
||||
if (value == 0) {
|
||||
return WeatherCategories.CLEAR_SKY.getNumVal();
|
||||
} else if (value == 1) {
|
||||
return WeatherCategories.FEW_CLOUDS.getNumVal();
|
||||
} else if (value == 2) {
|
||||
return WeatherCategories.SCATTERED_CLOUDS.getNumVal();
|
||||
} else if (value == 3) {
|
||||
return WeatherCategories.OVERCAST_CLOUDS.getNumVal();
|
||||
} else if (value >= 45 && value <= 48) {
|
||||
return WeatherCategories.MIST.getNumVal();
|
||||
} else if (value >= 50 && value <= 55) {
|
||||
return WeatherCategories.DRIZZLE_RAIN.getNumVal();
|
||||
} else if (value >= 56 && value <= 57) {
|
||||
return WeatherCategories.FREEZING_DRIZZLE_RAIN.getNumVal();
|
||||
} else if (value >= 60 && value <= 61) {
|
||||
return WeatherCategories.LIGHT_RAIN.getNumVal();
|
||||
} else if (value >= 62 && value <=63) {
|
||||
return WeatherCategories.MODERATE_RAIN.getNumVal();
|
||||
} else if (value >= 64 && value <=65) {
|
||||
return WeatherCategories.HEAVY_RAIN.getNumVal();
|
||||
} else if (value == 66) {
|
||||
return WeatherCategories.LIGHT_FREEZING_RAIN.getNumVal();
|
||||
} else if (value == 67) {
|
||||
return WeatherCategories.FREEZING_RAIN.getNumVal();
|
||||
} else if (value == 70 || value == 71 || value == 77) { // 77=snow grain
|
||||
return WeatherCategories.LIGHT_SNOW.getNumVal();
|
||||
} else if (value >= 72 && value <=73) {
|
||||
return WeatherCategories.MODERATE_SNOW.getNumVal();
|
||||
} else if (value >= 74 && value <=75 ) {
|
||||
return WeatherCategories.HEAVY_SNOW.getNumVal();
|
||||
} else if (value == 80 ) {
|
||||
return WeatherCategories.LIGHT_SHOWER_RAIN.getNumVal();
|
||||
} else if (value == 81 || value == 82 ) {
|
||||
return WeatherCategories.SHOWER_RAIN.getNumVal();
|
||||
} else if (value == 85) {
|
||||
return WeatherCategories.LIGHT_SHOWER_SNOW.getNumVal();
|
||||
} else if (value == 86) {
|
||||
return WeatherCategories.SHOWER_SNOW.getNumVal();
|
||||
} else if (value == 95) {
|
||||
return WeatherCategories.THUNDERSTORM.getNumVal();
|
||||
} else if (value == 96 || value == 99) {
|
||||
return WeatherCategories.THUNDERSTORM_HAIL.getNumVal();
|
||||
}
|
||||
// Fallback: ERROR
|
||||
return WeatherCategories.ERROR.getNumVal();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
package org.woheller69.weather.weather_api.open_meteo;
|
||||
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Handler;
|
||||
import android.widget.RemoteViews;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.volley.VolleyError;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.woheller69.weather.R;
|
||||
import org.woheller69.weather.activities.NavigationActivity;
|
||||
import org.woheller69.weather.database.CityToWatch;
|
||||
import org.woheller69.weather.database.CurrentWeatherData;
|
||||
import org.woheller69.weather.database.HourlyForecast;
|
||||
import org.woheller69.weather.database.WeekForecast;
|
||||
import org.woheller69.weather.database.SQLiteHelper;
|
||||
import org.woheller69.weather.ui.updater.ViewUpdater;
|
||||
import org.woheller69.weather.weather_api.IDataExtractor;
|
||||
import org.woheller69.weather.weather_api.IProcessHttpRequest;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
import org.woheller69.weather.weather_api.IApiToDatabaseConversion.WeatherCategories;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This class processes the HTTP requests that are made to the Open-Meteo API requesting the
|
||||
* current weather for all stored cities.
|
||||
*/
|
||||
public class ProcessOMweatherAPIRequest implements IProcessHttpRequest {
|
||||
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
private final String DEBUG_TAG = "process_forecast";
|
||||
|
||||
/**
|
||||
* Member variables
|
||||
*/
|
||||
private Context context;
|
||||
private SQLiteHelper dbHelper;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param context The context of the HTTP request.
|
||||
*/
|
||||
public ProcessOMweatherAPIRequest(Context context) {
|
||||
this.context = context;
|
||||
this.dbHelper = SQLiteHelper.getInstance(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the response to JSON and updates the database. Note that for this method no
|
||||
* UI-related operations are performed.
|
||||
*
|
||||
* @param response The response of the HTTP request.
|
||||
*/
|
||||
@Override
|
||||
public void processSuccessScenario(String response, int cityId) {
|
||||
|
||||
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()){
|
||||
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;
|
||||
}
|
||||
|
||||
//Extract current weather
|
||||
String rain60min=context.getResources().getString(R.string.error_no_rain60min_data);
|
||||
CurrentWeatherData weatherData = extractor.extractCurrentWeather(json.getString("current_weather"));
|
||||
|
||||
if (weatherData == null) {
|
||||
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();
|
||||
} else {
|
||||
weatherData.setCity_id(cityId);
|
||||
weatherData.setRain60min(rain60min);
|
||||
weatherData.setTimeSunrise(weekforecasts.get(0).getTimeSunrise());
|
||||
weatherData.setTimeSunset(weekforecasts.get(0).getTimeSunset());
|
||||
weatherData.setTimeZoneSeconds(json.getInt("utc_offset_seconds"));
|
||||
CurrentWeatherData current = dbHelper.getCurrentWeatherByCityId(cityId);
|
||||
if (current != null && current.getCity_id() == cityId) {
|
||||
dbHelper.updateCurrentWeather(weatherData);
|
||||
} else {
|
||||
dbHelper.addCurrentWeather(weatherData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//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);
|
||||
|
||||
ViewUpdater.updateCurrentWeatherData(weatherData);
|
||||
ViewUpdater.updateWeekForecasts(weekforecasts);
|
||||
ViewUpdater.updateForecasts(hourlyforecasts);
|
||||
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reanalyze weekforecasts and improve weather codes which are not representative for the day
|
||||
* @param weekforecasts
|
||||
* @param hourlyforecasts
|
||||
* @return
|
||||
*/
|
||||
private List<WeekForecast> reanalyzeWeekIDs(List<WeekForecast> weekforecasts, List<HourlyForecast> hourlyforecasts) {
|
||||
|
||||
Map<Integer,Integer> mappingTable = new HashMap<>();
|
||||
mappingTable.put(WeatherCategories.OVERCAST_CLOUDS.getNumVal(),WeatherCategories.SCATTERED_CLOUDS.getNumVal());
|
||||
mappingTable.put(WeatherCategories.MIST.getNumVal(),WeatherCategories.SCATTERED_CLOUDS.getNumVal());
|
||||
mappingTable.put(WeatherCategories.DRIZZLE_RAIN.getNumVal(),WeatherCategories.LIGHT_SHOWER_RAIN.getNumVal());
|
||||
mappingTable.put(WeatherCategories.FREEZING_DRIZZLE_RAIN.getNumVal(),WeatherCategories.LIGHT_SHOWER_RAIN.getNumVal());
|
||||
mappingTable.put(WeatherCategories.LIGHT_RAIN.getNumVal(),WeatherCategories.LIGHT_SHOWER_RAIN.getNumVal());
|
||||
mappingTable.put(WeatherCategories.LIGHT_FREEZING_RAIN.getNumVal(),WeatherCategories.LIGHT_SHOWER_RAIN.getNumVal());
|
||||
mappingTable.put(WeatherCategories.MODERATE_RAIN.getNumVal(),WeatherCategories.SHOWER_RAIN.getNumVal());
|
||||
mappingTable.put(WeatherCategories.HEAVY_RAIN.getNumVal(),WeatherCategories.SHOWER_RAIN.getNumVal());
|
||||
mappingTable.put(WeatherCategories.FREEZING_RAIN.getNumVal(),WeatherCategories.SHOWER_RAIN.getNumVal());
|
||||
mappingTable.put(WeatherCategories.LIGHT_SNOW.getNumVal(),WeatherCategories.LIGHT_SHOWER_SNOW.getNumVal());
|
||||
mappingTable.put(WeatherCategories.MODERATE_SNOW.getNumVal(),WeatherCategories.SHOWER_SNOW.getNumVal());
|
||||
mappingTable.put(WeatherCategories.HEAVY_SNOW.getNumVal(),WeatherCategories.SHOWER_SNOW.getNumVal());
|
||||
|
||||
Map<Integer,Integer> sunTable = new HashMap<>();
|
||||
sunTable.put(WeatherCategories.CLEAR_SKY.getNumVal(), 0);
|
||||
sunTable.put(WeatherCategories.FEW_CLOUDS.getNumVal(), 0);
|
||||
sunTable.put(WeatherCategories.SCATTERED_CLOUDS.getNumVal(), 0);
|
||||
|
||||
for (WeekForecast weekForecast: weekforecasts){
|
||||
Integer ID = weekForecast.getWeatherID();
|
||||
if (mappingTable.containsKey(ID)){
|
||||
int totalCount = 0;
|
||||
int sunCount = 0;
|
||||
long sunrise = weekForecast.getTimeSunrise()*1000L;
|
||||
long sunset = weekForecast.getTimeSunset()*1000L;
|
||||
for (HourlyForecast hourlyForecast: hourlyforecasts){
|
||||
if(hourlyForecast.getForecastTime() >= sunrise && hourlyForecast.getForecastTime() <= sunset){
|
||||
totalCount++;
|
||||
if(sunTable.containsKey(hourlyForecast.getWeatherID())) sunCount++;
|
||||
}
|
||||
}
|
||||
if (totalCount>0 && (float)sunCount/totalCount>0.2f) weekForecast.setWeatherID(mappingTable.get(ID));
|
||||
}
|
||||
}
|
||||
|
||||
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.setPrecipitation(totalEnergy/1000);
|
||||
}
|
||||
|
||||
return weekforecasts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows an error that the data could not be retrieved.
|
||||
*
|
||||
* @param error The error that occurred while executing the HTTP request.
|
||||
*/
|
||||
@Override
|
||||
public void processFailScenario(final VolleyError error) {
|
||||
Handler h = new Handler(this.context.getMainLooper());
|
||||
h.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (NavigationActivity.isVisible) Toast.makeText(context, context.getResources().getString(R.string.error_fetch_forecast), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue