package cgeo.geocaching; import cgeo.geocaching.activity.AbstractActivity; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.enumerations.CacheSize; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.enumerations.WaypointType; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.HumanDistance; import cgeo.geocaching.maps.CGeoMap; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import android.app.AlertDialog; import android.app.SearchManager; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageInfo; 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.util.Log; import android.view.ContextMenu; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.widget.RelativeLayout; import android.widget.TextView; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; public class cgeo extends AbstractActivity { private static final String SCAN_INTENT = "com.google.zxing.client.android.SCAN"; private static final int MENU_ABOUT = 0; private static final int MENU_HELPERS = 1; private static final int MENU_SETTINGS = 2; private static final int MENU_HISTORY = 3; private static final int MENU_SCAN = 4; private static final int SCAN_REQUEST_CODE = 1; private static final int MENU_OPEN_LIST = 100; public static final int SEARCH_REQUEST_CODE = 2; private int version = 0; private cgGeo geo = null; private UpdateLocationCallback geoUpdate = null; private TextView filterTitle = null; private boolean cleanupRunning = false; private int countBubbleCnt = 0; private Geopoint addCoords = null; private List
addresses = null; private boolean addressObtaining = false; private boolean initialized = false; private Handler updateUserInfoHandler = new Handler() { @Override public void handleMessage(Message msg) { TextView userInfoView = (TextView) findViewById(R.id.user_info); String userInfo = "geocaching.com" + Constants.SEPARATOR; if (cgBase.isActualLoginStatus()) { userInfo += cgBase.getActualUserName(); if (cgBase.getActualCachesFound() >= 0) { userInfo += " (" + String.valueOf(cgBase.getActualCachesFound()) + ")"; } userInfo += Constants.SEPARATOR; } userInfo += cgBase.getActualStatus(); userInfoView.setText(userInfo); } }; 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()); } if (geo != null) { addCoords = geo.coordsNow; } TextView navLocation = (TextView) findViewById(R.id.nav_location); navLocation.setText(addText.toString()); } } catch (Exception e) { // nothing } addresses = null; } }; 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(Settings.tag, "cgeo.fisrtLoginHander: " + e.toString()); } } }; public cgeo() { super("c:geo-main-screen"); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); app.setAction(null); app.cleanGeo(); app.cleanDir(); setContentView(R.layout.main); setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); // type to search try { PackageManager manager = this.getPackageManager(); PackageInfo info = manager.getPackageInfo(this.getPackageName(), 0); version = info.versionCode; Log.i(Settings.tag, "Starting " + info.packageName + " " + info.versionCode + " a.k.a " + info.versionName + "..."); info = null; manager = null; } catch (Exception e) { Log.i(Settings.tag, "No info."); } try { if (!Settings.isHelpShown()) { RelativeLayout helper = (RelativeLayout) findViewById(R.id.helper); if (helper != null) { helper.setVisibility(View.VISIBLE); helper.setClickable(true); helper.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { ActivityMixin.goManual(cgeo.this, "c:geo-intro"); view.setVisibility(View.GONE); } }); Settings.setHelpShown(); } } } catch (Exception e) { // nothing } init(); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); init(); } @Override public void onResume() { super.onResume(); updateUserInfoHandler.sendEmptyMessage(-1); init(); } @Override public void onDestroy() { initialized = false; app.showLoginToast = true; if (geo != null) { geo = app.removeGeo(); } super.onDestroy(); } @Override public void onStop() { initialized = false; if (geo != null) { geo = app.removeGeo(); } super.onStop(); } @Override public void onPause() { initialized = false; if (geo != null) { geo = app.removeGeo(); } super.onPause(); } @Override public boolean onCreateOptionsMenu(Menu menu) { menu.add(0, MENU_SETTINGS, 0, res.getString(R.string.menu_settings)).setIcon(android.R.drawable.ic_menu_preferences); menu.add(0, MENU_HISTORY, 0, res.getString(R.string.menu_history)).setIcon(android.R.drawable.ic_menu_recent_history); menu.add(0, MENU_HELPERS, 0, res.getString(R.string.menu_helpers)).setIcon(R.drawable.ic_menu_shopping); menu.add(0, MENU_SCAN, 0, res.getString(R.string.menu_scan_geo)).setIcon(R.drawable.ic_menu_barcode); menu.add(0, MENU_ABOUT, 0, res.getString(R.string.menu_about)).setIcon(android.R.drawable.ic_menu_info_details); return true; } @Override public boolean onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); MenuItem item = menu.findItem(MENU_SCAN); if (item != null) { item.setEnabled(isIntentAvailable(this, SCAN_INTENT)); } return true; } public static boolean isIntentAvailable(Context context, String intent) { final PackageManager packageManager = context.getPackageManager(); final List 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 MENU_ABOUT: showAbout(null); return true; case MENU_HELPERS: startActivity(new Intent(this, UsefulAppsActivity.class)); return true; case MENU_SETTINGS: startActivity(new Intent(this, cgeoinit.class)); return true; case MENU_HISTORY: cgeocaches.startActivityHistory(this); return true; case MENU_SCAN: Intent intent = new Intent(SCAN_INTENT); intent.putExtra("SCAN_MODE", "QR_CODE_MODE"); startActivityForResult(intent, SCAN_REQUEST_CODE); return true; default: break; } return false; } @Override public void onActivityResult(int requestCode, int resultCode, Intent intent) { if (requestCode == SCAN_REQUEST_CODE) { if (resultCode == RESULT_OK) { String scan = intent.getStringExtra("SCAN_RESULT"); if (StringUtils.isBlank(scan)) { return; } Intent searchIntent = new Intent(this, cgeoadvsearch.class); searchIntent.setAction(Intent.ACTION_SEARCH). putExtra(SearchManager.QUERY, scan). putExtra(cgeoadvsearch.EXTRAS_KEYWORDSEARCH, false); startActivityForResult(searchIntent, SEARCH_REQUEST_CODE); } else if (resultCode == RESULT_CANCELED) { // do nothing } } else if (requestCode == SEARCH_REQUEST_CODE) { // cgeoadvsearch 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(); } } } @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); // context menu for offline button if (v.getId() == R.id.search_offline) { List cacheLists = app.getLists(); int listCount = cacheLists.size(); menu.setHeaderTitle(res.getString(R.string.list_title)); for (int i = 0; i < listCount; i++) { StoredList list = cacheLists.get(i); menu.add(Menu.NONE, MENU_OPEN_LIST + list.id, Menu.NONE, list.getTitleAndCount()); } return; } // standard context menu menu.setHeaderTitle(res.getString(R.string.menu_filter)); //first add the most used types menu.add(1, 0, 0, CacheType.ALL.getL10n()); menu.add(1, 1, 0, CacheType.TRADITIONAL.getL10n()); menu.add(1, 2, 0, CacheType.MULTI.getL10n()); menu.add(1, 3, 0, CacheType.MYSTERY.getL10n()); // then add all other cache types sorted alphabetically List sorted = new ArrayList(); for (CacheType ct : CacheType.values()) { if (ct == CacheType.ALL || ct == CacheType.TRADITIONAL || ct == CacheType.MULTI || ct == CacheType.MYSTERY) { continue; } sorted.add(ct.getL10n()); } Collections.sort(sorted); for (String choice : sorted) { menu.add(1, menu.size(), 0, choice); } // mark current filter as checked menu.setGroupCheckable(1, true, true); boolean foundItem = false; int itemCount = menu.size(); String typeTitle = Settings.getCacheType().getL10n(); for (int i = 0; i < itemCount; i++) { if (menu.getItem(i).getTitle().equals(typeTitle)) { menu.getItem(i).setChecked(true); foundItem = true; break; } } if (!foundItem) { menu.getItem(0).setChecked(true); } } @Override public boolean onContextItemSelected(MenuItem item) { final int id = item.getItemId(); if (id == 0) { Settings.setCacheType(CacheType.ALL); setFilterTitle(); return true; } else if (id > MENU_OPEN_LIST) { int listId = id - MENU_OPEN_LIST; Settings.saveLastList(listId); cgeocaches.startActivityOffline(this); return true; } else if (id > 0) { final String itemTitle = item.getTitle().toString(); CacheType cacheType = CacheType.ALL; for (CacheType ct : CacheType.values()) { if (ct.getL10n().equalsIgnoreCase(itemTitle)) { cacheType = ct; break; } } Settings.setCacheType(cacheType); setFilterTitle(); return true; } return false; } private void setFilterTitle() { if (filterTitle == null) { filterTitle = (TextView) findViewById(R.id.filter_button_title); } filterTitle.setText(Settings.getCacheType().getL10n()); } private void init() { if (initialized) { return; } //TODO This is ugly fix for #486 bug should be reported to library if (Thread.currentThread().getContextClassLoader() == null) { Thread.currentThread().setContextClassLoader(new ClassLoader() { }); StringUtils.isNotBlank("haha"); } initialized = true; Settings.setLanguage(Settings.isUseEnglish()); /* * "update" the cache size/type. For a better performance * the resource strings are stored in the enum's. In case of a * locale change the resource strings don't get updated automatically. * That's why we have to do it on our own. */ for (CacheSize cacheSize : CacheSize.values()) { cacheSize.setL10n(); } for (CacheType cacheType : CacheType.values()) { cacheType.setL10n(); } for (LogType logType : LogType.values()) { logType.setL10n(); } for (WaypointType waypointType : WaypointType.values()) { waypointType.setL10n(); } Settings.getLogin(); if (app.firstRun) { (new firstLogin()).start(); } if (geo == null) { geoUpdate = new UpdateLocation(); geo = app.startGeo(geoUpdate); } final View findOnMap = findViewById(R.id.map); findOnMap.setClickable(true); findOnMap.setOnClickListener(new OnClickListener() { public void onClick(View v) { cgeoFindOnMap(v); } }); final View findByOffline = findViewById(R.id.search_offline); findByOffline.setClickable(true); findByOffline.setOnClickListener(new OnClickListener() { public void onClick(View v) { cgeoFindByOffline(v); } }); registerForContextMenu(findByOffline); final View advanced = findViewById(R.id.advanced_button); advanced.setClickable(true); advanced.setOnClickListener(new OnClickListener() { public void onClick(View v) { cgeoSearch(v); } }); final View any = findViewById(R.id.any_button); any.setClickable(true); any.setOnClickListener(new OnClickListener() { public void onClick(View v) { cgeoPoint(v); } }); final View filter = findViewById(R.id.filter_button); filter.setClickable(true); registerForContextMenu(filter); filter.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { openContextMenu(v); } }); updateCacheCounter(); setFilterTitle(); checkRestore(); (new cleanDatabase()).start(); } private void updateCacheCounter() { (new CountBubbleUpdateThread()).start(); } private void checkRestore() { if (!cgData.isNewlyCreatedDatebase() || null == cgData.isRestoreFile()) { 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() { public void onClick(DialogInterface dialog, int id) { dialog.dismiss(); cgData.resetNewlyCreatedDatabase(); app.restoreDatabase(cgeo.this); updateCacheCounter(); } }) .setNegativeButton(getString(android.R.string.no), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.cancel(); cgData.resetNewlyCreatedDatabase(); } }) .create() .show(); } private class UpdateLocation implements UpdateLocationCallback { private final View nearestView = findViewById(R.id.nearest); private final TextView navType = (TextView) findViewById(R.id.nav_type); private final TextView navAccuracy = (TextView) findViewById(R.id.nav_accuracy); private final TextView navSatellites = (TextView) findViewById(R.id.nav_satellites); private final TextView navLocation = (TextView) findViewById(R.id.nav_location); @Override public void updateLocation(cgGeo geo) { if (geo == null) { return; } try { if (geo.coordsNow != null) { if (!nearestView.isClickable()) { nearestView.setFocusable(true); nearestView.setClickable(true); nearestView.setOnClickListener(new OnClickListener() { public void onClick(View v) { cgeoFindNearest(v); } }); nearestView.setBackgroundResource(R.drawable.main_nearby); } String satellites = null; if (geo.satellitesFixed > 0) { satellites = res.getString(R.string.loc_sat) + ": " + geo.satellitesFixed + "/" + geo.satellitesVisible; } else if (geo.satellitesVisible >= 0) { satellites = res.getString(R.string.loc_sat) + ": 0/" + geo.satellitesVisible; } else { satellites = ""; } navSatellites.setText(satellites); navType.setText(res.getString(geo.locationProvider.resourceId)); if (geo.accuracyNow >= 0) { if (Settings.isUseMetricUnits()) { navAccuracy.setText("±" + Math.round(geo.accuracyNow) + " m"); } else { navAccuracy.setText("±" + Math.round(geo.accuracyNow * 3.2808399) + " ft"); } } else { navAccuracy.setText(null); } if (Settings.isShowAddress()) { if (addCoords == null) { navLocation.setText(res.getString(R.string.loc_no_addr)); } if (addCoords == null || (geo.coordsNow.distanceTo(addCoords) > 0.5 && !addressObtaining)) { (new ObtainAddressThread()).start(); } } else { if (geo.altitudeNow != null) { final String humanAlt = HumanDistance.getHumanDistance(geo.altitudeNow.floatValue() / 1000); navLocation.setText(geo.coordsNow + " | " + humanAlt); } else { navLocation.setText(geo.coordsNow.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(Settings.tag, "Failed to update location."); } } } /** * @param v * unused here but needed since this method is referenced from XML layout */ public void cgeoFindOnMap(View v) { findViewById(R.id.map).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 (geo == null || geo.coordsNow == null) { return; } findViewById(R.id.nearest).setPressed(true); cgeocaches.startActivityNearest(this, geo.coordsNow); } /** * @param v * unused here but needed since this method is referenced from XML layout */ public void cgeoFindByOffline(View v) { findViewById(R.id.search_offline).setPressed(true); cgeocaches.startActivityOffline(this); } /** * @param v * unused here but needed since this method is referenced from XML layout */ public void cgeoSearch(View v) { findViewById(R.id.advanced_button).setPressed(true); startActivity(new Intent(this, cgeoadvsearch.class)); } /** * @param v * unused here but needed since this method is referenced from XML layout */ public void cgeoPoint(View v) { findViewById(R.id.any_button).setPressed(true); startActivity(new Intent(this, cgeopoint.class)); } /** * @param v * unused here but needed since this method is referenced from XML layout */ public void cgeoFilter(View v) { findViewById(R.id.filter_button).setPressed(true); findViewById(R.id.filter_button).performClick(); } private class CountBubbleUpdateThread extends Thread { private Handler countBubbleHandler = new Handler() { private TextView countBubble = null; @Override public void handleMessage(Message msg) { try { if (countBubble == null) { countBubble = (TextView) findViewById(R.id.offline_count); } if (countBubbleCnt == 0) { countBubble.setVisibility(View.GONE); } else { countBubble.setText(Integer.toString(countBubbleCnt)); countBubble.bringToFront(); countBubble.setVisibility(View.VISIBLE); } } catch (Exception e) { Log.w(Settings.tag, "cgeo.countBubbleHander: " + e.toString()); } } }; @Override public void run() { if (app == null) { return; } int checks = 0; while (!app.storageStatus()) { try { wait(500); checks++; } catch (Exception e) { // nothing; } if (checks > 10) { return; } } countBubbleCnt = app.getAllStoredCachesCount(true, CacheType.ALL, null); countBubbleHandler.sendEmptyMessage(0); } } private class cleanDatabase extends Thread { @Override public void run() { if (app == null) { return; } if (cleanupRunning) { return; } boolean more = false; if (version != Settings.getVersion()) { Log.i(Settings.tag, "Initializing hard cleanup - version changed from " + Settings.getVersion() + " to " + version + "."); more = true; } cleanupRunning = true; app.cleanDatabase(more); cleanupRunning = false; if (version > 0) { Settings.setVersion(version); } } } private class firstLogin extends Thread { @Override public void run() { if (app == null) { return; } // login final StatusCode status = cgBase.login(); if (status == StatusCode.NO_ERROR) { app.firstRun = false; cgBase.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) { final Context context = cgeo.this; final Intent initIntent = new Intent(context, cgeoinit.class); context.startActivity(initIntent); } } } } private class ObtainAddressThread extends Thread { public ObtainAddressThread() { setPriority(Thread.MIN_PRIORITY); } @Override public void run() { if (geo == null) { return; } if (addressObtaining) { return; } addressObtaining = true; try { final Geocoder geocoder = new Geocoder(cgeo.this, Locale.getDefault()); addresses = geocoder.getFromLocation(geo.coordsNow.getLatitude(), geo.coordsNow.getLongitude(), 1); } catch (Exception e) { Log.i(Settings.tag, "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, cgeoabout.class)); } /** * @param view * unused here but needed since this method is referenced from XML layout */ public void goSearch(View view) { onSearchRequested(); } }