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
2 changes: 1 addition & 1 deletion differences.md
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ Android lets only certain apps, such as smartwatch companions, change where call
adb shell appops set fyi.quin.backtalk MANAGE_ONGOING_CALLS allow
```

Then turn on **Sound and vibration** > **Speaker when away from your ear**. Until you grant the permission, the setting is unavailable and shows the command. This feature requires Android 12 or later.
Then turn on **Sound and vibration** > **Speaker when away from your ear**. Until you grant the permission, the setting is unavailable and shows the command, and its **Copy command** action copies the command to the clipboard. This feature requires Android 12 or later.

## Direct touch

Expand Down
20 changes: 20 additions & 0 deletions lint-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,26 @@
line="177"
column="13"/>
</issue>
<issue
id="MissingTranslation"
message="&quot;action_copy_command&quot; is not translated in &quot;de&quot; (German), &quot;hi&quot; (Hindi), &quot;lo&quot; (Lao), &quot;pt&quot; (Portuguese), &quot;fil&quot; (Filipino; Pilipino), &quot;lt&quot; (Lithuanian), &quot;hr&quot; (Croatian), &quot;lv&quot; (Latvian), &quot;hu&quot; (Hungarian), &quot;hy&quot; (Armenian), &quot;uk&quot; (Ukrainian), &quot;id&quot; (Indonesian), &quot;ur&quot; (Urdu), &quot;mk&quot; (Macedonian), &quot;ml&quot; (Malayalam), &quot;mn&quot; (Mongolian), &quot;af&quot; (Afrikaans), &quot;in&quot; (Indonesian), &quot;mr&quot; (Marathi), &quot;uz&quot; (Uzbek), &quot;ms&quot; (Malay), &quot;el&quot; (Greek), &quot;en&quot; (English), &quot;is&quot; (Icelandic), &quot;it&quot; (Italian), &quot;am&quot; (Amharic), &quot;my&quot; (Burmese), &quot;es&quot; (Spanish), &quot;iw&quot; (Hebrew), &quot;zh&quot; (Chinese), &quot;et&quot; (Estonian), &quot;eu&quot; (Basque), &quot;ar&quot; (Arabic), &quot;as&quot; (Assamese), &quot;vi&quot; (Vietnamese), &quot;nb&quot; (Norwegian Bokmål), &quot;ja&quot; (Japanese), &quot;ne&quot; (Nepali), &quot;az&quot; (Azerbaijani), &quot;fa&quot; (Persian), &quot;zu&quot; (Zulu), &quot;ro&quot; (Romanian), &quot;nl&quot; (Dutch), &quot;no&quot; (Norwegian), &quot;be&quot; (Belarusian), &quot;fi&quot; (Finnish), &quot;ru&quot; (Russian), &quot;bg&quot; (Bulgarian), &quot;bn&quot; (Bangla), &quot;fr&quot; (French), &quot;bs&quot; (Bosnian), &quot;ka&quot; (Georgian), &quot;si&quot; (Sinhala), &quot;sk&quot; (Slovak), &quot;sl&quot; (Slovenian), &quot;ca&quot; (Catalan), &quot;sq&quot; (Albanian), &quot;sr&quot; (Serbian), &quot;kk&quot; (Kazakh), &quot;km&quot; (Khmer), &quot;kn&quot; (Kannada), &quot;or&quot; (Odia), &quot;sv&quot; (Swedish), &quot;ko&quot; (Korean), &quot;sw&quot; (Swahili), &quot;gl&quot; (Galician), &quot;ta&quot; (Tamil), &quot;gu&quot; (Gujarati), &quot;ky&quot; (Kyrgyz), &quot;cs&quot; (Czech), &quot;pa&quot; (Punjabi), &quot;te&quot; (Telugu), &quot;th&quot; (Thai), &quot;tl&quot; (Tagalog), &quot;pl&quot; (Polish), &quot;da&quot; (Danish), &quot;tr&quot; (Turkish)"
errorLine1=" &lt;string name=&quot;action_copy_command&quot;>Copy command&lt;/string>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="402"
column="13"/>
</issue>
<issue
id="MissingTranslation"
message="&quot;command_copied&quot; is not translated in &quot;de&quot; (German), &quot;hi&quot; (Hindi), &quot;lo&quot; (Lao), &quot;pt&quot; (Portuguese), &quot;fil&quot; (Filipino; Pilipino), &quot;lt&quot; (Lithuanian), &quot;hr&quot; (Croatian), &quot;lv&quot; (Latvian), &quot;hu&quot; (Hungarian), &quot;hy&quot; (Armenian), &quot;uk&quot; (Ukrainian), &quot;id&quot; (Indonesian), &quot;ur&quot; (Urdu), &quot;mk&quot; (Macedonian), &quot;ml&quot; (Malayalam), &quot;mn&quot; (Mongolian), &quot;af&quot; (Afrikaans), &quot;in&quot; (Indonesian), &quot;mr&quot; (Marathi), &quot;uz&quot; (Uzbek), &quot;ms&quot; (Malay), &quot;el&quot; (Greek), &quot;en&quot; (English), &quot;is&quot; (Icelandic), &quot;it&quot; (Italian), &quot;am&quot; (Amharic), &quot;my&quot; (Burmese), &quot;es&quot; (Spanish), &quot;iw&quot; (Hebrew), &quot;zh&quot; (Chinese), &quot;et&quot; (Estonian), &quot;eu&quot; (Basque), &quot;ar&quot; (Arabic), &quot;as&quot; (Assamese), &quot;vi&quot; (Vietnamese), &quot;nb&quot; (Norwegian Bokmål), &quot;ja&quot; (Japanese), &quot;ne&quot; (Nepali), &quot;az&quot; (Azerbaijani), &quot;fa&quot; (Persian), &quot;zu&quot; (Zulu), &quot;ro&quot; (Romanian), &quot;nl&quot; (Dutch), &quot;no&quot; (Norwegian), &quot;be&quot; (Belarusian), &quot;fi&quot; (Finnish), &quot;ru&quot; (Russian), &quot;bg&quot; (Bulgarian), &quot;bn&quot; (Bangla), &quot;fr&quot; (French), &quot;bs&quot; (Bosnian), &quot;ka&quot; (Georgian), &quot;si&quot; (Sinhala), &quot;sk&quot; (Slovak), &quot;sl&quot; (Slovenian), &quot;ca&quot; (Catalan), &quot;sq&quot; (Albanian), &quot;sr&quot; (Serbian), &quot;kk&quot; (Kazakh), &quot;km&quot; (Khmer), &quot;kn&quot; (Kannada), &quot;or&quot; (Odia), &quot;sv&quot; (Swedish), &quot;ko&quot; (Korean), &quot;sw&quot; (Swahili), &quot;gl&quot; (Galician), &quot;ta&quot; (Tamil), &quot;gu&quot; (Gujarati), &quot;ky&quot; (Kyrgyz), &quot;cs&quot; (Czech), &quot;pa&quot; (Punjabi), &quot;te&quot; (Telugu), &quot;th&quot; (Thai), &quot;tl&quot; (Tagalog), &quot;pl&quot; (Polish), &quot;da&quot; (Danish), &quot;tr&quot; (Turkish)"
errorLine1=" &lt;string name=&quot;command_copied&quot;>Command copied&lt;/string>"
errorLine2=" ~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="404"
column="13"/>
</issue>

<issue
id="MissingTranslation"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2026 Backtalk contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

package com.google.android.accessibility.talkback.preference.base

import android.content.Context
import android.os.Build
import android.util.AttributeSet
import android.widget.Toast
import androidx.core.view.ViewCompat
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat
import androidx.core.view.accessibility.AccessibilityViewCommand
import androidx.preference.PreferenceViewHolder
import com.google.android.accessibility.material.preference.AccessibilitySuiteSwitchPreference
import com.google.android.accessibility.talkback.R
import com.google.android.accessibility.talkback.utils.ClipboardUtils

/**
* A switch for a setting that needs a permission granted with adb. While [command] is set, the
* switch has a "Copy command" action that copies it to the clipboard.
*/
class CommandSwitchPreference
@JvmOverloads
constructor(context: Context, attrs: AttributeSet? = null) :
AccessibilitySuiteSwitchPreference(context, attrs) {

/** The adb command that grants the permission, or null once it is granted. */
var command: String? = null
set(value) {
if (field != value) {
field = value
notifyChanged()
}
}

override fun onBindViewHolder(holder: PreferenceViewHolder) {
super.onBindViewHolder(holder)
// Rows are reused for other preferences, so every bind removes or replaces the action.
ViewCompat.removeAccessibilityAction(holder.itemView, ACTION_COPY_COMMAND)
val text = command ?: return
val label = context.getString(R.string.action_copy_command)
ViewCompat.replaceAccessibilityAction(
holder.itemView,
AccessibilityActionCompat(ACTION_COPY_COMMAND, label),
label,
AccessibilityViewCommand { _, _ ->
copy(text)
true
},
)
}

private fun copy(text: String) {
if (!ClipboardUtils.copyToClipboard(context, text)) return
// From Android 13 the system confirms copies itself.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
Toast.makeText(context, R.string.command_copied, Toast.LENGTH_SHORT).show()
}
}

private companion object {
val ACTION_COPY_COMMAND = R.id.accessibility_custom_action_0
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {

/**
* The speakerphone setting needs a permission that the user grants with adb, and Android only has
* it from Android 12. Until it is granted, the setting is off and says how to grant it.
* it from Android 12. Until it is granted, the setting is off, says how to grant it and has an
* action that copies the command.
*/
private void updateSpeakerphonePreference() {
Preference pref = findPreference(getString(R.string.pref_speakerphone_away_from_ear_key));
Expand All @@ -106,13 +107,15 @@ private void updateSpeakerphonePreference() {
return;
}
boolean granted = SpeakerphoneSettings.INSTANCE.canControlCalls(getContext());
String command = SpeakerphoneSettings.INSTANCE.grantCommand(getContext());
pref.setEnabled(granted);
pref.setSummary(
granted
? getString(R.string.summary_pref_speakerphone_away_from_ear)
: getString(
R.string.summary_pref_speakerphone_needs_permission,
SpeakerphoneSettings.INSTANCE.grantCommand(getContext())));
: getString(R.string.summary_pref_speakerphone_needs_permission, command));
if (pref instanceof CommandSwitchPreference) {
((CommandSwitchPreference) pref).setCommand(granted ? null : command);
}
}

private void updateAudioOutputPreference() {
Expand Down
4 changes: 4 additions & 0 deletions talkback/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,10 @@
<string name="summary_pref_speakerphone_away_from_ear">During a call, switches to the speaker when you take the phone away from your ear, and back to the earpiece when you hold it up again.</string>
<!-- Summary of that setting when Backtalk does not have the permission. %1$s is the adb command that grants it. [CHAR LIMIT=NONE] -->
<string name="summary_pref_speakerphone_needs_permission">Needs a permission that Android does not give to screen readers. To grant it, run this command with adb or Shizuku, then come back to this screen: %1$s</string>
<!-- Action on a setting that needs a permission granted with adb. Copies the adb command to the clipboard. [CHAR LIMIT=NONE] -->
<string name="action_copy_command">Copy command</string>
<!-- Shown after the adb command was copied to the clipboard. [CHAR LIMIT=NONE] -->
<string name="command_copied">Command copied</string>
<!-- Title of the setting that lets other apps speak through Backtalk. [CHAR LIMIT=NONE] -->
<string name="title_pref_speech_control">Let other apps speak</string>
<!-- Summary of that setting. [CHAR LIMIT=NONE] -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
android:defaultValue="@bool/pref_proximity_default"
android:key="@string/pref_proximity_key"
android:title="@string/title_pref_cover_proximity_sensor" />
<com.google.android.accessibility.material.preference.AccessibilitySuiteSwitchPreference
<com.google.android.accessibility.talkback.preference.base.CommandSwitchPreference
android:defaultValue="false"
android:key="@string/pref_speakerphone_away_from_ear_key"
android:title="@string/title_pref_speakerphone_away_from_ear"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
android:defaultValue="@bool/pref_proximity_default"
android:key="@string/pref_proximity_key"
android:title="@string/title_pref_cover_proximity_sensor" />
<com.google.android.accessibility.material.preference.AccessibilitySuiteSwitchPreference
<com.google.android.accessibility.talkback.preference.base.CommandSwitchPreference
android:defaultValue="false"
android:key="@string/pref_speakerphone_away_from_ear_key"
android:title="@string/title_pref_speakerphone_away_from_ear"
Expand Down
Loading