├── .gitignore ├── .idea └── vcs.xml ├── CHANGE_LOG.md ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── devicestats │ │ └── android │ │ ├── AccessibilityChecksTestRunner.kt │ │ └── DeviceStatsAccessibilityTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── ic_launcher-playstore.png │ ├── java │ │ └── com │ │ │ └── devicestats │ │ │ └── android │ │ │ ├── DeviceStats.java │ │ │ ├── DeviceStatsAdapter.java │ │ │ ├── DimensionsFragment.java │ │ │ ├── MainActivity.java │ │ │ ├── StatsFragment.java │ │ │ ├── StatsGatherer.java │ │ │ └── Utils.java │ └── res │ │ ├── drawable-hdpi │ │ ├── arrow_two_head_horiz.9.png │ │ ├── arrow_two_head_vert.9.png │ │ ├── ic_cancel_red_24dp.png │ │ ├── ic_check_circle_green_24dp.png │ │ ├── ic_developer_mode_white_24dp.png │ │ ├── ic_lock_white_24dp.png │ │ ├── ic_perm_device_information_white_24dp.png │ │ ├── ic_phonelink_lock_white_24dp.png │ │ ├── ic_share_white_24dp.png │ │ ├── ic_speaker_phone_white_24dp.png │ │ └── ic_vpn_key_white_24dp.png │ │ ├── drawable-mdpi │ │ ├── arrow_two_head_horiz.9.png │ │ ├── arrow_two_head_vert.9.png │ │ ├── ic_cancel_red_24dp.png │ │ ├── ic_check_circle_green_24dp.png │ │ ├── ic_developer_mode_white_24dp.png │ │ ├── ic_lock_white_24dp.png │ │ ├── ic_perm_device_information_white_24dp.png │ │ ├── ic_phonelink_lock_white_24dp.png │ │ ├── ic_share_white_24dp.png │ │ ├── ic_speaker_phone_white_24dp.png │ │ └── ic_vpn_key_white_24dp.png │ │ ├── drawable-xhdpi │ │ ├── arrow_two_head_horiz.9.png │ │ ├── arrow_two_head_vert.9.png │ │ ├── ic_cancel_red_24dp.png │ │ ├── ic_check_circle_green_24dp.png │ │ ├── ic_developer_mode_white_24dp.png │ │ ├── ic_lock_white_24dp.png │ │ ├── ic_perm_device_information_white_24dp.png │ │ ├── ic_phonelink_lock_white_24dp.png │ │ ├── ic_share_white_24dp.png │ │ ├── ic_speaker_phone_white_24dp.png │ │ └── ic_vpn_key_white_24dp.png │ │ ├── drawable-xxhdpi │ │ ├── arrow_two_head_horiz.9.png │ │ ├── arrow_two_head_vert.9.png │ │ ├── ic_cancel_red_24dp.png │ │ ├── ic_check_circle_green_24dp.png │ │ ├── ic_developer_mode_white_24dp.png │ │ ├── ic_lock_white_24dp.png │ │ ├── ic_perm_device_information_white_24dp.png │ │ ├── ic_phonelink_lock_white_24dp.png │ │ ├── ic_share_white_24dp.png │ │ ├── ic_speaker_phone_white_24dp.png │ │ └── ic_vpn_key_white_24dp.png │ │ ├── drawable-xxxhdpi │ │ ├── arrow_two_head_horiz.9.png │ │ ├── arrow_two_head_vert.9.png │ │ ├── ic_cancel_red_24dp.png │ │ ├── ic_check_circle_green_24dp.png │ │ ├── ic_developer_mode_white_24dp.png │ │ ├── ic_lock_white_24dp.png │ │ ├── ic_perm_device_information_white_24dp.png │ │ ├── ic_phonelink_lock_white_24dp.png │ │ ├── ic_share_white_24dp.png │ │ ├── ic_speaker_phone_white_24dp.png │ │ └── ic_vpn_key_white_24dp.png │ │ ├── drawable │ │ ├── card_divider.xml │ │ └── ic_launcher_foreground.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── content_main.xml │ │ ├── element_card_double_column_row.xml │ │ ├── element_card_single_column_row.xml │ │ ├── element_card_view.xml │ │ ├── fragment_dimensions.xml │ │ └── fragment_stats.xml │ │ ├── menu │ │ └── default_menu.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher_round.png │ │ ├── values-hdpi │ │ └── dpiclass.xml │ │ ├── values-large │ │ ├── dimen.xml │ │ ├── screenclass.xml │ │ └── style.xml │ │ ├── values-ldpi │ │ └── dpiclass.xml │ │ ├── values-mdpi │ │ └── dpiclass.xml │ │ ├── values-normal │ │ └── screenclass.xml │ │ ├── values-small │ │ └── screenclass.xml │ │ ├── values-sw320dp │ │ └── screen_width_class.xml │ │ ├── values-sw600dp │ │ └── screen_width_class.xml │ │ ├── values-sw720dp │ │ └── screen_width_class.xml │ │ ├── values-tvdpi │ │ └── dpiclass.xml │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ ├── values-xhdpi │ │ └── dpiclass.xml │ │ ├── values-xlarge │ │ └── screenclass.xml │ │ ├── values-xxhdpi │ │ └── dpiclass.xml │ │ ├── values-xxxhdpi │ │ └── dpiclass.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── ic_launcher_background.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── devicestats │ └── android │ └── ExampleUnitTest.java ├── art ├── device-stats-feature-graphic.png ├── device-stats-feature-graphic.psd ├── screen_shot_algorithms.png ├── screen_shot_device_stats.png ├── screen_shot_features.png ├── screen_shot_home.png ├── screen_shot_home_with_5xframe.png ├── screen_shot_protocals.png └── web-hi-res.png ├── build.gradle ├── dependencies.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # java dumps during errors 2 | *.hprof 3 | 4 | /.idea/* 5 | /.idea/** 6 | *.iml 7 | .DS_Store 8 | 9 | # Built application files 10 | *.apk 11 | *.ap_ 12 | 13 | # Files for the Dalvik VM 14 | *.dex 15 | 16 | # Java class files 17 | *.class 18 | 19 | # Generated files 20 | bin/ 21 | gen/ 22 | 23 | # Gradle files 24 | .gradle/ 25 | build/ 26 | */build/** 27 | 28 | # Local configuration file (sdk path, etc) 29 | local.properties 30 | 31 | 32 | # Log Files 33 | *.log 34 | 35 | # memory dumps 36 | captures/ 37 | 38 | .idea/instapk.xml 39 | instapk.log* 40 | .externalNativeBuild 41 | */workspace.xml 42 | /firebase-distribution-release-notes.txt 43 | /firebase-adminsdk-service-account.json 44 | /google-api-service-account-details.json 45 | 46 | #Static analysis configuration files 47 | /static-analysis/detekt/detekt-config.yml 48 | /static-analysis/ktlint/.editorconfig 49 | 50 | # Keep 51 | # Uncomment if we need to update the codestyle 52 | # !.idea/codeStyles 53 | !.idea/inspectionProfiles 54 | !.idea/vcs.xml 55 | 56 | # Don't push these files in remote. They're to be encrypted. 57 | team-props/secure/* 58 | !team-props/secure/*.gpg 59 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /CHANGE_LOG.md: -------------------------------------------------------------------------------- 1 | # 3.0 2 | 3 | * Modernisation of dependencies and plugins i.e Switch to AndroidX 4 | * Linking to CI 5 | * Adding sample unit and instrumentation tests 6 | 7 | # 2.0 8 | 9 | * UI revamp thanks to Andy.B 10 | 11 | # 1.0 12 | 13 | Initial version. An App that reads and displays data about the device to aid testing. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Device stats app 2 | 3 | For app developers and testers to quickly and easily display and share device information and statistics. Simple enough for the client to use ;) 4 | 5 | Created initially as I was looking into some security hardening techniques and found some older Android devices and more recent Chinese devices didn't support the crypto algorithms and SSL Cipher suites I was expecting. 6 | 7 | 8 | Available on [Google play](https://play.google.com/store/apps/details?id=com.devicestats.android) 9 | 10 | 11 | 12 | 13 | ##Credits 14 | 15 | * [Andy.B](https://github.com/andyb129) for a UI overhaul from 1.0 to 1.1 16 | * Some if the device dimensions resource files and code are from https://bitbucket.org/hvisser/sizemeup - thanks to Hugo for that! 17 | 18 | ##Licence 19 | 20 | Copyright (c) 2015 Scott Alexander-Bown 21 | 22 | Licensed under the Apache License, Version 2.0 (the "License"); 23 | you may not use this file except in compliance with the License. 24 | You may obtain a copy of the License at 25 | 26 | http://www.apache.org/licenses/LICENSE-2.0 27 | 28 | Unless required by applicable law or agreed to in writing, software 29 | distributed under the License is distributed on an "AS IS" BASIS, 30 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 31 | See the License for the specific language governing permissions and 32 | limitations under the License. -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply from: '../dependencies.gradle' 4 | 5 | 6 | android { 7 | compileSdkVersion 30 8 | buildToolsVersion "30.0.3" 9 | defaultConfig { 10 | applicationId "com.devicestats.android" 11 | minSdkVersion 15 12 | targetSdkVersion 30 13 | versionCode 8 14 | versionName "3.0" 15 | 16 | archivesBaseName = "DeviceStats-$versionName-[$versionCode]" 17 | 18 | // this enabled the AccessibilityChecks once for all Instrumentation Tests 19 | testInstrumentationRunner "com.devicestats.android.AccessibilityChecksTestRunner" 20 | } 21 | 22 | compileOptions { 23 | // coreLibraryDesugaringEnabled = true 24 | sourceCompatibility javaCompatibilityVersion 25 | targetCompatibility javaCompatibilityVersion 26 | } 27 | 28 | kotlinOptions { 29 | jvmTarget = rootProject.ext.javaCompatibilityVersion 30 | } 31 | 32 | lintOptions { 33 | checkReleaseBuilds false 34 | abortOnError false 35 | } 36 | 37 | 38 | //check if the keystore details are defined in gradle.properties (this is so the key is not in github) 39 | if (project.hasProperty("DEVICE_STATS_FILE")) { 40 | signingConfigs { 41 | //from gradle.properties 42 | release { 43 | storeFile file(DEVICE_STATS_FILE) 44 | keyAlias DEVICE_STATS_KEY 45 | storePassword DEVICE_STATS_STORE_PASS 46 | keyPassword DEVICE_STATS_KEY_PASS 47 | } 48 | } 49 | } 50 | 51 | buildTypes { 52 | debug { 53 | debuggable true 54 | versionNameSuffix 'debug' 55 | } 56 | release { 57 | if (project.hasProperty("DEVICE_STATS_FILE")) { 58 | signingConfig = signingConfigs.release 59 | } 60 | minifyEnabled false 61 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 62 | } 63 | } 64 | } 65 | 66 | dependencies { 67 | 68 | implementation "com.google.android.material:material:$materialLibraryVersion" 69 | implementation "androidx.cardview:cardview:$cardviewVersion" 70 | implementation "androidx.coordinatorlayout:coordinatorlayout:$coordinatorlayoutVersion" 71 | 72 | //coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:$codeDesugaringLibraryVersion") 73 | 74 | testImplementation "junit:junit:$junitVersion" 75 | 76 | androidTestImplementation "junit:junit:$junitVersion" 77 | androidTestImplementation "androidx.annotation:annotation:$annotationVersion" 78 | androidTestImplementation "androidx.test.ext:junit:$androidXTestJunitVersion" 79 | androidTestImplementation "androidx.test:rules:$androidXTestVersion" 80 | androidTestImplementation "androidx.test:runner:$androidXTestVersion" 81 | androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion" 82 | androidTestImplementation "androidx.test.espresso:espresso-contrib:$espressoVersion" 83 | androidTestImplementation "androidx.test.espresso:espresso-intents:$espressoVersion" 84 | androidTestImplementation "androidx.test.espresso:espresso-idling-resource:$espressoVersion" 85 | androidTestImplementation "androidx.test.espresso:espresso-accessibility:$espressoVersion" 86 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion" 87 | 88 | } 89 | repositories { 90 | mavenCentral() 91 | } 92 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\1.docs\2.android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/devicestats/android/AccessibilityChecksTestRunner.kt: -------------------------------------------------------------------------------- 1 | package com.devicestats.android 2 | 3 | import androidx.test.espresso.accessibility.AccessibilityChecks 4 | import androidx.test.runner.AndroidJUnitRunner 5 | 6 | class AccessibilityChecksTestRunner : AndroidJUnitRunner() { 7 | 8 | init { 9 | AccessibilityChecks.enable().setRunChecksFromRootView(true) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/devicestats/android/DeviceStatsAccessibilityTest.kt: -------------------------------------------------------------------------------- 1 | package com.devicestats.android 2 | 3 | import androidx.test.espresso.Espresso 4 | import androidx.test.espresso.action.ViewActions 5 | import androidx.test.espresso.matcher.ViewMatchers.withId 6 | import androidx.test.ext.junit.runners.AndroidJUnit4 7 | import androidx.test.rule.ActivityTestRule 8 | import org.junit.Rule 9 | import org.junit.Test 10 | import org.junit.runner.RunWith 11 | 12 | @RunWith(AndroidJUnit4::class) 13 | class DeviceStatsAccessibilityTest { 14 | 15 | @get:Rule 16 | val activityTestRule = ActivityTestRule(MainActivity::class.java, true, true) 17 | 18 | @Test 19 | fun testAccessibility() { 20 | Espresso.onView(withId(R.id.viewpager)).perform(ViewActions.click()) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/com/devicestats/android/DeviceStats.java: -------------------------------------------------------------------------------- 1 | package com.devicestats.android; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import java.util.LinkedHashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | /** 11 | * Created by 1andbarb on 12/01/2016. 12 | */ 13 | public class DeviceStats implements Parcelable { 14 | 15 | public static final String HAS_HARDWARE = "Yes"; 16 | public static final String NO_HARDWARE = "No"; 17 | 18 | private String date; 19 | private String timeZone; 20 | 21 | private String manufacturer; 22 | private String model; 23 | private String product; 24 | private String apiLevel; 25 | private String release; 26 | 27 | private String brand; 28 | private String imei; 29 | private String serialNumber; 30 | private String secureId; 31 | 32 | private boolean hasNFC; 33 | private boolean hasBluetooth; 34 | private boolean hasAccel; 35 | private boolean hasBarom; 36 | private boolean hasCompass; 37 | private boolean hasGyro; 38 | private boolean hasCamera; 39 | private boolean hasFlash; 40 | private boolean hasCameraFront; 41 | private boolean hasGps; 42 | private boolean hasNetworkLoc; 43 | 44 | private LinkedHashMap deviceInfoLabelMap; 45 | private LinkedHashMap hardwareLabelMap; 46 | private List ciphersList; 47 | private List sslCiphersList; 48 | private List sslCiphersProtocolsList; 49 | private List protocolsList; 50 | private List algorithmsList; 51 | private List providerList; 52 | private String[] protocolsDefault; 53 | private String[] cipherSuitesDefault; 54 | private String[] protocolsSupport; 55 | private String[] cipherSuitesSupport; 56 | private Map> cryptoAlgorithmsAesMap; 57 | private Map> cryptoAlgorithmsEcMap; 58 | private int density; 59 | private int widthDpi; 60 | private int heightDpi; 61 | private int width; 62 | private int height; 63 | private String sizeQualifier; 64 | private String dpiQualifier; 65 | private String screenClass; 66 | 67 | public interface StatsLabels 68 | { 69 | String DATE_LABEL = "TIME"; 70 | String TIME_ZONE_LABEL = "TIMEZONE"; 71 | String MANUFACTURER_LABEL = "MANUFACTURER"; 72 | String MODEL_LABEL = "MODEL"; 73 | String API_LEVEL_LABEL = "API LEVEL"; 74 | String RELEASE_LABEL = "RELEASE"; 75 | String BRAND_LABEL = "BRAND"; 76 | String IEMI_LABEL = "IEMI"; 77 | String SERIAL_LABEL = "SERIAL NO"; 78 | String SECURE_ID_LABEL = "SECURE ID"; 79 | 80 | String IS_NFC_LABEL = "NFC"; 81 | String IS_BLUETOOTH_LABEL = "BLUETOOTH"; 82 | String IS_ACCELEROMETER_LABEL = "ACCELEROMETER"; 83 | String IS_BAROMETER_LABEL = "BAROMETER"; 84 | String IS_COMPASS_LABEL = "COMPASS"; 85 | String IS_GYROSCOPE_LABEL = "GYROSCOPE"; 86 | String IS_CAMERA_LABEL = "CAMERA"; 87 | String IS_FLASH_LABEL = "FLASH"; 88 | String IS_CAMERA_FRONT_LABEL = "CAMERA FRONT"; 89 | String IS_GPS_LABEL = "GPS"; 90 | String IS_NETWORK_LOCATION_LABEL = "NETWORK LOCATION"; 91 | } 92 | 93 | // convenience methods to set data 94 | 95 | public void setScreenInfo(int density, int widthDpi, int heightDpi, int width, int height, 96 | String sizeQualifier, String dpiQualifier, String screenClass) { 97 | 98 | this.density = density; 99 | this.widthDpi = widthDpi; 100 | this.heightDpi = heightDpi; 101 | this.width = width; 102 | this.height = height; 103 | this.sizeQualifier = sizeQualifier; 104 | this.dpiQualifier = dpiQualifier; 105 | this.screenClass = screenClass; 106 | } 107 | 108 | public void setDeviceInfo(String manufacturer, String model, String product, String apiLevel, 109 | String release, String date, String displayName) { 110 | this.manufacturer = manufacturer; 111 | this.model = model; 112 | this.product = product; 113 | this.apiLevel = apiLevel; 114 | this.release = release; 115 | this.date = date; 116 | } 117 | 118 | public void setDeviceId(String serial, String secureId) { 119 | this.serialNumber = serial; 120 | this.secureId = secureId; 121 | } 122 | 123 | public void setHardwareInfo(boolean hasNFC, boolean hasBluetooth, boolean hasAccel, 124 | boolean hasBarom, boolean hasCompass, boolean hasGyro, boolean hasCamera, 125 | boolean hasFlash, boolean hasCameraFront, boolean hasGps, boolean hasNetworkLoc) { 126 | 127 | this.hasNFC = hasNFC; 128 | this.hasBluetooth = hasBluetooth; 129 | this.hasAccel = hasAccel; 130 | this.hasBarom = hasBarom; 131 | this.hasCompass = hasCompass; 132 | this.hasGyro = hasGyro; 133 | this.hasCamera = hasCamera; 134 | this.hasFlash = hasFlash; 135 | this.hasCameraFront = hasCameraFront; 136 | this.hasGps = hasGps; 137 | this.hasNetworkLoc = hasNetworkLoc; 138 | } 139 | 140 | public void createDeviceInfoLabelMap() 141 | { 142 | deviceInfoLabelMap = new LinkedHashMap(); 143 | deviceInfoLabelMap.put(StatsLabels.DATE_LABEL, date); 144 | deviceInfoLabelMap.put(StatsLabels.TIME_ZONE_LABEL, timeZone); 145 | 146 | deviceInfoLabelMap.put(StatsLabels.MANUFACTURER_LABEL, manufacturer); 147 | deviceInfoLabelMap.put(StatsLabels.MODEL_LABEL, model); 148 | deviceInfoLabelMap.put(StatsLabels.API_LEVEL_LABEL, apiLevel); 149 | deviceInfoLabelMap.put(StatsLabels.RELEASE_LABEL, release); 150 | deviceInfoLabelMap.put(StatsLabels.BRAND_LABEL, brand); 151 | 152 | deviceInfoLabelMap.put(StatsLabels.SERIAL_LABEL, serialNumber); 153 | deviceInfoLabelMap.put(StatsLabels.SECURE_ID_LABEL, secureId); 154 | 155 | setDeviceInfoLabelMap(deviceInfoLabelMap); 156 | } 157 | 158 | public void createHardwareInfoLabelMap() 159 | { 160 | hardwareLabelMap = new LinkedHashMap(); 161 | hardwareLabelMap.put(StatsLabels.IS_NFC_LABEL, hasNFC ? HAS_HARDWARE : NO_HARDWARE); 162 | hardwareLabelMap.put(StatsLabels.IS_BLUETOOTH_LABEL, hasBluetooth ? HAS_HARDWARE : NO_HARDWARE); 163 | hardwareLabelMap.put(StatsLabels.IS_ACCELEROMETER_LABEL, hasAccel ? HAS_HARDWARE : NO_HARDWARE); 164 | hardwareLabelMap.put(StatsLabels.IS_BAROMETER_LABEL, hasBluetooth ? HAS_HARDWARE : NO_HARDWARE); 165 | hardwareLabelMap.put(StatsLabels.IS_COMPASS_LABEL, hasBluetooth ? HAS_HARDWARE : NO_HARDWARE); 166 | hardwareLabelMap.put(StatsLabels.IS_GYROSCOPE_LABEL, hasBluetooth ? HAS_HARDWARE : NO_HARDWARE); 167 | hardwareLabelMap.put(StatsLabels.IS_CAMERA_LABEL, hasBluetooth ? HAS_HARDWARE : NO_HARDWARE); 168 | hardwareLabelMap.put(StatsLabels.IS_FLASH_LABEL, hasBluetooth ? HAS_HARDWARE : NO_HARDWARE); 169 | hardwareLabelMap.put(StatsLabels.IS_CAMERA_FRONT_LABEL, hasBluetooth ? HAS_HARDWARE : NO_HARDWARE); 170 | hardwareLabelMap.put(StatsLabels.IS_GPS_LABEL, hasBluetooth ? HAS_HARDWARE : NO_HARDWARE); 171 | hardwareLabelMap.put(StatsLabels.IS_NETWORK_LOCATION_LABEL, hasBluetooth ? HAS_HARDWARE : NO_HARDWARE); 172 | setHardwareLabelMap(hardwareLabelMap); 173 | } 174 | 175 | public void setSslCipherInfo(String[] protocolsDefault, String[] cipherSuitesDefault, 176 | String[] protocolsSupport, String[] cipherSuitesSupport) { 177 | 178 | this.protocolsDefault = protocolsDefault; 179 | this.cipherSuitesDefault = cipherSuitesDefault; 180 | this.protocolsSupport = protocolsSupport; 181 | this.cipherSuitesSupport = cipherSuitesSupport; 182 | } 183 | 184 | public String[] getProtocolsDefault() { 185 | return protocolsDefault; 186 | } 187 | 188 | public String[] getCipherSuitesDefault() { 189 | return cipherSuitesDefault; 190 | } 191 | 192 | public String[] getProtocolsSupport() { 193 | return protocolsSupport; 194 | } 195 | 196 | public String[] getCipherSuitesSupport() { 197 | return cipherSuitesSupport; 198 | } 199 | 200 | public void setCryptoAlgorithmsAesMap(Map> cryptoAlgorithmsAesMap) { 201 | 202 | this.cryptoAlgorithmsAesMap = cryptoAlgorithmsAesMap; 203 | } 204 | 205 | public void setCryptoAlgorithmsEcMap(Map> cryptoAlgorithmsEcMap) { 206 | 207 | this.cryptoAlgorithmsEcMap = cryptoAlgorithmsEcMap; 208 | } 209 | 210 | // getters & setters 211 | 212 | public LinkedHashMap getDeviceInfoLabelMap() { 213 | return deviceInfoLabelMap; 214 | } 215 | 216 | public void setDeviceInfoLabelMap(LinkedHashMap deviceInfoLabelMap) { 217 | this.deviceInfoLabelMap = deviceInfoLabelMap; 218 | } 219 | 220 | public LinkedHashMap getHardwareLabelMap() { 221 | return hardwareLabelMap; 222 | } 223 | 224 | public void setHardwareLabelMap(LinkedHashMap hardwareLabelMap) { 225 | this.hardwareLabelMap = hardwareLabelMap; 226 | } 227 | 228 | public List getAlgorithmsList() { 229 | return algorithmsList; 230 | } 231 | 232 | public void setAlgorithmsList(List algorithmsList) { 233 | this.algorithmsList = algorithmsList; 234 | } 235 | 236 | public Map> getCryptoAlgorithmsAesMap() { 237 | return cryptoAlgorithmsAesMap; 238 | } 239 | 240 | public Map> getCryptoAlgorithmsEcMap() { 241 | return cryptoAlgorithmsEcMap; 242 | } 243 | 244 | public int getDensity() { 245 | return density; 246 | } 247 | 248 | public int getWidthDpi() { 249 | return widthDpi; 250 | } 251 | 252 | public int getHeightDpi() { 253 | return heightDpi; 254 | } 255 | 256 | public int getWidth() { 257 | return width; 258 | } 259 | 260 | public int getHeight() { 261 | return height; 262 | } 263 | 264 | public String getSizeQualifier() { 265 | return sizeQualifier; 266 | } 267 | 268 | public String getDpiQualifier() { 269 | return dpiQualifier; 270 | } 271 | 272 | public String getScreenClass() { 273 | return screenClass; 274 | } 275 | 276 | public String getModel() { 277 | return model; 278 | } 279 | 280 | @Override 281 | public int describeContents() { 282 | return 0; 283 | } 284 | 285 | @Override 286 | public void writeToParcel(Parcel dest, int flags) { 287 | dest.writeString(this.date); 288 | dest.writeString(this.timeZone); 289 | dest.writeString(this.manufacturer); 290 | dest.writeString(this.model); 291 | dest.writeString(this.product); 292 | dest.writeString(this.apiLevel); 293 | dest.writeString(this.release); 294 | dest.writeString(this.brand); 295 | dest.writeString(this.serialNumber); 296 | dest.writeString(this.secureId); 297 | dest.writeByte(hasNFC ? (byte) 1 : (byte) 0); 298 | dest.writeByte(hasBluetooth ? (byte) 1 : (byte) 0); 299 | dest.writeByte(hasAccel ? (byte) 1 : (byte) 0); 300 | dest.writeByte(hasBarom ? (byte) 1 : (byte) 0); 301 | dest.writeByte(hasCompass ? (byte) 1 : (byte) 0); 302 | dest.writeByte(hasGyro ? (byte) 1 : (byte) 0); 303 | dest.writeByte(hasCamera ? (byte) 1 : (byte) 0); 304 | dest.writeByte(hasFlash ? (byte) 1 : (byte) 0); 305 | dest.writeByte(hasCameraFront ? (byte) 1 : (byte) 0); 306 | dest.writeByte(hasGps ? (byte) 1 : (byte) 0); 307 | dest.writeByte(hasNetworkLoc ? (byte) 1 : (byte) 0); 308 | dest.writeSerializable(this.deviceInfoLabelMap); 309 | dest.writeSerializable(this.hardwareLabelMap); 310 | dest.writeStringList(this.ciphersList); 311 | dest.writeStringList(this.sslCiphersList); 312 | dest.writeStringList(this.sslCiphersProtocolsList); 313 | dest.writeStringList(this.protocolsList); 314 | dest.writeStringList(this.algorithmsList); 315 | dest.writeStringList(this.providerList); 316 | dest.writeStringArray(this.protocolsDefault); 317 | dest.writeStringArray(this.cipherSuitesDefault); 318 | dest.writeStringArray(this.protocolsSupport); 319 | dest.writeStringArray(this.cipherSuitesSupport); 320 | //TODO - couldn't add this to parcelable config but still gets carried through? look at if issues 321 | /*dest.writeSerializable(this.cryptoAlgorithmsAesMap); 322 | dest.writeSerializable(this.cryptoAlgorithmsEcMap);*/ 323 | dest.writeInt(this.density); 324 | dest.writeInt(this.widthDpi); 325 | dest.writeInt(this.heightDpi); 326 | dest.writeInt(this.width); 327 | dest.writeInt(this.height); 328 | dest.writeString(this.sizeQualifier); 329 | dest.writeString(this.dpiQualifier); 330 | dest.writeString(this.screenClass); 331 | } 332 | 333 | public DeviceStats() { 334 | } 335 | 336 | protected DeviceStats(Parcel in) { 337 | this.date = in.readString(); 338 | this.timeZone = in.readString(); 339 | this.manufacturer = in.readString(); 340 | this.model = in.readString(); 341 | this.product = in.readString(); 342 | this.apiLevel = in.readString(); 343 | this.release = in.readString(); 344 | this.brand = in.readString(); 345 | this.serialNumber = in.readString(); 346 | this.secureId = in.readString(); 347 | this.hasNFC = in.readByte() != 0; 348 | this.hasBluetooth = in.readByte() != 0; 349 | this.hasAccel = in.readByte() != 0; 350 | this.hasBarom = in.readByte() != 0; 351 | this.hasCompass = in.readByte() != 0; 352 | this.hasGyro = in.readByte() != 0; 353 | this.hasCamera = in.readByte() != 0; 354 | this.hasFlash = in.readByte() != 0; 355 | this.hasCameraFront = in.readByte() != 0; 356 | this.hasGps = in.readByte() != 0; 357 | this.hasNetworkLoc = in.readByte() != 0; 358 | this.deviceInfoLabelMap = (LinkedHashMap) in.readSerializable(); 359 | this.hardwareLabelMap = (LinkedHashMap) in.readSerializable(); 360 | this.ciphersList = in.createStringArrayList(); 361 | this.sslCiphersList = in.createStringArrayList(); 362 | this.sslCiphersProtocolsList = in.createStringArrayList(); 363 | this.protocolsList = in.createStringArrayList(); 364 | this.algorithmsList = in.createStringArrayList(); 365 | this.providerList = in.createStringArrayList(); 366 | this.protocolsDefault = in.createStringArray(); 367 | this.cipherSuitesDefault = in.createStringArray(); 368 | this.protocolsSupport = in.createStringArray(); 369 | this.cipherSuitesSupport = in.createStringArray(); 370 | //TODO - couldn't add this to parcelable config but still gets carried through? look at if issues 371 | /*this.cryptoAlgorithmsAesMap = in.readParcelable(Map>.class.getClassLoader()); 372 | this.cryptoAlgorithmsEcMap = in.readParcelable(Map>.class.getClassLoader());*/ 373 | this.density = in.readInt(); 374 | this.widthDpi = in.readInt(); 375 | this.heightDpi = in.readInt(); 376 | this.width = in.readInt(); 377 | this.height = in.readInt(); 378 | this.sizeQualifier = in.readString(); 379 | this.dpiQualifier = in.readString(); 380 | this.screenClass = in.readString(); 381 | } 382 | 383 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 384 | public DeviceStats createFromParcel(Parcel source) { 385 | return new DeviceStats(source); 386 | } 387 | 388 | public DeviceStats[] newArray(int size) { 389 | return new DeviceStats[size]; 390 | } 391 | }; 392 | } 393 | -------------------------------------------------------------------------------- /app/src/main/java/com/devicestats/android/DeviceStatsAdapter.java: -------------------------------------------------------------------------------- 1 | package com.devicestats.android; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | import android.text.Spannable; 6 | import android.text.SpannableString; 7 | import android.text.style.ImageSpan; 8 | import androidx.annotation.NonNull; 9 | import androidx.core.content.ContextCompat; 10 | import androidx.fragment.app.Fragment; 11 | import androidx.fragment.app.FragmentManager; 12 | import androidx.fragment.app.FragmentStatePagerAdapter; 13 | 14 | /** 15 | * Created by 1andbarb on 12/01/2016. 16 | */ 17 | public class DeviceStatsAdapter extends FragmentStatePagerAdapter { 18 | 19 | private static final int ITEM_COUNT = 6; 20 | private final DeviceStats mDeviceStats; 21 | private final Context mContext; 22 | 23 | private final int[] imageResId = { 24 | R.drawable.ic_developer_mode_white_24dp, 25 | R.drawable.ic_perm_device_information_white_24dp, 26 | R.drawable.ic_speaker_phone_white_24dp, 27 | R.drawable.ic_lock_white_24dp, 28 | R.drawable.ic_vpn_key_white_24dp, 29 | R.drawable.ic_phonelink_lock_white_24dp 30 | }; 31 | 32 | public DeviceStatsAdapter(Context ctx, FragmentManager fm, DeviceStats deviceStats) { 33 | super(fm); 34 | mContext = ctx; 35 | mDeviceStats = deviceStats; 36 | } 37 | 38 | @Override 39 | public Fragment getItem(int position) { 40 | switch (position) { 41 | case 0: 42 | return DimensionsFragment.newInstance(mDeviceStats); 43 | case 1: 44 | return StatsFragment.newInstance(mDeviceStats, StatsFragment.DeviceInfoType.DEVICE_INFO); 45 | case 2: 46 | return StatsFragment.newInstance(mDeviceStats, StatsFragment.DeviceInfoType.HARDWARE); 47 | case 3: 48 | return StatsFragment.newInstance(mDeviceStats, StatsFragment.DeviceInfoType.CIPHERS); 49 | case 4: 50 | return StatsFragment.newInstance(mDeviceStats, StatsFragment.DeviceInfoType.ALGORITHMS); 51 | default: 52 | return StatsFragment.newInstance(mDeviceStats, StatsFragment.DeviceInfoType.ALGORITHMS_CRYPTO); 53 | 54 | } 55 | } 56 | 57 | @Override 58 | public int getCount() { 59 | return ITEM_COUNT; 60 | } 61 | 62 | @Override 63 | public CharSequence getPageTitle(int position) { 64 | return getImageInSpannableString(imageResId[position]); 65 | } 66 | 67 | @NonNull 68 | private CharSequence getImageInSpannableString(int i) { 69 | Drawable image = ContextCompat.getDrawable(mContext, i); 70 | image.setBounds(0, 0, image.getIntrinsicWidth(), image.getIntrinsicHeight()); 71 | SpannableString sb = new SpannableString(" "); 72 | ImageSpan imageSpan = new ImageSpan(image, ImageSpan.ALIGN_BOTTOM); 73 | sb.setSpan(imageSpan, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 74 | return sb; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/com/devicestats/android/DimensionsFragment.java: -------------------------------------------------------------------------------- 1 | package com.devicestats.android; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.TextView; 8 | import androidx.fragment.app.Fragment; 9 | 10 | public class DimensionsFragment extends Fragment { 11 | 12 | private static final String TAG = "DimensionsFragment"; 13 | private static final String ARG_PARAM1 = "param1"; 14 | 15 | private TextView mLabelArrowWidthTextView; 16 | private TextView mLabelArrowHeightTextView; 17 | private TextView mModelTextView; 18 | private TextView mScreenDimensionDipsTextView; 19 | private TextView mScreenDimensionPixelsTextView; 20 | 21 | private DeviceStats mDeviceStats; 22 | 23 | public static DimensionsFragment newInstance(DeviceStats deviceStats) { 24 | DimensionsFragment fragment = new DimensionsFragment(); 25 | Bundle args = new Bundle(); 26 | args.putParcelable(ARG_PARAM1, deviceStats); 27 | fragment.setArguments(args); 28 | return fragment; 29 | } 30 | 31 | public DimensionsFragment() 32 | { 33 | 34 | } 35 | 36 | @Override 37 | public void onCreate(Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | if (getArguments() != null) { 40 | mDeviceStats = (DeviceStats) getArguments().getParcelable(ARG_PARAM1); 41 | } 42 | } 43 | 44 | @Override 45 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 46 | View view = inflater.inflate(R.layout.fragment_dimensions, container, false); 47 | 48 | getViews(view); 49 | setData(); 50 | 51 | return view; 52 | } 53 | 54 | @Override 55 | public void onViewCreated(View view, Bundle savedInstanceState) { 56 | super.onViewCreated(view, savedInstanceState); 57 | //need to delay this as dimensions aren't available initially 58 | view.post(new Runnable() { 59 | @Override 60 | public void run() { 61 | showViewDimensions(); 62 | } 63 | }); 64 | } 65 | 66 | private void getViews(View view) { 67 | mScreenDimensionPixelsTextView = (TextView) view.findViewById(R.id.screenDimensionsPixels); 68 | mScreenDimensionDipsTextView = (TextView) view.findViewById(R.id.screenDimensionsDips); 69 | mModelTextView = (TextView) view.findViewById(R.id.model); 70 | mLabelArrowWidthTextView = (TextView) view.findViewById(R.id.horizontal_label); 71 | mLabelArrowHeightTextView = (TextView) view.findViewById(R.id.vertical_label); 72 | } 73 | 74 | private void setData() { 75 | mScreenDimensionPixelsTextView.setText(getString(R.string.screen_dimensions_pixels, 76 | mDeviceStats.getHeight(), mDeviceStats.getWidth())); 77 | mScreenDimensionDipsTextView.setText(getString(R.string.screen_dimensions_dips, 78 | mDeviceStats.getHeightDpi(), mDeviceStats.getWidthDpi())); 79 | mModelTextView.setText(mDeviceStats.getModel()); 80 | } 81 | 82 | protected void showViewDimensions() { 83 | /*DisplayMetrics metrics = getResources().getDisplayMetrics(); 84 | mWidthDp = mWidth/metrics.density; 85 | mHeightDp = mHeight/metrics.density;*/ 86 | 87 | mLabelArrowWidthTextView.setText(getString(R.string.view_width, mDeviceStats.getWidthDpi(), 88 | mDeviceStats.getWidth())); 89 | mLabelArrowHeightTextView.setText(getString(R.string.view_height, mDeviceStats.getHeightDpi(), 90 | mDeviceStats.getHeight())); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /app/src/main/java/com/devicestats/android/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.devicestats.android; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | import androidx.appcompat.app.AppCompatActivity; 7 | import androidx.appcompat.widget.Toolbar; 8 | import androidx.viewpager.widget.ViewPager; 9 | import com.google.android.material.floatingactionbutton.FloatingActionButton; 10 | import com.google.android.material.tabs.TabLayout; 11 | 12 | public class MainActivity extends AppCompatActivity { 13 | 14 | private static String filename = "devicestats.txt"; 15 | 16 | private DeviceStats mDeviceStats; 17 | private String mDeviceStatsString; 18 | 19 | @Override 20 | protected void onCreate(Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | setContentView(R.layout.activity_main); 23 | 24 | initStats(); 25 | initViews(); 26 | } 27 | 28 | private void initStats() { 29 | //TODO - move to background thread 30 | StatsGatherer gatherer = new StatsGatherer(this); 31 | mDeviceStatsString = gatherer.printStatsToString(); 32 | mDeviceStats = gatherer.getDeviceStats(); 33 | } 34 | 35 | private void initViews() { 36 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 37 | setSupportActionBar(toolbar); 38 | 39 | TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs); 40 | 41 | ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager); 42 | DeviceStatsAdapter adapter = new DeviceStatsAdapter(this, getSupportFragmentManager(), mDeviceStats); 43 | viewPager.setOffscreenPageLimit(4); 44 | viewPager.setAdapter(adapter); 45 | 46 | tabLayout.setTabsFromPagerAdapter(adapter); 47 | tabLayout.setupWithViewPager(viewPager); 48 | viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout)); 49 | 50 | //TODO - maybe move this share back to action bar menu as does get in the way of data 51 | FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); 52 | fab.setOnClickListener(new View.OnClickListener() { 53 | @Override 54 | public void onClick(View view) { 55 | shareAllDeviceStatsText(); 56 | } 57 | }); 58 | } 59 | 60 | private void shareAllDeviceStatsText() { 61 | Intent sendIntent = new Intent(); 62 | sendIntent.setAction(Intent.ACTION_SEND); 63 | sendIntent.putExtra(Intent.EXTRA_TEXT, mDeviceStatsString); 64 | sendIntent.setType("text/plain"); 65 | startActivity(sendIntent); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/devicestats/android/StatsFragment.java: -------------------------------------------------------------------------------- 1 | package com.devicestats.android; 2 | 3 | 4 | import android.os.Bundle; 5 | import android.text.TextUtils; 6 | import android.util.Log; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.FrameLayout; 11 | import android.widget.ImageView; 12 | import android.widget.LinearLayout; 13 | import android.widget.TextView; 14 | import androidx.cardview.widget.CardView; 15 | import androidx.fragment.app.Fragment; 16 | import androidx.recyclerview.widget.RecyclerView; 17 | 18 | import java.util.HashMap; 19 | import java.util.LinkedHashMap; 20 | import java.util.List; 21 | import java.util.Map; 22 | 23 | 24 | /** 25 | * device stats fragment 26 | */ 27 | public class StatsFragment extends Fragment { 28 | 29 | private static final String TAG = "StatsFragment"; 30 | private static final String ARG_DEVICE_STATS = TAG + "_device_info_type"; 31 | private static final String ARG_DEVICE_INFO_TYPE = TAG + "_display_hardware"; 32 | private static final String EXTRA_DEVICE_STATS = TAG + "_extra_device_stats"; 33 | private static final String EXTRA_DEVICE_INFO_TYPE = TAG + "_extra_device_info_type"; 34 | 35 | private LinearLayout mCardParentLayout; 36 | 37 | public enum DeviceInfoType { 38 | DEVICE_INFO, 39 | HARDWARE, 40 | CIPHERS, 41 | ALGORITHMS, 42 | ALGORITHMS_CRYPTO 43 | } 44 | 45 | private DeviceStats mDeviceStats; 46 | private CardView mCardView; 47 | private RecyclerView mRecyclerView; 48 | private ImageView mCardHeaderImageView; 49 | private TextView mCardHeaderTextView; 50 | private DeviceInfoType mDeviceInfoType; 51 | 52 | public StatsFragment() { 53 | // Required empty public constructor 54 | } 55 | 56 | /** 57 | * Use this factory method to create a new instance of 58 | * this fragment using the provided parameters. 59 | * 60 | * @param deviceStats 61 | * @return A new instance of fragment StatsFragment. 62 | */ 63 | public static StatsFragment newInstance(DeviceStats deviceStats, DeviceInfoType deviceInfoType) { 64 | StatsFragment fragment = new StatsFragment(); 65 | Bundle args = new Bundle(); 66 | args.putParcelable(ARG_DEVICE_STATS, deviceStats); 67 | args.putSerializable(ARG_DEVICE_INFO_TYPE, deviceInfoType); 68 | fragment.setArguments(args); 69 | fragment.setRetainInstance(true); 70 | return fragment; 71 | } 72 | 73 | @Override 74 | public void onCreate(Bundle savedInstanceState) { 75 | super.onCreate(savedInstanceState); 76 | Log.i(TAG, "onCreate"); 77 | if (getArguments() != null) { 78 | mDeviceStats = (DeviceStats) getArguments().getParcelable(ARG_DEVICE_STATS); 79 | mDeviceInfoType = (DeviceInfoType) getArguments().getSerializable(ARG_DEVICE_INFO_TYPE); 80 | } 81 | } 82 | 83 | @Override 84 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 85 | Bundle savedInstanceState) { 86 | Log.i(TAG, "onCreateView"); 87 | // Inflate the layout for this fragment 88 | View view = inflater.inflate(R.layout.fragment_stats, container, false); 89 | mCardParentLayout = (LinearLayout) view.findViewById(R.id.fragment_stats_card_parent_layout); 90 | return view; 91 | } 92 | 93 | @Override 94 | public void onResume() { 95 | super.onResume(); 96 | Log.i(TAG, "onResume"); 97 | 98 | //needed to add cards here as onViewCreated doesn't get called on rotation of device 99 | addCards(); 100 | } 101 | 102 | private void addCards() { 103 | Log.i(TAG, "addCards"); 104 | mCardParentLayout.removeAllViews(); 105 | Map valueImageMap = new HashMap<>(); 106 | valueImageMap.put(DeviceStats.HAS_HARDWARE, R.drawable.ic_check_circle_green_24dp); 107 | valueImageMap.put(DeviceStats.NO_HARDWARE, R.drawable.ic_cancel_red_24dp); 108 | 109 | //add specified data from device stats 110 | switch (mDeviceInfoType) { 111 | case DEVICE_INFO: 112 | addRowsToCard(mDeviceStats.getDeviceInfoLabelMap(), null); 113 | break; 114 | case HARDWARE: 115 | addRowsToCard(mDeviceStats.getHardwareLabelMap(), valueImageMap); 116 | break; 117 | case CIPHERS: 118 | boolean isCollapseContent = true; 119 | addRowsToCardSingle("Default Protocols", null, new String[]{TextUtils.join(", ", mDeviceStats.getProtocolsDefault())}, isCollapseContent); 120 | addRowsToCardSingle("Default Cipher Suites", null, mDeviceStats.getCipherSuitesDefault(), isCollapseContent); 121 | addRowsToCardSingle("Supported Protocols", null, new String[]{TextUtils.join(", ", mDeviceStats.getProtocolsSupport())}, isCollapseContent); 122 | addRowsToCardSingle("Supported Cipher Suites", null, mDeviceStats.getCipherSuitesSupport(), isCollapseContent); 123 | break; 124 | case ALGORITHMS: 125 | addRowsToCardSingle("Algorithms", null, mDeviceStats.getAlgorithmsList(), false); 126 | break; 127 | case ALGORITHMS_CRYPTO: 128 | addRowsToCardSingle("Algorithm Filter: AES", "Provider: ", mDeviceStats.getCryptoAlgorithmsAesMap(), true); 129 | addRowsToCardSingle("Algorithm Filter: EC", "Provider: ", mDeviceStats.getCryptoAlgorithmsAesMap(), true); 130 | break; 131 | default: 132 | 133 | } 134 | } 135 | 136 | private void addRowsToCard(LinkedHashMap labelMap, Map valueImageMap) { 137 | LayoutInflater inflater = LayoutInflater.from(getActivity()); 138 | FrameLayout cardView = (FrameLayout) inflater.inflate(R.layout.element_card_view, null, false); 139 | LinearLayout cardBodyLayout = (LinearLayout) cardView.findViewById(R.id.fragment_stats_card_body_layout); 140 | for (Map.Entry entry : labelMap.entrySet()) { 141 | View rowView = inflater.inflate(R.layout.element_card_double_column_row, null, false); 142 | TextView textViewLabel = (TextView) rowView.findViewById(R.id.element_card_row_label); 143 | ImageView imageViewValue = (ImageView) rowView.findViewById(R.id.element_card_row_value_image); 144 | TextView textViewValue = (TextView) rowView.findViewById(R.id.element_card_row_value); 145 | textViewLabel.setText(entry.getKey()); 146 | if (valueImageMap != null) { 147 | imageViewValue.setImageDrawable(getResources() 148 | .getDrawable(valueImageMap.get(entry.getValue()))); 149 | setImageColumnVisibility(imageViewValue, textViewValue, true); 150 | } else { 151 | textViewValue.setText(entry.getValue()); 152 | setImageColumnVisibility(imageViewValue, textViewValue, false); 153 | } 154 | 155 | cardBodyLayout.addView(rowView); 156 | } 157 | mCardParentLayout.addView(cardView); 158 | } 159 | 160 | private void setImageColumnVisibility(ImageView imageViewValue, TextView textViewValue, 161 | boolean isVisible) { 162 | imageViewValue.setVisibility(isVisible ? View.VISIBLE : View.GONE); 163 | textViewValue.setVisibility(isVisible ? View.GONE : View.VISIBLE); 164 | } 165 | 166 | private void addRowsToCardSingle(String title, String subTitle, Map> valuesMap, boolean isCollapseContent) { 168 | for (Map.Entry> entry : valuesMap.entrySet()) { 169 | addRowsToCardSingle(title, subTitle.concat(entry.getKey()), entry.getValue(), isCollapseContent); 170 | } 171 | } 172 | 173 | private void addRowsToCardSingle(String title, String subTitle, List valuesList, boolean isCollapseContent) { 174 | addRowsToCardSingle(title, subTitle, valuesList.toArray(new String[0]), isCollapseContent); 175 | } 176 | 177 | private void addRowsToCardSingle(String title, String subTitle, String[] valuesList, boolean isCollapseContent) { 178 | LayoutInflater inflater = LayoutInflater.from(getActivity()); 179 | FrameLayout cardView = (FrameLayout) inflater.inflate(R.layout.element_card_view, null, false); 180 | final LinearLayout cardBodyLayout = (LinearLayout) cardView.findViewById(R.id.fragment_stats_card_body_layout); 181 | //add title if required 182 | if (title != null) { 183 | LinearLayout headerParentLayout = (LinearLayout) cardView.findViewById(R.id.fragment_stats_header_parent_layout); 184 | TextView textViewTitle = (TextView) cardView.findViewById(R.id.fragment_stats_header_text); 185 | textViewTitle.setText(title); 186 | if (subTitle != null) { 187 | TextView textViewSubTitle = (TextView) cardView.findViewById(R.id.fragment_stats_header_subtitle_text); 188 | textViewSubTitle.setText(subTitle); 189 | textViewSubTitle.setVisibility(View.VISIBLE); 190 | } 191 | if (isCollapseContent) { 192 | cardBodyLayout.setVisibility(View.GONE); 193 | headerParentLayout.setOnClickListener(new View.OnClickListener() { 194 | @Override 195 | public void onClick(View v) { 196 | if (cardBodyLayout.getVisibility() == View.GONE) { 197 | cardBodyLayout.setVisibility(View.VISIBLE); 198 | } else { 199 | cardBodyLayout.setVisibility(View.GONE); 200 | } 201 | } 202 | }); 203 | } 204 | headerParentLayout.setVisibility(View.VISIBLE); 205 | } 206 | for (String value : valuesList) { 207 | View rowView = inflater.inflate(R.layout.element_card_single_column_row, null, false); 208 | TextView textViewValue = (TextView) rowView.findViewById(R.id.element_card_row_value); 209 | textViewValue.setText(value); 210 | cardBodyLayout.addView(rowView); 211 | } 212 | mCardParentLayout.addView(cardView); 213 | } 214 | 215 | /*public class StatsCardwithList extends CardWithList { 216 | 217 | private final Context mContext; 218 | private int mHeaderImageId; 219 | private final String mHeaderText; 220 | private Map mKeyValueList; 221 | 222 | public StatsCardwithList(Context context, @DrawableRes int headerImageId, String headerText, Map keyValueList) { 223 | super(context); 224 | 225 | mContext = context; 226 | mHeaderImageId = headerImageId; 227 | mHeaderText = headerText; 228 | mKeyValueList = keyValueList; 229 | } 230 | 231 | @Override 232 | protected CardHeader initCardHeader() { 233 | //Add Header 234 | CardHeader header = new CardHeader(getContext(), R.layout.element_card_inner_header) { 235 | @Override 236 | public void setupInnerViewElements(ViewGroup parent, View view) { 237 | super.setupInnerViewElements(parent, view); 238 | TextView title = (TextView) view.findViewById(R.id.element_card_inner_header_text); 239 | ImageView image = (ImageView) view.findViewById(R.id.element_card_inner_header_image); 240 | if (title != null) { 241 | title.setText(mHeaderText); 242 | } 243 | if (image != null) { 244 | image.setImageDrawable(getResources().getDrawable(mHeaderImageId)); 245 | } 246 | } 247 | }; 248 | header.setTitle(mHeaderText); 249 | return header; 250 | } 251 | 252 | @Override 253 | protected void initCard() { 254 | 255 | } 256 | 257 | @Override 258 | protected List initChildren() { 259 | //Init the list 260 | List objectsList = new ArrayList(); 261 | StatObject statObject; 262 | for (Map.Entry entry : mKeyValueList.entrySet()) 263 | { 264 | statObject = new StatObject(this, entry.getKey(), entry.getValue()); 265 | objectsList.add(statObject); 266 | } 267 | return objectsList; 268 | } 269 | 270 | @Override 271 | public View setupChildView(int childPosition, ListObject object, View convertView, ViewGroup parent) { 272 | //Setup the ui elements inside the item 273 | TextView textViewLabel = (TextView) convertView.findViewById(R.id.element_card_inner_row_label); 274 | TextView textViewValue = (TextView) convertView.findViewById(R.id.element_card_inner_row_value); 275 | 276 | //Retrieve the values from the object 277 | StatObject stockObject = (StatObject) object; 278 | textViewLabel.setText(stockObject.mLabel); 279 | textViewValue.setText(stockObject.mValue); 280 | 281 | return convertView; 282 | } 283 | 284 | @Override 285 | public int getChildLayoutId() { 286 | return R.layout.element_card_inner_row; 287 | } 288 | 289 | public class StatObject extends DefaultListObject { 290 | 291 | private final String mLabel; 292 | private final String mValue; 293 | 294 | public StatObject(Card parentCard, String label, String value) { 295 | super(parentCard); 296 | mLabel = label; 297 | mValue = value; 298 | } 299 | } 300 | }*/ 301 | } 302 | -------------------------------------------------------------------------------- /app/src/main/java/com/devicestats/android/StatsGatherer.java: -------------------------------------------------------------------------------- 1 | package com.devicestats.android; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.Activity; 5 | import android.content.pm.PackageManager; 6 | import android.os.Build; 7 | import android.provider.Settings.Secure; 8 | import android.util.DisplayMetrics; 9 | 10 | import javax.net.ssl.*; 11 | import java.security.Provider; 12 | import java.security.Provider.Service; 13 | import java.security.SecureRandom; 14 | import java.security.Security; 15 | import java.sql.Date; 16 | import java.text.SimpleDateFormat; 17 | import java.util.*; 18 | 19 | public class StatsGatherer { 20 | 21 | private static final String BR = "\n"; 22 | private static final Object SEPERATOR = ":\t"; 23 | private static final Object HEADING_SYM = " *** "; 24 | private static final String ISO8601_FULL_DATE_STRING = "yyyy-MM-dd HH:mm:ss"; 25 | 26 | private Activity context; 27 | private StringBuilder builder; 28 | private DeviceStats deviceStats; 29 | 30 | public StatsGatherer(Activity context) { 31 | this.context = context; 32 | builder = new StringBuilder(); 33 | getStats(); 34 | } 35 | 36 | private void getStats() { 37 | 38 | deviceStats = new DeviceStats(); 39 | recordDeviceInfo(); 40 | recordDeviceId(); 41 | recordHardwareFeatures(); 42 | recordScreenInfo(); 43 | recordSupportedHTTPSCipherSuites(); 44 | recordAlgorithms(); 45 | recordCryptoAlgorithms(); 46 | deviceStats.createDeviceInfoLabelMap(); 47 | deviceStats.createHardwareInfoLabelMap(); 48 | } 49 | 50 | public DeviceStats getDeviceStats() { 51 | return deviceStats; 52 | } 53 | 54 | private void recordDeviceId() { 55 | String serial = ""; 56 | printHeading("Device Id info"); 57 | 58 | // We're using the Reflection API because Build.SERIAL is only available 59 | // since API Level 9 (Gingerbread, Android 2.3). 60 | try { 61 | serial = (String) Build.class.getField("SERIAL").get(null); 62 | printLine("serial-number", serial); 63 | } catch (Exception ignored) { 64 | } 65 | 66 | String secureId = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID); 67 | printLine("secure-id", secureId); 68 | 69 | deviceStats.setDeviceId(serial, secureId); 70 | } 71 | 72 | private void recordHardwareFeatures() { 73 | printHeading("Hardware features"); 74 | 75 | PackageManager pm = context.getPackageManager(); 76 | boolean hasNfc = pm.hasSystemFeature("android.hardware.nfc"); 77 | boolean hasBluetooth = pm.hasSystemFeature("android.hardware.bluetooth"); 78 | boolean hasAccel = pm.hasSystemFeature("android.hardware.sensor.accelerometer"); 79 | boolean hasBarom = pm.hasSystemFeature("android.hardware.sensor.barometer"); 80 | boolean hasCompass = pm.hasSystemFeature("android.hardware.sensor.compass"); 81 | boolean hasGyro = pm.hasSystemFeature("android.hardware.sensor.gyroscope"); 82 | boolean hasCamera = pm.hasSystemFeature(PackageManager.FEATURE_CAMERA); 83 | boolean hasFlash = pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH); 84 | boolean hasCameraFront = pm.hasSystemFeature("android.hardware.camera.front"); 85 | boolean hasGps = pm.hasSystemFeature("android.hardware.location.gps"); 86 | boolean hasNetworkLoc = pm.hasSystemFeature("android.hardware.location.network"); 87 | 88 | deviceStats.setHardwareInfo(hasNfc, hasBluetooth, hasAccel, hasBarom, hasCompass, 89 | hasGyro, hasCamera, hasFlash, hasCameraFront, hasGps, hasNetworkLoc); 90 | 91 | printLine("Has NFC", hasNfc); 92 | printLine("Has bluetooth", hasBluetooth); 93 | printLine("Has accelerometer", hasAccel); 94 | printLine("Has barometer", hasBarom); 95 | printLine("Has compass", hasCompass); 96 | printLine("Has gyroscope", hasGyro); 97 | printLine("Has camera", hasCamera); 98 | printLine("Has flash", hasFlash); 99 | printLine("Has front camera", hasCameraFront); 100 | printLine("Has GPS location", hasGps); 101 | printLine("Has network location", hasNetworkLoc); 102 | 103 | } 104 | 105 | private void recordScreenInfo() { 106 | 107 | DisplayMetrics metrics = new DisplayMetrics(); 108 | context.getWindowManager().getDefaultDisplay().getMetrics(metrics); 109 | 110 | final int density = metrics.densityDpi; 111 | final int xdpi = (int) (metrics.widthPixels / metrics.density); 112 | final int ydpi = (int) (metrics.heightPixels / metrics.density); 113 | final int width = metrics.widthPixels; 114 | final int height = metrics.heightPixels; 115 | 116 | final float widthInches = width / xdpi; 117 | final float heightInches = height / ydpi; 118 | final double diagInches = Math.sqrt(widthInches * widthInches 119 | + heightInches * heightInches); 120 | String sizeQualifier = context.getResources().getString(R.string.size_qualifier); 121 | String dpiQualifier = context.getResources().getString(R.string.dpi_qualifier); 122 | String screenClass = context.getResources().getString(R.string.screen_width_class); 123 | 124 | deviceStats.setScreenInfo(density, xdpi, ydpi, width, height, sizeQualifier, 125 | dpiQualifier, screenClass); 126 | 127 | //re-factored to use resource directories :-) 128 | printHeading("Screen Info"); 129 | StringBuilder infoStr = new StringBuilder(density + " dpi"); 130 | printLine("Screen density", infoStr.toString()); 131 | printLine("Actual dpi values", 132 | String.format(context.getString(R.string.dpi_string), xdpi, ydpi)); 133 | printLine("Averaged dpi: ", Math.round((xdpi + ydpi) / 2)); 134 | printLine( 135 | "Screen size", 136 | String.format("%s (%.2f\")", 137 | context.getString(R.string.label_screen_size), diagInches)); 138 | printLine("Screen dimensions", String.format("(%dx%d)", width, height)); 139 | } 140 | 141 | private void recordDeviceInfo() { 142 | SimpleDateFormat dateFormat = new SimpleDateFormat(ISO8601_FULL_DATE_STRING); 143 | String manufacturer = Build.MANUFACTURER; 144 | String model = Build.MODEL; 145 | String product = Build.PRODUCT; 146 | String apiLevel = String.valueOf(Build.VERSION.SDK_INT); 147 | String release = Build.VERSION.RELEASE; 148 | String date = dateFormat.format(new Date(System.currentTimeMillis())); 149 | String displayName = TimeZone.getDefault().getDisplayName(); 150 | deviceStats.setDeviceInfo(manufacturer, model, product, apiLevel, 151 | release, date, displayName); 152 | 153 | printHeading("Device info"); 154 | printLine("Manufacturer", manufacturer); 155 | printLine("Model", model); 156 | //printLine("Product", product); 157 | printLine("API Level", apiLevel); 158 | printLine("Release", release); 159 | printLine("System Time", date); 160 | printLine("TimeZone", displayName); 161 | } 162 | 163 | public String printStatsToString() { 164 | return builder.toString(); 165 | } 166 | 167 | private String printArray(String[] array, boolean newlinePerEntry) { 168 | if (array != null) { 169 | StringBuilder b = new StringBuilder(); 170 | for (String value : array) { 171 | b.append(value); 172 | if (newlinePerEntry) { 173 | b.append(BR); 174 | } else { 175 | b.append(","); 176 | } 177 | } 178 | return b.toString(); 179 | } 180 | return ""; 181 | } 182 | 183 | private String printArray(List array, boolean newlinePerEntry) { 184 | if (array != null) { 185 | StringBuilder b = new StringBuilder(); 186 | for (String value : array) { 187 | b.append(value); 188 | if (newlinePerEntry) { 189 | b.append(BR); 190 | } else { 191 | b.append(","); 192 | } 193 | } 194 | return b.toString(); 195 | } 196 | return ""; 197 | } 198 | 199 | private void printLine(String label, int value) { 200 | printLine(label, value + ""); 201 | } 202 | 203 | private void printLine(String label, boolean value) { 204 | printLine(label, value ? "true" : "false"); 205 | } 206 | 207 | private void printLine(String label, String value) { 208 | builder.append(label); 209 | builder.append(SEPERATOR); 210 | builder.append(value); 211 | builder.append(BR); 212 | } 213 | 214 | private void printHeading(String label) { 215 | builder.append(BR); 216 | builder.append(HEADING_SYM); 217 | builder.append(label); 218 | builder.append(HEADING_SYM); 219 | builder.append(BR); 220 | } 221 | 222 | /** 223 | * API 9+ 224 | * 225 | * @return 226 | */ 227 | @SuppressLint("NewApi") 228 | private void recordSupportedHTTPSCipherSuites() { 229 | printHeading("SSL CipherSuites"); 230 | try { 231 | 232 | SSLContext sslContext = SSLContext.getInstance("TLS"); 233 | /* 234 | * KeyManagerFactory kmf = KeyManagerFactory 235 | * .getInstance(KeyManagerFactory.getDefaultAlgorithm()); 236 | * kmf.init(null, null); 237 | * 238 | * TrustManagerFactory tmf = TrustManagerFactory 239 | * .getInstance(TrustManagerFactory.getDefaultAlgorithm()); 240 | * 241 | * sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new 242 | * SecureRandom()); 243 | */ 244 | sslContext.init(null, null, new SecureRandom()); 245 | 246 | SSLParameters defaultParams = sslContext.getDefaultSSLParameters(); 247 | String[] protocolsDefault = defaultParams.getProtocols(); 248 | String[] cipherSuitesDefault = defaultParams.getCipherSuites(); 249 | 250 | SSLParameters supportedParams = sslContext.getSupportedSSLParameters(); 251 | String[] protocolsSupport = supportedParams.getProtocols(); 252 | String[] cipherSuitesSupport = supportedParams.getCipherSuites(); 253 | 254 | deviceStats.setSslCipherInfo(protocolsDefault, cipherSuitesDefault, protocolsSupport, cipherSuitesSupport); 255 | 256 | printLine("Default Protocols", printArray(protocolsDefault, false)); 257 | printLine("Default CipherSuites", printArray(cipherSuitesDefault, true)); 258 | printLine("Supported Protocols", printArray(protocolsSupport, false)); 259 | printLine("Supported CipherSuites", printArray(cipherSuitesSupport, true)); 260 | 261 | } catch (Exception e) { 262 | e.printStackTrace(); 263 | } 264 | } 265 | 266 | private X509TrustManager findX509TrustManager(TrustManagerFactory tmf) { 267 | TrustManager trustManagers[] = tmf.getTrustManagers(); 268 | for (int i = 0; i < trustManagers.length; i++) { 269 | if (trustManagers[i] instanceof X509TrustManager) { 270 | return (X509TrustManager) trustManagers[i]; 271 | } 272 | } 273 | return null; 274 | } 275 | 276 | private void recordCryptoAlgorithms() { 277 | printHeading("Crypto Algorithms"); 278 | Map> cryptoAlgorithmsAesMap = listAlg("AES"); 279 | Map> cryptoAlgorithmsEcMap = listAlg("EC"); 280 | deviceStats.setCryptoAlgorithmsAesMap(cryptoAlgorithmsAesMap); 281 | deviceStats.setCryptoAlgorithmsEcMap(cryptoAlgorithmsEcMap); 282 | } 283 | 284 | private Map> listAlg(String algFilter) { 285 | Map> algorithmsMap = new LinkedHashMap<>(); 286 | 287 | printLine("Algorithm Filter", algFilter); 288 | Provider[] providers = Security.getProviders(); 289 | for (Provider p : providers) { 290 | 291 | String providerStr = String.format("%s/%s/%f\n", p.getName(), 292 | p.getInfo(), p.getVersion()); 293 | Set services = p.getServices(); 294 | List algs = new ArrayList(); 295 | for (Service s : services) { 296 | boolean match = true; 297 | if (algFilter != null) { 298 | match = s.getAlgorithm().toLowerCase() 299 | .contains(algFilter.toLowerCase()); 300 | } 301 | 302 | if (match) { 303 | 304 | String algStr = String.format("%s/%s/%s", s.getType(), 305 | s.getAlgorithm(), s.getClassName()); 306 | algs.add(algStr); 307 | } 308 | } 309 | if (!algs.isEmpty()) { 310 | Collections.sort(algs); 311 | printLine("Provider", ""); 312 | printLine(providerStr, printArray(algs, true)); 313 | algorithmsMap.put(providerStr, algs); 314 | } 315 | } 316 | return algorithmsMap; 317 | } 318 | 319 | private void recordAlgorithms() { 320 | List algorithmsList = new ArrayList<>(); 321 | Set algs = Security.getAlgorithms("cipher"); 322 | printHeading("Ciphers"); 323 | for (String alg : algs) { 324 | printLine(alg, ""); 325 | algorithmsList.add(alg); 326 | } 327 | deviceStats.setAlgorithmsList(algorithmsList); 328 | } 329 | } 330 | -------------------------------------------------------------------------------- /app/src/main/java/com/devicestats/android/Utils.java: -------------------------------------------------------------------------------- 1 | package com.devicestats.android; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.content.pm.PackageInfo; 6 | import android.content.pm.PackageManager; 7 | import android.os.Environment; 8 | 9 | import com.devicestats.android.R; 10 | 11 | import java.io.BufferedWriter; 12 | import java.io.File; 13 | import java.io.FileWriter; 14 | 15 | /** 16 | * Created by andyb129 on 22/01/2016. 17 | */ 18 | public class Utils { 19 | 20 | public static final String MIME_TYPE_EMAIL = "message/rfc822"; 21 | 22 | public static Intent newEmailIntent(final Context context, 23 | final String address, final String subject, final String body, 24 | boolean useEmailMime) { 25 | Intent intent = new Intent(Intent.ACTION_SEND); 26 | intent.putExtra(Intent.EXTRA_EMAIL, new String[] { address }); 27 | intent.putExtra(Intent.EXTRA_TEXT, body); 28 | intent.putExtra(Intent.EXTRA_SUBJECT, subject); 29 | intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 30 | if (useEmailMime) { 31 | intent.setType(MIME_TYPE_EMAIL); 32 | } 33 | 34 | return intent; 35 | } 36 | 37 | public static String getAppVersion(Context ctx) { 38 | String versionName = null; 39 | int versionCode = -1; 40 | try { 41 | PackageInfo pInfo = ctx.getPackageManager().getPackageInfo( 42 | ctx.getPackageName(), 0); 43 | versionName = pInfo.versionName; 44 | versionCode = pInfo.versionCode; 45 | } catch (PackageManager.NameNotFoundException ex) { 46 | versionName = null; 47 | } 48 | return ctx.getString(R.string.about_version, versionName, versionCode); 49 | } 50 | 51 | public static void dumpDataToSD(String filename, String results) throws Exception { 52 | File log = new File(Environment.getExternalStorageDirectory(), filename); 53 | 54 | if (log.exists()) { 55 | log.delete(); 56 | } 57 | 58 | BufferedWriter out = new BufferedWriter(new FileWriter( 59 | log.getAbsolutePath(), log.exists())); 60 | out.write(results); 61 | out.close(); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/arrow_two_head_horiz.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-hdpi/arrow_two_head_horiz.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/arrow_two_head_vert.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-hdpi/arrow_two_head_vert.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_cancel_red_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-hdpi/ic_cancel_red_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_check_circle_green_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-hdpi/ic_check_circle_green_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_developer_mode_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-hdpi/ic_developer_mode_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_lock_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-hdpi/ic_lock_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_perm_device_information_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-hdpi/ic_perm_device_information_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_phonelink_lock_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-hdpi/ic_phonelink_lock_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_share_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-hdpi/ic_share_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_speaker_phone_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-hdpi/ic_speaker_phone_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_vpn_key_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-hdpi/ic_vpn_key_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/arrow_two_head_horiz.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-mdpi/arrow_two_head_horiz.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/arrow_two_head_vert.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-mdpi/arrow_two_head_vert.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_cancel_red_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-mdpi/ic_cancel_red_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_check_circle_green_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-mdpi/ic_check_circle_green_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_developer_mode_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-mdpi/ic_developer_mode_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_lock_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-mdpi/ic_lock_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_perm_device_information_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-mdpi/ic_perm_device_information_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_phonelink_lock_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-mdpi/ic_phonelink_lock_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_share_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-mdpi/ic_share_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_speaker_phone_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-mdpi/ic_speaker_phone_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_vpn_key_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-mdpi/ic_vpn_key_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/arrow_two_head_horiz.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xhdpi/arrow_two_head_horiz.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/arrow_two_head_vert.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xhdpi/arrow_two_head_vert.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_cancel_red_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xhdpi/ic_cancel_red_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_check_circle_green_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xhdpi/ic_check_circle_green_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_developer_mode_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xhdpi/ic_developer_mode_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_lock_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xhdpi/ic_lock_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_perm_device_information_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xhdpi/ic_perm_device_information_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_phonelink_lock_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xhdpi/ic_phonelink_lock_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_share_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xhdpi/ic_share_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_speaker_phone_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xhdpi/ic_speaker_phone_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_vpn_key_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xhdpi/ic_vpn_key_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/arrow_two_head_horiz.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xxhdpi/arrow_two_head_horiz.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/arrow_two_head_vert.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xxhdpi/arrow_two_head_vert.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_cancel_red_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xxhdpi/ic_cancel_red_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_check_circle_green_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xxhdpi/ic_check_circle_green_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_developer_mode_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xxhdpi/ic_developer_mode_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_lock_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xxhdpi/ic_lock_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_perm_device_information_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xxhdpi/ic_perm_device_information_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_phonelink_lock_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xxhdpi/ic_phonelink_lock_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_share_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xxhdpi/ic_share_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_speaker_phone_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xxhdpi/ic_speaker_phone_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_vpn_key_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xxhdpi/ic_vpn_key_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/arrow_two_head_horiz.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xxxhdpi/arrow_two_head_horiz.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/arrow_two_head_vert.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xxxhdpi/arrow_two_head_vert.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_cancel_red_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xxxhdpi/ic_cancel_red_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_check_circle_green_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xxxhdpi/ic_check_circle_green_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_developer_mode_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xxxhdpi/ic_developer_mode_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_lock_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xxxhdpi/ic_lock_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_perm_device_information_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xxxhdpi/ic_perm_device_information_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_phonelink_lock_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xxxhdpi/ic_phonelink_lock_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_share_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xxxhdpi/ic_share_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_speaker_phone_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xxxhdpi/ic_speaker_phone_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_vpn_key_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/drawable-xxxhdpi/ic_vpn_key_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/card_divider.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 11 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 16 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /app/src/main/res/layout/content_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 20 | 21 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/layout/element_card_double_column_row.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 22 | 34 | 50 | 51 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /app/src/main/res/layout/element_card_single_column_row.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 22 | 23 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/element_card_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 14 | 15 | 19 | 25 | 31 | 40 | 51 | 52 | 59 | 60 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_dimensions.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 22 | 23 | 31 | 32 | 40 | 41 | 50 | 51 | 57 | 61 | 68 | 69 | 77 | 78 | 86 | 87 | 95 | 96 | 105 | 106 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_stats.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/menu/default_menu.xml: -------------------------------------------------------------------------------- 1 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values-hdpi/dpiclass.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | hdpi 4 | -------------------------------------------------------------------------------- /app/src/main/res/values-large/dimen.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 64sp 4 | 56sp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values-large/screenclass.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | large screen 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values-large/style.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values-ldpi/dpiclass.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | ldpi 4 | -------------------------------------------------------------------------------- /app/src/main/res/values-mdpi/dpiclass.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | mdpi 4 | -------------------------------------------------------------------------------- /app/src/main/res/values-normal/screenclass.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | normal screen 4 | -------------------------------------------------------------------------------- /app/src/main/res/values-small/screenclass.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | small 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values-sw320dp/screen_width_class.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Phone 4 | -------------------------------------------------------------------------------- /app/src/main/res/values-sw600dp/screen_width_class.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Tablet (small) 4 | -------------------------------------------------------------------------------- /app/src/main/res/values-sw720dp/screen_width_class.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Tablet 4 | -------------------------------------------------------------------------------- /app/src/main/res/values-tvdpi/dpiclass.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | tvdpi 4 | -------------------------------------------------------------------------------- /app/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values-xhdpi/dpiclass.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | xhdpi 4 | -------------------------------------------------------------------------------- /app/src/main/res/values-xlarge/screenclass.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | xlarge screen 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values-xxhdpi/dpiclass.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | xxhdpi 4 | -------------------------------------------------------------------------------- /app/src/main/res/values-xxxhdpi/dpiclass.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | xxxhdpi 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #3F51B5 5 | #303F9F 6 | #C5CAE9 7 | #FF5722 8 | #212121 9 | #727272 10 | #FFFFFF 11 | #B6B6B6 12 | 13 | #ABABAB 14 | #4285F4 15 | @color/secondary_text 16 | 17 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 16dp 6 | 24dp 7 | 48dp 8 | 13sp 9 | 48dp 10 | 11 | 32sp 12 | 28sp 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | DeviceStats 3 | 4 | Screen Size: 5 | Share Stats 6 | Save to SD card 7 | version %1$s [%2$d] 8 | 9 | %1$ddp (%2$dpx) 10 | %1$ddp\n(%2$dpx) 11 | %1$d x %2$d dp 12 | %1$d x %2$d px 13 | (x,y): (%1$d,%2$d) dpi 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 15 | 16 | 23 | 24 | 25 | 26 | 29 | 30 | 35 | 36 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/test/java/com/devicestats/android/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.devicestats.android; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test 12 | public void addition_isCorrect() throws Exception { 13 | assertEquals(4, 2 + 2); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /art/device-stats-feature-graphic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/art/device-stats-feature-graphic.png -------------------------------------------------------------------------------- /art/device-stats-feature-graphic.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/art/device-stats-feature-graphic.psd -------------------------------------------------------------------------------- /art/screen_shot_algorithms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/art/screen_shot_algorithms.png -------------------------------------------------------------------------------- /art/screen_shot_device_stats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/art/screen_shot_device_stats.png -------------------------------------------------------------------------------- /art/screen_shot_features.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/art/screen_shot_features.png -------------------------------------------------------------------------------- /art/screen_shot_home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/art/screen_shot_home.png -------------------------------------------------------------------------------- /art/screen_shot_home_with_5xframe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/art/screen_shot_home_with_5xframe.png -------------------------------------------------------------------------------- /art/screen_shot_protocals.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/art/screen_shot_protocals.png -------------------------------------------------------------------------------- /art/web-hi-res.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/art/web-hi-res.png -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | apply from: './dependencies.gradle' 2 | 3 | 4 | buildscript { 5 | apply from: './dependencies.gradle' 6 | 7 | repositories { 8 | google() 9 | mavenCentral() 10 | } 11 | dependencies { 12 | classpath "com.android.tools.build:gradle:$agpVersion" 13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" 14 | } 15 | } 16 | 17 | allprojects { 18 | ext.isCI = System.getenv('CI') ?: false 19 | repositories { 20 | google() 21 | mavenCentral() 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /dependencies.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | agpVersion = '7.1.0' 3 | javaCompatibilityVersion = JavaVersion.VERSION_11 4 | kotlinVersion = '1.6.10' 5 | codeDesugaringLibraryVersion = '1.1.5' 6 | 7 | coordinatorlayoutVersion = '1.1.0' 8 | materialLibraryVersion = '1.4.0' 9 | cardviewVersion = "1.0.0" 10 | 11 | //testings deps 12 | junitVersion = '4.13.2' 13 | annotationVersion = "1.2.0" 14 | androidXTestVersion = '1.4.0' 15 | archCoreTestingVersion = "2.1.0" 16 | androidXTestJunitVersion = "1.1.3" 17 | // Keep as 3.3.0 until issue with 3.4.0 and 3.5.0-alpha03 fixed https://stackoverflow.com/questions/66915068/test-error-noclassdeffounderror-failed-resolution-of-lorg-hamcrest-matchers 18 | espressoVersion = '3.3.0' 19 | 20 | } 21 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | org.gradle.jvmargs=-Xmx1536m 8 | android.useAndroidX=true 9 | android.enableJetifier=true 10 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scottyab/android-device-stats/769325ab32ef8321671151cae1aad319bce1a3bf/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Dec 09 10:38:24 GMT 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------