Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
## [3.0 Beta 5](https://github.com/sds100/KeyMapper/releases/tag/v3.0.0-beta.5)

#### 6 April 2025

- #1625 HTTP Request action.

###

## Bug fixes

- #1627 open camera app action does not work when device is locked

## [3.0 Beta 4](https://github.com/sds100/KeyMapper/releases/tag/v3.0.0-beta.4)

#### 2 April 2025
Expand Down
13 changes: 7 additions & 6 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ dependencies {
// kotlin stuff
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.0"

// random stuff
implementation "com.google.android.material:material:1.13.0-alpha12"
Expand All @@ -189,8 +189,9 @@ dependencies {
implementation "dev.rikka.shizuku:api:$shizuku_version"
implementation "dev.rikka.shizuku:provider:$shizuku_version"
implementation "org.lsposed.hiddenapibypass:hiddenapibypass:4.3"
proImplementation 'com.revenuecat.purchases:purchases:8.14.2'
proImplementation 'com.revenuecat.purchases:purchases:8.15.0'
proImplementation "com.airbnb.android:lottie-compose:6.6.3"
implementation("com.squareup.okhttp3:okhttp:4.12.0")

// splitties
implementation "com.louiscad.splitties:splitties-bitflags:$splitties_version"
Expand Down Expand Up @@ -220,15 +221,15 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.viewpager2:viewpager2:1.1.0"
implementation "androidx.datastore:datastore-preferences:1.1.3"
implementation "androidx.datastore:datastore-preferences:1.1.4"
implementation "androidx.core:core-splashscreen:1.0.1"
implementation "androidx.activity:activity-compose:1.10.1"
implementation "androidx.navigation:navigation-compose:2.8.9"
implementation "androidx.navigation:navigation-fragment-compose:2.8.9"
ksp "androidx.room:room-compiler:$room_version"

// Compose
Dependency composeBom = platform('androidx.compose:compose-bom-beta:2025.03.00')
Dependency composeBom = platform('androidx.compose:compose-bom-beta:2025.03.01')
implementation composeBom
implementation 'androidx.compose.foundation:foundation'
implementation "androidx.compose.ui:ui-android"
Expand All @@ -254,12 +255,12 @@ dependencies {
testImplementation "org.hamcrest:hamcrest-all:1.3"
testImplementation "androidx.test.ext:junit-ktx:$androidXTestExtKotlinRunnerVersion"
testImplementation "androidx.test:core-ktx:1.6.1"
testImplementation "org.robolectric:robolectric:4.12.1"
testImplementation "org.robolectric:robolectric:4.14.1"
testImplementation "androidx.arch.core:core-testing:2.2.0"
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion"
testImplementation "pl.pragmatists:JUnitParams:1.1.1"
testImplementation "org.mockito.kotlin:mockito-kotlin:4.0.0"
testImplementation "org.mockito:mockito-core:5.2.0"
testImplementation "org.mockito:mockito-core:5.15.2"
testImplementation "org.mockito:mockito-inline:5.2.0"

androidTestImplementation "androidx.test.ext:junit:$androidXTestExtKotlinRunnerVersion"
Expand Down
4 changes: 3 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,15 @@
<application
android:name="io.github.sds100.keymapper.KeyMapperApp"
android:allowBackup="true"
android:directBootAware="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:directBootAware="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme.NoActionBar"
android:usesCleartextTraffic="true"
tools:ignore="GoogleAppIndexingWarning">
<!-- Allow clear text traffic due to the HTTP Request Action. -->

<activity
android:name=".MainActivity"
Expand Down
1 change: 1 addition & 0 deletions app/src/main/assets/whats-new.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Key Mapper 3.0 is here! 🎉
🗂️ Grouping key maps into folders with shared constraints.

🔦 You can now change the flashlight brightness. Tip: use the constraint for when the flashlight is showing to remap your volume buttons to change the brightness.
🛜 Send HTTP requests with a new action.

❤️ There are also tonnes of improvements to make your key mapping experience more enjoyable.

Expand Down
17 changes: 17 additions & 0 deletions app/src/main/java/io/github/sds100/keymapper/actions/ActionData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import io.github.sds100.keymapper.system.camera.CameraLens
import io.github.sds100.keymapper.system.display.Orientation
import io.github.sds100.keymapper.system.intents.IntentExtraModel
import io.github.sds100.keymapper.system.intents.IntentTarget
import io.github.sds100.keymapper.system.network.HttpMethod
import io.github.sds100.keymapper.system.volume.DndMode
import io.github.sds100.keymapper.system.volume.RingerMode
import io.github.sds100.keymapper.system.volume.VolumeStream
Expand Down Expand Up @@ -828,4 +829,20 @@ sealed class ActionData : Comparable<ActionData> {
object DeviceControls : ActionData() {
override val id: ActionId = ActionId.DEVICE_CONTROLS
}

@Serializable
data class HttpRequest(
val description: String,
val method: HttpMethod,
val url: String,
val body: String,
val authorizationHeader: String,
) : ActionData() {
override val id: ActionId = ActionId.HTTP_REQUEST

override fun toString(): String {
// Do not leak sensitive request info to logs.
return "HttpRequest(description=$description)"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import io.github.sds100.keymapper.data.entities.getData
import io.github.sds100.keymapper.system.camera.CameraLens
import io.github.sds100.keymapper.system.intents.IntentExtraModel
import io.github.sds100.keymapper.system.intents.IntentTarget
import io.github.sds100.keymapper.system.network.HttpMethod
import io.github.sds100.keymapper.system.volume.DndMode
import io.github.sds100.keymapper.system.volume.RingerMode
import io.github.sds100.keymapper.system.volume.VolumeStream
import io.github.sds100.keymapper.util.getKey
import io.github.sds100.keymapper.util.success
import io.github.sds100.keymapper.util.then
import io.github.sds100.keymapper.util.valueOrNull
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import splitties.bitflags.hasFlag

Expand Down Expand Up @@ -496,6 +496,32 @@ object ActionDataEntityMapper {
ActionId.ANSWER_PHONE_CALL -> ActionData.AnswerCall
ActionId.END_PHONE_CALL -> ActionData.EndCall
ActionId.DEVICE_CONTROLS -> ActionData.DeviceControls
ActionId.HTTP_REQUEST -> {
val method = entity.extras.getData(ActionEntity.EXTRA_HTTP_METHOD).then {
HTTP_METHOD_MAP.getKey(it)!!.success()
}.valueOrNull() ?: return null

val description =
entity.extras.getData(ActionEntity.EXTRA_HTTP_DESCRIPTION).valueOrNull()
?: return null

val url = entity.extras.getData(ActionEntity.EXTRA_HTTP_URL).valueOrNull()
?: return null

val body = entity.extras.getData(ActionEntity.EXTRA_HTTP_BODY).valueOrNull() ?: ""

val authorizationHeader =
entity.extras.getData(ActionEntity.EXTRA_HTTP_AUTHORIZATION_HEADER)
.valueOrNull() ?: ""

ActionData.HttpRequest(
description = description,
method = method,
url = url,
body = body,
authorizationHeader = authorizationHeader,
)
}
}
}

Expand Down Expand Up @@ -713,6 +739,17 @@ object ActionDataEntityMapper {
EntityExtra(ActionEntity.EXTRA_SOUND_FILE_DESCRIPTION, data.soundDescription),
)

is ActionData.HttpRequest -> listOf(
EntityExtra(ActionEntity.EXTRA_HTTP_DESCRIPTION, data.description),
EntityExtra(ActionEntity.EXTRA_HTTP_METHOD, HTTP_METHOD_MAP[data.method]!!),
EntityExtra(ActionEntity.EXTRA_HTTP_URL, data.url),
EntityExtra(ActionEntity.EXTRA_HTTP_BODY, data.body),
EntityExtra(
ActionEntity.EXTRA_HTTP_AUTHORIZATION_HEADER,
data.authorizationHeader,
),
)

else -> emptyList()
}

Expand Down Expand Up @@ -750,6 +787,14 @@ object ActionDataEntityMapper {
IntentTarget.SERVICE to "SERVICE",
)

private val HTTP_METHOD_MAP = mapOf(
HttpMethod.GET to "GET",
HttpMethod.POST to "POST",
HttpMethod.PUT to "PUT",
HttpMethod.DELETE to "DELETE",
HttpMethod.PATCH to "PATCH",
)

/**
* DON'T CHANGE THESE
*/
Expand Down Expand Up @@ -865,5 +910,6 @@ object ActionDataEntityMapper {
ActionId.ANSWER_PHONE_CALL to "answer_phone_call",
ActionId.END_PHONE_CALL to "end_phone_call",
ActionId.DEVICE_CONTROLS to "device_controls",
ActionId.HTTP_REQUEST to "http_request",
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,6 @@ enum class ActionId {
ANSWER_PHONE_CALL,
END_PHONE_CALL,
DEVICE_CONTROLS,

HTTP_REQUEST,
}
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,7 @@ class ActionUiHelper(
ActionData.EndCall -> getString(R.string.action_end_call)

ActionData.DeviceControls -> getString(R.string.action_device_controls)
is ActionData.HttpRequest -> action.description
}

fun getIcon(action: ActionData): ComposeIconInfo = when (action) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import androidx.compose.material.icons.outlined.FlashlightOff
import androidx.compose.material.icons.outlined.FlashlightOn
import androidx.compose.material.icons.outlined.Fullscreen
import androidx.compose.material.icons.outlined.Home
import androidx.compose.material.icons.outlined.Http
import androidx.compose.material.icons.outlined.Keyboard
import androidx.compose.material.icons.outlined.KeyboardHide
import androidx.compose.material.icons.outlined.Link
Expand Down Expand Up @@ -220,6 +221,7 @@ object ActionUtils {
ActionId.TEXT_PASTE -> ActionCategory.CONTENT
ActionId.SCREENSHOT -> ActionCategory.CONTENT
ActionId.URL -> ActionCategory.CONTENT
ActionId.HTTP_REQUEST -> ActionCategory.CONTENT

ActionId.PHONE_CALL -> ActionCategory.TELEPHONY
ActionId.ANSWER_PHONE_CALL -> ActionCategory.TELEPHONY
Expand Down Expand Up @@ -339,6 +341,7 @@ object ActionUtils {
ActionId.ANSWER_PHONE_CALL -> R.string.action_answer_call
ActionId.END_PHONE_CALL -> R.string.action_end_call
ActionId.DEVICE_CONTROLS -> R.string.action_device_controls
ActionId.HTTP_REQUEST -> R.string.action_http_request
}

@DrawableRes
Expand Down Expand Up @@ -433,7 +436,6 @@ object ActionUtils {
ActionId.CONSUME_KEY_EVENT -> null
ActionId.OPEN_SETTINGS -> R.drawable.ic_outline_settings_24
ActionId.SHOW_POWER_MENU -> R.drawable.ic_outline_power_settings_new_24

ActionId.APP -> R.drawable.ic_outline_android_24
ActionId.APP_SHORTCUT -> R.drawable.ic_outline_open_in_new_24
ActionId.KEY_CODE -> R.drawable.ic_q_24
Expand All @@ -451,6 +453,7 @@ object ActionUtils {
ActionId.ANSWER_PHONE_CALL -> R.drawable.ic_outline_call_24
ActionId.END_PHONE_CALL -> R.drawable.ic_outline_call_end_24
ActionId.DEVICE_CONTROLS -> R.drawable.ic_home_automation
ActionId.HTTP_REQUEST -> null
}

fun getMinApi(id: ActionId): Int = when (id) {
Expand Down Expand Up @@ -766,6 +769,7 @@ object ActionUtils {
ActionId.ANSWER_PHONE_CALL -> Icons.Outlined.Call
ActionId.END_PHONE_CALL -> Icons.Outlined.CallEnd
ActionId.DEVICE_CONTROLS -> KeyMapperIcons.HomeIotDevice
ActionId.HTTP_REQUEST -> Icons.Outlined.Http
}
}

Expand Down Expand Up @@ -817,6 +821,7 @@ fun ActionData.isEditable(): Boolean = when (this) {
is ActionData.Text,
is ActionData.Url,
is ActionData.PhoneCall,
is ActionData.HttpRequest,
-> true

else -> false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ fun ActionsScreen(modifier: Modifier = Modifier, viewModel: ConfigActionsViewMod

EnableFlashlightActionBottomSheet(viewModel.createActionDelegate)
ChangeFlashlightStrengthActionBottomSheet(viewModel.createActionDelegate)
HttpRequestBottomSheet(viewModel.createActionDelegate)

ActionsScreen(
modifier = modifier,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ fun ChooseActionScreen(

EnableFlashlightActionBottomSheet(viewModel.createActionDelegate)
ChangeFlashlightStrengthActionBottomSheet(viewModel.createActionDelegate)
HttpRequestBottomSheet(viewModel.createActionDelegate)

ChooseActionScreen(
modifier = modifier,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import io.github.sds100.keymapper.system.camera.CameraLensUtils
import io.github.sds100.keymapper.system.display.Orientation
import io.github.sds100.keymapper.system.display.OrientationUtils
import io.github.sds100.keymapper.system.intents.ConfigIntentResult
import io.github.sds100.keymapper.system.network.HttpMethod
import io.github.sds100.keymapper.system.volume.DndMode
import io.github.sds100.keymapper.system.volume.DndModeUtils
import io.github.sds100.keymapper.system.volume.RingerMode
Expand Down Expand Up @@ -54,6 +55,8 @@ class CreateActionDelegate(
null,
)

var httpRequestBottomSheetState: ActionData.HttpRequest? by mutableStateOf(null)

init {
coroutineScope.launch {
useCase.isFlashlightEnabled().collectLatest { enabled ->
Expand Down Expand Up @@ -769,6 +772,20 @@ class CreateActionDelegate(
ActionId.ANSWER_PHONE_CALL -> return ActionData.AnswerCall
ActionId.END_PHONE_CALL -> return ActionData.EndCall
ActionId.DEVICE_CONTROLS -> return ActionData.DeviceControls
ActionId.HTTP_REQUEST -> {
if (oldData == null) {
httpRequestBottomSheetState = ActionData.HttpRequest(
description = "",
method = HttpMethod.GET,
url = "",
body = "",
authorizationHeader = "",
)
} else {
httpRequestBottomSheetState = oldData as? ActionData.HttpRequest
}
return null
}
}
}
}
Loading