summaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
Diffstat (limited to 'packages')
-rw-r--r--packages/SystemUI/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_data_switch_1.xml35
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_data_switch_2.xml35
-rw-r--r--packages/SystemUI/res/values/config.xml2
-rw-r--r--packages/SystemUI/res/values/ice_strings.xml7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DataSwitchTile.java251
7 files changed, 339 insertions, 2 deletions
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 85cf73fc88c6..d71d38a08ff0 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -297,6 +297,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 466ddf5c8974..345d162e68a5 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -82,7 +82,7 @@
<!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
<string name="quick_settings_tiles_stock" translatable="false">
- internet,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,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,dataswitch,heads_up
</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);
+ });
+ }
+ }
+}