package cgeo.geocaching.utils; import cgeo.contacts.ContactsAddon; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.connector.IConnector; import cgeo.geocaching.connector.capability.ILogin; import cgeo.geocaching.connector.gc.GCConnector; import cgeo.geocaching.playservices.GooglePlayServices; import cgeo.geocaching.sensors.MagnetometerAndAccelerometerProvider; import cgeo.geocaching.sensors.OrientationProvider; import cgeo.geocaching.sensors.RotationProvider; import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.settings.HwAccel; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.storage.DataStore; import cgeo.geocaching.storage.LocalStorage; import android.Manifest; import android.content.Context; import android.content.pm.PackageManager; import android.os.Build; import android.os.Build.VERSION; import android.os.Environment; import android.support.annotation.NonNull; import android.support.v4.content.ContextCompat; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.Locale; import com.google.android.gms.common.GoogleApiAvailability; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; public final class SystemInformation { private SystemInformation() { // Do not instantiate } @NonNull public static String getSystemInformation(@NonNull final Context context) { final String usedDirectionSensor; if (Settings.useOrientationSensor(context)) { usedDirectionSensor = "orientation"; } else if (RotationProvider.hasRotationSensor(context)) { usedDirectionSensor = "rotation vector"; } else { usedDirectionSensor = "magnetometer & accelerometer"; } final StringBuilder body = new StringBuilder("--- System information ---") .append("\nDevice: ").append(Build.MODEL).append(" (").append(Build.PRODUCT).append(", ").append(Build.BRAND).append(')') .append("\nAndroid version: ").append(VERSION.RELEASE) .append("\nAndroid build: ").append(Build.DISPLAY) .append("\nc:geo version: ").append(Version.getVersionName(context)); appendGooglePlayServicesVersion(context, body); body .append("\nLow power mode: ").append(Settings.useLowPowerMode() ? "active" : "inactive") .append("\nCompass capabilities: ").append(Sensors.getInstance().hasCompassCapabilities() ? "yes" : "no") .append("\nRotation vector sensor: ").append(presence(RotationProvider.hasRotationSensor(context))) .append("\nOrientation sensor: ").append(presence(OrientationProvider.hasOrientationSensor(context))) .append("\nMagnetometer & Accelerometer sensor: ").append(presence(MagnetometerAndAccelerometerProvider.hasMagnetometerAndAccelerometerSensors(context))) .append("\nDirection sensor used: ").append(usedDirectionSensor) .append("\nHide own/found: ").append(Settings.isExcludeMyCaches()) .append("\nMap strategy: ").append(Settings.getLiveMapStrategy().toString().toLowerCase(Locale.getDefault())) .append("\nHW acceleration: ").append(Settings.useHardwareAcceleration() ? "enabled" : "disabled") .append(" (").append(Settings.useHardwareAcceleration() == HwAccel.hwAccelShouldBeEnabled() ? "default state" : "manually changed").append(')') .append("\nSystem language: ").append(Locale.getDefault()); if (Settings.useEnglish()) { body.append(" (c:geo forced to English)"); } body.append("\nSystem date format: ").append(Formatter.getShortDateFormat()) .append("\nDebug mode active: ").append(Settings.isDebug() ? "yes" : "no"); appendDirectory(body, "\nSystem internal c:geo dir: ", LocalStorage.getInternalCgeoDirectory()); appendDirectory(body, "\nUser storage c:geo dir: ", LocalStorage.getExternalPublicCgeoDirectory()); appendDirectory(body, "\nGeocache data: ", LocalStorage.getGeocacheDataDirectory()); appendDatabase(body); appendPermissions(context, body); appendConnectors(body); if (GCConnector.getInstance().isActive()) { body.append("\nGeocaching.com date format: ").append(Settings.getGcCustomDate()); } appendAddons(body); body.append("\n--- End of system information ---\n"); return body.toString(); } private static void appendDatabase(@NonNull final StringBuilder body) { final File dbFile = DataStore.databasePath(); body.append("\nDatabase: ").append(dbFile) .append(" (").append(Formatter.formatBytes(dbFile.length())).append(") on ") .append(Settings.isDbOnSDCard() ? "user storage" : "system internal storage"); } private static void appendDirectory(@NonNull final StringBuilder body, @NonNull final String label, @NonNull final File directory) { body.append(label).append(directory).append(" (").append(Formatter.formatBytes(FileUtils.getFreeDiskSpace(directory))).append(" free)"); try { if (directory.getAbsolutePath().startsWith(LocalStorage.getInternalCgeoDirectory().getAbsolutePath())) { body.append(" internal"); } else if (VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { body.append(" external"); } else if (Environment.isExternalStorageRemovable(directory)) { body.append(" external removable"); } else { body.append(" external non-removable"); } } catch (final IllegalArgumentException ignored) { // thrown if the directory isn't pointing to an external storage body.append(" internal"); } } private static void appendConnectors(@NonNull final StringBuilder body) { final StringBuilder connectors = new StringBuilder(128); int connectorCount = 0; for (final IConnector connector : ConnectorFactory.getConnectors()) { if (connector.isActive()) { connectorCount++; connectors.append("\n ").append(connector.getName()); if (connector instanceof ILogin) { final ILogin login = (ILogin) connector; connectors.append(": ").append(login.isLoggedIn() ? "Logged in" : "Not logged in") .append(" (").append(login.getLoginStatusString()).append(')'); if (login.getName().equals("geocaching.com") && login.isLoggedIn()) { connectors.append(" / ").append(Settings.getGCMemberStatus()); } } } } body.append("\nGeocaching sites enabled:").append(connectorCount > 0 ? connectors : " None"); } private static void appendAddons(final StringBuilder body) { final List<String> addons = new ArrayList<>(2); if (ContactsAddon.isAvailable()) { addons.add("contacts"); } body.append("\nInstalled c:geo plugins: "); body.append(CollectionUtils.isNotEmpty(addons) ? StringUtils.join(addons, ", ") : " none"); } private static String presence(final boolean present) { return present ? "present" : "absent"; } private static void appendPermission(final Context context, final StringBuilder body, final String name, final String permission) { body.append('\n').append(name).append(" permission: ").append(ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED ? "granted" : "DENIED"); } private static void appendPermissions(final Context context, final StringBuilder body) { appendPermission(context, body, "Fine location", Manifest.permission.ACCESS_FINE_LOCATION); appendPermission(context, body, "Write external storage", Manifest.permission.WRITE_EXTERNAL_STORAGE); } private static void appendGooglePlayServicesVersion(final Context context, final StringBuilder body) { final boolean googlePlayServicesAvailable = GooglePlayServices.isAvailable(); body.append("\nGoogle Play services: ").append(googlePlayServicesAvailable ? (Settings.useGooglePlayServices() ? "enabled" : "disabled") : "unavailable"); if (googlePlayServicesAvailable) { body.append(" - "); try { body.append(StringUtils.defaultIfBlank(context.getPackageManager().getPackageInfo(GoogleApiAvailability.GOOGLE_PLAY_SERVICES_PACKAGE, 0).versionName, "unknown version")); } catch (final PackageManager.NameNotFoundException e) { body.append("unretrievable version (").append(e.getMessage()).append(')'); } } } }