Merge "Fix issue where EmergencyDialer is slow to open in some cases."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index f86cdef..c9281d1 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -259,6 +259,21 @@
android:targetActivity="MobileNetworkSettings" />
<!-- networks setting -->
+ <!-- "Choose network" screen. Used only when the Automatically
+ select network turned off-->
+ <activity android:name="NetworkSelectSettingActivity"
+ android:label="@string/choose_network_title"
+ android:theme="@style/NetworkOperatorsSettingsTheme"
+ android:configChanges="orientation|screenSize">
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <action android:name="android.intent.action.MAIN" />
+ <action android:name="android.settings.NETWORK_OPERATOR_SETTINGS" />
+ <action android:name="android.settings.DATA_ROAMING_SETTINGS" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
<!-- service to handle network query requests sent to RIL -->
<service android:name="NetworkQueryService" />
diff --git a/res/layout/choose_network.xml b/res/layout/choose_network.xml
new file mode 100644
index 0000000..2c327c56
--- /dev/null
+++ b/res/layout/choose_network.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/choose_network_content"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent">
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/choose_network_progress_header.xml b/res/layout/choose_network_progress_header.xml
new file mode 100644
index 0000000..671c297
--- /dev/null
+++ b/res/layout/choose_network_progress_header.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 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.
+-->
+
+<FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="3dp"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <View
+ android:id="@+id/progress_bar_background"
+ style="@style/TrimmedHorizontalProgressBar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/colorSecondary" />
+ <ProgressBar
+ android:id="@+id/progress_bar_animation"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/TrimmedHorizontalProgressBar"
+ android:indeterminate="true" />
+</FrameLayout>
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index f6d1b79..42f9563 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -110,4 +110,7 @@
<dimen name="floating_action_button_margin_bottom">32dp</dimen>
<dimen name="signal_strength_icon_size">24dp</dimen>
+
+ <!-- The size of the "emergency calling unavailable" message shown in the emergency dialer -->
+ <dimen name="emergency_call_warning_size">16sp</dimen>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f8afe6f..1bdd342 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -378,6 +378,8 @@
<string name="connect_later">Can\'t connect to this network right now. Try again later.</string>
<!-- Available networks screen, toast when registered on a specific network -->
<string name="registration_done">Registered on network.</string>
+ <!-- Available networks, toast when user selects automatic selection -->
+ <string name="already_auto">Already in automatic selection.</string>
<!-- Available networks screen, name of switch button for whether to select network automatically -->
<string name="select_automatically">Automatically select network</string>
<!-- Available networks screen, name of button when user wants to select network manually -->
@@ -1129,6 +1131,9 @@
<string name="dial_emergency_error">Can\'t call. <xliff:g id="non_emergency_number">%s</xliff:g> is not an emergency number.</string>
<!-- Emergency dialer: message displayed in an error dialog -->
<string name="dial_emergency_empty_error">Can\'t call. Dial an emergency number.</string>
+ <!-- Message displayed over top of the emergency dialer dialpad indicating to the user that
+ emergency calling is not currently available. -->
+ <string name="dial_emergency_calling_not_available">Emergency calling not available</string>
<!-- Displayed in the text entry box in the dialer when in landscape mode to guide the user
to dial using the physical keyboard -->
diff --git a/res/xml/choose_network.xml b/res/xml/choose_network.xml
new file mode 100644
index 0000000..fdf2aae
--- /dev/null
+++ b/res/xml/choose_network.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ android:title="@string/choose_network_title">
+
+ <PreferenceCategory
+ android:key="connected_network_operator_preference"/>
+
+ <PreferenceCategory
+ android:key="network_operators_preference"/>
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/res/xml/gsm_umts_options.xml b/res/xml/gsm_umts_options.xml
index e3e2617..1df4c84 100644
--- a/res/xml/gsm_umts_options.xml
+++ b/res/xml/gsm_umts_options.xml
@@ -31,6 +31,11 @@
android:key="button_network_select_key"
android:title="@string/network_select_title"
android:persistent="false"/>
+
+ <Preference
+ android:key="button_choose_network_key"
+ android:title="@string/choose_network_title"
+ android:fragment="com.android.phone.NetworkSelectSetting" />
</com.android.phone.NetworkOperators>
<!--We want separate APN setting from reset of settings because-->
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index 8da980c..fd85585 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -281,7 +281,9 @@
addPreferencesFromResource(R.xml.cdma_call_privacy);
}
} else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
-
+ if (mPhone.getIccCard() == null || !mPhone.getIccCard().getIccFdnAvailable()) {
+ prefSet.removePreference(fdnButton);
+ }
if (carrierConfig.getBoolean(
CarrierConfigManager.KEY_ADDITIONAL_CALL_SETTING_BOOL)) {
addPreferencesFromResource(R.xml.gsm_umts_call_options);
diff --git a/src/com/android/phone/CellInfoUtil.java b/src/com/android/phone/CellInfoUtil.java
new file mode 100644
index 0000000..c0409d8
--- /dev/null
+++ b/src/com/android/phone/CellInfoUtil.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2018 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 com.android.phone;
+
+import android.telephony.CellIdentity;
+import android.telephony.CellIdentityCdma;
+import android.telephony.CellIdentityGsm;
+import android.telephony.CellIdentityLte;
+import android.telephony.CellIdentityWcdma;
+import android.telephony.CellInfo;
+import android.telephony.CellInfoCdma;
+import android.telephony.CellInfoGsm;
+import android.telephony.CellInfoLte;
+import android.telephony.CellInfoWcdma;
+import android.telephony.TelephonyManager;
+import android.text.BidiFormatter;
+import android.text.TextDirectionHeuristics;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.telephony.OperatorInfo;
+
+/**
+ * Add static Utility functions to get information from the CellInfo object.
+ * TODO: Modify {@link CellInfo} for simplify those functions
+ */
+public final class CellInfoUtil {
+ private static final String TAG = "NetworkSelectSetting";
+
+ private CellInfoUtil() {
+ }
+
+ /**
+ * Get the network type from a CellInfo. Network types include
+ * {@link TelephonyManager#NETWORK_TYPE_LTE}, {@link TelephonyManager#NETWORK_TYPE_UMTS},
+ * {@link TelephonyManager#NETWORK_TYPE_GSM}, {@link TelephonyManager#NETWORK_TYPE_CDMA} and
+ * {@link TelephonyManager#NETWORK_TYPE_UNKNOWN}
+ * @return network types
+ */
+ public static int getNetworkType(CellInfo cellInfo) {
+ if (cellInfo instanceof CellInfoLte) {
+ return TelephonyManager.NETWORK_TYPE_LTE;
+ } else if (cellInfo instanceof CellInfoWcdma) {
+ return TelephonyManager.NETWORK_TYPE_UMTS;
+ } else if (cellInfo instanceof CellInfoGsm) {
+ return TelephonyManager.NETWORK_TYPE_GSM;
+ } else if (cellInfo instanceof CellInfoCdma) {
+ return TelephonyManager.NETWORK_TYPE_CDMA;
+ } else {
+ Log.e(TAG, "Invalid CellInfo type");
+ return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ }
+ }
+
+ /**
+ * Get signal level as an int from 0..4.
+ * @return Signal strength level
+ */
+ public static int getLevel(CellInfo cellInfo) {
+ if (cellInfo instanceof CellInfoLte) {
+ return ((CellInfoLte) cellInfo).getCellSignalStrength().getLevel();
+ } else if (cellInfo instanceof CellInfoWcdma) {
+ return ((CellInfoWcdma) cellInfo).getCellSignalStrength().getLevel();
+ } else if (cellInfo instanceof CellInfoGsm) {
+ return ((CellInfoGsm) cellInfo).getCellSignalStrength().getLevel();
+ } else if (cellInfo instanceof CellInfoCdma) {
+ return ((CellInfoCdma) cellInfo).getCellSignalStrength().getLevel();
+ } else {
+ Log.e(TAG, "Invalid CellInfo type");
+ return 0;
+ }
+ }
+
+ /**
+ * Wrap a CellIdentity into a CellInfo.
+ */
+ public static CellInfo wrapCellInfoWithCellIdentity(CellIdentity cellIdentity) {
+ if (cellIdentity instanceof CellIdentityLte) {
+ CellInfoLte cellInfo = new CellInfoLte();
+ cellInfo.setCellIdentity((CellIdentityLte) cellIdentity);
+ return cellInfo;
+ } else if (cellIdentity instanceof CellIdentityCdma) {
+ CellInfoCdma cellInfo = new CellInfoCdma();
+ cellInfo.setCellIdentity((CellIdentityCdma) cellIdentity);
+ return cellInfo;
+ } else if (cellIdentity instanceof CellIdentityWcdma) {
+ CellInfoWcdma cellInfo = new CellInfoWcdma();
+ cellInfo.setCellIdentity((CellIdentityWcdma) cellIdentity);
+ return cellInfo;
+ } else if (cellIdentity instanceof CellIdentityGsm) {
+ CellInfoGsm cellInfo = new CellInfoGsm();
+ cellInfo.setCellIdentity((CellIdentityGsm) cellIdentity);
+ return cellInfo;
+ } else {
+ Log.e(TAG, "Invalid CellInfo type");
+ return null;
+ }
+ }
+
+ /**
+ * Returns the title of the network obtained in the manual search.
+ *
+ * @param cellInfo contains the information of the network.
+ * @return Long Name if not null/empty, otherwise Short Name if not null/empty,
+ * else MCCMNC string.
+ */
+ public static String getNetworkTitle(CellInfo cellInfo) {
+ OperatorInfo oi = getOperatorInfoFromCellInfo(cellInfo);
+
+ if (!TextUtils.isEmpty(oi.getOperatorAlphaLong())) {
+ return oi.getOperatorAlphaLong();
+ } else if (!TextUtils.isEmpty(oi.getOperatorAlphaShort())) {
+ return oi.getOperatorAlphaShort();
+ } else {
+ BidiFormatter bidiFormatter = BidiFormatter.getInstance();
+ return bidiFormatter.unicodeWrap(oi.getOperatorNumeric(), TextDirectionHeuristics.LTR);
+ }
+ }
+
+ /**
+ * Wrap a cell info into an operator info.
+ */
+ public static OperatorInfo getOperatorInfoFromCellInfo(CellInfo cellInfo) {
+ OperatorInfo oi;
+ if (cellInfo instanceof CellInfoLte) {
+ CellInfoLte lte = (CellInfoLte) cellInfo;
+ oi = new OperatorInfo(
+ (String) lte.getCellIdentity().getOperatorAlphaLong(),
+ (String) lte.getCellIdentity().getOperatorAlphaShort(),
+ lte.getCellIdentity().getMobileNetworkOperator());
+ } else if (cellInfo instanceof CellInfoWcdma) {
+ CellInfoWcdma wcdma = (CellInfoWcdma) cellInfo;
+ oi = new OperatorInfo(
+ (String) wcdma.getCellIdentity().getOperatorAlphaLong(),
+ (String) wcdma.getCellIdentity().getOperatorAlphaShort(),
+ wcdma.getCellIdentity().getMobileNetworkOperator());
+ } else if (cellInfo instanceof CellInfoGsm) {
+ CellInfoGsm gsm = (CellInfoGsm) cellInfo;
+ oi = new OperatorInfo(
+ (String) gsm.getCellIdentity().getOperatorAlphaLong(),
+ (String) gsm.getCellIdentity().getOperatorAlphaShort(),
+ gsm.getCellIdentity().getMobileNetworkOperator());
+ } else if (cellInfo instanceof CellInfoCdma) {
+ CellInfoCdma cdma = (CellInfoCdma) cellInfo;
+ oi = new OperatorInfo(
+ (String) cdma.getCellIdentity().getOperatorAlphaLong(),
+ (String) cdma.getCellIdentity().getOperatorAlphaShort(),
+ "" /* operator numeric */);
+ } else {
+ Log.e(TAG, "Invalid CellInfo type");
+ oi = new OperatorInfo("", "", "");
+ }
+ return oi;
+ }
+}
diff --git a/src/com/android/phone/EmergencyDialer.java b/src/com/android/phone/EmergencyDialer.java
index 09bb8bc..31e1958 100644
--- a/src/com/android/phone/EmergencyDialer.java
+++ b/src/com/android/phone/EmergencyDialer.java
@@ -16,10 +16,11 @@
package com.android.phone;
+import static android.telephony.ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
+
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
-import android.app.StatusBarManager;
import android.app.WallpaperManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -29,13 +30,17 @@
import android.media.AudioManager;
import android.media.ToneGenerator;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.provider.Settings;
import android.telecom.PhoneAccount;
+import android.telecom.TelecomManager;
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneNumberUtils;
+import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import android.text.Editable;
import android.text.InputType;
import android.text.Spannable;
@@ -45,19 +50,20 @@
import android.text.method.DialerKeyListener;
import android.text.style.TtsSpan;
import android.util.Log;
+import android.util.TypedValue;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
-import android.widget.EditText;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.colorextraction.ColorExtractor.GradientColors;
import com.android.internal.colorextraction.drawable.GradientDrawable;
import com.android.phone.common.dialpad.DialpadKeyButton;
import com.android.phone.common.util.ViewUtil;
+import com.android.phone.common.widget.ResizingTextEditText;
/**
* EmergencyDialer is a special dialer that is used ONLY for dialing emergency calls.
@@ -110,7 +116,7 @@
/** 90% opacity, different from other gradients **/
private static final int BACKGROUND_GRADIENT_ALPHA = 230;
- EditText mDigits;
+ ResizingTextEditText mDigits;
private View mDialButton;
private View mDelete;
@@ -139,6 +145,9 @@
private GradientDrawable mBackgroundGradient;
private boolean mSupportsDarkText;
+ private boolean mIsWfcEmergencyCallingWarningEnabled;
+ private float mDefaultDigitsTextSize;
+
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// Do nothing
@@ -146,7 +155,7 @@
@Override
public void onTextChanged(CharSequence input, int start, int before, int changeCount) {
- // Do nothing
+ maybeChangeHintSize();
}
@Override
@@ -190,12 +199,13 @@
setContentView(R.layout.emergency_dialer);
- mDigits = (EditText) findViewById(R.id.digits);
+ mDigits = (ResizingTextEditText) findViewById(R.id.digits);
mDigits.setKeyListener(DialerKeyListener.getInstance());
mDigits.setOnClickListener(this);
mDigits.setOnKeyListener(this);
mDigits.setLongClickable(false);
mDigits.setInputType(InputType.TYPE_NULL);
+ mDefaultDigitsTextSize = mDigits.getScaledTextSize();
maybeAddNumberFormatting();
mBackgroundGradient = new GradientDrawable(this);
@@ -225,11 +235,16 @@
(CarrierConfigManager) getSystemService(Context.CARRIER_CONFIG_SERVICE);
PersistableBundle carrierConfig =
configMgr.getConfigForSubId(SubscriptionManager.getDefaultVoiceSubscriptionId());
+
if (carrierConfig.getBoolean(CarrierConfigManager.KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL)) {
mDialButton.setOnClickListener(this);
} else {
mDialButton.setVisibility(View.GONE);
}
+ mIsWfcEmergencyCallingWarningEnabled = carrierConfig.getInt(
+ CarrierConfigManager.KEY_EMERGENCY_NOTIFICATION_DELAY_INT) > -1;
+ maybeShowWfcEmergencyCallingWarning();
+
ViewUtil.setupFloatingActionButton(mDialButton, getResources());
if (icicle != null) {
@@ -580,10 +595,8 @@
playTone(ToneGenerator.TONE_PROP_NACK);
return;
}
- Intent intent = new Intent(Intent.ACTION_CALL_EMERGENCY);
- intent.setData(Uri.fromParts(PhoneAccount.SCHEME_TEL, mLastNumber, null));
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
+ TelecomManager tm = (TelecomManager) getSystemService(TELECOM_SERVICE);
+ tm.placeCall(Uri.fromParts(PhoneAccount.SCHEME_TEL, mLastNumber, null), null);
} else {
if (DBG) Log.d(LOG_TAG, "rejecting bad requested number " + mLastNumber);
@@ -720,4 +733,68 @@
updateTheme(colors.supportsDarkText());
}
}
+
+ /**
+ * Where a carrier requires a warning that emergency calling is not available while on WFC,
+ * add hint text above the dial pad which warns the user of this case.
+ */
+ private void maybeShowWfcEmergencyCallingWarning() {
+ if (!mIsWfcEmergencyCallingWarningEnabled) {
+ Log.i(LOG_TAG, "maybeShowWfcEmergencyCallingWarning: warning disabled by carrier.");
+ return;
+ }
+
+ // Use an async task rather than calling into Telephony on UI thread.
+ AsyncTask<Void, Void, Boolean> showWfcWarningTask = new AsyncTask<Void, Void, Boolean>() {
+ @Override
+ protected Boolean doInBackground(Void... voids) {
+ TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
+ boolean isWfcAvailable = tm.isWifiCallingAvailable();
+ ServiceState ss = tm.getServiceState();
+ boolean isCellAvailable =
+ ss.getRilVoiceRadioTechnology() != RIL_RADIO_TECHNOLOGY_UNKNOWN;
+ Log.i(LOG_TAG, "showWfcWarningTask: isWfcAvailable=" + isWfcAvailable
+ + " isCellAvailable=" + isCellAvailable
+ + "(rat=" + ss.getRilVoiceRadioTechnology() + ")");
+ return isWfcAvailable && !isCellAvailable;
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ if (result.booleanValue()) {
+ Log.i(LOG_TAG, "showWfcWarningTask: showing ecall warning");
+ mDigits.setHint(R.string.dial_emergency_calling_not_available);
+ } else {
+ Log.i(LOG_TAG, "showWfcWarningTask: hiding ecall warning");
+ mDigits.setHint("");
+ }
+ maybeChangeHintSize();
+ }
+ };
+ showWfcWarningTask.execute((Void) null);
+ }
+
+ /**
+ * Where a hint is applied and there are no digits dialed, disable autoresize of the dial digits
+ * edit view and set the font size to a smaller size appropriate for the emergency calling
+ * warning.
+ */
+ private void maybeChangeHintSize() {
+ if (TextUtils.isEmpty(mDigits.getHint())
+ || !TextUtils.isEmpty(mDigits.getText().toString())) {
+ // No hint or there are dialed digits, so use default size.
+ mDigits.setTextSize(TypedValue.COMPLEX_UNIT_SP, mDefaultDigitsTextSize);
+ // By default, the digits view auto-resizes to fit the text it contains, so
+ // enable that now.
+ mDigits.setResizeEnabled(true);
+ Log.i(LOG_TAG, "no hint - setting to " + mDigits.getScaledTextSize());
+ } else {
+ // Hint present and no dialed digits, set custom font size appropriate for the warning.
+ mDigits.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimensionPixelSize(
+ R.dimen.emergency_call_warning_size));
+ // Since we're populating this with a static text string, disable auto-resize.
+ mDigits.setResizeEnabled(false);
+ Log.i(LOG_TAG, "hint - setting to " + mDigits.getScaledTextSize());
+ }
+ }
}
diff --git a/src/com/android/phone/NetworkOperatorPreference.java b/src/com/android/phone/NetworkOperatorPreference.java
new file mode 100644
index 0000000..f29c038
--- /dev/null
+++ b/src/com/android/phone/NetworkOperatorPreference.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2018 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 com.android.phone;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.preference.Preference;
+import android.telephony.CellInfo;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.view.Gravity;
+
+import com.android.settingslib.graph.SignalDrawable;
+
+/**
+ * A Preference represents a network operator in the NetworkSelectSetting fragment.
+ */
+public class NetworkOperatorPreference extends Preference {
+
+ private static final String TAG = "NetworkOperatorPref";
+ private static final boolean DBG = true;
+ // number of signal strength level
+ public static final int NUMBER_OF_LEVELS = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
+ private CellInfo mCellInfo;
+ private int mLevel = -1;
+
+ // The following constants are used to draw signal icon.
+ private static final Drawable EMPTY_DRAWABLE = new ColorDrawable(Color.TRANSPARENT);
+ private static final int NO_CELL_DATA_CONNECTED_ICON = 0;
+
+ public NetworkOperatorPreference(CellInfo cellinfo, Context context) {
+ super(context);
+ mCellInfo = cellinfo;
+ refresh();
+ }
+
+ public CellInfo getCellInfo() {
+ return mCellInfo;
+ }
+
+ /**
+ * Refresh the NetworkOperatorPreference by updating the title and the icon.
+ */
+ public void refresh() {
+ if (DBG) Log.d(TAG, "refresh the network: " + CellInfoUtil.getNetworkTitle(mCellInfo));
+ setTitle(CellInfoUtil.getNetworkTitle(mCellInfo));
+ int level = CellInfoUtil.getLevel(mCellInfo);
+ if (DBG) Log.d(TAG, "refresh level: " + String.valueOf(level));
+ if (mLevel != level) {
+ mLevel = level;
+ updateIcon(mLevel);
+ }
+ }
+
+ /**
+ * Update the icon according to the input signal strength level.
+ */
+ public void setIcon(int level) {
+ updateIcon(level);
+ }
+
+ private int getIconId(int networkType) {
+ if (networkType == TelephonyManager.NETWORK_TYPE_CDMA) {
+ return R.drawable.signal_strength_1x;
+ } else if (networkType == TelephonyManager.NETWORK_TYPE_LTE) {
+ return R.drawable.signal_strength_lte;
+ } else if (networkType == TelephonyManager.NETWORK_TYPE_UMTS) {
+ return R.drawable.signal_strength_3g;
+ } else if (networkType == TelephonyManager.NETWORK_TYPE_GSM) {
+ return R.drawable.signal_strength_g;
+ } else {
+ return 0;
+ }
+ }
+
+ private void updateIcon(int level) {
+ if (level < 0 || level >= NUMBER_OF_LEVELS) return;
+ Context context = getContext();
+ // Make the signal strength drawable
+ int iconId = 0;
+ if (DBG) Log.d(TAG, "updateIcon level: " + String.valueOf(level));
+ iconId = SignalDrawable.getState(level, NUMBER_OF_LEVELS, false /* cutOut */);
+
+ SignalDrawable signalDrawable = new SignalDrawable(getContext());
+ signalDrawable.setLevel(iconId);
+ signalDrawable.setDarkIntensity(0);
+
+ // Make the network type drawable
+ int iconType = getIconId(CellInfoUtil.getNetworkType(mCellInfo));
+ Drawable networkDrawable =
+ iconType == NO_CELL_DATA_CONNECTED_ICON
+ ? EMPTY_DRAWABLE
+ : getContext()
+ .getResources().getDrawable(iconType, getContext().getTheme());
+
+ // Overlay the two drawables
+ Drawable[] layers = {networkDrawable, signalDrawable};
+ final int iconSize =
+ context.getResources().getDimensionPixelSize(R.dimen.signal_strength_icon_size);
+
+ LayerDrawable icons = new LayerDrawable(layers);
+ // Set the network type icon at the top left
+ icons.setLayerGravity(0 /* index of networkDrawable */, Gravity.TOP | Gravity.LEFT);
+ // Set the signal strength icon at the bottom right
+ icons.setLayerGravity(1 /* index of SignalDrawable */, Gravity.BOTTOM | Gravity.RIGHT);
+ icons.setLayerSize(1 /* index of SignalDrawable */, iconSize, iconSize);
+ setIcon(icons);
+ }
+}
diff --git a/src/com/android/phone/NetworkOperators.java b/src/com/android/phone/NetworkOperators.java
index 05cfe42..babd283 100644
--- a/src/com/android/phone/NetworkOperators.java
+++ b/src/com/android/phone/NetworkOperators.java
@@ -18,6 +18,7 @@
import android.app.ProgressDialog;
import android.content.Context;
+import android.content.Intent;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Message;
@@ -51,17 +52,24 @@
//String keys for preference lookup
public static final String BUTTON_NETWORK_SELECT_KEY = "button_network_select_key";
public static final String BUTTON_AUTO_SELECT_KEY = "button_auto_select_key";
+ public static final String BUTTON_CHOOSE_NETWORK_KEY = "button_choose_network_key";
public static final String CATEGORY_NETWORK_OPERATORS_KEY = "network_operators_category_key";
int mPhoneId = SubscriptionManager.INVALID_PHONE_INDEX;
+ private static final int ALREADY_IN_AUTO_SELECTION = 1;
//preference objects
private NetworkSelectListPreference mNetworkSelect;
private TwoStatePreference mAutoSelect;
+ private Preference mChooseNetwork;
private int mSubId;
private ProgressDialog mProgressDialog;
+ // There's two sets of Auto-Select UI in this class. {@link mNetworkSelect} is used for all
+ // pre-Pixel 3 devices, while {@link mChooseNetwork} is used for all devices after Pixel3.
+ boolean mEnableNewManualSelectNetworkUI;
+
public NetworkOperators(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -74,10 +82,16 @@
* Initialize NetworkOperators instance.
*/
public void initialize() {
- mNetworkSelect =
- (NetworkSelectListPreference) findPreference(BUTTON_NETWORK_SELECT_KEY);
- mAutoSelect =
- (TwoStatePreference) findPreference(BUTTON_AUTO_SELECT_KEY);
+ mEnableNewManualSelectNetworkUI = getContext().getResources().getBoolean(
+ com.android.internal.R.bool.config_enableNewAutoSelectNetworkUI);
+ mAutoSelect = (TwoStatePreference) findPreference(BUTTON_AUTO_SELECT_KEY);
+ mChooseNetwork = findPreference(BUTTON_CHOOSE_NETWORK_KEY);
+ mNetworkSelect = (NetworkSelectListPreference) findPreference(BUTTON_NETWORK_SELECT_KEY);
+ if (mEnableNewManualSelectNetworkUI) {
+ this.removePreference(mNetworkSelect);
+ } else {
+ this.removePreference(mChooseNetwork);
+ }
mProgressDialog = new ProgressDialog(getContext());
}
@@ -95,10 +109,22 @@
mAutoSelect.setOnPreferenceChangeListener(this);
}
- if (mNetworkSelect != null) {
- mNetworkSelect.initialize(mSubId, queryService, this, mProgressDialog);
+ if (mEnableNewManualSelectNetworkUI) {
+ if (mChooseNetwork != null) {
+ TelephonyManager telephonyManager = (TelephonyManager)
+ getContext().getSystemService(Context.TELEPHONY_SERVICE);
+ logd("data connection status " + telephonyManager.getDataState());
+ if (telephonyManager.getDataState() == telephonyManager.DATA_CONNECTED) {
+ mChooseNetwork.setSummary(telephonyManager.getNetworkOperatorName());
+ } else {
+ mChooseNetwork.setSummary(R.string.network_disconnected);
+ }
+ }
+ } else {
+ if (mNetworkSelect != null) {
+ mNetworkSelect.initialize(mSubId, queryService, this, mProgressDialog);
+ }
}
-
getNetworkSelectionMode();
}
@@ -113,6 +139,7 @@
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (preference == mAutoSelect) {
boolean autoSelect = (Boolean) newValue;
+ logd("onPreferenceChange autoSelect: " + String.valueOf(autoSelect));
selectNetworkAutomatic(autoSelect);
MetricsLogger.action(getContext(),
MetricsEvent.ACTION_MOBILE_NETWORK_AUTO_SELECT_NETWORK_TOGGLE, autoSelect);
@@ -136,7 +163,7 @@
displayNetworkSelectionFailed(ar.exception);
} else {
if (DBG) logd("automatic network selection: succeeded!");
- displayNetworkSelectionSucceeded();
+ displayNetworkSelectionSucceeded(msg.arg1);
}
break;
@@ -155,8 +182,14 @@
if (mAutoSelect != null) {
mAutoSelect.setChecked(autoSelect);
}
- if (mNetworkSelect != null) {
- mNetworkSelect.setEnabled(!autoSelect);
+ if (mEnableNewManualSelectNetworkUI) {
+ if (mChooseNetwork != null) {
+ mChooseNetwork.setEnabled(!autoSelect);
+ }
+ } else {
+ if (mNetworkSelect != null) {
+ mNetworkSelect.setEnabled(!autoSelect);
+ }
}
} catch (Exception e) {
if (DBG) loge("get network selection mode: unable to parse result.");
@@ -171,10 +204,9 @@
// Used by both mAutoSelect and mNetworkSelect buttons.
protected void displayNetworkSelectionFailed(Throwable ex) {
String status;
-
if ((ex != null && ex instanceof CommandException)
&& ((CommandException) ex).getCommandError()
- == CommandException.Error.ILLEGAL_SIM_OR_ME) {
+ == CommandException.Error.ILLEGAL_SIM_OR_ME) {
status = getContext().getResources().getString(R.string.not_allowed);
} else {
status = getContext().getResources().getString(R.string.connect_later);
@@ -195,8 +227,13 @@
}
// Used by both mAutoSelect and mNetworkSelect buttons.
- protected void displayNetworkSelectionSucceeded() {
- String status = getContext().getResources().getString(R.string.registration_done);
+ protected void displayNetworkSelectionSucceeded(int msgArg1) {
+ String status = null;
+ if (msgArg1 == ALREADY_IN_AUTO_SELECTION) {
+ status = getContext().getResources().getString(R.string.already_auto);
+ } else {
+ status = getContext().getResources().getString(R.string.registration_done);
+ }
final PhoneGlobals app = PhoneGlobals.getInstance();
app.notificationMgr.postTransientNotification(
@@ -204,9 +241,17 @@
}
private void selectNetworkAutomatic(boolean autoSelect) {
- if (mNetworkSelect != null) {
- mNetworkSelect.setEnabled(!autoSelect);
+ logd("selectNetworkAutomatic: " + String.valueOf(autoSelect));
+ if (mEnableNewManualSelectNetworkUI) {
+ if (mChooseNetwork != null) {
+ mChooseNetwork.setEnabled(!autoSelect);
+ }
+ } else {
+ if (mNetworkSelect != null) {
+ mNetworkSelect.setEnabled(!autoSelect);
+ }
}
+
if (autoSelect) {
if (DBG) logd("select network automatically...");
showAutoSelectProgressBar();
@@ -216,8 +261,17 @@
if (phone != null) {
phone.setNetworkSelectionModeAutomatic(msg);
}
- } else if (mNetworkSelect != null) {
- mNetworkSelect.onClick();
+ } else {
+ if (mEnableNewManualSelectNetworkUI) {
+ if (mChooseNetwork != null) {
+ // Open the choose Network page automatically when user turn off the auto-select
+ openChooseNetworkPage();
+ }
+ } else {
+ if (mNetworkSelect != null) {
+ mNetworkSelect.onClick();
+ }
+ }
}
}
@@ -245,8 +299,24 @@
mProgressDialog.show();
}
+ /**
+ * Open the Choose netwotk page via {@alink NetworkSelectSettingActivity}
+ */
+ public void openChooseNetworkPage() {
+ Intent intent = NetworkSelectSettingActivity.getIntent(getContext(), mPhoneId);
+ getContext().startActivity(intent);
+ }
+
protected boolean preferenceTreeClick(Preference preference) {
- return (preference == mAutoSelect || preference == mNetworkSelect);
+ if (mEnableNewManualSelectNetworkUI) {
+ logd("enable New AutoSelectNetwork UI");
+ if (preference == mChooseNetwork) {
+ openChooseNetworkPage();
+ }
+ return (preference == mAutoSelect || preference == mChooseNetwork);
+ } else {
+ return (preference == mAutoSelect || preference == mNetworkSelect);
+ }
}
private void logd(String msg) {
@@ -256,4 +326,4 @@
private void loge(String msg) {
Log.e(LOG_TAG, "[NetworksList] " + msg);
}
-}
+}
\ No newline at end of file
diff --git a/src/com/android/phone/NetworkSelectListPreference.java b/src/com/android/phone/NetworkSelectListPreference.java
index c2ed1ae..a8f29c9 100644
--- a/src/com/android/phone/NetworkSelectListPreference.java
+++ b/src/com/android/phone/NetworkSelectListPreference.java
@@ -124,7 +124,7 @@
logd("manual network selection: succeeded! "
+ getNetworkTitle(mCellInfo));
}
- mNetworkOperators.displayNetworkSelectionSucceeded();
+ mNetworkOperators.displayNetworkSelectionSucceeded(msg.arg1);
}
mNetworkOperators.getNetworkSelectionMode();
break;
diff --git a/src/com/android/phone/NetworkSelectSetting.java b/src/com/android/phone/NetworkSelectSetting.java
new file mode 100644
index 0000000..7ef3dea
--- /dev/null
+++ b/src/com/android/phone/NetworkSelectSetting.java
@@ -0,0 +1,634 @@
+/*
+ * Copyright (C) 2018 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 com.android.phone;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.metrics.LogMaker;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.preference.Preference;
+import android.preference.PreferenceCategory;
+import android.preference.PreferenceFragment;
+import android.preference.PreferenceScreen;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.CellIdentity;
+import android.telephony.CellInfo;
+import android.telephony.NetworkRegistrationState;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.telephony.OperatorInfo;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * "Choose network" settings UI for the Phone app.
+ */
+public class NetworkSelectSetting extends PreferenceFragment {
+
+ private static final String TAG = "NetworkSelectSetting";
+ private static final boolean DBG = true;
+
+ private static final int EVENT_NETWORK_SELECTION_DONE = 1;
+ private static final int EVENT_NETWORK_SCAN_RESULTS = 2;
+ private static final int EVENT_NETWORK_SCAN_ERROR = 3;
+ private static final int EVENT_NETWORK_SCAN_COMPLETED = 4;
+
+ private static final String PREF_KEY_CONNECTED_NETWORK_OPERATOR =
+ "connected_network_operator_preference";
+ private static final String PREF_KEY_NETWORK_OPERATORS = "network_operators_preference";
+
+ // used to add/remove NetworkOperatorsPreference.
+ private PreferenceCategory mNetworkOperatorsPreferences;
+ // used to add/remove connected NetworkOperatorPreference.
+ private PreferenceCategory mConnectedNetworkOperatorsPreference;
+ // manage the progress bar on the top of the page.
+ private View mProgressHeader;
+ private Preference mStatusMessagePreference;
+ private List<CellInfo> mCellInfoList;
+ private int mPhoneId = SubscriptionManager.INVALID_PHONE_INDEX;
+ private ViewGroup mFrameLayout;
+ private NetworkOperatorPreference mSelectedNetworkOperatorPreference;
+ private TelephonyManager mTelephonyManager;
+ private NetworkOperators mNetworkOperators;
+
+ private final Runnable mUpdateNetworkOperatorsRunnable = () -> {
+ updateNetworkOperatorsPreferenceCategory();
+ };
+
+ /**
+ * Create a new instance of this fragment.
+ */
+ public static NetworkSelectSetting newInstance(int phoneId) {
+ Bundle args = new Bundle();
+ args.putInt(NetworkSelectSettingActivity.KEY_PHONE_ID, phoneId);
+ NetworkSelectSetting fragment = new NetworkSelectSetting();
+ fragment.setArguments(args);
+
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ logd("onCreate");
+ super.onCreate(icicle);
+
+ mPhoneId = getArguments().getInt(NetworkSelectSettingActivity.KEY_PHONE_ID);
+
+ addPreferencesFromResource(R.xml.choose_network);
+ mConnectedNetworkOperatorsPreference =
+ (PreferenceCategory) findPreference(PREF_KEY_CONNECTED_NETWORK_OPERATOR);
+ mNetworkOperatorsPreferences =
+ (PreferenceCategory) findPreference(PREF_KEY_NETWORK_OPERATORS);
+ mStatusMessagePreference = new Preference(getContext());
+ mSelectedNetworkOperatorPreference = null;
+ mTelephonyManager = (TelephonyManager)
+ getContext().getSystemService(Context.TELEPHONY_SERVICE);
+ mNetworkOperators = new NetworkOperators(getContext());
+ setRetainInstance(true);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ logd("onViewCreated");
+ super.onViewCreated(view, savedInstanceState);
+
+ if (getListView() != null) {
+ getListView().setDivider(null);
+ }
+ // Inflate progress bar
+ final Activity activity = getActivity();
+ if (activity != null) {
+ mFrameLayout = activity.findViewById(R.id.choose_network_content);
+ final LayoutInflater inflater = activity.getLayoutInflater();
+ final View pinnedHeader =
+ inflater.inflate(R.layout.choose_network_progress_header, mFrameLayout, false);
+ mFrameLayout.addView(pinnedHeader);
+ mFrameLayout.setVisibility(View.VISIBLE);
+ mProgressHeader = pinnedHeader.findViewById(R.id.progress_bar_animation);
+ setProgressBarVisible(false);
+ }
+ forceConfigConnectedNetworkOperatorsPreferenceCategory();
+ }
+
+ @Override
+ public void onStart() {
+ if (DBG) logd("onStart");
+ super.onStart();
+
+ // Bind the NetworkQueryService
+ bindNetworkQueryService();
+ }
+
+ /**
+ * Invoked on each preference click in this hierarchy, overrides
+ * PreferenceActivity's implementation. Used to make sure we track the
+ * preference click events.
+ * Since the connected network operator is either faked (when no data connection) or already
+ * connected, we do not allow user to click the connected network operator.
+ */
+ @Override
+ public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
+ Preference preference) {
+ if (DBG) logd("User clicked the screen");
+ stopNetworkQuery();
+ setProgressBarVisible(false);
+ if (preference instanceof NetworkOperatorPreference) {
+ // Refresh the last selected item in case users reselect network.
+ if (mSelectedNetworkOperatorPreference != null) {
+ mSelectedNetworkOperatorPreference.setSummary("");
+ }
+
+ mSelectedNetworkOperatorPreference = (NetworkOperatorPreference) preference;
+ CellInfo cellInfo = mSelectedNetworkOperatorPreference.getCellInfo();
+ if (DBG) logd("User click a NetworkOperatorPreference: " + cellInfo.toString());
+
+ // Send metrics event
+ final LogMaker logMaker = new LogMaker(
+ MetricsProto.MetricsEvent.ACTION_MOBILE_NETWORK_MANUAL_SELECT_NETWORK)
+ .setType(MetricsProto.MetricsEvent.TYPE_ACTION);
+ if (CellInfoUtil.getNetworkTitle(cellInfo) != null) {
+ // Since operator list is loaded dynamically from modem, we cannot know which
+ // network user chooses if we only record integer index of newValue. So a new tag
+ // and a string value (network) is added in this MetricsEvent.
+ logMaker.addTaggedData(MetricsProto.MetricsEvent.FIELD_MOBILE_NETWORK,
+ CellInfoUtil.getNetworkTitle(cellInfo));
+ }
+ MetricsLogger.action(logMaker);
+
+ // Connect to the network
+ Message msg = mHandler.obtainMessage(EVENT_NETWORK_SELECTION_DONE);
+ Phone phone = PhoneFactory.getPhone(mPhoneId);
+ if (phone != null) {
+ if (DBG) {
+ logd("Connect to the network: " + CellInfoUtil.getNetworkTitle(cellInfo));
+ }
+ // Set summary as "Connecting" to the selected network.
+ mSelectedNetworkOperatorPreference.setSummary(R.string.network_connecting);
+
+ // Set summary as "Disconnected" to the previously connected network
+ if (mConnectedNetworkOperatorsPreference.getPreferenceCount() > 0) {
+ NetworkOperatorPreference connectedNetworkOperator = (NetworkOperatorPreference)
+ (mConnectedNetworkOperatorsPreference.getPreference(0));
+ if (!CellInfoUtil.getNetworkTitle(cellInfo).equals(
+ CellInfoUtil.getNetworkTitle(connectedNetworkOperator.getCellInfo()))) {
+ connectedNetworkOperator.setSummary(R.string.network_disconnected);
+ }
+ }
+
+ // Select network manually via Phone
+ OperatorInfo operatorInfo = CellInfoUtil.getOperatorInfoFromCellInfo(cellInfo);
+ if (DBG) logd("manually selected network operator: " + operatorInfo.toString());
+ phone.selectNetworkManually(operatorInfo, true, msg);
+ setProgressBarVisible(true);
+ return true;
+ } else {
+ loge("Error selecting network. phone is null.");
+ mSelectedNetworkOperatorPreference = null;
+ return false;
+ }
+
+ } else {
+ preferenceScreen.setEnabled(false);
+ return false;
+ }
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ if (!(getActivity() instanceof NetworkSelectSettingActivity)) {
+ throw new IllegalStateException("Parent activity is not NetworkSelectSettingActivity");
+ }
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (DBG) logd("onStop");
+ getView().removeCallbacks(mUpdateNetworkOperatorsRunnable);
+ stopNetworkQuery();
+ // Unbind the NetworkQueryService
+ unbindNetworkQueryService();
+ }
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ AsyncResult ar;
+ switch (msg.what) {
+ case EVENT_NETWORK_SELECTION_DONE:
+ if (DBG) logd("network selection done: hide the progress header");
+ setProgressBarVisible(false);
+
+ ar = (AsyncResult) msg.obj;
+ if (ar.exception != null) {
+ mNetworkOperators.displayNetworkSelectionFailed(ar.exception);
+ // Set summary as "Couldn't connect" to the selected network.
+ mSelectedNetworkOperatorPreference.setSummary(
+ R.string.network_could_not_connect);
+ } else {
+ if (DBG) logd("manual network selection: succeeded! ");
+ mNetworkOperators.displayNetworkSelectionSucceeded(msg.arg1);
+ // Set summary as "Connected" to the selected network.
+ mSelectedNetworkOperatorPreference.setSummary(R.string.network_connected);
+ }
+ break;
+
+ case EVENT_NETWORK_SCAN_RESULTS:
+ List<CellInfo> results = aggregateCellInfoList((List<CellInfo>) msg.obj);
+ mCellInfoList = new ArrayList<>(results);
+ if (DBG) logd("after aggregate: " + mCellInfoList.toString());
+ if (mCellInfoList != null && mCellInfoList.size() != 0) {
+ updateNetworkOperators();
+ } else {
+ addMessagePreference(R.string.empty_networks_list);
+ }
+
+ break;
+
+ case EVENT_NETWORK_SCAN_ERROR:
+ int error = msg.arg1;
+ if (DBG) logd("error while querying available networks " + error);
+ stopNetworkQuery();
+ addMessagePreference(R.string.network_query_error);
+ break;
+
+ case EVENT_NETWORK_SCAN_COMPLETED:
+ stopNetworkQuery();
+ if (DBG) logd("scan complete");
+ setProgressBarVisible(false);
+ if (mCellInfoList == null) {
+ // In case the scan timeout before getting any results
+ addMessagePreference(R.string.empty_networks_list);
+ }
+ break;
+ }
+ return;
+ }
+ };
+
+ private void loadNetworksList() {
+ if (DBG) logd("load networks list...");
+ setProgressBarVisible(true);
+ try {
+ if (mNetworkQueryService != null) {
+ if (DBG) logd("start network query");
+ mNetworkQueryService
+ .startNetworkQuery(mCallback, mPhoneId, true /* is incremental result */);
+ } else {
+ if (DBG) logd("unable to start network query, mNetworkQueryService is null");
+ addMessagePreference(R.string.network_query_error);
+ }
+ } catch (RemoteException e) {
+ loge("loadNetworksList: exception from startNetworkQuery " + e);
+ addMessagePreference(R.string.network_query_error);
+ }
+ }
+
+ /**
+ * This implementation of INetworkQueryServiceCallback is used to receive
+ * callback notifications from the network query service.
+ */
+ private final INetworkQueryServiceCallback mCallback = new INetworkQueryServiceCallback.Stub() {
+
+ /** Returns the scan results to the user, this callback will be called at lease one time. */
+ public void onResults(List<CellInfo> results) {
+ if (DBG) logd("get scan results.");
+ Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_RESULTS, results);
+ msg.sendToTarget();
+ }
+
+ /**
+ * Informs the user that the scan has stopped.
+ *
+ * This callback will be called when the scan is finished or cancelled by the user.
+ * The related NetworkScanRequest will be deleted after this callback.
+ */
+ public void onComplete() {
+ if (DBG) logd("network scan completed.");
+ Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_COMPLETED);
+ msg.sendToTarget();
+ }
+
+ /**
+ * Informs the user that there is some error about the scan.
+ *
+ * This callback will be called whenever there is any error about the scan, and the scan
+ * will be terminated. onComplete() will NOT be called.
+ */
+ public void onError(int error) {
+ if (DBG) logd("get onError callback with error code: " + error);
+ Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_ERROR, error, 0 /* arg2 */);
+ msg.sendToTarget();
+ }
+ };
+
+ /**
+ * Updates network operators from {@link INetworkQueryServiceCallback#onResults()}.
+ */
+ private void updateNetworkOperators() {
+ if (DBG) logd("updateNetworkOperators");
+ if (getActivity() != null) {
+ final View view = getView();
+ final Handler handler = view.getHandler();
+ if (handler != null && handler.hasCallbacks(mUpdateNetworkOperatorsRunnable)) {
+ return;
+ }
+ view.post(mUpdateNetworkOperatorsRunnable);
+ }
+ }
+
+ /**
+ * Update the currently available network operators list, which only contains the unregistered
+ * network operators. So if the device has no data and the network operator in the connected
+ * network operator category shows "Disconnected", it will also exist in the available network
+ * operator category for user to select. On the other hand, if the device has data and the
+ * network operator in the connected network operator category shows "Connected", it will not
+ * exist in the available network category.
+ */
+ private void updateNetworkOperatorsPreferenceCategory() {
+ mNetworkOperatorsPreferences.removeAll();
+
+ configConnectedNetworkOperatorsPreferenceCategory();
+ for (int index = 0; index < mCellInfoList.size(); index++) {
+ if (!mCellInfoList.get(index).isRegistered()) {
+ NetworkOperatorPreference pref =
+ new NetworkOperatorPreference(mCellInfoList.get(index), getContext());
+ pref.setKey(CellInfoUtil.getNetworkTitle(mCellInfoList.get(index)));
+ pref.setOrder(index);
+ mNetworkOperatorsPreferences.addPreference(pref);
+ }
+ }
+ }
+
+ /**
+ * Config the connected network operator preference when the page was created. When user get
+ * into this page, the device might or might not have data connection.
+ * - If the device has data:
+ * 1. use {@code ServiceState#getNetworkRegistrationStates()} to get the currently
+ * registered cellIdentity, wrap it into a CellInfo;
+ * 2. set the signal strength level as strong;
+ * 3. use {@link TelephonyManager#getNetworkOperatorName()} to get the title of the
+ * previously connected network operator, since the CellIdentity got from step 1 only has
+ * PLMN.
+ * - If the device has no data, we will remove the connected network operators list from the
+ * screen.
+ */
+ private void forceConfigConnectedNetworkOperatorsPreferenceCategory() {
+ if (DBG) logd("Force config ConnectedNetworkOperatorsPreferenceCategory");
+ if (mTelephonyManager.getDataState() == mTelephonyManager.DATA_CONNECTED) {
+ // Try to get the network registration states
+ ServiceState ss = mTelephonyManager.getServiceStateForSubscriber(mPhoneId);
+ List<NetworkRegistrationState> networkList =
+ ss.getNetworkRegistrationStates(AccessNetworkConstants.TransportType.WWAN);
+ if (networkList == null || networkList.size() == 0) {
+ loge("getNetworkRegistrationStates return null");
+ // Remove the connected network operators category
+ removeConnectedNetworkOperatorPreference();
+ return;
+ }
+ CellIdentity cellIdentity = networkList.get(0).getCellIdentity();
+ CellInfo cellInfo = CellInfoUtil.wrapCellInfoWithCellIdentity(cellIdentity);
+ if (cellInfo != null) {
+ if (DBG) logd("Currently registered cell: " + cellInfo.toString());
+ NetworkOperatorPreference pref =
+ new NetworkOperatorPreference(cellInfo, getContext());
+ pref.setTitle(mTelephonyManager.getNetworkOperatorName());
+ pref.setSummary(R.string.network_connected);
+ // Update the signal strength icon, since the default signalStrength value would be
+ // zero (it would be quite confusing why the connected network has no signal)
+ pref.setIcon(NetworkOperatorPreference.NUMBER_OF_LEVELS - 1);
+
+ mConnectedNetworkOperatorsPreference.addPreference(pref);
+ } else {
+ loge("Invalid CellIfno: " + cellInfo.toString());
+ // Remove the connected network operators category
+ removeConnectedNetworkOperatorPreference();
+ }
+ } else {
+ if (DBG) logd("No currently registered cell");
+ // Remove the connected network operators category
+ removeConnectedNetworkOperatorPreference();
+ }
+ }
+
+ /**
+ * Configure the ConnectedNetworkOperatorsPreferenceCategory. The category only need to be
+ * configured if the category is currently empty or the operator network title of the previous
+ * connected network is different from the new one.
+ */
+ private void configConnectedNetworkOperatorsPreferenceCategory() {
+ if (DBG) logd("config ConnectedNetworkOperatorsPreferenceCategory");
+ // Remove the category if the CellInfo list is empty or does not have registered cell.
+ if (mCellInfoList.size() == 0) {
+ if (DBG) logd("empty cellinfo list");
+ removeConnectedNetworkOperatorPreference();
+ }
+ CellInfo connectedNetworkOperator = null;
+ for (CellInfo cellInfo: mCellInfoList) {
+ if (cellInfo.isRegistered()) {
+ connectedNetworkOperator = cellInfo;
+ break;
+ }
+ }
+ if (connectedNetworkOperator == null) {
+ if (DBG) logd("no registered network");
+ removeConnectedNetworkOperatorPreference();
+ return;
+ }
+
+ // config the category if it is empty.
+ if (mConnectedNetworkOperatorsPreference.getPreferenceCount() == 0) {
+ if (DBG) logd("ConnectedNetworkSelectList is empty, add one");
+ addConnectedNetworkOperatorPreference(connectedNetworkOperator);
+ return;
+ }
+ NetworkOperatorPreference previousConnectedNetworkOperator = (NetworkOperatorPreference)
+ (mConnectedNetworkOperatorsPreference.getPreference(0));
+
+ // config the category if the network title of the previous connected network is different
+ // from the new one.
+ String cTitle = CellInfoUtil.getNetworkTitle(connectedNetworkOperator);
+ String pTitle = CellInfoUtil.getNetworkTitle(
+ previousConnectedNetworkOperator.getCellInfo());
+ if (!cTitle.equals(pTitle)) {
+ if (DBG) logd("reconfig the category: connected network changed");
+ addConnectedNetworkOperatorPreference(connectedNetworkOperator);
+ return;
+ }
+ if (DBG) logd("same network operator is connected, only refresh the connected network");
+ // Otherwise same network operator is connected, only refresh the connected network
+ // operator preference (first and the only one in this category).
+ ((NetworkOperatorPreference) mConnectedNetworkOperatorsPreference.getPreference(0))
+ .refresh();
+ return;
+ }
+
+ /**
+ * Creates a Preference for the given {@link CellInfo} and adds it to the
+ * {@link #mConnectedNetworkOperatorsPreference}.
+ */
+ private void addConnectedNetworkOperatorPreference(CellInfo cellInfo) {
+ if (DBG) logd("addConnectedNetworkOperatorPreference");
+ // Remove the current ConnectedNetworkOperatorsPreference
+ removeConnectedNetworkOperatorPreference();
+ final NetworkOperatorPreference pref =
+ new NetworkOperatorPreference(cellInfo, getContext());
+ pref.setSummary(R.string.network_connected);
+ mConnectedNetworkOperatorsPreference.addPreference(pref);
+ PreferenceScreen preferenceScreen = getPreferenceScreen();
+ preferenceScreen.addPreference(mConnectedNetworkOperatorsPreference);
+ }
+
+ /** Removes all preferences and hide the {@link #mConnectedNetworkOperatorsPreference}. */
+ private void removeConnectedNetworkOperatorPreference() {
+ mConnectedNetworkOperatorsPreference.removeAll();
+ PreferenceScreen preferenceScreen = getPreferenceScreen();
+ preferenceScreen.removePreference(mConnectedNetworkOperatorsPreference);
+ }
+
+ protected void setProgressBarVisible(boolean visible) {
+ if (mProgressHeader != null) {
+ mProgressHeader.setVisibility(visible ? View.VISIBLE : View.GONE);
+ }
+ }
+
+ private void addMessagePreference(int messageId) {
+ if (DBG) logd("remove callback");
+ getView().removeCallbacks(mUpdateNetworkOperatorsRunnable);
+ setProgressBarVisible(false);
+ if (DBG) logd("addMessagePreference");
+ mStatusMessagePreference.setTitle(messageId);
+ removeConnectedNetworkOperatorPreference();
+ mNetworkOperatorsPreferences.removeAll();
+ mNetworkOperatorsPreferences.addPreference(mStatusMessagePreference);
+ }
+
+ /**
+ * The Scan results may contains several cell infos with different radio technologies and signal
+ * strength for one network operator. Aggregate the CellInfoList by retaining only the cell info
+ * with the strongest signal strength.
+ */
+ private List<CellInfo> aggregateCellInfoList(List<CellInfo> cellInfoList) {
+ if (DBG) logd("before aggregate: " + cellInfoList.toString());
+ Map<String, CellInfo> map = new HashMap<>();
+ for (CellInfo cellInfo: cellInfoList) {
+ String networkTitle = CellInfoUtil.getNetworkTitle(cellInfo);
+ if (cellInfo.isRegistered() || !map.containsKey(networkTitle)) {
+ map.put(networkTitle, cellInfo);
+ } else {
+ if (map.get(networkTitle).isRegistered()
+ || CellInfoUtil.getLevel(map.get(networkTitle))
+ > CellInfoUtil.getLevel(cellInfo)) {
+ // Skip if the stored cellInfo is registered or has higher signal strength level
+ continue;
+ }
+ // Otherwise replace it with the new CellInfo
+ map.put(networkTitle, cellInfo);
+ }
+ }
+ return new ArrayList<>(map.values());
+ }
+
+ /**
+ * Service connection code for the NetworkQueryService.
+ * Handles the work of binding to a local object so that we can make
+ * the appropriate service calls.
+ */
+
+ /** Local service interface */
+ private INetworkQueryService mNetworkQueryService = null;
+ /** Flag indicating whether we have called bind on the service. */
+ boolean mShouldUnbind;
+
+ /** Service connection */
+ private final ServiceConnection mNetworkQueryServiceConnection = new ServiceConnection() {
+
+ /** Handle the task of binding the local object to the service */
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ if (DBG) logd("connection created, binding local service.");
+ mNetworkQueryService = ((NetworkQueryService.LocalBinder) service).getService();
+ // Load the network list only when the service is well connected.
+ loadNetworksList();
+ }
+
+ /** Handle the task of cleaning up the local binding */
+ public void onServiceDisconnected(ComponentName className) {
+ if (DBG) logd("connection disconnected, cleaning local binding.");
+ mNetworkQueryService = null;
+ }
+ };
+
+ private void bindNetworkQueryService() {
+ if (DBG) logd("bindNetworkQueryService");
+ getContext().bindService(new Intent(getContext(), NetworkQueryService.class).setAction(
+ NetworkQueryService.ACTION_LOCAL_BINDER),
+ mNetworkQueryServiceConnection, Context.BIND_AUTO_CREATE);
+ mShouldUnbind = true;
+ }
+
+ private void unbindNetworkQueryService() {
+ if (DBG) logd("unbindNetworkQueryService");
+ if (mShouldUnbind) {
+ if (DBG) logd("mShouldUnbind is true");
+ // unbind the service.
+ getContext().unbindService(mNetworkQueryServiceConnection);
+ mShouldUnbind = false;
+ }
+ }
+
+ private void stopNetworkQuery() {
+ // Stop the network query process
+ try {
+ if (mNetworkQueryService != null) {
+ if (DBG) logd("Stop network query");
+ mNetworkQueryService.stopNetworkQuery();
+ mNetworkQueryService.unregisterCallback(mCallback);
+ }
+ } catch (RemoteException e) {
+ loge("Exception from stopNetworkQuery " + e);
+ }
+ }
+
+ private void logd(String msg) {
+ Log.d(TAG, msg);
+ }
+
+ private void loge(String msg) {
+ Log.e(TAG, msg);
+ }
+}
diff --git a/src/com/android/phone/NetworkSelectSettingActivity.java b/src/com/android/phone/NetworkSelectSettingActivity.java
new file mode 100644
index 0000000..2f730ad
--- /dev/null
+++ b/src/com/android/phone/NetworkSelectSettingActivity.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 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 com.android.phone;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+
+/**
+ * Activity associated with NetworkSelectSetting fragment
+ */
+public class NetworkSelectSettingActivity extends Activity {
+ private static final String TAG = "NetworkSelectSettingActivity";
+ public static final String KEY_PHONE_ID = "phone_id";
+
+ /**
+ * Returns the Android Intent that led to this Activity being created.
+ */
+ public static Intent getIntent(Context context, int phoneId) {
+ Intent intent = new Intent(context, NetworkSelectSettingActivity.class);
+ intent.putExtra(KEY_PHONE_ID, phoneId);
+ return intent;
+ }
+
+ @Override
+ public void onCreate(Bundle savedState) {
+ Log.d(TAG, "onCreate()");
+ super.onCreate(savedState);
+ int phoneId = getIntent().getExtras().getInt(KEY_PHONE_ID);
+ setContentView(R.layout.choose_network);
+
+ FragmentManager fragmentManager = getFragmentManager();
+ Fragment fragment = fragmentManager.findFragmentById(R.id.choose_network_content);
+ if (fragment == null) {
+ fragmentManager.beginTransaction()
+ .add(R.id.choose_network_content,
+ NetworkSelectSetting.newInstance(phoneId), TAG)
+ .commit();
+ }
+ }
+}
diff --git a/src/com/android/services/telephony/ImsConferenceController.java b/src/com/android/services/telephony/ImsConferenceController.java
index 4bae058..971dd7b 100644
--- a/src/com/android/services/telephony/ImsConferenceController.java
+++ b/src/com/android/services/telephony/ImsConferenceController.java
@@ -389,5 +389,8 @@
android.telephony.DisconnectCause.IMS_MERGED_SUCCESSFULLY)));
connection.destroy();
mImsConferences.add(conference);
+ // If one of the participants failed to join the conference, recalculate will set the
+ // conferenceable connections for the conference to show merge calls option.
+ recalculateConferenceable();
}
}
diff --git a/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/EmbmsSampleDownloadService.java b/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/EmbmsSampleDownloadService.java
index 7bd0f70..6ddebb8 100644
--- a/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/EmbmsSampleDownloadService.java
+++ b/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/EmbmsSampleDownloadService.java
@@ -31,8 +31,9 @@
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.telephony.MbmsDownloadSession;
+import android.telephony.mbms.DownloadProgressListener;
import android.telephony.mbms.DownloadRequest;
-import android.telephony.mbms.DownloadStateCallback;
+import android.telephony.mbms.DownloadStatusListener;
import android.telephony.mbms.FileInfo;
import android.telephony.mbms.FileServiceInfo;
import android.telephony.mbms.MbmsDownloadSessionCallback;
@@ -143,9 +144,16 @@
}
@Override
- public int registerStateCallback(DownloadRequest downloadRequest,
- DownloadStateCallback callback) throws RemoteException {
- mDownloadStateCallbacks.put(downloadRequest, callback);
+ public int addStatusListener(DownloadRequest downloadRequest,
+ DownloadStatusListener callback) throws RemoteException {
+ mDownloadStatusCallbacks.put(downloadRequest, callback);
+ return MbmsErrors.SUCCESS;
+ }
+
+ @Override
+ public int addProgressListener(DownloadRequest downloadRequest,
+ DownloadProgressListener callback) throws RemoteException {
+ mDownloadProgressCallbacks.put(downloadRequest, callback);
return MbmsErrors.SUCCESS;
}
@@ -183,7 +191,9 @@
// A map of app-identifiers to (maps of service-ids to sets of temp file uris in use)
private final Map<FrontendAppIdentifier, Map<String, Set<Uri>>> mTempFilesInUse =
new ConcurrentHashMap<>();
- private final Map<DownloadRequest, DownloadStateCallback> mDownloadStateCallbacks =
+ private final Map<DownloadRequest, DownloadStatusListener> mDownloadStatusCallbacks =
+ new ConcurrentHashMap<>();
+ private final Map<DownloadRequest, DownloadProgressListener> mDownloadProgressCallbacks =
new ConcurrentHashMap<>();
private HandlerThread mHandlerThread;
@@ -334,13 +344,14 @@
UriPathPair tempFile, FileInfo fileToDownload) {
int result = MbmsDownloadSession.RESULT_SUCCESSFUL;
// Test Callback
- DownloadStateCallback c = mDownloadStateCallbacks.get(request);
- if (c != null) {
- c.onProgressUpdated(request, fileToDownload, 0, 10, 0, 10);
+ DownloadStatusListener statusListener = mDownloadStatusCallbacks.get(request);
+ DownloadProgressListener progressListener = mDownloadProgressCallbacks.get(request);
+ if (progressListener != null) {
+ progressListener.onProgressUpdated(request, fileToDownload, 0, 10, 0, 10);
}
// Test Callback
- if (c != null) {
- c.onStateUpdated(request, fileToDownload,
+ if (statusListener != null) {
+ statusListener.onStatusUpdated(request, fileToDownload,
MbmsDownloadSession.STATUS_ACTIVELY_DOWNLOADING);
}
try {
@@ -367,8 +378,8 @@
result = MbmsDownloadSession.RESULT_CANCELLED;
}
// Test Callback
- if (c != null) {
- c.onProgressUpdated(request, fileToDownload, 10, 10, 10, 10);
+ if (progressListener != null) {
+ progressListener.onProgressUpdated(request, fileToDownload, 10, 10, 10, 10);
}
// Take a round-trip through the download request serialization to exercise it
DownloadRequest request1 = DownloadRequest.Builder.fromSerializedRequest(
diff --git a/testapps/EmbmsTestDownloadApp/res/layout/activity_main.xml b/testapps/EmbmsTestDownloadApp/res/layout/activity_main.xml
index 1aea6a5..8bbc806 100644
--- a/testapps/EmbmsTestDownloadApp/res/layout/activity_main.xml
+++ b/testapps/EmbmsTestDownloadApp/res/layout/activity_main.xml
@@ -51,85 +51,93 @@
android:layout_column="0"
android:text="@string/bind_button" />
<Button
- android:id="@+id/set_temp_root_button"
+ android:id="@+id/set_temp_root_button_internal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_row="0"
android:layout_column="1"
- android:text="@string/set_temp_root_button" />
+ android:text="@string/set_temp_root_button_internal" />
+ <Button
+ android:id="@+id/set_temp_root_button_external"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_row="1"
+ android:layout_column="0"
+ android:text="@string/set_temp_root_button_external" />
<Button
android:id="@+id/get_file_services_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_row="1"
- android:layout_column="0"
+ android:layout_column="1"
android:text="@string/get_file_services_button" />
<Button
android:id="@+id/request_dl_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_row="1"
- android:layout_column="1"
+ android:layout_row="2"
+ android:layout_column="0"
android:text="@string/request_dl_button" />
<Button
android:id="@+id/request_cleanup_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_row="2"
- android:layout_column="0"
+ android:layout_column="1"
android:text="@string/request_cleanup_button" />
<Button
android:id="@+id/request_spurious_temp_files_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_row="2"
- android:layout_column="1"
+ android:layout_row="3"
+ android:layout_column="0"
android:text="@string/request_spurious_temp_files_button" />
<Button
android:id="@+id/delay_download_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_row="3"
+ android:layout_row="4"
android:layout_column="0"
android:text="@string/delay_download_button" />
- <NumberPicker
+ <EditText
android:id="@+id/delay_factor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_row="3"
+ android:inputType="number"
+ android:layout_row="4"
android:layout_column="1"/>
<Button
android:id="@+id/cancel_download_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_row="4"
+ android:layout_row="5"
android:layout_column="0"
android:text="@string/cancel_download_button" />
<Button
+ android:id="@+id/register_all_callback_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_row="5"
+ android:layout_column="1"
+ android:text="@string/register_all_callback_button" />
+ <Button
android:id="@+id/register_state_callback_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_row="5"
+ android:layout_row="6"
android:layout_column="0"
android:text="@string/register_state_callback_button" />
<Button
android:id="@+id/register_progress_callback_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_row="5"
+ android:layout_row="6"
android:layout_column="1"
android:text="@string/register_progress_callback_button" />
- <Button
- android:id="@+id/register_all_callback_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_row="6"
- android:layout_column="0"
- android:text="@string/register_all_callback_button" />
<Spinner
android:id="@+id/active_downloads"
- android:layout_row="7"
+ android:layout_row="8"
android:layout_column="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
diff --git a/testapps/EmbmsTestDownloadApp/res/values/donottranslate_strings.xml b/testapps/EmbmsTestDownloadApp/res/values/donottranslate_strings.xml
index b032b26..d1ee79e 100644
--- a/testapps/EmbmsTestDownloadApp/res/values/donottranslate_strings.xml
+++ b/testapps/EmbmsTestDownloadApp/res/values/donottranslate_strings.xml
@@ -19,7 +19,8 @@
<string name="bind_button">Bind</string>
<string name="request_dl_button">Request DL</string>
<string name="get_file_services_button">Fetch file services</string>
- <string name="set_temp_root_button">Set temp file root</string>
+ <string name="set_temp_root_button_internal">Set tmp root (int)</string>
+ <string name="set_temp_root_button_external">Set tmp root (ext)</string>
<string name="request_cleanup_button">Cleanup</string>
<string name="request_spurious_temp_files_button">Request more temp files</string>
<string name="delay_download_button">Delay download</string>
diff --git a/testapps/EmbmsTestDownloadApp/src/com/android/phone/testapps/embmsdownload/EmbmsTestDownloadApp.java b/testapps/EmbmsTestDownloadApp/src/com/android/phone/testapps/embmsdownload/EmbmsTestDownloadApp.java
index 76baf05..acdbdcb 100644
--- a/testapps/EmbmsTestDownloadApp/src/com/android/phone/testapps/embmsdownload/EmbmsTestDownloadApp.java
+++ b/testapps/EmbmsTestDownloadApp/src/com/android/phone/testapps/embmsdownload/EmbmsTestDownloadApp.java
@@ -27,8 +27,9 @@
import android.support.v7.widget.RecyclerView;
import android.telephony.MbmsDownloadSession;
import android.telephony.SubscriptionManager;
+import android.telephony.mbms.DownloadProgressListener;
import android.telephony.mbms.DownloadRequest;
-import android.telephony.mbms.DownloadStateCallback;
+import android.telephony.mbms.DownloadStatusListener;
import android.telephony.mbms.FileInfo;
import android.telephony.mbms.FileServiceInfo;
import android.telephony.mbms.MbmsDownloadSessionCallback;
@@ -37,8 +38,8 @@
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
+import android.widget.EditText;
import android.widget.ImageView;
-import android.widget.NumberPicker;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
@@ -183,6 +184,7 @@
private FileServiceInfoAdapter mFileServiceInfoAdapter;
private DownloadRequestAdapter mDownloadRequestAdapter;
private ImageAdapter mImageAdapter;
+ private boolean mIsTempDirExternal = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -207,12 +209,26 @@
mDownloadManager = MbmsDownloadSession.create(this, mHandler::post, mCallback);
});
- Button setTempFileRootButton = (Button) findViewById(R.id.set_temp_root_button);
- setTempFileRootButton.setOnClickListener((view) -> {
+ Button setTempFileRootButtonExternal =
+ (Button) findViewById(R.id.set_temp_root_button_external);
+ setTempFileRootButtonExternal.setOnClickListener((view) -> {
+ File downloadDir = new File(EmbmsTestDownloadApp.this.getExternalFilesDir(null),
+ CUSTOM_EMBMS_TEMP_FILE_LOCATION);
+ downloadDir.mkdirs();
+ mDownloadManager.setTempFileRootDirectory(downloadDir);
+ mIsTempDirExternal = true;
+ Toast.makeText(EmbmsTestDownloadApp.this,
+ "temp file root set to " + downloadDir, Toast.LENGTH_SHORT).show();
+ });
+
+ Button setTempFileRootButtonInternal =
+ (Button) findViewById(R.id.set_temp_root_button_internal);
+ setTempFileRootButtonInternal.setOnClickListener((view) -> {
File downloadDir = new File(EmbmsTestDownloadApp.this.getFilesDir(),
CUSTOM_EMBMS_TEMP_FILE_LOCATION);
downloadDir.mkdirs();
mDownloadManager.setTempFileRootDirectory(downloadDir);
+ mIsTempDirExternal = false;
Toast.makeText(EmbmsTestDownloadApp.this,
"temp file root set to " + downloadDir, Toast.LENGTH_SHORT).show();
});
@@ -253,14 +269,13 @@
SideChannel.requestSpuriousTempFiles(EmbmsTestDownloadApp.this,
(FileServiceInfo) serviceSelector.getSelectedItem()));
- NumberPicker downloadDelayPicker = (NumberPicker) findViewById(R.id.delay_factor);
- downloadDelayPicker.setMinValue(1);
- downloadDelayPicker.setMaxValue(50);
+ EditText downloadDelay = findViewById(R.id.delay_factor);
+ downloadDelay.setText(String.valueOf(5));
Button delayDownloadButton = (Button) findViewById(R.id.delay_download_button);
delayDownloadButton.setOnClickListener((view) ->
SideChannel.delayDownloads(EmbmsTestDownloadApp.this,
- downloadDelayPicker.getValue()));
+ Integer.valueOf(downloadDelay.getText().toString())));
final Spinner downloadRequestSpinner = (Spinner) findViewById(R.id.active_downloads);
downloadRequestSpinner.setAdapter(mDownloadRequestAdapter);
@@ -292,8 +307,8 @@
"No DownloadRequest Pending for progress...", Toast.LENGTH_SHORT).show();
return;
}
- mDownloadManager.registerStateCallback(req, sInstance.getMainThreadHandler()::post,
- new DownloadStateCallback(DownloadStateCallback.PROGRESS_UPDATES) {
+ mDownloadManager.addProgressListener(req, sInstance.getMainThreadHandler()::post,
+ new DownloadProgressListener() {
@Override
public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
int currentDownloadSize, int fullDownloadSize,
@@ -303,16 +318,6 @@
+ " fd: " + fullDownloadSize, Toast.LENGTH_SHORT)
.show();
}
-
- @Override
- public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
- @MbmsDownloadSession.DownloadStatus int state) {
- // only registered for state callback, this shouldn't happen!
- Toast.makeText(EmbmsTestDownloadApp.this,
- "State ERROR: received state update for callback that didn't"
- + " filter it",
- Toast.LENGTH_SHORT).show();
- }
});
});
@@ -330,21 +335,10 @@
"No DownloadRequest Pending for state...", Toast.LENGTH_SHORT).show();
return;
}
- mDownloadManager.registerStateCallback(req, sInstance.getMainThreadHandler()::post,
- new DownloadStateCallback(DownloadStateCallback.STATE_UPDATES) {
+ mDownloadManager.addStatusListener(req, sInstance.getMainThreadHandler()::post,
+ new DownloadStatusListener() {
@Override
- public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
- int currentDownloadSize, int fullDownloadSize,
- int currentDecodedSize, int fullDecodedSize) {
- // only registered for state callback, this shouldn't happen!
- Toast.makeText(EmbmsTestDownloadApp.this,
- "Progress ERROR: received progress update for"
- + " callback that didn't "
- + "filter it", Toast.LENGTH_SHORT).show();
- }
-
- @Override
- public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
+ public void onStatusUpdated(DownloadRequest request, FileInfo fileInfo,
@MbmsDownloadSession.DownloadStatus int state) {
Toast.makeText(EmbmsTestDownloadApp.this,
"State Updated (" + fileInfo + ") state: " + state,
@@ -367,8 +361,20 @@
"No DownloadRequest Pending for state...", Toast.LENGTH_SHORT).show();
return;
}
- mDownloadManager.registerStateCallback(req, sInstance.getMainThreadHandler()::post,
- new DownloadStateCallback() {
+
+ mDownloadManager.addStatusListener(req, sInstance.getMainThreadHandler()::post,
+ new DownloadStatusListener() {
+ @Override
+ public void onStatusUpdated(DownloadRequest request, FileInfo fileInfo,
+ @MbmsDownloadSession.DownloadStatus int state) {
+ Toast.makeText(EmbmsTestDownloadApp.this,
+ "State Updated (" + fileInfo + ") state: " + state,
+ Toast.LENGTH_SHORT).show();
+ }
+ });
+
+ mDownloadManager.addProgressListener(req, sInstance.getMainThreadHandler()::post,
+ new DownloadProgressListener() {
@Override
public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
int currentDownloadSize, int fullDownloadSize,
@@ -378,14 +384,6 @@
+ " fd: " + fullDownloadSize, Toast.LENGTH_SHORT)
.show();
}
-
- @Override
- public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
- @MbmsDownloadSession.DownloadStatus int state) {
- Toast.makeText(EmbmsTestDownloadApp.this,
- "State Updated (" + fileInfo + ") state: " + state,
- Toast.LENGTH_SHORT).show();
- }
});
});
}
@@ -448,14 +446,15 @@
private Uri getDestination(String serviceId) {
File dest;
+ File baseDir = mIsTempDirExternal ? getExternalFilesDir(null) : getFilesDir();
try {
if (serviceId.contains("2")) {
- dest = new File(getFilesDir().getCanonicalFile(), "images/animals/");
+ dest = new File(baseDir.getCanonicalFile(), "images/animals/");
if (!dest.exists()) {
dest.mkdirs();
}
} else {
- dest = new File(getFilesDir().getCanonicalFile(), "images/");
+ dest = new File(baseDir.getCanonicalFile(), "images/");
if (!dest.exists()) {
dest.mkdirs();
}
diff --git a/tests/src/com/android/phone/CallFeaturesSettingTest.java b/tests/src/com/android/phone/CallFeaturesSettingTest.java
new file mode 100644
index 0000000..816642a
--- /dev/null
+++ b/tests/src/com/android/phone/CallFeaturesSettingTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 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 com.android.phone;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.mockito.Mockito.when;
+
+import android.support.test.filters.FlakyTest;
+import android.support.test.rule.ActivityTestRule;
+
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.lang.reflect.Field;
+
+public class CallFeaturesSettingTest {
+ @Mock
+ Phone mMockPhone;
+ @Mock
+ IccCard mMockIccCard;
+ @Rule
+ public ActivityTestRule<CallFeaturesSetting> mRule =
+ new ActivityTestRule<>(CallFeaturesSetting.class);
+ private CallFeaturesSetting mActivity;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mActivity = mRule.getActivity();
+ }
+
+ @FlakyTest
+ @Test
+ public void onResume_fdnIsAvailable_shouldShowFdnMenu() throws NoSuchFieldException,
+ IllegalAccessException {
+ when(mMockPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_GSM);
+ when(mMockPhone.getIccCard()).thenReturn(mMockIccCard);
+ when(mMockIccCard.getIccFdnAvailable()).thenReturn(true);
+ getField("mPhone").set(mActivity, mMockPhone);
+
+ mActivity.onResume();
+
+ // Check the FDN menu is displayed.
+ onView(withText(R.string.fdn)).check(matches(isDisplayed()));
+ }
+
+ @FlakyTest
+ @Test
+ public void onResume_iccCardIsNull_shouldNotShowFdnMenu() throws NoSuchFieldException,
+ IllegalAccessException {
+ when(mMockPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_GSM);
+ when(mMockPhone.getIccCard()).thenReturn(null);
+ getField("mPhone").set(mActivity, mMockPhone);
+
+ mActivity.onResume();
+
+ // Check the FDN menu is not displayed.
+ onView(withText(R.string.fdn)).check(doesNotExist());
+ }
+
+ @FlakyTest
+ @Test
+ public void onResume_fdnIsNotAvailable_shouldNotShowFdnMenu() throws NoSuchFieldException,
+ IllegalAccessException {
+ when(mMockPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_GSM);
+ when(mMockPhone.getIccCard()).thenReturn(mMockIccCard);
+ when(mMockIccCard.getIccFdnAvailable()).thenReturn(false);
+ getField("mPhone").set(mActivity, mMockPhone);
+
+ mActivity.onResume();
+
+ // Check the FDN menu is not displayed.
+ onView(withText(R.string.fdn)).check(doesNotExist());
+ }
+
+ private Field getField(String fieldName) throws NoSuchFieldException {
+ Field field = mActivity.getClass().getDeclaredField(fieldName);
+ field.setAccessible(true);
+ return field;
+ }
+}