├── .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 |
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 |
17 |
18 |
19 |
20 |
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 |
--------------------------------------------------------------------------------