1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.connecteddevice.display; 18 19 import android.content.Context; 20 import android.graphics.drawable.Drawable; 21 import android.os.UserHandle; 22 23 import androidx.annotation.NonNull; 24 import androidx.annotation.Nullable; 25 import androidx.annotation.VisibleForTesting; 26 import androidx.preference.Preference; 27 28 import com.android.settings.R; 29 import com.android.settings.connecteddevice.DevicePreferenceCallback; 30 import com.android.settings.connecteddevice.display.ExternalDisplaySettingsConfiguration.DisplayListener; 31 import com.android.settings.connecteddevice.display.ExternalDisplaySettingsConfiguration.Injector; 32 import com.android.settings.core.SubSettingLauncher; 33 import com.android.settings.overlay.FeatureFactory; 34 import com.android.settingslib.RestrictedLockUtils; 35 import com.android.settingslib.RestrictedLockUtilsInternal; 36 import com.android.settingslib.RestrictedPreference; 37 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; 38 39 public class ExternalDisplayUpdater { 40 41 private static final String PREF_KEY = "external_display_settings"; 42 private final int mMetricsCategory; 43 @NonNull 44 private final MetricsFeatureProvider mMetricsFeatureProvider; 45 @NonNull 46 private final Runnable mUpdateRunnable = this::update; 47 @NonNull 48 private final DevicePreferenceCallback mDevicePreferenceCallback; 49 @Nullable 50 private RestrictedPreference mPreference; 51 @Nullable 52 private Injector mInjector; 53 private final DisplayListener mListener = new DisplayListener() { 54 @Override 55 public void update(int displayId) { 56 refreshPreference(); 57 } 58 }; 59 ExternalDisplayUpdater(@onNull DevicePreferenceCallback callback, int metricsCategory)60 public ExternalDisplayUpdater(@NonNull DevicePreferenceCallback callback, int metricsCategory) { 61 mDevicePreferenceCallback = callback; 62 mMetricsCategory = metricsCategory; 63 mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(); 64 } 65 66 /** 67 * Set the context to generate the {@link Preference}, so it could get the correct theme. 68 */ initPreference(@onNull Context context)69 public void initPreference(@NonNull Context context) { 70 initPreference(context, new Injector(context)); 71 } 72 73 @VisibleForTesting initPreference(@onNull Context context, Injector injector)74 void initPreference(@NonNull Context context, Injector injector) { 75 mInjector = injector; 76 mPreference = new RestrictedPreference(context, null /* AttributeSet */); 77 mPreference.setTitle(R.string.external_display_settings_title); 78 mPreference.setSummary(getSummary()); 79 mPreference.setIcon(getDrawable(context)); 80 mPreference.setKey(PREF_KEY); 81 mPreference.setDisabledByAdmin(checkIfUsbDataSignalingIsDisabled(context)); 82 mPreference.setOnPreferenceClickListener((Preference p) -> { 83 mMetricsFeatureProvider.logClickedPreference(p, mMetricsCategory); 84 // New version - uses a separate screen. 85 new SubSettingLauncher(context) 86 .setDestination(ExternalDisplayPreferenceFragment.class.getName()) 87 .setTitleRes(R.string.external_display_settings_title) 88 .setSourceMetricsCategory(mMetricsCategory) 89 .launch(); 90 return true; 91 }); 92 } 93 94 /** 95 * Unregister the display listener. 96 */ unregisterCallback()97 public void unregisterCallback() { 98 if (mInjector != null) { 99 mInjector.unregisterDisplayListener(mListener); 100 } 101 } 102 103 /** 104 * Register the display listener. 105 */ registerCallback()106 public void registerCallback() { 107 if (mInjector != null) { 108 mInjector.registerDisplayListener(mListener); 109 } 110 } 111 112 @VisibleForTesting 113 @Nullable checkIfUsbDataSignalingIsDisabled(Context context)114 protected RestrictedLockUtils.EnforcedAdmin checkIfUsbDataSignalingIsDisabled(Context context) { 115 return RestrictedLockUtilsInternal.checkIfUsbDataSignalingIsDisabled(context, 116 UserHandle.myUserId()); 117 } 118 119 @VisibleForTesting 120 @Nullable getDrawable(Context context)121 protected Drawable getDrawable(Context context) { 122 return context.getDrawable(R.drawable.ic_external_display_32dp); 123 } 124 125 @Nullable getSummary()126 protected CharSequence getSummary() { 127 if (mInjector == null) { 128 return null; 129 } 130 var context = mInjector.getContext(); 131 if (context == null) { 132 return null; 133 } 134 135 var allDisplays = mInjector.getConnectedDisplays(); 136 for (var display : allDisplays) { 137 if (display.isEnabled() == DisplayIsEnabled.YES) { 138 return context.getString(R.string.external_display_on); 139 } 140 } 141 return allDisplays.isEmpty() ? null : context.getString(R.string.external_display_off); 142 } 143 144 /** 145 * Updates preference, possibly removing it entirely. 146 */ refreshPreference()147 public void refreshPreference() { 148 if (mInjector == null) { 149 return; 150 } 151 unscheduleUpdate(); 152 mInjector.getHandler().post(mUpdateRunnable); 153 } 154 unscheduleUpdate()155 private void unscheduleUpdate() { 156 if (mInjector == null) { 157 return; 158 } 159 mInjector.getHandler().removeCallbacks(mUpdateRunnable); 160 } 161 update()162 private void update() { 163 var summary = getSummary(); 164 if (mPreference == null) { 165 return; 166 } 167 mPreference.setSummary(summary); 168 if (summary != null) { 169 mDevicePreferenceCallback.onDeviceAdded(mPreference); 170 } else { 171 mDevicePreferenceCallback.onDeviceRemoved(mPreference); 172 } 173 } 174 } 175