Java tutorial
/* Copyright 2016 Alan Sparrow This file is part of Wifefinder. Wifefind is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. */ package uk.org.freeflight.wifefinder; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.database.Cursor; import android.location.Location; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; import android.os.Bundle; import android.os.CountDownTimer; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v4.view.MenuItemCompat; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.SimpleCursorAdapter; import android.widget.Spinner; import android.widget.TextView; import java.text.SimpleDateFormat; public class MainActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener { final static int PERMISSION_REQUEST_LOCATION = 1; final static String AIRCRAFT_ID_PREF = "AIRCRAFT_ID"; final static String LAT_PREF = "LAT_PREF"; final static String LON_PREF = "LON_PREF"; final static String LOC_UPDATE_PREF = "LOC_UPDATE_PREF"; final static int LOCATION_TIMEOUT = 30000; final static double LOCATION_ACCURACY = 25.0; // Maximum allowable age, in seconds final static long MAX_AGE = 12 * 3600; WifeFinderDb db; SimpleCursorAdapter aircraftCursorAdapter; long id; WifeFinderLocationService locationService; CountDownTimer locationTimer; AircraftPosition aircraftPosition; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Get previously used aircraft id id = getPreferences(MODE_PRIVATE).getLong(AIRCRAFT_ID_PREF, -1); // Setup App Bar Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_main); setSupportActionBar(toolbar); toolbar.setTitle(R.string.app_title); db = new WifeFinderDb(this); aircraftCursorAdapter = null; // Own location locationService = new WifeFinderLocationService(this); locationService.setLatitude(getPreferences(MODE_PRIVATE).getFloat(LAT_PREF, 0)); locationService.setLongitude(getPreferences(MODE_PRIVATE).getFloat(LON_PREF, 0)); // Aircraft position aircraftPosition = new AircraftPosition(); // Own location locationTimer locationTimer = new CountDownTimer(LOCATION_TIMEOUT, 1000) { public void onTick(long msRemaining) { if (locationService.hasAccuracy() && (locationService.getAccuracy() < LOCATION_ACCURACY)) { stopLocationUpdate(); } } public void onFinish() { stopLocationUpdate(); if (!locationService.hasAccuracy()) { new AlertDialog.Builder(MainActivity.this).setTitle(R.string.no_location_title) .setMessage(R.string.no_location_msg).setIcon(android.R.drawable.ic_dialog_info) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { } }).show(); } } }; } @Override public boolean onCreateOptionsMenu(Menu menu) { // Create cursor adaptor for the spinner control Cursor cursor = db.makeCursor(); String[] from = { WifeFinderDb.DbContract.AircraftTable.COLUMN_NAME_AIRCRAFT_REG }; int[] to = { android.R.id.text1 }; aircraftCursorAdapter = new SimpleCursorAdapter(this, R.layout.spinner_item, cursor, from, to, 0); aircraftCursorAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); // Spinner setup getMenuInflater().inflate(R.menu.menu_main, menu); MenuItem item = menu.findItem(R.id.aircraft_spinner); Spinner spinner = (Spinner) MenuItemCompat.getActionView(item); spinner.setAdapter(aircraftCursorAdapter); setSpinnerItemById(spinner, id); spinner.setOnItemSelectedListener(this); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_update: startPositionUpdate(); return true; case R.id.action_aircraft_list: Intent intent = new Intent(this, AircraftListActivity.class); startActivity(intent); return true; case R.id.action_update_location: startLocationUpdate(); return true; case R.id.action_show_map: showMap(); return true; default: return super.onOptionsItemSelected(item); } } @Override public void onItemSelected(AdapterView<?> parent, View view, int pos, long _id) { id = _id; } @Override public void onNothingSelected(AdapterView<?> parent) { id = -1; } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { switch (requestCode) { case PERMISSION_REQUEST_LOCATION: { if (grantResults.length != 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { startLocationUpdate(); } else { new AlertDialog.Builder(this).setTitle("ERROR") .setMessage("Sorry, Wife Finder doesn't work without access to ownLocation") .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { finish(); } }).setIcon(android.R.drawable.ic_dialog_alert).show(); } } } } @Override protected void onResume() { super.onResume(); if (aircraftCursorAdapter != null) { Cursor newCursor = db.makeCursor(); Cursor oldCursor = aircraftCursorAdapter.swapCursor(newCursor); oldCursor.close(); } if (getPreferences(MODE_PRIVATE).getBoolean(LOC_UPDATE_PREF, false)) { startLocationUpdate(); } } @Override protected void onPause() { getPreferences(MODE_PRIVATE).edit().putLong(AIRCRAFT_ID_PREF, id) .putBoolean(LOC_UPDATE_PREF, locationService.isRunning()) .putFloat(LON_PREF, (float) locationService.getLongitude()) .putFloat(LAT_PREF, (float) locationService.getLatitude()).apply(); stopLocationUpdate(); super.onPause(); } void setSpinnerItemById(Spinner spinner, long _id) { int spinnerCount = spinner.getCount(); for (int i = 0; i < spinnerCount; i++) { Cursor value = (Cursor) spinner.getItemAtPosition(i); long id = value.getLong(value.getColumnIndex("_id")); if (id == _id) { spinner.setSelection(i); } } } void startLocationUpdate() { if (locationService.isRunning()) return; if (!locationService.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)) { ActivityCompat.requestPermissions(this, new String[] { android.Manifest.permission.ACCESS_FINE_LOCATION }, PERMISSION_REQUEST_LOCATION); } else { locationService.start(); locationTimer.start(); } } void stopLocationUpdate() { locationTimer.cancel(); locationService.stop(); updateDisplay(); } void startPositionUpdate() { ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); if (networkInfo != null && networkInfo.isConnected()) { setDisplay(getString(R.string.updating_text), "", "", ""); String aircraft_id = db.getAircraftId(id); String id_type = db.getAircraftIdType(id); new PositionUpdateTask(this, MAX_AGE).execute(id_type.toLowerCase() + ":" + aircraft_id.toUpperCase()); } else { new AlertDialog.Builder(this).setTitle("Network Error") .setMessage("Sorry, network connection is unavailable") .setIcon(android.R.drawable.ic_dialog_alert) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { } }).show(); } } void setPosition(AircraftPosition pos) { aircraftPosition = pos; updateDisplay(); } void updateDisplay() { if (aircraftPosition.isValid) { Location p = new Location(""); p.setLongitude(aircraftPosition.longitude); p.setLatitude(aircraftPosition.latitude); // Range String range_str = String.format("%.1f km", locationService.distanceTo(p) / 1000); // Bearing double bearing = locationService.bearingTo(p); if (bearing < 0) { bearing += 360; } String bearing_str = String.format("%.0f", bearing); // Altitude String altitude_str = String.format("%d ft", (int) (aircraftPosition.altitude * 3.28)); // Time SimpleDateFormat outDateFormat = new SimpleDateFormat("HH:mm:ss"); String time_str = outDateFormat.format(aircraftPosition.time); setDisplay(range_str, bearing_str, altitude_str, time_str); } else { setDisplay(getString(R.string.not_found_text), "", "", ""); } } void setDisplay(String range, String bearing, String altitude, String time) { TextView tv_range = (TextView) findViewById(R.id.textView_range); TextView tv_bearing = (TextView) findViewById(R.id.textView_bearing); TextView tv_altitude = (TextView) findViewById(R.id.textView_altitude); TextView tv_time = (TextView) findViewById(R.id.textView_time); tv_range.setText(range); tv_bearing.setText(bearing); tv_altitude.setText(altitude); tv_time.setText(time); } void showMap() { // Display labelled marker at aircraft position String uri_str = String.format("geo:%f,%f?z=8&q=%f,%f(%s)", aircraftPosition.latitude, aircraftPosition.longitude, aircraftPosition.latitude, aircraftPosition.longitude, Uri.encode(db.getAircraftReg((id)))); Uri gmmIntentUri = Uri.parse(uri_str); Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri); if (mapIntent.resolveActivity(getPackageManager()) != null) { startActivity(mapIntent); } } }