From 10ac660448bc96d9ed1a36f9cf803322f7a5bac1 Mon Sep 17 00:00:00 2001 From: Omkar Chandorkar Date: Sun, 18 Jun 2023 17:33:42 +0530 Subject: [PATCH] ParanoidSystemUI: Restore Wifi and Cellular tiles - taken from https://github.com/aospa/android_frameworks_base/blob/f64485029f221d15fd686368bd3f62789dadc490 Change-Id: I80820b233adcb7ad1961272ad3c8d4c3dcaf387c Signed-off-by: Omkar Chandorkar --- .../systemui/qs/tileimpl/ParanoidQSModule.kt | 14 + .../aospa/systemui/qs/tiles/CellularTile.java | 301 ++++++++++++++++++ src/co/aospa/systemui/qs/tiles/WifiTile.java | 286 +++++++++++++++++ 3 files changed, 601 insertions(+) create mode 100644 src/co/aospa/systemui/qs/tiles/CellularTile.java create mode 100644 src/co/aospa/systemui/qs/tiles/WifiTile.java diff --git a/src/co/aospa/systemui/qs/tileimpl/ParanoidQSModule.kt b/src/co/aospa/systemui/qs/tileimpl/ParanoidQSModule.kt index 57193b6..b808d6d 100644 --- a/src/co/aospa/systemui/qs/tileimpl/ParanoidQSModule.kt +++ b/src/co/aospa/systemui/qs/tileimpl/ParanoidQSModule.kt @@ -21,10 +21,12 @@ import com.android.systemui.qs.tileimpl.QSTileImpl import co.aospa.systemui.qs.tiles.AlwaysOnDisplayTile; import co.aospa.systemui.qs.tiles.BluetoothDialogTile; import co.aospa.systemui.qs.tiles.CaffeineTile; +import co.aospa.systemui.qs.tiles.CellularTile import co.aospa.systemui.qs.tiles.DataSwitchTile; import co.aospa.systemui.qs.tiles.DcDimmingTile; import co.aospa.systemui.qs.tiles.HeadsUpTile; import co.aospa.systemui.qs.tiles.PowerShareTile; +import co.aospa.systemui.qs.tiles.WifiTile import dagger.Binds import dagger.Module import dagger.multibindings.IntoMap @@ -51,6 +53,12 @@ interface ParanoidQSModule { @StringKey(CaffeineTile.TILE_SPEC) fun bindCaffeineTile(caffeineTile: CaffeineTile): QSTileImpl<*> + /** Inject CellularTile into tileMap in QSModule */ + @Binds + @IntoMap + @StringKey(CellularTile.TILE_SPEC) + fun bindCellularTile(cellularTile: CellularTile): QSTileImpl<*> + /** Inject DataSwitchTile into tileMap in QSModule */ @Binds @IntoMap @@ -75,4 +83,10 @@ interface ParanoidQSModule { @StringKey(PowerShareTile.TILE_SPEC) fun bindPowerShareTile(powerShareTile: PowerShareTile): QSTileImpl<*> + /** Inject WifiTile into tileMap in QSModule */ + @Binds + @IntoMap + @StringKey(WifiTile.TILE_SPEC) + fun bindWifiTile(wifiTile: WifiTile): QSTileImpl<*> + } diff --git a/src/co/aospa/systemui/qs/tiles/CellularTile.java b/src/co/aospa/systemui/qs/tiles/CellularTile.java new file mode 100644 index 0000000..9379b33 --- /dev/null +++ b/src/co/aospa/systemui/qs/tiles/CellularTile.java @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2014 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. + */ + +package co.aospa.systemui.qs.tiles; + +import static com.android.systemui.Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA; + +import android.annotation.NonNull; +import android.app.AlertDialog; +import android.app.AlertDialog.Builder; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; +import android.provider.Settings; +import android.service.quicksettings.Tile; +import android.telephony.SubscriptionManager; +import android.text.Html; +import android.text.TextUtils; +import android.view.View; +import android.view.WindowManager.LayoutParams; +import android.widget.Switch; + +import androidx.annotation.Nullable; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.settingslib.net.DataUsageController; +import com.android.systemui.Prefs; +import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.plugins.qs.QSIconView; +import com.android.systemui.plugins.qs.QSTile.SignalState; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.SignalTileView; +import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.statusbar.connectivity.IconState; +import com.android.systemui.statusbar.connectivity.MobileDataIndicators; +import com.android.systemui.statusbar.connectivity.NetworkController; +import com.android.systemui.statusbar.connectivity.SignalCallback; +import com.android.systemui.statusbar.phone.SystemUIDialog; +import com.android.systemui.statusbar.policy.KeyguardStateController; + +import javax.inject.Inject; + +/** Quick settings tile: Cellular **/ +public class CellularTile extends QSTileImpl { + private static final String ENABLE_SETTINGS_DATA_PLAN = "enable.settings.data.plan"; + + private final NetworkController mController; + private final DataUsageController mDataController; + private final KeyguardStateController mKeyguard; + private final CellSignalCallback mSignalCallback = new CellSignalCallback(); + + @Inject + public CellularTile( + QSHost host, + @Background Looper backgroundLooper, + @Main Handler mainHandler, + FalsingManager falsingManager, + MetricsLogger metricsLogger, + StatusBarStateController statusBarStateController, + ActivityStarter activityStarter, + QSLogger qsLogger, + NetworkController networkController, + KeyguardStateController keyguardStateController + + ) { + super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + statusBarStateController, activityStarter, qsLogger); + mController = networkController; + mKeyguard = keyguardStateController; + mDataController = mController.getMobileDataController(); + mController.observe(getLifecycle(), mSignalCallback); + } + + @Override + public SignalState newTileState() { + return new SignalState(); + } + + @Override + public QSIconView createTileView(Context context) { + return new SignalTileView(context); + } + + @Override + public Intent getLongClickIntent() { + if (getState().state == Tile.STATE_UNAVAILABLE) { + return new Intent(Settings.ACTION_WIRELESS_SETTINGS); + } + return getCellularSettingIntent(); + } + + @Override + protected void handleClick(@Nullable View view) { + if (getState().state == Tile.STATE_UNAVAILABLE) { + return; + } + if (mDataController.isMobileDataEnabled()) { + maybeShowDisableDialog(); + } else { + mDataController.setMobileDataEnabled(true); + } + } + + private void maybeShowDisableDialog() { + if (Prefs.getBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA, false)) { + // Directly turn off mobile data if the user has seen the dialog before. + mDataController.setMobileDataEnabled(false); + return; + } + String carrierName = mController.getMobileDataNetworkName(); + boolean isInService = mController.isMobileDataNetworkInService(); + if (TextUtils.isEmpty(carrierName) || !isInService) { + carrierName = mContext.getString(R.string.mobile_data_disable_message_default_carrier); + } + AlertDialog dialog = new Builder(mContext) + .setTitle(R.string.mobile_data_disable_title) + .setMessage(mContext.getString(R.string.mobile_data_disable_message, carrierName)) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton( + com.android.internal.R.string.alert_windows_notification_turn_off_action, + (d, w) -> { + mDataController.setMobileDataEnabled(false); + Prefs.putBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA, true); + }) + .create(); + dialog.getWindow().setType(LayoutParams.TYPE_KEYGUARD_DIALOG); + SystemUIDialog.setShowForAllUsers(dialog, true); + SystemUIDialog.registerDismissListener(dialog); + SystemUIDialog.setWindowOnTop(dialog, mKeyguard.isShowing()); + dialog.show(); + } + + @Override + protected void handleSecondaryClick(@Nullable View view) { + handleLongClick(view); + } + + @Override + public CharSequence getTileLabel() { + return mContext.getString(R.string.quick_settings_cellular_detail_title); + } + + @Override + protected void handleUpdateState(SignalState state, Object arg) { + CallbackInfo cb = (CallbackInfo) arg; + if (cb == null) { + cb = mSignalCallback.mInfo; + } + + final Resources r = mContext.getResources(); + state.label = r.getString(R.string.mobile_data); + boolean mobileDataEnabled = mDataController.isMobileDataSupported() + && mDataController.isMobileDataEnabled(); + state.value = mobileDataEnabled; + state.activityIn = mobileDataEnabled && cb.activityIn; + state.activityOut = mobileDataEnabled && cb.activityOut; + state.expandedAccessibilityClassName = Switch.class.getName(); + if (cb.noSim) { + state.icon = ResourceIcon.get(R.drawable.ic_qs_no_sim); + } else { + state.icon = ResourceIcon.get(R.drawable.ic_swap_vert); + } + + if (cb.noSim) { + state.state = Tile.STATE_UNAVAILABLE; + state.secondaryLabel = r.getString(R.string.keyguard_missing_sim_message_short); + } else if (cb.airplaneModeEnabled) { + state.state = Tile.STATE_UNAVAILABLE; + state.secondaryLabel = r.getString(R.string.status_bar_airplane); + } else if (mobileDataEnabled) { + state.state = Tile.STATE_ACTIVE; + state.secondaryLabel = appendMobileDataType( + // Only show carrier name if there are more than 1 subscription + cb.multipleSubs ? cb.dataSubscriptionName : "", + getMobileDataContentName(cb)); + } else { + state.state = Tile.STATE_INACTIVE; + state.secondaryLabel = r.getString(R.string.cell_data_off); + } + + state.contentDescription = state.label; + if (state.state == Tile.STATE_INACTIVE) { + // This information is appended later by converting the Tile.STATE_INACTIVE state. + state.stateDescription = ""; + } else { + state.stateDescription = state.secondaryLabel; + } + } + + private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) { + if (TextUtils.isEmpty(dataType)) { + return Html.fromHtml(current.toString(), 0); + } + if (TextUtils.isEmpty(current)) { + return Html.fromHtml(dataType.toString(), 0); + } + String concat = mContext.getString(R.string.mobile_carrier_text_format, current, dataType); + return Html.fromHtml(concat, 0); + } + + private CharSequence getMobileDataContentName(CallbackInfo cb) { + if (cb.roaming && !TextUtils.isEmpty(cb.dataContentDescription)) { + String roaming = mContext.getString(R.string.data_connection_roaming); + String dataDescription = cb.dataContentDescription.toString(); + return mContext.getString(R.string.mobile_data_text_format, roaming, dataDescription); + } + if (cb.roaming) { + return mContext.getString(R.string.data_connection_roaming); + } + return cb.dataContentDescription; + } + + @Override + public int getMetricsCategory() { + return MetricsEvent.QS_CELLULAR; + } + + @Override + public boolean isAvailable() { + return mController.hasMobileDataFeature() + && mHost.getUserContext().getUserId() == UserHandle.USER_SYSTEM; + } + + private static final class CallbackInfo { + boolean airplaneModeEnabled; + @Nullable + CharSequence dataSubscriptionName; + @Nullable + CharSequence dataContentDescription; + boolean activityIn; + boolean activityOut; + boolean noSim; + boolean roaming; + boolean multipleSubs; + } + + private final class CellSignalCallback implements SignalCallback { + private final CallbackInfo mInfo = new CallbackInfo(); + + @Override + public void setMobileDataIndicators(@NonNull MobileDataIndicators indicators) { + if (indicators.qsIcon == null) { + // Not data sim, don't display. + return; + } + mInfo.dataSubscriptionName = mController.getMobileDataNetworkName(); + mInfo.dataContentDescription = indicators.qsDescription != null + ? indicators.typeContentDescriptionHtml : null; + mInfo.activityIn = indicators.activityIn; + mInfo.activityOut = indicators.activityOut; + mInfo.roaming = indicators.roaming; + mInfo.multipleSubs = mController.getNumberSubscriptions() > 1; + refreshState(mInfo); + } + + @Override + public void setNoSims(boolean show, boolean simDetected) { + mInfo.noSim = show; + refreshState(mInfo); + } + + @Override + public void setIsAirplaneMode(@NonNull IconState icon) { + mInfo.airplaneModeEnabled = icon.visible; + refreshState(mInfo); + } + } + + static Intent getCellularSettingIntent() { + Intent intent = new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS); + int dataSub = SubscriptionManager.getDefaultDataSubscriptionId(); + if (dataSub != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + intent.putExtra(Settings.EXTRA_SUB_ID, + SubscriptionManager.getDefaultDataSubscriptionId()); + } + return intent; + } +} diff --git a/src/co/aospa/systemui/qs/tiles/WifiTile.java b/src/co/aospa/systemui/qs/tiles/WifiTile.java new file mode 100644 index 0000000..8f3f3b6 --- /dev/null +++ b/src/co/aospa/systemui/qs/tiles/WifiTile.java @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2014 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. + */ + +package co.aospa.systemui.qs.tiles; + +import android.annotation.NonNull; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.os.Handler; +import android.os.Looper; +import android.provider.Settings; +import android.service.quicksettings.Tile; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; +import android.widget.Switch; + +import androidx.annotation.Nullable; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.plugins.qs.QSIconView; +import com.android.systemui.plugins.qs.QSTile; +import com.android.systemui.plugins.qs.QSTile.SignalState; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.qs.AlphaControlledSignalTileView; +import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.tileimpl.QSIconViewImpl; +import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.statusbar.connectivity.AccessPointController; +import com.android.systemui.statusbar.connectivity.NetworkController; +import com.android.systemui.statusbar.connectivity.SignalCallback; +import com.android.systemui.statusbar.connectivity.WifiIcons; +import com.android.systemui.statusbar.connectivity.WifiIndicators; + +import javax.inject.Inject; + +/** Quick settings tile: Wifi **/ +public class WifiTile extends QSTileImpl { + private static final Intent WIFI_SETTINGS = new Intent(Settings.ACTION_WIFI_SETTINGS); + + protected final NetworkController mController; + private final AccessPointController mWifiController; + private final QSTile.SignalState mStateBeforeClick = newTileState(); + + protected final WifiSignalCallback mSignalCallback = new WifiSignalCallback(); + private boolean mExpectDisabled; + + @Inject + public WifiTile( + QSHost host, + @Background Looper backgroundLooper, + @Main Handler mainHandler, + FalsingManager falsingManager, + MetricsLogger metricsLogger, + StatusBarStateController statusBarStateController, + ActivityStarter activityStarter, + QSLogger qsLogger, + NetworkController networkController, + AccessPointController accessPointController + ) { + super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + statusBarStateController, activityStarter, qsLogger); + mController = networkController; + mWifiController = accessPointController; + mController.observe(getLifecycle(), mSignalCallback); + mStateBeforeClick.spec = "wifi"; + } + + @Override + public SignalState newTileState() { + return new SignalState(); + } + + @Override + public QSIconView createTileView(Context context) { + return new AlphaControlledSignalTileView(context); + } + + @Override + public Intent getLongClickIntent() { + return WIFI_SETTINGS; + } + + @Override + protected void handleClick(@Nullable View view) { + // Secondary clicks are header clicks, just toggle. + mState.copyTo(mStateBeforeClick); + boolean wifiEnabled = mState.value; + // Immediately enter transient state when turning on wifi. + refreshState(wifiEnabled ? null : ARG_SHOW_TRANSIENT_ENABLING); + mController.setWifiEnabled(!wifiEnabled); + mExpectDisabled = wifiEnabled; + if (mExpectDisabled) { + mHandler.postDelayed(() -> { + if (mExpectDisabled) { + mExpectDisabled = false; + refreshState(); + } + }, QSIconViewImpl.QS_ANIM_LENGTH); + } + } + + @Override + protected void handleSecondaryClick(@Nullable View view) { + if (!mWifiController.canConfigWifi()) { + mActivityStarter.postStartActivityDismissingKeyguard( + new Intent(Settings.ACTION_WIFI_SETTINGS), 0); + return; + } + if (!mState.value) { + mController.setWifiEnabled(true); + } + } + + @Override + public CharSequence getTileLabel() { + return mContext.getString(R.string.quick_settings_wifi_label); + } + + @Override + protected void handleUpdateState(SignalState state, Object arg) { + if (DEBUG) Log.d(TAG, "handleUpdateState arg=" + arg); + final CallbackInfo cb = mSignalCallback.mInfo; + if (mExpectDisabled) { + if (cb.enabled) { + return; // Ignore updates until disabled event occurs. + } else { + mExpectDisabled = false; + } + } + boolean transientEnabling = arg == ARG_SHOW_TRANSIENT_ENABLING; + boolean wifiConnected = cb.enabled && (cb.wifiSignalIconId > 0) + && (cb.ssid != null || cb.wifiSignalIconId != WifiIcons.QS_WIFI_NO_NETWORK); + boolean wifiNotConnected = (cb.ssid == null) + && (cb.wifiSignalIconId == WifiIcons.QS_WIFI_NO_NETWORK); + if (state.slash == null) { + state.slash = new SlashState(); + state.slash.rotation = 6; + } + state.slash.isSlashed = false; + boolean isTransient = transientEnabling || cb.isTransient; + state.secondaryLabel = getSecondaryLabel(isTransient, cb.statusLabel); + state.state = Tile.STATE_ACTIVE; + state.dualTarget = true; + state.value = transientEnabling || cb.enabled; + state.activityIn = cb.enabled && cb.activityIn; + state.activityOut = cb.enabled && cb.activityOut; + final StringBuffer minimalContentDescription = new StringBuffer(); + final StringBuffer minimalStateDescription = new StringBuffer(); + final Resources r = mContext.getResources(); + if (isTransient) { + state.icon = ResourceIcon.get( + com.android.internal.R.drawable.ic_signal_wifi_transient_animation); + state.label = r.getString(R.string.quick_settings_wifi_label); + } else if (!state.value) { + state.slash.isSlashed = true; + state.state = Tile.STATE_INACTIVE; + state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_DISABLED); + state.label = r.getString(R.string.quick_settings_wifi_label); + } else if (wifiConnected) { + state.icon = ResourceIcon.get(cb.wifiSignalIconId); + state.label = cb.ssid != null ? removeDoubleQuotes(cb.ssid) : getTileLabel(); + } else if (wifiNotConnected) { + state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_NO_NETWORK); + state.label = r.getString(R.string.quick_settings_wifi_label); + } else { + state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_NO_NETWORK); + state.label = r.getString(R.string.quick_settings_wifi_label); + } + minimalContentDescription.append( + mContext.getString(R.string.quick_settings_wifi_label)).append(","); + if (state.value) { + if (wifiConnected) { + minimalStateDescription.append(cb.wifiSignalContentDescription); + minimalContentDescription.append(removeDoubleQuotes(cb.ssid)); + if (!TextUtils.isEmpty(state.secondaryLabel)) { + minimalContentDescription.append(",").append(state.secondaryLabel); + } + } + } + state.stateDescription = minimalStateDescription.toString(); + state.contentDescription = minimalContentDescription.toString(); + state.dualLabelContentDescription = r.getString( + R.string.accessibility_quick_settings_open_settings, getTileLabel()); + state.expandedAccessibilityClassName = Switch.class.getName(); + } + + private CharSequence getSecondaryLabel(boolean isTransient, String statusLabel) { + return isTransient + ? mContext.getString(R.string.quick_settings_wifi_secondary_label_transient) + : statusLabel; + } + + @Override + public int getMetricsCategory() { + return MetricsEvent.QS_WIFI; + } + + @Override + public boolean isAvailable() { + return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI); + } + + @Nullable + private static String removeDoubleQuotes(String string) { + if (string == null) return null; + final int length = string.length(); + if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) { + return string.substring(1, length - 1); + } + return string; + } + + protected static final class CallbackInfo { + boolean enabled; + boolean connected; + int wifiSignalIconId; + @Nullable + String ssid; + boolean activityIn; + boolean activityOut; + @Nullable + String wifiSignalContentDescription; + boolean isTransient; + @Nullable + public String statusLabel; + + @Override + public String toString() { + return new StringBuilder("CallbackInfo[") + .append("enabled=").append(enabled) + .append(",connected=").append(connected) + .append(",wifiSignalIconId=").append(wifiSignalIconId) + .append(",ssid=").append(ssid) + .append(",activityIn=").append(activityIn) + .append(",activityOut=").append(activityOut) + .append(",wifiSignalContentDescription=").append(wifiSignalContentDescription) + .append(",isTransient=").append(isTransient) + .append(']').toString(); + } + } + + protected final class WifiSignalCallback implements SignalCallback { + final CallbackInfo mInfo = new CallbackInfo(); + + @Override + public void setWifiIndicators(@NonNull WifiIndicators indicators) { + if (DEBUG) Log.d(TAG, "onWifiSignalChanged enabled=" + indicators.enabled); + if (indicators.qsIcon == null) { + return; + } + mInfo.enabled = indicators.enabled; + mInfo.connected = indicators.qsIcon.visible; + mInfo.wifiSignalIconId = indicators.qsIcon.icon; + mInfo.ssid = indicators.description; + mInfo.activityIn = indicators.activityIn; + mInfo.activityOut = indicators.activityOut; + mInfo.wifiSignalContentDescription = indicators.qsIcon.contentDescription; + mInfo.isTransient = indicators.isTransient; + mInfo.statusLabel = indicators.statusLabel; + refreshState(); + } + } +}