Skip to content

Commit a6cdc6c

Browse files
Myself5finish0314
authored andcommitted
SystemUI: Introduce DataSwitchTile
* Based on OnePlus' OxygenOS tile, reworked to work with AOSP toggling without requirements on proprietary telephony-ext features neobuddy89: Updated for Android 14 and squashed below - From: DennySPB <dennyspb@gmail.com> Date: Thu, 5 Dec 2019 12:40:17 +0300 Subject: DataSwitchTile: collapse notification panel onClick Change-Id: I37a064c910ea05493b41bdd0123ca3a6aca1f25d Signed-off-by: DennySPB <dennyspb@gmail.com> From: micky387 <mickaelsaibi@free.fr> Date: Wed, 11 Mar 2020 13:36:51 +0100 Subject: DataSwitchTile: dont show toast on click and add drawable for No SIM Change-Id: Ib9d26630879420216e67de1a1e6fc82c5c2ace5e From: DennySPb <dennyspb@gmail.com> Date: Tue, 17 Nov 2020 11:39:43 +0300 Subject: SystemUI: Show carrier name of opposite slot in DataSwitch tile label make it more user friendly Change-Id: I53094db21fe21b1a4bd9ee76fd28661622eb5e26 From: Ido Ben-Hur <idoybh2@gmail.com> Date: Wed, 14 Jul 2021 01:43:13 +0900 Subject: DataSwitchTile: Improve the code * Get rid of deprecated AsyncTask * Removed unused imports * Finalize global vars * Some other formatting and minor stuff Change-Id: Id3e39c98dac6200301d15bf752f866f47875633d From: Pranav Vashi <neobuddy89@gmail.com> Date: Mon, 7 Feb 2022 15:06:47 +0530 Subject: DataSwitchTile: Fix issue when subId is non-binary Fixes: crdroidandroid/android_frameworks_base#770 Other changes: 1. Collapse QS panel after switching sim. 2. Change drawable to reflect opposite sim number. 3. Move move opposite sim label to secondary level. Signed-off-by: Pranav Vashi <neobuddy89@gmail.com> From: ShevT <evgeny.shishkov@gmail.com> Date: Sat, 12 Feb 2022 00:02:04 +0300 Subject: DataSwitchTile: Resolve initial tile state After loading the system, we do not see on the tile the telecom operator to which we will switch. Instead, we see "On". And so on until we switch the network. If you pull out one SIM, then the name of the operator to which we could switch to will "freeze" on the tile. Let's fix this. neobuddy89: Improvise logic and rewrite. Change-Id: I7a4996b48e014825c5452c1259454e9b7c70ce82 Signed-off-by: Pranav Vashi <neobuddy89@gmail.com> From: Joe Maples <joe@maples.dev> Date: Sat, 29 Feb 2020 16:38:14 -0500 Subject: DataSwitchTile: Use Mobile Data panel Change-Id: I9f818eea1ab906bb1721e0aab4cbb138187e507b Signed-off-by: SagarMakhar <sagarmakhar@gmail.com> From: Hernán Castañón Álvarez <herna@paranoidandroid.co> Date: Thu, 4 Jun 2020 19:08:06 +0000 Subject: [PATCH 0533/1200] DataSwitchTile: Update SIMs QS icons * Designed and made by Andrew Fluck. Change-Id: I1b83c360029382a8a1d0598758cddf1634f7d489 Co-authored-by: Andrew Fluck <andrew@aospa.co> Signed-off-by: Hernán Castañón Álvarez <herna@paranoidandroid.co> From: Pranav Vashi <neobuddy89@gmail.com> Date: Sat, 25 Mar 2023 14:16:57 +0530 Subject: DataSwitchTile: Show active sim as tile current state Signed-off-by: Pranav Vashi <neobuddy89@gmail.com> Change-Id: Ie2e280c07f24f9da6b4ee218b72501a2713ce429 Signed-off-by: Pranav Vashi <neobuddy89@gmail.com>
1 parent 8539c80 commit a6cdc6c

9 files changed

Lines changed: 328 additions & 1 deletion

File tree

data/etc/com.android.systemui.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,5 +96,7 @@
9696
<permission name="android.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW"/>
9797
<permission name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" />
9898
<permission name="android.permission.SET_UNRESTRICTED_GESTURE_EXCLUSION" />
99+
<permission name="android.permission.REBOOT"/>
100+
<permission name="android.permission.WRITE_APN_SETTINGS" />
99101
</privapp-permissions>
100102
</permissions>

