Skip to content

Commit

Permalink
add ability to define custom rules
Browse files Browse the repository at this point in the history
  • Loading branch information
BigBoot committed Jan 8, 2022
1 parent 0d927ff commit 558b3d2
Show file tree
Hide file tree
Showing 16 changed files with 644 additions and 32 deletions.
14 changes: 12 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties
plugins {
id("com.android.application")
id("kotlin-android")
id("com.google.devtools.ksp").version("1.6.10-1.0.2")
}

val properties = gradleLocalProperties(rootDir)
Expand Down Expand Up @@ -43,10 +44,19 @@ android {
}

dependencies {
implementation("androidx.core:core-ktx:1.7.0")
implementation("com.google.android.gms:play-services-wearable:17.1.0")
implementation("androidx.core:core-ktx:1.7.0")
implementation("androidx.wear:wear:1.2.0")
implementation("androidx.percentlayout:percentlayout:1.0.0")
implementation("androidx.legacy:legacy-support-v4:1.0.0")
implementation("androidx.recyclerview:recyclerview:1.2.1")
implementation("androidx.wear:wear:1.2.0")
implementation("androidx.activity:activity-ktx:1.4.0")
implementation("androidx.fragment:fragment-ktx:1.4.0")
implementation("androidx.appcompat:appcompat:1.4.0")
implementation("com.google.android.material:material:1.4.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.2")
implementation("com.squareup.moshi:moshi-kotlin:1.13.0") {
// exclude("org.jetbrains.kotlin", "kotlin-reflect ")
}
ksp("com.squareup.moshi:moshi-kotlin-codegen:1.13.0")
}
25 changes: 14 additions & 11 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,46 +8,49 @@

<application
android:allowBackup="true"
android:fullBackupOnly="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@android:style/Theme.DeviceDefault">

<uses-library
android:name="com.google.android.wearable"
android:required="true" />

<!--
Set to true if your app is Standalone, that is, it does not require the handheld
app to run.
-->
<meta-data
android:name="com.google.android.wearable.standalone"
android:value="true" />

<activity
android:name="de.bigboot.watch4payswitch.MainActivity"
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name">
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat.Light">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<activity
android:name=".SelectPredefinedActivity"
android:exported="false"
android:theme="@style/Theme.AppCompat.Light" />

<service android:name="de.bigboot.watch4payswitch.AccessibilityService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
<service
android:name=".AccessibilityService"
android:exported="false"
android:label="@string/accessibility_service_label"
android:exported="false">
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>

<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accessibility_service_config" />
</service>

</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@ import android.accessibilityservice.AccessibilityService
import android.content.Intent
import android.util.Log
import android.view.accessibility.AccessibilityEvent
import java.util.*

