├── .gitignore
├── LICENSE
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── pspdfkit
│ │ └── labs
│ │ └── quickdemo
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── assets
│ │ └── setup-guide.html
│ ├── kotlin
│ │ └── com
│ │ │ └── pspdfkit
│ │ │ └── labs
│ │ │ └── quickdemo
│ │ │ ├── DemoMode.kt
│ │ │ ├── Utils.kt
│ │ │ ├── activity
│ │ │ ├── ConfigurationActivity.kt
│ │ │ └── SetupGuideActivity.kt
│ │ │ └── service
│ │ │ └── DemoModeTileService.kt
│ └── res
│ │ ├── drawable
│ │ ├── ic_configuration_activity.xml
│ │ ├── ic_demo_mode.xml
│ │ └── ic_launcher.xml
│ │ ├── layout
│ │ └── activity_setup_guide.xml
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ ├── values
│ │ ├── array.xml
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ │ └── xml
│ │ └── demo_mode_preferences.xml
│ └── test
│ └── java
│ └── com
│ └── pspdfkit
│ └── labs
│ └── quickdemo
│ └── ExampleUnitTest.kt
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
├── setup-guide.md
├── setup.sh
└── showcase.gif
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the ART/Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 | out/
15 |
16 | # Gradle files
17 | .gradle/
18 | build/
19 |
20 | # Local configuration file (sdk path, etc)
21 | local.properties
22 |
23 | # Proguard folder generated by Eclipse
24 | proguard/
25 |
26 | # Log Files
27 | *.log
28 |
29 | # Android Studio Navigation editor temp files
30 | .navigation/
31 |
32 | # Android Studio captures folder
33 | captures/
34 |
35 | # Intellij
36 | *.iml
37 | .idea
38 |
39 | # Keystore files
40 | *.jks
41 |
42 | # External native build folder generated in Android Studio 2.2 and later
43 | .externalNativeBuild
44 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016-2019 PSPDFKit Labs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # QuickDemo
2 |
3 | _QuickDemo_ is a Nougat 7.0 [quick settings tile](https://developer.android.com/about/versions/nougat/android-7.0.html#tile_api) for fast access to the Marshmallow 6.0 [System UI demo mode](https://android.googlesource.com/platform/frameworks/base/+/android-6.0.0_r1/packages/SystemUI/docs/demo_mode.md). The app also provides a configuration activity for customizing available demo mode settings.
4 |
5 | Release blog post: https://pspdfkit.com/blog/2016/clean-statusbar-with-systemui-and-quickdemo/
6 |
7 | 
8 |
9 | ## Requirements
10 |
11 | * Android SDK (API 25)
12 | * Android Studio 2.2+
13 | * `adb` (for installing the app and granting the required permissions)
14 | * Android emulator or devices running Marshmallow (API 23+)
15 |
16 | ## Building
17 |
18 | To build and run the app, you can open the project with Android Studio and press `Run`. Alternatively you can install the app from the command line.
19 |
20 | ```bash
21 | git clone git@github.com:PSPDFKit-labs/QuickDemo.git
22 | cd QuickDemo/
23 | ./gradlew installDebug
24 | ```
25 |
26 | You can also run the [`setup.sh`](https://github.com/PSPDFKit-labs/QuickDemo/blob/master/setup.sh) script, wich will check for ANDROID_HOME, clone the project, and use Gradle to install and setup the tool. The script will also remove files of the project after installation.
27 |
28 | ## Setup
29 |
30 | ### With Gradle
31 |
32 | If you cloned the project, you can run `setupDemoMode` gradle task to do the setup.
33 |
34 | This can be done either by finding and selecting `setupDemoMode` in `Gradle` window in Android Studio, or by running the following:
35 |
36 | ```bash
37 | ./gradlew setupDemoMode
38 | ```
39 |
40 | ### Manually via adb
41 |
42 | 1. When launching the app for the first time you need to grant the `android.permission.DUMP` permission, which is required to control the System UI demo mode. You need to do this using `adb`.
43 |
44 | ```bash
45 | adb shell pm grant com.pspdfkit.labs.quickdemo android.permission.DUMP
46 | ```
47 |
48 | 2. Since the System UI tuner (and its demo mode) is an experimental Android feature, you need to activate it globally.
49 |
50 | ```bash
51 | adb shell settings put global sysui_demo_allowed 1
52 | ```
53 |
54 | ## Usage
55 |
56 | 1. The app comes with a quick settings tile which you can use to quickly toggle the demo mode.
57 | 1. Completely open the status bar drawer, expanding all quick setting tiles.
58 | 2. Press the edit button on top of the drawer, to show the quick setting tiles picker.
59 | 3. Drag the QuickDemo tile to your desired position.
60 | 4. Exit edit mode, and tap the tile.
61 |
62 | 2. You can launch QuickDemo activity to configure all displayed icons of the demo mode.
63 | 1. You can find the activity in your app launcher.
64 |
65 | ## Feedback and contribution
66 |
67 | Since this project is open source, feel free to use it, give feedback, or contribute in any way you find suitable.
68 |
69 | ## About
70 |
71 |
72 |
73 |
74 |
75 | This project is maintained and funded by [PSPDFKit](https://pspdfkit.com/).
76 |
77 | See [our other open source projects](https://github.com/PSPDFKit-labs), read [our blog](https://pspdfkit.com/blog/) or say hello on Twitter ([@PSPDFKit](https://twitter.com/pspdfkit)).
78 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016-2019 PSPDFKit GmbH. All rights reserved.
3 | *
4 | * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
5 | * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT.
6 | * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
7 | * This notice may not be removed from this file
8 | */
9 |
10 | apply plugin: 'com.android.application'
11 | apply plugin: 'kotlin-android'
12 | apply plugin: 'android-command'
13 |
14 | android {
15 | compileSdkVersion 28
16 | defaultConfig {
17 | applicationId "com.pspdfkit.labs.quickdemo"
18 | minSdkVersion 23
19 | targetSdkVersion 28
20 | versionCode 1
21 | versionName "1.0"
22 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
23 | }
24 | buildTypes {
25 | release {
26 | minifyEnabled false
27 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
28 | }
29 | }
30 | sourceSets {
31 | main.java.srcDirs += 'src/main/kotlin'
32 | }
33 | }
34 |
35 | dependencies {
36 | androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {
37 | exclude group: 'com.android.support', module: 'support-annotations'
38 | })
39 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
40 | implementation 'androidx.appcompat:appcompat:1.0.2'
41 | implementation 'androidx.legacy:legacy-support-v4:1.0.0'
42 |
43 | implementation 'io.reactivex.rxjava2:rxjava:2.2.4'
44 | implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
45 |
46 | testImplementation 'junit:junit:4.12'
47 | }
48 |
49 | task setupDemoMode(type: com.novoda.gradle.command.AdbTask) {
50 | description 'Demo Mode initial setup'
51 | doLast {
52 | runCommand(['shell', 'settings', 'put', 'global', 'sysui_demo_allowed', '1'])
53 | runCommand(['shell', 'pm', 'grant', 'com.pspdfkit.labs.quickdemo', 'android.permission.DUMP'])
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/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 /Users/david/Library/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/pspdfkit/labs/quickdemo/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016-2019 PSPDFKit GmbH. All rights reserved.
3 | *
4 | * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
5 | * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT.
6 | * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
7 | * This notice may not be removed from this file
8 | */
9 |
10 | package com.pspdfkit.labs.quickdemo
11 |
12 | import androidx.test.InstrumentationRegistry
13 | import androidx.test.runner.AndroidJUnit4
14 | import org.junit.Assert.assertEquals
15 | import org.junit.Test
16 | import org.junit.runner.RunWith
17 |
18 | /**
19 | * Instrumentation test, which will execute on an Android device.
20 |
21 | * @see [Testing documentation](http://d.android.com/tools/testing)
22 | */
23 | @RunWith(AndroidJUnit4::class)
24 | class ExampleInstrumentedTest {
25 | @Test
26 | @Throws(Exception::class)
27 | fun useAppContext() {
28 | // Context of the app under test.
29 | val appContext = InstrumentationRegistry.getTargetContext()
30 |
31 | assertEquals("com.pspdfkit.demoqstile", appContext.packageName)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
14 |
15 |
16 |
19 |
20 |
21 |
22 |
23 |
29 |
34 |
35 |
36 |
37 |
38 |
39 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/assets/setup-guide.html:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | setup-guide
18 |
19 |
20 |
623 |
624 |
763 |
764 |
765 |
766 |
767 |
768 |
769 | QuickDemo is a tool for Android developers. If you don't know what this application is for, do not use it!
770 |
771 | Setup
772 |
773 | To enable QuickDemo you need to do two things.
774 |
775 |
776 | - Enable the System UI demo mode on your phone.
777 | - Grant the
android.permission.DUMP
to the QuickDemo app. This permissions is protected and can't be requested at runtime.
778 |
779 |
780 | This can be achieved by running the following gradle
task.
781 |
782 |
783 |
784 | If you don't have the project. Both steps can be performed manually from command line via adb
.
785 |
786 | # Ensure demo mode is activated.
787 | adb shell settings put global sysui_demo_allowed 1
788 |
789 | # Grant required permissions.
790 | adb shell pm grant com.pspdfkit.labs.quickdemo android.permission.DUMP
791 |
792 | Please note: The permissions is granted until you revoke it or uninstall the app.
793 |
794 |
797 |
798 |
799 |
800 |
801 |
802 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/pspdfkit/labs/quickdemo/DemoMode.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016-2019 PSPDFKit GmbH. All rights reserved.
3 | *
4 | * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
5 | * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT.
6 | * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
7 | * This notice may not be removed from this file
8 | */
9 |
10 | package com.pspdfkit.labs.quickdemo
11 |
12 | import android.content.Context
13 | import android.content.Intent
14 | import android.content.SharedPreferences
15 | import android.content.pm.PackageManager
16 | import android.preference.PreferenceManager
17 | import android.provider.Settings
18 | import androidx.core.content.ContextCompat
19 | import kotlin.properties.ReadWriteProperty
20 | import kotlin.reflect.KProperty
21 |
22 | class DemoMode private constructor(context: Context) {
23 | private val applicationContext: Context = context.applicationContext
24 | private val sharedPreferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
25 | private val demoModeProperties = mutableSetOf>()
26 |
27 | companion object {
28 | private var singleton: DemoMode? = null
29 |
30 | /** Returns the demo mode singleton */
31 | fun get(context: Context): DemoMode {
32 | var demoMode = singleton
33 | if (demoMode == null) {
34 | demoMode = DemoMode(context)
35 | singleton = demoMode
36 | }
37 |
38 | return demoMode
39 | }
40 |
41 | const val DEFAULT_MOBILE_DATATYPE = "hidden"
42 | const val DEFAULT_MOBILE_LEVEL = 4
43 | const val DEFAULT_WIFI_LEVEL = 4
44 | const val DEFAULT_NETWORK_NUM_OF_SIMS = 1
45 | const val DEFAULT_STATUS_BAR_MODE = "transparent"
46 | const val DEFAULT_BATTERY_LEVEL = 100
47 | const val DEFAULT_STATUS_HIDDEN = "hidden"
48 | }
49 |
50 | /** Returns `true` if the required permissions to send out demo mode broadcasts are available, or `false` if they need to be requested first. */
51 | val requiredPermissionsGranted: Boolean
52 | get() = ContextCompat.checkSelfPermission(applicationContext, "android.permission.DUMP") == PackageManager.PERMISSION_GRANTED
53 |
54 | /** Returns `true` if the demo mode has been activated in system UI. **/
55 | val demoModeAllowed: Boolean
56 | get() = Settings.Global.getInt(applicationContext.contentResolver, "sysui_demo_allowed", 0) == 1
57 |
58 | /**
59 | * Enables or disables the demo mode. If this is set to `false`, no other demo mode settings will have effect.
60 | */
61 | var enabled: Boolean
62 | get() = sharedPreferences.getBoolean("demoEnabled", false)
63 | set(value) {
64 | sharedPreferences.edit().putBoolean("demoEnabled", value).commit()
65 |
66 | if (value) {
67 | sendDemoCommand("enter")
68 | for (property in demoModeProperties) {
69 | property.issueDemoModeCommand()
70 | }
71 | } else {
72 | sendDemoCommand("exit")
73 | }
74 |
75 | }
76 |
77 | /**
78 | * If set to `true`, all notifications get hidden.
79 | */
80 | var hideNotifications by booleanPreference("hideNotifications", true) { value ->
81 | if (enabled) sendDemoCommand("notifications", "visible" to !value)
82 | }
83 |
84 | /**
85 | * Sets the time in the format of `hhmm`.
86 | */
87 | var time by stringPreference("time", "0700") { value ->
88 | if (enabled) sendDemoCommand("clock", "hhmm" to value)
89 | }
90 |
91 | /**
92 | * Can be set to any of `opaque`, `translucent`, `semi-transparent`, `transparent`, or `warning`.
93 | */
94 | var statusBarMode by stringPreference("statusBarMode", DEFAULT_STATUS_BAR_MODE) { value ->
95 | if (enabled) sendDemoCommand("bars", "mode" to value)
96 | }
97 |
98 | /**
99 | * Configure the network display (Wi-Fi, mobile, airplane, etc.).
100 | */
101 | val network = Network()
102 |
103 | inner class Network {
104 | /**
105 | * Enables or disables airplane mode. Disabled by default.
106 | */
107 | var showAirplane by booleanPreference("networkShowAirplane", false) { value ->
108 | if (enabled) sendDemoCommand("network", "airplane" to if (value) "show" else "hide")
109 | }
110 |
111 | /**
112 | * Enables or disables full network connectivity mode. Enabled by default.
113 | */
114 | var fullConnectivity by booleanPreference("networkFullConnectivity", true) { value ->
115 | if (enabled) sendDemoCommand("network", "fully" to value)
116 | }
117 |
118 | /**
119 | * Shows or hides the Wi-Fi icon. Hidden by default.
120 | */
121 | var showWifi by booleanPreference("networkShowWifi", false) { value ->
122 | if (enabled) sendDemoCommand("network", "wifi" to if (value) "show" else "hide")
123 | }
124 |
125 | /**
126 | * Sets the Wi-Fi connection level. Can be an integer from `0` (no reception) to `4` (full reception). Defaults to `4`.
127 | */
128 | var wifiLevel by intPreference("networkWifiLevel", DEFAULT_WIFI_LEVEL) { value ->
129 | if (enabled && showWifi) sendDemoCommand("network", "wifi" to "show", "level" to value)
130 | }
131 |
132 | /**
133 | * Shows or hides the mobile network icon. Shown by default.
134 | */
135 | var mobileShown by booleanPreference("networkMobileShown", true) { value ->
136 | if (enabled) sendDemoCommand("network", "mobile" to if (value) "show" else "hide")
137 | }
138 |
139 | /**
140 | * Sets the mobile network level. Can be an integer from `0` (no reception) to `4` (full reception). Defaults to `4`.
141 | */
142 | var mobileLevel by intPreference("networkMobileLevel", DEFAULT_MOBILE_LEVEL) { value ->
143 | if (enabled && mobileShown) sendDemoCommand("network", "mobile" to "show", "level" to value)
144 | }
145 |
146 | /**
147 | * Sets the mobile network connection type. Can be any of `1x`, `3g`, `4g`, `e`, `g`, `h`, `lte`, `roam`, or any other value to hide. Defaults to `lte`.
148 | */
149 | var mobileDatatype by stringPreference("networkMobileDatatype", DEFAULT_MOBILE_DATATYPE) { value ->
150 | if (enabled && mobileShown) sendDemoCommand("network", "mobile" to "show", "datatype" to value)
151 | }
152 |
153 | /**
154 | * Sets mobile signal icon to carrier network change UX when disconnected. Disabled by default.
155 | */
156 | var showCarrierNetworkChange by booleanPreference("networkCarrierNetworkChange", false) { value ->
157 | if (enabled) sendDemoCommand("network", "carriernetworkchange" to if (value) "show" else "hide")
158 | }
159 |
160 | /**
161 | * Sets the number of used SIMs. Can be an integer from `1` to `8`. Defaults to `1`.
162 | */
163 | var numberOfSims by intPreference("networkNumberOfSims", DEFAULT_NETWORK_NUM_OF_SIMS) { value ->
164 | if (enabled) sendDemoCommand("network", "sims" to value)
165 | }
166 |
167 | /**
168 | * Show or hide the "no SIM" icon. Hidden by default.
169 | */
170 | var showNoSim by booleanPreference("networkShowNoSim", false) { value ->
171 | if (enabled) sendDemoCommand("network", "nosim" to if (value) "show" else "hide")
172 | }
173 | }
174 |
175 | /**
176 | * Configure the status icons (alarm, volume, bluetooth, etc.)
177 | */
178 | val status = Status()
179 |
180 | inner class Status {
181 | /**
182 | * Sets the volume icon. Can be any of `silent`, `vibrate`, or any other value to hide the icon. Hidden by default.
183 | */
184 | var volume by stringPreference("statusVolume", DEFAULT_STATUS_HIDDEN) { value ->
185 | if (enabled) sendDemoCommand("status", "volume" to value)
186 | }
187 |
188 | /**
189 | * Sets the bluetooth icon. Can be any of `connected`, `disconnected`, `hidden`. Hidden by default.
190 | */
191 | var bluetooth by stringPreference("statusBluetooth", DEFAULT_STATUS_HIDDEN, { value ->
192 | if (enabled) sendDemoCommand("status", "bluetooth" to value)
193 | })
194 |
195 | /**
196 | * Shows or hides the icon in the location slot. Hidden by default.
197 | */
198 | var showLocation by booleanPreference("statusLocation", false) { value ->
199 | if (enabled) sendDemoCommand("status", "location" to if (value) "show" else "hide")
200 | }
201 |
202 | /**
203 | * Shows or hides the alarm clock icon. Hidden by default.
204 | */
205 | var showAlarm by booleanPreference("statusAlarm", false) { value ->
206 | if (enabled) sendDemoCommand("status", "alarm" to if (value) "show" else "hide")
207 | }
208 |
209 | /**
210 | * Shows or hides the sync icon. Hidden by default.
211 | */
212 | var showSync by booleanPreference("statusSync", false) { value ->
213 | if (enabled) sendDemoCommand("status", "sync" to if (value) "show" else "hide")
214 | }
215 |
216 | /**
217 | * Shows or hides the TTY icon. Hidden by default.
218 | */
219 | var showTty by booleanPreference("statusTty", false) { value ->
220 | if (enabled) sendDemoCommand("status", "tty" to if (value) "show" else "hide")
221 | }
222 |
223 | /**
224 | * Shows or hides the CDMA ERI icon. Hidden by default.
225 | */
226 | var showEri by booleanPreference("statusEri", false) { value ->
227 | if (enabled) sendDemoCommand("status", "eri" to if (value) "show" else "hide")
228 | }
229 |
230 | /**
231 | * Shows or hides the mute icon. Hidden by default.
232 | */
233 | var showMute by booleanPreference("statusMute", false) { value ->
234 | if (enabled) sendDemoCommand("status", "mute" to if (value) "show" else "hide")
235 | }
236 |
237 | /**
238 | * Shows or hides the speakerphone icon. Hidden by default.
239 | */
240 | var showSpeakerphone by booleanPreference("statusSpeakerphone", false) { value ->
241 | if (enabled) sendDemoCommand("status", "speakerphone" to if (value) "show" else "hide")
242 | }
243 | }
244 |
245 | /**
246 | * Configure the battery display.
247 | */
248 | val battery = Battery()
249 |
250 | inner class Battery() {
251 | /**
252 | * Sets the shown battery level. Can be an integer from `0` (depleted) to `100` (fully charged). Defaults to `100`.
253 | */
254 | var level by intPreference("batteryLevel", DEFAULT_BATTERY_LEVEL) { value ->
255 | if (enabled) sendDemoCommand("battery", "level" to value)
256 | }
257 |
258 | /**
259 | * Enables or disables the battery being shown as being plugged in and charging. Defaults to `false`.
260 | */
261 | var plugged by booleanPreference("batteryPlugged", false) { value ->
262 | if (enabled) sendDemoCommand("battery", "plugged" to value)
263 | }
264 |
265 | /**
266 | * Enables or disables the device's power save mode. Defaults to `false`.
267 | */
268 | var powersave by booleanPreference("batteryPowersave", default = false) { value ->
269 | if (enabled) sendDemoCommand("battery", "powersave" to value)
270 | }
271 | }
272 |
273 | private fun sendDemoCommand(command: String, vararg extras: Pair) {
274 | val intent = Intent("com.android.systemui.demo").apply {
275 | putExtra("command", command)
276 | for ((key, value) in extras) {
277 | putExtra(key, value.toString())
278 | }
279 | }
280 |
281 | applicationContext.sendBroadcast(intent)
282 | }
283 |
284 | abstract class DemoModeProperty : ReadWriteProperty, SharedPreferences.OnSharedPreferenceChangeListener {
285 | abstract val key: String
286 | abstract fun issueDemoModeCommand()
287 | abstract fun getStoredValue(): T
288 | override fun getValue(thisRef: Any, property: KProperty<*>) = getStoredValue()
289 | override fun onSharedPreferenceChanged(p0: SharedPreferences?, changedKey: String?) {
290 | if (key == changedKey) issueDemoModeCommand()
291 | }
292 | }
293 |
294 | fun booleanPreference(key: String, default: Boolean, onSendCommand: (Boolean) -> Unit) = object : DemoModeProperty() {
295 | override val key = key
296 | override fun issueDemoModeCommand() = onSendCommand(getStoredValue())
297 | override fun getStoredValue() = sharedPreferences.getBoolean(key, default)
298 | override fun setValue(thisRef: Any, property: KProperty<*>, value: Boolean) {
299 | sharedPreferences.edit().putBoolean(key, value).apply()
300 | }
301 | }.apply {
302 | sharedPreferences.registerOnSharedPreferenceChangeListener(this)
303 | demoModeProperties.add(this)
304 | }
305 |
306 | fun intPreference(key: String, default: Int, onSendCommand: (Int) -> Unit) = object : DemoModeProperty() {
307 | override val key = key
308 | override fun issueDemoModeCommand() = onSendCommand(getStoredValue())
309 | override fun getStoredValue() = sharedPreferences.getString(key, default.toString()).toInt()
310 | override fun setValue(thisRef: Any, property: KProperty<*>, value: Int) {
311 | sharedPreferences.edit().putString(key, value.toString()).apply()
312 | }
313 | }.apply {
314 | sharedPreferences.registerOnSharedPreferenceChangeListener(this)
315 | demoModeProperties.add(this)
316 | }
317 |
318 | fun stringPreference(key: String, default: String, onSendCommand: (String) -> Unit) = object : DemoModeProperty() {
319 | override val key = key
320 | override fun issueDemoModeCommand() = onSendCommand(getStoredValue())
321 | override fun getStoredValue() = sharedPreferences.getString(key, default)
322 | override fun setValue(thisRef: Any, property: KProperty<*>, value: String) {
323 | sharedPreferences.edit().putString(key, value).apply()
324 | }
325 | }.apply {
326 | sharedPreferences.registerOnSharedPreferenceChangeListener(this)
327 | demoModeProperties.add(this)
328 | }
329 | }
330 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/pspdfkit/labs/quickdemo/Utils.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016-2019 PSPDFKit GmbH. All rights reserved.
3 | *
4 | * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
5 | * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT.
6 | * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
7 | * This notice may not be removed from this file
8 | */
9 |
10 | package com.pspdfkit.labs.quickdemo
11 |
12 | import android.content.Context
13 | import android.widget.Toast
14 |
15 | /** Shows a normal toast message. */
16 | fun Context.toast(message: CharSequence, length: Int = Toast.LENGTH_SHORT)
17 | = Toast.makeText(this, message, length).show()
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/pspdfkit/labs/quickdemo/activity/ConfigurationActivity.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016-2019 PSPDFKit GmbH. All rights reserved.
3 | *
4 | * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
5 | * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT.
6 | * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
7 | * This notice may not be removed from this file
8 | */
9 |
10 | package com.pspdfkit.labs.quickdemo.activity
11 |
12 |
13 | import android.content.BroadcastReceiver
14 | import android.content.Context
15 | import android.content.Intent
16 | import android.content.IntentFilter
17 | import android.content.SharedPreferences
18 | import android.os.Bundle
19 | import android.preference.PreferenceFragment
20 | import android.preference.SwitchPreference
21 | import androidx.annotation.ArrayRes
22 | import androidx.appcompat.app.AppCompatActivity
23 | import com.pspdfkit.labs.quickdemo.DemoMode
24 | import com.pspdfkit.labs.quickdemo.R
25 |
26 | class ConfigurationActivity : AppCompatActivity() {
27 |
28 | override fun onCreate(savedInstanceState: Bundle?) {
29 | super.onCreate(savedInstanceState)
30 | supportActionBar?.setIcon(R.drawable.ic_configuration_activity)
31 | fragmentManager.beginTransaction().replace(android.R.id.content, DemoModePreferences()).commit()
32 | }
33 |
34 | override fun onStart() {
35 | super.onStart()
36 |
37 | // Whenever this activity comes to the foreground, we check if the required permissions have been granted.
38 | // If the permissions are missing, launch the setup guide.
39 | val demoMode = DemoMode.get(this)
40 | if (!demoMode.requiredPermissionsGranted || !demoMode.demoModeAllowed) {
41 | SetupGuideActivity.launch(this)
42 | // We have to finish this activity, otherwise users that do not complete the setup guide would end up in a endless loop.
43 | finish()
44 | }
45 | }
46 |
47 | class DemoModePreferences : PreferenceFragment(), SharedPreferences.OnSharedPreferenceChangeListener {
48 | private lateinit var demoMode: DemoMode
49 | private var updateFromReceiver: Boolean = false
50 |
51 | private val demoModeChangeReceiver = object : BroadcastReceiver() {
52 | override fun onReceive(p0: Context?, p1: Intent?) {
53 | updateFromReceiver = true
54 | updateDemoModeSwitch()
55 | updateFromReceiver = false
56 | }
57 | }
58 |
59 | override fun onCreate(savedInstanceState: Bundle?) {
60 | super.onCreate(savedInstanceState)
61 | demoMode = DemoMode.get(context)
62 | addPreferencesFromResource(R.xml.demo_mode_preferences)
63 | preferenceManager.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
64 | updateSummaries()
65 | }
66 |
67 | fun updateSummaries() {
68 | setSummaryFromArrayValue("batteryLevel", R.array.batteryLevelEscaped, R.array.batteryLevelValues, DemoMode.DEFAULT_BATTERY_LEVEL.toString())
69 | setSummaryFromArrayValue("statusBarMode", R.array.statusBarModes, R.array.statusBarModeValues, DemoMode.DEFAULT_STATUS_BAR_MODE)
70 | setSummaryFromArrayValue("networkMobileDatatype", R.array.networkMobileDatatypes, R.array.networkMobileDatatypeValues, DemoMode.DEFAULT_MOBILE_DATATYPE)
71 | setSummaryFromArrayValue("networkMobileLevel", R.array.networkReceptionLevels, R.array.networkReceptionLevelValues, DemoMode.DEFAULT_MOBILE_LEVEL.toString())
72 | setSummaryFromArrayValue("networkNumberOfSims", R.array.networkNumOfSims, R.array.networkNumOfSims, DemoMode.DEFAULT_NETWORK_NUM_OF_SIMS.toString())
73 | setSummaryFromArrayValue("networkWifiLevel", R.array.networkReceptionLevels, R.array.networkReceptionLevelValues, DemoMode.DEFAULT_WIFI_LEVEL.toString())
74 | setSummaryFromArrayValue("statusVolume", R.array.statusVolume, R.array.statusVolumeValues, DemoMode.DEFAULT_STATUS_HIDDEN)
75 | setSummaryFromArrayValue("statusBluetooth", R.array.statusBluetooth, R.array.statusBluetoothValues, DemoMode.DEFAULT_STATUS_HIDDEN)
76 | }
77 |
78 | fun setSummaryFromArrayValue(key: String, @ArrayRes labelArrayRes: Int, @ArrayRes valueArrayRes: Int, default: String) {
79 | val preference = findPreference(key)
80 | val value = preferenceManager.sharedPreferences.getString(key, default)
81 | val i = resources.getStringArray(valueArrayRes).indexOf(value)
82 | if (i >= 0) preference.summary = resources.getStringArray(labelArrayRes)[i]
83 | }
84 |
85 | override fun onStart() {
86 | super.onStart()
87 | context.registerReceiver(demoModeChangeReceiver, IntentFilter("com.android.systemui.demo"))
88 | updateDemoModeSwitch()
89 | }
90 |
91 | override fun onStop() {
92 | super.onStop()
93 | context.unregisterReceiver(demoModeChangeReceiver)
94 | }
95 |
96 | private fun updateDemoModeSwitch() {
97 | val enabled = findPreference("enable_demo") as SwitchPreference
98 | enabled.isChecked = demoMode.enabled
99 | }
100 |
101 | override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
102 | when (key) {
103 | "enable_demo" -> {
104 | if (!updateFromReceiver) demoMode.enabled = sharedPreferences.getBoolean(key, false)
105 | }
106 | "batteryLevel", "statusBarMode", "networkMobileDatatype", "networkMobileLevel",
107 | "networkNumberOfSims", "networkWifiLevel", "statusVolume", "statusBluetooth" -> {
108 | updateSummaries()
109 | }
110 | }
111 | }
112 | }
113 |
114 | companion object {
115 | fun launch(context: Context) {
116 | val intent = Intent(context, ConfigurationActivity::class.java)
117 | context.startActivity(intent)
118 | }
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/pspdfkit/labs/quickdemo/activity/SetupGuideActivity.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016-2019 PSPDFKit GmbH. All rights reserved.
3 | *
4 | * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
5 | * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT.
6 | * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
7 | * This notice may not be removed from this file
8 | */
9 |
10 | package com.pspdfkit.labs.quickdemo.activity
11 |
12 | import android.content.Context
13 | import android.content.Intent
14 | import android.os.Bundle
15 | import androidx.appcompat.app.AppCompatActivity
16 | import android.webkit.WebView
17 | import android.widget.Toast
18 | import com.pspdfkit.labs.quickdemo.DemoMode
19 | import com.pspdfkit.labs.quickdemo.R
20 | import com.pspdfkit.labs.quickdemo.toast
21 | import io.reactivex.Observable
22 | import io.reactivex.android.schedulers.AndroidSchedulers
23 | import io.reactivex.disposables.Disposable
24 | import java.util.concurrent.TimeUnit
25 |
26 | /**
27 | * An interactive introduction guide for first-time users.
28 | */
29 | class SetupGuideActivity : AppCompatActivity() {
30 |
31 | var permissionChecking: Disposable? = null
32 |
33 | override fun onCreate(savedInstanceState: Bundle?) {
34 | super.onCreate(savedInstanceState)
35 | val demoMode = DemoMode.get(this)
36 |
37 | // If the app has already been granted the required permissions, there's no need to setup.
38 | if (demoMode.requiredPermissionsGranted && demoMode.demoModeAllowed) finish()
39 |
40 | // Setup content is just informative. We're using a webview to show pre-formatted instructions.
41 | setContentView(R.layout.activity_setup_guide)
42 | val webView: WebView = findViewById(R.id.webview)
43 | webView.loadUrl("file:///android_asset/setup-guide.html")
44 |
45 | // We periodically check if the permission has been granted. Once this happened, we notify the user and finish the activity.
46 | permissionChecking = Observable.interval(1, TimeUnit.SECONDS)
47 | .filter { demoMode.requiredPermissionsGranted && demoMode.demoModeAllowed }
48 | .observeOn(AndroidSchedulers.mainThread())
49 | .doOnNext {
50 | toast("QuickDemo has been successfully set up! Ready for activating demo mode.", Toast.LENGTH_LONG)
51 | ConfigurationActivity.launch(this)
52 | finish()
53 | }
54 | .subscribe()
55 | }
56 |
57 | override fun onDestroy() {
58 | super.onDestroy()
59 |
60 | // Stop permission checking when destroying the activity (preventing leaks).
61 | permissionChecking?.dispose()
62 | permissionChecking = null
63 | }
64 |
65 | companion object {
66 | fun launch(context: Context) {
67 | context.startActivity(intent(context))
68 | }
69 |
70 | fun intent(context: Context) = Intent(context, SetupGuideActivity::class.java)
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/com/pspdfkit/labs/quickdemo/service/DemoModeTileService.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016-2019 PSPDFKit GmbH. All rights reserved.
3 | *
4 | * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
5 | * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT.
6 | * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
7 | * This notice may not be removed from this file
8 | */
9 |
10 | package com.pspdfkit.labs.quickdemo.service
11 |
12 | import android.content.Intent
13 | import android.graphics.drawable.Icon
14 | import android.service.quicksettings.Tile
15 | import android.service.quicksettings.TileService
16 | import com.pspdfkit.labs.quickdemo.DemoMode
17 | import com.pspdfkit.labs.quickdemo.R
18 | import com.pspdfkit.labs.quickdemo.activity.SetupGuideActivity
19 |
20 | class DemoModeTileService : TileService() {
21 |
22 | private lateinit var demoMode: DemoMode
23 |
24 | override fun onCreate() {
25 | super.onCreate()
26 | demoMode = DemoMode.get(this)
27 | }
28 |
29 | override fun onStartListening() {
30 | qsTile.icon = Icon.createWithResource(this, R.drawable.ic_demo_mode)
31 | qsTile.label = "Demo mode"
32 | updateIcon()
33 | }
34 |
35 | override fun onClick() {
36 | if (!demoMode.requiredPermissionsGranted || !demoMode.demoModeAllowed) {
37 | startActivityAndCollapse(SetupGuideActivity.intent(this))
38 | return
39 | }
40 |
41 | demoMode.enabled = !demoMode.enabled
42 | updateIcon()
43 |
44 | val it = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
45 | sendBroadcast(it)
46 | }
47 |
48 | private fun updateIcon() {
49 | qsTile.state = if (demoMode.enabled) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE
50 | qsTile.updateTile()
51 | }
52 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_configuration_activity.xml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_demo_mode.xml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_setup_guide.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 | 64dp
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values/array.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 | - Transparent
14 | - Opaque
15 | - Translucent
16 | - Semi-Transparent
17 | - Warning
18 |
19 |
20 |
21 | - transparent
22 | - opaque
23 | - translucent
24 | - semi-transparent
25 | - warning
26 |
27 |
28 |
29 | - 1x
30 | - 3G
31 | - 4G
32 | - Edge
33 | - GPRS
34 | - HSPA
35 | - LTE
36 | - Roaming
37 | - Hidden
38 |
39 |
40 |
41 | - 1x
42 | - 3g
43 | - 4g
44 | - e
45 | - g
46 | - h
47 | - lte
48 | - roam
49 | - hidden
50 |
51 |
52 |
53 | - 100% (fully charged)
54 | - 85%
55 | - 70%
56 | - 50%
57 | - 30%
58 | - 15%
59 | - 0% (depleted)
60 |
61 |
62 |
63 | - 100%% (fully charged)
64 | - 85%%
65 | - 70%%
66 | - 50%%
67 | - 30%%
68 | - 15%%
69 | - 0%% (depleted)
70 |
71 |
72 |
73 | - 100
74 | - 85
75 | - 70
76 | - 50
77 | - 30
78 | - 15
79 | - 0
80 |
81 |
82 |
83 | - 4 (full reception)
84 | - 3
85 | - 2
86 | - 1
87 | - 0 (no reception)
88 |
89 |
90 |
91 | - 4
92 | - 3
93 | - 2
94 | - 1
95 | - 0
96 |
97 |
98 |
99 | - 1
100 | - 2
101 | - 3
102 | - 4
103 | - 5
104 | - 6
105 | - 7
106 | - 8
107 |
108 |
109 |
110 | - Hidden
111 | - Silent
112 | - Vibrate
113 |
114 |
115 |
116 | - hidden
117 | - silent
118 | - vibrate
119 |
120 |
121 |
122 | - Hidden
123 | - Connected
124 | - Disconnected
125 |
126 |
127 |
128 | - hidden
129 | - connected
130 | - disconnected
131 |
132 |
133 |
134 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 | #0282BE
13 | #015580
14 | #4FC3F7
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 | 16dp
12 | 16dp
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 | QuickDemo
12 | Demo mode
13 | QuickDemo Settings
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/demo_mode_preferences.xml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
12 |
13 |
17 |
18 |
19 |
26 |
27 |
28 |
29 |
33 |
34 |
35 |
36 |
42 |
43 |
47 |
48 |
52 |
53 |
54 |
55 |
56 |
60 | android:singleLine="true"/>
61 |
62 |
63 |
64 |
65 |
69 |
70 |
74 |
75 |
81 |
82 |
89 |
90 |
94 |
95 |
99 |
100 |
104 |
105 |
111 |
112 |
113 |
114 |
118 |
119 |
125 |
126 |
127 |
128 |
134 |
135 |
141 |
142 |
146 |
147 |
151 |
152 |
156 |
157 |
161 |
162 |
166 |
167 |
171 |
172 |
173 |
174 |
175 |
--------------------------------------------------------------------------------
/app/src/test/java/com/pspdfkit/labs/quickdemo/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016-2019 PSPDFKit GmbH. All rights reserved.
3 | *
4 | * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
5 | * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT.
6 | * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
7 | * This notice may not be removed from this file
8 | */
9 |
10 | package com.pspdfkit.labs.quickdemo
11 |
12 | import org.junit.Assert.assertEquals
13 | import org.junit.Test
14 |
15 | /**
16 | * Example local unit test, which will execute on the development machine (host).
17 |
18 | * @see [Testing documentation](http://d.android.com/tools/testing)
19 | */
20 | class ExampleUnitTest {
21 | @Test
22 | @Throws(Exception::class)
23 | fun addition_isCorrect() {
24 | assertEquals(4, (2 + 2).toLong())
25 | }
26 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016-2019 PSPDFKit GmbH. All rights reserved.
3 | *
4 | * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
5 | * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT.
6 | * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
7 | * This notice may not be removed from this file
8 | */
9 |
10 | buildscript {
11 | ext.kotlin_version = '1.3.31'
12 |
13 | repositories {
14 | jcenter()
15 | google()
16 | }
17 |
18 | dependencies {
19 | classpath 'com.android.tools.build:gradle:3.4.1'
20 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
21 | classpath 'com.novoda:gradle-android-command-plugin:1.6.2'
22 | }
23 | }
24 |
25 | allprojects {
26 | repositories {
27 | jcenter()
28 | google()
29 | }
30 | }
31 |
32 | task clean(type: Delete) {
33 | delete rootProject.buildDir
34 | }
35 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (c) 2016-2019 PSPDFKit GmbH. All rights reserved.
3 | #
4 | # THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
5 | # AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT.
6 | # UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
7 | # This notice may not be removed from this file
8 | #
9 |
10 | org.gradle.jvmargs=-Xmx1536m
11 | kotlin.incremental=true
12 | android.useAndroidX=true
13 | android.enableJetifier=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PSPDFKit-labs/QuickDemo/8fda952e4019f3b6437d1605be86165764dd88ce/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #
2 | # gradle-wrapper.properties
3 | #
4 | # PSPDFKit
5 | #
6 | # Copyright (c) 2016 PSPDFKit GmbH. All rights reserved.
7 | #
8 | # THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
9 | # AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT.
10 | # UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
11 | # This notice may not be removed from this file
12 | #
13 |
14 | #Mon Oct 16 10:43:14 EDT 2017
15 | distributionBase=GRADLE_USER_HOME
16 | distributionPath=wrapper/dists
17 | zipStoreBase=GRADLE_USER_HOME
18 | zipStorePath=wrapper/dists
19 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
20 |
--------------------------------------------------------------------------------
/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 | /*
2 | * Copyright (c) 2016-2019 PSPDFKit GmbH. All rights reserved.
3 | *
4 | * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
5 | * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE PSPDFKIT LICENSE AGREEMENT.
6 | * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
7 | * This notice may not be removed from this file
8 | */
9 |
10 | include ':app'
11 |
--------------------------------------------------------------------------------
/setup-guide.md:
--------------------------------------------------------------------------------
1 | ***QuickDemo is a tool for Android developers. If you don't know what this application is for, do not use it!***
2 |
3 | ## Setup
4 |
5 | To enable _QuickDemo_ you need to do two things.
6 |
7 | 1. Enable the System UI demo mode on your phone.
8 | 2. Grant the `android.permission.DUMP` to the QuickDemo app. This permissions is protected and can't be requested at runtime.
9 |
10 | This can be achieved by running the following `gradle` task.
11 |
12 | ```
13 | ./gradlew setupDemoMode
14 | ```
15 |
16 | If you don't have the project. Both steps can be performed manually from command line via `adb`.
17 |
18 | ```
19 | # Ensure demo mode is activated.
20 | adb shell settings put global sysui_demo_allowed 1
21 |
22 | # Grant required permissions.
23 | adb shell pm grant com.pspdfkit.labs.quickdemo android.permission.DUMP
24 | ```
25 |
26 | **Please note:** The permissions is granted until you revoke it or uninstall the app.
27 |
--------------------------------------------------------------------------------
/setup.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | if [[ $1 ]] || [[ ${ANDROID_HOME} ]];
4 | then
5 | cd ~
6 | git clone git@github.com:PSPDFKit-labs/QuickDemo.git
7 | cd QuickDemo/
8 | if [[ $1 ]]; then echo "sdk.dir="$1 >>local.properties
9 | fi
10 | ./gradlew installDebug
11 | ./gradlew setupDemoMode
12 | cd ~
13 | yes | rm -r QuickDemo
14 | else
15 | echo "Missing ANDROID_HOME"
16 | echo "Usage: ./quick_demo.sh ANDROID_HOME"
17 | echo "or set an ANROID_HOME environment variable"
18 | fi
19 |
--------------------------------------------------------------------------------
/showcase.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PSPDFKit-labs/QuickDemo/8fda952e4019f3b6437d1605be86165764dd88ce/showcase.gif
--------------------------------------------------------------------------------