packages/SystemUI/AndroidManifest.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,9 @@
405405
<!-- To broadcast status of the GameSpaceManager -->
406406
<uses-permission android:name="android.permission.MANAGE_GAME_MODE" />
407407

408+
<!-- DataSwitch tile -->
409+
<uses-permission android:name="android.permission.WRITE_APN_SETTINGS" />
410+
408411
<protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
409412
<protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
410413
<protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<vector android:height="48dp" android:viewportHeight="24"
2+
android:viewportWidth="24" android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
3+
<path android:fillColor="#ffffffff" android:pathData="M19.99,4c0,-1.1 -0.89,-2 -1.99,-2h-7.17c-0.53,0 -1.04,0.21 -1.42,0.59L4.59,7.41C4.21,7.79 4,8.3 4,8.83L4,20c0,1.1 0.9,2 2,2h12.01c1.1,0 1.99,-0.9 1.99,-2l-0.01,-16zM8,19c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM16,19c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM8,15c-0.55,0 -1,-0.45 -1,-1v-2c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v2c0,0.55 -0.45,1 -1,1zM12,19c-0.55,0 -1,-0.45 -1,-1v-2c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v2c0,0.55 -0.45,1 -1,1zM12,13c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM16,15c-0.55,0 -1,-0.45 -1,-1v-2c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v2c0,0.55 -0.45,1 -1,1z" android:strokeColor="#00000000"/>
4+
</vector>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="24"
5+
android:viewportHeight="24">
6+
<path
7+
android:pathData="M10,2H18C19.1,2 20,2.9 20,4V14H18V4H10.83L6,8.83V20H12V22H6C4.9,22 4,21.1 4,20V8L10,2Z"
8+
android:fillColor="#000000"
9+
android:fillType="evenOdd"/>
10+
<path
11+
android:pathData="M15.4492,11H14.1289V6.7969L12.832,7.1758V6.1758L15.3281,5.3125H15.4492V11Z"
12+
android:fillColor="#000000"/>
13+
<path
14+
android:pathData="M14,20V16H18L14,20Z"
15+
android:fillColor="#000000"/>
16+
<path
17+
android:pathData="M20,18L20,22L16,22L20,18Z"
18+
android:fillColor="#000000"/>
19+
</vector>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="24"
5+
android:viewportHeight="24">
6+
<path
7+
android:pathData="M10,2H18C19.1,2 20,2.9 20,4V14H18V4H10.83L6,8.83V20H12V22H6C4.9,22 4,21.1 4,20V8L10,2Z"
8+
android:fillColor="#000000"
9+
android:fillType="evenOdd"/>
10+
<path
11+
android:pathData="M16.5,11H12.5312V10.1406L14.3594,8.2188C14.8099,7.7057 15.0352,7.2982 15.0352,6.9961C15.0352,6.7513 14.9818,6.5651 14.875,6.4375C14.7682,6.3099 14.6133,6.2461 14.4102,6.2461C14.2096,6.2461 14.0469,6.332 13.9219,6.5039C13.7969,6.6732 13.7344,6.8854 13.7344,7.1406H12.4141C12.4141,6.7917 12.5013,6.47 12.6758,6.1758C12.8503,5.8789 13.0924,5.6471 13.4023,5.4805C13.7122,5.3138 14.0586,5.2305 14.4414,5.2305C15.056,5.2305 15.5286,5.3724 15.8594,5.6563C16.1927,5.9401 16.3594,6.3477 16.3594,6.8789C16.3594,7.1029 16.3177,7.3216 16.2344,7.5352C16.151,7.7461 16.0208,7.9688 15.8438,8.2031C15.6693,8.4349 15.3867,8.7461 14.9961,9.1367L14.2617,9.9844H16.5V11Z"
12+
android:fillColor="#000000"/>
13+
<path
14+
android:pathData="M14,20V16H18L14,20Z"
15+
android:fillColor="#000000"/>
16+
<path
17+
android:pathData="M20,18L20,22L16,22L20,18Z"
18+
android:fillColor="#000000"/>
19+
</vector>

packages/SystemUI/res/values/avium_srting.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,7 @@
6363

