├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ ├── images │ │ ├── marker-icon-2x.png │ │ ├── marker-icon.png │ │ └── marker-shadow.png │ ├── leaflet.css │ ├── leaflet.js │ └── map.html │ ├── ic_launcher-playstore.png │ ├── java │ └── org │ │ └── woheller69 │ │ └── spritpreise │ │ ├── activities │ │ ├── AboutActivity.java │ │ ├── CityGasPricesActivity.java │ │ ├── ManageLocationsActivity.java │ │ ├── NavigationActivity.java │ │ ├── SettingsActivity.java │ │ └── SplashActivity.java │ │ ├── api │ │ ├── IDataExtractor.java │ │ ├── IHttpRequestForStations.java │ │ ├── IProcessHttpRequest.java │ │ └── tankerkoenig │ │ │ ├── TKDataExtractor.java │ │ │ ├── TKHttpRequestForStations.java │ │ │ └── TKProcessHttpRequest.java │ │ ├── database │ │ ├── City.java │ │ ├── CityToWatch.java │ │ ├── SQLiteHelper.java │ │ └── Station.java │ │ ├── dialogs │ │ └── AddLocationDialogOmGeocodingAPI.java │ │ ├── firststart │ │ └── TutorialActivity.java │ │ ├── http │ │ ├── HttpRequestType.java │ │ ├── IHttpRequest.java │ │ └── VolleyHttpRequest.java │ │ ├── preferences │ │ └── AppPreferencesManager.java │ │ ├── services │ │ └── UpdateDataService.java │ │ ├── ui │ │ ├── CityFragment.java │ │ ├── Help │ │ │ └── StringFormatUtils.java │ │ ├── RecycleList │ │ │ ├── CityAdapter.java │ │ │ ├── ItemTouchHelperAdapter.java │ │ │ ├── ItemViewHolder.java │ │ │ ├── OnSwipeDownListener.java │ │ │ ├── RecyclerItemClickListener.java │ │ │ ├── RecyclerOverviewListAdapter.java │ │ │ ├── SimpleItemTouchHelperCallback.java │ │ │ └── StationAdapter.java │ │ ├── updater │ │ │ ├── IUpdateableCityUI.java │ │ │ └── ViewUpdater.java │ │ ├── util │ │ │ ├── AutoSuggestAdapter.java │ │ │ └── geocodingApiCall.java │ │ └── viewPager │ │ │ └── CityPagerAdapter.java │ │ └── widget │ │ └── Widget.java │ └── res │ ├── drawable-hdpi │ └── splash_icon.png │ ├── drawable-mdpi │ └── splash_icon.png │ ├── drawable-nodpi │ └── widget_preview.png │ ├── drawable-xhdpi │ └── splash_icon.png │ ├── drawable-xxhdpi │ └── splash_icon.png │ ├── drawable-xxxhdpi │ └── splash_icon.png │ ├── drawable │ ├── app_icon.xml │ ├── baseline_star_rate_24.xml │ ├── ic_add_location_alt_24px.xml │ ├── ic_arrow_back_24px.xml │ ├── ic_direction_32dp.xml │ ├── ic_edit_location_24px.xml │ ├── ic_highlight_32dp.xml │ ├── ic_info_24px.xml │ ├── ic_launcher_foreground.xml │ ├── ic_launcher_monochrome.xml │ ├── ic_local_gas_station_black_24dp.xml │ ├── ic_local_gas_station_green_24dp.xml │ ├── ic_local_gas_station_red_24dp.xml │ ├── ic_location_32dp.xml │ ├── ic_location_on_white_24dp.xml │ ├── ic_map_24px.xml │ ├── ic_playpause.xml │ ├── ic_rainviewer.xml │ ├── ic_refresh_24px.xml │ ├── ic_settings_24px.xml │ ├── ic_skip_next_24px.xml │ ├── ic_skip_previous_24px.xml │ ├── ic_south_24px.xml │ ├── ic_widget_gas_station_24dp.xml │ ├── map_back.png │ ├── recycle_view_line_divider.xml │ ├── rounded_green.xml │ ├── rounded_grey.xml │ ├── rounded_highlight.xml │ ├── rounded_lightred.xml │ ├── rounded_orange.xml │ ├── rounded_red.xml │ ├── rounded_transparent.xml │ ├── rounded_yellow.xml │ ├── splash_screen.xml │ ├── star_primary_dark_24.xml │ ├── star_yellow_24.xml │ ├── transparent_no_margin.xml │ └── widget_background.xml │ ├── layout-land │ └── card_stations.xml │ ├── layout │ ├── about.xml │ ├── activity_about.xml │ ├── activity_city_gas_prices.xml │ ├── activity_manage_locations.xml │ ├── activity_settings.xml │ ├── activity_tutorial.xml │ ├── app_bar_main.xml │ ├── card_details.xml │ ├── card_overview.xml │ ├── card_stations.xml │ ├── card_week.xml │ ├── city_fragment.xml │ ├── content_manage_locations.xml │ ├── dialog_add_location.xml │ ├── list_group.xml │ ├── list_item.xml │ ├── list_item_autocomplete.xml │ ├── list_item_city_list.xml │ ├── list_item_station.xml │ ├── list_item_week_forecast.xml │ ├── menu_refresh_action_view.xml │ ├── menu_update_location_view.xml │ ├── nav_header_main.xml │ ├── toolbar.xml │ ├── tutorial_slide1.xml │ ├── tutorial_slide2.xml │ ├── tutorial_slide3.xml │ ├── tutorial_slide4.xml │ └── widget.xml │ ├── menu │ ├── activity_city_gas_prices.xml │ └── activity_main_drawer.xml │ ├── mipmap-anydpi-v26 │ └── ic_launcher.xml │ ├── mipmap-hdpi │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ └── ic_launcher_round.png │ ├── values-de │ └── strings.xml │ ├── values-night │ └── colors.xml │ ├── values │ ├── arrays.xml │ ├── colors.xml │ ├── dimens.xml │ ├── drawables.xml │ ├── ic_launcher_background.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ ├── pref_general.xml │ └── widget_info.xml ├── build.gradle ├── fastlane └── metadata │ └── android │ ├── de-DE │ ├── changelogs │ │ ├── 10.txt │ │ ├── 11.txt │ │ ├── 12.txt │ │ ├── 13.txt │ │ ├── 14.txt │ │ ├── 15.txt │ │ ├── 16.txt │ │ ├── 17.txt │ │ ├── 18.txt │ │ ├── 19.txt │ │ ├── 20.txt │ │ ├── 21.txt │ │ ├── 22.txt │ │ ├── 23.txt │ │ ├── 24.txt │ │ ├── 25.txt │ │ ├── 26.txt │ │ ├── 27.txt │ │ └── 28.txt │ ├── full_description.txt │ ├── short_description.txt │ └── title.txt │ └── en-US │ ├── changelogs │ ├── 10.txt │ ├── 11.txt │ ├── 12.txt │ ├── 13.txt │ ├── 14.txt │ ├── 15.txt │ ├── 16.txt │ ├── 17.txt │ ├── 18.txt │ ├── 19.txt │ ├── 20.txt │ ├── 21.txt │ ├── 22.txt │ ├── 23.txt │ ├── 24.txt │ ├── 25.txt │ ├── 26.txt │ ├── 27.txt │ └── 28.txt │ ├── full_description.txt │ ├── images │ ├── icon.png │ └── phoneScreenshots │ │ ├── 1.png │ │ └── 2.png │ ├── short_description.txt │ └── title.txt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **How to Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Environment (please complete the following information):** 27 | - App version: [e.g. v1.6] 28 | - Android version: [e.g. 10] 29 | - Device: [e.g. Google Pixel 4] 30 | 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #Ignore Android studion files 2 | /.idea 3 | 4 | # Built application files 5 | /build/ 6 | 7 | # Ignore the Database config file 8 | app/src/main/res/raw/ormlite_config.txt 9 | 10 | # Crashlytics configuations 11 | com_crashlytics_export_strings.xml 12 | 13 | # Local configuration file (sdk path, etc) 14 | local.properties 15 | 16 | # Gradle generated files 17 | .gradle/ 18 | 19 | # Signing files 20 | .signing/ 21 | 22 | # User-specific configurations 23 | .idea/libraries/ 24 | .idea/workspace.xml 25 | .idea/tasks.xml 26 | .idea/.name 27 | .idea/compiler.xml 28 | .idea/copyright/profiles_settings.xml 29 | .idea/encodings.xml 30 | .idea/misc.xml 31 | .idea/modules.xml 32 | .idea/scopes/scope_settings.xml 33 | .idea/vcs.xml 34 | *.iml 35 | 36 | # OS-specific files 37 | .DS_Store 38 | .DS_Store? 39 | ._* 40 | .Spotlight-V100 41 | .Trashes 42 | ehthumbs.db 43 | Thumbs.db 44 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | 5 | compileSdk 35 6 | android.buildFeatures.buildConfig true 7 | namespace 'org.woheller69.spritpreise' 8 | 9 | defaultConfig { 10 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 11 | applicationId "org.woheller69.spritpreise" 12 | minSdk 26 13 | targetSdk 35 14 | versionCode 28 15 | versionName "2.8" 16 | 17 | buildConfigField "String", "DEFAULT_API_KEY", "\"${project.findProperty("prop1")}\"" 18 | buildConfigField "String", "UNPATCHED_API_KEY", "\"OTgxMjBmODAtNzYwNy0zOTQxLTI3NGUtMDE5MGI0NDg2NmZh\"" 19 | buildConfigField "String", "BASE_URL", "\"https://creativecommons.tankerkoenig.de/json/\"" 20 | buildConfigField "String", "GITHUB_URL","\"https://github.com/woheller69/spritpreise/\"" 21 | } 22 | 23 | buildTypes { 24 | release { 25 | minifyEnabled false 26 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 27 | } 28 | } 29 | } 30 | 31 | dependencies { 32 | implementation fileTree(dir: 'libs', include: ['*.jar']) 33 | implementation 'androidx.preference:preference:1.2.1' 34 | implementation 'androidx.appcompat:appcompat:1.7.0' 35 | implementation 'com.google.android.material:material:1.12.0' 36 | implementation 'androidx.legacy:legacy-support-v4:1.0.0' 37 | implementation 'com.android.volley:volley:1.2.1' 38 | implementation 'androidx.recyclerview:recyclerview:1.4.0' 39 | implementation 'androidx.cardview:cardview:1.0.0' 40 | implementation 'com.github.woheller69:preferences:1.2' 41 | implementation 'org.osmdroid:osmdroid-android:6.1.18' 42 | } 43 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/yonjuni/Android/Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 20 | 21 | 25 | 26 | 27 | 28 | 29 | 30 | 36 | 41 | 42 | 47 | 50 | 51 | 52 | 57 | 60 | 61 | 62 | 65 | 66 | 70 | 71 | 73 | 74 | 75 | 76 | 77 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /app/src/main/assets/images/marker-icon-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/spritpreise/1e752d920f569b6b66069015c9ede2709880b7da/app/src/main/assets/images/marker-icon-2x.png -------------------------------------------------------------------------------- /app/src/main/assets/images/marker-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/spritpreise/1e752d920f569b6b66069015c9ede2709880b7da/app/src/main/assets/images/marker-icon.png -------------------------------------------------------------------------------- /app/src/main/assets/images/marker-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/spritpreise/1e752d920f569b6b66069015c9ede2709880b7da/app/src/main/assets/images/marker-shadow.png -------------------------------------------------------------------------------- /app/src/main/assets/map.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/spritpreise/1e752d920f569b6b66069015c9ede2709880b7da/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/activities/AboutActivity.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.activities; 2 | 3 | import android.os.Build; 4 | import android.os.Bundle; 5 | import android.text.method.LinkMovementMethod; 6 | import android.view.WindowInsetsController; 7 | import android.widget.TextView; 8 | 9 | import org.woheller69.spritpreise.BuildConfig; 10 | import org.woheller69.spritpreise.R; 11 | 12 | /** 13 | * Created by yonjuni on 15.06.16. 14 | */ 15 | public class AboutActivity extends NavigationActivity { 16 | 17 | protected void onCreate(Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | setContentView(R.layout.activity_about); 20 | 21 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { 22 | getWindow().getInsetsController().setSystemBarsAppearance( 23 | WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS, 24 | WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS 25 | ); 26 | } 27 | 28 | ((TextView) findViewById(R.id.githubURL)).setMovementMethod(LinkMovementMethod.getInstance()); 29 | ((TextView) findViewById(R.id.tankerkoenigURL)).setMovementMethod(LinkMovementMethod.getInstance()); 30 | ((TextView) findViewById(R.id.textFieldVersionName)).setText(BuildConfig.VERSION_NAME); 31 | 32 | } 33 | 34 | @Override 35 | protected int getNavigationDrawerID() { 36 | return R.id.nav_about; 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/activities/SettingsActivity.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.activities; 2 | 3 | 4 | import android.Manifest; 5 | import android.content.Intent; 6 | import android.content.SharedPreferences; 7 | import android.content.pm.PackageManager; 8 | import android.net.Uri; 9 | import android.os.Build; 10 | import android.os.Bundle; 11 | import android.view.WindowInsetsController; 12 | 13 | import androidx.annotation.NonNull; 14 | import androidx.annotation.RequiresApi; 15 | import androidx.appcompat.app.AlertDialog; 16 | import androidx.core.app.ActivityCompat; 17 | import androidx.preference.Preference; 18 | import androidx.preference.PreferenceFragmentCompat; 19 | import androidx.preference.PreferenceManager; 20 | 21 | import org.woheller69.spritpreise.R; 22 | import org.woheller69.spritpreise.database.SQLiteHelper; 23 | 24 | import static java.lang.Boolean.TRUE; 25 | 26 | public class SettingsActivity extends NavigationActivity implements SharedPreferences.OnSharedPreferenceChangeListener{ 27 | 28 | @Override 29 | protected void onRestart() { 30 | super.onRestart(); 31 | 32 | recreate(); 33 | } 34 | 35 | @RequiresApi(api = Build.VERSION_CODES.Q) 36 | @Override 37 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 38 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 39 | if (requestCode == 1) { 40 | if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 41 | if ((ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) && (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)) { 42 | if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED) { 43 | 44 | AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this); 45 | String message = getString(R.string.rationale_background_location); 46 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 47 | message = message + ": \n\n >> " + getPackageManager().getBackgroundPermissionOptionLabel().toString() +" <<"; 48 | } 49 | 50 | alertDialogBuilder.setMessage(message); 51 | alertDialogBuilder.setPositiveButton(getString(R.string.dialog_OK_button), (dialog, which) -> requestBackgroundLocation()); 52 | alertDialogBuilder.setNegativeButton(getString(R.string.dialog_NO_button), (dialog, which) -> { 53 | }); 54 | 55 | AlertDialog alertDialog = alertDialogBuilder.create(); 56 | alertDialog.show(); 57 | 58 | } 59 | } 60 | } 61 | } 62 | } 63 | 64 | @RequiresApi(api = Build.VERSION_CODES.Q) 65 | private void requestBackgroundLocation() { 66 | ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION}, 2); 67 | } 68 | 69 | @Override 70 | protected void onResume() { 71 | super.onResume(); 72 | PreferenceManager.getDefaultSharedPreferences(this.getApplicationContext()).registerOnSharedPreferenceChangeListener(this); 73 | 74 | } 75 | 76 | @Override 77 | protected void onCreate(Bundle savedInstanceState) { 78 | super.onCreate(savedInstanceState); 79 | 80 | setContentView(R.layout.activity_settings); 81 | 82 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { 83 | getWindow().getInsetsController().setSystemBarsAppearance( 84 | WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS, 85 | WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS 86 | ); 87 | } 88 | 89 | } 90 | 91 | @Override 92 | protected int getNavigationDrawerID() { 93 | return R.id.nav_settings; 94 | } 95 | 96 | @RequiresApi(api = Build.VERSION_CODES.Q) 97 | @Override 98 | public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) { 99 | 100 | if (s.equals("pref_GPS")) { 101 | if (sharedPreferences.getBoolean("pref_GPS", false) == TRUE) { 102 | if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { 103 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 104 | ActivityCompat.requestPermissions(this, 105 | new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, 1); 106 | } else { 107 | ActivityCompat.requestPermissions(this, 108 | new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION}, 1); 109 | } 110 | 111 | } 112 | } 113 | } else if (s.equals("pref_type")||s.equals("pref_sort")||s.equals(("pref_searchRadius"))){ 114 | SQLiteHelper database = SQLiteHelper.getInstance(getApplicationContext().getApplicationContext()); 115 | database.deleteAllStations(); 116 | } 117 | } 118 | 119 | /** 120 | * This fragment shows general preferences only. It is used when the 121 | * activity is showing a two-pane settings UI. 122 | */ 123 | public static class GeneralPreferenceFragment extends PreferenceFragmentCompat { 124 | @Override 125 | public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 126 | setPreferencesFromResource(R.xml.pref_general, rootKey); 127 | Preference button = getPreferenceManager().findPreference("register"); 128 | if (button != null){ 129 | button.setOnPreferenceClickListener(preference -> { 130 | startActivity(new Intent(Intent.ACTION_VIEW, 131 | Uri.parse("https://creativecommons.tankerkoenig.de"))); 132 | return true; 133 | }); 134 | } 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/activities/SplashActivity.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.activities; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import androidx.appcompat.app.AppCompatActivity; 6 | import androidx.preference.PreferenceManager; 7 | 8 | import org.woheller69.spritpreise.firststart.TutorialActivity; 9 | import org.woheller69.spritpreise.preferences.AppPreferencesManager; 10 | 11 | /** 12 | * Created by yonjuni on 24.10.16. 13 | */ 14 | 15 | public class SplashActivity extends AppCompatActivity { 16 | private AppPreferencesManager prefManager; 17 | @Override 18 | protected void onCreate(Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | 21 | prefManager = new AppPreferencesManager(PreferenceManager.getDefaultSharedPreferences(this)); 22 | if (prefManager.isFirstTimeLaunch(this)){ //First time got to TutorialActivity 23 | Intent mainIntent = new Intent(SplashActivity.this, TutorialActivity.class); 24 | SplashActivity.this.startActivity(mainIntent); 25 | } else { //otherwise directly start CityGasPricesActivity 26 | Intent mainIntent = new Intent(SplashActivity.this, CityGasPricesActivity.class); 27 | SplashActivity.this.startActivity(mainIntent); 28 | } 29 | SplashActivity.this.finish(); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/api/IDataExtractor.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.api; 2 | 3 | import android.content.Context; 4 | 5 | import org.woheller69.spritpreise.database.Station; 6 | 7 | /** 8 | * This interface defines the frame of the functionality to extract information which 9 | * is returned by some API. 10 | */ 11 | public interface IDataExtractor { 12 | 13 | 14 | boolean wasCityFound(String data); 15 | 16 | 17 | Station extractStation(String data, Context context); 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/api/IHttpRequestForStations.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.api; 2 | 3 | /** 4 | * This generic interface is for making an HTTP request to some API, process the data and 5 | * finally trigger some mechanism to update the UI. 6 | */ 7 | public interface IHttpRequestForStations { 8 | 9 | /** 10 | * @param lat The latitude of the city to get the data for. 11 | * @param lon The longitude of the city to get the data for. 12 | */ 13 | void perform(float lat, float lon, int cityId); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/api/IProcessHttpRequest.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.api; 2 | 3 | import com.android.volley.VolleyError; 4 | 5 | public interface IProcessHttpRequest { 6 | 7 | /** 8 | * The method that will be executed in case of a successful HTTP request. 9 | * 10 | * @param response The response of the HTTP request. 11 | */ 12 | void processSuccessScenario(String response, int cityId); 13 | 14 | /** 15 | * This method will be executed in case any error arose while executing the HTTP request. 16 | * 17 | * @param error The error that occurred while executing the HTTP request. 18 | */ 19 | void processFailScenario(VolleyError error); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/api/tankerkoenig/TKDataExtractor.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.api.tankerkoenig; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import androidx.preference.PreferenceManager; 6 | 7 | import org.json.JSONException; 8 | import org.json.JSONObject; 9 | import org.woheller69.spritpreise.database.Station; 10 | import org.woheller69.spritpreise.api.IDataExtractor; 11 | import java.util.Date; 12 | import java.util.TimeZone; 13 | 14 | 15 | public class TKDataExtractor implements IDataExtractor { 16 | 17 | @Override 18 | public boolean wasCityFound(String data) { 19 | try { 20 | JSONObject json = new JSONObject(data); 21 | return json.has("ok") && (json.getBoolean("ok")); 22 | } catch (JSONException e) { 23 | e.printStackTrace(); 24 | return false; 25 | } 26 | } 27 | 28 | @Override 29 | public Station extractStation(String data, Context context) { 30 | try { 31 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); 32 | Station station = new Station(); 33 | station.setTimestamp((long) ((System.currentTimeMillis())/ 1000)); 34 | 35 | JSONObject json = new JSONObject(data); 36 | 37 | if (json.has("diesel") && !json.isNull("diesel")) station.setDiesel(json.getDouble("diesel")); 38 | if (json.has("e5") && !json.isNull("e5")) station.setE5( json.getDouble("e5")); 39 | if (json.has("e10") && !json.isNull("e10")) station.setE10( json.getDouble("e10")); 40 | 41 | if (json.has("price")) { 42 | if (!json.isNull("price")) { 43 | switch (sharedPreferences.getString("pref_type", "all")) { 44 | case "diesel": 45 | station.setDiesel(json.getDouble("price")); 46 | break; 47 | case "e5": 48 | station.setE5(json.getDouble("price")); 49 | break; 50 | case "e10": 51 | station.setE10(json.getDouble("price")); 52 | break; 53 | } 54 | } else return null; 55 | } 56 | if (sharedPreferences.getBoolean("pref_hide_closed",false) && !json.getBoolean("isOpen")) return null; 57 | station.setOpen(json.getBoolean("isOpen")); 58 | station.setBrand(json.getString("brand")); 59 | if (json.getString("brand").equals("")) station.setBrand(json.getString("name")); 60 | station.setName(json.getString("name")); 61 | station.setAddress1(json.getString("street")+" "+json.getString("houseNumber")); 62 | station.setAddress2(formatPostCode(json.getString("postCode")) +" "+json.getString("place")); 63 | station.setDistance(json.getDouble("dist")); 64 | station.setLatitude(json.getDouble("lat")); 65 | station.setLongitude(json.getDouble("lng")); 66 | station.setUuid(json.getString("id")); 67 | 68 | return station; 69 | } catch (JSONException e) { 70 | e.printStackTrace(); 71 | } 72 | return null; 73 | } 74 | 75 | public static String formatPostCode(String string) { 76 | // Adds a leading 0 to the postcode string if needed 77 | return string.length() == 4 ? ("0" + string) : string; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/api/tankerkoenig/TKHttpRequestForStations.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.api.tankerkoenig; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | 6 | import androidx.preference.PreferenceManager; 7 | 8 | import org.woheller69.spritpreise.BuildConfig; 9 | import org.woheller69.spritpreise.http.HttpRequestType; 10 | import org.woheller69.spritpreise.http.IHttpRequest; 11 | import org.woheller69.spritpreise.http.VolleyHttpRequest; 12 | import org.woheller69.spritpreise.preferences.AppPreferencesManager; 13 | import org.woheller69.spritpreise.api.IHttpRequestForStations; 14 | 15 | /** 16 | * This class provides the functionality for making and processing HTTP requests to 17 | * Tankerkönig to retrieve the latest gas prices for all stored cities. 18 | */ 19 | public class TKHttpRequestForStations implements IHttpRequestForStations { 20 | 21 | /** 22 | * Member variables. 23 | */ 24 | private Context context; 25 | 26 | /** 27 | * @param context The context to use. 28 | */ 29 | public TKHttpRequestForStations(Context context) { 30 | this.context = context; 31 | } 32 | 33 | /** 34 | * @see IHttpRequestForStations#perform(float, float,int) 35 | */ 36 | @Override 37 | public void perform(float lat, float lon, int cityId) { 38 | IHttpRequest httpRequest = new VolleyHttpRequest(context, cityId); 39 | final String URL = getUrlForQueryingStations(context, lat, lon); 40 | httpRequest.make(URL, HttpRequestType.GET, new TKProcessHttpRequest(context)); 41 | } 42 | 43 | protected String getUrlForQueryingStations(Context context, float lat, float lon) { 44 | AppPreferencesManager prefManager = 45 | new AppPreferencesManager(PreferenceManager.getDefaultSharedPreferences(context)); 46 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); 47 | return String.format( 48 | "%slist.php?lat=%s&lng=%s&rad=%s&sort=%s&type=%s&apikey=%s", 49 | BuildConfig.BASE_URL, 50 | lat, 51 | lon, 52 | sharedPreferences.getString("pref_searchRadius","3"), 53 | sharedPreferences.getBoolean("pref_sort",false)&&(!sharedPreferences.getString("pref_type","all").equals("all")) ? "price" : "dist", 54 | sharedPreferences.getString("pref_type","all"), 55 | prefManager.getTKApiKey(context) 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/api/tankerkoenig/TKProcessHttpRequest.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.api.tankerkoenig; 2 | 3 | import android.appwidget.AppWidgetManager; 4 | import android.content.ComponentName; 5 | import android.content.Context; 6 | import android.os.Handler; 7 | 8 | import android.util.Log; 9 | import android.widget.RemoteViews; 10 | import android.widget.Toast; 11 | 12 | import com.android.volley.VolleyError; 13 | 14 | import org.json.JSONArray; 15 | import org.json.JSONException; 16 | import org.json.JSONObject; 17 | import org.woheller69.spritpreise.R; 18 | import org.woheller69.spritpreise.activities.NavigationActivity; 19 | import org.woheller69.spritpreise.database.CityToWatch; 20 | import org.woheller69.spritpreise.database.Station; 21 | import org.woheller69.spritpreise.database.SQLiteHelper; 22 | import org.woheller69.spritpreise.ui.updater.ViewUpdater; 23 | import org.woheller69.spritpreise.api.IDataExtractor; 24 | import org.woheller69.spritpreise.api.IProcessHttpRequest; 25 | import org.woheller69.spritpreise.widget.Widget; 26 | import static org.woheller69.spritpreise.database.SQLiteHelper.getWidgetCityID; 27 | 28 | import java.util.ArrayList; 29 | import java.util.List; 30 | 31 | /** 32 | * This class processes the HTTP requests that are made to the Tankerkönig API requesting the 33 | * current prices for all stored cities. 34 | */ 35 | public class TKProcessHttpRequest implements IProcessHttpRequest { 36 | 37 | /** 38 | * Member variables 39 | */ 40 | private Context context; 41 | private SQLiteHelper dbHelper; 42 | 43 | /** 44 | * Constructor. 45 | * 46 | * @param context The context of the HTTP request. 47 | */ 48 | public TKProcessHttpRequest(Context context) { 49 | this.context = context; 50 | this.dbHelper = SQLiteHelper.getInstance(context); 51 | } 52 | 53 | /** 54 | * Converts the response to JSON and updates the database. Note that for this method no 55 | * UI-related operations are performed. 56 | * 57 | * @param response The response of the HTTP request. 58 | */ 59 | @Override 60 | public void processSuccessScenario(String response, int cityId) { 61 | IDataExtractor extractor = new TKDataExtractor(); 62 | dbHelper.deleteStationsByCityId(cityId); //start with empty stations list 63 | List stations = new ArrayList<>(); 64 | if (extractor.wasCityFound(response)) { 65 | try { 66 | JSONObject json = new JSONObject(response); 67 | JSONArray list = json.getJSONArray("stations"); 68 | for (int i = 0; i < list.length(); i++) { 69 | String currentItem = list.get(i).toString(); 70 | Log.d("Extract", currentItem); 71 | Station station = extractor.extractStation(currentItem,context); 72 | if (station != null) { // Could retrieve all data, so add it to the list 73 | station.setCity_id(cityId); 74 | // add it to the database 75 | dbHelper.addStation(station); 76 | stations.add(station); 77 | } 78 | } 79 | } catch (JSONException e) { 80 | e.printStackTrace(); 81 | } 82 | } else { 83 | final String ERROR_MSG = context.getResources().getString(R.string.error_fetch_stations); 84 | if (NavigationActivity.isVisible) 85 | Toast.makeText(context, ERROR_MSG, Toast.LENGTH_LONG).show(); 86 | } 87 | ViewUpdater.updateStations(stations,cityId); 88 | possiblyUpdateWidgets(cityId, stations); 89 | } 90 | 91 | /** 92 | * Shows an error that the data could not be retrieved. 93 | * 94 | * @param error The error that occurred while executing the HTTP request. 95 | */ 96 | @Override 97 | public void processFailScenario(final VolleyError error) { 98 | Log.d("Error", String.valueOf(error)); 99 | Handler h = new Handler(this.context.getMainLooper()); 100 | h.post(new Runnable() { 101 | @Override 102 | public void run() { 103 | if (NavigationActivity.isVisible) Toast.makeText(context, context.getResources().getString(R.string.error_fetch_stations), Toast.LENGTH_LONG).show(); 104 | } 105 | }); 106 | } 107 | 108 | private void possiblyUpdateWidgets(int cityID, List stations) { 109 | //search for widgets with same city ID 110 | int widgetCityID = getWidgetCityID(context); 111 | 112 | int[] widgetIDs = AppWidgetManager.getInstance(context).getAppWidgetIds(new ComponentName(context, Widget.class)); 113 | 114 | for (int widgetID : widgetIDs) { 115 | //check if city ID is same 116 | if (cityID == widgetCityID) { 117 | //perform update for the widget 118 | 119 | RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget); 120 | AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); 121 | 122 | CityToWatch city = dbHelper.getCityToWatch(cityID); 123 | 124 | Widget.updateView(context, appWidgetManager, views, widgetID, city, stations); 125 | appWidgetManager.updateAppWidget(widgetID, views); 126 | } 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/database/City.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.database; 2 | 3 | 4 | /** 5 | * Created by yonjuni on 04.01.17. 6 | * data object for city 7 | *

