Skip to content

Commit 9fb405c

Browse files
committed
Add 'Show taps' tile; update README and graphic resources
1 parent b97cef2 commit 9fb405c

30 files changed

+319
-60
lines changed

README.md

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,39 @@
11
# Dev-QuickSettings
2-
Android Quick Settings Tiles for Android developers
2+
## What is this?
3+
Android Quick Settings Tiles for Android developers, written in [Kotlin](https://kotlinlang.org/).
4+
5+
![Main image](https://raw.githubusercontent.com/adriangl/Android-Dev-QuickSettings/master/graphics/screenshots/en-US_main.png)
6+
7+
The app is based on the work of [Nick Butcher](https://github.com/nickbutcher) and his [Animator Duration Quick Settings Tile](https://github.com/nickbutcher/AnimatorDurationTile) which I borrowed for the animation duration tile.
8+
9+
## Current features
10+
The app provides a few Quick Settings tiles that act as shortcut to Development Options in the Android device:
11+
* USB Debugging
12+
* Demo mode
13+
* Keep screen on
14+
* Animator duration scale
15+
* Show taps on screen
16+
* Destroy activities
17+
18+
## Where can I get it?
19+
You can get it from [Google Play](https://play.google.com/store/apps/details?id=com.adriangl.devquicksettings) or from the [Releases](https://github.com/adriangl/Android-Dev-QuickSettings/releases) page :)
20+
21+
[![Get it on Google Play](https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png)](https://play.google.com/store/apps/details?id=com.adriangl.devquicksettings)
22+
23+
24+
## License
25+
```
26+
Copyright (C) 2017 Adrián García
27+
28+
Licensed under the Apache License, Version 2.0 (the "License");
29+
you may not use this file except in compliance with the License.
30+
You may obtain a copy of the License at
31+
32+
http://www.apache.org/licenses/LICENSE-2.0
33+
34+
Unless required by applicable law or agreed to in writing, software
35+
distributed under the License is distributed on an "AS IS" BASIS,
36+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
37+
See the License for the specific language governing permissions and
38+
limitations under the License.
39+
```

app/build.gradle

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,17 @@
1616

1717
apply plugin: "com.android.application"
1818
apply plugin: "kotlin-android"
19+
apply from: "$rootDir/common.gradle"
1920

2021
android {
2122
compileSdkVersion 25
2223
buildToolsVersion "25.0.2"
2324
defaultConfig {
2425
applicationId "com.adriangl.devquicksettings"
2526
minSdkVersion 24
26-
targetSdkVersion 25
27-
versionCode 1
28-
versionName "1.0"
27+
targetSdkVersion 22
28+
versionCode computeVersionCode()
29+
versionName computeVersionName()
2930
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
3031
}
3132
buildTypes {

app/src/main/AndroidManifest.xml

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
<?xml version="1.0" encoding="utf-8"?>
2-
<!--
1+
<?xml version="1.0" encoding="utf-8"?><!--
32
~ Copyright (C) 2017 Adrián García
43
~
54
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,9 +18,17 @@
1918
xmlns:tools="http://schemas.android.com/tools"
2019
package="com.adriangl.devquicktiles">
2120

21+
<!--
22+
Permission needed to read/write from Settings, must be granted from adb with
23+
adb shell pm grant <package-name> android.permission.WRITE_SETTINGS
24+
-->
25+
<uses-permission
26+
android:name="android.permission.WRITE_SETTINGS"
27+
tools:ignore="ProtectedPermissions" />
28+
2229
<!--
2330
Permission needed to read/write from secure Settings, must be granted from adb with
24-
adb shell pm grant <package-name> android.permission.WRITE_SECURE_SETTING
31+
adb shell pm grant <package-name> android.permission.WRITE_SECURE_SETTINGS
2532
-->
2633
<uses-permission
2734
android:name="android.permission.WRITE_SECURE_SETTINGS"
@@ -40,6 +47,7 @@
4047
android:icon="@mipmap/ic_launcher"
4148
android:label="@string/app_name"
4249
android:name=".base.BaseApplication"
50+
android:roundIcon="@mipmap/ic_launcher_round"
4351
android:supportsRtl="true"
4452
android:theme="@style/AppTheme">
4553

@@ -93,6 +101,16 @@
93101
</intent-filter>
94102
</service>
95103

104+
<service
105+
android:icon="@drawable/ic_qs_show_taps_enabled"
106+
android:label="@string/qs_show_taps"
107+
android:name="com.adriangl.devquicktiles.tiles.show_taps.ShowTapsTileService"
108+
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
109+
<intent-filter>
110+
<action android:name="android.service.quicksettings.action.QS_TILE" />
111+
</intent-filter>
112+
</service>
113+
96114
<activity android:name=".MainActivity">
97115
<intent-filter>
98116
<action android:name="android.intent.action.MAIN" />

app/src/main/kotlin/com/adriangl/devquicktiles/MainActivity.kt

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -97,25 +97,29 @@ class MainActivity : AppCompatActivity() {
9797
private fun getDescriptionItems(): List<QsDescriptionsItem> {
9898
return listOf(
9999
QsDescriptionsItem(
100-
R.drawable.ic_qs_animator_duration_enabled,
101-
getString(R.string.qs_animator_duration),
102-
getString(R.string.qs_animator_duration_description)),
100+
R.drawable.ic_qs_usb_debugging_enabled,
101+
getString(R.string.qs_usb_debugging),
102+
getString(R.string.qs_usb_debugging_description)),
103103
QsDescriptionsItem(
104104
R.drawable.ic_qs_demo_mode_enabled,
105105
getString(R.string.qs_demo_mode),
106106
getString(R.string.qs_demo_mode_description)),
107-
QsDescriptionsItem(
108-
R.drawable.ic_qs_finish_activities_enabled,
109-
getString(R.string.qs_finish_activities),
110-
getString(R.string.qs_finish_activities_description)),
111107
QsDescriptionsItem(
112108
R.drawable.ic_qs_keep_screen_on_enabled,
113109
getString(R.string.qs_keep_screen_on),
114110
getString(R.string.qs_keep_screen_on_description)),
115111
QsDescriptionsItem(
116-
R.drawable.ic_qs_usb_debugging_enabled,
117-
getString(R.string.qs_usb_debugging),
118-
getString(R.string.qs_usb_debugging_description))
112+
R.drawable.ic_qs_animator_duration_enabled,
113+
getString(R.string.qs_animator_duration),
114+
getString(R.string.qs_animator_duration_description)),
115+
QsDescriptionsItem(
116+
R.drawable.ic_qs_show_taps_enabled,
117+
getString(R.string.qs_show_taps),
118+
getString(R.string.qs_show_taps_description)),
119+
QsDescriptionsItem(
120+
R.drawable.ic_qs_finish_activities_enabled,
121+
getString(R.string.qs_finish_activities),
122+
getString(R.string.qs_finish_activities_description))
119123
)
120124
}
121125

app/src/main/kotlin/com/adriangl/devquicktiles/tiles/DevelopmentTileService.kt

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,42 +27,36 @@ abstract class DevelopmentTileService<T : Any> : TileService() {
2727

2828
lateinit var value: T
2929

30-
override fun onTileAdded() {
31-
Timber.d("Tile added: {label=%s}", qsTile.label)
32-
}
33-
3430
override fun onStartListening() {
3531
Timber.d("Tile started listening: {label=%s, state=%s}", qsTile.label, qsTile.state)
3632
value = queryValue()
3733
updateState()
3834
}
3935

4036
override fun onClick() {
41-
try {
42-
Timber.d("Tile clicked: {label=%s, state=%s}", qsTile.label, qsTile.state)
43-
setNextValue()
44-
} catch (e: SecurityException) {
45-
Toast.makeText(applicationContext,
46-
getString(R.string.qs_permissions_not_granted),
47-
Toast.LENGTH_SHORT)
48-
.show()
49-
}
50-
}
51-
52-
override fun onTileRemoved() {
53-
Timber.d("Tile removed: {label=%s}", qsTile.label)
37+
Timber.d("Tile clicked: {label=%s, state=%s}", qsTile.label, qsTile.state)
38+
setNextValue()
5439
}
5540

5641
private fun setNextValue() {
5742
val newIndex = ((getValueList().indexOf(value) + 1) % getValueList().size)
58-
value = getValueList()[newIndex]
43+
val newValue = getValueList()[newIndex]
5944
Timber.d("New value: %s, Tile: {label=%s, state=%s}", value, qsTile.label, qsTile.state)
6045

6146
// Disable tile while setting the value
6247
qsTile.state = Tile.STATE_UNAVAILABLE
6348
qsTile.updateTile()
6449

65-
saveValue(value)
50+
try {
51+
if (saveValue(newValue)) {
52+
value = newValue
53+
}
54+
} catch (e: Exception) {
55+
val permissionNotGrantedString = getString(R.string.qs_permissions_not_granted)
56+
Toast.makeText(applicationContext, permissionNotGrantedString, Toast.LENGTH_LONG)
57+
.show()
58+
Timber.e(e, permissionNotGrantedString)
59+
}
6660

6761
updateState()
6862
}
@@ -83,7 +77,7 @@ abstract class DevelopmentTileService<T : Any> : TileService() {
8377

8478
abstract fun queryValue(): T
8579

86-
abstract fun saveValue(value: T)
80+
abstract fun saveValue(value: T): Boolean
8781

8882
abstract fun getIcon(value: T): Icon?
8983

app/src/main/kotlin/com/adriangl/devquicktiles/tiles/animatorduration/AnimatorDurationTileService.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ class AnimatorDurationTileService : DevelopmentTileService<Float>() {
3232
}
3333

3434
override fun queryValue(): Float {
35-
return SettingsUtils.getFloatSetting(contentResolver, SETTING)
35+
return SettingsUtils.getFloatFromGlobalSettings(contentResolver, SETTING)
3636
}
3737

38-
override fun saveValue(value: Float) {
39-
SettingsUtils.setFloatSetting(contentResolver, SETTING, value)
38+
override fun saveValue(value: Float): Boolean {
39+
return SettingsUtils.setFloatToGlobalSettings(contentResolver, SETTING, value)
4040
}
4141

4242
override fun getValueList(): List<Float> {

app/src/main/kotlin/com/adriangl/devquicktiles/tiles/demomode/DemoModeTileService.kt

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,24 @@ class DemoModeTileService : DevelopmentTileService<Int>() {
4040

4141
override fun queryValue(): Int {
4242
return listOf(DemoMode.DEMO_MODE_ALLOWED, DemoMode.DEMO_MODE_ON)
43-
.fold(1, { current, key -> SettingsUtils.getIntSetting(contentResolver, key) and current })
43+
.fold(1, { current, key -> SettingsUtils.getIntFromGlobalSettings(contentResolver, key) and current })
4444
}
4545

46-
override fun saveValue(value: Int) {
47-
listOf(DemoMode.DEMO_MODE_ALLOWED, DemoMode.DEMO_MODE_ON)
48-
.forEach { SettingsUtils.setIntSetting(contentResolver, it, value) }
49-
if (value != 0) {
50-
startDemoMode()
46+
override fun saveValue(value: Int): Boolean {
47+
val isSettingEnabled =
48+
listOf(DemoMode.DEMO_MODE_ALLOWED, DemoMode.DEMO_MODE_ON)
49+
.fold(true) {
50+
initial, setting -> initial && SettingsUtils.setIntToGlobalSettings(contentResolver, setting, value)
51+
}
52+
if (isSettingEnabled) {
53+
if (value != 0) {
54+
startDemoMode()
55+
} else {
56+
stopDemoMode()
57+
}
58+
return true
5159
} else {
52-
stopDemoMode()
60+
return false
5361
}
5462
}
5563

app/src/main/kotlin/com/adriangl/devquicktiles/tiles/finishactivities/FinishActivitiesTileService.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ class FinishActivitiesTileService : DevelopmentTileService<Int>() {
3232
}
3333

3434
override fun queryValue(): Int {
35-
var value = SettingsUtils.getIntSetting(contentResolver, SETTING)
35+
var value = SettingsUtils.getIntFromGlobalSettings(contentResolver, SETTING)
3636
if (value > 1) value = 1
3737
return value
3838
}
3939

40-
override fun saveValue(value: Int) {
41-
SettingsUtils.setIntSetting(contentResolver, SETTING, value)
40+
override fun saveValue(value: Int) : Boolean {
41+
return SettingsUtils.setIntToGlobalSettings(contentResolver, SETTING, value)
4242
}
4343

4444
override fun getValueList(): List<Int> {

app/src/main/kotlin/com/adriangl/devquicktiles/tiles/screenon/KeepScreenOnTileService.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ class KeepScreenOnTileService : DevelopmentTileService<Int>() {
3333
}
3434

3535
override fun queryValue(): Int {
36-
return SettingsUtils.getIntSetting(contentResolver, SETTING)
36+
return SettingsUtils.getIntFromGlobalSettings(contentResolver, SETTING)
3737
}
3838

39-
override fun saveValue(value: Int) {
40-
SettingsUtils.setIntSetting(contentResolver, SETTING, value)
39+
override fun saveValue(value: Int) : Boolean {
40+
return SettingsUtils.setIntToGlobalSettings(contentResolver, SETTING, value)
4141
}
4242

4343
override fun getValueList(): List<Int> {
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright (C) 2017 Adrián García
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.adriangl.devquicktiles.tiles.show_taps
18+
19+
import android.graphics.drawable.Icon
20+
import com.adriangl.devquicktiles.R
21+
import com.adriangl.devquicktiles.tiles.DevelopmentTileService
22+
import com.adriangl.devquicktiles.utils.SettingsUtils
23+
24+
25+
class ShowTapsTileService : DevelopmentTileService<Int>() {
26+
companion object {
27+
val SETTING = "show_touches" // This is hidden for developers, so we use the string resource
28+
}
29+
30+
override fun isActive(value: Int): Boolean {
31+
return value != 0
32+
}
33+
34+
override fun queryValue(): Int {
35+
var value = SettingsUtils.getIntFromSystemSettings(contentResolver, SETTING)
36+
if (value > 1) value = 1
37+
return value
38+
}
39+
40+
override fun saveValue(value: Int): Boolean {
41+
/*
42+
* The proper way to do this would be to check Settings.System.canWrite().
43+
* If we can write there then write the setting and if we can't, then launch an Intent to
44+
* Settings.ACTION_MANAGE_WRITE_SETTINGS to enable the app to write system settings.
45+
*
46+
* The problem is that from API 23+ we can't do that with the "show_touches" setting
47+
* (and others I suppose) as it throws an IllegalArgumentException:
48+
* You cannot change private secure settings.
49+
*
50+
* So we have to fall back to use a targetSdkVersion of 22 so we can write the setting.
51+
* Kinda hacky, but it works ¯\_(ツ)_/¯.
52+
*/
53+
54+
return SettingsUtils.setIntToSystemSettings(contentResolver, SETTING, value)
55+
}
56+
57+
override fun getValueList(): List<Int> {
58+
return listOf(0, 1)
59+
}
60+
61+
override fun getIcon(value: Int): Icon? {
62+
return Icon.createWithResource(applicationContext,
63+
if (value != 0) R.drawable.ic_qs_show_taps_enabled else R.drawable.ic_qs_show_taps_disabled)
64+
}
65+
66+
override fun getLabel(value: Int): CharSequence? {
67+
return getString(R.string.qs_show_taps)
68+
}
69+
}

app/src/main/kotlin/com/adriangl/devquicktiles/tiles/usbdebug/UsbDebuggingTileService.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ class UsbDebuggingTileService : DevelopmentTileService<Int>() {
3232
}
3333

3434
override fun queryValue(): Int {
35-
var value = SettingsUtils.getIntSetting(contentResolver, SETTING)
35+
var value = SettingsUtils.getIntFromGlobalSettings(contentResolver, SETTING)
3636
if (value > 1) value = 1
3737
return value
3838
}
3939

40-
override fun saveValue(value: Int) {
41-
SettingsUtils.setIntSetting(contentResolver, SETTING, value)
40+
override fun saveValue(value: Int) : Boolean {
41+
return SettingsUtils.setIntToGlobalSettings(contentResolver, SETTING, value)
4242
}
4343

4444
override fun getValueList(): List<Int> {

0 commit comments

Comments
 (0)