6464
<!-- 5G toggle in Internet Dialog -->
6565
<string name="enable_fiveg">Enable 5G</string>
66+
67+
<!-- DataSwitch Tile -->
68+
<string name="qs_data_switch_label">Switch data card</string>
6669
</resources>

packages/SystemUI/res/values/config.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@
115115

116116
<!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
117117
<string name="quick_settings_tiles_stock" translatable="false">
118-
internet,wifi,cell,bt,flashlight,dnd,modes_dnd,alarm,airplane,nfc,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,color_correction,dream,font_scaling,record_issue,hearing_devices,notes,desktopeffects,ambient_display,aod,caffeine,heads_up,powershare,profiles,reading_mode,sync,usb_tether,vpn
118+
internet,wifi,cell,bt,flashlight,dnd,modes_dnd,alarm,airplane,nfc,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,color_correction,dream,font_scaling,record_issue,hearing_devices,notes,desktopeffects,ambient_display,aod,caffeine,compass,cpuinfo,dataswitch,heads_up,powershare,profiles,reading_mode,sync,usb_tether,vpn
119119
</string>
120120

121121
<!-- The tiles to display in QuickSettings -->

packages/SystemUI/src/com/android/systemui/lineage/LineageModule.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import com.android.systemui.qs.tiles.AmbientDisplayTile
2121
import com.android.systemui.qs.tiles.AODTile
2222
import com.android.systemui.qs.tiles.CaffeineTile
2323
import com.android.systemui.qs.tiles.CellularTile
24+
import com.android.systemui.qs.tiles.DataSwitchTile
2425
import com.android.systemui.qs.tiles.HeadsUpTile
2526
import com.android.systemui.qs.tiles.PowerShareTile
2627
import com.android.systemui.qs.tiles.ProfilesTile
@@ -61,6 +62,12 @@ interface LineageModule {
6162
@StringKey(CellularTile.TILE_SPEC)
6263
fun bindCellularTile(cellularTile: CellularTile): QSTileImpl<*>
6364

65+
/** Inject DataSwitchTile into tileMap in QSModule */
66+
@Binds
67+
@IntoMap
68+
@StringKey(DataSwitchTile.TILE_SPEC)
69+
fun bindDataSwitchTile(dataSwitchTile: DataSwitchTile): QSTileImpl<*>
70+
6471
/** Inject HeadsUpTile into tileMap in QSModule */
6572
@Binds
6673
@IntoMap
Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
package com.android.systemui.qs.tiles;
2+
3+
import android.content.BroadcastReceiver;
4+
import android.content.Context;
5+
import android.content.Intent;
6+
import android.content.IntentFilter;
7+
import android.os.AsyncTask;
8+
import android.os.Handler;
9+
import android.os.Looper;
10+
import android.os.SystemProperties;
11+
import android.provider.Settings;
12+
import android.telephony.PhoneStateListener;
13+
import android.telephony.SubscriptionInfo;
14+
import android.telephony.SubscriptionManager;
15+
import android.telephony.TelephonyManager;
16+
import android.text.TextUtils;
17+
import android.util.Log;
18+
19+
import androidx.annotation.Nullable;
20+
21+
import com.android.internal.logging.MetricsLogger;
22+
23+
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
24+
import com.android.internal.telephony.IccCardConstants;
25+
import com.android.internal.telephony.TelephonyIntents;
26+
import com.android.systemui.animation.Expandable;
27+
import com.android.systemui.dagger.qualifiers.Background;
28+
import com.android.systemui.dagger.qualifiers.Main;
29+
import com.android.systemui.plugins.ActivityStarter;
30+
import com.android.systemui.plugins.FalsingManager;
31+
import com.android.systemui.plugins.qs.QSTile.BooleanState;
32+
import com.android.systemui.plugins.statusbar.StatusBarStateController;
33+
import com.android.systemui.qs.QsEventLogger;
34+
import com.android.systemui.qs.QSHost;
35+
import com.android.systemui.qs.logging.QSLogger;
36+
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
37+
import com.android.systemui.qs.tileimpl.QSTileImpl;
38+
import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon;
39+
import com.android.systemui.res.R;
40+
41+
import java.util.List;
42+
43+
import javax.inject.Inject;
44+
45+
public class DataSwitchTile extends QSTileImpl<BooleanState> {
46+
47+
public static final String TILE_SPEC = "dataswitch";
48+
49+
private boolean mCanSwitch = true;
50+
private boolean mRegistered = false;
51+
private int mSimCount = 0;
52+
BroadcastReceiver mSimReceiver = new BroadcastReceiver() {
53+
public void onReceive(Context context, Intent intent) {
54+
Log.d(TAG, "mSimReceiver:onReceive");
55+
refreshState();
56+
}
57+
};
58+
private final MyCallStateListener mPhoneStateListener;
59+
private final SubscriptionManager mSubscriptionManager;
60+
private final TelephonyManager mTelephonyManager;
61+
private final PanelInteractor mPanelInteractor;
62+
63+
class MyCallStateListener extends PhoneStateListener {
64+
MyCallStateListener() {
65+
}
66+
67+
public void onCallStateChanged(int state, String arg1) {
68+
mCanSwitch = mTelephonyManager.getCallState() == 0;
69+
refreshState();
70+
}
71+
}
72+
73+
@Inject
74+
public DataSwitchTile(
75+
QSHost host,
76+
QsEventLogger uiEventLogger,
77+
@Background Looper backgroundLooper,
78+
@Main Handler mainHandler,
79+
FalsingManager falsingManager,
80+
MetricsLogger metricsLogger,
81+
StatusBarStateController statusBarStateController,
82+
ActivityStarter activityStarter,
83+
QSLogger qsLogger,
84+
PanelInteractor panelInteractor
85+
) {
86+
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
87+
statusBarStateController, activityStarter, qsLogger);
88+
mSubscriptionManager = SubscriptionManager.from(host.getContext());
89+
mTelephonyManager = TelephonyManager.from(host.getContext());
90+
mPhoneStateListener = new MyCallStateListener();
91+
mPanelInteractor = panelInteractor;
92+
}
93+
94+
@Override
95+
public boolean isAvailable() {
96+
int count = TelephonyManager.getDefault().getPhoneCount();
97+
Log.d(TAG, "phoneCount: " + count);
98+
return count >= 2;
99+
}
100+
101+
@Override
102+
public BooleanState newTileState() {
103+
return new BooleanState();
104+
}
105+
106+
@Override
107+
public void handleSetListening(boolean listening) {
108+
if (listening) {
109+
if (!mRegistered) {
110+
IntentFilter filter = new IntentFilter();
111+
filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
112+
mContext.registerReceiver(mSimReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
113+
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
114+
mRegistered = true;
115+
}
116+
refreshState();
117+
} else if (mRegistered) {
118+
mContext.unregisterReceiver(mSimReceiver);
119+
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
120+
mRegistered = false;
121+
}
122+
}
123+
124+
private void updateSimCount() {
125+
String simState = SystemProperties.get("gsm.sim.state");
126+
Log.d(TAG, "DataSwitchTile:updateSimCount:simState=" + simState);
127+
mSimCount = 0;
128+
try {
129+
String[] sims = TextUtils.split(simState, ",");
130+
for (String sim : sims) {
131+
if (!sim.isEmpty()
132+
&& !sim.equalsIgnoreCase(IccCardConstants.INTENT_VALUE_ICC_ABSENT)
133+
&& !sim.equalsIgnoreCase(IccCardConstants.INTENT_VALUE_ICC_NOT_READY)) {
134+
mSimCount++;
135+
}
136+
}
137+
} catch (Exception e) {
138+
Log.e(TAG, "Error to parse sim state");
139+
}
140+
Log.d(TAG, "DataSwitchTile:updateSimCount:mSimCount=" + mSimCount);
141+
}
142+
143+
@Override
144+
protected void handleClick(@Nullable Expandable expandable) {
145+
if (!mCanSwitch) {
146+
Log.d(TAG, "Call state=" + mTelephonyManager.getCallState());
147+
} else if (mSimCount == 0) {
148+
Log.d(TAG, "handleClick:no sim card");
149+
} else if (mSimCount == 1) {
150+
Log.d(TAG, "handleClick:only one sim card");
151+
} else {
152+
AsyncTask.execute(() -> {
153+
toggleMobileDataEnabled();
154+
refreshState();
155+
});
156+
mPanelInteractor.collapsePanels();
157+
}
158+
}
159+
160+
@Override
161+
public Intent getLongClickIntent() {
162+
return new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS);
163+
}
164+
165+
@Override
166+
public CharSequence getTileLabel() {
167+
return mContext.getString(R.string.qs_data_switch_label);
168+
}
169+
170+
@Override
171+
protected void handleUpdateState(BooleanState state, Object arg) {
172+
boolean activeSIMZero;
173+
if (arg == null) {
174+
int defaultPhoneId = mSubscriptionManager.getPhoneId(
175+
mSubscriptionManager.getDefaultDataSubscriptionId());
176+
Log.d(TAG, "default data phone id=" + defaultPhoneId);
177+
activeSIMZero = defaultPhoneId == 0;
178+
} else {
179+
activeSIMZero = (Boolean) arg;
180+
}
181+
updateSimCount();
182+
switch (mSimCount) {
183+
case 0:
184+
state.icon = ResourceIcon.get(R.drawable.ic_qs_data_switch_0);
185+
state.value = false;
186+
state.secondaryLabel = mContext.getString(R.string.tile_unavailable);
187+
break;
188+
case 1:
189+
state.icon = ResourceIcon.get(activeSIMZero
190+
? R.drawable.ic_qs_data_switch_1
191+
: R.drawable.ic_qs_data_switch_2);
192+
state.value = false;
193+
state.secondaryLabel = mContext.getString(R.string.tile_unavailable);
194+
break;
195+
case 2:
196+
state.icon = ResourceIcon.get(activeSIMZero
197+
? R.drawable.ic_qs_data_switch_1
198+
: R.drawable.ic_qs_data_switch_2);
199+
state.value = true;
200+
state.secondaryLabel = getActiveSlotName();
201+
break;
202+
default:
203+
state.icon = ResourceIcon.get(R.drawable.ic_qs_data_switch_1);
204+
state.value = false;
205+
state.secondaryLabel = mContext.getString(R.string.tile_unavailable);
206+
break;
207+
}
208+
if (mSimCount < 2) {
209+
state.state = 0;
210+
} else if (!mCanSwitch) {
211+
state.state = 0;
212+
Log.d(TAG, "call state isn't idle, set to unavailable.");
213+
} else {
214+
state.state = state.value ? 2 : 1;
215+
}
216+
217+
state.label = mContext.getString(R.string.qs_data_switch_label);
218+
}
219+
220+
/**
221+
* Set whether to enable data for {@code subId}, also whether to disable data for other
222+
* subscription
223+
*/
224+
private void toggleMobileDataEnabled() {
225+
TelephonyManager telephonyManager;
226+
boolean dataEnabled = false;
227+
boolean foundActive = false;
228+
int subId;
229+
List<SubscriptionInfo> subInfoList =
230+
mSubscriptionManager.getActiveSubscriptionInfoList(true);
231+
if (subInfoList != null) {
232+
for (SubscriptionInfo subInfo : subInfoList) {
233+
subId = subInfo.getSubscriptionId();
234+
telephonyManager =
235+
mTelephonyManager.createForSubscriptionId(subId);
236+
dataEnabled = telephonyManager.getDataEnabled();
237+
if (subInfo.isOpportunistic() && dataEnabled) {
238+
// We never disable mobile data for opportunistic subscriptions.
239+
continue;
240+
} else {
241+
dataEnabled = !dataEnabled && !foundActive;
242+
telephonyManager.setDataEnabled(dataEnabled);
243+
if (dataEnabled) mSubscriptionManager.setDefaultDataSubId(subId);
244+
// Indicate we found sim with active data, disable data on remaining sim.
245+
if (!foundActive) foundActive = dataEnabled;
246+
}
247+
Log.d(TAG, "Changed subID " + subId + " to "
248+
+ !dataEnabled);
249+
}
250+
}
251+
}
252+
253+
private String getActiveSlotName() {
254+
TelephonyManager telephonyManager;
255+
String mInitialState = mContext.getString(R.string.tile_unavailable);
256+
List<SubscriptionInfo> subInfoList =
257+
mSubscriptionManager.getActiveSubscriptionInfoList(true);
258+
if (subInfoList != null) {
259+
for (SubscriptionInfo subInfo : subInfoList) {
260+
telephonyManager =
261+
mTelephonyManager.createForSubscriptionId(subInfo.getSubscriptionId());
262+
if (telephonyManager.getDataEnabled()) {
263+
// Active SIM found
264+
return subInfo.getDisplayName().toString();
265+
}
266+
}
267+
}
268+
return mInitialState;
269+
}
270+
}

0 commit comments

Comments
 (0)