From e0282a43011fe0dce86f57ae39b02dcfbc4d2497 Mon Sep 17 00:00:00 2001 From: Mustafa Ali Date: Mon, 18 Sep 2017 20:50:20 -0400 Subject: [PATCH] v1.0.4 (#22) * Updated Gradle and Kotlin deps, minor cleanup, made the commands text selectable to allow copy/paste * Added feature to view licenses * Added feature to rate the app * Added feature to request a feature * Added Timber * New features list implementation, Circle CI setup * Added build status badge * Added help menu item, bumped version numbers --- .circleci/config.yml | 27 ++++++++ README.md | 2 + app/build.gradle | 20 ++++-- app/src/main/AndroidManifest.xml | 1 + .../mustafaali/devqstiles/DevQSApplication.kt | 13 ++++ .../xyz/mustafaali/devqstiles/MainActivity.kt | 69 +++++++++++++++++-- .../mustafaali/devqstiles/model/Feature.kt | 3 + .../service/ToggleAnimatorDurationService.kt | 6 +- .../service/ToggleDemoModeService.kt | 6 +- .../service/ToggleKeepScreenOnService.kt | 6 +- .../service/ToggleShowTapsService.kt | 20 +++--- .../service/ToggleUsbDebuggingService.kt | 6 +- .../devqstiles/ui/FeaturesAdapter.kt | 27 ++++++++ .../devqstiles/util/AnimatorDurationScaler.kt | 15 ++-- .../mustafaali/devqstiles/util/Extensions.kt | 9 +++ app/src/main/res/drawable/ic_help_outline.xml | 9 +++ app/src/main/res/layout/activity_main.xml | 9 ++- .../main/res/layout/feature_item_layout.xml | 39 +++++++++++ app/src/main/res/menu/main.xml | 20 +++++- .../main/res/raw/third_party_license_metadata | 17 +++++ app/src/main/res/raw/third_party_licenses | 18 +++++ app/src/main/res/values/strings.xml | 20 ++---- build.gradle | 14 ++-- gradle/wrapper/gradle-wrapper.properties | 4 +- 24 files changed, 300 insertions(+), 80 deletions(-) create mode 100644 .circleci/config.yml create mode 100644 app/src/main/kotlin/xyz/mustafaali/devqstiles/DevQSApplication.kt create mode 100644 app/src/main/kotlin/xyz/mustafaali/devqstiles/model/Feature.kt create mode 100644 app/src/main/kotlin/xyz/mustafaali/devqstiles/ui/FeaturesAdapter.kt create mode 100644 app/src/main/kotlin/xyz/mustafaali/devqstiles/util/Extensions.kt create mode 100644 app/src/main/res/drawable/ic_help_outline.xml create mode 100644 app/src/main/res/layout/feature_item_layout.xml create mode 100644 app/src/main/res/raw/third_party_license_metadata create mode 100644 app/src/main/res/raw/third_party_licenses diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..975cc49 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,27 @@ +version: 2 +jobs: + build: + working_directory: ~/code + docker: + - image: circleci/android:api-26-alpha + environment: + JVM_OPTS: -Xmx3200m + steps: + - checkout + - restore_cache: + key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }} + - run: + name: Download Dependencies + command: ./gradlew androidDependencies + - save_cache: + paths: + - ~/.gradle + key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }} + - run: + name: Compile & Run Tests + command: ./gradlew assemble test + - store_artifacts: + path: app/build/reports + destination: reports + - store_test_results: + path: app/build/test-results \ No newline at end of file diff --git a/README.md b/README.md index 7de8055..11c9e87 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ Android Quick Settings tiles for developers. Works on Android 7.0+. +[![CircleCI](https://circleci.com/gh/mustafa01ali/Dev-Tiles.svg?style=svg)](https://circleci.com/gh/mustafa01ali/Dev-Tiles) + ## Usage 1. Install the app 2. Grant permissions - `adb shell pm grant xyz.mustafaali.devqstiles android.permission.WRITE_SECURE_SETTINGS`, `adb shell pm grant xyz.mustafaali.devqstiles android.permission.DUMP` diff --git a/app/build.gradle b/app/build.gradle index d39234d..b244a3b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,9 +1,15 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' +apply plugin: 'com.google.gms.oss.licenses.plugin' + +repositories { + mavenCentral() + google() +} android { - compileSdkVersion 25 + compileSdkVersion 26 buildToolsVersion "25.0.3" defaultConfig { applicationId "xyz.mustafaali.devqstiles" @@ -12,8 +18,8 @@ android { // Writing show taps setting fails on higher versions targetSdkVersion 22 - versionCode 5 - versionName "1.0.3" + versionCode 6 + versionName "1.0.4" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { @@ -36,10 +42,10 @@ dependencies { }) compile "com.android.support:appcompat-v7:$support_lib_version" + compile "com.android.support:recyclerview-v7:$support_lib_version" compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" + compile 'com.google.android.gms:play-services-oss-licenses:11.2.2' + compile 'com.jakewharton.timber:timber:4.5.1' testCompile 'junit:junit:4.12' -} -repositories { - mavenCentral() -} +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 65b55e9..e45a95e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,6 +11,7 @@ tools:ignore="ProtectedPermissions" /> { + openHelpVideo() + true + } R.id.menu_share_app -> { shareApp() - return true + true + } + R.id.menu_rate_app -> { + openStoreListing() + true + } + R.id.menu_request_feature -> { + openEmailClient() + true } - else -> return super.onOptionsItemSelected(item) + R.id.menu_oss_licenses -> { + startActivity(Intent(this, OssLicensesMenuActivity::class.java)) + true + } + else -> super.onOptionsItemSelected(item) + } + } + + private fun openStoreListing() { + val intent = Intent(Intent.ACTION_VIEW) + intent.data = Uri.parse("market://details?id=xyz.mustafaali.devqstiles") + try { + startActivity(intent) + } catch(e: ActivityNotFoundException) { + Timber.e("Couldn't launch activity, maybe PlayStore is not installed") } } - fun initUi() { + private fun openHelpVideo() { + startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://www.youtube.com/watch?v=tdSAobQq1nQ"))) + } + + private fun openEmailClient() { + val intent = Intent(Intent.ACTION_SEND) + intent.type = "message/rfc822" + intent.putExtra(android.content.Intent.EXTRA_EMAIL, arrayOf("mustafaali.apps@gmail.com")) + intent.putExtra(Intent.EXTRA_SUBJECT, "[DevTiles] Feature Request") + startActivity(intent) + } + + private fun initUi() { copyButton.setOnClickListener({ sharePermissionsCommand() }) - featuresDescriptionTextView.text = Html.fromHtml(getString(R.string.features_description), Html.FROM_HTML_MODE_COMPACT) + featuresRecyclerView.layoutManager = LinearLayoutManager(this) + featuresRecyclerView.setHasFixedSize(true) + featuresRecyclerView.adapter = FeaturesAdapter(getFeaturesList()) {} } private fun shareApp() { @@ -51,4 +98,14 @@ class MainActivity : AppCompatActivity() { sendIntent.type = "text/plain" startActivity(sendIntent) } + + private fun getFeaturesList(): List { + return listOf( + Feature("Toggle USB Debugging", "Enable/disable USB debugging from your notification drawer", R.drawable.ic_toggle_usb_debugging), + Feature("Keep Screen On", "Keep screen on when connected via USB, but turn it off when connected to a charger", R.drawable.ic_toggle_keep_screen_on), + Feature("Show Touches", "Show touch points when you touch the screen, ideal for demos", R.drawable.ic_toggle_show_taps), + Feature("Demo Mode", "Cleans up the status bar for those perfect screenshots", R.drawable.ic_toggle_demo_mode), + Feature("Change Animator Duration", "Change the default animator duration to easily debug animations", R.drawable.ic_animator_duration) + ) + } } diff --git a/app/src/main/kotlin/xyz/mustafaali/devqstiles/model/Feature.kt b/app/src/main/kotlin/xyz/mustafaali/devqstiles/model/Feature.kt new file mode 100644 index 0000000..a40e976 --- /dev/null +++ b/app/src/main/kotlin/xyz/mustafaali/devqstiles/model/Feature.kt @@ -0,0 +1,3 @@ +package xyz.mustafaali.devqstiles.model + +data class Feature (val title: String, val description: String, val drawableId: Int) diff --git a/app/src/main/kotlin/xyz/mustafaali/devqstiles/service/ToggleAnimatorDurationService.kt b/app/src/main/kotlin/xyz/mustafaali/devqstiles/service/ToggleAnimatorDurationService.kt index 50f2ff4..5ba8e3c 100644 --- a/app/src/main/kotlin/xyz/mustafaali/devqstiles/service/ToggleAnimatorDurationService.kt +++ b/app/src/main/kotlin/xyz/mustafaali/devqstiles/service/ToggleAnimatorDurationService.kt @@ -1,5 +1,6 @@ package xyz.mustafaali.devqstiles.service +import android.annotation.SuppressLint import android.graphics.drawable.Icon import android.service.quicksettings.TileService import android.support.v7.app.AlertDialog @@ -15,7 +16,7 @@ import xyz.mustafaali.devqstiles.util.AnimatorDurationScaler.getIcon */ class ToggleAnimatorDurationService : TileService() { - val choices = arrayOf( + private val choices = arrayOf( "Animation off", "Animation scale .5x", "Animation scale 1x", @@ -25,7 +26,7 @@ class ToggleAnimatorDurationService : TileService() { "Animation scale 10x" ) - val scales = listOf( + private val scales = listOf( 0f, 0.5f, 1f, @@ -44,6 +45,7 @@ class ToggleAnimatorDurationService : TileService() { showDialog(getDialog(scales.indexOf(current))) } + @SuppressLint("RestrictedApi") private fun getDialog(selectedIndex: Int): AlertDialog { val builder = AlertDialog.Builder(ContextThemeWrapper(this, R.style.AppTheme_Dialog)) builder.setTitle(R.string.dialog_animator_duration_title) diff --git a/app/src/main/kotlin/xyz/mustafaali/devqstiles/service/ToggleDemoModeService.kt b/app/src/main/kotlin/xyz/mustafaali/devqstiles/service/ToggleDemoModeService.kt index dcc64ec..c0eea0a 100644 --- a/app/src/main/kotlin/xyz/mustafaali/devqstiles/service/ToggleDemoModeService.kt +++ b/app/src/main/kotlin/xyz/mustafaali/devqstiles/service/ToggleDemoModeService.kt @@ -9,10 +9,10 @@ import android.provider.Settings */ class ToggleDemoModeService : BaseTileService() { - val DEMO_MODE_ALLOWED = "sysui_demo_allowed" - val DEMO_MODE_ON = "sysui_tuner_demo_on" + private val DEMO_MODE_ALLOWED = "sysui_demo_allowed" + private val DEMO_MODE_ON = "sysui_tuner_demo_on" - val STATUS_ICONS = listOf( + private val STATUS_ICONS = listOf( "volume", "bluetooth", "location", diff --git a/app/src/main/kotlin/xyz/mustafaali/devqstiles/service/ToggleKeepScreenOnService.kt b/app/src/main/kotlin/xyz/mustafaali/devqstiles/service/ToggleKeepScreenOnService.kt index 55f61a7..94a8089 100644 --- a/app/src/main/kotlin/xyz/mustafaali/devqstiles/service/ToggleKeepScreenOnService.kt +++ b/app/src/main/kotlin/xyz/mustafaali/devqstiles/service/ToggleKeepScreenOnService.kt @@ -2,7 +2,7 @@ package xyz.mustafaali.devqstiles.service import android.os.BatteryManager import android.provider.Settings -import android.util.Log +import timber.log.Timber /** * Tile Service to keep the screen on when the device is connected to a USB port on a computer. @@ -10,8 +10,6 @@ import android.util.Log * If the device is connected to a wall charger, the screen will turn off. */ class ToggleKeepScreenOnService : BaseTileService() { - val TAG = javaClass.simpleName - override fun onClick() { val newValue = if (isFeatureEnabled()) 0 else BatteryManager.BATTERY_PLUGGED_USB @@ -27,7 +25,7 @@ class ToggleKeepScreenOnService : BaseTileService() { try { return Settings.Global.getInt(contentResolver, Settings.Global.STAY_ON_WHILE_PLUGGED_IN) == BatteryManager.BATTERY_PLUGGED_USB } catch (e: Settings.SettingNotFoundException) { - Log.e(TAG, e.message) + Timber.e(e, e.message) } return false } diff --git a/app/src/main/kotlin/xyz/mustafaali/devqstiles/service/ToggleShowTapsService.kt b/app/src/main/kotlin/xyz/mustafaali/devqstiles/service/ToggleShowTapsService.kt index 051abf5..ff75abc 100644 --- a/app/src/main/kotlin/xyz/mustafaali/devqstiles/service/ToggleShowTapsService.kt +++ b/app/src/main/kotlin/xyz/mustafaali/devqstiles/service/ToggleShowTapsService.kt @@ -1,34 +1,30 @@ package xyz.mustafaali.devqstiles.service import android.provider.Settings -import android.util.Log -import android.widget.Toast - -import xyz.mustafaali.devqstiles.R +import timber.log.Timber class ToggleShowTapsService : BaseTileService() { - val TAG = this.javaClass.simpleName - val SHOW_TOUCHES = "show_touches" + private val SHOW_TOUCHES = "show_touches" override fun onClick() { val newValue = if (isFeatureEnabled()) 0 else 1 try { Settings.System.putInt(contentResolver, SHOW_TOUCHES, newValue) - } catch (se: SecurityException) { + } catch (e: Exception) { showPermissionError() - Log.e(TAG, se.message) + Timber.e(e, e.message) } updateTile() } override fun isFeatureEnabled(): Boolean { - try { - return Settings.System.getInt(contentResolver, SHOW_TOUCHES) == 1 + return try { + Settings.System.getInt(contentResolver, SHOW_TOUCHES) == 1 } catch (e: Settings.SettingNotFoundException) { - e.printStackTrace() - return false + Timber.e(e, e.message) + false } } } diff --git a/app/src/main/kotlin/xyz/mustafaali/devqstiles/service/ToggleUsbDebuggingService.kt b/app/src/main/kotlin/xyz/mustafaali/devqstiles/service/ToggleUsbDebuggingService.kt index 1dd3357..302ea1e 100644 --- a/app/src/main/kotlin/xyz/mustafaali/devqstiles/service/ToggleUsbDebuggingService.kt +++ b/app/src/main/kotlin/xyz/mustafaali/devqstiles/service/ToggleUsbDebuggingService.kt @@ -1,15 +1,13 @@ package xyz.mustafaali.devqstiles.service import android.provider.Settings -import android.util.Log +import timber.log.Timber /** * Tile Service to toggle USB Debugging. */ class ToggleUsbDebuggingService : BaseTileService() { - val TAG = javaClass.simpleName - override fun onStartListening() { super.onStartListening() updateTile() @@ -21,7 +19,7 @@ class ToggleUsbDebuggingService : BaseTileService() { try { Settings.Global.putString(contentResolver, Settings.Global.ADB_ENABLED, newValue) } catch (se: SecurityException) { - Log.e(TAG, se.message) + Timber.e(se, se.message) showPermissionError() } updateTile() diff --git a/app/src/main/kotlin/xyz/mustafaali/devqstiles/ui/FeaturesAdapter.kt b/app/src/main/kotlin/xyz/mustafaali/devqstiles/ui/FeaturesAdapter.kt new file mode 100644 index 0000000..a6baa52 --- /dev/null +++ b/app/src/main/kotlin/xyz/mustafaali/devqstiles/ui/FeaturesAdapter.kt @@ -0,0 +1,27 @@ +package xyz.mustafaali.devqstiles.ui + +import android.support.v7.widget.RecyclerView +import android.view.View +import android.view.ViewGroup +import kotlinx.android.synthetic.main.feature_item_layout.view.* +import xyz.mustafaali.devqstiles.R +import xyz.mustafaali.devqstiles.model.Feature +import xyz.mustafaali.devqstiles.util.inflate + +class FeaturesAdapter(val features: List, val listener: (Feature) -> Unit) : RecyclerView.Adapter() { + class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + fun bind(feature: Feature, listener: (Feature) -> Unit) = with(itemView) { + featureImageView.setImageResource(feature.drawableId) + featureTitle.text = feature.title + featureDescription.text = feature.description + setOnClickListener { listener(feature) } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = ViewHolder(parent.inflate(R.layout.feature_item_layout)) + + override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(features[position], listener) + + override fun getItemCount(): Int = features.size + +} \ No newline at end of file diff --git a/app/src/main/kotlin/xyz/mustafaali/devqstiles/util/AnimatorDurationScaler.kt b/app/src/main/kotlin/xyz/mustafaali/devqstiles/util/AnimatorDurationScaler.kt index d363ba0..9ab4129 100644 --- a/app/src/main/kotlin/xyz/mustafaali/devqstiles/util/AnimatorDurationScaler.kt +++ b/app/src/main/kotlin/xyz/mustafaali/devqstiles/util/AnimatorDurationScaler.kt @@ -5,9 +5,8 @@ import android.content.Context import android.provider.Settings import android.support.annotation.DrawableRes import android.support.annotation.FloatRange -import android.util.Log import android.widget.Toast - +import timber.log.Timber import xyz.mustafaali.devqstiles.R /** @@ -15,8 +14,6 @@ import xyz.mustafaali.devqstiles.R */ object AnimatorDurationScaler { - private val TAG = "AnimatorDurationScaler" - @DrawableRes fun getIcon(scale: Float): Int { if (scale <= 0f) { @@ -43,7 +40,7 @@ object AnimatorDurationScaler { scale = Settings.Global.getFloat(contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE) } catch (e: Settings.SettingNotFoundException) { - Log.e(TAG, "Could not read Animator Duration Scale setting", e) + Timber.e(e, "Could not read Animator Duration Scale setting") } return scale @@ -52,15 +49,15 @@ object AnimatorDurationScaler { fun setAnimatorScale( context: Context, @FloatRange(from = 0.0, to = 10.0) scale: Float): Boolean { - try { + return try { Settings.Global.putFloat( context.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, scale) - return true + true } catch (se: SecurityException) { val message = context.getString(R.string.permission_required_toast) Toast.makeText(context.applicationContext, message, Toast.LENGTH_LONG).show() - Log.d(TAG, message) - return false + Timber.e(se, message) + false } } diff --git a/app/src/main/kotlin/xyz/mustafaali/devqstiles/util/Extensions.kt b/app/src/main/kotlin/xyz/mustafaali/devqstiles/util/Extensions.kt new file mode 100644 index 0000000..7cae945 --- /dev/null +++ b/app/src/main/kotlin/xyz/mustafaali/devqstiles/util/Extensions.kt @@ -0,0 +1,9 @@ +package xyz.mustafaali.devqstiles.util + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup + +fun ViewGroup.inflate(layoutRes: Int): View { + return LayoutInflater.from(context).inflate(layoutRes, this, false) +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_help_outline.xml b/app/src/main/res/drawable/ic_help_outline.xml new file mode 100644 index 0000000..6998234 --- /dev/null +++ b/app/src/main/res/drawable/ic_help_outline.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 37145fe..659ffe4 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -27,6 +27,7 @@ android:layout_marginTop="@dimen/activity_vertical_margin" android:fontFamily="monospace" android:text="@string/permission_command" + android:textIsSelectable="true" android:textStyle="bold" />