MainActivity.java (12826B)
1 package com.example.project_assignment; 2 3 import android.Manifest; 4 5 import androidx.annotation.NonNull; 6 import androidx.appcompat.app.AppCompatActivity; 7 import androidx.constraintlayout.widget.ConstraintLayout; 8 import androidx.core.app.ActivityCompat; 9 import androidx.recyclerview.widget.LinearLayoutManager; 10 import androidx.recyclerview.widget.RecyclerView; 11 12 import android.content.Context; 13 import android.content.Intent; 14 import android.content.SharedPreferences; 15 import android.content.pm.PackageManager; 16 import android.location.Location; 17 import android.location.LocationListener; 18 import android.location.LocationManager; 19 import android.os.Bundle; 20 import android.util.Log; 21 import android.view.Gravity; 22 import android.view.LayoutInflater; 23 import android.view.View; 24 import android.widget.Button; 25 import android.widget.EditText; 26 import android.widget.PopupWindow; 27 import android.widget.RadioGroup; 28 import android.widget.SeekBar; 29 import android.widget.TextView; 30 import android.widget.Toast; 31 32 import com.example.project_assignment.comparators.SortByLocation; 33 import com.example.project_assignment.comparators.SortByName; 34 import com.example.project_assignment.comparators.SortByWeight; 35 import com.google.android.material.floatingactionbutton.FloatingActionButton; 36 import com.google.gson.Gson; 37 import com.google.gson.reflect.TypeToken; 38 39 import java.lang.reflect.Type; 40 import java.util.ArrayList; 41 import java.util.Collections; 42 import java.util.List; 43 44 @SuppressWarnings("FieldCanBeLocal") 45 public class MainActivity extends AppCompatActivity implements JsonTask.JsonTaskListener, LocationListener { 46 47 //private final String JSON_URL = "https://data.nasa.gov/resource/y77d-th95.json"; 48 private final String JSON_URL = "https://mobprog.webug.se/json-api?login=a22willi"; 49 private LocationManager locationManager; 50 private SharedPreferences preference; 51 private SharedPreferences.Editor preferenceEditor; 52 53 private ArrayList<Meteorite> meteorites; 54 private ArrayList<Meteorite> unalteredMeteoriteList; 55 56 private RecyclerViewAdapter adapter; 57 58 private boolean distanceSet = false; 59 60 @Override 61 protected void onCreate(Bundle savedInstanceState) { 62 super.onCreate(savedInstanceState); 63 setContentView(R.layout.activity_main); 64 65 new JsonTask(this).execute(JSON_URL); 66 67 preference = getSharedPreferences("preferences", MODE_PRIVATE); 68 preferenceEditor = preference.edit(); 69 70 locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); 71 72 //request permission to use location services: fine location and coarse location. 73 if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { 74 75 Toast.makeText(MainActivity.this, "The app needs access to location services.", Toast.LENGTH_LONG); 76 ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, 1); 77 return; 78 } 79 locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this); 80 81 FloatingActionButton showSortingOptionsButton = findViewById(R.id.showSortingOptionsButton); 82 showSortingOptionsButton.setOnClickListener(new View.OnClickListener() { 83 @Override 84 public void onClick(View view) { 85 showFilterOptions(view); 86 } 87 }); 88 89 FloatingActionButton showAboutButton = findViewById(R.id.showAboutSectionButton); 90 showAboutButton.setOnClickListener(new View.OnClickListener() { 91 @Override 92 public void onClick(View view) { 93 Intent intent = new Intent(MainActivity.this, AboutActivity.class); 94 startActivity(intent); 95 } 96 }); 97 } 98 99 /** 100 * This function runs after JSON data has been fetched from remote database. 101 * 102 * @param json 103 */ 104 @Override 105 public void onPostExecute(String json) { 106 Gson gson = new Gson(); 107 108 Type type = new TypeToken<List<Meteorite>>() { 109 }.getType(); 110 meteorites = gson.fromJson(json, type); 111 unalteredMeteoriteList = new ArrayList<Meteorite>(); 112 unalteredMeteoriteList.addAll(meteorites); 113 114 for (Meteorite m : meteorites) { 115 Log.d("Meteorite: ", m.toString()); 116 } 117 118 119 //Initialize recyclerview and inject meteorite data. 120 RecyclerView recyclerView = findViewById(R.id.recyclerView); 121 adapter = new RecyclerViewAdapter(this, meteorites); 122 recyclerView.setAdapter(adapter); 123 recyclerView.setLayoutManager(new LinearLayoutManager(this)); 124 adapter.notifyDataSetChanged(); 125 } 126 127 /** 128 * Open popupwindow that shows all possible filter options. 129 * 130 * @param view Current view (MainActivity) 131 */ 132 private void showFilterOptions(View view) { 133 134 //initialize layoutInflater 135 LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); 136 View popupView = inflater.inflate(R.layout.filter_popup, null); 137 138 int width = ConstraintLayout.LayoutParams.MATCH_PARENT; 139 int height = ConstraintLayout.LayoutParams.MATCH_PARENT; 140 boolean focusable = true; 141 final PopupWindow popupWindow = new PopupWindow(popupView, width, height, focusable); 142 popupWindow.showAtLocation(view, Gravity.CENTER, 0, 0); 143 144 //fetch all necessary views and widgets in popup. 145 TextView distanceProgress = popupView.findViewById(R.id.distanceProgress); 146 Button confirmButton = popupView.findViewById(R.id.confirmFilterButton); 147 Button cancelButton = popupView.findViewById(R.id.cancelFilterButton); 148 SeekBar seekBar = popupView.findViewById(R.id.distanceBar); 149 EditText minMass = popupView.findViewById(R.id.setMinimumMass); 150 EditText maxMass = popupView.findViewById(R.id.setMaximumMass); 151 EditText minYear = popupView.findViewById(R.id.setMinimumYear); 152 EditText maxYear = popupView.findViewById(R.id.setMaximumYear); 153 RadioGroup sorting = popupView.findViewById(R.id.sortingGroup); 154 155 156 //set all listeners 157 confirmButton.setOnClickListener(new View.OnClickListener() { 158 @Override 159 public void onClick(View view) { 160 161 //reinitialize saved meteorites. 162 meteorites.clear(); 163 meteorites.addAll(unalteredMeteoriteList); 164 165 //fetch all editText data. 166 int intMinMass = fetchEditTextData(minMass); 167 int intMaxMass = fetchEditTextData(maxMass); 168 int intMinYear = fetchEditTextData(minYear); 169 int intMaxYear = fetchEditTextData(maxYear); 170 int distance = Integer.parseInt(String.valueOf(seekBar.getProgress())); 171 int selectedSortingButton = sorting.getCheckedRadioButtonId(); 172 173 ArrayList<Meteorite> toRemove = new ArrayList<Meteorite>(); 174 175 //check if meteorites match filter settings. 176 for (Meteorite m : meteorites) { 177 boolean matchingFilter = m.matchingFilter(intMinMass, intMaxMass, intMinYear, intMaxYear, distance); 178 if (!matchingFilter) { 179 toRemove.add(m); 180 } 181 } 182 183 //remove meteorites that did not match the filter settings. 184 for (Meteorite m : toRemove) { 185 meteorites.remove(m); 186 } 187 188 switch (selectedSortingButton) { 189 case R.id.radioButtonSortLocation: 190 Collections.sort(meteorites, new SortByLocation()); 191 preferenceEditor.putString("sort", "location"); 192 break; 193 case R.id.radioButtonSortWeight: 194 Collections.sort(meteorites, new SortByWeight()); 195 preferenceEditor.putString("sort", "mass"); 196 break; 197 default: 198 Collections.sort(meteorites, new SortByName()); 199 preferenceEditor.putString("name", "name"); 200 } 201 202 //save filter options. 203 preferenceEditor.putInt("minMass", intMinMass); 204 preferenceEditor.putInt("maxMass", intMaxMass); 205 preferenceEditor.putInt("minYear", intMinYear); 206 preferenceEditor.putInt("maxYear", intMaxYear); 207 preferenceEditor.putInt("distance", distance); 208 preferenceEditor.apply(); 209 210 adapter.notifyDataSetChanged(); 211 212 try { 213 Thread.sleep(100); 214 } catch (InterruptedException e) { 215 throw new RuntimeException(e); 216 } 217 popupWindow.dismiss(); 218 } 219 }); 220 221 cancelButton.setOnClickListener(new View.OnClickListener() { 222 @Override 223 public void onClick(View view) { 224 try { 225 Thread.sleep(100); 226 } catch (InterruptedException e) { 227 throw new RuntimeException(e); 228 } 229 popupWindow.dismiss(); 230 } 231 }); 232 233 seekBar.setMax(10000); //set max value of seekbar to 20000km 234 seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 235 @Override 236 public void onProgressChanged(SeekBar seekBar, int i, boolean b) { 237 distanceProgress.setText(seekBar.getProgress() + "km"); 238 } 239 240 @Override 241 public void onStartTrackingTouch(SeekBar seekBar) { 242 243 } 244 245 @Override 246 public void onStopTrackingTouch(SeekBar seekBar) { 247 248 } 249 }); 250 } 251 252 private int fetchEditTextData(EditText editText) { 253 int output = 0; 254 if (!String.valueOf(editText.getText()).isEmpty()) { 255 output = Integer.parseInt(String.valueOf(editText.getText())); 256 return output; 257 } 258 return output; 259 } 260 261 262 /** 263 * Runs after location has been pulled. 264 * 265 * @param location the users current location data. 266 */ 267 @Override 268 public void onLocationChanged(@NonNull Location location) { 269 /* Prevent meteorite locations from being recaculated each time users location changes, 270 * Or the meteorites have not yet been fetched from database. */ 271 if (distanceSet || meteorites.isEmpty()) { 272 return; 273 } 274 275 Toast.makeText(this, "Loaded location", Toast.LENGTH_SHORT).show(); 276 277 for (Meteorite m : meteorites) { 278 m.generateDistanceFrom(location.getLatitude(), location.getLongitude()); 279 } 280 281 distanceSet = true; 282 283 //apply stored filter settings. 284 filterMeteorites(); 285 } 286 287 /** 288 * Used to filter meteorites based on their mass, date and relative location to the user. 289 */ 290 public void filterMeteorites() { 291 //if there are saved filter settings, load them. 292 if (preference.contains("minMass")) { 293 Toast.makeText(MainActivity.this, "Loaded stored filter", Toast.LENGTH_SHORT).show(); 294 ArrayList<Meteorite> toRemove = new ArrayList<>(); 295 296 /* 297 Since the list which is being iterated through cannot be changed during its iterations. 298 Append all meteorites which are to be removed to a new list. 299 */ 300 for (Meteorite m : meteorites) { 301 boolean matchingFilter = m.matchingFilter(preference.getInt("minMass", 0), preference.getInt("maxMass", 0), preference.getInt("minYear", 0), preference.getInt("maxYear", 0), preference.getInt("distance", 0)); 302 if (!matchingFilter) { 303 toRemove.add(m); 304 } 305 } 306 //remove meteorites that did not match the filter settings. 307 for (Meteorite m : toRemove) { 308 meteorites.remove(m); 309 } 310 311 //sort the meteories based on the users preferred sorting method. 312 switch (preference.getString("sort", "")) { 313 case "location": 314 Collections.sort(meteorites, new SortByLocation()); 315 break; 316 case "mass": 317 Collections.sort(meteorites, new SortByWeight()); 318 break; 319 default: 320 Collections.sort(meteorites, new SortByName()); 321 } 322 adapter.notifyDataSetChanged(); 323 } 324 } 325 } 326