Merge "Adding the on-device captions service toggle into the volume rocker."

This commit is contained in:
Nadav Bar
2019-03-13 04:54:28 +00:00
committed by Android (Google) Code Review
11 changed files with 210 additions and 3 deletions

View File

@@ -5589,6 +5589,15 @@ public final class Settings {
private static final Validator ALLOW_MOCK_LOCATION_VALIDATOR = BOOLEAN_VALIDATOR;
/**
* Setting to indicate that on device captions are enabled.
*
* @hide
*/
public static final String ODI_CAPTIONS_ENABLED = "odi_captions_enabled";
private static final Validator ODI_CAPTIONS_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
/**
* On Android 8.0 (API level 26) and higher versions of the platform,
* a 64-bit number (expressed as a hexadecimal string), unique to
@@ -8950,6 +8959,7 @@ public final class Settings {
VALIDATORS.put(SILENCE_TIMER_GESTURE_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR);
VALIDATORS.put(SILENCE_CALL_GESTURE_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR);
VALIDATORS.put(SILENCE_NOTIFICATION_GESTURE_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR);
VALIDATORS.put(ODI_CAPTIONS_ENABLED, ODI_CAPTIONS_ENABLED_VALIDATOR);
}
/**

View File

@@ -3513,6 +3513,12 @@
-->
<string name="config_defaultAttentionService" translatable="false"></string>
<!-- The component name for the system-wide captions service.
This service must be trusted, as it controls part of the UI of the volume bar.
Example: "com.android.captions/.SystemCaptionsService"
-->
<string name="config_defaultSystemCaptionsService" translatable="false"></string>
<!-- The package name for the incident report approver app.
This app is usually PermissionController or an app that replaces it. When
a bugreport or incident report with EXPLICT-level sharing flags is going to be

View File

@@ -3382,6 +3382,7 @@
<java-symbol type="string" name="config_defaultAppPredictionService" />
<java-symbol type="string" name="config_defaultContentSuggestionsService" />
<java-symbol type="string" name="config_defaultAttentionService" />
<java-symbol type="string" name="config_defaultSystemCaptionsService" />
<java-symbol type="string" name="notification_channel_foreground_service" />
<java-symbol type="string" name="foreground_service_app_in_background" />

View File

@@ -58,6 +58,10 @@ public interface VolumeDialogController {
void userActivity();
void getState();
boolean areCaptionsEnabled();
void setCaptionsEnabled(boolean isEnabled);
void getCaptionsComponentState();
@ProvidesInterface(version = StreamState.VERSION)
public static final class StreamState {
public static final int VERSION = 1;
@@ -186,5 +190,6 @@ public interface VolumeDialogController {
void onScreenOff();
void onShowSafetyWarning(int flags);
void onAccessibilityModeChanged(Boolean showA11yStream);
void onCaptionComponentStateChanged(Boolean isComponentEnabled);
}
}

View File

@@ -0,0 +1,24 @@
<!--
Copyright (C) 2019 The Android Open Source Project
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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M20,4C21.1,4 22,4.9 22,6L22,18C22,19.1 21.1,20 20,20L4,20C2.9,20 2,19.1 2,18L2,6C2,4.9 2.9,4 4,4L20,4ZM20,18L20,6L4,6L4,18L20,18ZM6,10L8,10L8,12L6,12L6,10ZM6,14L14,14L14,16L6,16L6,14ZM16,14L18,14L18,16L16,16L16,14ZM10,10L18,10L18,12L10,12L10,10Z"/>
</vector>

View File

@@ -0,0 +1,24 @@
<!--
Copyright (C) 2019 The Android Open Source Project
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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M16.9675,14L18,14L18,15.0275L16.9675,14ZM20,17.0176L20,6L8.9281,6L6.9182,4L20,4C21.1,4 22,4.9 22,6L22,18C22,18.2949 21.9353,18.5755 21.8194,18.8281L20,17.0176ZM12.9478,10L18,10L18,12L14.9576,12L12.9478,10ZM1.2823,0.8824L22.8489,22.4489L21.6337,23.6641L17.9696,20L4,20C2.9,20 2,19.1 2,18L2,6C2,5.4577 2.2188,4.964 2.5724,4.6028L0.0672,2.0975L1.2823,0.8824ZM13.9696,16L6,16L6,14L11.9696,14L8,10.0304L8,12L6,12L6,10L7.9696,10L4,6.0304L4,18L15.9696,18L13.9696,16Z"/>
</vector>

View File

@@ -98,5 +98,27 @@
</FrameLayout>
</LinearLayout>
<FrameLayout
android:id="@+id/odi_captions"
android:layout_width="@dimen/volume_dialog_caption_size"
android:layout_height="@dimen/volume_dialog_caption_size"
android:layout_marginTop="@dimen/volume_dialog_spacer"
android:translationZ="@dimen/volume_dialog_elevation"
android:layout_gravity="right"
android:clipToPadding="false"
android:visibility="gone"
android:background="@drawable/rounded_bg_full">
<com.android.keyguard.AlphaOptimizedImageButton
android:id="@+id/odi_captions_icon"
android:src="@drawable/ic_volume_odi_captions_disabled"
style="@style/VolumeButtons"
android:background="@drawable/rounded_ripple"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:tint="@color/accent_tint_color_selector"
android:layout_gravity="center"
android:soundEffectsEnabled="false" />
</FrameLayout>
</LinearLayout>
</FrameLayout>

View File

@@ -336,6 +336,8 @@
<dimen name="volume_dialog_ringer_size">64dp</dimen>
<dimen name="volume_dialog_caption_size">64dp</dimen>
<dimen name="volume_dialog_tap_target_size">48dp</dimen>
<dimen name="volume_dialog_spacer">4dp</dimen>

View File

@@ -55,6 +55,7 @@ public class Events {
public static final int EVENT_RINGER_TOGGLE = 18; // (ringer_mode)
public static final int EVENT_SHOW_USB_OVERHEAT_ALARM = 19; // (reason|int) (keyguard|bool)
public static final int EVENT_DISMISS_USB_OVERHEAT_ALARM = 20; // (reason|int) (keyguard|bool)
public static final int EVENT_ODI_CAPTIONS_CLICK = 21;
private static final String[] EVENT_TAGS = {
"show_dialog",
@@ -77,7 +78,8 @@ public class Events {
"zen_mode_config_changed",
"ringer_toggle",
"show_usb_overheat_alarm",
"dismiss_usb_overheat_alarm"
"dismiss_usb_overheat_alarm",
"odi_captions_click"
};
public static final int DISMISS_REASON_UNKNOWN = 0;
@@ -90,6 +92,7 @@ public class Events {
public static final int DISMISS_STREAM_GONE = 7;
public static final int DISMISS_REASON_OUTPUT_CHOOSER = 8;
public static final int DISMISS_REASON_USB_OVERHEAD_ALARM_CHANGED = 9;
public static final int DISMISS_REASON_ODI_CAPTIONS_CLICKED = 10;
public static final String[] DISMISS_REASONS = {
"unknown",
"touch_outside",
@@ -100,7 +103,8 @@ public class Events {
"done_clicked",
"a11y_stream_changed",
"output_chooser",
"usb_temperature_below_threshold"
"usb_temperature_below_threshold",
"odi_captions_clicked"
};
public static final int SHOW_REASON_UNKNOWN = 0;

View File

@@ -48,6 +48,7 @@ import android.os.Vibrator;
import android.provider.Settings;
import android.service.notification.Condition;
import android.service.notification.ZenModeConfig;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.view.accessibility.AccessibilityManager;
@@ -270,6 +271,22 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
mWorker.sendEmptyMessage(W.GET_STATE);
}
public boolean areCaptionsEnabled() {
int currentValue = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ODI_CAPTIONS_ENABLED, 0);
return currentValue == 1;
}
public void setCaptionsEnabled(boolean isEnabled) {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ODI_CAPTIONS_ENABLED, isEnabled ? 1 : 0);
}
public void getCaptionsComponentState() {
if (mDestroyed) return;
mWorker.sendEmptyMessage(W.GET_CAPTIONS_COMPONENT_STATE);
}
public void notifyVisible(boolean visible) {
if (mDestroyed) return;
mWorker.obtainMessage(W.NOTIFY_VISIBLE, visible ? 1 : 0, 0).sendToTarget();
@@ -365,6 +382,38 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
}
}
private void onGetCaptionsComponentStateW() {
try {
String componentNameString = mContext.getString(
com.android.internal.R.string.config_defaultSystemCaptionsService);
if (TextUtils.isEmpty(componentNameString)) {
// component doesn't exist
mCallbacks.onCaptionComponentStateChanged(false);
return;
}
if (D.BUG) {
Log.i(TAG, String.format(
"isCaptionsServiceEnabled componentNameString=%s", componentNameString));
}
ComponentName componentName = ComponentName.unflattenFromString(componentNameString);
if (componentName == null) {
mCallbacks.onCaptionComponentStateChanged(false);
return;
}
PackageManager packageManager = mContext.getPackageManager();
mCallbacks.onCaptionComponentStateChanged(
packageManager.getComponentEnabledSetting(componentName)
== PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
} catch (Exception ex) {
Log.e(TAG,
"isCaptionsServiceEnabled failed to check for captions component", ex);
mCallbacks.onCaptionComponentStateChanged(false);
}
}
private void onAccessibilityModeChanged(Boolean showA11yStream) {
mCallbacks.onAccessibilityModeChanged(showA11yStream);
}
@@ -718,6 +767,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
private static final int USER_ACTIVITY = 13;
private static final int SHOW_SAFETY_WARNING = 14;
private static final int ACCESSIBILITY_MODE_CHANGED = 15;
private static final int GET_CAPTIONS_COMPONENT_STATE = 16;
W(Looper looper) {
super(looper);
@@ -740,8 +790,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
case NOTIFY_VISIBLE: onNotifyVisibleW(msg.arg1 != 0); break;
case USER_ACTIVITY: onUserActivityW(); break;
case SHOW_SAFETY_WARNING: onShowSafetyWarningW(msg.arg1); break;
case GET_CAPTIONS_COMPONENT_STATE: onGetCaptionsComponentStateW(); break;
case ACCESSIBILITY_MODE_CHANGED: onAccessibilityModeChanged((Boolean) msg.obj);
}
}
}
@@ -881,6 +931,15 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
});
}
}
@Override
public void onCaptionComponentStateChanged(Boolean isComponentEnabled) {
boolean componentEnabled = isComponentEnabled == null ? false : isComponentEnabled;
for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
entry.getValue().post(
() -> entry.getKey().onCaptionComponentStateChanged(componentEnabled));
}
}
}

View File

@@ -29,6 +29,7 @@ import static android.view.View.ACCESSIBILITY_LIVE_REGION_POLITE;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static com.android.systemui.volume.Events.DISMISS_REASON_ODI_CAPTIONS_CLICKED;
import static com.android.systemui.volume.Events.DISMISS_REASON_SETTINGS_CLICKED;
import android.animation.ObjectAnimator;
@@ -125,6 +126,8 @@ public class VolumeDialogImpl implements VolumeDialog {
private ViewGroup mDialogRowsView;
private ViewGroup mRinger;
private ImageButton mRingerIcon;
private ViewGroup mODICaptionsView;
private ImageButton mODICaptionsIcon;
private View mSettingsView;
private ImageButton mSettingsIcon;
private FrameLayout mZenIcon;
@@ -240,6 +243,10 @@ public class VolumeDialogImpl implements VolumeDialog {
mRingerIcon = mRinger.findViewById(R.id.ringer_icon);
mZenIcon = mRinger.findViewById(R.id.dnd_icon);
}
mODICaptionsView = mDialog.findViewById(R.id.odi_captions);
if (mODICaptionsView != null) {
mODICaptionsIcon = mODICaptionsView.findViewById(R.id.odi_captions_icon);
}
mSettingsView = mDialog.findViewById(R.id.settings_container);
mSettingsIcon = mDialog.findViewById(R.id.settings);
@@ -270,6 +277,7 @@ public class VolumeDialogImpl implements VolumeDialog {
updateRowsH(getActiveRow());
initRingerH();
initSettingsH();
initODICaptionsH();
}
protected ViewGroup getDialogView() {
@@ -478,6 +486,42 @@ public class VolumeDialogImpl implements VolumeDialog {
updateRingerH();
}
private void initODICaptionsH() {
if (mODICaptionsIcon != null) {
mODICaptionsIcon.setOnClickListener(v -> {
onCaptionIconClicked();
Events.writeEvent(mContext, Events.EVENT_ODI_CAPTIONS_CLICK);
dismissH(DISMISS_REASON_ODI_CAPTIONS_CLICKED);
});
}
mController.getCaptionsComponentState();
}
private void updateODICaptionsH(boolean isServiceComponentEnabled) {
if (mODICaptionsView != null) {
mODICaptionsView.setVisibility(isServiceComponentEnabled ? VISIBLE : GONE);
}
if (!isServiceComponentEnabled) return;
updateCaptionsIcon();
}
private void updateCaptionsIcon() {
mHandler.post(
mODICaptionsIcon.setImageResourceAsync(
mController.areCaptionsEnabled()
? R.drawable.ic_volume_odi_captions
: R.drawable.ic_volume_odi_captions_disabled));
}
private void onCaptionIconClicked() {
boolean isEnabled = mController.areCaptionsEnabled();
mController.setCaptionsEnabled(!isEnabled);
updateCaptionsIcon();
}
private void incrementManualToggleCount() {
ContentResolver cr = mContext.getContentResolver();
int ringerCount = Settings.Secure.getInt(cr, Settings.Secure.MANUAL_RINGER_TOGGLE_COUNT, 0);
@@ -558,6 +602,7 @@ public class VolumeDialogImpl implements VolumeDialog {
mDialog.show();
Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
mController.notifyVisible(true);
mController.getCaptionsComponentState();
}
protected void rescheduleTimeoutH() {
@@ -1151,6 +1196,11 @@ public class VolumeDialogImpl implements VolumeDialog {
}
}
@Override
public void onCaptionComponentStateChanged(Boolean isComponentEnabled) {
updateODICaptionsH(isComponentEnabled);
}
};
private final class H extends Handler {