diff options
Diffstat (limited to 'main/src/cgeo/geocaching/MainActivity.java')
| -rw-r--r-- | main/src/cgeo/geocaching/MainActivity.java | 770 |
1 files changed, 770 insertions, 0 deletions
diff --git a/main/src/cgeo/geocaching/MainActivity.java b/main/src/cgeo/geocaching/MainActivity.java new file mode 100644 index 0000000..7579a1d --- /dev/null +++ b/main/src/cgeo/geocaching/MainActivity.java @@ -0,0 +1,770 @@ +package cgeo.geocaching; + +import butterknife.InjectView; +import butterknife.Views; + +import cgeo.geocaching.activity.AbstractActivity; +import cgeo.geocaching.connector.gc.Login; +import cgeo.geocaching.enumerations.CacheType; +import cgeo.geocaching.enumerations.StatusCode; +import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.geopoint.Units; +import cgeo.geocaching.maps.CGeoMap; +import cgeo.geocaching.ui.Formatter; +import cgeo.geocaching.utils.GeoDirHandler; +import cgeo.geocaching.utils.Log; +import cgeo.geocaching.utils.RunnableWithArgument; +import cgeo.geocaching.utils.Version; + +import com.google.zxing.integration.android.IntentIntegrator; +import com.google.zxing.integration.android.IntentResult; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; + +import android.app.AlertDialog; +import android.app.AlertDialog.Builder; +import android.app.SearchManager; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Configuration; +import android.location.Address; +import android.location.Geocoder; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.ImageView; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Locale; + +public class MainActivity extends AbstractActivity { + @InjectView(R.id.user_info) protected TextView userInfoView; + @InjectView(R.id.nav_satellites) protected TextView navSatellites; + @InjectView(R.id.filter_button_title)protected TextView filterTitle; + @InjectView(R.id.map) protected ImageView findOnMap; + @InjectView(R.id.search_offline) protected ImageView findByOffline; + @InjectView(R.id.advanced_button) protected ImageView advanced; + @InjectView(R.id.any_button) protected ImageView any; + @InjectView(R.id.filter_button) protected ImageView filter; + @InjectView(R.id.nearest) protected ImageView nearestView; + @InjectView(R.id.nav_type) protected TextView navType ; + @InjectView(R.id.nav_accuracy) protected TextView navAccuracy ; + @InjectView(R.id.nav_location) protected TextView navLocation ; + @InjectView(R.id.offline_count) protected TextView countBubble ; + + private static final String SCAN_INTENT = "com.google.zxing.client.android.SCAN"; + public static final int SEARCH_REQUEST_CODE = 2; + + private int version = 0; + private boolean cleanupRunning = false; + private int countBubbleCnt = 0; + private Geopoint addCoords = null; + private List<Address> addresses = null; + private boolean addressObtaining = false; + private boolean initialized = false; + + final private UpdateLocation locationUpdater = new UpdateLocation(); + + private Handler updateUserInfoHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + + StringBuilder userInfo = new StringBuilder("geocaching.com").append(Formatter.SEPARATOR); + if (Settings.isGCConnectorActive()) { + if (Login.isActualLoginStatus()) { + userInfo.append(Login.getActualUserName()); + if (Login.getActualCachesFound() >= 0) { + userInfo.append(" (").append(String.valueOf(Login.getActualCachesFound())).append(')'); + } + userInfo.append(Formatter.SEPARATOR); + } + userInfo.append(Login.getActualStatus()); + } + else { + userInfo.append("<disabled>"); // TODO this is just a quick fix. We need some better status implementation showing multiple connectors. + } + + userInfoView.setText(userInfo.toString()); + } + }; + + private Handler obtainAddressHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + try { + if (CollectionUtils.isNotEmpty(addresses)) { + final Address address = addresses.get(0); + final StringBuilder addText = new StringBuilder(); + + if (address.getCountryName() != null) { + addText.append(address.getCountryName()); + } + if (address.getLocality() != null) { + if (addText.length() > 0) { + addText.append(", "); + } + addText.append(address.getLocality()); + } else if (address.getAdminArea() != null) { + if (addText.length() > 0) { + addText.append(", "); + } + addText.append(address.getAdminArea()); + } + + addCoords = app.currentGeo().getCoords(); + + navLocation.setText(addText.toString()); + } + } catch (Exception e) { + // nothing + } + + addresses = null; + } + }; + + private class SatellitesHandler extends GeoDirHandler { + + private boolean gpsEnabled = false; + private int satellitesFixed = 0; + private int satellitesVisible = 0; + + @Override + public void updateGeoData(final IGeoData data) { + if (data.getGpsEnabled() == gpsEnabled && + data.getSatellitesFixed() == satellitesFixed && + data.getSatellitesVisible() == satellitesVisible) { + return; + } + gpsEnabled = data.getGpsEnabled(); + satellitesFixed = data.getSatellitesFixed(); + satellitesVisible = data.getSatellitesVisible(); + + if (gpsEnabled) { + if (satellitesFixed > 0) { + navSatellites.setText(res.getString(R.string.loc_sat) + ": " + satellitesFixed + '/' + satellitesVisible); + } else if (satellitesVisible >= 0) { + navSatellites.setText(res.getString(R.string.loc_sat) + ": 0/" + satellitesVisible); + } + } else { + navSatellites.setText(res.getString(R.string.loc_gps_disabled)); + } + } + + } + + private SatellitesHandler satellitesHandler = new SatellitesHandler(); + + private Handler firstLoginHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + try { + final StatusCode reason = (StatusCode) msg.obj; + + if (reason != null && reason != StatusCode.NO_ERROR) { //LoginFailed + showToast(res.getString(reason == StatusCode.MAINTENANCE ? reason.getErrorString() : R.string.err_login_failed_toast)); + } + } catch (Exception e) { + Log.w("cgeo.firstLoginHander", e); + } + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + // don't call the super implementation with the layout argument, as that would set the wrong theme + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + Views.inject(this); + + if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) { + // If we had been open already, start from the last used activity. + finish(); + return; + } + + setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); // type to search + + version = Version.getVersionCode(this); + Log.i("Starting " + getPackageName() + ' ' + version + " a.k.a " + Version.getVersionName(this)); + + init(); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + init(); + } + + @Override + public void onResume() { + super.onResume(); + locationUpdater.startGeo(); + satellitesHandler.startGeo(); + updateUserInfoHandler.sendEmptyMessage(-1); + init(); + } + + @Override + public void onDestroy() { + initialized = false; + app.showLoginToast = true; + + super.onDestroy(); + } + + @Override + public void onStop() { + initialized = false; + super.onStop(); + } + + @Override + public void onPause() { + initialized = false; + locationUpdater.stopGeo(); + satellitesHandler.stopGeo(); + super.onPause(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main_activity_options, menu); + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); + MenuItem item = menu.findItem(R.id.menu_scan); + if (item != null) { + item.setEnabled(isIntentAvailable(SCAN_INTENT)); + } + return true; + } + + public static boolean isIntentAvailable(String intent) { + final PackageManager packageManager = cgeoapplication.getInstance().getPackageManager(); + final List<ResolveInfo> list = packageManager.queryIntentActivities( + new Intent(intent), PackageManager.MATCH_DEFAULT_ONLY); + + return CollectionUtils.isNotEmpty(list); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + final int id = item.getItemId(); + switch (id) { + case R.id.menu_about: + showAbout(null); + return true; + case R.id.menu_helpers: + startActivity(new Intent(this, UsefulAppsActivity.class)); + return true; + case R.id.menu_settings: + startActivity(new Intent(this, SettingsActivity.class)); + return true; + case R.id.menu_history: + cgeocaches.startActivityHistory(this); + return true; + case R.id.menu_scan: + startScannerApplication(); + return true; + default: + return super.onOptionsItemSelected(item); + } + + } + + private void startScannerApplication() { + IntentIntegrator integrator = new IntentIntegrator(this); + integrator.initiateScan(IntentIntegrator.QR_CODE_TYPES); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent intent) { + IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent); + if (scanResult != null) { + String scan = scanResult.getContents(); + if (StringUtils.isBlank(scan)) { + return; + } + SearchActivity.startActivityScan(scan, this); + } else if (requestCode == SEARCH_REQUEST_CODE) { + // SearchActivity activity returned without making a search + if (resultCode == RESULT_CANCELED) { + String query = intent.getStringExtra(SearchManager.QUERY); + if (query == null) { + query = ""; + } + new AlertDialog.Builder(this) + .setMessage(res.getString(R.string.unknown_scan) + "\n\n" + query) + .setPositiveButton(getString(android.R.string.ok), null) + .create() + .show(); + } + } + } + + private void setFilterTitle() { + filterTitle.setText(Settings.getCacheType().getL10n()); + } + + private void init() { + if (initialized) { + return; + } + + initialized = true; + + Settings.setLanguage(Settings.isUseEnglish()); + Settings.getLogin(); + + if (app.firstRun) { + (new FirstLoginThread()).start(); + } + + findOnMap.setClickable(true); + findOnMap.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + cgeoFindOnMap(v); + } + }); + + findByOffline.setClickable(true); + findByOffline.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + cgeoFindByOffline(v); + } + }); + findByOffline.setOnLongClickListener(new View.OnLongClickListener() { + + @Override + public boolean onLongClick(View v) { + new StoredList.UserInterface(MainActivity.this).promptForListSelection(R.string.list_title, new RunnableWithArgument<Integer>() { + + @Override + public void run(Integer selectedListId) { + Settings.saveLastList(selectedListId); + cgeocaches.startActivityOffline(MainActivity.this); + } + }); + return true; + } + }); + findByOffline.setLongClickable(true); + + advanced.setClickable(true); + advanced.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + cgeoSearch(v); + } + }); + + any.setClickable(true); + any.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + cgeoPoint(v); + } + }); + + filter.setClickable(true); + filter.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + selectGlobalTypeFilter(); + } + }); + filter.setOnLongClickListener(new View.OnLongClickListener() { + + @Override + public boolean onLongClick(View v) { + selectGlobalTypeFilter(); + return true; + } + }); + + updateCacheCounter(); + + setFilterTitle(); + checkRestore(); + (new CleanDatabaseThread()).start(); + } + + protected void selectGlobalTypeFilter() { + final List<CacheType> cacheTypes = new ArrayList<CacheType>(); + + //first add the most used types + cacheTypes.add(CacheType.ALL); + cacheTypes.add(CacheType.TRADITIONAL); + cacheTypes.add(CacheType.MULTI); + cacheTypes.add(CacheType.MYSTERY); + + // then add all other cache types sorted alphabetically + List<CacheType> sorted = new ArrayList<CacheType>(); + sorted.addAll(Arrays.asList(CacheType.values())); + sorted.removeAll(cacheTypes); + + Collections.sort(sorted, new Comparator<CacheType>() { + + @Override + public int compare(CacheType left, CacheType right) { + return left.getL10n().compareToIgnoreCase(right.getL10n()); + } + }); + + cacheTypes.addAll(sorted); + + int checkedItem = cacheTypes.indexOf(Settings.getCacheType()); + if (checkedItem < 0) { + checkedItem = 0; + } + + String[] items = new String[cacheTypes.size()]; + for (int i = 0; i < cacheTypes.size(); i++) { + items[i] = cacheTypes.get(i).getL10n(); + } + + Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.menu_filter); + builder.setSingleChoiceItems(items, checkedItem, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int position) { + CacheType cacheType = cacheTypes.get(position); + Settings.setCacheType(cacheType); + setFilterTitle(); + dialog.dismiss(); + } + + }); + builder.create().show(); + } + + void updateCacheCounter() { + (new CountBubbleUpdateThread()).start(); + } + + private void checkRestore() { + if (!cgData.isNewlyCreatedDatebase() || null == cgData.getRestoreFile()) { + return; + } + new AlertDialog.Builder(this) + .setTitle(res.getString(R.string.init_backup_restore)) + .setMessage(res.getString(R.string.init_restore_confirm)) + .setCancelable(false) + .setPositiveButton(getString(android.R.string.yes), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + dialog.dismiss(); + cgData.resetNewlyCreatedDatabase(); + app.restoreDatabase(MainActivity.this); + } + }) + .setNegativeButton(getString(android.R.string.no), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + cgData.resetNewlyCreatedDatabase(); + } + }) + .create() + .show(); + } + + private class UpdateLocation extends GeoDirHandler { + + @Override + public void updateGeoData(final IGeoData geo) { + try { + if (geo.getCoords() != null) { + if (!nearestView.isClickable()) { + nearestView.setFocusable(true); + nearestView.setClickable(true); + nearestView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + cgeoFindNearest(v); + } + }); + nearestView.setBackgroundResource(R.drawable.main_nearby); + } + + navType.setText(res.getString(geo.getLocationProvider().resourceId)); + + if (geo.getAccuracy() >= 0) { + int speed = Math.round(geo.getSpeed()) * 60 * 60 / 1000; + navAccuracy.setText("±" + Units.getDistanceFromMeters(geo.getAccuracy()) + Formatter.SEPARATOR + Units.getSpeed(speed)); + } else { + navAccuracy.setText(null); + } + + if (Settings.isShowAddress()) { + if (addCoords == null) { + navLocation.setText(res.getString(R.string.loc_no_addr)); + } + if (addCoords == null || (geo.getCoords().distanceTo(addCoords) > 0.5 && !addressObtaining)) { + (new ObtainAddressThread()).start(); + } + } else { + if (geo.getAltitude() != 0.0) { + final String humanAlt = Units.getDistanceFromKilometers((float) geo.getAltitude() / 1000); + navLocation.setText(geo.getCoords() + " | " + humanAlt); + } else { + navLocation.setText(geo.getCoords().toString()); + } + } + } else { + if (nearestView.isClickable()) { + nearestView.setFocusable(false); + nearestView.setClickable(false); + nearestView.setOnClickListener(null); + nearestView.setBackgroundResource(R.drawable.main_nearby_disabled); + } + navType.setText(null); + navAccuracy.setText(null); + navLocation.setText(res.getString(R.string.loc_trying)); + } + } catch (Exception e) { + Log.w("Failed to update location."); + } + } + } + + /** + * @param v + * unused here but needed since this method is referenced from XML layout + */ + public void cgeoFindOnMap(View v) { + findOnMap.setPressed(true); + CGeoMap.startActivityLiveMap(this); + } + + /** + * @param v + * unused here but needed since this method is referenced from XML layout + */ + public void cgeoFindNearest(View v) { + if (app.currentGeo().getCoords() == null) { + return; + } + + nearestView.setPressed(true); + cgeocaches.startActivityNearest(this, app.currentGeo().getCoords()); + } + + /** + * @param v + * unused here but needed since this method is referenced from XML layout + */ + public void cgeoFindByOffline(View v) { + findByOffline.setPressed(true); + cgeocaches.startActivityOffline(this); + } + + /** + * @param v + * unused here but needed since this method is referenced from XML layout + */ + public void cgeoSearch(View v) { + advanced.setPressed(true); + startActivity(new Intent(this, SearchActivity.class)); + } + + /** + * @param v + * unused here but needed since this method is referenced from XML layout + */ + public void cgeoPoint(View v) { + any.setPressed(true); + startActivity(new Intent(this, NavigateAnyPointActivity.class)); + } + + /** + * @param v + * unused here but needed since this method is referenced from XML layout + */ + public void cgeoFilter(View v) { + filter.setPressed(true); + filter.performClick(); + } + + /** + * @param v + * unused here but needed since this method is referenced from XML layout + */ + public void cgeoNavSettings(View v) { + startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS)); + } + + private class CountBubbleUpdateThread extends Thread { + private Handler countBubbleHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + try { + if (countBubbleCnt == 0) { + countBubble.setVisibility(View.GONE); + } else { + countBubble.setText(Integer.toString(countBubbleCnt)); + countBubble.bringToFront(); + countBubble.setVisibility(View.VISIBLE); + } + } catch (Exception e) { + Log.w("cgeo.countBubbleHander", e); + } + } + }; + + @Override + public void run() { + if (app == null) { + return; + } + + int checks = 0; + while (!cgData.isInitialized()) { + try { + wait(500); + checks++; + } catch (Exception e) { + // nothing; + } + + if (checks > 10) { + return; + } + } + + countBubbleCnt = cgData.getAllCachesCount(); + + countBubbleHandler.sendEmptyMessage(0); + } + } + + private class CleanDatabaseThread extends Thread { + + @Override + public void run() { + if (app == null) { + return; + } + if (cleanupRunning) { + return; + } + + boolean more = false; + if (version != Settings.getVersion()) { + Log.i("Initializing hard cleanup - version changed from " + Settings.getVersion() + " to " + version + "."); + + more = true; + } + + cleanupRunning = true; + cgData.clean(more); + cleanupRunning = false; + + if (version > 0) { + Settings.setVersion(version); + } + } + } + + private class FirstLoginThread extends Thread { + + @Override + public void run() { + if (app == null) { + return; + } + + if (!Settings.isGCConnectorActive()) { + return; + } + + // login + final StatusCode status = Login.login(); + + if (status == StatusCode.NO_ERROR) { + app.firstRun = false; + Login.detectGcCustomDate(); + updateUserInfoHandler.sendEmptyMessage(-1); + } + + if (app.showLoginToast) { + firstLoginHandler.sendMessage(firstLoginHandler.obtainMessage(0, status)); + app.showLoginToast = false; + + // invoke settings activity to insert login details + if (status == StatusCode.NO_LOGIN_INFO_STORED) { + SettingsActivity.startActivity(MainActivity.this); + } + } + } + } + + private class ObtainAddressThread extends Thread { + + public ObtainAddressThread() { + setPriority(Thread.MIN_PRIORITY); + } + + @Override + public void run() { + if (addressObtaining) { + return; + } + addressObtaining = true; + + try { + final Geocoder geocoder = new Geocoder(MainActivity.this, Locale.getDefault()); + final Geopoint coords = app.currentGeo().getCoords(); + addresses = geocoder.getFromLocation(coords.getLatitude(), coords.getLongitude(), 1); + } catch (Exception e) { + Log.i("Failed to obtain address"); + } + + obtainAddressHandler.sendEmptyMessage(0); + + addressObtaining = false; + } + } + + /** + * @param view + * unused here but needed since this method is referenced from XML layout + */ + public void showAbout(View view) { + startActivity(new Intent(this, AboutActivity.class)); + } + + /** + * @param view + * unused here but needed since this method is referenced from XML layout + */ + public void goSearch(View view) { + onSearchRequested(); + } + +} |
