You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MapsFragment.java 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. package com.example.meinwald.ui.map;
  2. import android.Manifest;
  3. import android.content.pm.PackageManager;
  4. import android.graphics.Bitmap;
  5. import android.graphics.BitmapFactory;
  6. import android.location.Location;
  7. import android.os.Bundle;
  8. import android.os.IBinder;
  9. import android.preference.PreferenceManager;
  10. import android.util.Log;
  11. import android.view.LayoutInflater;
  12. import android.view.View;
  13. import android.view.ViewGroup;
  14. import android.widget.ListView;
  15. import com.example.meinwald.BuildConfig;
  16. import com.example.meinwald.R;
  17. import com.example.meinwald.ui.area.AreaReference;
  18. import com.example.meinwald.ui.area.OwnArea;
  19. import com.example.meinwald.ui.task.OwnTask;
  20. import com.google.android.gms.dynamic.IObjectWrapper;
  21. import com.google.android.gms.location.FusedLocationProviderClient;
  22. import com.google.android.gms.location.LocationServices;
  23. import com.google.android.gms.maps.CameraUpdateFactory;
  24. import com.google.android.gms.maps.GoogleMap;
  25. import com.google.android.gms.maps.OnMapReadyCallback;
  26. import com.google.android.gms.maps.SupportMapFragment;
  27. import com.google.android.gms.maps.model.BitmapDescriptor;
  28. import com.google.android.gms.maps.model.CameraPosition;
  29. import com.google.android.gms.maps.model.LatLng;
  30. import com.google.android.gms.maps.model.MarkerOptions;
  31. import com.google.android.gms.maps.model.Polyline;
  32. import com.google.android.gms.maps.model.PolylineOptions;
  33. import com.google.android.gms.tasks.OnCompleteListener;
  34. import com.google.android.gms.tasks.Task;
  35. import org.json.JSONArray;
  36. import org.json.JSONException;
  37. import java.io.BufferedReader;
  38. import java.io.File;
  39. import java.io.FileReader;
  40. import java.io.IOException;
  41. import java.util.ArrayList;
  42. import java.util.List;
  43. import androidx.annotation.NonNull;
  44. import androidx.core.app.ActivityCompat;
  45. import androidx.core.content.ContextCompat;
  46. import androidx.fragment.app.Fragment;
  47. import androidx.fragment.app.FragmentTransaction;
  48. import static com.example.meinwald.R.layout.fragment_map;
  49. public class MapsFragment extends Fragment implements OnMapReadyCallback {
  50. /**
  51. * ______________________________________________________________________________________________
  52. * MAPS
  53. */
  54. // A default location and default zoom to use when location permission is not granted.
  55. private static final LatLng M_DEFAULT_LOCATION = new LatLng(49.5, 11.0); //Nuremberg
  56. private static final int DEFAULT_ZOOM = 1;
  57. private static final float MAX_ZOOM = (float) 20.0;
  58. private static final int PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1;
  59. private static final String TAG = MapsFragment.class.getSimpleName();
  60. // Keys for storing activity state.
  61. private static final String KEY_CAMERA_POSITION = "camera_position";
  62. private static final String KEY_LOCATION = "location";
  63. private GoogleMap mMap;
  64. private CameraPosition mCameraPosition;
  65. /**
  66. * The entry point to the Fused Location Provider.
  67. */
  68. private FusedLocationProviderClient mFusedLocationProviderClient;
  69. private boolean mLocationPermissionGranted;
  70. /**
  71. * The geographical location where the device is currently located.
  72. * That is the last-known location retrieved by the Fused Location Provider.
  73. */
  74. private Location mLastKnownLocation;
  75. /**
  76. * ______________________________________________________________________________________________
  77. * TASKS
  78. */
  79. private List<OwnTask> allTasks;
  80. private static final String KEY_ALL_TASKS = "allTasks";
  81. /**
  82. * ______________________________________________________________________________________________
  83. * STORAGEE
  84. */
  85. private static final int READ_EXTERNAL_STORAGE_CODE = 200;
  86. private static final int WRITE_EXTERNAL_STORAGE_CODE = 201;
  87. private boolean mReadStorageGranted;
  88. private boolean mWriteStorageGranted;
  89. /**
  90. * ______________________________________________________________________________________________
  91. * AREA
  92. */
  93. private List<OwnArea> allAreas;
  94. private static final String KEY_AREA_REFERENCES = "areaReferences";
  95. private List<AreaReference> areaReferences;
  96. @Override
  97. public void onCreate (Bundle savedInstanceState)
  98. {
  99. super.onCreate(savedInstanceState);
  100. areaReferences = new ArrayList<>();
  101. areaReferences = readAreaReferencesFromPreferences();
  102. }
  103. /**
  104. * Is called when the MapsFragment view is created. Initializes all service connections, the location client and map itself.
  105. * @param inflater Calling LayoutInflater
  106. * @param container ViewGroup
  107. * @param savedInstanceState Applications savedInstanceState
  108. * @return
  109. */
  110. public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
  111. //get view to inflate
  112. View root = inflater.inflate(fragment_map, container, false);
  113. //get storage permission
  114. getReadStoragePermission();
  115. //get tasks
  116. allTasks = readTasksFromPreferences();
  117. //get areas
  118. //instantiate area lists
  119. allAreas = new ArrayList<>();
  120. //get saved areas
  121. allAreas = loadAreasFromPreferences();
  122. //display map fragment
  123. SupportMapFragment mapFragment = new SupportMapFragment();
  124. FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
  125. transaction.add(R.id.map, mapFragment).commit();
  126. //construct a FusedLocationProviderClient.
  127. mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(getActivity());
  128. //wait for map is ready
  129. mapFragment.getMapAsync(new OnMapReadyCallback() {
  130. @Override
  131. public void onMapReady(GoogleMap googleMap) {
  132. //save map instance locally
  133. mMap = googleMap;
  134. //set max zoom level
  135. mMap.setMaxZoomPreference(MAX_ZOOM);
  136. //retrieve saved instance state
  137. mCameraPosition = parseCameraPosition(PreferenceManager.getDefaultSharedPreferences(getContext()).getString(KEY_CAMERA_POSITION, new CameraPosition(new LatLng(45.0,11.0), (float) 1.0, (float) 0.0,(float) 0.0).toString()));
  138. mLastKnownLocation = new Location(PreferenceManager.getDefaultSharedPreferences(getContext()).getString(KEY_LOCATION, new LatLng(45.0,11.0).toString()));
  139. mCameraPosition = parseCameraPosition(PreferenceManager.getDefaultSharedPreferences(getContext()).getString(KEY_CAMERA_POSITION, new CameraPosition(new LatLng(45.0,11.0), (float) 1.0, (float) 0.0,(float) 0.0).toString()));
  140. //move camera to last position
  141. mMap.moveCamera(CameraUpdateFactory.newCameraPosition(mCameraPosition));
  142. //get permission
  143. getLocationPermission();
  144. //turn on the My Location layer and the related control on the map.
  145. updateLocationUI();
  146. // Get the current location of the device and set the position of the map.
  147. getDeviceLocation();
  148. }
  149. });
  150. return root;
  151. }
  152. @Override
  153. public void onPause() {
  154. //save current location and camera position to shared preferences
  155. if (mMap.getCameraPosition() != null && mLastKnownLocation != null) {
  156. PreferenceManager.getDefaultSharedPreferences(getContext()).edit().putString(KEY_CAMERA_POSITION, mMap.getCameraPosition().toString()).commit();
  157. PreferenceManager.getDefaultSharedPreferences(getContext()).edit().putString(KEY_LOCATION, mLastKnownLocation.toString()).commit();
  158. }
  159. super.onPause();
  160. }
  161. private void drawAreas()
  162. {
  163. for (OwnArea area: allAreas)
  164. {
  165. PolylineOptions options = new PolylineOptions().clickable(true);
  166. for (LatLng location: area.getLocations())
  167. {
  168. options.add(location);
  169. if (BuildConfig.DEBUG)
  170. {
  171. Log.d(TAG, "Position: " + location.toString());
  172. }
  173. }
  174. Polyline polyline1 = mMap.addPolyline(options);
  175. }
  176. }
  177. /**
  178. * Reads areaReferences saved in preferences as jsonStrings
  179. *
  180. * @return list of AreaReferences
  181. */
  182. private List<AreaReference> readAreaReferencesFromPreferences() {
  183. if (BuildConfig.DEBUG)
  184. {
  185. Log.d(TAG, "readAreaReferencesFromPreferences()");
  186. }
  187. List<AreaReference> areas = new ArrayList<>();
  188. try {
  189. String jsonString = PreferenceManager.getDefaultSharedPreferences(getContext()).getString(KEY_AREA_REFERENCES, "error");
  190. if(!"error".equalsIgnoreCase(jsonString)) {
  191. JSONArray jsonObjects = new JSONArray(jsonString);
  192. for (int i = 0; i<jsonObjects.length();i++) {
  193. areas.add(new AreaReference(jsonObjects.getJSONObject(i)));
  194. }
  195. }
  196. else
  197. {
  198. if (BuildConfig.DEBUG)
  199. {
  200. Log.e(TAG, "read area reference error");
  201. }
  202. }
  203. } catch (JSONException e) {
  204. if (BuildConfig.DEBUG) {
  205. Log.e(TAG, "Error while parsing area references as jsonObjects from preferences");
  206. e.printStackTrace();
  207. }
  208. }
  209. for (AreaReference reference: areas)
  210. {
  211. if (BuildConfig.DEBUG)
  212. {
  213. Log.d(TAG, "read reference: " + reference.toString());
  214. }
  215. }
  216. return areas;
  217. }
  218. /**
  219. * Get OwnAreas from a JSONString.
  220. * @return
  221. */
  222. private List<OwnArea> loadAreasFromPreferences()
  223. {
  224. if (BuildConfig.DEBUG)
  225. {
  226. Log.d(TAG, "loadAreasFromPreferences() reference size: " + areaReferences.size());
  227. }
  228. List<OwnArea> areas = new ArrayList<>();
  229. Integer i = 0;
  230. for (AreaReference reference: areaReferences)
  231. {
  232. String jsonString = readAreaFromExternalStorage(reference.getPath());
  233. areas.add(new OwnArea(jsonString));
  234. if (BuildConfig.DEBUG)
  235. {
  236. Log.d(TAG, "load area from preferences: " + areas.get(i).toString());
  237. }
  238. i++;
  239. }
  240. return areas;
  241. }
  242. /**
  243. * Read OwnArea from file in external storage and return as a string.
  244. *
  245. * @param path File path where the OwnArea is saved.
  246. * @return
  247. */
  248. private String readAreaFromExternalStorage(String path)
  249. {
  250. if (BuildConfig.DEBUG)
  251. {
  252. Log.d(TAG, "readAreaFromExternalStorage()");
  253. }
  254. //Get the text file
  255. File file = new File(path);
  256. //Read text from file
  257. StringBuilder text = new StringBuilder();
  258. try {
  259. BufferedReader br = new BufferedReader(new FileReader(file));
  260. String line;
  261. while ((line = br.readLine()) != null) {
  262. text.append(line);
  263. }
  264. br.close();
  265. }
  266. catch (IOException e) {
  267. if (BuildConfig.DEBUG)
  268. {
  269. Log.e(TAG, "Failed read from external storage: " + e.toString());
  270. }
  271. }
  272. return text.toString();
  273. }
  274. @Override
  275. public void onResume() {
  276. super.onResume();
  277. }
  278. /**
  279. * Parses a string and creates a new CameraPosition from it.
  280. * Use CameraPosition.toString() to get corresponding string.
  281. * @param posString String which includes a camera position.
  282. * @return The parsed position of type CameraPosition.
  283. */
  284. private CameraPosition parseCameraPosition(String posString) {
  285. //parse string from shared preferences (CameraPosition.toString())
  286. String[] stringsplit = posString.split(" ");
  287. float lat = Float.parseFloat(stringsplit[1].split(",")[0].substring(1));
  288. float lng = Float.parseFloat(stringsplit[1].split(",")[1].substring(0,stringsplit[1].split(",")[1].length()-1));
  289. float zoom = Float.parseFloat(stringsplit[2].split("=")[1].substring(0,stringsplit[2].split("=")[1].length()-1));
  290. float tilt = Float.parseFloat(stringsplit[3].split("=")[1].substring(0,stringsplit[3].split("=")[1].length()-1));
  291. float bearing = Float.parseFloat(stringsplit[4].split("=")[1].substring(0,stringsplit[4].split("=")[1].length()-1));
  292. return new CameraPosition(new LatLng(lat,lng),zoom,tilt,bearing);
  293. }
  294. /**
  295. * Request location permission, so that we can get the location of the device.
  296. * The result of the permission request is handled by a callback,
  297. * onRequestPermissionsResult.
  298. *
  299. */
  300. private void getLocationPermission() {
  301. if (ContextCompat.checkSelfPermission(getActivity().getApplicationContext(),
  302. android.Manifest.permission.ACCESS_FINE_LOCATION)
  303. == PackageManager.PERMISSION_GRANTED) {
  304. mLocationPermissionGranted = true;
  305. } else {
  306. ActivityCompat.requestPermissions(getActivity(),
  307. new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION},
  308. PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
  309. }
  310. }
  311. /**
  312. * DOCU ME!
  313. * TOOD: What are grant results etc for?
  314. * @param requestCode
  315. * @param permissions
  316. * @param grantResults
  317. */
  318. @Override
  319. public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
  320. {
  321. mLocationPermissionGranted = false;
  322. switch (requestCode) {
  323. case PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION: {
  324. // If request is cancelled, the result arrays are empty.
  325. if (grantResults.length > 0
  326. && grantResults[0] == PackageManager.PERMISSION_GRANTED)
  327. {
  328. updateLocationUI();
  329. mLocationPermissionGranted = true;
  330. }
  331. }
  332. case READ_EXTERNAL_STORAGE_CODE: {
  333. if (grantResults.length > 0
  334. && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
  335. mReadStorageGranted = true;
  336. }
  337. }
  338. }
  339. }
  340. /**
  341. * Get the best and most recent location of the device, which may be null in rare
  342. * cases when a location is not available.
  343. *
  344. */
  345. private void getDeviceLocation() {
  346. try {
  347. if (mLocationPermissionGranted) {
  348. Task<Location> locationResult = mFusedLocationProviderClient.getLastLocation();
  349. locationResult.addOnCompleteListener(getActivity(), new OnCompleteListener<Location>() {
  350. @Override
  351. public void onComplete(@NonNull Task<Location> task) {
  352. if (task.isSuccessful()) {
  353. // Set the last known position to the current location of the device.
  354. mLastKnownLocation = task.getResult();
  355. if (BuildConfig.DEBUG) {
  356. if (task.getResult() != null)
  357. {
  358. Log.d(TAG, "getDeviceLocation " + task.getResult().toString());
  359. }
  360. }
  361. } else {
  362. if (BuildConfig.DEBUG) {
  363. Log.d(TAG, "Current location is null. Using defaults.");
  364. Log.e(TAG, "Exception: %s", task.getException());
  365. }
  366. mMap.moveCamera(CameraUpdateFactory
  367. .newLatLngZoom(M_DEFAULT_LOCATION, DEFAULT_ZOOM));
  368. //mMap.getUiSettings().setMyLocationButtonEnabled(false);
  369. if (BuildConfig.DEBUG) {
  370. Log.d(TAG, "getDeviceLocation: task not successful");
  371. }
  372. }
  373. }
  374. });
  375. }
  376. } catch (SecurityException e) {
  377. if (BuildConfig.DEBUG) {
  378. Log.e(TAG, "Exception occurred: ");
  379. e.printStackTrace();
  380. }
  381. }
  382. }
  383. private void updateLocationUI() {
  384. if (mMap == null) {
  385. if (BuildConfig.DEBUG) {
  386. Log.d(TAG, "Permission: Map not ready!");
  387. }
  388. return;
  389. }
  390. try {
  391. if (mLocationPermissionGranted) {
  392. mMap.setMyLocationEnabled(true);
  393. mMap.getUiSettings().setMapToolbarEnabled(true);
  394. mMap.getUiSettings().setMyLocationButtonEnabled(true);
  395. mMap.getUiSettings().setCompassEnabled(true);
  396. mMap.getUiSettings().setZoomControlsEnabled(true);
  397. if (BuildConfig.DEBUG) {
  398. Log.d(TAG, "Permission: Granted!");
  399. }
  400. } else {
  401. mMap.setMyLocationEnabled(false);
  402. mMap.getUiSettings().setMyLocationButtonEnabled(false);
  403. mMap.getUiSettings().setZoomControlsEnabled(false);
  404. mMap.getUiSettings().setCompassEnabled(false);
  405. mMap.getUiSettings().setMapToolbarEnabled(false);
  406. mLastKnownLocation = null;
  407. if (BuildConfig.DEBUG) {
  408. Log.d(TAG, "Permission: Denied!");
  409. }
  410. getLocationPermission();
  411. }
  412. } catch (SecurityException e) {
  413. if (BuildConfig.DEBUG) {
  414. Log.e(TAG, "Exception occurred: ");
  415. e.printStackTrace();
  416. }
  417. }
  418. for (OwnTask task: allTasks)
  419. {
  420. MarkerOptions marker = new MarkerOptions();
  421. //set options
  422. marker.position(new LatLng(task.getLocation().getLatitude(), task.getLocation().getLongitude()));
  423. marker.title(task.getTitle());
  424. mMap.addMarker(marker);
  425. }
  426. drawAreas();
  427. }
  428. /**
  429. * Request read external storage permission, so that we can get saved images from external storage.
  430. * The result of the permission request is handled by a callback,
  431. * onRequestPermissionsResult.
  432. *
  433. */
  434. private void getReadStoragePermission() {
  435. if (ContextCompat.checkSelfPermission(getActivity().getApplicationContext(),
  436. Manifest.permission.READ_EXTERNAL_STORAGE)
  437. == PackageManager.PERMISSION_GRANTED) {
  438. mReadStorageGranted = true;
  439. } else {
  440. ActivityCompat.requestPermissions(getActivity(),
  441. new String[]{android.Manifest.permission.READ_EXTERNAL_STORAGE},
  442. READ_EXTERNAL_STORAGE_CODE);
  443. }
  444. }
  445. /**
  446. * Reads tasks saved in preferences as jsonStrings
  447. *
  448. * @return list of OwnTask
  449. */
  450. private List<OwnTask> readTasksFromPreferences() {
  451. List<OwnTask> tasks = new ArrayList<>();
  452. try {
  453. String jsonString = PreferenceManager.getDefaultSharedPreferences(getContext()).getString(KEY_ALL_TASKS, "error");
  454. if(!"error".equalsIgnoreCase(jsonString)) {
  455. JSONArray jsonObjects = new JSONArray(jsonString);
  456. for (int i = 0; i<jsonObjects.length();i++) {
  457. tasks.add(new OwnTask(jsonObjects.getJSONObject(i)));
  458. if (BuildConfig.DEBUG) {
  459. Log.d(TAG, "Task loaded from shared preferences: " + tasks.get(i).toString());
  460. }
  461. Bitmap image = BitmapFactory.decodeFile(tasks.get(i).getPathImage() + File.separator + tasks.get(i).getId() + ".jpeg");
  462. if (BuildConfig.DEBUG) {
  463. Log.d(TAG, "Image loaded from shared preferences: " + image.getByteCount());
  464. }
  465. tasks.get(i).setImage(image);
  466. }
  467. }
  468. } catch (JSONException e) {
  469. if (BuildConfig.DEBUG) {
  470. Log.e(TAG, "Error while parsing tasks as jsonObjects from preferences");
  471. e.printStackTrace();
  472. }
  473. }
  474. return tasks;
  475. }
  476. @Override
  477. public void onMapReady(GoogleMap googleMap) { }
  478. }