class AccessibilityService : AccessibilityService() {
private var quickPanelVisible = false

private var rules: List<ActivityRule> = emptyList()
private var rulesRevision: UUID = UUID.randomUUID()

override fun onServiceConnected() {
super.onServiceConnected()
Expand All @@ -20,18 +25,47 @@ class AccessibilityService : AccessibilityService() {
override fun onInterrupt() {}

override fun onAccessibilityEvent(event: AccessibilityEvent) {
Log.v(this::class.simpleName, "Active package changed: ${event.packageName}")
Log.v(this::class.simpleName, event.toString())

if(event.packageName == getString(R.string.source_package_name))
if(event.packageName == "com.google.android.apps.wearable.systemui")
{
if(event.contentChangeTypes and AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_APPEARED != 0)
{
quickPanelVisible = true
}

if(event.contentChangeTypes and AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED != 0)
{
quickPanelVisible = false
}
}

getAppPreferences().let { prefs ->
if(prefs.revision() != rulesRevision) {
rulesRevision = prefs.revision()
rules = prefs.getRules()
}
}

val rule = rules.firstOrNull {
it.source.packageName == event.packageName ||
(it.source.packageName == PredefinedSources.POWER_MENU.packageName
&& !quickPanelVisible
&& event.packageName == "com.google.android.apps.wearable.systemui"
&& event.className == "com.google.android.clockwork.systemui.globalactions.dialog.GlobalActionDialog")
}

if(rule != null)
{
if(rule.source.packageName == PredefinedSources.POWER_MENU.packageName)
{
performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK)
}

startActivity(Intent().apply {
setClassName(
getString(R.string.target_package_name),
getString(R.string.target_activity)
)
setClassName(rule.target.packageName, rule.target.activityName)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})

}
}
}
126 changes: 126 additions & 0 deletions app/src/main/kotlin/de/bigboot/watch4payswitch/ActivityRuleFragment.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package de.bigboot.watch4payswitch

import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.contract.ActivityResultContract
import androidx.core.widget.doOnTextChanged
import de.bigboot.watch4payswitch.databinding.FragmentActivityRuleBinding
import java.lang.IndexOutOfBoundsException
import java.util.*

private const val ARG_RULE_ID = "RULE_ID"

private abstract class SelectPredefined : ActivityResultContract<Unit?, String?>() {
override fun parseResult(resultCode: Int, intent: Intent?) : String? {
if (resultCode != Activity.RESULT_OK) {
return null
}
return intent?.getStringExtra(SelectPredefinedActivity.EXTRA_SELECTED_ACTIVITY) ?: ""
}
}

private class SelectPredefinedSource : SelectPredefined() {
override fun createIntent(context: Context, input: Unit?) =
Intent(context, SelectPredefinedActivity::class.java).apply {
putExtra(SelectPredefinedActivity.EXTRA_ACTIVITY_TYPE, SelectPredefinedActivity.ActivityType.Source.name)
}
}

private class SelectPredefinedTarget : SelectPredefined() {
override fun createIntent(context: Context, input: Unit?) =
Intent(context, SelectPredefinedActivity::class.java).apply {
putExtra(SelectPredefinedActivity.EXTRA_ACTIVITY_TYPE, SelectPredefinedActivity.ActivityType.Target.name)
}
}


class ActivityRuleFragment : Fragment() {
var onDelete: (()->Unit)? = null

private lateinit var ruleId: UUID
private lateinit var binding: FragmentActivityRuleBinding

private val selectPredefinedSource = registerForActivityResult(SelectPredefinedSource()) {
it?.let {
binding.texteditRuleSource.setText(it)
}
}

private val selectPredefinedTarget = registerForActivityResult(SelectPredefinedTarget()) {
it?.let {
binding.texteditRuleTarget.setText(it)
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
ruleId = UUID.fromString(it.getString(ARG_RULE_ID))
}
}

@SuppressLint("SetTextI18n")
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentActivityRuleBinding.inflate(inflater, container, false)

val rule = context?.getAppPreferences()?.getRule(ruleId)

if(rule != null)
{
binding.texteditRuleSource.setText(rule.source.packageName)
binding.texteditRuleTarget.setText("${rule.target.packageName}/${rule.target.activityName}")

binding.texteditRuleSource.doOnTextChanged { _, _, _, _ -> saveRule() }
binding.texteditRuleTarget.doOnTextChanged { _, _, _, _ -> saveRule() }

binding.fabPredefinedSource.setOnClickListener { selectPredefinedSource.launch(null) }
binding.fabPredefinedTarget.setOnClickListener { selectPredefinedTarget.launch(null) }
}

binding.buttonDeleteRule.setOnClickListener {
onDelete?.invoke()
}

return binding.root
}

private fun saveRule() {

try {
val rule = context?.getAppPreferences()?.getRule(ruleId)!!
val sourcePackageName = binding.texteditRuleSource.text.toString()
val (targetPackageName, targetActivityName) = binding.texteditRuleTarget.text.toString()
.split('/').take(2)

context?.getAppPreferences()?.saveRule(rule.copy(
source = rule.source.copy(
packageName = sourcePackageName.toString()
),
target = rule.target.copy(
packageName = targetPackageName,
activityName = targetActivityName
)
))
} catch (_: IndexOutOfBoundsException) {}
}

companion object {
@JvmStatic
fun newInstance(ruleId: UUID = UUID.randomUUID()) =
ActivityRuleFragment().apply {
arguments = Bundle().apply {
putString(ARG_RULE_ID, ruleId.toString())
}
}
}
}

0 comments on commit 558b3d2

Please sign in to comment.