diff options
author | Christian Oder <myself5@carbonrom.org> | 2019-11-26 23:35:01 +0100 |
---|---|---|
committer | alk3pInjection <webmaster@raspii.tech> | 2022-02-18 09:36:24 +0800 |
commit | 45c5b77da5cf3f470deaf3e5e86b635544158030 (patch) | |
tree | 123fcc3a448eaa8c04825a51d52562b8a7390da8 | |
parent | ef1b3049ddf5aa23d5000b1aebf60314b78ddd67 (diff) |
SystemUI: Introduce DataSwitchTile
* Based on OnePlus' OxygenOS tile, reworked to work with AOSP toggling
without requirements on proprietary telephony-ext features.
* QS icons are designed and made by Andrew Fluck.
Co-authored-by: idoybh <idoybh2@gmail.com>
Co-authored-by: jhonboy121 <alfredmathew05@gmail.com>
Change-Id: Ie2e280c07f24f9da6b4ee218b72501a2713ce429
8 files changed, 340 insertions, 2 deletions
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml index f2a33de008d6..437ad2fa035e 100644 --- a/data/etc/com.android.systemui.xml +++ b/data/etc/com.android.systemui.xml @@ -60,6 +60,7 @@ <permission name="android.permission.UPDATE_APP_OPS_STATS"/> <permission name="android.permission.USE_RESERVED_DISK"/> <permission name="android.permission.WATCH_APPOPS"/> + <permission name="android.permission.WRITE_APN_SETTINGS"/> <permission name="android.permission.WRITE_DREAM_STATE"/> <permission name="android.permission.WRITE_MEDIA_STORAGE"/> <permission name="android.permission.MANAGE_EXTERNAL_STORAGE"/> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 1e3b45eada21..41b320caf600 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -294,6 +294,9 @@ <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" /> + <!-- DataSwitch tile --> + <uses-permission android:name="android.permission.WRITE_APN_SETTINGS" /> + <application android:name=".SystemUIApplication" android:persistent="true" diff --git a/packages/SystemUI/res/drawable/ic_qs_data_switch_1.xml b/packages/SystemUI/res/drawable/ic_qs_data_switch_1.xml new file mode 100644 index 000000000000..aa8139294b1d --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_data_switch_1.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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:pathData="M10,2H18C19.1,2 20,2.9 20,4V14H18V4H10.83L6,8.83V20H12V22H6C4.9,22 4,21.1 4,20V8L10,2Z" + android:fillColor="#000000" + android:fillType="evenOdd"/> + <path + android:pathData="M15.4492,11H14.1289V6.7969L12.832,7.1758V6.1758L15.3281,5.3125H15.4492V11Z" + android:fillColor="#000000"/> + <path + android:pathData="M14,20V16H18L14,20Z" + android:fillColor="#000000"/> + <path + android:pathData="M20,18L20,22L16,22L20,18Z" + android:fillColor="#000000"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_data_switch_2.xml b/packages/SystemUI/res/drawable/ic_qs_data_switch_2.xml new file mode 100644 index 000000000000..cd0b057174d9 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_data_switch_2.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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:pathData="M10,2H18C19.1,2 20,2.9 20,4V14H18V4H10.83L6,8.83V20H12V22H6C4.9,22 4,21.1 4,20V8L10,2Z" + android:fillColor="#000000" + android:fillType="evenOdd"/> + <path + 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" + android:fillColor="#000000"/> + <path + android:pathData="M14,20V16H18L14,20Z" + android:fillColor="#000000"/> + <path + android:pathData="M20,18L20,22L16,22L20,18Z" + android:fillColor="#000000"/> +</vector> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 9226ca873156..18dd1dce7727 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -107,7 +107,7 @@ <!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" --> <string name="quick_settings_tiles_stock" translatable="false"> - internet,wifi,cell,bt,flashlight,dnd,alarm,airplane,nfc,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,ambient_display,aod,caffeine,heads_up + internet,wifi,cell,bt,flashlight,dnd,alarm,airplane,nfc,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,ambient_display,aod,caffeine,heads_up,dataswitch </string> <!-- The tiles to display in QuickSettings --> diff --git a/packages/SystemUI/res/values/ice_strings.xml b/packages/SystemUI/res/values/ice_strings.xml index 36c1e3a2592c..d4d8b24834fd 100644 --- a/packages/SystemUI/res/values/ice_strings.xml +++ b/packages/SystemUI/res/values/ice_strings.xml @@ -38,4 +38,11 @@ <string name="accessibility_quick_settings_heads_up_on">Heads up on.</string> <string name="accessibility_quick_settings_heads_up_changed_off">Heads up turned off.</string> <string name="accessibility_quick_settings_heads_up_changed_on">Heads up turned on.</string> + + <!-- DataSwitch Tile --> + <string name="qs_data_switch_label">Switch data card</string> + <string name="qs_data_switch_toast_0">Currently no SIM card is inserted</string> + <string name="qs_data_switch_toast_1">Currently only one SIM card is inserted</string> + <string name="qs_data_switch_changed_1">Switch data card to SIM 1.</string> + <string name="qs_data_switch_changed_2">Switch data card to SIM 2.</string> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java index 7a18353792ee..1301708ac648 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java @@ -55,6 +55,7 @@ import com.android.systemui.qs.tiles.UiModeNightTile; import com.android.systemui.qs.tiles.UserTile; import com.android.systemui.qs.tiles.WifiTile; import com.android.systemui.qs.tiles.WorkModeTile; +import com.android.systemui.qs.tiles.DataSwitchTile; import com.android.systemui.util.leak.GarbageMonitor; import javax.inject.Inject; @@ -98,6 +99,7 @@ public class QSFactoryImpl implements QSFactory { private final Provider<AODTile> mAODTileProvider; private final Provider<CaffeineTile> mCaffeineTileProvider; private final Provider<HeadsUpTile> mHeadsUpTileProvider; + private final Provider<DataSwitchTile> mDataSwitchTileProvider; private final Lazy<QSHost> mQsHostLazy; private final Provider<CustomTile.Builder> mCustomTileBuilderProvider; @@ -136,7 +138,8 @@ public class QSFactoryImpl implements QSFactory { Provider<AmbientDisplayTile> ambientDisplayTileProvider, Provider<AODTile> aodTileProvider, Provider<CaffeineTile> caffeineTileProvider, - Provider<HeadsUpTile> headsUpTileProvider) { + Provider<HeadsUpTile> headsUpTileProvider, + Provider<DataSwitchTile> dataSwitchTileProvider) { mQsHostLazy = qsHostLazy; mCustomTileBuilderProvider = customTileBuilderProvider; @@ -171,6 +174,7 @@ public class QSFactoryImpl implements QSFactory { mAODTileProvider = aodTileProvider; mCaffeineTileProvider = caffeineTileProvider; mHeadsUpTileProvider = headsUpTileProvider; + mDataSwitchTileProvider = dataSwitchTileProvider; } public QSTile createTile(String tileSpec) { @@ -246,6 +250,8 @@ public class QSFactoryImpl implements QSFactory { return mCaffeineTileProvider.get(); case "heads_up": return mHeadsUpTileProvider.get(); + case "dataswitch": + return mDataSwitchTileProvider.get(); } // Custom tiles diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSwitchTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSwitchTile.java new file mode 100644 index 000000000000..1850b1aa5e38 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSwitchTile.java @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use mHost 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.android.systemui.qs.tiles; + +import android.annotation.Nullable; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; +import android.os.Looper; +import android.os.SystemProperties; +import android.provider.Settings; +import android.telephony.PhoneStateListener; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; +import android.widget.Toast; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.telephony.IccCardConstants; +import com.android.internal.telephony.TelephonyIntents; +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.R; +import com.android.systemui.plugins.qs.QSTile.BooleanState; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon; + +import java.util.concurrent.Executors; +import java.util.List; + +import javax.inject.Inject; + +public class DataSwitchTile extends QSTileImpl<BooleanState> { + private boolean mCanSwitch = true; + private boolean mRegistered; + private int mSimCount; + + private final Intent mLongClickIntent; + private final String mTileLabel; + private final BroadcastReceiver mSimReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (DEBUG) Log.d(TAG, "mSimReceiver:onReceive"); + refreshState(); + } + }; + private final PhoneStateListener mPhoneStateListener; + private final SubscriptionManager mSubscriptionManager; + private final TelephonyManager mTelephonyManager; + + @Inject + public DataSwitchTile( + QSHost host, + @Background Looper backgroundLooper, + @Main Handler mainHandler, + FalsingManager falsingManager, + MetricsLogger metricsLogger, + StatusBarStateController statusBarStateController, + ActivityStarter activityStarter, + QSLogger qsLogger + ) { + super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + statusBarStateController, activityStarter, qsLogger); + mLongClickIntent = new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS); + mTileLabel = mContext.getString(R.string.qs_data_switch_label); + mSubscriptionManager = SubscriptionManager.from(mContext); + mTelephonyManager = TelephonyManager.from(mContext); + mPhoneStateListener = new PhoneStateListener() { + @Override + public void onCallStateChanged(int state, String arg1) { + mCanSwitch = mTelephonyManager.getCallState() == 0; + refreshState(); + } + }; + } + + @Override + public boolean isAvailable() { + int count = TelephonyManager.getDefault().getPhoneCount(); + if (DEBUG) Log.d(TAG, "phoneCount: " + count); + return count >= 2; + } + + @Override + public BooleanState newTileState() { + final BooleanState state = new BooleanState(); + state.label = mContext.getString(R.string.qs_data_switch_label); + return state; + } + + @Override + public void handleSetListening(boolean listening) { + if (listening) { + if (!mRegistered) { + final IntentFilter filter = new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED); + mContext.registerReceiver(mSimReceiver, filter); + mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); + mRegistered = true; + } + refreshState(); + } else if (mRegistered) { + mContext.unregisterReceiver(mSimReceiver); + mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); + mRegistered = false; + } + } + + private void updateSimCount() { + String simState = SystemProperties.get("gsm.sim.state"); + if (DEBUG) Log.d(TAG, "DataSwitchTile:updateSimCount:simState=" + simState); + mSimCount = 0; + try { + final String[] sims = TextUtils.split(simState, ","); + for (String sim : sims) { + if (!sim.isEmpty() + && !sim.equalsIgnoreCase(IccCardConstants.INTENT_VALUE_ICC_ABSENT) + && !sim.equalsIgnoreCase(IccCardConstants.INTENT_VALUE_ICC_NOT_READY)) { + mSimCount++; + } + } + } catch (NullPointerException e) { + Log.e(TAG, "Error on parsing sim state " + e.getMessage()); + } + if (DEBUG) Log.d(TAG, "DataSwitchTile:updateSimCount:mSimCount=" + mSimCount); + } + + @Override + protected void handleClick(@Nullable View view) { + if (!mCanSwitch) { + if (DEBUG) Log.d(TAG, "Call state=" + mTelephonyManager.getCallState()); + } else if (mSimCount == 0) { + if (DEBUG) Log.d(TAG, "handleClick:no sim card"); + Toast.makeText(mContext, R.string.qs_data_switch_toast_0, + Toast.LENGTH_LONG).show(); + } else if (mSimCount == 1) { + if (DEBUG) Log.d(TAG, "handleClick:only one sim card"); + Toast.makeText(mContext, R.string.qs_data_switch_toast_1, + Toast.LENGTH_LONG).show(); + } else { + Executors.newSingleThreadExecutor().execute(() -> { + toggleMobileDataEnabled(); + refreshState(); + }); + } + } + + @Override + public Intent getLongClickIntent() { + return mLongClickIntent; + } + + @Override + public CharSequence getTileLabel() { + return mTileLabel; + } + + @Override + protected void handleUpdateState(BooleanState state, Object arg) { + boolean activeSIMZero; + if (arg == null) { + int defaultPhoneId = mSubscriptionManager.getDefaultDataPhoneId(); + if (DEBUG) Log.d(TAG, "default data phone id=" + defaultPhoneId); + activeSIMZero = defaultPhoneId == 0; + } else { + activeSIMZero = (Boolean) arg; + } + updateSimCount(); + state.value = mSimCount == 2; + if (mSimCount == 1 || mSimCount == 2) { + state.icon = ResourceIcon.get(activeSIMZero + ? R.drawable.ic_qs_data_switch_1 + : R.drawable.ic_qs_data_switch_2); + } else { + state.icon = ResourceIcon.get(R.drawable.ic_qs_data_switch_1); + } + if (mSimCount < 2 || !mCanSwitch) { + state.state = 0; + if (!mCanSwitch && DEBUG) Log.d(TAG, "call state isn't idle, set to unavailable."); + } else { + state.state = state.value ? 2 : 1; + } + + state.contentDescription = + mContext.getString(activeSIMZero + ? R.string.qs_data_switch_changed_1 + : R.string.qs_data_switch_changed_2); + } + + @Override + public int getMetricsCategory() { + return MetricsEvent.ICE; + } + + @Override + protected String composeChangeAnnouncement() { + return mContext.getString(mState.value + ? R.string.qs_data_switch_changed_1 + : R.string.qs_data_switch_changed_2); + } + + /** + * Set whether to enable data for {@code subId}, also whether to disable data for other + * subscription + */ + private void toggleMobileDataEnabled() { + // Get opposite slot 2 ^ 3 = 1, 1 ^ 3 = 2 + int subId = SubscriptionManager.getDefaultDataSubscriptionId() ^ 3; + final TelephonyManager telephonyManager = + mTelephonyManager.createForSubscriptionId(subId); + telephonyManager.setDataEnabled(true); + mSubscriptionManager.setDefaultDataSubId(subId); + if (DEBUG) Log.d(TAG, "Enabled subID: " + subId); + + final List<SubscriptionInfo> subInfoList = + mSubscriptionManager.getActiveSubscriptionInfoList(true); + if (subInfoList != null) { + // We never disable mobile data for opportunistic subscriptions. + subInfoList.stream() + .filter(subInfo -> !subInfo.isOpportunistic()) + .map(subInfo -> subInfo.getSubscriptionId()) + .filter(id -> id != subId) + .forEach(id -> { + mTelephonyManager.createForSubscriptionId(id).setDataEnabled(false); + if (DEBUG) Log.d(TAG, "Disabled subID: " + id); + }); + } + } +} |