8 | * Structure taken from the old orm package from previous versions of this app. 9 | */ 10 | 11 | public class City { 12 | 13 | private int cityId; 14 | private String cityName; 15 | private String countryCode; 16 | private float lon; 17 | private float lat; 18 | 19 | public City() { 20 | } 21 | 22 | public int getCityId() { 23 | return cityId; 24 | } 25 | 26 | public void setCityId(int cityId) { 27 | this.cityId = cityId; 28 | } 29 | 30 | public String getCityName() { 31 | return cityName; 32 | } 33 | 34 | public void setCityName(String cityName) { 35 | this.cityName = cityName; 36 | } 37 | 38 | public String getCountryCode() { 39 | return countryCode; 40 | } 41 | 42 | public void setCountryCode(String countryCode) { 43 | this.countryCode = countryCode; 44 | } 45 | 46 | public void setLatitude(float latitude) { 47 | lat = latitude; 48 | } 49 | 50 | public float getLatitude() { 51 | return lat; 52 | } 53 | 54 | public float getLongitude() { 55 | return lon; 56 | } 57 | 58 | public void setLongitude(float lon) { 59 | this.lon = lon; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/database/CityToWatch.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.database; 2 | 3 | /** 4 | * This class is the database model for the cities to watch. 'Cities to watch' means the locations 5 | * for which a user would like to see the gas prices. 6 | */ 7 | public class CityToWatch { 8 | 9 | private int id; 10 | private int cityId; 11 | private String cityName; 12 | private float lon; 13 | private float lat; 14 | private int rank; 15 | 16 | public CityToWatch() { 17 | } 18 | 19 | public CityToWatch(int rank, int id, int cityId, float lon, float lat, String cityName) { 20 | this.rank = rank; 21 | this.lon = lon; 22 | this.lat = lat; 23 | this.id = id; 24 | this.cityId = cityId; 25 | this.cityName = cityName; 26 | } 27 | 28 | public int getId() { 29 | return id; 30 | } 31 | 32 | public void setId(int id) { 33 | this.id = id; 34 | } 35 | 36 | public int getCityId() { 37 | return cityId; 38 | } 39 | 40 | public void setCityId(int cityId) { 41 | this.cityId = cityId; 42 | } 43 | 44 | public String getCityName() { 45 | return cityName; 46 | } 47 | 48 | public void setCityName(String cityName) { 49 | this.cityName = cityName; 50 | } 51 | 52 | public int getRank() { 53 | return rank; 54 | } 55 | 56 | public void setRank(int rank) { 57 | this.rank = rank; 58 | } 59 | 60 | public void setLongitude(float lon) { this.lon = lon; } 61 | 62 | public float getLongitude() { return lon; } 63 | 64 | public float getLatitude() { return lat; } 65 | 66 | public void setLatitude(float lat) { this.lat = lat; } 67 | } -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/database/Station.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.database; 2 | 3 | /** 4 | * This class is the database model for the stations. 5 | */ 6 | public class Station { 7 | 8 | private int id; 9 | private int city_id; 10 | private long timestamp; 11 | private double diesel; 12 | private double e5; 13 | private double e10; 14 | private boolean isOpen; 15 | private String brand; 16 | private String name; 17 | private String address1; 18 | private String address2; 19 | private double distance; 20 | private double latitude; 21 | private double longitude; 22 | private String uuid; 23 | 24 | 25 | public Station() { 26 | } 27 | 28 | public Station(int id, int city_id, long timestamp, double diesel, double e5, double e10, boolean isOpen, String brand, String name, String address1, String address2, double distance, double latitude, double longitude, String uuid) { 29 | this.id = id; 30 | this.city_id = city_id; 31 | this.timestamp = timestamp; 32 | this.diesel = diesel; 33 | this.e5 = e5; 34 | this.e10 = e10; 35 | this.isOpen = isOpen; 36 | this.brand = brand; 37 | this.name = name; 38 | this.address1 = address1; 39 | this.address2 = address2; 40 | this.distance = distance; 41 | this.latitude = latitude; 42 | this.longitude = longitude; 43 | this.uuid = uuid; 44 | } 45 | 46 | public int getId() { 47 | return id; 48 | } 49 | 50 | public void setId(int id) { 51 | this.id = id; 52 | } 53 | 54 | public int getCity_id() { 55 | return city_id; 56 | } 57 | 58 | public void setCity_id(int city_id) { 59 | this.city_id = city_id; 60 | } 61 | 62 | public long getTimestamp() { 63 | return timestamp; 64 | } 65 | 66 | public void setTimestamp(long timestamp) { 67 | this.timestamp = timestamp; 68 | } 69 | 70 | public double getDiesel() { 71 | return diesel; 72 | } 73 | 74 | public void setDiesel(double diesel) { 75 | this.diesel = diesel; 76 | } 77 | 78 | public double getE5() { 79 | return e5; 80 | } 81 | 82 | public void setE5(double e5) { 83 | this.e5 = e5; 84 | } 85 | 86 | public double getE10() { 87 | return e10; 88 | } 89 | 90 | public void setE10(double e10) { 91 | this.e10 = e10; 92 | } 93 | 94 | public boolean isOpen() { 95 | return isOpen; 96 | } 97 | 98 | public void setOpen(boolean open) { 99 | isOpen = open; 100 | } 101 | 102 | public String getBrand() { 103 | return brand; 104 | } 105 | 106 | public void setBrand(String brand) { 107 | this.brand = brand; 108 | } 109 | 110 | public String getName() { 111 | return name; 112 | } 113 | 114 | public void setName(String name) { 115 | this.name = name; 116 | } 117 | 118 | public String getAddress1() { 119 | return address1; 120 | } 121 | 122 | public void setAddress1(String address1) { 123 | this.address1 = address1; 124 | } 125 | 126 | public String getAddress2() { 127 | return address2; 128 | } 129 | 130 | public void setAddress2(String address2) { 131 | this.address2 = address2; 132 | } 133 | 134 | public double getDistance() { 135 | return distance; 136 | } 137 | 138 | public void setDistance(double distance) { 139 | this.distance = distance; 140 | } 141 | 142 | public double getLatitude() { 143 | return latitude; 144 | } 145 | 146 | public void setLatitude(double latitude) { 147 | this.latitude = latitude; 148 | } 149 | 150 | public double getLongitude() { 151 | return longitude; 152 | } 153 | 154 | public void setLongitude(double longitude) { 155 | this.longitude = longitude; 156 | } 157 | 158 | public String getUuid() { 159 | return uuid; 160 | } 161 | 162 | public void setUuid(String uuid) { 163 | this.uuid = uuid; 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/http/HttpRequestType.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.http; 2 | 3 | /** 4 | * A list of all the possible HTTP request types (there are more, for sure, but for this project 5 | * the four below are definitely sufficient). 6 | */ 7 | public enum HttpRequestType { 8 | POST, 9 | GET, 10 | PUT, 11 | DELETE 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/http/IHttpRequest.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.http; 2 | 3 | import org.woheller69.spritpreise.api.IProcessHttpRequest; 4 | 5 | /** 6 | * This interface defines the template for making HTTP request. Furthermore, it provides a generic 7 | * way for handling the responses. 8 | */ 9 | public interface IHttpRequest { 10 | 11 | /** 12 | * Makes an HTTP request and processes the response. 13 | * 14 | * @param URL The target of the HTTP request. 15 | * @param method Which method to use for the HTTP request (e.g. GET or POST) 16 | * @param requestProcessor This object with its implemented methods processSuccessScenario and 17 | * processFailScenario defines how to handle the response in the success 18 | * and error case respectively. 19 | */ 20 | void make(final String URL, HttpRequestType method, IProcessHttpRequest requestProcessor); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/http/VolleyHttpRequest.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.http; 2 | 3 | import android.content.Context; 4 | import com.android.volley.AuthFailureError; 5 | import com.android.volley.Request; 6 | import com.android.volley.RequestQueue; 7 | import com.android.volley.Response; 8 | import com.android.volley.VolleyError; 9 | import com.android.volley.toolbox.StringRequest; 10 | import com.android.volley.toolbox.Volley; 11 | 12 | import org.woheller69.spritpreise.BuildConfig; 13 | import org.woheller69.spritpreise.api.IProcessHttpRequest; 14 | 15 | import java.util.HashMap; 16 | import java.util.Map; 17 | 18 | 19 | /** 20 | * This class implements the IHttpRequest interface. It provides HTTP requests by using Volley. 21 | * See: https://developer.android.com/training/volley/simple.html 22 | */ 23 | public class VolleyHttpRequest implements IHttpRequest { 24 | 25 | private Context context; 26 | private int cityId; 27 | 28 | /** 29 | * Constructor. 30 | * 31 | * @param context Volley needs a context "for creating the cache dir". 32 | * @see Volley#newRequestQueue(Context) 33 | */ 34 | public VolleyHttpRequest(Context context, int cityId) { 35 | this.context = context; 36 | this.cityId = cityId; 37 | } 38 | 39 | /** 40 | * @see IHttpRequest#make(String, HttpRequestType, IProcessHttpRequest) 41 | */ 42 | @Override 43 | public void make(String URL, HttpRequestType method, final IProcessHttpRequest requestProcessor) { 44 | RequestQueue queue = Volley.newRequestQueue(context); 45 | 46 | // Set the request method 47 | int requestMethod; 48 | switch (method) { 49 | case POST: 50 | requestMethod = Request.Method.POST; 51 | break; 52 | case GET: 53 | requestMethod = Request.Method.GET; 54 | break; 55 | case PUT: 56 | requestMethod = Request.Method.PUT; 57 | break; 58 | case DELETE: 59 | requestMethod = Request.Method.DELETE; 60 | break; 61 | default: 62 | requestMethod = Request.Method.GET; 63 | } 64 | 65 | // Execute the request and handle the response 66 | StringRequest stringRequest = new StringRequest(requestMethod, URL, 67 | new Response.Listener() { 68 | @Override 69 | public void onResponse(String response) { 70 | requestProcessor.processSuccessScenario(response, cityId); 71 | } 72 | }, 73 | new Response.ErrorListener() { 74 | @Override 75 | public void onErrorResponse(VolleyError error) { 76 | requestProcessor.processFailScenario(error); 77 | } 78 | } 79 | ) { 80 | @Override 81 | public Map getHeaders() { //from https://stackoverflow.com/questions/17049473/how-to-set-custom-header-in-volley-request 82 | Map params = new HashMap(); 83 | params.put("User-Agent", BuildConfig.APPLICATION_ID + "/" + BuildConfig.VERSION_NAME); 84 | return params; 85 | } 86 | }; 87 | queue.add(stringRequest); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/preferences/AppPreferencesManager.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.preferences; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.os.Handler; 6 | import android.os.Looper; 7 | import android.widget.Toast; 8 | 9 | import org.woheller69.spritpreise.BuildConfig; 10 | import org.woheller69.spritpreise.R; 11 | import static org.woheller69.preferences.Utils.getKey; 12 | import androidx.preference.PreferenceManager; 13 | 14 | /** 15 | * This class provides access and methods for relevant preferences. 16 | */ 17 | public class AppPreferencesManager { 18 | 19 | 20 | /** 21 | * Member variables 22 | */ 23 | SharedPreferences preferences; 24 | 25 | /** 26 | * Constructor. 27 | * 28 | * @param preferences Source for the preferences to use. 29 | */ 30 | public AppPreferencesManager(SharedPreferences preferences) { 31 | this.preferences = preferences; 32 | } 33 | 34 | public boolean isFirstTimeLaunch(Context context) { 35 | boolean result = preferences.getInt("versionCode",0)==0; //true if versionCode not yet initialized 36 | if (isApiKeyMissing()) return true; //show Tutorial at every launch until API key is set 37 | else return result; 38 | } 39 | 40 | public boolean isApiKeyMissing() { 41 | return (preferences.getString("API_key_value", "").equals("") && BuildConfig.DEFAULT_API_KEY.equals(BuildConfig.UNPATCHED_API_KEY)); 42 | } 43 | 44 | 45 | public String getTKApiKey(Context context){ 46 | String prefValue = preferences.getString("API_key_value", ""); 47 | if (prefValue.length()==36) return prefValue; // if a valid key has been entered use it 48 | else if (BuildConfig.DEFAULT_API_KEY.equals(BuildConfig.UNPATCHED_API_KEY)){ // no key entered and build config not patched when compiling 49 | new Handler(Looper.getMainLooper()).post(() -> Toast.makeText(context, context.getResources().getString(R.string.settings_title_API_key), Toast.LENGTH_LONG).show()); 50 | return ""; 51 | } else { 52 | return getKey(BuildConfig.DEFAULT_API_KEY); 53 | } 54 | } 55 | 56 | public boolean showStarDialog(Context context) { 57 | int versionCode = preferences.getInt("versionCode",BuildConfig.VERSION_CODE); 58 | boolean askForStar=preferences.getBoolean("askForStar",true); 59 | 60 | 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 61 | SharedPreferences.Editor editor = preferences.edit(); 62 | editor.putInt("versionCode", BuildConfig.VERSION_CODE); 63 | editor.apply(); 64 | return true; 65 | } else { 66 | SharedPreferences.Editor editor = preferences.edit(); 67 | editor.putInt("versionCode", BuildConfig.VERSION_CODE); 68 | editor.apply(); 69 | return false; 70 | } 71 | } 72 | 73 | public void setAskForStar(boolean askForStar){ 74 | SharedPreferences.Editor editor = preferences.edit(); 75 | editor.putBoolean("askForStar", askForStar); 76 | editor.apply(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/services/UpdateDataService.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.services; 2 | 3 | import android.content.Intent; 4 | import android.content.SharedPreferences; 5 | import android.os.Handler; 6 | import androidx.preference.PreferenceManager; 7 | import androidx.core.app.JobIntentService; 8 | import android.widget.Toast; 9 | 10 | import org.woheller69.spritpreise.BuildConfig; 11 | import org.woheller69.spritpreise.R; 12 | import org.woheller69.spritpreise.activities.NavigationActivity; 13 | import org.woheller69.spritpreise.database.CityToWatch; 14 | import org.woheller69.spritpreise.database.Station; 15 | import org.woheller69.spritpreise.database.SQLiteHelper; 16 | import org.woheller69.spritpreise.api.IHttpRequestForStations; 17 | import org.woheller69.spritpreise.api.tankerkoenig.TKHttpRequestForStations; 18 | 19 | import java.io.IOException; 20 | import java.net.InetAddress; 21 | import java.net.URL; 22 | import java.util.List; 23 | import java.util.concurrent.ExecutionException; 24 | import java.util.concurrent.Executors; 25 | import java.util.concurrent.Future; 26 | import java.util.concurrent.TimeUnit; 27 | import java.util.concurrent.TimeoutException; 28 | 29 | /** 30 | * This class provides the functionality to fetch price data for a given city as a background 31 | * task. 32 | */ 33 | 34 | public class UpdateDataService extends JobIntentService { 35 | 36 | public static final String UPDATE_SINGLE_ACTION = "org.woheller69.spritpreise.services.UpdateDataService.UPDATE_SINGLE_ACTION"; 37 | public static final String SKIP_UPDATE_INTERVAL = "skipUpdateInterval"; 38 | private static final long MIN_UPDATE_INTERVAL=20; 39 | 40 | private SQLiteHelper dbHelper; 41 | private SharedPreferences prefManager; 42 | 43 | /** 44 | * Constructor. 45 | */ 46 | public UpdateDataService() { 47 | super(); 48 | } 49 | 50 | @Override 51 | public void onCreate() { 52 | super.onCreate(); 53 | dbHelper = SQLiteHelper.getInstance(getApplicationContext()); 54 | prefManager = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); 55 | } 56 | 57 | @Override 58 | protected void onHandleWork(Intent intent) { 59 | if (!isOnline(2000)) { 60 | Handler h = new Handler(getApplicationContext().getMainLooper()); 61 | h.post(new Runnable() { 62 | @Override 63 | public void run() { 64 | if (NavigationActivity.isVisible) Toast.makeText(getApplicationContext(), getResources().getString(R.string.error_no_internet), Toast.LENGTH_LONG).show(); 65 | } 66 | }); 67 | return; 68 | } 69 | 70 | if (intent != null) { 71 | if (UPDATE_SINGLE_ACTION.equals(intent.getAction())) handleUpdateSingle(intent); 72 | } 73 | } 74 | 75 | 76 | 77 | private void handleUpdateSingle(Intent intent) { 78 | int cityId = intent.getIntExtra("cityId",-1); 79 | CityToWatch city = dbHelper.getCityToWatch(cityId); 80 | handleUpdateStationsAction(intent, cityId, city.getLatitude(), city.getLongitude()); 81 | } 82 | 83 | private void handleUpdateStationsAction(Intent intent, int cityId, float lat, float lon) { 84 | boolean skipUpdateInterval = intent.getBooleanExtra(SKIP_UPDATE_INTERVAL, false); 85 | 86 | long timestamp = 0; 87 | long systemTime = System.currentTimeMillis() / 1000; 88 | long updateInterval = (long) (Float.parseFloat(prefManager.getString("pref_updateInterval", "15")) * 60); 89 | 90 | List stations = dbHelper.getStationsByCityId(cityId); 91 | if (stations.size() > 0) { // check timestamp of stations 92 | timestamp = stations.get(0).getTimestamp(); 93 | } 94 | 95 | if (skipUpdateInterval) { 96 | // check timestamp of the current stations 97 | if ((timestamp+MIN_UPDATE_INTERVAL-systemTime)>0) skipUpdateInterval=false; //even if skipUpdateInterval is true, never update if less than MIN_UPDATE_INTERVAL s 98 | } 99 | 100 | // Update if update forced or if a certain time has passed 101 | if (skipUpdateInterval || timestamp + updateInterval - systemTime <= 0) { 102 | 103 | 104 | IHttpRequestForStations stationsRequest = new TKHttpRequestForStations(getApplicationContext()); 105 | stationsRequest.perform(lat, lon, cityId); 106 | 107 | } 108 | } 109 | 110 | private boolean isOnline(int timeOut) { //https://stackoverflow.com/questions/9570237/android-check-internet-connection 111 | InetAddress inetAddress = null; 112 | try { 113 | Future future = Executors.newSingleThreadExecutor().submit(() -> { 114 | try { 115 | URL url = new URL(BuildConfig.BASE_URL); 116 | return InetAddress.getByName(url.getHost()); 117 | } catch ( IOException e) { 118 | return null; 119 | } 120 | }); 121 | inetAddress = future.get(timeOut, TimeUnit.MILLISECONDS); 122 | future.cancel(true); 123 | } catch (InterruptedException | ExecutionException | TimeoutException e) { 124 | } 125 | return inetAddress!=null && !inetAddress.toString().isEmpty(); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/ui/CityFragment.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.ui; 2 | 3 | import static org.woheller69.spritpreise.ui.RecycleList.CityAdapter.DETAILS; 4 | import static org.woheller69.spritpreise.ui.RecycleList.CityAdapter.OVERVIEW; 5 | import static org.woheller69.spritpreise.ui.RecycleList.CityAdapter.STATIONS; 6 | 7 | import android.annotation.SuppressLint; 8 | import android.content.Context; 9 | import android.os.Bundle; 10 | 11 | import androidx.annotation.NonNull; 12 | import androidx.annotation.Nullable; 13 | import androidx.fragment.app.Fragment; 14 | import androidx.recyclerview.widget.LinearLayoutManager; 15 | import androidx.recyclerview.widget.RecyclerView; 16 | 17 | import android.view.LayoutInflater; 18 | 19 | import android.view.View; 20 | import android.view.ViewGroup; 21 | 22 | import org.woheller69.spritpreise.R; 23 | import org.woheller69.spritpreise.activities.CityGasPricesActivity; 24 | import org.woheller69.spritpreise.database.Station; 25 | import org.woheller69.spritpreise.ui.RecycleList.CityAdapter; 26 | import org.woheller69.spritpreise.ui.RecycleList.OnSwipeDownListener; 27 | import org.woheller69.spritpreise.ui.updater.IUpdateableCityUI; 28 | import org.woheller69.spritpreise.ui.updater.ViewUpdater; 29 | import org.woheller69.spritpreise.ui.viewPager.CityPagerAdapter; 30 | 31 | import java.util.List; 32 | 33 | //Fragment with the viewholders for a location 34 | public class CityFragment extends Fragment implements IUpdateableCityUI { 35 | 36 | private int mCityId = -1; 37 | private static final int[] mDataSetTypes = {STATIONS}; //Before: {OVERVIEW, DETAILS, STATIONS} OVERVIEW and DETAILS unused at the moment. 38 | 39 | private CityAdapter mAdapter; 40 | 41 | private RecyclerView recyclerView; 42 | 43 | public static CityFragment newInstance(Bundle args) 44 | { 45 | CityFragment cityFragment = new CityFragment(); 46 | cityFragment.setArguments(args); 47 | return cityFragment; 48 | } 49 | 50 | public void setAdapter(CityAdapter adapter) { 51 | mAdapter = adapter; 52 | 53 | if (recyclerView != null) { 54 | recyclerView.setAdapter(mAdapter); 55 | recyclerView.setFocusable(false); 56 | } 57 | } 58 | 59 | public void loadData() { 60 | 61 | mAdapter = new CityAdapter(mCityId, mDataSetTypes, getContext()); 62 | setAdapter(mAdapter); 63 | } 64 | 65 | 66 | @Override 67 | public void onResume() { 68 | loadData(); 69 | super.onResume(); 70 | } 71 | 72 | @Override 73 | public void onAttach(@NonNull Context context) { 74 | super.onAttach(context); 75 | 76 | ViewUpdater.addSubscriber(this); 77 | } 78 | 79 | @Override 80 | public void onDetach() { 81 | ViewUpdater.removeSubscriber(this); 82 | 83 | super.onDetach(); 84 | } 85 | 86 | @Override 87 | public void onPause() { 88 | mAdapter.removeMyPositionListenerGPS(); 89 | super.onPause(); 90 | } 91 | 92 | @SuppressLint("ClickableViewAccessibility") 93 | @Override 94 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 95 | final View v = inflater.inflate(R.layout.city_fragment, container, false); 96 | 97 | recyclerView = v.findViewById(R.id.CityRecyclerView); 98 | recyclerView.setLayoutManager(new LinearLayoutManager(getContext()){ 99 | public boolean canScrollVertically() { //Make parent recyclerview not scrollable (not needed in this app) and scroll stations instead 100 | return false; 101 | } 102 | }); 103 | 104 | 105 | Bundle args = getArguments(); 106 | mCityId = args.getInt("city_id"); 107 | 108 | return v; 109 | } 110 | 111 | @Override 112 | public void processUpdateStations(List stations, int cityID) { 113 | 114 | if (mAdapter != null && mCityId==cityID) { 115 | mAdapter.updateStationsData(stations); 116 | } 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/ui/Help/StringFormatUtils.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.ui.Help; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.text.SpannableString; 6 | import android.text.Spanned; 7 | import android.text.style.ForegroundColorSpan; 8 | import android.text.style.SuperscriptSpan; 9 | import android.text.style.TextAppearanceSpan; 10 | 11 | import androidx.preference.PreferenceManager; 12 | import java.math.RoundingMode; 13 | import java.text.DateFormat; 14 | import java.text.DecimalFormat; 15 | import java.text.SimpleDateFormat; 16 | import java.util.Locale; 17 | import java.util.TimeZone; 18 | 19 | import static java.lang.Boolean.TRUE; 20 | 21 | import org.woheller69.spritpreise.R; 22 | 23 | public final class StringFormatUtils { 24 | 25 | private static final DecimalFormat decimalFormat = new DecimalFormat("0.0"); 26 | private static final DecimalFormat intFormat = new DecimalFormat("0"); 27 | 28 | public static SpannableString formatPrice(Context context, String prefix, Double price, String suffix){ 29 | DecimalFormat format = (DecimalFormat) DecimalFormat.getInstance(Locale.ENGLISH); 30 | format.applyPattern("0.000"); 31 | String pricestring = format.format(price); 32 | SpannableString priceformat = new SpannableString(prefix + pricestring + suffix); 33 | priceformat.setSpan(new TextAppearanceSpan(context, android.R.style.TextAppearance_Small), priceformat.length()-3, priceformat.length()-2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 34 | priceformat.setSpan(new SuperscriptSpan(), priceformat.length()-3, priceformat.length()-2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 35 | priceformat.setSpan(new ForegroundColorSpan(context.getColor(R.color.colorPrimaryDark)), 0, priceformat.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 36 | return priceformat; 37 | } 38 | 39 | public static String formatDecimal(float decimal) { 40 | decimalFormat.setRoundingMode(RoundingMode.HALF_UP); 41 | return removeMinusIfZerosOnly(decimalFormat.format(decimal)); 42 | } 43 | 44 | public static String formatInt(float decimal) { 45 | intFormat.setRoundingMode(RoundingMode.HALF_UP); 46 | return removeMinusIfZerosOnly(intFormat.format(decimal)); 47 | } 48 | 49 | public static String formatInt(float decimal, String appendix) { 50 | return String.format("%s\u200a%s", removeMinusIfZerosOnly(formatInt(decimal)), appendix); //\u200a adds tiny space 51 | } 52 | 53 | public static String formatDecimal(float decimal, String appendix) { 54 | return String.format("%s\u200a%s", removeMinusIfZerosOnly(formatDecimal(decimal)), appendix); 55 | } 56 | 57 | public static String formatTimeWithoutZone(Context context, long time) { 58 | SharedPreferences sharedPreferences= PreferenceManager.getDefaultSharedPreferences(context); 59 | SimpleDateFormat tf; 60 | java.text.DateFormat df = java.text.DateFormat.getDateInstance(DateFormat.SHORT); 61 | df.setTimeZone(TimeZone.getTimeZone("GMT")); 62 | if (android.text.format.DateFormat.is24HourFormat(context) || sharedPreferences.getBoolean("pref_TimeFormat", true)==TRUE){ 63 | tf = new SimpleDateFormat("HH:mm", Locale.getDefault()); 64 | tf.setTimeZone(TimeZone.getTimeZone("GMT")); 65 | }else { 66 | tf = new SimpleDateFormat("hh:mm aa", Locale.getDefault()); 67 | tf.setTimeZone(TimeZone.getTimeZone("GMT")); 68 | } 69 | return df.format(time)+" "+tf.format(time); 70 | } 71 | 72 | 73 | 74 | public static String removeMinusIfZerosOnly(String string){ 75 | // It removes (replaces with "") the minus sign if it's followed by 0-n characters of "0.00000...", 76 | // so this will work for any similar result such as "-0", "-0." or "-0.000000000" 77 | // https://newbedev.com/negative-sign-in-case-of-zero-in-java 78 | return string.replaceAll("^-(?=0(\\.0*)?$)", ""); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/ui/RecycleList/ItemTouchHelperAdapter.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.ui.RecycleList; 2 | 3 | /** 4 | * This interface defines the functionality that can be bound to touch events. 5 | * For the most part it has been taken from 6 | * https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-b9456d2b1aaf#.hmhbe8sku 7 | * as of 2016-08-03 8 | */ 9 | public interface ItemTouchHelperAdapter { 10 | 11 | /** 12 | * This method removes an item from an adapter at the specified positin. 13 | * 14 | * @param position The position of the item to remove. 15 | */ 16 | void onItemDismiss(int position); 17 | 18 | /** 19 | * This method is required to remove items from the list that is used to display the data 20 | * whenever an item is deleted by swiping. 21 | * 22 | * @param fromPosition The from position. 23 | * @param toPosition The to position. 24 | */ 25 | void onItemMove(int fromPosition, int toPosition); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/ui/RecycleList/ItemViewHolder.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.ui.RecycleList; 2 | 3 | import androidx.recyclerview.widget.RecyclerView; 4 | import android.view.View; 5 | import android.widget.TextView; 6 | 7 | import org.woheller69.spritpreise.R; 8 | 9 | /** 10 | * This class holds instances of items that are to be displayed in the list. 11 | * The idea of this class has been taken from 12 | * https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-b9456d2b1aaf#.hmhbe8sku 13 | * as of 2016-08-03. Parts of the code were copied from that source. 14 | */ 15 | public class ItemViewHolder extends RecyclerView.ViewHolder { 16 | 17 | /** 18 | * Member variables 19 | */ 20 | private TextView tvInformation; 21 | 22 | 23 | /** 24 | * Constructor. 25 | * 26 | * @param itemView The view that contains the fields that are to be set for each list item. 27 | */ 28 | public ItemViewHolder(View itemView) { 29 | super(itemView); 30 | tvInformation = (TextView) itemView.findViewById(R.id.city_overview_list_item_text); 31 | } 32 | 33 | /** 34 | * @return Returns the TextView of the item. 35 | */ 36 | public TextView getTvInformation() { 37 | return tvInformation; 38 | } 39 | 40 | 41 | } -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/ui/RecycleList/OnSwipeDownListener.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.ui.RecycleList; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.view.GestureDetector; 6 | import android.view.MotionEvent; 7 | import android.view.View; 8 | 9 | public class OnSwipeDownListener implements View.OnTouchListener { 10 | 11 | private final GestureDetector gestureDetector; 12 | 13 | public OnSwipeDownListener(Context context) { 14 | gestureDetector = new GestureDetector(context, new GestureListener()); 15 | } 16 | 17 | @SuppressLint("ClickableViewAccessibility") 18 | public boolean onTouch(final View view, final MotionEvent motionEvent) { 19 | return gestureDetector.onTouchEvent(motionEvent); 20 | } 21 | 22 | private final class GestureListener extends GestureDetector.SimpleOnGestureListener { 23 | 24 | private static final int SWIPE_THRESHOLD = 120; 25 | private static final int SWIPE_VELOCITY_THRESHOLD = 120; 26 | 27 | @Override 28 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 29 | boolean result = false; 30 | try { 31 | float diffY = e2.getY() - e1.getY(); 32 | if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) { 33 | if (diffY > 0) { 34 | onSwipeDown(); 35 | } 36 | } 37 | } catch (Exception exception) { 38 | exception.printStackTrace(); 39 | } 40 | return result; 41 | } 42 | } 43 | 44 | public void onSwipeDown() { } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/ui/RecycleList/RecyclerItemClickListener.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.ui.RecycleList; 2 | 3 | import android.content.Context; 4 | import androidx.recyclerview.widget.RecyclerView; 5 | import android.view.GestureDetector; 6 | import android.view.MotionEvent; 7 | import android.view.View; 8 | 9 | /** 10 | * Provides the functionality to detect (long) touch events on RecyclerView items. 11 | * The code has been taken from 12 | * http://stackoverflow.com/questions/24471109/recyclerview-onclick (answer of H. Azizkhani) as of 13 | * 2016-08-04. 14 | */ 15 | 16 | public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener { 17 | private OnItemClickListener mListener; 18 | 19 | public interface OnItemClickListener { 20 | void onItemClick(View view, int position); 21 | 22 | void onLongItemClick(View view, int position); 23 | 24 | } 25 | 26 | private GestureDetector mGestureDetector; 27 | 28 | public RecyclerItemClickListener(Context context, final RecyclerView recyclerView, OnItemClickListener listener) { 29 | mListener = listener; 30 | mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { 31 | 32 | @Override 33 | public boolean onSingleTapUp(MotionEvent e) { 34 | return true; 35 | } 36 | 37 | @Override 38 | public void onLongPress(MotionEvent e) { 39 | View child = recyclerView.findChildViewUnder(e.getX(), e.getY()); 40 | if (child != null && mListener != null) { 41 | mListener.onLongItemClick(child, recyclerView.getChildAdapterPosition(child)); 42 | } 43 | } 44 | 45 | }); 46 | } 47 | 48 | @Override 49 | public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) { 50 | View childView = view.findChildViewUnder(e.getX(), e.getY()); 51 | if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) { 52 | mListener.onItemClick(childView, view.getChildAdapterPosition(childView)); 53 | return true; 54 | } 55 | return false; 56 | } 57 | 58 | @Override 59 | public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { 60 | 61 | } 62 | 63 | @Override 64 | public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { 65 | } 66 | } -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/ui/RecycleList/RecyclerOverviewListAdapter.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.ui.RecycleList; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.recyclerview.widget.RecyclerView; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | 10 | import org.woheller69.spritpreise.R; 11 | import org.woheller69.spritpreise.database.CityToWatch; 12 | import org.woheller69.spritpreise.database.SQLiteHelper; 13 | 14 | import java.util.Collections; 15 | import java.util.List; 16 | 17 | /** 18 | * This is the adapter for the RecyclerList that is to be used for the overview of added locations. 19 | * For the most part, it has been taken from 20 | * https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-b9456d2b1aaf#.hmhbe8sku 21 | * as of 2016-08-03 22 | */ 23 | public class RecyclerOverviewListAdapter extends RecyclerView.Adapter implements ItemTouchHelperAdapter { 24 | 25 | /** 26 | * Member variables 27 | */ 28 | private Context context; 29 | private final List cities; 30 | 31 | SQLiteHelper database; 32 | 33 | 34 | /** 35 | * Constructor. 36 | */ 37 | public RecyclerOverviewListAdapter(Context context, List cities) { 38 | this.context = context; 39 | this.cities = cities; 40 | this.database = SQLiteHelper.getInstance(context); 41 | } 42 | 43 | 44 | /** 45 | * @see RecyclerView.Adapter#onCreateViewHolder(ViewGroup, int) 46 | * Returns the template for a list item. 47 | */ 48 | @Override 49 | public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 50 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_city_list, parent, false); 51 | return new ItemViewHolder(view); 52 | } 53 | 54 | /** 55 | * @see RecyclerView.Adapter#onBindViewHolder(RecyclerView.ViewHolder, int) 56 | * Sets the content of items. 57 | */ 58 | @Override 59 | public void onBindViewHolder(ItemViewHolder holder, int position) { 60 | holder.getTvInformation().setText(cities.get(position).getCityName()); 61 | } 62 | 63 | /** 64 | * @see RecyclerView.Adapter#getItemCount() 65 | */ 66 | @Override 67 | public int getItemCount() { 68 | return cities.size(); 69 | } 70 | 71 | /** 72 | * @see ItemTouchHelperAdapter#onItemDismiss(int) 73 | * Removes an item from the list. 74 | */ 75 | @Override 76 | public void onItemDismiss(int position) { 77 | 78 | CityToWatch city = cities.get(position); 79 | database.deleteCityToWatch(city); 80 | cities.remove(position); 81 | notifyItemRemoved(position); 82 | } 83 | 84 | /** 85 | * @see ItemTouchHelperAdapter#onItemMove(int, int) 86 | */ 87 | @Override 88 | public void onItemMove(int fromPosition, int toPosition) { 89 | // For updating the database records 90 | CityToWatch fromCityToWatch = cities.get(fromPosition); 91 | int fromRank = fromCityToWatch.getRank(); 92 | CityToWatch toCityToWatch = cities.get(toPosition); 93 | int toRank = toCityToWatch.getRank(); 94 | 95 | fromCityToWatch.setRank(toRank); 96 | toCityToWatch.setRank(fromRank); 97 | database.updateCityToWatch(fromCityToWatch); 98 | database.updateCityToWatch(toCityToWatch); 99 | Collections.swap(cities, fromPosition, toPosition); 100 | notifyItemMoved(fromPosition, toPosition); 101 | 102 | } 103 | 104 | public String getCityName(int position){ 105 | CityToWatch cityToWatch = cities.get(position); 106 | return cityToWatch.getCityName(); 107 | } 108 | 109 | public void renameCity(int position, String s) { 110 | CityToWatch cityToWatch = cities.get(position); 111 | cityToWatch.setCityName(s); 112 | database.updateCityToWatch(cityToWatch); 113 | notifyItemChanged(position); 114 | } 115 | } -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/ui/RecycleList/SimpleItemTouchHelperCallback.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.ui.RecycleList; 2 | 3 | import androidx.recyclerview.widget.RecyclerView; 4 | import androidx.recyclerview.widget.ItemTouchHelper; 5 | 6 | /** 7 | * To use the ItemTouchHelper we need to create an ItemTouchHelper.Callback which this class is. 8 | * For the most part it has been taken from 9 | * https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-b9456d2b1aaf#.hmhbe8sku 10 | * as of 2016-08-03 11 | */ 12 | public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback { 13 | 14 | private final ItemTouchHelperAdapter adapter; 15 | 16 | /** 17 | * Constructor. 18 | * 19 | * @param adapter The adapter to bind the ItemTouchHelper to. 20 | */ 21 | public SimpleItemTouchHelperCallback(ItemTouchHelperAdapter adapter) { 22 | this.adapter = adapter; 23 | } 24 | 25 | /** 26 | * @see ItemTouchHelper.Callback#isLongPressDragEnabled() 27 | * As it is not supported, false will be returned. 28 | */ 29 | @Override 30 | public boolean isLongPressDragEnabled() { 31 | return true; 32 | } 33 | 34 | /** 35 | * @see ItemTouchHelper.Callback#isItemViewSwipeEnabled() 36 | * As this feature is supported, true will be returned. 37 | */ 38 | @Override 39 | public boolean isItemViewSwipeEnabled() { 40 | return true; 41 | } 42 | 43 | /** 44 | * @see androidx.recyclerview.widget.ItemTouchHelper.Callback#getMovementFlags(RecyclerView, RecyclerView.ViewHolder) 45 | * Sets the swipe flags for start and end. 46 | */ 47 | @Override 48 | public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { 49 | int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; 50 | int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END; 51 | return makeMovementFlags(dragFlags, swipeFlags); 52 | } 53 | 54 | /** 55 | * @see androidx.recyclerview.widget.ItemTouchHelper.Callback#onMove(RecyclerView, RecyclerView.ViewHolder, RecyclerView.ViewHolder) 56 | */ 57 | @Override 58 | public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { 59 | adapter.onItemMove(viewHolder.getBindingAdapterPosition(), target.getBindingAdapterPosition()); 60 | return true; 61 | } 62 | 63 | /** 64 | * @see androidx.recyclerview.widget.ItemTouchHelper.Callback#onSwiped(RecyclerView.ViewHolder, int) 65 | * On swipe, the corresponding element is removed from the list. 66 | */ 67 | @Override 68 | public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { 69 | adapter.onItemDismiss(viewHolder.getBindingAdapterPosition()); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/ui/RecycleList/StationAdapter.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.ui.RecycleList; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.annotation.NonNull; 6 | import androidx.core.content.res.ResourcesCompat; 7 | import androidx.preference.PreferenceManager; 8 | import androidx.recyclerview.widget.RecyclerView; 9 | 10 | import android.content.SharedPreferences; 11 | import android.view.LayoutInflater; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import android.widget.ImageView; 15 | import android.widget.TextView; 16 | 17 | import org.woheller69.spritpreise.R; 18 | import org.woheller69.spritpreise.database.Station; 19 | import org.woheller69.spritpreise.ui.Help.StringFormatUtils; 20 | 21 | import java.time.Instant; 22 | import java.util.List; 23 | import java.util.TimeZone; 24 | 25 | //** 26 | // * Created by yonjuni on 02.01.17. 27 | // * Adapter for the horizontal listView for course of the day. 28 | // */import java.util.List; 29 | 30 | public class StationAdapter extends RecyclerView.Adapter { 31 | 32 | private final List stationList; 33 | private final Context context; 34 | private int selected = -1; 35 | 36 | //Adapter for Stations recycler view 37 | StationAdapter(List stationList, Context context) { 38 | this.context = context; 39 | this.stationList = stationList; 40 | } 41 | 42 | 43 | @NonNull 44 | @Override 45 | public StationViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 46 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_station, parent, false); 47 | return new StationViewHolder(view); 48 | } 49 | 50 | @Override 51 | public void onBindViewHolder(@NonNull StationViewHolder holder, int position) { 52 | SharedPreferences prefManager = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); 53 | 54 | 55 | if (prefManager.getBoolean("prefBrands", false)) { //if preferred brands are defined 56 | String[] brands = prefManager.getString("prefBrandsString", "").split(","); //read comma separated list 57 | for (String brand : brands) { 58 | if (stationList.get(position).getBrand().toLowerCase().contains(brand.toLowerCase().trim())) { 59 | holder.fav.setVisibility(View.VISIBLE); 60 | break; 61 | } 62 | } 63 | } 64 | if (stationList.get(position).getDiesel()>0){ 65 | holder.diesel.setText(StringFormatUtils.formatPrice(context, "D: ",stationList.get(position).getDiesel()," €")); 66 | } else holder.diesel.setVisibility(View.GONE); 67 | if (stationList.get(position).getE5()>0){ 68 | holder.e5.setText( StringFormatUtils.formatPrice(context, "E5: ",stationList.get(position).getE5()," €")); 69 | } else holder.e5.setVisibility(View.GONE); 70 | if (stationList.get(position).getE10()>0){ 71 | holder.e10.setText(StringFormatUtils.formatPrice(context, "E10: ",stationList.get(position).getE10()," €")); 72 | } else holder.e10.setVisibility(View.GONE); 73 | holder.dist.setText(stationList.get(position).getDistance()+" km"); 74 | holder.address.setText((stationList.get(position).getAddress1()+", "+stationList.get(position).getAddress2()).toUpperCase()); 75 | if (stationList.get(position).isOpen()) { 76 | holder.isOpen.setImageDrawable(ResourcesCompat.getDrawable(context.getResources(),R.drawable.ic_local_gas_station_green_24dp, null)); 77 | } 78 | else { 79 | holder.isOpen.setImageDrawable(ResourcesCompat.getDrawable(context.getResources(),R.drawable.ic_local_gas_station_red_24dp, null)); 80 | } 81 | 82 | holder.name.setText(stationList.get(position).getBrand()); 83 | 84 | if (position == selected) holder.itemView.setBackground(ResourcesCompat.getDrawable(context.getResources(),R.drawable.rounded_highlight,null)); 85 | else holder.itemView.setBackground(ResourcesCompat.getDrawable(context.getResources(),R.drawable.rounded_transparent,null)); 86 | 87 | } 88 | 89 | @Override 90 | public int getItemCount() { 91 | return stationList.size(); 92 | } 93 | 94 | public void setSelected(int position) { 95 | int oldSelected = selected; 96 | selected = position; 97 | notifyItemChanged(oldSelected); 98 | notifyItemChanged(selected); 99 | } 100 | 101 | public int getPosUUID(String id) { 102 | 103 | for (int i=0;i stations, int cityID); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/ui/updater/ViewUpdater.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.ui.updater; 2 | 3 | import org.woheller69.spritpreise.database.Station; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * Created by chris on 24.01.2017. 10 | */ 11 | 12 | public class ViewUpdater { 13 | private static List subscribers = new ArrayList<>(); 14 | 15 | public static void addSubscriber(IUpdateableCityUI sub) { 16 | if (!subscribers.contains(sub)) { 17 | subscribers.add(sub); 18 | } 19 | } 20 | 21 | public static void removeSubscriber(IUpdateableCityUI sub) { 22 | subscribers.remove(sub); 23 | } 24 | 25 | public static void updateStations(List stations, int cityID) { 26 | ArrayList subcopy = new ArrayList<>(subscribers); 27 | for (IUpdateableCityUI sub : subcopy) { 28 | sub.processUpdateStations(stations,cityID); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/ui/util/AutoSuggestAdapter.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.ui.util; 2 | /* 3 | * Taken from https://github.com/Truiton/AutoSuggestTextViewAPICall 4 | * Modified by woheller69 5 | */ 6 | 7 | 8 | import android.content.Context; 9 | 10 | import android.widget.ArrayAdapter; 11 | import android.widget.Filter; 12 | import android.widget.Filterable; 13 | 14 | import androidx.annotation.NonNull; 15 | import androidx.annotation.Nullable; 16 | 17 | import org.woheller69.spritpreise.database.City; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | public class AutoSuggestAdapter extends ArrayAdapter implements Filterable { 23 | private final List mlistData; 24 | private final List mlistCity; 25 | 26 | public AutoSuggestAdapter(@NonNull Context context, int resource) { 27 | super(context, resource); 28 | mlistData = new ArrayList<>(); 29 | mlistCity = new ArrayList<>(); 30 | } 31 | 32 | public void setData(List list, List cityList) { 33 | mlistData.clear(); 34 | mlistCity.clear(); 35 | mlistData.addAll(list); 36 | mlistCity.addAll(cityList); 37 | } 38 | 39 | @Override 40 | public int getCount() { 41 | return mlistData.size(); 42 | } 43 | 44 | @Nullable 45 | @Override 46 | public String getItem(int position) { 47 | return mlistData.get(position); 48 | } 49 | 50 | /** 51 | * Used to Return the full object directly from adapter. 52 | * 53 | * @param position 54 | * @return 55 | */ 56 | public City getObject(int position) { 57 | return mlistCity.get(position); 58 | } 59 | 60 | @NonNull 61 | @Override 62 | public Filter getFilter() { 63 | Filter dataFilter = new Filter() { 64 | @Override 65 | protected FilterResults performFiltering(CharSequence constraint) { 66 | FilterResults filterResults = new FilterResults(); 67 | if (constraint != null) { 68 | filterResults.values = mlistData; 69 | filterResults.count = mlistData.size(); 70 | } 71 | return filterResults; 72 | } 73 | 74 | @Override 75 | protected void publishResults(CharSequence constraint, FilterResults results) { 76 | if (results != null && (results.count > 0)) { 77 | notifyDataSetChanged(); 78 | } else { 79 | notifyDataSetInvalidated(); 80 | } 81 | } 82 | }; 83 | return dataFilter; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/ui/util/geocodingApiCall.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.ui.util; 2 | 3 | import android.content.Context; 4 | 5 | import com.android.volley.Request; 6 | import com.android.volley.RequestQueue; 7 | import com.android.volley.Response; 8 | import com.android.volley.toolbox.StringRequest; 9 | import com.android.volley.toolbox.Volley; 10 | 11 | /** 12 | * Created by MG on 04-03-2018. 13 | * 14 | * Taken from https://github.com/Truiton/AutoSuggestTextViewAPICall 15 | * Modified by woheller69 16 | */ 17 | 18 | public class geocodingApiCall { 19 | private static geocodingApiCall mInstance; 20 | private RequestQueue mRequestQueue; 21 | private static Context mCtx; 22 | 23 | public geocodingApiCall(Context ctx) { 24 | mCtx = ctx.getApplicationContext(); 25 | mRequestQueue = getRequestQueue(); 26 | } 27 | 28 | public static synchronized geocodingApiCall getInstance(Context context) { 29 | if (mInstance == null) { 30 | mInstance = new geocodingApiCall(context); 31 | } 32 | return mInstance; 33 | } 34 | 35 | public RequestQueue getRequestQueue() { 36 | if (mRequestQueue == null) { 37 | mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext()); 38 | } 39 | return mRequestQueue; 40 | } 41 | 42 | public void addToRequestQueue(Request req) { 43 | getRequestQueue().add(req); 44 | } 45 | 46 | public static void make(Context ctx, String query, String url, String lang, Response.Listener 47 | listener, Response.ErrorListener errorListener) { 48 | url = url + query+"&language="+lang; 49 | StringRequest stringRequest = new StringRequest(Request.Method.GET, url, 50 | listener, errorListener); 51 | geocodingApiCall.getInstance(ctx).addToRequestQueue(stringRequest); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/spritpreise/ui/viewPager/CityPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.spritpreise.ui.viewPager; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import androidx.annotation.NonNull; 7 | import androidx.fragment.app.FragmentManager; 8 | import androidx.lifecycle.Lifecycle; 9 | import androidx.viewpager2.adapter.FragmentStateAdapter; 10 | 11 | import org.woheller69.spritpreise.database.CityToWatch; 12 | import org.woheller69.spritpreise.database.Station; 13 | import org.woheller69.spritpreise.database.SQLiteHelper; 14 | import org.woheller69.spritpreise.services.UpdateDataService; 15 | import org.woheller69.spritpreise.ui.CityFragment; 16 | import org.woheller69.spritpreise.ui.updater.IUpdateableCityUI; 17 | 18 | import java.util.Collections; 19 | import java.util.List; 20 | 21 | import static androidx.core.app.JobIntentService.enqueueWork; 22 | import static org.woheller69.spritpreise.services.UpdateDataService.SKIP_UPDATE_INTERVAL; 23 | 24 | public class CityPagerAdapter extends FragmentStateAdapter implements IUpdateableCityUI { 25 | 26 | private final SQLiteHelper database; 27 | 28 | private List cities; 29 | 30 | //Adapter for the Viewpager switching between different locations 31 | public CityPagerAdapter(Context context, @NonNull FragmentManager supportFragmentManager, @NonNull Lifecycle lifecycle) { 32 | super(supportFragmentManager,lifecycle); 33 | this.database = SQLiteHelper.getInstance(context); 34 | loadCities(); 35 | } 36 | 37 | public void loadCities() { 38 | this.cities = database.getAllCitiesToWatch(); 39 | Collections.sort(cities, (o1, o2) -> o1.getRank() - o2.getRank()); 40 | } 41 | 42 | @NonNull 43 | @Override 44 | public CityFragment createFragment(int position) { 45 | Bundle args = new Bundle(); 46 | args.putInt("city_id", cities.get(position).getCityId()); 47 | 48 | return CityFragment.newInstance(args); 49 | } 50 | 51 | @Override 52 | public int getItemCount() { 53 | return cities.size(); 54 | } 55 | 56 | 57 | public CharSequence getPageTitle(int position) { 58 | return cities.get(position).getCityName(); 59 | } 60 | 61 | public static void refreshSingleData(Context context, Boolean asap, int cityId) { 62 | Intent intent = new Intent(context, UpdateDataService.class); 63 | intent.setAction(UpdateDataService.UPDATE_SINGLE_ACTION); 64 | intent.putExtra(SKIP_UPDATE_INTERVAL, asap); 65 | intent.putExtra("cityId",cityId); 66 | enqueueWork(context, UpdateDataService.class, 0, intent); 67 | } 68 | 69 | 70 | @Override 71 | public void processUpdateStations(List stations, int cityID) { 72 | 73 | } 74 | 75 | public int getCityIDForPos(int pos) { 76 | CityToWatch city = cities.get(pos); 77 | return city.getCityId(); 78 | } 79 | 80 | public int getPosForCityID(int cityID) { 81 | for (int i = 0; i < cities.size(); i++) { 82 | CityToWatch city = cities.get(i); 83 | if (city.getCityId() == cityID) { 84 | return i; 85 | } 86 | } 87 | return -1; // item not found 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/splash_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/spritpreise/1e752d920f569b6b66069015c9ede2709880b7da/app/src/main/res/drawable-hdpi/splash_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/splash_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/spritpreise/1e752d920f569b6b66069015c9ede2709880b7da/app/src/main/res/drawable-mdpi/splash_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/widget_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/spritpreise/1e752d920f569b6b66069015c9ede2709880b7da/app/src/main/res/drawable-nodpi/widget_preview.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/splash_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/spritpreise/1e752d920f569b6b66069015c9ede2709880b7da/app/src/main/res/drawable-xhdpi/splash_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/splash_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/spritpreise/1e752d920f569b6b66069015c9ede2709880b7da/app/src/main/res/drawable-xxhdpi/splash_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/splash_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/spritpreise/1e752d920f569b6b66069015c9ede2709880b7da/app/src/main/res/drawable-xxxhdpi/splash_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/app_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 8 | 13 | 18 | 23 | 24 | 26 | 27 | 29 | 34 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_star_rate_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_add_location_alt_24px.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_arrow_back_24px.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_direction_32dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_edit_location_24px.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_highlight_32dp.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_info_24px.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 6 | 8 | 13 | 14 | 16 | 17 | 19 | 24 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_monochrome.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | 13 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_local_gas_station_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_local_gas_station_green_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_local_gas_station_red_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_location_32dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_location_on_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_map_24px.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_playpause.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_rainviewer.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_refresh_24px.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_settings_24px.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_skip_next_24px.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_skip_previous_24px.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_south_24px.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_widget_gas_station_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/map_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/spritpreise/1e752d920f569b6b66069015c9ede2709880b7da/app/src/main/res/drawable/map_back.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/recycle_view_line_divider.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/rounded_green.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/rounded_grey.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/rounded_highlight.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/rounded_lightred.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/rounded_orange.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/rounded_red.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/rounded_transparent.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/rounded_yellow.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/splash_screen.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/star_primary_dark_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/star_yellow_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/transparent_no_margin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/widget_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout-land/card_stations.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 18 | 19 | 23 | 24 | 30 | 31 | 41 | 42 | 46 | 47 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /app/src/main/res/layout/about.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 18 | 19 | 27 | 28 | 34 | 35 | 36 | 42 | 43 | 50 | 51 | 52 | 53 | 61 | 62 | 63 | 70 | 71 | 80 | 81 | 89 | 90 | 91 | 98 | 99 | 107 | 108 | 115 | 116 | 124 | 125 | 132 | 133 | 140 | 141 | 150 | 151 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_about.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_city_gas_prices.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 17 | 18 | 23 | 24 | 31 | 32 | 39 | 40 | 44 | 45 | 46 | 47 | 48 | 49 | 57 | 58 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_manage_locations.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 17 | 18 | 26 | 27 | 32 | 33 | 34 | 35 | 36 | 37 | 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_tutorial.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 12 | 13 | 21 | 22 | 28 | 29 |