Skip to content
This repository has been archived by the owner on Jan 23, 2024. It is now read-only.

Commit

Permalink
broadcast
Browse files Browse the repository at this point in the history
  • Loading branch information
lucky committed Jul 11, 2022
1 parent 573458c commit 3378cfa
Show file tree
Hide file tree
Showing 30 changed files with 1,019 additions and 251 deletions.
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Volta

Additional actions on volume buttons.
Actions on volume buttons.

[<img
src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
Expand All @@ -19,18 +19,19 @@ Additional actions on volume buttons.
Tiny app to add several actions to volume buttons.

Actions:
* Long Volume Up - next track
* Long Volume Down - previous track
* Double Volume Up - flashlight
* Long Volume Up - next track
* Long Volume Down - previous track
* Double Volume Up - flashlight/broadcast
* Double Volume Down - flashlight/broadcast

Does NOT work when the screen is fully off. For a better user experience use
[Key Mapper](https://github.com/sds100/KeyMapper).

## Permissions

* ACCESSIBILITY - receive volume key events
* VIBRATE - notify on action success
* FLASHLIGHT - toggle flashlight
* VIBRATE - notify on action success
* FLASHLIGHT - toggle flashlight

## License
[![GNU GPLv3 Image](https://www.gnu.org/graphics/gplv3-127x51.png)](https://www.gnu.org/licenses/gpl-3.0.en.html)
Expand Down
5 changes: 3 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ android {
applicationId "me.lucky.volta"
minSdk 23
targetSdk 32
versionCode 6
versionName "1.0.5"
versionCode 7
versionName "1.1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
Expand Down Expand Up @@ -48,4 +48,5 @@ dependencies {
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

implementation 'androidx.preference:preference-ktx:1.2.0'
implementation 'androidx.drawerlayout:drawerlayout:1.1.1'
}
2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
</activity>

<service
android:name=".AccessibilityService"
android:name=".core.AccessibilityService"
android:exported="true"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
Expand Down
3 changes: 1 addition & 2 deletions app/src/main/java/me/lucky/volta/Application.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ package me.lucky.volta
import android.app.Application
import com.google.android.material.color.DynamicColors

@Suppress("unused")
class Application : Application() {
override fun onCreate() {
super.onCreate()
DynamicColors.applyToActivitiesIfAvailable(this)
}
}
}
144 changes: 25 additions & 119 deletions app/src/main/java/me/lucky/volta/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,24 +1,16 @@
package me.lucky.volta

import android.accessibilityservice.AccessibilityServiceInfo
import android.content.Intent
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraManager
import android.os.Bundle
import android.provider.Settings
import android.view.View
import android.view.accessibility.AccessibilityManager
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar

import me.lucky.volta.databinding.ActivityMainBinding
import me.lucky.volta.fragment.*

class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var prefs: Preferences
private var accessibilityManager: AccessibilityManager? = null
private var cameraManager: CameraManager? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand All @@ -31,93 +23,22 @@ class MainActivity : AppCompatActivity() {

private fun init() {
prefs = Preferences(this)
accessibilityManager = getSystemService(AccessibilityManager::class.java)
cameraManager = getSystemService(CameraManager::class.java)
if (!hasFlashlight()) hideFlashlight()
binding.apply {
track.isChecked = prefs.isTrackChecked
flashlight.isChecked = prefs.isFlashlightChecked
toggle.isChecked = prefs.isEnabled
}
}

private fun hasFlashlight(): Boolean {
return try {
cameraManager?.cameraIdList?.any {
cameraManager
?.getCameraCharacteristics(it)
?.get(CameraCharacteristics.FLASH_INFO_AVAILABLE) ?: false
} ?: false
} catch (exc: Exception) { false }
replaceFragment(MainFragment())
}

private fun setup() {
binding.apply {
track.setOnCheckedChangeListener { _, isChecked ->
prefs.isTrackChecked = isChecked
}
track.setOnLongClickListener {
showTrackOptions()
true
}
flashlight.setOnCheckedChangeListener { _, isChecked ->
prefs.isFlashlightChecked = isChecked
}
flashlight.setOnLongClickListener {
showFlashlightOptions()
true
}
toggle.setOnCheckedChangeListener { _, isChecked ->
prefs.isEnabled = isChecked
if (isChecked && !hasPermissions())
startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS))
}
private fun setup() = binding.apply {
appBar.setNavigationOnClickListener {
drawer.open()
}
navigation.setNavigationItemSelectedListener {
replaceFragment(getFragment(it.itemId))
it.isChecked = true
drawer.close()
true
}
}

private fun showTrackOptions() {
var options = prefs.trackOptions
val values = TrackOption.values()
MaterialAlertDialogBuilder(this)
.setTitle(R.string.track)
.setMultiChoiceItems(
resources.getStringArray(R.array.track_options),
values.map { options.and(it.value) != 0 }.toBooleanArray()
) { _, index, isChecked ->
val flag = values[index]
options = when (isChecked) {
true -> options.or(flag.value)
false -> options.and(flag.value.inv())
}
}
.setPositiveButton(android.R.string.ok) { _, _ ->
prefs.trackOptions = options
}
.show()
}

private fun showFlashlightOptions() {
var options = prefs.flashlightOptions
val values = TrackOption.values()
MaterialAlertDialogBuilder(this)
.setTitle(R.string.flashlight)
.setMultiChoiceItems(
resources.getStringArray(R.array.flashlight_options),
values.map { options.and(it.value) != 0 }.toBooleanArray()
) { _, index, isChecked ->
val flag = values[index]
options = when (isChecked) {
true -> options.or(flag.value)
false -> options.and(flag.value.inv())
}
}
.setPositiveButton(android.R.string.ok) { _, _ ->
prefs.flashlightOptions = options
}
.show()
}

private fun showProminentDisclosure() {
private fun showProminentDisclosure() =
MaterialAlertDialogBuilder(this)
.setTitle(R.string.prominent_disclosure_title)
.setMessage(R.string.prominent_disclosure_message)
Expand All @@ -128,33 +49,18 @@ class MainActivity : AppCompatActivity() {
finishAndRemoveTask()
}
.show()
}

private fun hideFlashlight() {
binding.flashlight.visibility = View.GONE
binding.flashlightDescription.visibility = View.GONE
}

override fun onStart() {
super.onStart()
update()
}
private fun replaceFragment(f: Fragment) =
supportFragmentManager
.beginTransaction()
.replace(binding.fragment.id, f)
.commit()

private fun update() {
if (prefs.isEnabled && !hasPermissions())
Snackbar.make(
binding.toggle,
R.string.service_unavailable_popup,
Snackbar.LENGTH_SHORT,
).show()
}

private fun hasPermissions(): Boolean {
for (info in accessibilityManager?.getEnabledAccessibilityServiceList(
AccessibilityServiceInfo.FEEDBACK_GENERIC,
) ?: return true) {
if (info.resolveInfo.serviceInfo.packageName == packageName) return true
}
return false
private fun getFragment(id: Int) = when (id) {
R.id.nav_main -> MainFragment()
R.id.nav_track -> TrackFragment()
R.id.nav_double_up -> DoubleUpFragment()
R.id.nav_double_down -> DoubleDownFragment()
else -> MainFragment()
}
}
}
97 changes: 84 additions & 13 deletions app/src/main/java/me/lucky/volta/Preferences.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,33 @@ import androidx.preference.PreferenceManager
class Preferences(ctx: Context) {
companion object {
private const val ENABLED = "enabled"

private const val TRACK_CHECKED = "track_checked"
private const val TRACK_OPTIONS = "track_options"
private const val FLASHLIGHT_CHECKED = "flashlight_checked"
private const val FLASHLIGHT_OPTIONS = "flashlight_options"

private const val DOUBLE_UP_CHECKED = "double_up_checked"
private const val DOUBLE_UP_OPTIONS = "double_up_options"
private const val DOUBLE_UP_MODE = "double_up_mode"

private const val DOUBLE_DOWN_CHECKED = "double_down_checked"
private const val DOUBLE_DOWN_OPTIONS = "double_down_options"
private const val DOUBLE_DOWN_MODE = "double_down_mode"

private const val BROADCAST_0_ACTION = "broadcast_0_action"
private const val BROADCAST_0_RECEIVER = "broadcast_0_receiver"
private const val BROADCAST_0_KEY = "broadcast_0_key"
private const val BROADCAST_0_VALUE = "broadcast_0_value"

private const val BROADCAST_1_ACTION = "broadcast_1_action"
private const val BROADCAST_1_RECEIVER = "broadcast_1_receiver"
private const val BROADCAST_1_KEY = "broadcast_1_key"
private const val BROADCAST_1_VALUE = "broadcast_1_value"

private const val SHOW_PROMINENT_DISCLOSURE = "show_prominent_disclosure"

// migration
private const val SERVICE_ENABLED = "service_enabled"
private const val FLASHLIGHT_CHECKED = "flashlight_checked"
}

private val prefs = PreferenceManager.getDefaultSharedPreferences(ctx)
Expand All @@ -28,26 +47,78 @@ class Preferences(ctx: Context) {
set(value) = prefs.edit { putBoolean(TRACK_CHECKED, value) }

var trackOptions: Int
get() = prefs.getInt(TRACK_OPTIONS, TrackOption.VIBRATE.value)
get() = prefs.getInt(TRACK_OPTIONS, Option.VIBRATE.value)
set(value) = prefs.edit { putInt(TRACK_OPTIONS, value) }

var isFlashlightChecked: Boolean
get() = prefs.getBoolean(FLASHLIGHT_CHECKED, false)
set(value) = prefs.edit { putBoolean(FLASHLIGHT_CHECKED, value) }
var isDoubleUpChecked: Boolean
get() = prefs.getBoolean(
DOUBLE_UP_CHECKED,
prefs.getBoolean(FLASHLIGHT_CHECKED, false),
)
set(value) = prefs.edit { putBoolean(DOUBLE_UP_CHECKED, value) }

var doubleUpOptions: Int
get() = prefs.getInt(DOUBLE_UP_OPTIONS, Option.VIBRATE.value)
set(value) = prefs.edit { putInt(DOUBLE_UP_OPTIONS, value) }

var doubleUpMode: Int
get() = prefs.getInt(DOUBLE_UP_MODE, Mode.FLASHLIGHT.value)
set(value) = prefs.edit { putInt(DOUBLE_UP_MODE, value) }

var isDoubleDownChecked: Boolean
get() = prefs.getBoolean(DOUBLE_DOWN_CHECKED, false)
set(value) = prefs.edit { putBoolean(DOUBLE_DOWN_CHECKED, value) }

var flashlightOptions: Int
get() = prefs.getInt(FLASHLIGHT_OPTIONS, FlashlightOption.VIBRATE.value)
set(value) = prefs.edit { putInt(FLASHLIGHT_OPTIONS, value) }
var doubleDownOptions: Int
get() = prefs.getInt(DOUBLE_DOWN_OPTIONS, Option.VIBRATE.value)
set(value) = prefs.edit { putInt(DOUBLE_DOWN_OPTIONS, value) }

var doubleDownMode: Int
get() = prefs.getInt(DOUBLE_DOWN_MODE, Mode.FLASHLIGHT.value)
set(value) = prefs.edit { putInt(DOUBLE_DOWN_MODE, value) }

var isShowProminentDisclosure: Boolean
get() = prefs.getBoolean(SHOW_PROMINENT_DISCLOSURE, true)
set(value) = prefs.edit { putBoolean(SHOW_PROMINENT_DISCLOSURE, value) }
}

enum class TrackOption(val value: Int) {
VIBRATE(1),
var broadcast0Action: String
get() = prefs.getString(BROADCAST_0_ACTION, "") ?: ""
set(value) = prefs.edit { putString(BROADCAST_0_ACTION, value) }

var broadcast0Receiver: String
get() = prefs.getString(BROADCAST_0_RECEIVER, "") ?: ""
set(value) = prefs.edit { putString(BROADCAST_0_RECEIVER, value) }

var broadcast0Key: String
get() = prefs.getString(BROADCAST_0_KEY, "") ?: ""
set(value) = prefs.edit { putString(BROADCAST_0_KEY, value) }

var broadcast0Value: String
get() = prefs.getString(BROADCAST_0_VALUE, "") ?: ""
set(value) = prefs.edit { putString(BROADCAST_0_VALUE, value) }

var broadcast1Action: String
get() = prefs.getString(BROADCAST_1_ACTION, "") ?: ""
set(value) = prefs.edit { putString(BROADCAST_1_ACTION, value) }

var broadcast1Receiver: String
get() = prefs.getString(BROADCAST_1_RECEIVER, "") ?: ""
set(value) = prefs.edit { putString(BROADCAST_1_RECEIVER, value) }

var broadcast1Key: String
get() = prefs.getString(BROADCAST_1_KEY, "") ?: ""
set(value) = prefs.edit { putString(BROADCAST_1_KEY, value) }

var broadcast1Value: String
get() = prefs.getString(BROADCAST_1_VALUE, "") ?: ""
set(value) = prefs.edit { putString(BROADCAST_1_VALUE, value) }
}

enum class FlashlightOption(val value: Int) {
enum class Option(val value: Int) {
VIBRATE(1),
}

enum class Mode(val value: Int) {
FLASHLIGHT(0),
BROADCAST(1),
}
11 changes: 11 additions & 0 deletions app/src/main/java/me/lucky/volta/Utils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package me.lucky.volta

class Utils {
companion object {
fun setFlag(key: Int, value: Int, enabled: Boolean) =
when(enabled) {
true -> key.or(value)
false -> key.and(value.inv())
}
}
}
Loading

0 comments on commit 3378cfa

Please sign in to comment.