diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 0a5f24ba1be..7bf376a047c 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -94,6 +94,7 @@
+
-
-
-
-
@@ -1486,7 +1483,6 @@
@@ -1576,9 +1572,17 @@
android:windowSoftInputMode="stateHidden|adjustResize"
android:theme="@style/GlifTheme.Light"/>
-
-
-
+
+
+
+
+
@@ -3110,6 +3114,10 @@
android:label="@string/connected_device_connections_title"
android:taskAffinity="com.android.settings"
android:parentActivityName="Settings$ConnectedDeviceDashboardActivity">
+
+
+
+
@@ -3173,6 +3181,12 @@
+
+
diff --git a/res/layout/battery_history_chart.xml b/res/layout/battery_history_chart.xml
deleted file mode 100644
index c0c37f12951..00000000000
--- a/res/layout/battery_history_chart.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/res/layout/dashboard.xml b/res/layout/dashboard.xml
index 536c00a7075..ccb50ae10ed 100644
--- a/res/layout/dashboard.xml
+++ b/res/layout/dashboard.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
-
-
-
-
+
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/values/config.xml b/res/values/config.xml
index 2920ac839d4..f5105232035 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -106,9 +106,6 @@
-->
-
- true
-
true
@@ -133,4 +130,7 @@
devices will be able to vary their amplitude but do not possess enough dynamic range to
have distinct intensity levels -->
false
+
+
+ true
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 234172827ba..024d212b9e0 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -33,8 +33,6 @@
16dip
8dip
- 120dp
-
228dip
440dip
36sp
diff --git a/res/values/themes.xml b/res/values/themes.xml
index 63944d9bb34..43a9b78f777 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -161,7 +161,7 @@
- #00000000
-
@@ -170,6 +170,7 @@
- @*android:color/primary_device_default_settings_light
- @*android:color/primary_dark_device_default_settings_light
- @*android:color/accent_device_default_light
+ - @style/PreferenceTheme
diff --git a/res/xml/default_autofill_picker_settings.xml b/res/xml/default_autofill_picker_settings.xml
new file mode 100644
index 00000000000..26dff7eca87
--- /dev/null
+++ b/res/xml/default_autofill_picker_settings.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/xml/language_and_input.xml b/res/xml/language_and_input.xml
index ec15f0d5268..50c6a824579 100644
--- a/res/xml/language_and_input.xml
+++ b/res/xml/language_and_input.xml
@@ -54,10 +54,10 @@
android:persistent="false"
android:fragment="com.android.settings.inputmethod.SpellCheckersSettings" />
-
diff --git a/res/xml/top_level_settings.xml b/res/xml/top_level_settings.xml
new file mode 100644
index 00000000000..6c91aeae70b
--- /dev/null
+++ b/res/xml/top_level_settings.xml
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 4f011c19707..94de8da6e38 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -121,7 +121,6 @@ public class Settings extends SettingsActivity {
}
public static class DirectoryAccessSettingsActivity extends SettingsActivity { /* empty */ }
- public static class TopLevelSettings extends SettingsActivity { /* empty */ }
public static class ApnSettingsActivity extends SettingsActivity { /* empty */ }
public static class WifiCallingSettingsActivity extends SettingsActivity { /* empty */ }
public static class MemorySettingsActivity extends SettingsActivity { /* empty */ }
diff --git a/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java b/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java
index b946e3ef532..b9713d7e8b0 100644
--- a/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java
+++ b/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceController.java
@@ -39,7 +39,6 @@ import com.android.settings.bluetooth.BluetoothDeviceDetailsFragment;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause;
@@ -82,6 +81,7 @@ public class AccessibilityHearingAidPreferenceController extends BasePreferenceC
};
private final LocalBluetoothManager mLocalBluetoothManager;
+ private final BluetoothAdapter mBluetoothAdapter;
//cache value of supporting hearing aid or not
private boolean mHearingAidProfileSupported;
private FragmentManager mFragmentManager;
@@ -89,6 +89,7 @@ public class AccessibilityHearingAidPreferenceController extends BasePreferenceC
public AccessibilityHearingAidPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
mLocalBluetoothManager = getLocalBluetoothManager();
+ mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mHearingAidProfileSupported = isHearingAidProfileSupported();
}
@@ -151,8 +152,7 @@ public class AccessibilityHearingAidPreferenceController extends BasePreferenceC
if (!mHearingAidProfileSupported) {
return null;
}
- final LocalBluetoothAdapter localAdapter = mLocalBluetoothManager.getBluetoothAdapter();
- if (!localAdapter.isEnabled()) {
+ if (!mBluetoothAdapter.isEnabled()) {
return null;
}
final List deviceList = mLocalBluetoothManager.getProfileManager()
@@ -166,8 +166,7 @@ public class AccessibilityHearingAidPreferenceController extends BasePreferenceC
}
private boolean isHearingAidProfileSupported() {
- final LocalBluetoothAdapter localAdapter = mLocalBluetoothManager.getBluetoothAdapter();
- final List supportedList = localAdapter.getSupportedProfiles();
+ final List supportedList = mBluetoothAdapter.getSupportedProfiles();
if (supportedList.contains(BluetoothProfile.HEARING_AID)) {
return true;
}
diff --git a/src/com/android/settings/accounts/TopLevelAccountEntryPreferenceController.java b/src/com/android/settings/accounts/TopLevelAccountEntryPreferenceController.java
new file mode 100644
index 00000000000..a8d93d589d8
--- /dev/null
+++ b/src/com/android/settings/accounts/TopLevelAccountEntryPreferenceController.java
@@ -0,0 +1,68 @@
+/*
+ * 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.settings.accounts;
+
+import android.content.Context;
+import android.icu.text.ListFormatter;
+import android.os.UserHandle;
+import android.text.BidiFormatter;
+import android.text.TextUtils;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.accounts.AuthenticatorHelper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TopLevelAccountEntryPreferenceController extends BasePreferenceController {
+ public TopLevelAccountEntryPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE_UNSEARCHABLE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ final AuthenticatorHelper authHelper = new AuthenticatorHelper(mContext,
+ UserHandle.of(UserHandle.myUserId()), null /* OnAccountsUpdateListener */);
+ final String[] types = authHelper.getEnabledAccountTypes();
+ final BidiFormatter bidiFormatter = BidiFormatter.getInstance();
+ final List summaries = new ArrayList<>();
+
+ if (types == null || types.length == 0) {
+ summaries.add(mContext.getString(R.string.account_dashboard_default_summary));
+ } else {
+ // Show up to 3 account types, ignore any null value
+ int accountToAdd = Math.min(3, types.length);
+
+ for (int i = 0; i < types.length && accountToAdd > 0; i++) {
+ final CharSequence label = authHelper.getLabelForType(mContext, types[i]);
+ if (TextUtils.isEmpty(label)) {
+ continue;
+ }
+
+ summaries.add(bidiFormatter.unicodeWrap(label));
+ accountToAdd--;
+ }
+ }
+ return ListFormatter.getInstance().format(summaries);
+ }
+}
diff --git a/src/com/android/settings/applications/autofill/AutofillPickerTrampolineActivity.java b/src/com/android/settings/applications/autofill/AutofillPickerTrampolineActivity.java
index 1db3c82804f..9500fd5ae34 100644
--- a/src/com/android/settings/applications/autofill/AutofillPickerTrampolineActivity.java
+++ b/src/com/android/settings/applications/autofill/AutofillPickerTrampolineActivity.java
@@ -16,6 +16,7 @@ package com.android.settings.applications.autofill;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
+import android.os.UserHandle;
import android.view.autofill.AutofillManager;
import com.android.settings.applications.defaultapps.DefaultAutofillPicker;
@@ -36,7 +37,8 @@ public class AutofillPickerTrampolineActivity extends Activity {
// First check if the current user's service already belongs to the app...
final Intent intent = getIntent();
final String packageName = intent.getData().getSchemeSpecificPart();
- final String currentService = DefaultAutofillPicker.getDefaultKey(this);
+ final String currentService = DefaultAutofillPicker.getDefaultKey(
+ this, UserHandle.myUserId());
if (currentService != null && currentService.startsWith(packageName)) {
// ...and succeed right away if it does.
setResult(RESULT_OK);
diff --git a/src/com/android/settings/applications/defaultapps/AutofillPicker.java b/src/com/android/settings/applications/defaultapps/AutofillPicker.java
new file mode 100644
index 00000000000..3f392e59a08
--- /dev/null
+++ b/src/com/android/settings/applications/defaultapps/AutofillPicker.java
@@ -0,0 +1,84 @@
+/*
+ * 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.settings.applications.defaultapps;
+
+import android.content.Context;
+import android.provider.SearchIndexableResource;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.applications.defaultapps.DefaultAutofillPreferenceController;
+import com.android.settings.applications.defaultapps.DefaultWorkAutofillPreferenceController;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.search.Indexable;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.search.SearchIndexable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
+public class AutofillPicker extends DashboardFragment {
+ private static final String TAG = "AutofillPicker";
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsProto.MetricsEvent.DEFAULT_AUTOFILL_PICKER;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.default_autofill_picker_settings;
+ }
+
+ @Override
+ protected List createPreferenceControllers(Context context) {
+ return buildPreferenceControllers(context);
+ }
+
+ public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider() {
+ @Override
+ public List getXmlResourcesToIndex(Context context,
+ boolean enabled) {
+ SearchIndexableResource searchIndexableResource =
+ new SearchIndexableResource(context);
+ searchIndexableResource.xmlResId = R.xml.default_autofill_picker_settings;
+ return Arrays.asList(searchIndexableResource);
+ }
+
+ @Override
+ public List getPreferenceControllers(Context
+ context) {
+ return buildPreferenceControllers(context);
+ }
+ };
+
+ private static List buildPreferenceControllers(Context context) {
+ return Arrays.asList(
+ new DefaultAutofillPreferenceController(context),
+ new DefaultWorkAutofillPreferenceController(context));
+ }
+}
diff --git a/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceController.java b/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceController.java
index 6016dbc118f..78248e67c93 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceController.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceController.java
@@ -82,12 +82,16 @@ public abstract class DefaultAppPreferenceController extends AbstractPreferenceC
final Intent settingIntent = getSettingIntent(app);
if (settingIntent != null) {
((GearPreference) preference).setOnGearClickListener(
- p -> mContext.startActivity(settingIntent));
+ p -> startActivity(settingIntent));
} else {
((GearPreference) preference).setOnGearClickListener(null);
}
}
+ protected void startActivity(Intent intent) {
+ mContext.startActivity(intent);
+ }
+
protected abstract DefaultAppInfo getDefaultAppInfo();
/**
diff --git a/src/com/android/settings/applications/defaultapps/DefaultAutofillPicker.java b/src/com/android/settings/applications/defaultapps/DefaultAutofillPicker.java
index 9bb82d4fff0..1705dc578a6 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultAutofillPicker.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultAutofillPicker.java
@@ -27,25 +27,23 @@ import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.net.Uri;
import android.os.Bundle;
+import android.os.UserHandle;
import android.provider.Settings;
import android.service.autofill.AutofillService;
import android.service.autofill.AutofillServiceInfo;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
-
+import androidx.preference.Preference;
import com.android.internal.content.PackageMonitor;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settingslib.applications.DefaultAppInfo;
import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.CandidateInfo;
-
import java.util.ArrayList;
import java.util.List;
-import androidx.preference.Preference;
-
public class DefaultAutofillPicker extends DefaultAppPickerFragment {
private static final String TAG = "DefaultAutofillPicker";
@@ -73,8 +71,10 @@ public class DefaultAutofillPicker extends DefaultAppPickerFragment {
activity.setResult(Activity.RESULT_CANCELED);
activity.finish();
};
+ // If mCancelListener is not null, fragment is started from
+ // ACTION_REQUEST_SET_AUTOFILL_SERVICE and we should always use the calling uid.
+ mUserId = UserHandle.myUserId();
}
-
mSettingsPackageMonitor.register(activity, activity.getMainLooper(), false);
update();
}
@@ -159,8 +159,10 @@ public class DefaultAutofillPicker extends DefaultAppPickerFragment {
* @return The preference or {@code null} if no service can be added
*/
private Preference newAddServicePreferenceOrNull() {
- final String searchUri = Settings.Secure.getString(getActivity().getContentResolver(),
- Settings.Secure.AUTOFILL_SERVICE_SEARCH_URI);
+ final String searchUri = Settings.Secure.getStringForUser(
+ getActivity().getContentResolver(),
+ Settings.Secure.AUTOFILL_SERVICE_SEARCH_URI,
+ mUserId);
if (TextUtils.isEmpty(searchUri)) {
return null;
}
@@ -189,8 +191,8 @@ public class DefaultAutofillPicker extends DefaultAppPickerFragment {
@Override
protected List getCandidates() {
final List candidates = new ArrayList<>();
- final List resolveInfos = mPm.queryIntentServices(
- AUTOFILL_PROBE, PackageManager.GET_META_DATA);
+ final List resolveInfos = mPm.queryIntentServicesAsUser(
+ AUTOFILL_PROBE, PackageManager.GET_META_DATA, mUserId);
final Context context = getContext();
for (ResolveInfo info : resolveInfos) {
final String permission = info.serviceInfo.permission;
@@ -210,8 +212,9 @@ public class DefaultAutofillPicker extends DefaultAppPickerFragment {
return candidates;
}
- public static String getDefaultKey(Context context) {
- String setting = Settings.Secure.getString(context.getContentResolver(), SETTING);
+ public static String getDefaultKey(Context context, int userId) {
+ String setting = Settings.Secure.getStringForUser(
+ context.getContentResolver(), SETTING, userId);
if (setting != null) {
ComponentName componentName = ComponentName.unflattenFromString(setting);
if (componentName != null) {
@@ -223,7 +226,7 @@ public class DefaultAutofillPicker extends DefaultAppPickerFragment {
@Override
protected String getDefaultKey() {
- return getDefaultKey(getContext());
+ return getDefaultKey(getContext(), mUserId);
}
@Override
@@ -239,7 +242,7 @@ public class DefaultAutofillPicker extends DefaultAppPickerFragment {
@Override
protected boolean setDefaultKey(String key) {
- Settings.Secure.putString(getContext().getContentResolver(), SETTING, key);
+ Settings.Secure.putStringForUser(getContext().getContentResolver(), SETTING, key, mUserId);
// Check if activity was launched from Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE
// intent, and set proper result if so...
@@ -263,16 +266,19 @@ public class DefaultAutofillPicker extends DefaultAppPickerFragment {
private final String mSelectedKey;
private final Context mContext;
+ private final int mUserId;
- public AutofillSettingIntentProvider(Context context, String key) {
+ public AutofillSettingIntentProvider(Context context, int userId, String key) {
mSelectedKey = key;
mContext = context;
+ mUserId = userId;
}
@Override
public Intent getIntent() {
- final List resolveInfos = mContext.getPackageManager().queryIntentServices(
- AUTOFILL_PROBE, PackageManager.GET_META_DATA);
+ final List resolveInfos = mContext.getPackageManager()
+ .queryIntentServicesAsUser(
+ AUTOFILL_PROBE, PackageManager.GET_META_DATA, mUserId);
for (ResolveInfo resolveInfo : resolveInfos) {
final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
diff --git a/src/com/android/settings/applications/defaultapps/DefaultAutofillPreferenceController.java b/src/com/android/settings/applications/defaultapps/DefaultAutofillPreferenceController.java
index bab1d167df9..d32322b6fdb 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultAutofillPreferenceController.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultAutofillPreferenceController.java
@@ -44,7 +44,7 @@ public class DefaultAutofillPreferenceController extends DefaultAppPreferenceCon
@Override
public String getPreferenceKey() {
- return "default_autofill";
+ return "default_autofill_main";
}
@Override
@@ -54,7 +54,7 @@ public class DefaultAutofillPreferenceController extends DefaultAppPreferenceCon
}
final DefaultAutofillPicker.AutofillSettingIntentProvider intentProvider =
new DefaultAutofillPicker.AutofillSettingIntentProvider(
- mContext, info.getKey());
+ mContext, mUserId, info.getKey());
return intentProvider.getIntent();
}
diff --git a/src/com/android/settings/applications/defaultapps/DefaultWorkAutofillPreferenceController.java b/src/com/android/settings/applications/defaultapps/DefaultWorkAutofillPreferenceController.java
new file mode 100644
index 00000000000..ea4eff6b657
--- /dev/null
+++ b/src/com/android/settings/applications/defaultapps/DefaultWorkAutofillPreferenceController.java
@@ -0,0 +1,82 @@
+/*
+ * 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.settings.applications.defaultapps;
+
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import com.android.settings.Utils;
+import com.android.settingslib.applications.DefaultAppInfo;
+
+public class DefaultWorkAutofillPreferenceController extends DefaultAutofillPreferenceController {
+ private final UserHandle mUserHandle;
+
+ public DefaultWorkAutofillPreferenceController(Context context) {
+ super(context);
+ mUserHandle = Utils.getManagedProfile(mUserManager);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (mUserHandle == null) {
+ return false;
+ }
+ return super.isAvailable();
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return "default_autofill_work";
+ }
+
+ @Override
+ protected DefaultAppInfo getDefaultAppInfo() {
+ final String flattenComponent = Settings.Secure.getStringForUser(
+ mContext.getContentResolver(),
+ DefaultAutofillPicker.SETTING,
+ mUserHandle.getIdentifier());
+ if (!TextUtils.isEmpty(flattenComponent)) {
+ DefaultAppInfo appInfo = new DefaultAppInfo(
+ mContext,
+ mPackageManager,
+ mUserHandle.getIdentifier(),
+ ComponentName.unflattenFromString(flattenComponent));
+ return appInfo;
+ }
+ return null;
+ }
+
+ @Override
+ protected Intent getSettingIntent(DefaultAppInfo info) {
+ if (info == null) {
+ return null;
+ }
+ final DefaultAutofillPicker.AutofillSettingIntentProvider intentProvider =
+ new DefaultAutofillPicker.AutofillSettingIntentProvider(
+ mContext, mUserHandle.getIdentifier(), info.getKey());
+ return intentProvider.getIntent();
+ }
+
+ @Override
+ protected void startActivity(Intent intent) {
+ mContext.startActivityAsUser(intent, mUserHandle);
+ }
+}
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollAnimationDrawable.java b/src/com/android/settings/biometrics/face/FaceEnrollAnimationDrawable.java
new file mode 100644
index 00000000000..0da666cc9f6
--- /dev/null
+++ b/src/com/android/settings/biometrics/face/FaceEnrollAnimationDrawable.java
@@ -0,0 +1,85 @@
+/*
+ * 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.settings.biometrics.face;
+
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+
+/**
+ * A drawable containing the circle cutout.
+ */
+public class FaceEnrollAnimationDrawable extends Drawable {
+
+ private Rect mBounds;
+ private final Paint mSquarePaint;
+ private final Paint mCircleCutoutPaint;
+
+ public FaceEnrollAnimationDrawable() {
+ mSquarePaint = new Paint();
+ mSquarePaint.setColor(Color.WHITE);
+ mSquarePaint.setAntiAlias(true);
+
+ mCircleCutoutPaint = new Paint();
+ mCircleCutoutPaint.setColor(Color.TRANSPARENT);
+ mCircleCutoutPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+ mCircleCutoutPaint.setAntiAlias(true);
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ mBounds = bounds;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ if (mBounds == null) {
+ return;
+ }
+ canvas.save();
+
+ // Draw a rectangle covering the whole view
+ canvas.drawRect(0, 0, mBounds.width(), mBounds.height(), mSquarePaint);
+
+ // Clear a circle in the middle for the camera preview
+ canvas.drawCircle(mBounds.exactCenterX(), mBounds.exactCenterY(),
+ mBounds.height() / 2, mCircleCutoutPaint);
+
+ canvas.restore();
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+}
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java b/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java
index 9786363cbc1..7fac9f6724f 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java
@@ -40,10 +40,12 @@ public class FaceEnrollEnrolling extends BiometricsEnrollEnrolling {
private static final String TAG = "FaceEnrollEnrolling";
private static final boolean DEBUG = true;
+ private static final String TAG_FACE_PREVIEW = "tag_preview";
private TextView mErrorText;
private Interpolator mLinearOutSlowInInterpolator;
private boolean mShouldFinishOnStop = true;
+ private FaceEnrollPreviewFragment mFaceCameraPreview;
public static class FaceErrorDialog extends BiometricErrorDialog {
static FaceErrorDialog newInstance(CharSequence msg, int msgId) {
@@ -92,6 +94,18 @@ public class FaceEnrollEnrolling extends BiometricsEnrollEnrolling {
}
}
+ @Override
+ public void startEnrollment() {
+ super.startEnrollment();
+ mFaceCameraPreview = (FaceEnrollPreviewFragment) getSupportFragmentManager()
+ .findFragmentByTag(TAG_FACE_PREVIEW);
+ if (mFaceCameraPreview == null) {
+ mFaceCameraPreview = new FaceEnrollPreviewFragment();
+ getSupportFragmentManager().beginTransaction().add(mFaceCameraPreview, TAG_FACE_PREVIEW)
+ .commitAllowingStateLoss();
+ }
+ }
+
@Override
protected Intent getFinishIntent() {
return new Intent(this, FaceEnrollFinish.class);
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollPreviewFragment.java b/src/com/android/settings/biometrics/face/FaceEnrollPreviewFragment.java
new file mode 100644
index 00000000000..2b92ccbc115
--- /dev/null
+++ b/src/com/android/settings/biometrics/face/FaceEnrollPreviewFragment.java
@@ -0,0 +1,347 @@
+/*
+ * 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.settings.biometrics.face;
+
+import android.content.Context;
+import android.graphics.Matrix;
+import android.graphics.SurfaceTexture;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.util.Size;
+import android.view.Surface;
+import android.view.TextureView;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.core.InstrumentedPreferenceFragment;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Fragment that contains the logic for showing and controlling the camera preview, circular
+ * overlay, as well as the enrollment animations.
+ */
+public class FaceEnrollPreviewFragment extends InstrumentedPreferenceFragment {
+
+ private static final String TAG = "FaceEnrollPreviewFragment";
+
+ private static final int MAX_PREVIEW_WIDTH = 1920;
+ private static final int MAX_PREVIEW_HEIGHT = 1080;
+
+ private Handler mHandler = new Handler(Looper.getMainLooper());
+ private CameraManager mCameraManager;
+ private String mCameraId;
+ private CameraDevice mCameraDevice;
+ private CaptureRequest.Builder mPreviewRequestBuilder;
+ private CameraCaptureSession mCaptureSession;
+ private CaptureRequest mPreviewRequest;
+ private Size mPreviewSize;
+
+ // View used to contain the circular cutout and enrollment animation drawable
+ private ImageView mCircleView;
+
+ // Drawable containing the circular cutout and enrollment animations
+ private FaceEnrollAnimationDrawable mAnimationDrawable;
+
+ // Texture used for showing the camera preview
+ private FaceSquareTextureView mTextureView;
+
+ private final TextureView.SurfaceTextureListener mSurfaceTextureListener =
+ new TextureView.SurfaceTextureListener() {
+
+ @Override
+ public void onSurfaceTextureAvailable(
+ SurfaceTexture surfaceTexture, int width, int height) {
+ openCamera(width, height);
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(
+ SurfaceTexture surfaceTexture, int width, int height) {
+ // Shouldn't be called, but do this for completeness.
+ configureTransform(width, height);
+ }
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
+ return true;
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
+
+ }
+ };
+
+ private final CameraDevice.StateCallback mCameraStateCallback =
+ new CameraDevice.StateCallback() {
+
+ @Override
+ public void onOpened(CameraDevice cameraDevice) {
+ mCameraDevice = cameraDevice;
+
+ try {
+ // Configure the size of default buffer
+ SurfaceTexture texture = mTextureView.getSurfaceTexture();
+ texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
+
+ // This is the output Surface we need to start preview
+ Surface surface = new Surface(texture);
+
+ // Set up a CaptureRequest.Builder with the output Surface
+ mPreviewRequestBuilder =
+ mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ mPreviewRequestBuilder.addTarget(surface);
+
+ // Create a CameraCaptureSession for camera preview
+ mCameraDevice.createCaptureSession(Arrays.asList(surface),
+ new CameraCaptureSession.StateCallback() {
+
+ @Override
+ public void onConfigured(CameraCaptureSession cameraCaptureSession) {
+ // The camera is already closed
+ if (null == mCameraDevice) {
+ return;
+ }
+ // When the session is ready, we start displaying the preview.
+ mCaptureSession = cameraCaptureSession;
+ try {
+ // Auto focus should be continuous for camera preview.
+ mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
+ CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
+
+ // Finally, we start displaying the camera preview.
+ mPreviewRequest = mPreviewRequestBuilder.build();
+ mCaptureSession.setRepeatingRequest(mPreviewRequest,
+ null /* listener */, mHandler);
+ } catch (CameraAccessException e) {
+ Log.e(TAG, "Unable to access camera", e);
+ }
+ }
+
+ @Override
+ public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
+ Log.e(TAG, "Unable to configure camera");
+ }
+ }, null /* handler */);
+ } catch (CameraAccessException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onDisconnected(CameraDevice cameraDevice) {
+ cameraDevice.close();
+ mCameraDevice = null;
+ }
+
+ @Override
+ public void onError(CameraDevice cameraDevice, int error) {
+ cameraDevice.close();
+ mCameraDevice = null;
+ }
+ };
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsProto.MetricsEvent.FACE_ENROLL_PREVIEW;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mTextureView = getActivity().findViewById(R.id.texture_view);
+ mCircleView = getActivity().findViewById(R.id.circle_view);
+
+ // Must disable hardware acceleration for this view, otherwise transparency breaks
+ mCircleView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+
+ mAnimationDrawable = new FaceEnrollAnimationDrawable();
+ mCircleView.setImageDrawable(mAnimationDrawable);
+
+ mCameraManager = (CameraManager) getContext().getSystemService(Context.CAMERA_SERVICE);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ // When the screen is turned off and turned back on, the SurfaceTexture is already
+ // available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open
+ // a camera and start preview from here (otherwise, we wait until the surface is ready in
+ // the SurfaceTextureListener).
+ if (mTextureView.isAvailable()) {
+ openCamera(mTextureView.getWidth(), mTextureView.getHeight());
+ } else {
+ mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ closeCamera();
+ }
+
+ /**
+ * Sets up member variables related to camera.
+ *
+ * @param width The width of available size for camera preview
+ * @param height The height of available size for camera preview
+ */
+ private void setUpCameraOutputs(int width, int height) {
+ try {
+ for (String cameraId : mCameraManager.getCameraIdList()) {
+ CameraCharacteristics characteristics =
+ mCameraManager.getCameraCharacteristics(cameraId);
+
+ // Find front facing camera
+ Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
+ if (facing == null || facing != CameraCharacteristics.LENS_FACING_FRONT) {
+ continue;
+ }
+ mCameraId = cameraId;
+
+ // Get the stream configurations
+ StreamConfigurationMap map = characteristics.get(
+ CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+ mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
+ width, height, MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT);
+ }
+ } catch (CameraAccessException e) {
+ Log.e(TAG, "Unable to access camera", e);
+ }
+ }
+
+ /**
+ * Opens the camera specified by mCameraId.
+ * @param width The width of the texture view
+ * @param height The height of the texture view
+ */
+ private void openCamera(int width, int height) {
+ try {
+ setUpCameraOutputs(width, height);
+ mCameraManager.openCamera(mCameraId, mCameraStateCallback, mHandler);
+ configureTransform(width, height);
+ } catch (CameraAccessException e) {
+ Log.e(TAG, "Unable to open camera", e);
+ }
+ }
+
+ /**
+ * Chooses the optimal resolution for the camera to open.
+ */
+ private Size chooseOptimalSize(Size[] choices, int textureViewWidth, int textureViewHeight,
+ int maxWidth, int maxHeight) {
+ // Collect the supported resolutions that are at least as big as the preview Surface
+ List bigEnough = new ArrayList<>();
+ // Collect the supported resolutions that are smaller than the preview Surface
+ List notBigEnough = new ArrayList<>();
+
+ for (Size option : choices) {
+ if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight &&
+ option.getHeight() == option.getWidth()) {
+ if (option.getWidth() >= textureViewWidth &&
+ option.getHeight() >= textureViewHeight) {
+ bigEnough.add(option);
+ } else {
+ notBigEnough.add(option);
+ }
+ }
+ }
+
+ // Pick the smallest of those big enough. If there is no one big enough, pick the
+ // largest of those not big enough.
+ if (bigEnough.size() > 0) {
+ return Collections.min(bigEnough, new CompareSizesByArea());
+ } else if (notBigEnough.size() > 0) {
+ return Collections.max(notBigEnough, new CompareSizesByArea());
+ } else {
+ Log.e(TAG, "Couldn't find any suitable preview size");
+ return choices[0];
+ }
+ }
+
+ /**
+ * Configures the necessary {@link android.graphics.Matrix} transformation to `mTextureView`.
+ * This method should be called after the camera preview size is determined in
+ * setUpCameraOutputs and also the size of `mTextureView` is fixed.
+ *
+ * @param viewWidth The width of `mTextureView`
+ * @param viewHeight The height of `mTextureView`
+ */
+ private void configureTransform(int viewWidth, int viewHeight) {
+ if (mTextureView == null) {
+ return;
+ }
+
+ // Fix the aspect ratio
+ Matrix matrix = new Matrix();
+ float scaleX = (float) viewWidth / mPreviewSize.getWidth();
+ float scaleY = (float) viewHeight / mPreviewSize.getHeight();
+
+ // Now divide by smaller one so it fills up the original space
+ float smaller = Math.min(scaleX, scaleY);
+ scaleX = scaleX / smaller;
+ scaleY = scaleY / smaller;
+
+ // Apply the scale
+ matrix.setScale(scaleX, scaleY);
+
+ mTextureView.setTransform(matrix);
+ }
+
+ private void closeCamera() {
+ if (mCaptureSession != null) {
+ mCaptureSession.close();
+ mCaptureSession = null;
+ }
+ if (mCameraDevice != null) {
+ mCameraDevice.close();
+ mCameraDevice = null;
+ }
+ }
+
+ /**
+ * Compares two {@code Size}s based on their areas.
+ */
+ private static class CompareSizesByArea implements Comparator {
+ @Override
+ public int compare(Size lhs, Size rhs) {
+ // We cast here to ensure the multiplications won't overflow
+ return Long.signum((long) lhs.getWidth() * lhs.getHeight() -
+ (long) rhs.getWidth() * rhs.getHeight());
+ }
+
+ }
+
+}
diff --git a/src/com/android/settings/biometrics/face/FaceSquareFrameLayout.java b/src/com/android/settings/biometrics/face/FaceSquareFrameLayout.java
new file mode 100644
index 00000000000..3aed5241f9a
--- /dev/null
+++ b/src/com/android/settings/biometrics/face/FaceSquareFrameLayout.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.settings.biometrics.face;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+/**
+ * Square layout that sets the height to be the same as width.
+ */
+public class FaceSquareFrameLayout extends FrameLayout {
+
+ public FaceSquareFrameLayout(Context context) {
+ super(context);
+ }
+
+ public FaceSquareFrameLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public FaceSquareFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public FaceSquareFrameLayout(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // Don't call super, manually set their size below
+ int size = MeasureSpec.getSize(widthMeasureSpec);
+
+ // Set this frame layout to be a square
+ setMeasuredDimension(size, size);
+
+ // Set the children to be the same size (square) as well
+ final int numChildren = getChildCount();
+ for (int i = 0; i < numChildren; i++) {
+ int spec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
+ this.getChildAt(i).measure(spec, spec);
+ }
+ }
+}
diff --git a/src/com/android/settings/biometrics/face/FaceSquareTextureView.java b/src/com/android/settings/biometrics/face/FaceSquareTextureView.java
new file mode 100644
index 00000000000..ebbbc27cb75
--- /dev/null
+++ b/src/com/android/settings/biometrics/face/FaceSquareTextureView.java
@@ -0,0 +1,52 @@
+/*
+ * 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.settings.biometrics.face;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.TextureView;
+
+/**
+ * A square {@link TextureView}.
+ */
+public class FaceSquareTextureView extends TextureView {
+
+ public FaceSquareTextureView(Context context) {
+ this(context, null);
+ }
+
+ public FaceSquareTextureView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public FaceSquareTextureView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ int height = MeasureSpec.getSize(heightMeasureSpec);
+
+ if (width < height) {
+ setMeasuredDimension(width, width);
+ } else {
+ setMeasuredDimension(height, height);
+ }
+ }
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
index 84cab40baaf..c786356eb46 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
@@ -15,6 +15,7 @@
*/
package com.android.settings.bluetooth;
+import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.os.Bundle;
@@ -115,16 +116,30 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback,
* Force to update the list of bluetooth devices
*/
public void forceUpdate() {
- Collection cachedDevices =
+ if (BluetoothAdapter.getDefaultAdapter().isEnabled()) {
+ final Collection cachedDevices =
+ mLocalManager.getCachedDeviceManager().getCachedDevicesCopy();
+ for (CachedBluetoothDevice cachedBluetoothDevice : cachedDevices) {
+ update(cachedBluetoothDevice);
+ }
+ }
+ }
+
+ public void removeAllDevicesFromPreference() {
+ final Collection cachedDevices =
mLocalManager.getCachedDeviceManager().getCachedDevicesCopy();
for (CachedBluetoothDevice cachedBluetoothDevice : cachedDevices) {
- update(cachedBluetoothDevice);
+ removePreference(cachedBluetoothDevice);
}
}
@Override
public void onBluetoothStateChanged(int bluetoothState) {
- forceUpdate();
+ if (BluetoothAdapter.STATE_ON == bluetoothState) {
+ forceUpdate();
+ } else if (BluetoothAdapter.STATE_OFF == bluetoothState) {
+ removeAllDevicesFromPreference();
+ }
}
@Override
diff --git a/src/com/android/settings/bluetooth/BluetoothSliceBuilder.java b/src/com/android/settings/bluetooth/BluetoothSliceBuilder.java
index 7a569947b4f..3c2f1b8964f 100644
--- a/src/com/android/settings/bluetooth/BluetoothSliceBuilder.java
+++ b/src/com/android/settings/bluetooth/BluetoothSliceBuilder.java
@@ -37,6 +37,7 @@ import com.android.settings.slices.SliceBuilderUtils;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
import androidx.slice.builders.ListBuilder;
+import androidx.slice.builders.ListBuilder.RowBuilder;
import androidx.slice.builders.SliceAction;
/**
@@ -93,7 +94,7 @@ public class BluetoothSliceBuilder {
return new ListBuilder(context, BLUETOOTH_URI, ListBuilder.INFINITY)
.setAccentColor(color)
- .addRow(b -> b
+ .addRow(new RowBuilder()
.setTitle(title)
.addEndItem(toggleSliceAction)
.setPrimaryAction(primarySliceAction))
diff --git a/src/com/android/settings/bluetooth/RequestPermissionActivity.java b/src/com/android/settings/bluetooth/RequestPermissionActivity.java
index fff6f12ccc5..54854065e99 100644
--- a/src/com/android/settings/bluetooth/RequestPermissionActivity.java
+++ b/src/com/android/settings/bluetooth/RequestPermissionActivity.java
@@ -32,17 +32,17 @@ import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
+import androidx.appcompat.app.AlertDialog;
+
import com.android.settings.R;
import com.android.settingslib.bluetooth.BluetoothDiscoverableTimeoutReceiver;
-import androidx.appcompat.app.AlertDialog;
-
/**
* RequestPermissionActivity asks the user whether to enable discovery. This is
* usually started by an application wanted to start bluetooth and or discovery
*/
public class RequestPermissionActivity extends Activity implements
- DialogInterface.OnClickListener {
+ DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
// Command line to test this
// adb shell am start -a android.bluetooth.adapter.action.REQUEST_ENABLE
// adb shell am start -a android.bluetooth.adapter.action.REQUEST_DISCOVERABLE
@@ -188,6 +188,7 @@ public class RequestPermissionActivity extends Activity implements
builder.setNegativeButton(getString(R.string.deny), this);
}
+ builder.setOnDismissListener(this);
mDialog = builder.create();
mDialog.show();
}
@@ -238,12 +239,16 @@ public class RequestPermissionActivity extends Activity implements
break;
case DialogInterface.BUTTON_NEGATIVE:
- setResult(RESULT_CANCELED);
- finish();
+ cancelAndFinish();
break;
}
}
+ @Override
+ public void onDismiss(final DialogInterface dialog) {
+ cancelAndFinish();
+ }
+
private void proceedAndFinish() {
int returnCode;
diff --git a/src/com/android/settings/connecteddevice/TopLevelConnectedDevicesPreferenceController.java b/src/com/android/settings/connecteddevice/TopLevelConnectedDevicesPreferenceController.java
new file mode 100644
index 00000000000..6f16db6e368
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/TopLevelConnectedDevicesPreferenceController.java
@@ -0,0 +1,40 @@
+/*
+ * 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.settings.connecteddevice;
+
+import android.content.Context;
+
+import com.android.settings.core.BasePreferenceController;
+
+public class TopLevelConnectedDevicesPreferenceController extends BasePreferenceController {
+
+ public TopLevelConnectedDevicesPreferenceController(Context context,
+ String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE_UNSEARCHABLE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ return mContext.getText(
+ AdvancedConnectedDeviceController.getConnectedDevicesSummaryResourceId(mContext));
+ }
+}
diff --git a/src/com/android/settings/dashboard/DashboardAdapter.java b/src/com/android/settings/dashboard/DashboardAdapter.java
index 7e7cf46caf4..a2d810b4e9e 100644
--- a/src/com/android/settings/dashboard/DashboardAdapter.java
+++ b/src/com/android/settings/dashboard/DashboardAdapter.java
@@ -16,7 +16,6 @@
package com.android.settings.dashboard;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Bundle;
@@ -40,17 +39,17 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.R.id;
import com.android.settings.dashboard.DashboardData.ConditionHeaderData;
-import com.android.settings.dashboard.conditional.Condition;
-import com.android.settings.dashboard.conditional.ConditionAdapter;
import com.android.settings.dashboard.suggestions.SuggestionAdapter;
+import com.android.settings.homepage.conditional.Condition;
+import com.android.settings.homepage.conditional.ConditionAdapter;
import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.widget.RoundedHomepageIcon;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.Tile;
-import com.android.settingslib.drawer.TileUtils;
import com.android.settingslib.suggestions.SuggestionControllerMixinCompat;
import com.android.settingslib.utils.IconCache;
@@ -64,7 +63,7 @@ public class DashboardAdapter extends RecyclerView.Adapter getPreferencesForCategory(FragmentActivity activity, Context context,
- int sourceMetricsCategory, String key);
-
/**
* Get all tiles, grouped by category.
*/
List getAllCategories();
- /**
- * Whether or not we should tint icons in setting pages.
- *
- * @deprecated in favor of color icons in homepage
- */
- @Deprecated
- boolean shouldTintIcon();
-
/**
* Returns an unique string key for the tile.
*/
@@ -72,6 +47,7 @@ public interface DashboardFeatureProvider {
* Binds preference to data provided by tile.
*
* @param activity If tile contains intent to launch, it will be launched from this activity
+ * @param forceRoundedIcon Whether or not injected tiles from other packages should be forced to rounded icon.
* @param sourceMetricsCategory The context (source) from which an action is performed
* @param pref The preference to bind data
* @param tile The binding data
@@ -79,8 +55,8 @@ public interface DashboardFeatureProvider {
* @param baseOrder The order offset value. When binding, pref's order is determined by
* both this value and tile's own priority.
*/
- void bindPreferenceToTile(FragmentActivity activity, int sourceMetricsCategory, Preference pref,
- Tile tile, String key, int baseOrder);
+ void bindPreferenceToTile(FragmentActivity activity, boolean forceRoundedIcon,
+ int sourceMetricsCategory, Preference pref, Tile tile, String key, int baseOrder);
/**
* Returns additional intent filter action for dashboard tiles
diff --git a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
index e4dd7a61aa9..7c6593628ad 100644
--- a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
+++ b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
@@ -25,6 +25,7 @@ import android.content.Context;
import android.content.IContentProvider;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.provider.Settings;
@@ -42,6 +43,7 @@ import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.dashboard.profileselector.ProfileSelectDialog;
import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.widget.RoundedHomepageIcon;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
import com.android.settingslib.drawer.DashboardCategory;
@@ -49,7 +51,6 @@ import com.android.settingslib.drawer.Tile;
import com.android.settingslib.drawer.TileUtils;
import com.android.settingslib.utils.ThreadUtils;
-import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -80,39 +81,11 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
return mCategoryManager.getTilesByCategory(mContext, key);
}
- @Override
- public List getPreferencesForCategory(FragmentActivity activity, Context context,
- int sourceMetricsCategory, String key) {
- final DashboardCategory category = getTilesForCategory(key);
- if (category == null) {
- Log.d(TAG, "NO dashboard tiles for " + TAG);
- return null;
- }
- final List tiles = category.getTiles();
- if (tiles == null || tiles.isEmpty()) {
- Log.d(TAG, "tile list is empty, skipping category " + category.key);
- return null;
- }
- final List preferences = new ArrayList<>();
- for (Tile tile : tiles) {
- final Preference pref = new Preference(context);
- bindPreferenceToTile(activity, sourceMetricsCategory, pref, tile, null /* key */,
- Preference.DEFAULT_ORDER /* baseOrder */);
- preferences.add(pref);
- }
- return preferences;
- }
-
@Override
public List getAllCategories() {
return mCategoryManager.getCategories(mContext);
}
- @Override
- public boolean shouldTintIcon() {
- return mContext.getResources().getBoolean(R.bool.config_tintSettingIcon);
- }
-
@Override
public String getDashboardKeyForTile(Tile tile) {
if (tile == null) {
@@ -128,8 +101,8 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
}
@Override
- public void bindPreferenceToTile(FragmentActivity activity, int sourceMetricsCategory,
- Preference pref, Tile tile, String key, int baseOrder) {
+ public void bindPreferenceToTile(FragmentActivity activity, boolean forceRoundedIcon,
+ int sourceMetricsCategory, Preference pref, Tile tile, String key, int baseOrder) {
if (pref == null) {
return;
}
@@ -140,7 +113,7 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
pref.setKey(getDashboardKeyForTile(tile));
}
bindSummary(pref, tile);
- bindIcon(pref, tile);
+ bindIcon(pref, tile, forceRoundedIcon);
final Bundle metadata = tile.getMetaData();
String clsName = null;
String action = null;
@@ -220,10 +193,16 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
}
@VisibleForTesting
- void bindIcon(Preference preference, Tile tile) {
+ void bindIcon(Preference preference, Tile tile, boolean forceRoundedIcon) {
final Icon tileIcon = tile.getIcon(mContext);
if (tileIcon != null) {
- preference.setIcon(tileIcon.loadDrawable(preference.getContext()));
+ Drawable iconDrawable = tileIcon.loadDrawable(preference.getContext());
+ if (forceRoundedIcon
+ && !TextUtils.equals(mContext.getPackageName(), tile.getPackageName())) {
+ iconDrawable = new RoundedHomepageIcon(mContext, iconDrawable);
+ ((RoundedHomepageIcon) iconDrawable).setBackgroundColor(mContext, tile);
+ }
+ preference.setIcon(iconDrawable);
} else if (tile.getMetaData() != null
&& tile.getMetaData().containsKey(META_DATA_PREFERENCE_ICON_URI)) {
ThreadUtils.postOnBackgroundThread(() -> {
diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java
index acf885dee0f..a90950e5521 100644
--- a/src/com/android/settings/dashboard/DashboardFragment.java
+++ b/src/com/android/settings/dashboard/DashboardFragment.java
@@ -207,6 +207,10 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
@Override
protected abstract int getPreferenceScreenResId();
+ protected boolean shouldForceRoundedIcon() {
+ return false;
+ }
+
protected T use(Class clazz) {
List controllerList = mPreferenceControllers.get(clazz);
if (controllerList != null) {
@@ -343,6 +347,7 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
final int tintColor = a.getColor(0, context.getColor(android.R.color.white));
a.recycle();
// Install dashboard tiles.
+ final boolean forceRoundedIcons = shouldForceRoundedIcon();
for (Tile tile : tiles) {
final String key = mDashboardFeatureProvider.getDashboardKeyForTile(tile);
if (TextUtils.isEmpty(key)) {
@@ -361,13 +366,15 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
if (mDashboardTilePrefKeys.contains(key)) {
// Have the key already, will rebind.
final Preference preference = screen.findPreference(key);
- mDashboardFeatureProvider.bindPreferenceToTile(getActivity(), getMetricsCategory(),
- preference, tile, key, mPlaceholderPreferenceController.getOrder());
+ mDashboardFeatureProvider.bindPreferenceToTile(getActivity(), forceRoundedIcons,
+ getMetricsCategory(), preference, tile, key,
+ mPlaceholderPreferenceController.getOrder());
} else {
// Don't have this key, add it.
final Preference pref = new Preference(getPrefContext());
- mDashboardFeatureProvider.bindPreferenceToTile(getActivity(), getMetricsCategory(),
- pref, tile, key, mPlaceholderPreferenceController.getOrder());
+ mDashboardFeatureProvider.bindPreferenceToTile(getActivity(), forceRoundedIcons,
+ getMetricsCategory(), pref, tile, key,
+ mPlaceholderPreferenceController.getOrder());
screen.addPreference(pref);
mDashboardTilePrefKeys.add(key);
}
diff --git a/src/com/android/settings/dashboard/DashboardFragmentRegistry.java b/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
index cd478fc43ad..f5330a7b5eb 100644
--- a/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
+++ b/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
@@ -30,6 +30,7 @@ import com.android.settings.deviceinfo.StorageDashboardFragment;
import com.android.settings.display.NightDisplaySettings;
import com.android.settings.fuelgauge.PowerUsageSummary;
import com.android.settings.gestures.GestureSettings;
+import com.android.settings.homepage.TopLevelSettings;
import com.android.settings.language.LanguageAndInputSettings;
import com.android.settings.network.NetworkDashboardFragment;
import com.android.settings.notification.ConfigureNotificationSettings;
@@ -61,6 +62,9 @@ public class DashboardFragmentRegistry {
static {
PARENT_TO_CATEGORY_KEY_MAP = new ArrayMap<>();
+ // TODO(b/110405144): Add the mapping when IA.homepage intent-filter is is removed.
+ // PARENT_TO_CATEGORY_KEY_MAP.put(TopLevelSettings.class.getName(),
+ // CategoryKey.CATEGORY_HOMEPAGE);
PARENT_TO_CATEGORY_KEY_MAP.put(
NetworkDashboardFragment.class.getName(), CategoryKey.CATEGORY_NETWORK);
PARENT_TO_CATEGORY_KEY_MAP.put(ConnectedDeviceDashboardFragment.class.getName(),
@@ -98,9 +102,9 @@ public class DashboardFragmentRegistry {
PARENT_TO_CATEGORY_KEY_MAP.put(ZenModeSettings.class.getName(),
CategoryKey.CATEGORY_DO_NOT_DISTURB);
PARENT_TO_CATEGORY_KEY_MAP.put(GestureSettings.class.getName(),
- CategoryKey.CATEGORY_GESTURES);
+ CategoryKey.CATEGORY_GESTURES);
PARENT_TO_CATEGORY_KEY_MAP.put(NightDisplaySettings.class.getName(),
- CategoryKey.CATEGORY_NIGHT_DISPLAY);
+ CategoryKey.CATEGORY_NIGHT_DISPLAY);
CATEGORY_KEY_TO_PARENT_MAP = new ArrayMap<>(PARENT_TO_CATEGORY_KEY_MAP.size());
diff --git a/src/com/android/settings/dashboard/DashboardSummary.java b/src/com/android/settings/dashboard/DashboardSummary.java
index c6b69e9953d..9609098ab35 100644
--- a/src/com/android/settings/dashboard/DashboardSummary.java
+++ b/src/com/android/settings/dashboard/DashboardSummary.java
@@ -26,17 +26,22 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import androidx.annotation.VisibleForTesting;
+import androidx.annotation.WorkerThread;
+import androidx.loader.app.LoaderManager;
+import androidx.recyclerview.widget.LinearLayoutManager;
+
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.core.InstrumentedFragment;
import com.android.settings.core.SettingsBaseActivity;
import com.android.settings.core.SettingsBaseActivity.CategoryListener;
-import com.android.settings.dashboard.conditional.Condition;
-import com.android.settings.dashboard.conditional.ConditionManager;
-import com.android.settings.dashboard.conditional.ConditionManager.ConditionListener;
-import com.android.settings.dashboard.conditional.FocusRecyclerView;
-import com.android.settings.dashboard.conditional.FocusRecyclerView.FocusListener;
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
+import com.android.settings.homepage.conditional.Condition;
+import com.android.settings.homepage.conditional.ConditionManager;
+import com.android.settings.homepage.conditional.ConditionManager.ConditionListener;
+import com.android.settings.homepage.conditional.FocusRecyclerView;
+import com.android.settings.homepage.conditional.FocusRecyclerView.FocusListener;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.ActionBarShadowController;
import com.android.settingslib.drawer.CategoryKey;
@@ -46,11 +51,12 @@ import com.android.settingslib.utils.ThreadUtils;
import java.util.List;
-import androidx.annotation.VisibleForTesting;
-import androidx.annotation.WorkerThread;
-import androidx.loader.app.LoaderManager;
-import androidx.recyclerview.widget.LinearLayoutManager;
-
+/**
+ * Deprecated in favor of {@link com.android.settings.homepage.TopLevelSettings}
+ *
+ * @deprecated
+ */
+@Deprecated
public class DashboardSummary extends InstrumentedFragment
implements CategoryListener, ConditionListener,
FocusListener, SuggestionControllerMixinCompat.SuggestionControllerHost {
diff --git a/src/com/android/settings/deviceinfo/TopLevelStoragePreferenceController.java b/src/com/android/settings/deviceinfo/TopLevelStoragePreferenceController.java
new file mode 100644
index 00000000000..c6fc23b1df2
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/TopLevelStoragePreferenceController.java
@@ -0,0 +1,57 @@
+/*
+ * 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.settings.deviceinfo;
+
+import android.content.Context;
+import android.os.storage.StorageManager;
+import android.text.format.Formatter;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.deviceinfo.PrivateStorageInfo;
+import com.android.settingslib.deviceinfo.StorageManagerVolumeProvider;
+
+import java.text.NumberFormat;
+
+public class TopLevelStoragePreferenceController extends BasePreferenceController {
+
+ private final StorageManager mStorageManager;
+ private final StorageManagerVolumeProvider mStorageManagerVolumeProvider;
+
+ public TopLevelStoragePreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mStorageManager = mContext.getSystemService(StorageManager.class);
+ mStorageManagerVolumeProvider = new StorageManagerVolumeProvider(mStorageManager);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE_UNSEARCHABLE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ // TODO: Register listener.
+ final NumberFormat percentageFormat = NumberFormat.getPercentInstance();
+ final PrivateStorageInfo info = PrivateStorageInfo.getPrivateStorageInfo(
+ mStorageManagerVolumeProvider);
+ double privateUsedBytes = info.totalBytes - info.freeBytes;
+ return mContext.getString(R.string.storage_summary,
+ percentageFormat.format(privateUsedBytes / info.totalBytes),
+ Formatter.formatFileSize(mContext, info.freeBytes));
+ }
+}
diff --git a/src/com/android/settings/flashlight/FlashlightSliceBuilder.java b/src/com/android/settings/flashlight/FlashlightSliceBuilder.java
index a689fd23066..d4309ca66d0 100644
--- a/src/com/android/settings/flashlight/FlashlightSliceBuilder.java
+++ b/src/com/android/settings/flashlight/FlashlightSliceBuilder.java
@@ -42,6 +42,7 @@ import com.android.settings.slices.SliceBroadcastReceiver;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
import androidx.slice.builders.ListBuilder;
+import androidx.slice.builders.ListBuilder.RowBuilder;
import androidx.slice.builders.SliceAction;
@@ -93,7 +94,7 @@ public class FlashlightSliceBuilder {
IconCompat.createWithResource(context, R.drawable.ic_signal_flashlight);
return new ListBuilder(context, FLASHLIGHT_URI, ListBuilder.INFINITY)
.setAccentColor(color)
- .addRow(b -> b
+ .addRow(new RowBuilder()
.setTitle(context.getText(R.string.power_flashlight))
.setTitleItem(icon, ICON_IMAGE)
.setPrimaryAction(
diff --git a/src/com/android/settings/fuelgauge/BatteryHistoryChart.java b/src/com/android/settings/fuelgauge/BatteryHistoryChart.java
deleted file mode 100644
index e442a071bd1..00000000000
--- a/src/com/android/settings/fuelgauge/BatteryHistoryChart.java
+++ /dev/null
@@ -1,1366 +0,0 @@
-/*
- * Copyright (C) 2010 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.settings.fuelgauge;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.DashPathEffect;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.Typeface;
-import android.os.BatteryStats;
-import android.os.BatteryStats.HistoryItem;
-import android.os.SystemClock;
-import android.telephony.ServiceState;
-import android.text.TextPaint;
-import android.text.format.DateFormat;
-import android.text.format.Formatter;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.TimeUtils;
-import android.util.TypedValue;
-import android.view.View;
-
-import com.android.settings.R;
-import com.android.settings.Utils;
-
-import libcore.icu.LocaleData;
-
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Locale;
-
-public class BatteryHistoryChart extends View {
- static final boolean DEBUG = false;
- static final String TAG = "BatteryHistoryChart";
-
- static final int CHART_DATA_X_MASK = 0x0000ffff;
- static final int CHART_DATA_BIN_MASK = 0xffff0000;
- static final int CHART_DATA_BIN_SHIFT = 16;
-
- static class ChartData {
- int[] mColors;
- Paint[] mPaints;
-
- int mNumTicks;
- int[] mTicks;
- int mLastBin;
-
- void setColors(int[] colors) {
- mColors = colors;
- mPaints = new Paint[colors.length];
- for (int i=0; i 0) {
- mTicks = new int[width*2];
- } else {
- mTicks = null;
- }
- mNumTicks = 0;
- mLastBin = 0;
- }
-
- void addTick(int x, int bin) {
- if (bin != mLastBin && mNumTicks < mTicks.length) {
- mTicks[mNumTicks] = (x&CHART_DATA_X_MASK) | (bin<> CHART_DATA_BIN_SHIFT;
- if (lastBin != 0) {
- canvas.drawRect(lastX, top, x, bottom, mPaints[lastBin]);
- }
- lastBin = bin;
- lastX = x;
- }
-
- }
- }
-
- static final int SANS = 1;
- static final int SERIF = 2;
- static final int MONOSPACE = 3;
-
- // First value if for phone off; first value is "scanning"; following values
- // are battery stats signal strength buckets.
- static final int NUM_PHONE_SIGNALS = 7;
-
- final Paint mBatteryBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- final Paint mBatteryGoodPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- final Paint mBatteryWarnPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- final Paint mBatteryCriticalPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- final Paint mTimeRemainPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- final Paint mChargingPaint = new Paint();
- final Paint mScreenOnPaint = new Paint();
- final Paint mGpsOnPaint = new Paint();
- final Paint mFlashlightOnPaint = new Paint();
- final Paint mCameraOnPaint = new Paint();
- final Paint mWifiRunningPaint = new Paint();
- final Paint mCpuRunningPaint = new Paint();
- final Paint mDateLinePaint = new Paint();
- final ChartData mPhoneSignalChart = new ChartData();
- final TextPaint mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
- final TextPaint mHeaderTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
- final Paint mDebugRectPaint = new Paint();
-
- final Path mBatLevelPath = new Path();
- final Path mBatGoodPath = new Path();
- final Path mBatWarnPath = new Path();
- final Path mBatCriticalPath = new Path();
- final Path mTimeRemainPath = new Path();
- final Path mChargingPath = new Path();
- final Path mScreenOnPath = new Path();
- final Path mGpsOnPath = new Path();
- final Path mFlashlightOnPath = new Path();
- final Path mCameraOnPath = new Path();
- final Path mWifiRunningPath = new Path();
- final Path mCpuRunningPath = new Path();
- final Path mDateLinePath = new Path();
-
- BatteryStats mStats;
- Intent mBatteryBroadcast;
- long mStatsPeriod;
- String mMaxPercentLabelString;
- String mMinPercentLabelString;
- String mDurationString;
- String mChargeDurationString;
- String mDrainString;
- String mChargingLabel;
- String mScreenOnLabel;
- String mGpsOnLabel;
- String mCameraOnLabel;
- String mFlashlightOnLabel;
- String mWifiRunningLabel;
- String mCpuRunningLabel;
- String mPhoneSignalLabel;
-
- BatteryInfo mInfo;
-
- int mChartMinHeight;
- int mHeaderHeight;
-
- int mBatteryWarnLevel;
- int mBatteryCriticalLevel;
-
- int mTextAscent;
- int mTextDescent;
- int mHeaderTextAscent;
- int mHeaderTextDescent;
- int mMaxPercentLabelStringWidth;
- int mMinPercentLabelStringWidth;
- int mDurationStringWidth;
- int mChargeLabelStringWidth;
- int mChargeDurationStringWidth;
- int mDrainStringWidth;
-
- boolean mLargeMode;
-
- int mLastWidth = -1;
- int mLastHeight = -1;
-
- int mLineWidth;
- int mThinLineWidth;
- int mChargingOffset;
- int mScreenOnOffset;
- int mGpsOnOffset;
- int mFlashlightOnOffset;
- int mCameraOnOffset;
- int mWifiRunningOffset;
- int mCpuRunningOffset;
- int mPhoneSignalOffset;
- int mLevelOffset;
- int mLevelTop;
- int mLevelBottom;
- int mLevelLeft;
- int mLevelRight;
-
- int mNumHist;
- long mHistStart;
- long mHistDataEnd;
- long mHistEnd;
- long mStartWallTime;
- long mEndDataWallTime;
- long mEndWallTime;
- int mBatLow;
- int mBatHigh;
- boolean mHaveWifi;
- boolean mHaveGps;
- boolean mHavePhoneSignal;
- boolean mHaveCamera;
- boolean mHaveFlashlight;
-
- final ArrayList mTimeLabels = new ArrayList();
- final ArrayList mDateLabels = new ArrayList();
-
- Bitmap mBitmap;
- Canvas mCanvas;
-
- static class TextAttrs {
- ColorStateList textColor = null;
- int textSize = 15;
- int typefaceIndex = -1;
- int styleIndex = -1;
-
- void retrieve(Context context, TypedArray from, int index) {
- TypedArray appearance = null;
- int ap = from.getResourceId(index, -1);
- if (ap != -1) {
- appearance = context.obtainStyledAttributes(ap,
- com.android.internal.R.styleable.TextAppearance);
- }
- if (appearance != null) {
- int n = appearance.getIndexCount();
- for (int i = 0; i < n; i++) {
- int attr = appearance.getIndex(i);
-
- switch (attr) {
- case com.android.internal.R.styleable.TextAppearance_textColor:
- textColor = appearance.getColorStateList(attr);
- break;
-
- case com.android.internal.R.styleable.TextAppearance_textSize:
- textSize = appearance.getDimensionPixelSize(attr, textSize);
- break;
-
- case com.android.internal.R.styleable.TextAppearance_typeface:
- typefaceIndex = appearance.getInt(attr, -1);
- break;
-
- case com.android.internal.R.styleable.TextAppearance_textStyle:
- styleIndex = appearance.getInt(attr, -1);
- break;
- }
- }
-
- appearance.recycle();
- }
- }
-
- void apply(Context context, TextPaint paint) {
- paint.density = context.getResources().getDisplayMetrics().density;
- paint.setCompatibilityScaling(
- context.getResources().getCompatibilityInfo().applicationScale);
-
- paint.setColor(textColor.getDefaultColor());
- paint.setTextSize(textSize);
-
- Typeface tf = null;
- switch (typefaceIndex) {
- case SANS:
- tf = Typeface.SANS_SERIF;
- break;
-
- case SERIF:
- tf = Typeface.SERIF;
- break;
-
- case MONOSPACE:
- tf = Typeface.MONOSPACE;
- break;
- }
-
- setTypeface(paint, tf, styleIndex);
- }
-
- public void setTypeface(TextPaint paint, Typeface tf, int style) {
- if (style > 0) {
- if (tf == null) {
- tf = Typeface.defaultFromStyle(style);
- } else {
- tf = Typeface.create(tf, style);
- }
-
- paint.setTypeface(tf);
- // now compute what (if any) algorithmic styling is needed
- int typefaceStyle = tf != null ? tf.getStyle() : 0;
- int need = style & ~typefaceStyle;
- paint.setFakeBoldText((need & Typeface.BOLD) != 0);
- paint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
- } else {
- paint.setFakeBoldText(false);
- paint.setTextSkewX(0);
- paint.setTypeface(tf);
- }
- }
- }
-
- static class TimeLabel {
- final int x;
- final String label;
- final int width;
-
- TimeLabel(TextPaint paint, int x, Calendar cal, boolean use24hr) {
- this.x = x;
- final String bestFormat = DateFormat.getBestDateTimePattern(
- Locale.getDefault(), use24hr ? "km" : "ha");
- label = DateFormat.format(bestFormat, cal).toString();
- width = (int)paint.measureText(label);
- }
- }
-
- static class DateLabel {
- final int x;
- final String label;
- final int width;
-
- DateLabel(TextPaint paint, int x, Calendar cal, boolean dayFirst) {
- this.x = x;
- final String bestFormat = DateFormat.getBestDateTimePattern(
- Locale.getDefault(), dayFirst ? "dM" : "Md");
- label = DateFormat.format(bestFormat, cal).toString();
- width = (int)paint.measureText(label);
- }
- }
-
- public BatteryHistoryChart(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- if (DEBUG) Log.d(TAG, "New BatteryHistoryChart!");
-
- mBatteryWarnLevel = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_lowBatteryWarningLevel);
- mBatteryCriticalLevel = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_criticalBatteryWarningLevel);
-
- mThinLineWidth = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- 2, getResources().getDisplayMetrics());
-
- int accentColor = Utils.getColorAccentDefaultColor(mContext);
- mBatteryBackgroundPaint.setColor(accentColor);
- mBatteryBackgroundPaint.setStyle(Paint.Style.FILL);
- mBatteryGoodPaint.setARGB(128, 0, 128, 0);
- mBatteryGoodPaint.setStyle(Paint.Style.STROKE);
- mBatteryWarnPaint.setARGB(128, 128, 128, 0);
- mBatteryWarnPaint.setStyle(Paint.Style.STROKE);
- mBatteryCriticalPaint.setARGB(192, 128, 0, 0);
- mBatteryCriticalPaint.setStyle(Paint.Style.STROKE);
- mTimeRemainPaint.setColor(0xFFCED7BB);
- mTimeRemainPaint.setStyle(Paint.Style.FILL);
- mChargingPaint.setStyle(Paint.Style.STROKE);
- mScreenOnPaint.setStyle(Paint.Style.STROKE);
- mGpsOnPaint.setStyle(Paint.Style.STROKE);
- mCameraOnPaint.setStyle(Paint.Style.STROKE);
- mFlashlightOnPaint.setStyle(Paint.Style.STROKE);
- mWifiRunningPaint.setStyle(Paint.Style.STROKE);
- mCpuRunningPaint.setStyle(Paint.Style.STROKE);
- mPhoneSignalChart.setColors(com.android.settings.Utils.BADNESS_COLORS);
- mDebugRectPaint.setARGB(255, 255, 0, 0);
- mDebugRectPaint.setStyle(Paint.Style.STROKE);
- mScreenOnPaint.setColor(accentColor);
- mGpsOnPaint.setColor(accentColor);
- mCameraOnPaint.setColor(accentColor);
- mFlashlightOnPaint.setColor(accentColor);
- mWifiRunningPaint.setColor(accentColor);
- mCpuRunningPaint.setColor(accentColor);
- mChargingPaint.setColor(accentColor);
-
- TypedArray a =
- context.obtainStyledAttributes(
- attrs, R.styleable.BatteryHistoryChart, 0, 0);
-
- final TextAttrs mainTextAttrs = new TextAttrs();
- final TextAttrs headTextAttrs = new TextAttrs();
- mainTextAttrs.retrieve(context, a, R.styleable.BatteryHistoryChart_android_textAppearance);
- headTextAttrs.retrieve(context, a, R.styleable.BatteryHistoryChart_headerAppearance);
-
- int shadowcolor = 0;
- float dx=0, dy=0, r=0;
-
- int n = a.getIndexCount();
- for (int i = 0; i < n; i++) {
- int attr = a.getIndex(i);
-
- switch (attr) {
- case R.styleable.BatteryHistoryChart_android_shadowColor:
- shadowcolor = a.getInt(attr, 0);
- break;
-
- case R.styleable.BatteryHistoryChart_android_shadowDx:
- dx = a.getFloat(attr, 0);
- break;
-
- case R.styleable.BatteryHistoryChart_android_shadowDy:
- dy = a.getFloat(attr, 0);
- break;
-
- case R.styleable.BatteryHistoryChart_android_shadowRadius:
- r = a.getFloat(attr, 0);
- break;
-
- case R.styleable.BatteryHistoryChart_android_textColor:
- mainTextAttrs.textColor = a.getColorStateList(attr);
- headTextAttrs.textColor = a.getColorStateList(attr);
- break;
-
- case R.styleable.BatteryHistoryChart_android_textSize:
- mainTextAttrs.textSize = a.getDimensionPixelSize(attr, mainTextAttrs.textSize);
- headTextAttrs.textSize = a.getDimensionPixelSize(attr, headTextAttrs.textSize);
- break;
-
- case R.styleable.BatteryHistoryChart_android_typeface:
- mainTextAttrs.typefaceIndex = a.getInt(attr, mainTextAttrs.typefaceIndex);
- headTextAttrs.typefaceIndex = a.getInt(attr, headTextAttrs.typefaceIndex);
- break;
-
- case R.styleable.BatteryHistoryChart_android_textStyle:
- mainTextAttrs.styleIndex = a.getInt(attr, mainTextAttrs.styleIndex);
- headTextAttrs.styleIndex = a.getInt(attr, headTextAttrs.styleIndex);
- break;
-
- case R.styleable.BatteryHistoryChart_barPrimaryColor:
- mBatteryBackgroundPaint.setColor(a.getInt(attr, 0));
- mScreenOnPaint.setColor(a.getInt(attr, 0));
- mGpsOnPaint.setColor(a.getInt(attr, 0));
- mCameraOnPaint.setColor(a.getInt(attr, 0));
- mFlashlightOnPaint.setColor(a.getInt(attr, 0));
- mWifiRunningPaint.setColor(a.getInt(attr, 0));
- mCpuRunningPaint.setColor(a.getInt(attr, 0));
- mChargingPaint.setColor(a.getInt(attr, 0));
- break;
-
- case R.styleable.BatteryHistoryChart_barPredictionColor:
- mTimeRemainPaint.setColor(a.getInt(attr, 0));
- break;
-
- case R.styleable.BatteryHistoryChart_chartMinHeight:
- mChartMinHeight = a.getDimensionPixelSize(attr, 0);
- break;
- }
- }
-
- a.recycle();
-
- mainTextAttrs.apply(context, mTextPaint);
- headTextAttrs.apply(context, mHeaderTextPaint);
-
- mDateLinePaint.set(mTextPaint);
- mDateLinePaint.setStyle(Paint.Style.STROKE);
- int hairlineWidth = mThinLineWidth/2;
- if (hairlineWidth < 1) {
- hairlineWidth = 1;
- }
- mDateLinePaint.setStrokeWidth(hairlineWidth);
- mDateLinePaint.setPathEffect(new DashPathEffect(new float[] {
- mThinLineWidth * 2, mThinLineWidth * 2 }, 0));
-
- if (shadowcolor != 0) {
- mTextPaint.setShadowLayer(r, dx, dy, shadowcolor);
- mHeaderTextPaint.setShadowLayer(r, dx, dy, shadowcolor);
- }
- }
-
- void setStats(BatteryStats stats, Intent broadcast) {
- mStats = stats;
- mBatteryBroadcast = broadcast;
-
- if (DEBUG) Log.d(TAG, "Setting stats...");
-
- final long elapsedRealtimeUs = SystemClock.elapsedRealtime() * 1000;
-
- long uSecTime = mStats.computeBatteryRealtime(elapsedRealtimeUs,
- BatteryStats.STATS_SINCE_CHARGED);
- mStatsPeriod = uSecTime;
- mChargingLabel = getContext().getString(R.string.battery_stats_charging_label);
- mScreenOnLabel = getContext().getString(R.string.battery_stats_screen_on_label);
- mGpsOnLabel = getContext().getString(R.string.battery_stats_gps_on_label);
- mCameraOnLabel = getContext().getString(R.string.battery_stats_camera_on_label);
- mFlashlightOnLabel = getContext().getString(R.string.battery_stats_flashlight_on_label);
- mWifiRunningLabel = getContext().getString(R.string.battery_stats_wifi_running_label);
- mCpuRunningLabel = getContext().getString(R.string.battery_stats_wake_lock_label);
- mPhoneSignalLabel = getContext().getString(R.string.battery_stats_phone_signal_label);
-
- mMaxPercentLabelString = Utils.formatPercentage(100);
- mMinPercentLabelString = Utils.formatPercentage(0);
- BatteryInfo.getBatteryInfo(getContext(), info -> {
- mInfo = info;
- mDrainString = "";
- mChargeDurationString = "";
- setContentDescription(mInfo.chargeLabel);
-
- int pos = 0;
- int lastInteresting = 0;
- byte lastLevel = -1;
- mBatLow = 0;
- mBatHigh = 100;
- mStartWallTime = 0;
- mEndDataWallTime = 0;
- mEndWallTime = 0;
- mHistStart = 0;
- mHistEnd = 0;
- long lastWallTime = 0;
- long lastRealtime = 0;
- int aggrStates = 0;
- int aggrStates2 = 0;
- boolean first = true;
- if (stats.startIteratingHistoryLocked()) {
- final HistoryItem rec = new HistoryItem();
- while (stats.getNextHistoryLocked(rec)) {
- pos++;
- if (first) {
- first = false;
- mHistStart = rec.time;
- }
- if (rec.cmd == HistoryItem.CMD_CURRENT_TIME
- || rec.cmd == HistoryItem.CMD_RESET) {
- // If there is a ridiculously large jump in time, then we won't be
- // able to create a good chart with that data, so just ignore the
- // times we got before and pretend like our data extends back from
- // the time we have now.
- // Also, if we are getting a time change and we are less than 5 minutes
- // since the start of the history real time, then also use this new
- // time to compute the base time, since whatever time we had before is
- // pretty much just noise.
- if (rec.currentTime > (lastWallTime+(180*24*60*60*1000L))
- || rec.time < (mHistStart+(5*60*1000L))) {
- mStartWallTime = 0;
- }
- lastWallTime = rec.currentTime;
- lastRealtime = rec.time;
- if (mStartWallTime == 0) {
- mStartWallTime = lastWallTime - (lastRealtime-mHistStart);
- }
- }
- if (rec.isDeltaData()) {
- if (rec.batteryLevel != lastLevel || pos == 1) {
- lastLevel = rec.batteryLevel;
- }
- lastInteresting = pos;
- mHistDataEnd = rec.time;
- aggrStates |= rec.states;
- aggrStates2 |= rec.states2;
- }
- }
- }
- mHistEnd = mHistDataEnd + (mInfo.remainingTimeUs/1000);
- mEndDataWallTime = lastWallTime + mHistDataEnd - lastRealtime;
- mEndWallTime = mEndDataWallTime + (mInfo.remainingTimeUs/1000);
- mNumHist = lastInteresting;
- mHaveGps = (aggrStates&HistoryItem.STATE_GPS_ON_FLAG) != 0;
- mHaveFlashlight = (aggrStates2&HistoryItem.STATE2_FLASHLIGHT_FLAG) != 0;
- mHaveCamera = (aggrStates2&HistoryItem.STATE2_CAMERA_FLAG) != 0;
- mHaveWifi = (aggrStates2&HistoryItem.STATE2_WIFI_RUNNING_FLAG) != 0
- || (aggrStates&(HistoryItem.STATE_WIFI_FULL_LOCK_FLAG
- |HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG
- |HistoryItem.STATE_WIFI_SCAN_FLAG)) != 0;
- if (!com.android.settingslib.Utils.isWifiOnly(getContext())) {
- mHavePhoneSignal = true;
- }
- if (mHistEnd <= mHistStart) mHistEnd = mHistStart+1;
- }, mStats, false /* shortString */);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- mMaxPercentLabelStringWidth = (int)mTextPaint.measureText(mMaxPercentLabelString);
- mMinPercentLabelStringWidth = (int)mTextPaint.measureText(mMinPercentLabelString);
- mDrainStringWidth = (int)mHeaderTextPaint.measureText(mDrainString);
- mChargeLabelStringWidth = (int) mHeaderTextPaint.measureText(
- mInfo.chargeLabel.toString());
- mChargeDurationStringWidth = (int)mHeaderTextPaint.measureText(mChargeDurationString);
- mTextAscent = (int)mTextPaint.ascent();
- mTextDescent = (int)mTextPaint.descent();
- mHeaderTextAscent = (int)mHeaderTextPaint.ascent();
- mHeaderTextDescent = (int)mHeaderTextPaint.descent();
- int headerTextHeight = mHeaderTextDescent - mHeaderTextAscent;
- mHeaderHeight = headerTextHeight*2 - mTextAscent;
- setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
- getDefaultSize(mChartMinHeight+mHeaderHeight, heightMeasureSpec));
- }
-
- void finishPaths(int w, int h, int levelh, int startX, int y, Path curLevelPath,
- int lastX, boolean lastCharging, boolean lastScreenOn, boolean lastGpsOn,
- boolean lastFlashlightOn, boolean lastCameraOn, boolean lastWifiRunning,
- boolean lastCpuRunning, Path lastPath) {
- if (curLevelPath != null) {
- if (lastX >= 0 && lastX < w) {
- if (lastPath != null) {
- lastPath.lineTo(w, y);
- }
- curLevelPath.lineTo(w, y);
- }
- curLevelPath.lineTo(w, mLevelTop+levelh);
- curLevelPath.lineTo(startX, mLevelTop+levelh);
- curLevelPath.close();
- }
-
- if (lastCharging) {
- mChargingPath.lineTo(w, h-mChargingOffset);
- }
- if (lastScreenOn) {
- mScreenOnPath.lineTo(w, h-mScreenOnOffset);
- }
- if (lastGpsOn) {
- mGpsOnPath.lineTo(w, h-mGpsOnOffset);
- }
- if (lastFlashlightOn) {
- mFlashlightOnPath.lineTo(w, h-mFlashlightOnOffset);
- }
- if (lastCameraOn) {
- mCameraOnPath.lineTo(w, h-mCameraOnOffset);
- }
- if (lastWifiRunning) {
- mWifiRunningPath.lineTo(w, h-mWifiRunningOffset);
- }
- if (lastCpuRunning) {
- mCpuRunningPath.lineTo(w, h - mCpuRunningOffset);
- }
- if (mHavePhoneSignal) {
- mPhoneSignalChart.finish(w);
- }
- }
-
- private boolean is24Hour() {
- return DateFormat.is24HourFormat(getContext());
- }
-
- private boolean isDayFirst() {
- final String value = LocaleData.get(getResources().getConfiguration().locale)
- .getDateFormat(java.text.DateFormat.SHORT);
- return value.indexOf('M') > value.indexOf('d');
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
-
- if (DEBUG) Log.d(TAG, "onSizeChanged: " + oldw + "x" + oldh + " to " + w + "x" + h);
-
- if (mLastWidth == w && mLastHeight == h) {
- return;
- }
-
- if (mLastWidth == 0 || mLastHeight == 0) {
- return;
- }
-
- if (DEBUG) Log.d(TAG, "Rebuilding chart for: " + w + "x" + h);
-
- mLastWidth = w;
- mLastHeight = h;
- mBitmap = null;
- mCanvas = null;
-
- int textHeight = mTextDescent - mTextAscent;
- if (h > ((textHeight*10)+mChartMinHeight)) {
- mLargeMode = true;
- if (h > (textHeight*15)) {
- // Plenty of room for the chart.
- mLineWidth = textHeight/2;
- } else {
- // Compress lines to make more room for chart.
- mLineWidth = textHeight/3;
- }
- } else {
- mLargeMode = false;
- mLineWidth = mThinLineWidth;
- }
- if (mLineWidth <= 0) mLineWidth = 1;
-
- mLevelTop = mHeaderHeight;
- mLevelLeft = mMaxPercentLabelStringWidth + mThinLineWidth*3;
- mLevelRight = w;
- int levelWidth = mLevelRight-mLevelLeft;
-
- mTextPaint.setStrokeWidth(mThinLineWidth);
- mBatteryGoodPaint.setStrokeWidth(mThinLineWidth);
- mBatteryWarnPaint.setStrokeWidth(mThinLineWidth);
- mBatteryCriticalPaint.setStrokeWidth(mThinLineWidth);
- mChargingPaint.setStrokeWidth(mLineWidth);
- mScreenOnPaint.setStrokeWidth(mLineWidth);
- mGpsOnPaint.setStrokeWidth(mLineWidth);
- mCameraOnPaint.setStrokeWidth(mLineWidth);
- mFlashlightOnPaint.setStrokeWidth(mLineWidth);
- mWifiRunningPaint.setStrokeWidth(mLineWidth);
- mCpuRunningPaint.setStrokeWidth(mLineWidth);
- mDebugRectPaint.setStrokeWidth(1);
-
- int fullBarOffset = textHeight + mLineWidth;
-
- if (mLargeMode) {
- mChargingOffset = mLineWidth;
- mScreenOnOffset = mChargingOffset + fullBarOffset;
- mCpuRunningOffset = mScreenOnOffset + fullBarOffset;
- mWifiRunningOffset = mCpuRunningOffset + fullBarOffset;
- mGpsOnOffset = mWifiRunningOffset + (mHaveWifi ? fullBarOffset : 0);
- mFlashlightOnOffset = mGpsOnOffset + (mHaveGps ? fullBarOffset : 0);
- mCameraOnOffset = mFlashlightOnOffset + (mHaveFlashlight ? fullBarOffset : 0);
- mPhoneSignalOffset = mCameraOnOffset + (mHaveCamera ? fullBarOffset : 0);
- mLevelOffset = mPhoneSignalOffset + (mHavePhoneSignal ? fullBarOffset : 0)
- + mLineWidth*2 + mLineWidth/2;
- if (mHavePhoneSignal) {
- mPhoneSignalChart.init(w);
- }
- } else {
- mScreenOnOffset = mGpsOnOffset = mCameraOnOffset = mFlashlightOnOffset =
- mWifiRunningOffset = mCpuRunningOffset = mChargingOffset =
- mPhoneSignalOffset = 0;
- mLevelOffset = fullBarOffset + mThinLineWidth*4;
- if (mHavePhoneSignal) {
- mPhoneSignalChart.init(0);
- }
- }
-
- mBatLevelPath.reset();
- mBatGoodPath.reset();
- mBatWarnPath.reset();
- mTimeRemainPath.reset();
- mBatCriticalPath.reset();
- mScreenOnPath.reset();
- mGpsOnPath.reset();
- mFlashlightOnPath.reset();
- mCameraOnPath.reset();
- mWifiRunningPath.reset();
- mCpuRunningPath.reset();
- mChargingPath.reset();
-
- mTimeLabels.clear();
- mDateLabels.clear();
-
- final long walltimeStart = mStartWallTime;
- final long walltimeChange = mEndWallTime > walltimeStart
- ? (mEndWallTime-walltimeStart) : 1;
- long curWalltime = mStartWallTime;
- long lastRealtime = 0;
-
- final int batLow = mBatLow;
- final int batChange = mBatHigh-mBatLow;
-
- final int levelh = h - mLevelOffset - mLevelTop;
- mLevelBottom = mLevelTop + levelh;
-
- int x = mLevelLeft, y = 0, startX = mLevelLeft, lastX = -1, lastY = -1;
- int i = 0;
- Path curLevelPath = null;
- Path lastLinePath = null;
- boolean lastCharging = false, lastScreenOn = false, lastGpsOn = false;
- boolean lastFlashlightOn = false, lastCameraOn = false;
- boolean lastWifiRunning = false, lastWifiSupplRunning = false, lastCpuRunning = false;
- int lastWifiSupplState = BatteryStats.WIFI_SUPPL_STATE_INVALID;
- final int N = mNumHist;
- if (mEndDataWallTime > mStartWallTime && mStats.startIteratingHistoryLocked()) {
- final HistoryItem rec = new HistoryItem();
- while (mStats.getNextHistoryLocked(rec) && i < N) {
- if (rec.isDeltaData()) {
- curWalltime += rec.time-lastRealtime;
- lastRealtime = rec.time;
- x = mLevelLeft + (int)(((curWalltime-walltimeStart)*levelWidth)/walltimeChange);
- if (x < 0) {
- x = 0;
- }
- if (false) {
- StringBuilder sb = new StringBuilder(128);
- sb.append("walloff=");
- TimeUtils.formatDuration(curWalltime - walltimeStart, sb);
- sb.append(" wallchange=");
- TimeUtils.formatDuration(walltimeChange, sb);
- sb.append(" x=");
- sb.append(x);
- Log.d("foo", sb.toString());
- }
- y = mLevelTop + levelh - ((rec.batteryLevel-batLow)*(levelh-1))/batChange;
-
- if (lastX != x) {
- // We have moved by at least a pixel.
- if (lastY != y) {
- // Don't plot changes within a pixel.
- Path path;
- byte value = rec.batteryLevel;
- if (value <= mBatteryCriticalLevel) path = mBatCriticalPath;
- else if (value <= mBatteryWarnLevel) path = mBatWarnPath;
- else path = null; //mBatGoodPath;
-
- if (path != lastLinePath) {
- if (lastLinePath != null) {
- lastLinePath.lineTo(x, y);
- }
- if (path != null) {
- path.moveTo(x, y);
- }
- lastLinePath = path;
- } else if (path != null) {
- path.lineTo(x, y);
- }
-
- if (curLevelPath == null) {
- curLevelPath = mBatLevelPath;
- curLevelPath.moveTo(x, y);
- startX = x;
- } else {
- curLevelPath.lineTo(x, y);
- }
- lastX = x;
- lastY = y;
- }
- }
-
- if (mLargeMode) {
- final boolean charging =
- (rec.states&HistoryItem.STATE_BATTERY_PLUGGED_FLAG) != 0;
- if (charging != lastCharging) {
- if (charging) {
- mChargingPath.moveTo(x, h-mChargingOffset);
- } else {
- mChargingPath.lineTo(x, h-mChargingOffset);
- }
- lastCharging = charging;
- }
-
- final boolean screenOn =
- (rec.states&HistoryItem.STATE_SCREEN_ON_FLAG) != 0;
- if (screenOn != lastScreenOn) {
- if (screenOn) {
- mScreenOnPath.moveTo(x, h-mScreenOnOffset);
- } else {
- mScreenOnPath.lineTo(x, h-mScreenOnOffset);
- }
- lastScreenOn = screenOn;
- }
-
- final boolean gpsOn =
- (rec.states&HistoryItem.STATE_GPS_ON_FLAG) != 0;
- if (gpsOn != lastGpsOn) {
- if (gpsOn) {
- mGpsOnPath.moveTo(x, h-mGpsOnOffset);
- } else {
- mGpsOnPath.lineTo(x, h-mGpsOnOffset);
- }
- lastGpsOn = gpsOn;
- }
-
- final boolean flashlightOn =
- (rec.states2&HistoryItem.STATE2_FLASHLIGHT_FLAG) != 0;
- if (flashlightOn != lastFlashlightOn) {
- if (flashlightOn) {
- mFlashlightOnPath.moveTo(x, h-mFlashlightOnOffset);
- } else {
- mFlashlightOnPath.lineTo(x, h-mFlashlightOnOffset);
- }
- lastFlashlightOn = flashlightOn;
- }
-
- final boolean cameraOn =
- (rec.states2&HistoryItem.STATE2_CAMERA_FLAG) != 0;
- if (cameraOn != lastCameraOn) {
- if (cameraOn) {
- mCameraOnPath.moveTo(x, h-mCameraOnOffset);
- } else {
- mCameraOnPath.lineTo(x, h-mCameraOnOffset);
- }
- lastCameraOn = cameraOn;
- }
-
- final int wifiSupplState =
- ((rec.states2&HistoryItem.STATE2_WIFI_SUPPL_STATE_MASK)
- >> HistoryItem.STATE2_WIFI_SUPPL_STATE_SHIFT);
- boolean wifiRunning;
- if (lastWifiSupplState != wifiSupplState) {
- lastWifiSupplState = wifiSupplState;
- switch (wifiSupplState) {
- case BatteryStats.WIFI_SUPPL_STATE_DISCONNECTED:
- case BatteryStats.WIFI_SUPPL_STATE_DORMANT:
- case BatteryStats.WIFI_SUPPL_STATE_INACTIVE:
- case BatteryStats.WIFI_SUPPL_STATE_INTERFACE_DISABLED:
- case BatteryStats.WIFI_SUPPL_STATE_INVALID:
- case BatteryStats.WIFI_SUPPL_STATE_UNINITIALIZED:
- wifiRunning = lastWifiSupplRunning = false;
- break;
- default:
- wifiRunning = lastWifiSupplRunning = true;
- break;
- }
- } else {
- wifiRunning = lastWifiSupplRunning;
- }
- if ((rec.states&(HistoryItem.STATE_WIFI_FULL_LOCK_FLAG
- |HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG
- |HistoryItem.STATE_WIFI_SCAN_FLAG)) != 0) {
- wifiRunning = true;
- }
- if (wifiRunning != lastWifiRunning) {
- if (wifiRunning) {
- mWifiRunningPath.moveTo(x, h-mWifiRunningOffset);
- } else {
- mWifiRunningPath.lineTo(x, h-mWifiRunningOffset);
- }
- lastWifiRunning = wifiRunning;
- }
-
- final boolean cpuRunning =
- (rec.states&HistoryItem.STATE_CPU_RUNNING_FLAG) != 0;
- if (cpuRunning != lastCpuRunning) {
- if (cpuRunning) {
- mCpuRunningPath.moveTo(x, h - mCpuRunningOffset);
- } else {
- mCpuRunningPath.lineTo(x, h - mCpuRunningOffset);
- }
- lastCpuRunning = cpuRunning;
- }
-
- if (mLargeMode && mHavePhoneSignal) {
- int bin;
- if (((rec.states&HistoryItem.STATE_PHONE_STATE_MASK)
- >> HistoryItem.STATE_PHONE_STATE_SHIFT)
- == ServiceState.STATE_POWER_OFF) {
- bin = 0;
- } else if ((rec.states&HistoryItem.STATE_PHONE_SCANNING_FLAG) != 0) {
- bin = 1;
- } else {
- bin = (rec.states&HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_MASK)
- >> HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_SHIFT;
- bin += 2;
- }
- mPhoneSignalChart.addTick(x, bin);
- }
- }
-
- } else {
- long lastWalltime = curWalltime;
- if (rec.cmd == HistoryItem.CMD_CURRENT_TIME
- || rec.cmd == HistoryItem.CMD_RESET) {
- if (rec.currentTime >= mStartWallTime) {
- curWalltime = rec.currentTime;
- } else {
- curWalltime = mStartWallTime + (rec.time-mHistStart);
- }
- lastRealtime = rec.time;
- }
-
- if (rec.cmd != HistoryItem.CMD_OVERFLOW
- && (rec.cmd != HistoryItem.CMD_CURRENT_TIME
- || Math.abs(lastWalltime-curWalltime) > (60*60*1000))) {
- if (curLevelPath != null) {
- finishPaths(x+1, h, levelh, startX, lastY, curLevelPath, lastX,
- lastCharging, lastScreenOn, lastGpsOn, lastFlashlightOn,
- lastCameraOn, lastWifiRunning, lastCpuRunning, lastLinePath);
- lastX = lastY = -1;
- curLevelPath = null;
- lastLinePath = null;
- lastCharging = lastScreenOn = lastGpsOn = lastFlashlightOn =
- lastCameraOn = lastCpuRunning = false;
- }
- }
- }
-
- i++;
- }
- mStats.finishIteratingHistoryLocked();
- }
-
- if (lastY < 0 || lastX < 0) {
- // Didn't get any data...
- x = lastX = mLevelLeft;
- y = lastY = mLevelTop + levelh - ((mInfo.batteryLevel -batLow)*(levelh-1))/batChange;
- Path path;
- byte value = (byte)mInfo.batteryLevel;
- if (value <= mBatteryCriticalLevel) path = mBatCriticalPath;
- else if (value <= mBatteryWarnLevel) path = mBatWarnPath;
- else path = null; //mBatGoodPath;
- if (path != null) {
- path.moveTo(x, y);
- lastLinePath = path;
- }
- mBatLevelPath.moveTo(x, y);
- curLevelPath = mBatLevelPath;
- x = w;
- } else {
- // Figure out where the actual data ends on the screen.
- x = mLevelLeft + (int)(((mEndDataWallTime-walltimeStart)*levelWidth)/walltimeChange);
- if (x < 0) {
- x = 0;
- }
- }
-
- finishPaths(x, h, levelh, startX, lastY, curLevelPath, lastX,
- lastCharging, lastScreenOn, lastGpsOn, lastFlashlightOn, lastCameraOn,
- lastWifiRunning, lastCpuRunning, lastLinePath);
-
- if (x < w) {
- // If we reserved room for the remaining time, create a final path to draw
- // that part of the UI.
- mTimeRemainPath.moveTo(x, lastY);
- int fullY = mLevelTop + levelh - ((100-batLow)*(levelh-1))/batChange;
- int emptyY = mLevelTop + levelh - ((0-batLow)*(levelh-1))/batChange;
- if (mInfo.discharging) {
- mTimeRemainPath.lineTo(mLevelRight, emptyY);
- } else {
- mTimeRemainPath.lineTo(mLevelRight, fullY);
- mTimeRemainPath.lineTo(mLevelRight, emptyY);
- }
- mTimeRemainPath.lineTo(x, emptyY);
- mTimeRemainPath.close();
- }
-
- if (mStartWallTime > 0 && mEndWallTime > mStartWallTime) {
- // Create the time labels at the bottom.
- boolean is24hr = is24Hour();
- Calendar calStart = Calendar.getInstance();
- calStart.setTimeInMillis(mStartWallTime);
- calStart.set(Calendar.MILLISECOND, 0);
- calStart.set(Calendar.SECOND, 0);
- calStart.set(Calendar.MINUTE, 0);
- long startRoundTime = calStart.getTimeInMillis();
- if (startRoundTime < mStartWallTime) {
- calStart.set(Calendar.HOUR_OF_DAY, calStart.get(Calendar.HOUR_OF_DAY)+1);
- startRoundTime = calStart.getTimeInMillis();
- }
- Calendar calEnd = Calendar.getInstance();
- calEnd.setTimeInMillis(mEndWallTime);
- calEnd.set(Calendar.MILLISECOND, 0);
- calEnd.set(Calendar.SECOND, 0);
- calEnd.set(Calendar.MINUTE, 0);
- long endRoundTime = calEnd.getTimeInMillis();
- if (startRoundTime < endRoundTime) {
- addTimeLabel(calStart, mLevelLeft, mLevelRight, is24hr);
- Calendar calMid = Calendar.getInstance();
- calMid.setTimeInMillis(mStartWallTime+((mEndWallTime-mStartWallTime)/2));
- calMid.set(Calendar.MILLISECOND, 0);
- calMid.set(Calendar.SECOND, 0);
- calMid.set(Calendar.MINUTE, 0);
- long calMidMillis = calMid.getTimeInMillis();
- if (calMidMillis > startRoundTime && calMidMillis < endRoundTime) {
- addTimeLabel(calMid, mLevelLeft, mLevelRight, is24hr);
- }
- addTimeLabel(calEnd, mLevelLeft, mLevelRight, is24hr);
- }
-
- // Create the date labels if the chart includes multiple days
- if (calStart.get(Calendar.DAY_OF_YEAR) != calEnd.get(Calendar.DAY_OF_YEAR) ||
- calStart.get(Calendar.YEAR) != calEnd.get(Calendar.YEAR)) {
- boolean isDayFirst = isDayFirst();
- calStart.set(Calendar.HOUR_OF_DAY, 0);
- startRoundTime = calStart.getTimeInMillis();
- if (startRoundTime < mStartWallTime) {
- calStart.set(Calendar.DAY_OF_YEAR, calStart.get(Calendar.DAY_OF_YEAR) + 1);
- startRoundTime = calStart.getTimeInMillis();
- }
- calEnd.set(Calendar.HOUR_OF_DAY, 0);
- endRoundTime = calEnd.getTimeInMillis();
- if (startRoundTime < endRoundTime) {
- addDateLabel(calStart, mLevelLeft, mLevelRight, isDayFirst);
- Calendar calMid = Calendar.getInstance();
-
- // The middle between two beginnings of days can be anywhere between -1 to 13
- // after the beginning of the "median" day.
- calMid.setTimeInMillis(startRoundTime + ((endRoundTime - startRoundTime) / 2)
- + 2 * 60 * 60 * 1000);
- calMid.set(Calendar.HOUR_OF_DAY, 0);
- calMid.set(Calendar.MINUTE, 0);
- long calMidMillis = calMid.getTimeInMillis();
- if (calMidMillis > startRoundTime && calMidMillis < endRoundTime) {
- addDateLabel(calMid, mLevelLeft, mLevelRight, isDayFirst);
- }
- }
- addDateLabel(calEnd, mLevelLeft, mLevelRight, isDayFirst);
- }
- }
-
- if (mTimeLabels.size() < 2) {
- // If there are fewer than 2 time labels, then they are useless. Just
- // show an axis label giving the entire duration.
- mDurationString = Formatter.formatShortElapsedTime(getContext(),
- mEndWallTime - mStartWallTime);
- mDurationStringWidth = (int)mTextPaint.measureText(mDurationString);
- } else {
- mDurationString = null;
- mDurationStringWidth = 0;
- }
- }
-
- void addTimeLabel(Calendar cal, int levelLeft, int levelRight, boolean is24hr) {
- final long walltimeStart = mStartWallTime;
- final long walltimeChange = mEndWallTime-walltimeStart;
- mTimeLabels.add(new TimeLabel(mTextPaint,
- levelLeft + (int)(((cal.getTimeInMillis()-walltimeStart)*(levelRight-levelLeft))
- / walltimeChange),
- cal, is24hr));
- }
-
- void addDateLabel(Calendar cal, int levelLeft, int levelRight, boolean isDayFirst) {
- final long walltimeStart = mStartWallTime;
- final long walltimeChange = mEndWallTime-walltimeStart;
- mDateLabels.add(new DateLabel(mTextPaint,
- levelLeft + (int)(((cal.getTimeInMillis()-walltimeStart)*(levelRight-levelLeft))
- / walltimeChange),
- cal, isDayFirst));
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- final int width = getWidth();
- final int height = getHeight();
-
- //buildBitmap(width, height);
-
- if (DEBUG) Log.d(TAG, "onDraw: " + width + "x" + height);
- //canvas.drawBitmap(mBitmap, 0, 0, null);
- drawChart(canvas, width, height);
- }
-
- void buildBitmap(int width, int height) {
- if (mBitmap != null && width == mBitmap.getWidth() && height == mBitmap.getHeight()) {
- return;
- }
-
- if (DEBUG) Log.d(TAG, "buildBitmap: " + width + "x" + height);
-
- mBitmap = Bitmap.createBitmap(getResources().getDisplayMetrics(), width, height,
- Bitmap.Config.ARGB_8888);
- mCanvas = new Canvas(mBitmap);
- drawChart(mCanvas, width, height);
- }
-
- void drawChart(Canvas canvas, int width, int height) {
- final boolean layoutRtl = isLayoutRtl();
- final int textStartX = layoutRtl ? width : 0;
- final int textEndX = layoutRtl ? 0 : width;
- final Paint.Align textAlignLeft = layoutRtl ? Paint.Align.RIGHT : Paint.Align.LEFT;
- final Paint.Align textAlignRight = layoutRtl ? Paint.Align.LEFT : Paint.Align.RIGHT;
-
- if (DEBUG) {
- canvas.drawRect(1, 1, width, height, mDebugRectPaint);
- }
-
- if (DEBUG) Log.d(TAG, "Drawing level path.");
- canvas.drawPath(mBatLevelPath, mBatteryBackgroundPaint);
- if (!mTimeRemainPath.isEmpty()) {
- if (DEBUG) Log.d(TAG, "Drawing time remain path.");
- canvas.drawPath(mTimeRemainPath, mTimeRemainPaint);
- }
- if (mTimeLabels.size() > 1) {
- int y = mLevelBottom - mTextAscent + (mThinLineWidth*4);
- int ytick = mLevelBottom+mThinLineWidth+(mThinLineWidth/2);
- mTextPaint.setTextAlign(Paint.Align.LEFT);
- int lastX = 0;
- for (int i=0; i (width-nextLabel.width-mTextAscent)) {
- continue;
- }
- if (DEBUG) Log.d(TAG, "Drawing middle label: " + label.label + " @ " + x);
- canvas.drawText(label.label, x, y, mTextPaint);
- canvas.drawLine(label.x, ytick, label.x, ytick + mThinLineWidth, mTextPaint);
- lastX = x + label.width;
- } else {
- int x = label.x - label.width/2;
- if ((x+label.width) >= width) {
- x = width-1-label.width;
- }
- if (DEBUG) Log.d(TAG, "Drawing right label: " + label.label + " @ " + x);
- canvas.drawText(label.label, x, y, mTextPaint);
- canvas.drawLine(label.x, ytick, label.x, ytick+mThinLineWidth, mTextPaint);
- }
- }
- } else if (mDurationString != null) {
- int y = mLevelBottom - mTextAscent + (mThinLineWidth*4);
- mTextPaint.setTextAlign(Paint.Align.LEFT);
- canvas.drawText(mDurationString,
- mLevelLeft + (mLevelRight-mLevelLeft)/2 - mDurationStringWidth/2,
- y, mTextPaint);
- }
-
- int headerTop = -mHeaderTextAscent + (mHeaderTextDescent-mHeaderTextAscent)/3;
- mHeaderTextPaint.setTextAlign(textAlignLeft);
- if (DEBUG) Log.d(TAG, "Drawing charge label string: " + mInfo.chargeLabel);
- canvas.drawText(mInfo.chargeLabel.toString(), textStartX, headerTop,
- mHeaderTextPaint);
- int stringHalfWidth = mChargeDurationStringWidth / 2;
- if (layoutRtl) stringHalfWidth = -stringHalfWidth;
- int headerCenter = ((width-mChargeDurationStringWidth-mDrainStringWidth)/2)
- + (layoutRtl ? mDrainStringWidth : mChargeLabelStringWidth);
- if (DEBUG) Log.d(TAG, "Drawing charge duration string: " + mChargeDurationString);
- canvas.drawText(mChargeDurationString, headerCenter - stringHalfWidth, headerTop,
- mHeaderTextPaint);
- mHeaderTextPaint.setTextAlign(textAlignRight);
- if (DEBUG) Log.d(TAG, "Drawing drain string: " + mDrainString);
- canvas.drawText(mDrainString, textEndX, headerTop, mHeaderTextPaint);
-
- if (!mBatGoodPath.isEmpty()) {
- if (DEBUG) Log.d(TAG, "Drawing good battery path");
- canvas.drawPath(mBatGoodPath, mBatteryGoodPaint);
- }
- if (!mBatWarnPath.isEmpty()) {
- if (DEBUG) Log.d(TAG, "Drawing warn battery path");
- canvas.drawPath(mBatWarnPath, mBatteryWarnPaint);
- }
- if (!mBatCriticalPath.isEmpty()) {
- if (DEBUG) Log.d(TAG, "Drawing critical battery path");
- canvas.drawPath(mBatCriticalPath, mBatteryCriticalPaint);
- }
- if (mHavePhoneSignal) {
- if (DEBUG) Log.d(TAG, "Drawing phone signal path");
- int top = height-mPhoneSignalOffset - (mLineWidth/2);
- mPhoneSignalChart.draw(canvas, top, mLineWidth);
- }
- if (!mScreenOnPath.isEmpty()) {
- if (DEBUG) Log.d(TAG, "Drawing screen on path");
- canvas.drawPath(mScreenOnPath, mScreenOnPaint);
- }
- if (!mChargingPath.isEmpty()) {
- if (DEBUG) Log.d(TAG, "Drawing charging path");
- canvas.drawPath(mChargingPath, mChargingPaint);
- }
- if (mHaveGps) {
- if (!mGpsOnPath.isEmpty()) {
- if (DEBUG) Log.d(TAG, "Drawing gps path");
- canvas.drawPath(mGpsOnPath, mGpsOnPaint);
- }
- }
- if (mHaveFlashlight) {
- if (!mFlashlightOnPath.isEmpty()) {
- if (DEBUG) Log.d(TAG, "Drawing flashlight path");
- canvas.drawPath(mFlashlightOnPath, mFlashlightOnPaint);
- }
- }
- if (mHaveCamera) {
- if (!mCameraOnPath.isEmpty()) {
- if (DEBUG) Log.d(TAG, "Drawing camera path");
- canvas.drawPath(mCameraOnPath, mCameraOnPaint);
- }
- }
- if (mHaveWifi) {
- if (!mWifiRunningPath.isEmpty()) {
- if (DEBUG) Log.d(TAG, "Drawing wifi path");
- canvas.drawPath(mWifiRunningPath, mWifiRunningPaint);
- }
- }
- if (!mCpuRunningPath.isEmpty()) {
- if (DEBUG) Log.d(TAG, "Drawing running path");
- canvas.drawPath(mCpuRunningPath, mCpuRunningPaint);
- }
-
- if (mLargeMode) {
- if (DEBUG) Log.d(TAG, "Drawing large mode labels");
- Paint.Align align = mTextPaint.getTextAlign();
- mTextPaint.setTextAlign(textAlignLeft); // large-mode labels always aligned to start
- if (mHavePhoneSignal) {
- canvas.drawText(mPhoneSignalLabel, textStartX,
- height - mPhoneSignalOffset - mTextDescent, mTextPaint);
- }
- if (mHaveGps) {
- canvas.drawText(mGpsOnLabel, textStartX,
- height - mGpsOnOffset - mTextDescent, mTextPaint);
- }
- if (mHaveFlashlight) {
- canvas.drawText(mFlashlightOnLabel, textStartX,
- height - mFlashlightOnOffset - mTextDescent, mTextPaint);
- }
- if (mHaveCamera) {
- canvas.drawText(mCameraOnLabel, textStartX,
- height - mCameraOnOffset - mTextDescent, mTextPaint);
- }
- if (mHaveWifi) {
- canvas.drawText(mWifiRunningLabel, textStartX,
- height - mWifiRunningOffset - mTextDescent, mTextPaint);
- }
- canvas.drawText(mCpuRunningLabel, textStartX,
- height - mCpuRunningOffset - mTextDescent, mTextPaint);
- canvas.drawText(mChargingLabel, textStartX,
- height - mChargingOffset - mTextDescent, mTextPaint);
- canvas.drawText(mScreenOnLabel, textStartX,
- height - mScreenOnOffset - mTextDescent, mTextPaint);
- mTextPaint.setTextAlign(align);
- }
-
- canvas.drawLine(mLevelLeft-mThinLineWidth, mLevelTop, mLevelLeft-mThinLineWidth,
- mLevelBottom+(mThinLineWidth/2), mTextPaint);
- if (mLargeMode) {
- for (int i=0; i<10; i++) {
- int y = mLevelTop + mThinLineWidth/2 + ((mLevelBottom-mLevelTop)*i)/10;
- canvas.drawLine(mLevelLeft-mThinLineWidth*2-mThinLineWidth/2, y,
- mLevelLeft-mThinLineWidth-mThinLineWidth/2, y, mTextPaint);
- }
- }
- if (DEBUG) Log.d(TAG, "Drawing max percent, origw=" + mMaxPercentLabelStringWidth
- + ", noww=" + (int)mTextPaint.measureText(mMaxPercentLabelString));
- canvas.drawText(mMaxPercentLabelString, 0, mLevelTop, mTextPaint);
- canvas.drawText(mMinPercentLabelString,
- mMaxPercentLabelStringWidth-mMinPercentLabelStringWidth,
- mLevelBottom - mThinLineWidth, mTextPaint);
- canvas.drawLine(mLevelLeft/2, mLevelBottom+mThinLineWidth, width,
- mLevelBottom+mThinLineWidth, mTextPaint);
-
- if (mDateLabels.size() > 0) {
- int ytop = mLevelTop + mTextAscent;
- int ybottom = mLevelBottom;
- int lastLeft = mLevelRight;
- mTextPaint.setTextAlign(Paint.Align.LEFT);
- for (int i=mDateLabels.size()-1; i>=0; i--) {
- DateLabel label = mDateLabels.get(i);
- int left = label.x - mThinLineWidth;
- int x = label.x + mThinLineWidth*2;
- if ((x+label.width) >= lastLeft) {
- x = label.x - mThinLineWidth*2 - label.width;
- left = x - mThinLineWidth;
- if (left >= lastLeft) {
- // okay we give up.
- continue;
- }
- }
- if (left < mLevelLeft) {
- // Won't fit on left, give up.
- continue;
- }
- mDateLinePath.reset();
- mDateLinePath.moveTo(label.x, ytop);
- mDateLinePath.lineTo(label.x, ybottom);
- canvas.drawPath(mDateLinePath, mDateLinePaint);
- canvas.drawText(label.label, x, ytop - mTextAscent, mTextPaint);
- }
- }
- }
-}
diff --git a/src/com/android/settings/fuelgauge/BatteryHistoryDetail.java b/src/com/android/settings/fuelgauge/BatteryHistoryDetail.java
deleted file mode 100644
index 1343fefca60..00000000000
--- a/src/com/android/settings/fuelgauge/BatteryHistoryDetail.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2009 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.settings.fuelgauge;
-
-import android.content.Intent;
-import android.os.BatteryStats;
-import android.os.BatteryStats.HistoryItem;
-import android.os.Bundle;
-import android.util.TypedValue;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.os.BatteryStatsHelper;
-import com.android.settings.R;
-import com.android.settings.SettingsPreferenceFragment;
-import com.android.settings.fuelgauge.BatteryActiveView.BatteryActiveProvider;
-import com.android.settings.widget.UsageView;
-
-public class BatteryHistoryDetail extends SettingsPreferenceFragment {
- public static final String EXTRA_STATS = "stats";
- public static final String EXTRA_BROADCAST = "broadcast";
- public static final String BATTERY_HISTORY_FILE = "tmp_bat_history.bin";
-
- private BatteryStats mStats;
- private Intent mBatteryBroadcast;
-
- private BatteryFlagParser mChargingParser;
- private BatteryFlagParser mScreenOn;
- private BatteryFlagParser mGpsParser;
- private BatteryFlagParser mFlashlightParser;
- private BatteryFlagParser mCameraParser;
- private BatteryWifiParser mWifiParser;
- private BatteryFlagParser mCpuParser;
- private BatteryCellParser mPhoneParser;
-
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- String histFile = getArguments().getString(EXTRA_STATS);
- mStats = BatteryStatsHelper.statsFromFile(getActivity(), histFile);
- mBatteryBroadcast = getArguments().getParcelable(EXTRA_BROADCAST);
-
- TypedValue value = new TypedValue();
- getContext().getTheme().resolveAttribute(android.R.attr.colorAccent, value, true);
- int accentColor = getContext().getColor(value.resourceId);
-
- mChargingParser = new BatteryFlagParser(accentColor, false,
- HistoryItem.STATE_BATTERY_PLUGGED_FLAG);
- mScreenOn = new BatteryFlagParser(accentColor, false,
- HistoryItem.STATE_SCREEN_ON_FLAG);
- mGpsParser = new BatteryFlagParser(accentColor, false,
- HistoryItem.STATE_GPS_ON_FLAG);
- mFlashlightParser = new BatteryFlagParser(accentColor, true,
- HistoryItem.STATE2_FLASHLIGHT_FLAG);
- mCameraParser = new BatteryFlagParser(accentColor, true,
- HistoryItem.STATE2_CAMERA_FLAG);
- mWifiParser = new BatteryWifiParser(accentColor);
- mCpuParser = new BatteryFlagParser(accentColor, false,
- HistoryItem.STATE_CPU_RUNNING_FLAG);
- mPhoneParser = new BatteryCellParser();
- setHasOptionsMenu(true);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- return inflater.inflate(R.layout.battery_history_detail, container, false);
- }
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- updateEverything();
- }
-
- private void updateEverything() {
- BatteryInfo.getBatteryInfo(getContext(), info -> {
- final View view = getView();
- info.bindHistory((UsageView) view.findViewById(R.id.battery_usage), mChargingParser,
- mScreenOn, mGpsParser, mFlashlightParser, mCameraParser, mWifiParser,
- mCpuParser, mPhoneParser);
- ((TextView) view.findViewById(R.id.charge)).setText(info.batteryPercentString);
- ((TextView) view.findViewById(R.id.estimation)).setText(info.remainingLabel);
-
- bindData(mChargingParser, R.string.battery_stats_charging_label, R.id.charging_group);
- bindData(mScreenOn, R.string.battery_stats_screen_on_label, R.id.screen_on_group);
- bindData(mGpsParser, R.string.battery_stats_gps_on_label, R.id.gps_group);
- bindData(mFlashlightParser, R.string.battery_stats_flashlight_on_label,
- R.id.flashlight_group);
- bindData(mCameraParser, R.string.battery_stats_camera_on_label, R.id.camera_group);
- bindData(mWifiParser, R.string.battery_stats_wifi_running_label, R.id.wifi_group);
- bindData(mCpuParser, R.string.battery_stats_wake_lock_label, R.id.cpu_group);
- bindData(mPhoneParser, R.string.battery_stats_phone_signal_label,
- R.id.cell_network_group);
- }, mStats, false /* shortString */);
- }
-
- private void bindData(BatteryActiveProvider provider, int label, int groupId) {
- View group = getView().findViewById(groupId);
- group.setVisibility(provider.hasData() ? View.VISIBLE : View.GONE);
- ((TextView) group.findViewById(android.R.id.title)).setText(label);
- ((BatteryActiveView) group.findViewById(R.id.battery_active)).setProvider(provider);
- }
-
- @Override
- public int getMetricsCategory() {
- return MetricsEvent.FUELGAUGE_BATTERY_HISTORY_DETAIL;
- }
-}
diff --git a/src/com/android/settings/fuelgauge/BatterySaverController.java b/src/com/android/settings/fuelgauge/BatterySaverController.java
index e2440a517a8..e77393ee8f9 100644
--- a/src/com/android/settings/fuelgauge/BatterySaverController.java
+++ b/src/com/android/settings/fuelgauge/BatterySaverController.java
@@ -22,19 +22,16 @@ import android.os.Looper;
import android.os.PowerManager;
import android.provider.Settings;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.BasePreferenceController;
-import com.android.settings.dashboard.conditional.BatterySaverCondition;
-import com.android.settings.dashboard.conditional.ConditionManager;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
-
public class BatterySaverController extends BasePreferenceController
implements LifecycleObserver, OnStart, OnStop, BatterySaverReceiver.BatterySaverListener {
private static final String KEY_BATTERY_SAVER = "battery_saver_summary";
@@ -82,11 +79,6 @@ public class BatterySaverController extends BasePreferenceController
mBatteryStateChangeReceiver.setListening(false);
}
- @VisibleForTesting
- void refreshConditionManager() {
- ConditionManager.get(mContext).getCondition(BatterySaverCondition.class).refreshState();
- }
-
@Override
public CharSequence getSummary() {
final boolean isPowerSaveOn = mPowerManager.isPowerSaveMode();
diff --git a/src/com/android/settings/fuelgauge/PowerUsageBase.java b/src/com/android/settings/fuelgauge/PowerUsageBase.java
index 06200a3cc3c..a11270befb7 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageBase.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageBase.java
@@ -72,8 +72,6 @@ public abstract class PowerUsageBase extends DashboardFragment {
@Override
public void onResume() {
super.onResume();
-
- BatteryStatsHelper.dropFile(getActivity(), BatteryHistoryDetail.BATTERY_HISTORY_FILE);
mBatteryBroadcastReceiver.register();
}
diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
index 2ae58763ba4..75631206e53 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
@@ -17,13 +17,13 @@
package com.android.settings.fuelgauge;
import static com.android.settings.fuelgauge.BatteryBroadcastReceiver.BatteryUpdateType;
+import static com.android.settings.fuelgauge.TopLevelBatteryPreferenceController.getDashboardLabel;
import android.app.Activity;
import android.content.Context;
import android.os.BatteryStats;
import android.os.Bundle;
import android.provider.SearchIndexableResource;
-import android.text.BidiFormatter;
import android.text.format.Formatter;
import android.view.Menu;
import android.view.MenuInflater;
@@ -32,6 +32,11 @@ import android.view.View;
import android.view.View.OnLongClickListener;
import android.widget.TextView;
+import androidx.annotation.VisibleForTesting;
+import androidx.loader.app.LoaderManager;
+import androidx.loader.app.LoaderManager.LoaderCallbacks;
+import androidx.loader.content.Loader;
+
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
@@ -51,11 +56,6 @@ import com.android.settingslib.utils.StringUtil;
import java.util.Collections;
import java.util.List;
-import androidx.annotation.VisibleForTesting;
-import androidx.loader.app.LoaderManager;
-import androidx.loader.app.LoaderManager.LoaderCallbacks;
-import androidx.loader.content.Loader;
-
/**
* Displays a list of apps and subsystems that consume power, ordered by how much power was
* consumed since the last time it was unplugged.
@@ -147,9 +147,9 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
protected void updateViews(List batteryInfos) {
final BatteryMeterView batteryView = mBatteryLayoutPref
- .findViewById(R.id.battery_header_icon);
+ .findViewById(R.id.battery_header_icon);
final TextView percentRemaining =
- mBatteryLayoutPref.findViewById(R.id.battery_percent);
+ mBatteryLayoutPref.findViewById(R.id.battery_percent);
final TextView summary1 = mBatteryLayoutPref.findViewById(R.id.summary1);
final TextView summary2 = mBatteryLayoutPref.findViewById(R.id.summary2);
BatteryInfo oldInfo = batteryInfos.get(0);
@@ -160,13 +160,13 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
// can sometimes say 0 time remaining because battery stats requires the phone
// be unplugged for a period of time before being willing ot make an estimate.
summary1.setText(mPowerFeatureProvider.getOldEstimateDebugString(
- Formatter.formatShortElapsedTime(getContext(),
- PowerUtil.convertUsToMs(oldInfo.remainingTimeUs))));
+ Formatter.formatShortElapsedTime(getContext(),
+ PowerUtil.convertUsToMs(oldInfo.remainingTimeUs))));
// for this one we can just set the string directly
summary2.setText(mPowerFeatureProvider.getEnhancedEstimateDebugString(
- Formatter.formatShortElapsedTime(getContext(),
- PowerUtil.convertUsToMs(newInfo.remainingTimeUs))));
+ Formatter.formatShortElapsedTime(getContext(),
+ PowerUtil.convertUsToMs(newInfo.remainingTimeUs))));
batteryView.setBatteryLevel(oldInfo.batteryLevel);
batteryView.setCharging(!oldInfo.discharging);
@@ -419,19 +419,6 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
}
}
- @VisibleForTesting
- static CharSequence getDashboardLabel(Context context, BatteryInfo info) {
- CharSequence label;
- final BidiFormatter formatter = BidiFormatter.getInstance();
- if (info.remainingLabel == null) {
- label = info.batteryPercentString;
- } else {
- label = context.getString(R.string.power_remaining_settings_home_page,
- formatter.unicodeWrap(info.batteryPercentString),
- formatter.unicodeWrap(info.remainingLabel));
- }
- return label;
- }
public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider() {
diff --git a/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java b/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java
new file mode 100644
index 00000000000..82058183e53
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java
@@ -0,0 +1,90 @@
+/*
+ * 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.settings.fuelgauge;
+
+import android.content.Context;
+import android.text.BidiFormatter;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+
+public class TopLevelBatteryPreferenceController extends BasePreferenceController implements
+ LifecycleObserver, OnStart, OnStop {
+
+ private final BatteryBroadcastReceiver mBatteryBroadcastReceiver;
+ private Preference mPreference;
+ private BatteryInfo mBatteryInfo;
+
+ public TopLevelBatteryPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mBatteryBroadcastReceiver = new BatteryBroadcastReceiver(mContext);
+ mBatteryBroadcastReceiver.setBatteryChangedListener(type -> {
+ BatteryInfo.getBatteryInfo(mContext, info -> {
+ mBatteryInfo = info;
+ updateState(mPreference);
+ }, true /* shortString */);
+ });
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE_UNSEARCHABLE;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(getPreferenceKey());
+ }
+
+ @Override
+ public void onStart() {
+ mBatteryBroadcastReceiver.register();
+ }
+
+ @Override
+ public void onStop() {
+ mBatteryBroadcastReceiver.unRegister();
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ return getDashboardLabel(mContext, mBatteryInfo);
+ }
+
+ static CharSequence getDashboardLabel(Context context, BatteryInfo info) {
+ if (info == null || context == null) {
+ return null;
+ }
+ CharSequence label;
+ final BidiFormatter formatter = BidiFormatter.getInstance();
+ if (info.remainingLabel == null) {
+ label = info.batteryPercentString;
+ } else {
+ label = context.getString(R.string.power_remaining_settings_home_page,
+ formatter.unicodeWrap(info.batteryPercentString),
+ formatter.unicodeWrap(info.remainingLabel));
+ }
+ return label;
+ }
+}
diff --git a/src/com/android/settings/homepage/CardContentProvider.java b/src/com/android/settings/homepage/CardContentProvider.java
new file mode 100644
index 00000000000..3081ae1fa81
--- /dev/null
+++ b/src/com/android/settings/homepage/CardContentProvider.java
@@ -0,0 +1,166 @@
+/*
+ * 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.settings.homepage;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.net.Uri;
+import android.os.Build;
+import android.os.StrictMode;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * Provider stores and manages user interaction feedback for homepage contextual cards.
+ */
+public class CardContentProvider extends ContentProvider {
+
+ private static final String TAG = "CardContentProvider";
+
+ public static final String CARD_AUTHORITY = "com.android.settings.homepage.CardContentProvider";
+
+ /** URI matcher for ContentProvider queries. */
+ private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+ /** URI matcher type for cards table */
+ private static final int MATCH_CARDS = 100;
+ /** URI matcher type for card log table */
+ private static final int MATCH_CARD_LOG = 200;
+
+ static {
+ sUriMatcher.addURI(CARD_AUTHORITY, CardDatabaseHelper.CARD_TABLE, MATCH_CARDS);
+ }
+
+ private CardDatabaseHelper mDBHelper;
+
+ @Override
+ public boolean onCreate() {
+ mDBHelper = CardDatabaseHelper.getInstance(getContext());
+ return true;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
+ try {
+ if (Build.IS_DEBUGGABLE) {
+ enableStrictMode(true);
+ }
+
+ final SQLiteDatabase database = mDBHelper.getWritableDatabase();
+ final String table = getTableFromMatch(uri);
+ final long ret = database.insert(table, null, values);
+ if (ret != -1) {
+ getContext().getContentResolver().notifyChange(uri, null);
+ } else {
+ Log.e(TAG, "The CardContentProvider insertion failed! Plase check SQLiteDatabase's "
+ + "message.");
+ }
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
+ return uri;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
+ try {
+ if (Build.IS_DEBUGGABLE) {
+ enableStrictMode(true);
+ }
+
+ final SQLiteDatabase database = mDBHelper.getWritableDatabase();
+ final String table = getTableFromMatch(uri);
+ final int rowsDeleted = database.delete(table, selection, selectionArgs);
+ getContext().getContentResolver().notifyChange(uri, null);
+ return rowsDeleted;
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ throw new UnsupportedOperationException("getType operation not supported currently.");
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
+ try {
+ if (Build.IS_DEBUGGABLE) {
+ enableStrictMode(true);
+ }
+
+ final SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
+ final String table = getTableFromMatch(uri);
+ queryBuilder.setTables(table);
+ final SQLiteDatabase database = mDBHelper.getReadableDatabase();
+ final Cursor cursor = queryBuilder.query(database,
+ projection, selection, selectionArgs, null, null, sortOrder);
+
+ cursor.setNotificationUri(getContext().getContentResolver(), uri);
+ return cursor;
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
+ try {
+ if (Build.IS_DEBUGGABLE) {
+ enableStrictMode(true);
+ }
+
+ final SQLiteDatabase database = mDBHelper.getWritableDatabase();
+ final String table = getTableFromMatch(uri);
+ final int rowsUpdated = database.update(table, values, selection, selectionArgs);
+ getContext().getContentResolver().notifyChange(uri, null);
+ return rowsUpdated;
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
+ }
+
+ private void enableStrictMode(boolean enabled) {
+ StrictMode.setThreadPolicy(enabled
+ ? new StrictMode.ThreadPolicy.Builder().detectAll().build()
+ : StrictMode.ThreadPolicy.LAX);
+ }
+
+ @VisibleForTesting
+ String getTableFromMatch(Uri uri) {
+ final int match = sUriMatcher.match(uri);
+ String table;
+ switch (match) {
+ case MATCH_CARDS:
+ table = CardDatabaseHelper.CARD_TABLE;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown Uri format: " + uri);
+ }
+ return table;
+ }
+}
diff --git a/src/com/android/settings/homepage/CardDatabaseHelper.java b/src/com/android/settings/homepage/CardDatabaseHelper.java
new file mode 100644
index 00000000000..b4dc221d39b
--- /dev/null
+++ b/src/com/android/settings/homepage/CardDatabaseHelper.java
@@ -0,0 +1,190 @@
+/*
+ * 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.settings.homepage;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * Defines the schema for the Homepage Cards database.
+ */
+public class CardDatabaseHelper extends SQLiteOpenHelper {
+ private static final String DATABASE_NAME = "homepage_cards.db";
+ private static final int DATABASE_VERSION = 1;
+
+ public static final String CARD_TABLE = "cards";
+
+ public interface CardColumns {
+ /**
+ * Primary key. Name of the card.
+ */
+ String NAME = "name";
+
+ /**
+ * Type of the card.
+ */
+ String TYPE = "type";
+
+ /**
+ * Score of the card. Higher numbers have higher priorities.
+ */
+ String SCORE = "score";
+
+ /**
+ * URI of the slice card.
+ */
+ String SLICE_URI = "slice_uri";
+
+ /**
+ * Category of the card. The value is between 0 to 3.
+ */
+ String CATEGORY = "category";
+
+ /**
+ * URI decides the card can be shown.
+ */
+ String AVAILABILITY_URI = "availability_uri";
+
+ /**
+ * Keep the card last display's locale.
+ */
+ String LOCALIZED_TO_LOCALE = "localized_to_locale";
+
+ /**
+ * Package name for all card candidates.
+ */
+ String PACKAGE_NAME = "package_name";
+
+ /**
+ * Application version of the package.
+ */
+ String APP_VERSION = "app_version";
+
+ /**
+ * Title resource name of the package.
+ */
+ String TITLE_RES_NAME = "title_res_name";
+
+ /**
+ * Title of the package to be shown.
+ */
+ String TITLE_TEXT = "title_text";
+
+ /**
+ * Summary resource name of the package.
+ */
+ String SUMMARY_RES_NAME = "summary_res_name";
+
+ /**
+ * Summary of the package to be shown.
+ */
+ String SUMMARY_TEXT = "summary_text";
+
+ /**
+ * Icon resource name of the package.
+ */
+ String ICON_RES_NAME = "icon_res_name";
+
+ /**
+ * Icon resource id of the package.
+ */
+ String ICON_RES_ID = "icon_res_id";
+
+ /**
+ * PendingIntent for for custom view card candidate. Do action when user press card.
+ */
+ String CARD_ACTION = "card_action";
+
+ /**
+ * Expire time of the card. The unit of the value is mini-second.
+ */
+ String EXPIRE_TIME_MS = "expire_time_ms";
+ }
+
+ private static final String CREATE_CARD_TABLE =
+ "CREATE TABLE " + CARD_TABLE +
+ "(" +
+ CardColumns.NAME +
+ " TEXT NOT NULL PRIMARY KEY, " +
+ CardColumns.TYPE +
+ " INTEGER NOT NULL, " +
+ CardColumns.SCORE +
+ " DOUBLE NOT NULL, " +
+ CardColumns.SLICE_URI +
+ " TEXT, " +
+ CardColumns.CATEGORY +
+ " INTEGER DEFAULT 0 CHECK (" +
+ CardColumns.CATEGORY +
+ " >= 0 AND " +
+ CardColumns.CATEGORY +
+ " <= 3), " +
+ CardColumns.AVAILABILITY_URI +
+ " TEXT, " +
+ CardColumns.LOCALIZED_TO_LOCALE +
+ " TEXT, " +
+ CardColumns.PACKAGE_NAME +
+ " TEXT NOT NULL, " +
+ CardColumns.APP_VERSION +
+ " TEXT NOT NULL, " +
+ CardColumns.TITLE_RES_NAME +
+ " TEXT, " +
+ CardColumns.TITLE_TEXT +
+ " TEXT, " +
+ CardColumns.SUMMARY_RES_NAME +
+ " TEXT, " +
+ CardColumns.SUMMARY_TEXT +
+ " TEXT, " +
+ CardColumns.ICON_RES_NAME +
+ " TEXT, " +
+ CardColumns.ICON_RES_ID +
+ " INTEGER DEFAULT 0, " +
+ CardColumns.CARD_ACTION +
+ " TEXT, " +
+ CardColumns.EXPIRE_TIME_MS +
+ " INTEGER " +
+ ");";
+
+ public CardDatabaseHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL(CREATE_CARD_TABLE);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ if (oldVersion < newVersion) {
+ db.execSQL("DROP TABLE IF EXISTS " + CARD_TABLE);
+ onCreate(db);
+ }
+ }
+
+ @VisibleForTesting
+ static CardDatabaseHelper sCardDatabaseHelper;
+
+ public static synchronized CardDatabaseHelper getInstance(Context context) {
+ if (sCardDatabaseHelper == null) {
+ sCardDatabaseHelper = new CardDatabaseHelper(context.getApplicationContext());
+ }
+ return sCardDatabaseHelper;
+ }
+}
diff --git a/src/com/android/settings/homepage/HomepageFragment.java b/src/com/android/settings/homepage/HomepageFragment.java
index ff89dd5a4dc..2e22a035d9b 100644
--- a/src/com/android/settings/homepage/HomepageFragment.java
+++ b/src/com/android/settings/homepage/HomepageFragment.java
@@ -26,6 +26,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.Toolbar;
+import androidx.annotation.NonNull;
+
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.SettingsHomepageActivity;
@@ -39,8 +41,6 @@ import com.google.android.material.bottomappbar.BottomAppBar;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
-import androidx.annotation.NonNull;
-
public class HomepageFragment extends InstrumentedFragment {
private static final String TAG = "HomepageFragment";
@@ -70,7 +70,7 @@ public class HomepageFragment extends InstrumentedFragment {
private void setupBottomBar() {
final Activity activity = getActivity();
- mSearchButton = (FloatingActionButton) activity.findViewById(R.id.search_fab);
+ mSearchButton = activity.findViewById(R.id.search_fab);
mSearchButton.setOnClickListener(v -> {
final Intent intent = SearchFeatureProvider.SEARCH_UI_INTENT;
@@ -79,7 +79,7 @@ public class HomepageFragment extends InstrumentedFragment {
startActivityForResult(intent, 0 /* requestCode */);
});
mBottomSheetBehavior = BottomSheetBehavior.from(activity.findViewById(R.id.bottom_sheet));
- final BottomAppBar bottomBar = (BottomAppBar) activity.findViewById(R.id.bar);
+ final BottomAppBar bottomBar = activity.findViewById(R.id.bar);
bottomBar.setOnClickListener(v -> {
mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
});
@@ -87,7 +87,7 @@ public class HomepageFragment extends InstrumentedFragment {
final int screenWidthpx = getResources().getDisplayMetrics().widthPixels;
final View searchbar = activity.findViewById(R.id.search_bar_container);
final View bottombar = activity.findViewById(R.id.bar);
- final Toolbar searchActionBar = (Toolbar) activity.findViewById(R.id.search_action_bar);
+ final Toolbar searchActionBar = activity.findViewById(R.id.search_action_bar);
searchActionBar.setNavigationIcon(R.drawable.ic_search_floating_24dp);
@@ -95,6 +95,7 @@ public class HomepageFragment extends InstrumentedFragment {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
if (!mBottomFragmentLoaded) {
+ // TODO(b/110405144): Switch to {@link TopLevelSettings} when it's ready.
SettingsHomepageActivity.switchToFragment(getActivity(),
R.id.bottom_sheet_fragment, DashboardSummary.class.getName());
mBottomFragmentLoaded = true;
diff --git a/src/com/android/settings/homepage/TopLevelSettings.java b/src/com/android/settings/homepage/TopLevelSettings.java
new file mode 100644
index 00000000000..c450157d254
--- /dev/null
+++ b/src/com/android/settings/homepage/TopLevelSettings.java
@@ -0,0 +1,116 @@
+/*
+ * 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.settings.homepage;
+
+import static com.android.settings.search.actionbar.SearchMenuController
+ .NEED_SEARCH_ICON_IN_ACTION_BAR;
+import static com.android.settingslib.search.SearchIndexable.MOBILE;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.provider.SearchIndexableResource;
+
+import androidx.fragment.app.Fragment;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragmentCompat;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.core.instrumentation.Instrumentable;
+import com.android.settingslib.search.SearchIndexable;
+
+import java.util.Arrays;
+import java.util.List;
+
+@SearchIndexable(forTarget = MOBILE)
+public class TopLevelSettings extends DashboardFragment implements
+ PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
+
+ private static final String TAG = "TopLevelSettings";
+
+ public TopLevelSettings() {
+ final Bundle args = new Bundle();
+ // Disable the search icon because this page uses a full search view in actionbar.
+ args.putBoolean(NEED_SEARCH_ICON_IN_ACTION_BAR, false);
+ setArguments(args);
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.top_level_settings;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsProto.MetricsEvent.DASHBOARD_SUMMARY;
+ }
+
+ @Override
+ public int getHelpResource() {
+ // Disable the help icon because this page uses a full search view in actionbar.
+ return 0;
+ }
+
+ @Override
+ public Fragment getCallbackFragment() {
+ return this;
+ }
+
+ @Override
+ public boolean onPreferenceStartFragment(PreferenceFragmentCompat caller, Preference pref) {
+ new SubSettingLauncher(getActivity())
+ .setDestination(pref.getFragment())
+ .setArguments(pref.getExtras())
+ .setSourceMetricsCategory(caller instanceof Instrumentable
+ ? ((Instrumentable) caller).getMetricsCategory()
+ : Instrumentable.METRICS_CATEGORY_UNKNOWN)
+ .setTitleRes(-1)
+ .launch();
+ return true;
+ }
+
+ @Override
+ protected boolean shouldForceRoundedIcon() {
+ return getContext().getResources()
+ .getBoolean(R.bool.config_force_rounded_icon_TopLevelSettings);
+ }
+
+ public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider() {
+ @Override
+ public List getXmlResourcesToIndex(
+ Context context, boolean enabled) {
+ final SearchIndexableResource sir = new SearchIndexableResource(context);
+ sir.xmlResId = R.xml.top_level_settings;
+ return Arrays.asList(sir);
+ }
+
+ @Override
+ protected boolean isPageSearchEnabled(Context context) {
+ // Never searchable, all entries in this page are already indexed elsewhere.
+ return false;
+ }
+ };
+}
diff --git a/src/com/android/settings/dashboard/conditional/AbnormalRingerConditionBase.java b/src/com/android/settings/homepage/conditional/AbnormalRingerConditionBase.java
similarity index 98%
rename from src/com/android/settings/dashboard/conditional/AbnormalRingerConditionBase.java
rename to src/com/android/settings/homepage/conditional/AbnormalRingerConditionBase.java
index eaec6d08cc7..ef564feef09 100644
--- a/src/com/android/settings/dashboard/conditional/AbnormalRingerConditionBase.java
+++ b/src/com/android/settings/homepage/conditional/AbnormalRingerConditionBase.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settings.dashboard.conditional;
+package com.android.settings.homepage.conditional;
import android.content.BroadcastReceiver;
import android.content.Context;
diff --git a/src/com/android/settings/dashboard/conditional/AirplaneModeCondition.java b/src/com/android/settings/homepage/conditional/AirplaneModeCondition.java
similarity index 98%
rename from src/com/android/settings/dashboard/conditional/AirplaneModeCondition.java
rename to src/com/android/settings/homepage/conditional/AirplaneModeCondition.java
index 792a090ddf9..9d21e438f7b 100644
--- a/src/com/android/settings/dashboard/conditional/AirplaneModeCondition.java
+++ b/src/com/android/settings/homepage/conditional/AirplaneModeCondition.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.settings.dashboard.conditional;
+package com.android.settings.homepage.conditional;
import android.content.BroadcastReceiver;
import android.content.Context;
diff --git a/src/com/android/settings/dashboard/conditional/BackgroundDataCondition.java b/src/com/android/settings/homepage/conditional/BackgroundDataCondition.java
similarity index 97%
rename from src/com/android/settings/dashboard/conditional/BackgroundDataCondition.java
rename to src/com/android/settings/homepage/conditional/BackgroundDataCondition.java
index d5372a24c05..84ae9241412 100644
--- a/src/com/android/settings/dashboard/conditional/BackgroundDataCondition.java
+++ b/src/com/android/settings/homepage/conditional/BackgroundDataCondition.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.settings.dashboard.conditional;
+package com.android.settings.homepage.conditional;
import android.content.Intent;
import android.graphics.drawable.Drawable;
diff --git a/src/com/android/settings/dashboard/conditional/BatterySaverCondition.java b/src/com/android/settings/homepage/conditional/BatterySaverCondition.java
similarity index 98%
rename from src/com/android/settings/dashboard/conditional/BatterySaverCondition.java
rename to src/com/android/settings/homepage/conditional/BatterySaverCondition.java
index b0c1de4edb1..1301b3de252 100644
--- a/src/com/android/settings/dashboard/conditional/BatterySaverCondition.java
+++ b/src/com/android/settings/homepage/conditional/BatterySaverCondition.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.settings.dashboard.conditional;
+package com.android.settings.homepage.conditional;
import android.content.Intent;
import android.graphics.drawable.Drawable;
diff --git a/src/com/android/settings/dashboard/conditional/CellularDataCondition.java b/src/com/android/settings/homepage/conditional/CellularDataCondition.java
similarity index 98%
rename from src/com/android/settings/dashboard/conditional/CellularDataCondition.java
rename to src/com/android/settings/homepage/conditional/CellularDataCondition.java
index bc0cbd5200f..7b382a7cf28 100644
--- a/src/com/android/settings/dashboard/conditional/CellularDataCondition.java
+++ b/src/com/android/settings/homepage/conditional/CellularDataCondition.java
@@ -8,7 +8,7 @@
* 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.settings.dashboard.conditional;
+package com.android.settings.homepage.conditional;
import android.content.BroadcastReceiver;
import android.content.Context;
diff --git a/src/com/android/settings/dashboard/conditional/Condition.java b/src/com/android/settings/homepage/conditional/Condition.java
similarity index 98%
rename from src/com/android/settings/dashboard/conditional/Condition.java
rename to src/com/android/settings/homepage/conditional/Condition.java
index 82452ff6f0c..f91e422aa6c 100644
--- a/src/com/android/settings/dashboard/conditional/Condition.java
+++ b/src/com/android/settings/homepage/conditional/Condition.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settings.dashboard.conditional;
+package com.android.settings.homepage.conditional;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -22,12 +22,12 @@ import android.content.IntentFilter;
import android.graphics.drawable.Drawable;
import android.os.PersistableBundle;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-import androidx.annotation.VisibleForTesting;
-
public abstract class Condition {
private static final String KEY_SILENCE = "silence";
diff --git a/src/com/android/settings/dashboard/conditional/ConditionAdapter.java b/src/com/android/settings/homepage/conditional/ConditionAdapter.java
similarity index 99%
rename from src/com/android/settings/dashboard/conditional/ConditionAdapter.java
rename to src/com/android/settings/homepage/conditional/ConditionAdapter.java
index a6478e21867..e975146d095 100644
--- a/src/com/android/settings/dashboard/conditional/ConditionAdapter.java
+++ b/src/com/android/settings/homepage/conditional/ConditionAdapter.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.settings.dashboard.conditional;
+package com.android.settings.homepage.conditional;
import android.content.Context;
import android.util.Log;
@@ -22,6 +22,10 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
+import androidx.annotation.VisibleForTesting;
+import androidx.recyclerview.widget.ItemTouchHelper;
+import androidx.recyclerview.widget.RecyclerView;
+
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardAdapter.DashboardItemHolder;
@@ -32,10 +36,6 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import java.util.List;
import java.util.Objects;
-import androidx.annotation.VisibleForTesting;
-import androidx.recyclerview.widget.ItemTouchHelper;
-import androidx.recyclerview.widget.RecyclerView;
-
public class ConditionAdapter extends RecyclerView.Adapter {
public static final String TAG = "ConditionAdapter";
diff --git a/src/com/android/settings/dashboard/conditional/ConditionManager.java b/src/com/android/settings/homepage/conditional/ConditionManager.java
similarity index 98%
rename from src/com/android/settings/dashboard/conditional/ConditionManager.java
rename to src/com/android/settings/homepage/conditional/ConditionManager.java
index 2754d8a5e64..e84b71a3d26 100644
--- a/src/com/android/settings/dashboard/conditional/ConditionManager.java
+++ b/src/com/android/settings/homepage/conditional/ConditionManager.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.settings.dashboard.conditional;
+package com.android.settings.homepage.conditional;
import android.content.Context;
import android.os.AsyncTask;
@@ -44,7 +44,7 @@ public class ConditionManager implements LifecycleObserver, OnResume, OnPause {
private static final boolean DEBUG = false;
- private static final String PKG = "com.android.settings.dashboard.conditional.";
+ private static final String PKG = "com.android.settings.homepage.conditional.";
private static final String FILE_NAME = "condition_state.xml";
private static final String TAG_CONDITIONS = "cs";
diff --git a/src/com/android/settings/dashboard/conditional/DndCondition.java b/src/com/android/settings/homepage/conditional/DndCondition.java
similarity index 99%
rename from src/com/android/settings/dashboard/conditional/DndCondition.java
rename to src/com/android/settings/homepage/conditional/DndCondition.java
index 32184e174fc..3e8052b4808 100644
--- a/src/com/android/settings/dashboard/conditional/DndCondition.java
+++ b/src/com/android/settings/homepage/conditional/DndCondition.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.settings.dashboard.conditional;
+package com.android.settings.homepage.conditional;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
@@ -26,13 +26,13 @@ import android.provider.Settings;
import android.provider.Settings.Global;
import android.service.notification.ZenModeConfig;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.notification.ZenModeSettings;
-import androidx.annotation.VisibleForTesting;
-
public class DndCondition extends Condition {
private static final String TAG = "DndCondition";
diff --git a/src/com/android/settings/dashboard/conditional/FocusRecyclerView.java b/src/com/android/settings/homepage/conditional/FocusRecyclerView.java
similarity index 96%
rename from src/com/android/settings/dashboard/conditional/FocusRecyclerView.java
rename to src/com/android/settings/homepage/conditional/FocusRecyclerView.java
index abe4743cb3e..78b9eede458 100644
--- a/src/com/android/settings/dashboard/conditional/FocusRecyclerView.java
+++ b/src/com/android/settings/homepage/conditional/FocusRecyclerView.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.settings.dashboard.conditional;
+package com.android.settings.homepage.conditional;
import android.content.Context;
import android.util.AttributeSet;
diff --git a/src/com/android/settings/dashboard/conditional/HotspotCondition.java b/src/com/android/settings/homepage/conditional/HotspotCondition.java
similarity index 98%
rename from src/com/android/settings/dashboard/conditional/HotspotCondition.java
rename to src/com/android/settings/homepage/conditional/HotspotCondition.java
index 68f2382176e..7212e841701 100644
--- a/src/com/android/settings/dashboard/conditional/HotspotCondition.java
+++ b/src/com/android/settings/homepage/conditional/HotspotCondition.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.settings.dashboard.conditional;
+package com.android.settings.homepage.conditional;
import android.content.BroadcastReceiver;
import android.content.Context;
diff --git a/src/com/android/settings/dashboard/conditional/NightDisplayCondition.java b/src/com/android/settings/homepage/conditional/NightDisplayCondition.java
similarity index 98%
rename from src/com/android/settings/dashboard/conditional/NightDisplayCondition.java
rename to src/com/android/settings/homepage/conditional/NightDisplayCondition.java
index c3ba534fd83..95769734713 100644
--- a/src/com/android/settings/dashboard/conditional/NightDisplayCondition.java
+++ b/src/com/android/settings/homepage/conditional/NightDisplayCondition.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settings.dashboard.conditional;
+package com.android.settings.homepage.conditional;
import android.content.Intent;
import android.graphics.drawable.Drawable;
diff --git a/src/com/android/settings/dashboard/conditional/RingerMutedCondition.java b/src/com/android/settings/homepage/conditional/RingerMutedCondition.java
similarity index 97%
rename from src/com/android/settings/dashboard/conditional/RingerMutedCondition.java
rename to src/com/android/settings/homepage/conditional/RingerMutedCondition.java
index 7f7bc2be090..740e6e48bab 100644
--- a/src/com/android/settings/dashboard/conditional/RingerMutedCondition.java
+++ b/src/com/android/settings/homepage/conditional/RingerMutedCondition.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settings.dashboard.conditional;
+package com.android.settings.homepage.conditional;
import static android.content.Context.NOTIFICATION_SERVICE;
diff --git a/src/com/android/settings/dashboard/conditional/RingerVibrateCondition.java b/src/com/android/settings/homepage/conditional/RingerVibrateCondition.java
similarity index 96%
rename from src/com/android/settings/dashboard/conditional/RingerVibrateCondition.java
rename to src/com/android/settings/homepage/conditional/RingerVibrateCondition.java
index 6af05c1faed..ea91c0ef28b 100644
--- a/src/com/android/settings/dashboard/conditional/RingerVibrateCondition.java
+++ b/src/com/android/settings/homepage/conditional/RingerVibrateCondition.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settings.dashboard.conditional;
+package com.android.settings.homepage.conditional;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
diff --git a/src/com/android/settings/dashboard/conditional/WorkModeCondition.java b/src/com/android/settings/homepage/conditional/WorkModeCondition.java
similarity index 98%
rename from src/com/android/settings/dashboard/conditional/WorkModeCondition.java
rename to src/com/android/settings/homepage/conditional/WorkModeCondition.java
index 941d5b02b68..9e467faf446 100644
--- a/src/com/android/settings/dashboard/conditional/WorkModeCondition.java
+++ b/src/com/android/settings/homepage/conditional/WorkModeCondition.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settings.dashboard.conditional;
+package com.android.settings.homepage.conditional;
import android.content.Context;
import android.content.Intent;
diff --git a/src/com/android/settings/language/LanguageAndInputSettings.java b/src/com/android/settings/language/LanguageAndInputSettings.java
index c983c071ba8..6c7c0d3861c 100644
--- a/src/com/android/settings/language/LanguageAndInputSettings.java
+++ b/src/com/android/settings/language/LanguageAndInputSettings.java
@@ -27,10 +27,10 @@ import android.speech.tts.TtsEngines;
import android.text.TextUtils;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
-
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
-import com.android.settings.applications.defaultapps.DefaultAutofillPreferenceController;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.dashboard.SummaryLoader;
import com.android.settings.inputmethod.PhysicalKeyboardPreferenceController;
@@ -41,14 +41,10 @@ import com.android.settings.widget.PreferenceCategoryController;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.search.SearchIndexable;
-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
@SearchIndexable
public class LanguageAndInputSettings extends DashboardFragment {
@@ -122,7 +118,6 @@ public class LanguageAndInputSettings extends DashboardFragment {
// Input Assistance
controllers.add(new SpellCheckerPreferenceController(context));
- controllers.add(new DefaultAutofillPreferenceController(context));
return controllers;
}
diff --git a/src/com/android/settings/location/LocationSliceBuilder.java b/src/com/android/settings/location/LocationSliceBuilder.java
index cbdf7f0f709..4883ee8bbe8 100644
--- a/src/com/android/settings/location/LocationSliceBuilder.java
+++ b/src/com/android/settings/location/LocationSliceBuilder.java
@@ -37,6 +37,7 @@ import com.android.settings.slices.SliceBuilderUtils;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
import androidx.slice.builders.ListBuilder;
+import androidx.slice.builders.ListBuilder.RowBuilder;
import androidx.slice.builders.SliceAction;
/**
@@ -70,7 +71,7 @@ public class LocationSliceBuilder {
return new ListBuilder(context, LOCATION_URI, ListBuilder.INFINITY)
.setAccentColor(color)
- .addRow(b -> b
+ .addRow(new RowBuilder()
.setTitle(title)
.setTitleItem(icon, ICON_IMAGE)
.setPrimaryAction(primarySliceAction))
diff --git a/src/com/android/settings/mobilenetwork/Enhanced4gLteSliceHelper.java b/src/com/android/settings/mobilenetwork/Enhanced4gLteSliceHelper.java
index f8f1447c979..960a082adf6 100644
--- a/src/com/android/settings/mobilenetwork/Enhanced4gLteSliceHelper.java
+++ b/src/com/android/settings/mobilenetwork/Enhanced4gLteSliceHelper.java
@@ -49,6 +49,7 @@ import androidx.annotation.VisibleForTesting;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
import androidx.slice.builders.ListBuilder;
+import androidx.slice.builders.ListBuilder.RowBuilder;
import androidx.slice.builders.SliceAction;
/**
@@ -183,7 +184,7 @@ public class Enhanced4gLteSliceHelper {
return new ListBuilder(mContext, sliceUri, ListBuilder.INFINITY)
.setAccentColor(Utils.getColorAccentDefaultColor(mContext))
- .addRow(b -> b
+ .addRow(new RowBuilder()
.setTitle(getEnhanced4glteModeTitle(subId))
.addEndItem(
new SliceAction(
diff --git a/src/com/android/settings/network/NetworkDashboardFragment.java b/src/com/android/settings/network/NetworkDashboardFragment.java
index d5ef9aa0503..fbb0b20a543 100644
--- a/src/com/android/settings/network/NetworkDashboardFragment.java
+++ b/src/com/android/settings/network/NetworkDashboardFragment.java
@@ -149,6 +149,7 @@ public class NetworkDashboardFragment extends DashboardFragment implements
return 0;
}
+ // TODO(b/110405144): Remove SummaryProvider
@VisibleForTesting
static class SummaryProvider implements SummaryLoader.SummaryProvider {
diff --git a/src/com/android/settings/network/TopLevelNetworkEntryPreferenceController.java b/src/com/android/settings/network/TopLevelNetworkEntryPreferenceController.java
new file mode 100644
index 00000000000..567e52e596f
--- /dev/null
+++ b/src/com/android/settings/network/TopLevelNetworkEntryPreferenceController.java
@@ -0,0 +1,79 @@
+/*
+ * 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.settings.network;
+
+import android.content.Context;
+import android.icu.text.ListFormatter;
+import android.text.BidiFormatter;
+import android.text.TextUtils;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.wifi.WifiMasterSwitchPreferenceController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TopLevelNetworkEntryPreferenceController extends BasePreferenceController {
+
+ private final WifiMasterSwitchPreferenceController mWifiPreferenceController;
+ private final MobileNetworkPreferenceController mMobileNetworkPreferenceController;
+ private final TetherPreferenceController mTetherPreferenceController;
+
+ public TopLevelNetworkEntryPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mMobileNetworkPreferenceController = new MobileNetworkPreferenceController(mContext);
+ mTetherPreferenceController = new TetherPreferenceController(
+ mContext, null /* lifecycle */);
+ mWifiPreferenceController = new WifiMasterSwitchPreferenceController(
+ mContext, null /* metrics */);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE_UNSEARCHABLE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ final String wifiSummary = BidiFormatter.getInstance()
+ .unicodeWrap(mContext.getString(R.string.wifi_settings_title));
+ final String mobileSummary = mContext.getString(
+ R.string.network_dashboard_summary_mobile);
+ final String dataUsageSummary = mContext.getString(
+ R.string.network_dashboard_summary_data_usage);
+ final String hotspotSummary = mContext.getString(
+ R.string.network_dashboard_summary_hotspot);
+
+ final List summaries = new ArrayList<>();
+ if (mWifiPreferenceController.isAvailable()
+ && !TextUtils.isEmpty(wifiSummary)) {
+ summaries.add(wifiSummary);
+ }
+ if (mMobileNetworkPreferenceController.isAvailable() && !TextUtils.isEmpty(mobileSummary)) {
+ summaries.add(mobileSummary);
+ }
+ if (!TextUtils.isEmpty(dataUsageSummary)) {
+ summaries.add(dataUsageSummary);
+ }
+ if (mTetherPreferenceController.isAvailable()
+ && !TextUtils.isEmpty(hotspotSummary)) {
+ summaries.add(hotspotSummary);
+ }
+ return ListFormatter.getInstance().format(summaries);
+ }
+}
diff --git a/src/com/android/settings/notification/ZenModeSliceBuilder.java b/src/com/android/settings/notification/ZenModeSliceBuilder.java
index ad39d713055..dcdf6ef10eb 100644
--- a/src/com/android/settings/notification/ZenModeSliceBuilder.java
+++ b/src/com/android/settings/notification/ZenModeSliceBuilder.java
@@ -40,6 +40,7 @@ import com.android.settings.slices.SliceBuilderUtils;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
import androidx.slice.builders.ListBuilder;
+import androidx.slice.builders.ListBuilder.RowBuilder;
import androidx.slice.builders.SliceAction;
public class ZenModeSliceBuilder {
@@ -94,7 +95,7 @@ public class ZenModeSliceBuilder {
return new ListBuilder(context, ZEN_MODE_URI, ListBuilder.INFINITY)
.setAccentColor(color)
- .addRow(b -> b
+ .addRow(new RowBuilder()
.setTitle(title)
.addEndItem(toggleSliceAction)
.setPrimaryAction(primarySliceAction))
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
index 65d72f11bbc..f5b3b054c54 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
@@ -23,6 +23,7 @@ import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
+import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
@@ -70,7 +71,7 @@ public class ConfirmDeviceCredentialActivity extends Activity {
KeyguardManager.EXTRA_ALTERNATE_BUTTON_LABEL);
boolean frp = KeyguardManager.ACTION_CONFIRM_FRP_CREDENTIAL.equals(intent.getAction());
- int userId = Utils.getCredentialOwnerUserId(this);
+ int userId = UserHandle.myUserId();
if (isInternalActivity()) {
try {
userId = Utils.getUserIdFromBundle(this, intent.getExtras());
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java b/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
index 0f6eeb3bb63..23bc26f3daf 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
@@ -125,8 +125,7 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
mCancelButton = (Button) view.findViewById(R.id.cancelButton);
mFingerprintIcon = (ImageView) view.findViewById(R.id.fingerprintIcon);
mFingerprintHelper = new FingerprintUiHelper(
- mFingerprintIcon,
- (TextView) view.findViewById(R.id.errorText), this, mEffectiveUserId);
+ mFingerprintIcon, view.findViewById(R.id.errorText), this, mUserId);
boolean showCancelButton = getActivity().getIntent().getBooleanExtra(
SHOW_CANCEL_BUTTON, false);
boolean hasAlternateButton = mFrp && !TextUtils.isEmpty(mFrpAlternateButtonText);
diff --git a/src/com/android/settings/security/TopLevelSecurityEntryPreferenceController.java b/src/com/android/settings/security/TopLevelSecurityEntryPreferenceController.java
new file mode 100644
index 00000000000..4b004240685
--- /dev/null
+++ b/src/com/android/settings/security/TopLevelSecurityEntryPreferenceController.java
@@ -0,0 +1,52 @@
+/*
+ * 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.settings.security;
+
+import android.content.Context;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.core.BasePreferenceController;
+
+public class TopLevelSecurityEntryPreferenceController extends BasePreferenceController {
+
+ public TopLevelSecurityEntryPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE_UNSEARCHABLE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ final FingerprintManager fpm =
+ Utils.getFingerprintManagerOrNull(mContext);
+ final FaceManager faceManager =
+ Utils.getFaceManagerOrNull(mContext);
+ if (faceManager != null && faceManager.isHardwareDetected()) {
+ return mContext.getText(R.string.security_dashboard_summary_face);
+ } else if (fpm != null && fpm.isHardwareDetected()) {
+ return mContext.getText(R.string.security_dashboard_summary);
+ } else {
+ return mContext.getText(R.string.security_dashboard_summary_no_fingerprint);
+ }
+ }
+}
diff --git a/src/com/android/settings/slices/SliceBuilderUtils.java b/src/com/android/settings/slices/SliceBuilderUtils.java
index c1c3b8e8229..b613ca4cb4a 100644
--- a/src/com/android/settings/slices/SliceBuilderUtils.java
+++ b/src/com/android/settings/slices/SliceBuilderUtils.java
@@ -29,6 +29,7 @@ import android.net.Uri;
import android.os.Bundle;
import android.provider.SettingsSlicesContract;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
@@ -47,12 +48,15 @@ import com.android.settingslib.core.AbstractPreferenceController;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Set;
import java.util.stream.Collectors;
import androidx.annotation.VisibleForTesting;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
import androidx.slice.builders.ListBuilder;
+import androidx.slice.builders.ListBuilder.InputRangeBuilder;
+import androidx.slice.builders.ListBuilder.RowBuilder;
import androidx.slice.builders.SliceAction;
@@ -250,11 +254,11 @@ public class SliceBuilderUtils {
(TogglePreferenceController) controller;
final SliceAction sliceAction = getToggleAction(context, sliceData,
toggleController.isChecked());
- final List keywords = buildSliceKeywords(sliceData);
+ final Set keywords = buildSliceKeywords(sliceData);
return new ListBuilder(context, sliceData.getUri(), ListBuilder.INFINITY)
.setAccentColor(color)
- .addRow(rowBuilder -> rowBuilder
+ .addRow(new RowBuilder()
.setTitle(sliceData.getTitle())
.setSubtitle(subtitleText)
.setPrimaryAction(
@@ -270,11 +274,11 @@ public class SliceBuilderUtils {
final IconCompat icon = getSafeIcon(context, sliceData);
final CharSequence subtitleText = getSubtitleText(context, controller, sliceData);
@ColorInt final int color = Utils.getColorAccentDefaultColor(context);
- final List keywords = buildSliceKeywords(sliceData);
+ final Set keywords = buildSliceKeywords(sliceData);
return new ListBuilder(context, sliceData.getUri(), ListBuilder.INFINITY)
.setAccentColor(color)
- .addRow(rowBuilder -> rowBuilder
+ .addRow(new RowBuilder()
.setTitle(sliceData.getTitle())
.setSubtitle(subtitleText)
.setPrimaryAction(
@@ -293,11 +297,11 @@ public class SliceBuilderUtils {
final CharSequence subtitleText = getSubtitleText(context, controller, sliceData);
final SliceAction primaryAction = new SliceAction(contentIntent, icon,
sliceData.getTitle());
- final List keywords = buildSliceKeywords(sliceData);
+ final Set keywords = buildSliceKeywords(sliceData);
return new ListBuilder(context, sliceData.getUri(), ListBuilder.INFINITY)
.setAccentColor(color)
- .addInputRange(builder -> builder
+ .addInputRange(new InputRangeBuilder()
.setTitle(sliceData.getTitle())
.setSubtitle(subtitleText)
.setPrimaryAction(primaryAction)
@@ -343,8 +347,8 @@ public class SliceBuilderUtils {
|| TextUtils.equals(summary, doublePlaceHolder));
}
- private static List buildSliceKeywords(SliceData data) {
- final List keywords = new ArrayList<>();
+ private static Set buildSliceKeywords(SliceData data) {
+ final Set keywords = new ArraySet<>();
keywords.add(data.getTitle());
@@ -366,7 +370,7 @@ public class SliceBuilderUtils {
private static Slice buildUnavailableSlice(Context context, SliceData data) {
final String title = data.getTitle();
- final List keywords = buildSliceKeywords(data);
+ final Set keywords = buildSliceKeywords(data);
@ColorInt final int color = Utils.getColorAccentDefaultColor(context);
final CharSequence summary = context.getText(R.string.disabled_dependent_setting_summary);
final IconCompat icon = IconCompat.createWithResource(context, data.getIconResource());
@@ -375,9 +379,9 @@ public class SliceBuilderUtils {
return new ListBuilder(context, data.getUri(), ListBuilder.INFINITY)
.setAccentColor(color)
- .addRow(builder -> builder
+ .addRow(new RowBuilder()
.setTitle(title)
- .setTitleItem(icon)
+ .setTitleItem(icon, ListBuilder.SMALL_IMAGE)
.setSubtitle(summary)
.setPrimaryAction(primaryAction))
.setKeywords(keywords)
diff --git a/src/com/android/settings/dashboard/RoundedHomepageIcon.java b/src/com/android/settings/widget/RoundedHomepageIcon.java
similarity index 53%
rename from src/com/android/settings/dashboard/RoundedHomepageIcon.java
rename to src/com/android/settings/widget/RoundedHomepageIcon.java
index 9848034d7e5..f7927e756ca 100644
--- a/src/com/android/settings/dashboard/RoundedHomepageIcon.java
+++ b/src/com/android/settings/widget/RoundedHomepageIcon.java
@@ -14,20 +14,26 @@
* limitations under the License.
*/
-package com.android.settings.dashboard;
+package com.android.settings.widget;
import static androidx.annotation.VisibleForTesting.NONE;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_HINT;
+
import android.content.Context;
+import android.content.pm.PackageManager;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
+import android.os.Bundle;
import android.util.Log;
-import com.android.settings.R;
-
import androidx.annotation.VisibleForTesting;
+import com.android.settings.R;
+import com.android.settingslib.drawer.Tile;
+
public class RoundedHomepageIcon extends LayerDrawable {
private static final String TAG = "RoundedHomepageIcon";
@@ -36,7 +42,7 @@ public class RoundedHomepageIcon extends LayerDrawable {
int mBackgroundColor = -1;
public RoundedHomepageIcon(Context context, Drawable foreground) {
- super(new Drawable[] {
+ super(new Drawable[]{
context.getDrawable(R.drawable.ic_homepage_generic_background),
foreground
});
@@ -45,6 +51,33 @@ public class RoundedHomepageIcon extends LayerDrawable {
setLayerInset(1 /* index */, insetPx, insetPx, insetPx, insetPx);
}
+ public void setBackgroundColor(Context context, Tile tile) {
+ final Bundle metaData = tile.getMetaData();
+ try {
+ if (metaData != null) {
+ // Load from bg.argb first
+ int bgColor = metaData.getInt(META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB,
+ 0 /* default */);
+ // Not found, load from bg.hint
+ if (bgColor == 0) {
+ final int colorRes = metaData.getInt(META_DATA_PREFERENCE_ICON_BACKGROUND_HINT,
+ 0 /* default */);
+ if (colorRes != 0) {
+ bgColor = context.getPackageManager()
+ .getResourcesForApplication(tile.getPackageName())
+ .getColor(colorRes, null /* theme */);
+ }
+ }
+ // If found anything, use it.
+ if (bgColor != 0) {
+ setBackgroundColor(bgColor);
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Failed to set background color for " + tile.getPackageName());
+ }
+ }
+
public void setBackgroundColor(int color) {
mBackgroundColor = color;
getDrawable(0).setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
diff --git a/src/com/android/settings/wifi/WifiSliceBuilder.java b/src/com/android/settings/wifi/WifiSliceBuilder.java
index a7bf16a5954..f6628a071fc 100644
--- a/src/com/android/settings/wifi/WifiSliceBuilder.java
+++ b/src/com/android/settings/wifi/WifiSliceBuilder.java
@@ -42,6 +42,7 @@ import com.android.settings.slices.SliceBuilderUtils;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
import androidx.slice.builders.ListBuilder;
+import androidx.slice.builders.ListBuilder.RowBuilder;
import androidx.slice.builders.SliceAction;
/**
@@ -95,7 +96,7 @@ public class WifiSliceBuilder {
return new ListBuilder(context, WIFI_URI, ListBuilder.INFINITY)
.setAccentColor(color)
- .addRow(b -> b
+ .addRow(new RowBuilder()
.setTitle(title)
.setSubtitle(summary)
.addEndItem(toggleSliceAction)
diff --git a/src/com/android/settings/wifi/calling/WifiCallingSliceHelper.java b/src/com/android/settings/wifi/calling/WifiCallingSliceHelper.java
index f9014b70664..baf2a977267 100644
--- a/src/com/android/settings/wifi/calling/WifiCallingSliceHelper.java
+++ b/src/com/android/settings/wifi/calling/WifiCallingSliceHelper.java
@@ -209,7 +209,7 @@ public class WifiCallingSliceHelper {
return new ListBuilder(mContext, sliceUri, ListBuilder.INFINITY)
.setAccentColor(Utils.getColorAccentDefaultColor(mContext))
- .addRow(b -> b
+ .addRow(new RowBuilder()
.setTitle(mContext.getText(R.string.wifi_calling_settings_title))
.addEndItem(
new SliceAction(
@@ -298,7 +298,7 @@ public class WifiCallingSliceHelper {
// Top row shows information on current preference state
ListBuilder listBuilder = new ListBuilder(mContext, sliceUri, ListBuilder.INFINITY)
.setAccentColor(Utils.getColorAccentDefaultColor(mContext));
- listBuilder.setHeader(new ListBuilder.HeaderBuilder(listBuilder)
+ listBuilder.setHeader(new ListBuilder.HeaderBuilder()
.setTitle(mContext.getText(R.string.wifi_calling_mode_title))
.setSubtitle(getWifiCallingPreferenceSummary(currentWfcPref))
.setPrimaryAction(new SliceAction(
@@ -338,7 +338,7 @@ public class WifiCallingSliceHelper {
int preferenceTitleResId, String action, boolean checked) {
final IconCompat icon =
IconCompat.createWithResource(mContext, R.drawable.radio_button_check);
- return new RowBuilder(listBuilder)
+ return new RowBuilder()
.setTitle(mContext.getText(preferenceTitleResId))
.setTitleItem(new SliceAction(getBroadcastIntent(action),
icon, mContext.getText(preferenceTitleResId), checked));
@@ -488,7 +488,7 @@ public class WifiCallingSliceHelper {
final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.wifi_signal);
return new ListBuilder(mContext, sliceUri, ListBuilder.INFINITY)
.setAccentColor(Utils.getColorAccentDefaultColor(mContext))
- .addRow(b -> b
+ .addRow(new RowBuilder()
.setTitle(title)
.setSubtitle(subtitle)
.setPrimaryAction(new SliceAction(
diff --git a/tests/robotests/assets/grandfather_not_implementing_index_provider b/tests/robotests/assets/grandfather_not_implementing_index_provider
index 37869e97cf2..1a7b28958a5 100644
--- a/tests/robotests/assets/grandfather_not_implementing_index_provider
+++ b/tests/robotests/assets/grandfather_not_implementing_index_provider
@@ -39,7 +39,6 @@ com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionMi
com.android.settings.enterprise.ApplicationListFragment$EnterpriseInstalledPackages
com.android.settings.enterprise.EnterpriseSetDefaultAppsListFragment
com.android.settings.fuelgauge.AdvancedPowerUsageDetail
-com.android.settings.fuelgauge.BatteryHistoryDetail
com.android.settings.fuelgauge.InactiveApps
com.android.settings.fuelgauge.RestrictedAppDetails
com.android.settings.IccLockSettings
diff --git a/tests/robotests/res/values-mcc999/config.xml b/tests/robotests/res/values-mcc999/config.xml
index 676a8dd0c71..bbbdcc2e849 100644
--- a/tests/robotests/res/values-mcc999/config.xml
+++ b/tests/robotests/res/values-mcc999/config.xml
@@ -15,7 +15,6 @@
-->
- false
false
false
false
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java
index 8957f8547ed..9fdcef58778 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java
@@ -40,10 +40,10 @@ import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
+import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.HearingAidProfile;
-import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
@@ -55,23 +55,24 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
import java.util.ArrayList;
import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class)
-@Config(shadows = {ShadowBluetoothUtils.class})
+@Config(shadows = {ShadowBluetoothAdapter.class, ShadowBluetoothUtils.class})
public class AccessibilityHearingAidPreferenceControllerTest {
private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1";
private static final String TEST_DEVICE_NAME = "TEST_HEARING_AID_BT_DEVICE_NAME";
private static final String HEARING_AID_PREFERENCE = "hearing_aid_preference";
private BluetoothAdapter mBluetoothAdapter;
+ private ShadowBluetoothAdapter mShadowBluetoothAdapter;
private BluetoothManager mBluetoothManager;
private BluetoothDevice mBluetoothDevice;
private Context mContext;
private Preference mHearingAidPreference;
- private List mProfileSupportedList;
private AccessibilityHearingAidPreferenceController mPreferenceController;
@Mock
@@ -79,8 +80,6 @@ public class AccessibilityHearingAidPreferenceControllerTest {
@Mock
private CachedBluetoothDeviceManager mCachedDeviceManager;
@Mock
- private LocalBluetoothAdapter mLocalBluetoothAdapter;
- @Mock
private LocalBluetoothManager mLocalBluetoothManager;
@Mock
private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
@@ -161,7 +160,7 @@ public class AccessibilityHearingAidPreferenceControllerTest {
@Test
public void onNotSupportHearingAidProfile_doNotDoReceiverOperation() {
//clear bluetooth supported profile
- mProfileSupportedList.clear();
+ mShadowBluetoothAdapter.clearSupportedProfiles();
mPreferenceController = new AccessibilityHearingAidPreferenceController(mContext, HEARING_AID_PREFERENCE);
mPreferenceController.setPreference(mHearingAidPreference);
//not call registerReceiver()
@@ -178,18 +177,17 @@ public class AccessibilityHearingAidPreferenceControllerTest {
mLocalBluetoothManager = ShadowBluetoothUtils.getLocalBtManager(mContext);
mBluetoothManager = new BluetoothManager(mContext);
mBluetoothAdapter = mBluetoothManager.getAdapter();
- when(mLocalBluetoothManager.getBluetoothAdapter()).thenReturn(mLocalBluetoothAdapter);
- when(mLocalBluetoothAdapter.isEnabled()).thenReturn(true);
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
}
private void setupHearingAidEnvironment() {
+ mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
- mProfileSupportedList = new ArrayList();
- mProfileSupportedList.add(BluetoothProfile.HEARING_AID);
- when(mLocalBluetoothAdapter.getSupportedProfiles()).thenReturn(mProfileSupportedList);
+ mShadowBluetoothAdapter.enable();
+ mShadowBluetoothAdapter.addSupportedProfiles(BluetoothProfile.HEARING_AID);
when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice);
when(mCachedBluetoothDevice.getName()).thenReturn(TEST_DEVICE_NAME);
when(mCachedBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(true);
diff --git a/tests/robotests/src/com/android/settings/accounts/AccountDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/accounts/AccountDashboardFragmentTest.java
index 40dcf7ad693..41ac4500b73 100644
--- a/tests/robotests/src/com/android/settings/accounts/AccountDashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/AccountDashboardFragmentTest.java
@@ -15,40 +15,20 @@
*/
package com.android.settings.accounts;
-import static com.android.settings.accounts.AccountDashboardFragmentTest
- .ShadowAuthenticationHelper.LABELS;
-import static com.android.settings.accounts.AccountDashboardFragmentTest
- .ShadowAuthenticationHelper.TYPES;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import android.content.Context;
-import android.os.UserHandle;
import android.provider.SearchIndexableResource;
-import android.text.TextUtils;
-import com.android.settings.R;
-import com.android.settings.dashboard.SummaryLoader;
-import com.android.settings.testutils.Robolectric;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
-import com.android.settingslib.accounts.AuthenticatorHelper;
import com.android.settingslib.drawer.CategoryKey;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.annotation.Resetter;
import java.util.List;
-import androidx.fragment.app.FragmentActivity;
-
@RunWith(SettingsRobolectricTestRunner.class)
public class AccountDashboardFragmentTest {
@@ -59,66 +39,11 @@ public class AccountDashboardFragmentTest {
mFragment = new AccountDashboardFragment();
}
- @After
- public void tearDown() {
- ShadowAuthenticationHelper.reset();
- }
-
@Test
public void testCategory_isAccount() {
assertThat(mFragment.getCategoryKey()).isEqualTo(CategoryKey.CATEGORY_ACCOUNT);
}
- @Test
- @Config(shadows = {
- ShadowAuthenticationHelper.class
- })
- public void updateSummary_hasAccount_shouldDisplayUpTo3AccountTypes() {
- final SummaryLoader loader = mock(SummaryLoader.class);
- final FragmentActivity activity = Robolectric.buildActivity(
- FragmentActivity.class).setup().get();
-
- final SummaryLoader.SummaryProvider provider =
- AccountDashboardFragment.SUMMARY_PROVIDER_FACTORY.createSummaryProvider(activity,
- loader);
- provider.setListening(true);
-
- verify(loader).setSummary(provider, LABELS[0] + ", " + LABELS[1] + ", and " + LABELS[2]);
- }
-
- @Test
- @Config(shadows = ShadowAuthenticationHelper.class)
- public void updateSummary_noAccount_shouldDisplayDefaultSummary() {
- ShadowAuthenticationHelper.setEnabledAccount(null);
- final SummaryLoader loader = mock(SummaryLoader.class);
- final FragmentActivity activity = Robolectric.buildActivity(FragmentActivity.class).setup().get();
-
- final SummaryLoader.SummaryProvider provider =
- AccountDashboardFragment.SUMMARY_PROVIDER_FACTORY.createSummaryProvider(activity,
- loader);
- provider.setListening(true);
-
- verify(loader).setSummary(provider,
- activity.getString(R.string.account_dashboard_default_summary));
- }
-
- @Test
- @Config(shadows = ShadowAuthenticationHelper.class)
- public void updateSummary_noAccountTypeLabel_shouldNotDisplayNullEntry() {
- final SummaryLoader loader = mock(SummaryLoader.class);
- final FragmentActivity activity = Robolectric.buildActivity(FragmentActivity.class).setup().get();
- final String[] enabledAccounts = {TYPES[0], "unlabeled_account_type", TYPES[1]};
- ShadowAuthenticationHelper.setEnabledAccount(enabledAccounts);
-
- final SummaryLoader.SummaryProvider provider =
- AccountDashboardFragment.SUMMARY_PROVIDER_FACTORY.createSummaryProvider(activity,
- loader);
- provider.setListening(true);
-
- // should only show the 2 accounts with labels
- verify(loader).setSummary(provider, LABELS[0] + " and " + LABELS[1]);
- }
-
@Test
public void testSearchIndexProvider_shouldIndexResource() {
final List indexRes =
@@ -129,43 +54,5 @@ public class AccountDashboardFragmentTest {
assertThat(indexRes.get(0).xmlResId).isEqualTo(mFragment.getPreferenceScreenResId());
}
- @Implements(AuthenticatorHelper.class)
- public static class ShadowAuthenticationHelper {
- static final String[] TYPES = {"type1", "type2", "type3", "type4"};
- static final String[] LABELS = {"LABEL1", "LABEL2", "LABEL3", "LABEL4"};
- private static String[] sEnabledAccount = TYPES;
-
- public void __constructor__(Context context, UserHandle userHandle,
- AuthenticatorHelper.OnAccountsUpdateListener listener) {
- }
-
- private static void setEnabledAccount(String[] enabledAccount) {
- sEnabledAccount = enabledAccount;
- }
-
- @Resetter
- public static void reset() {
- sEnabledAccount = TYPES;
- }
-
- @Implementation
- public String[] getEnabledAccountTypes() {
- return sEnabledAccount;
- }
-
- @Implementation
- public CharSequence getLabelForType(Context context, final String accountType) {
- if (TextUtils.equals(accountType, TYPES[0])) {
- return LABELS[0];
- } else if (TextUtils.equals(accountType, TYPES[1])) {
- return LABELS[1];
- } else if (TextUtils.equals(accountType, TYPES[2])) {
- return LABELS[2];
- } else if (TextUtils.equals(accountType, TYPES[3])) {
- return LABELS[3];
- }
- return null;
- }
- }
}
diff --git a/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java
index 6b19e59d43e..eca9f862b30 100644
--- a/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java
@@ -130,7 +130,7 @@ public class AccountDetailDashboardFragmentTest {
final FragmentActivity activity = Robolectric.setupActivity(FragmentActivity.class);
final Preference preference = new Preference(mContext);
- dashboardFeatureProvider.bindPreferenceToTile(activity,
+ dashboardFeatureProvider.bindPreferenceToTile(activity, false /* forceRoundedIcon */,
MetricsProto.MetricsEvent.DASHBOARD_SUMMARY, preference, tile, null /* key */,
Preference.DEFAULT_ORDER);
diff --git a/tests/robotests/src/com/android/settings/accounts/TopLevelAccountEntryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accounts/TopLevelAccountEntryPreferenceControllerTest.java
new file mode 100644
index 00000000000..79e292dcf94
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accounts/TopLevelAccountEntryPreferenceControllerTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.settings.accounts;
+
+import static com.android.settings.accounts.TopLevelAccountEntryPreferenceControllerTest
+ .ShadowAuthenticationHelper.LABELS;
+import static com.android.settings.accounts.TopLevelAccountEntryPreferenceControllerTest
+ .ShadowAuthenticationHelper.TYPES;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.text.TextUtils;
+
+import com.android.settings.R;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.accounts.AuthenticatorHelper;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(shadows = {TopLevelAccountEntryPreferenceControllerTest.ShadowAuthenticationHelper.class})
+public class TopLevelAccountEntryPreferenceControllerTest {
+
+ private TopLevelAccountEntryPreferenceController mController;
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mController = new TopLevelAccountEntryPreferenceController(mContext, "test_key");
+ }
+
+ @After
+ public void tearDown() {
+ ShadowAuthenticationHelper.reset();
+ }
+
+ @Test
+
+ public void updateSummary_hasAccount_shouldDisplayUpTo3AccountTypes() {
+ assertThat(mController.getSummary())
+ .isEqualTo(LABELS[0] + ", " + LABELS[1] + ", and " + LABELS[2]);
+ }
+
+ @Test
+ public void updateSummary_noAccount_shouldDisplayDefaultSummary() {
+ ShadowAuthenticationHelper.setEnabledAccount(null);
+
+ assertThat(mController.getSummary()).isEqualTo(
+ mContext.getText(R.string.account_dashboard_default_summary));
+ }
+
+ @Test
+ public void updateSummary_noAccountTypeLabel_shouldNotDisplayNullEntry() {
+ final String[] enabledAccounts = {TYPES[0], "unlabeled_account_type", TYPES[1]};
+ ShadowAuthenticationHelper.setEnabledAccount(enabledAccounts);
+
+
+ // should only show the 2 accounts with labels
+ assertThat(mController.getSummary()).isEqualTo(LABELS[0] + " and " + LABELS[1]);
+ }
+
+ @Implements(AuthenticatorHelper.class)
+ public static class ShadowAuthenticationHelper {
+
+ static final String[] TYPES = {"type1", "type2", "type3", "type4"};
+ static final String[] LABELS = {"LABEL1", "LABEL2", "LABEL3", "LABEL4"};
+ private static String[] sEnabledAccount = TYPES;
+
+ public void __constructor__(Context context, UserHandle userHandle,
+ AuthenticatorHelper.OnAccountsUpdateListener listener) {
+ }
+
+ private static void setEnabledAccount(String[] enabledAccount) {
+ sEnabledAccount = enabledAccount;
+ }
+
+ @Resetter
+ public static void reset() {
+ sEnabledAccount = TYPES;
+ }
+
+ @Implementation
+ public String[] getEnabledAccountTypes() {
+ return sEnabledAccount;
+ }
+
+ @Implementation
+ public CharSequence getLabelForType(Context context, final String accountType) {
+ if (TextUtils.equals(accountType, TYPES[0])) {
+ return LABELS[0];
+ } else if (TextUtils.equals(accountType, TYPES[1])) {
+ return LABELS[1];
+ } else if (TextUtils.equals(accountType, TYPES[2])) {
+ return LABELS[2];
+ } else if (TextUtils.equals(accountType, TYPES[3])) {
+ return LABELS[3];
+ }
+ return null;
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAutofillPickerTest.java b/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAutofillPickerTest.java
index ed823c113bd..dd57315365b 100644
--- a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAutofillPickerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAutofillPickerTest.java
@@ -17,65 +17,196 @@
package com.android.settings.applications.defaultapps;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.robolectric.RuntimeEnvironment.application;
-import android.app.Activity;
+import android.app.AppOpsManager;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.UserHandle;
import android.os.UserManager;
-
+import androidx.fragment.app.FragmentActivity;
+import androidx.preference.PreferenceScreen;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.SettingsShadowResources;
+import com.android.settings.testutils.shadow.ShadowProcess;
+import com.android.settings.testutils.shadow.ShadowSecureSettings;
import com.android.settingslib.applications.DefaultAppInfo;
-
+import java.util.Arrays;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers;
@RunWith(SettingsRobolectricTestRunner.class)
+@Config(shadows = {
+ SettingsShadowResources.SettingsShadowTheme.class,
+ ShadowProcess.class,
+ ShadowSecureSettings.class
+ })
public class DefaultAutofillPickerTest {
- private static final String TEST_APP_KEY = "foo.bar/foo.bar.Baz";
+ private static final String MAIN_APP_KEY = "main.foo.bar/foo.bar.Baz";
+ private static final String MANAGED_APP_KEY = "managed.foo.bar/foo.bar.Baz";
+ private static final int MANAGED_PROFILE_UID = 10;
+ private static final int MAIN_PROFILE_UID = 0;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private Activity mActivity;
+ private FragmentActivity mActivity;
@Mock
private UserManager mUserManager;
@Mock
+ private AppOpsManager mAppOpsManager;
+ @Mock
private PackageManager mPackageManager;
+ @Mock
+ private PreferenceScreen mScreen;
+
private DefaultAutofillPicker mPicker;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
FakeFeatureFactory.setupForTest();
+
+ Resources res = application.getResources();
+
+ when(mActivity.getApplicationContext()).thenReturn(mActivity);
+ when(mActivity.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
when(mActivity.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+ when(mActivity.getTheme()).thenReturn(res.newTheme());
+ when(mActivity.getResources()).thenReturn(res);
+
mPicker = spy(new DefaultAutofillPicker());
- mPicker.onAttach((Context) mActivity);
+
+ doReturn(application.getApplicationContext()).when(mPicker).getContext();
+ doReturn(mActivity).when(mPicker).getActivity();
+ doReturn(res).when(mPicker).getResources();
+ doReturn(mScreen).when(mPicker).getPreferenceScreen();
+
+ doNothing().when(mPicker).onCreatePreferences(any(), any());
+ doNothing().when(mPicker).updateCandidates();
ReflectionHelpers.setField(mPicker, "mPm", mPackageManager);
-
- doReturn(RuntimeEnvironment.application).when(mPicker).getContext();
}
@Test
public void setAndGetDefaultAppKey_shouldUpdateDefaultAutoFill() {
- assertThat(mPicker.setDefaultKey(TEST_APP_KEY)).isTrue();
- assertThat(mPicker.getDefaultKey()).isEqualTo(TEST_APP_KEY);
+ mPicker.onAttach((Context) mActivity);
+
+ ReflectionHelpers.setField(
+ mPicker, "mUserId", MAIN_PROFILE_UID * UserHandle.PER_USER_RANGE);
+ assertThat(mPicker.setDefaultKey(MAIN_APP_KEY)).isTrue();
+ ReflectionHelpers.setField(
+ mPicker, "mUserId", MANAGED_PROFILE_UID * UserHandle.PER_USER_RANGE);
+ assertThat(mPicker.setDefaultKey(MANAGED_APP_KEY)).isTrue();
+
+ ReflectionHelpers.setField(
+ mPicker, "mUserId", MAIN_PROFILE_UID * UserHandle.PER_USER_RANGE);
+ assertThat(mPicker.getDefaultKey()).isEqualTo(MAIN_APP_KEY);
+ ReflectionHelpers.setField(
+ mPicker, "mUserId", MANAGED_PROFILE_UID * UserHandle.PER_USER_RANGE);
+ assertThat(mPicker.getDefaultKey()).isEqualTo(MANAGED_APP_KEY);
}
@Test
public void getConfirmationMessage_shouldNotBeNull() {
+ mPicker.onAttach((Context) mActivity);
+
final DefaultAppInfo info = mock(DefaultAppInfo.class);
when(info.loadLabel()).thenReturn("test_app_name");
assertThat(mPicker.getConfirmationMessage(info)).isNotNull();
}
+
+ @Test
+ public void mUserId_shouldDeriveUidFromManagedCaller() {
+ setupUserManager();
+ setupCaller();
+ ShadowProcess.setMyUid(MANAGED_PROFILE_UID * UserHandle.PER_USER_RANGE);
+
+ mPicker.onAttach((Context) mActivity);
+ mPicker.onCreate(null);
+
+ assertUserId(MANAGED_PROFILE_UID);
+ }
+
+ @Test
+ public void mUserId_shouldDeriveUidFromMainCaller() {
+ setupUserManager();
+ setupCaller();
+ ShadowProcess.setMyUid(MAIN_PROFILE_UID * UserHandle.PER_USER_RANGE);
+
+ mPicker.onAttach((Context) mActivity);
+ mPicker.onCreate(null);
+
+ assertUserId(MAIN_PROFILE_UID);
+ }
+
+ @Test
+ public void mUserId_shouldDeriveUidFromManagedClick() {
+ setupUserManager();
+ setupClick(/* forWork= */ true);
+ ShadowProcess.setMyUid(MAIN_PROFILE_UID * UserHandle.PER_USER_RANGE);
+
+ mPicker.onAttach((Context) mActivity);
+ mPicker.onCreate(null);
+
+ assertUserId(MANAGED_PROFILE_UID);
+ }
+
+ @Test
+ public void mUserId_shouldDeriveUidFromMainClick() {
+ setupUserManager();
+ setupClick(/* forWork= */ false);
+ ShadowProcess.setMyUid(MAIN_PROFILE_UID * UserHandle.PER_USER_RANGE);
+
+ mPicker.onAttach((Context) mActivity);
+ mPicker.onCreate(null);
+
+ assertUserId(MAIN_PROFILE_UID);
+ }
+
+ private void setupUserManager() {
+ UserHandle mainUserHandle = new UserHandle(MAIN_PROFILE_UID);
+ UserHandle managedUserHandle = new UserHandle(MANAGED_PROFILE_UID);
+ UserInfo managedUserInfo = new UserInfo(
+ MANAGED_PROFILE_UID, "managed", UserInfo.FLAG_MANAGED_PROFILE);
+ when(mUserManager.getUserProfiles())
+ .thenReturn(Arrays.asList(mainUserHandle, managedUserHandle));
+ when(mUserManager.getUserInfo(MANAGED_PROFILE_UID))
+ .thenReturn(managedUserInfo);
+ when(mUserManager.getUserHandle()).thenReturn(MAIN_PROFILE_UID);
+ }
+
+ private void setupCaller() {
+ Intent intent = new Intent();
+ intent.putExtra("package_name", "any package name");
+ when(mActivity.getIntent()).thenReturn(intent);
+ }
+
+ private void setupClick(boolean forWork) {
+ Bundle bundle = new Bundle();
+ bundle.putBoolean("for_work", forWork);
+ doReturn(bundle).when(mPicker).getArguments();
+ }
+
+ private void assertUserId(int userId) {
+ assertThat((Integer) ReflectionHelpers.getField(mPicker, "mUserId"))
+ .isEqualTo(userId);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java
index cf73d41eb78..e676cf4b041 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java
@@ -22,6 +22,7 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
@@ -31,6 +32,7 @@ import com.android.settings.connecteddevice.DevicePreferenceCallback;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.ShadowAudioManager;
+import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.HeadsetProfile;
@@ -44,12 +46,13 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
import java.util.ArrayList;
import java.util.Collection;
@RunWith(SettingsRobolectricTestRunner.class)
-@Config(shadows = {ShadowAudioManager.class})
+@Config(shadows = {ShadowAudioManager.class, ShadowBluetoothAdapter.class})
public class AvailableMediaBluetoothDeviceUpdaterTest {
@Mock
private DashboardFragment mDashboardFragment;
@@ -73,12 +76,15 @@ public class AvailableMediaBluetoothDeviceUpdaterTest {
private Collection cachedDevices;
private ShadowAudioManager mShadowAudioManager;
private BluetoothDevicePreference mPreference;
+ private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mShadowAudioManager = ShadowAudioManager.getShadow();
+ mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ mShadowBluetoothAdapter.setEnabled(true);
mContext = RuntimeEnvironment.application;
doReturn(mContext).when(mDashboardFragment).getContext();
cachedDevices =
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceUpdaterTest.java
index 9f81711c88c..f338e3668d2 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceUpdaterTest.java
@@ -22,15 +22,19 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.Intent;
+import androidx.preference.Preference;
import com.android.settings.SettingsActivity;
import com.android.settings.connecteddevice.DevicePreferenceCallback;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import org.junit.Before;
@@ -40,10 +44,14 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
-import androidx.preference.Preference;
+import java.util.ArrayList;
+import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
public class BluetoothDeviceUpdaterTest {
@Mock
@@ -58,18 +66,26 @@ public class BluetoothDeviceUpdaterTest {
private SettingsActivity mSettingsActivity;
@Mock
private LocalBluetoothManager mLocalManager;
+ @Mock
+ private CachedBluetoothDeviceManager mCachedDeviceManager;
private Context mContext;
private BluetoothDeviceUpdater mBluetoothDeviceUpdater;
private BluetoothDevicePreference mPreference;
+ private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+ private List mCachedDevices = new ArrayList();
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
+ mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ mCachedDevices.add(mCachedBluetoothDevice);
doReturn(mContext).when(mDashboardFragment).getContext();
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mLocalManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
+ when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(mCachedDevices);
mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice, false);
mBluetoothDeviceUpdater =
@@ -171,4 +187,38 @@ public class BluetoothDeviceUpdaterTest {
// Shouldn't crash
mBluetoothDeviceUpdater.unregisterCallback();
}
+
+ @Test
+ public void forceUpdate_bluetoothDisabled_doNothing() {
+ mShadowBluetoothAdapter.setEnabled(false);
+ mBluetoothDeviceUpdater.forceUpdate();
+
+ verify(mDevicePreferenceCallback, never()).onDeviceAdded(any(Preference.class));
+ }
+
+ @Test
+ public void forceUpdate_bluetoothEnabled_addPreference() {
+ mShadowBluetoothAdapter.setEnabled(true);
+ mBluetoothDeviceUpdater.forceUpdate();
+
+ verify(mDevicePreferenceCallback).onDeviceAdded(any(Preference.class));
+ }
+
+ @Test
+ public void onBluetoothStateChanged_bluetoothStateIsOn_forceUpdate() {
+ mShadowBluetoothAdapter.setEnabled(true);
+ mBluetoothDeviceUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_ON);
+
+ verify(mDevicePreferenceCallback).onDeviceAdded(any(Preference.class));
+ }
+
+ @Test
+ public void onBluetoothStateChanged_bluetoothStateIsOff_removeAllDevicesFromPreference() {
+ mBluetoothDeviceUpdater.mPreferenceMap.put(mBluetoothDevice, mPreference);
+
+ mBluetoothDeviceUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_OFF);
+
+ verify(mDevicePreferenceCallback).onDeviceRemoved(mPreference);
+ assertThat(mBluetoothDeviceUpdater.mPreferenceMap.containsKey(mBluetoothDevice)).isFalse();
+ }
}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java
index d94a8a7cf0a..ece71d73d9c 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java
@@ -23,6 +23,7 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
@@ -32,6 +33,7 @@ import com.android.settings.connecteddevice.DevicePreferenceCallback;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.ShadowAudioManager;
+import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -43,12 +45,13 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
import java.util.ArrayList;
import java.util.Collection;
@RunWith(SettingsRobolectricTestRunner.class)
-@Config(shadows = {ShadowAudioManager.class})
+@Config(shadows = {ShadowAudioManager.class, ShadowBluetoothAdapter.class})
public class ConnectedBluetoothDeviceUpdaterTest {
@Mock
private DashboardFragment mDashboardFragment;
@@ -67,12 +70,15 @@ public class ConnectedBluetoothDeviceUpdaterTest {
private ConnectedBluetoothDeviceUpdater mBluetoothDeviceUpdater;
private Collection cachedDevices;
private ShadowAudioManager mShadowAudioManager;
+ private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mShadowAudioManager = ShadowAudioManager.getShadow();
+ mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ mShadowBluetoothAdapter.setEnabled(true);
mContext = RuntimeEnvironment.application;
doReturn(mContext).when(mDashboardFragment).getContext();
cachedDevices =
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/TopLevelConnectedDevicesPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/TopLevelConnectedDevicesPreferenceControllerTest.java
new file mode 100644
index 00000000000..8816bec88f3
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/connecteddevice/TopLevelConnectedDevicesPreferenceControllerTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.settings.connecteddevice;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import com.android.settings.R;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class TopLevelConnectedDevicesPreferenceControllerTest {
+
+ private Context mContext;
+ private TopLevelConnectedDevicesPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mController = new TopLevelConnectedDevicesPreferenceController(mContext, "test_key");
+ }
+
+ @Test
+ @Config(shadows = ShadowAdvancedConnectedDeviceController.class)
+ public void getSummary_shouldCallAdvancedConnectedDeviceController() {
+ assertThat(mController.getSummary())
+ .isEqualTo(mContext.getText(R.string.settings_label_launcher));
+ }
+
+ @Implements(AdvancedConnectedDeviceController.class)
+ private static class ShadowAdvancedConnectedDeviceController {
+
+ @Implementation
+ public static int getConnectedDevicesSummaryResourceId(Context context) {
+ return R.string.settings_label_launcher;
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java
index 8f10af7afec..d3288b6ce7a 100644
--- a/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java
@@ -45,14 +45,14 @@ import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
-import com.android.settings.dashboard.conditional.Condition;
+import com.android.settings.homepage.conditional.Condition;
import com.android.settings.dashboard.suggestions.SuggestionAdapter;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.SettingsShadowResources;
+import com.android.settings.widget.RoundedHomepageIcon;
import com.android.settingslib.drawer.CategoryKey;
import com.android.settingslib.drawer.Tile;
-import com.android.settingslib.drawer.TileUtils;
import com.android.settingslib.utils.IconCache;
import org.junit.Before;
@@ -95,7 +95,6 @@ public class DashboardAdapterTest {
mActivityInfo.packageName = "pkg";
mActivityInfo.name = "class";
mActivityInfo.metaData = new Bundle();
- when(mFactory.dashboardFeatureProvider.shouldTintIcon()).thenReturn(true);
when(mContext.getSystemService(Context.WINDOW_SERVICE)).thenReturn(mWindowManager);
when(mContext.getResources()).thenReturn(mResources);
@@ -115,7 +114,7 @@ public class DashboardAdapterTest {
spy(new DashboardAdapter(mContext, null /* savedInstanceState */,
null /* conditions */, null /* suggestionControllerMixin */,
null /* lifecycle */));
- final List suggestions = makeSuggestionsV2("pkg1", "pkg2", "pkg3");
+ final List suggestions = makeSuggestions("pkg1", "pkg2", "pkg3");
adapter.setSuggestions(suggestions);
final RecyclerView data = mock(RecyclerView.class);
@@ -147,7 +146,7 @@ public class DashboardAdapterTest {
spy(new DashboardAdapter(mContext, null /* savedInstanceState */,
null /* conditions */, null /* suggestionControllerMixin */,
null /* lifecycle */));
- final List suggestions = makeSuggestionsV2("pkg1");
+ final List suggestions = makeSuggestions("pkg1");
adapter.setSuggestions(suggestions);
final DashboardData dashboardData = adapter.mDashboardData;
reset(adapter); // clear interactions tracking
@@ -164,7 +163,7 @@ public class DashboardAdapterTest {
spy(new DashboardAdapter(mContext, null /* savedInstanceState */,
null /* conditions */, null /* suggestionControllerMixin */,
null /* lifecycle */));
- final List suggestions = makeSuggestionsV2("pkg1");
+ final List suggestions = makeSuggestions("pkg1");
adapter.setSuggestions(suggestions);
reset(adapter); // clear interactions tracking
@@ -178,7 +177,7 @@ public class DashboardAdapterTest {
public void onBindSuggestion_shouldSetSuggestionAdapterAndNoCrash() {
mDashboardAdapter = new DashboardAdapter(mContext, null /* savedInstanceState */,
null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
- final List suggestions = makeSuggestionsV2("pkg1");
+ final List suggestions = makeSuggestions("pkg1");
mDashboardAdapter.setSuggestions(suggestions);
@@ -243,55 +242,6 @@ public class DashboardAdapterTest {
.isInstanceOf(RoundedHomepageIcon.class);
}
- @Test
- public void onBindTile_externalTileWithBackgroundColorRawValue_shouldUpdateIcon() {
- final Context context = spy(RuntimeEnvironment.application);
- final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile, null);
- final DashboardAdapter.DashboardItemHolder holder =
- new DashboardAdapter.DashboardItemHolder(view);
- final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
- tile.getMetaData().putInt(DashboardAdapter.META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB,
- 0xff0000);
- doReturn(Icon.createWithResource(context, R.drawable.ic_settings))
- .when(tile).getIcon(context);
- final IconCache iconCache = new IconCache(context);
- mDashboardAdapter = new DashboardAdapter(context, null /* savedInstanceState */,
- null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
- ReflectionHelpers.setField(mDashboardAdapter, "mCache", iconCache);
-
- doReturn("another.package").when(context).getPackageName();
- mDashboardAdapter.onBindTile(holder, tile);
-
- final RoundedHomepageIcon homepageIcon = (RoundedHomepageIcon) iconCache.getIcon(
- tile.getIcon(context));
- assertThat(homepageIcon.mBackgroundColor).isEqualTo(0xff0000);
- }
-
- @Test
- public void onBindTile_externalTileWithBackgroundColorHint_shouldUpdateIcon() {
- final Context context = spy(RuntimeEnvironment.application);
- final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile, null);
- final DashboardAdapter.DashboardItemHolder holder =
- new DashboardAdapter.DashboardItemHolder(view);
- final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
- tile.getMetaData().putInt(TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_HINT,
- R.color.memory_critical);
- doReturn(Icon.createWithResource(context, R.drawable.ic_settings))
- .when(tile).getIcon(context);
- final IconCache iconCache = new IconCache(context);
- mDashboardAdapter = new DashboardAdapter(context, null /* savedInstanceState */,
- null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */);
- ReflectionHelpers.setField(mDashboardAdapter, "mCache", iconCache);
-
- doReturn("another.package").when(context).getPackageName();
- mDashboardAdapter.onBindTile(holder, tile);
-
- final RoundedHomepageIcon homepageIcon = (RoundedHomepageIcon) iconCache.getIcon(
- tile.getIcon(context));
- assertThat(homepageIcon.mBackgroundColor)
- .isEqualTo(RuntimeEnvironment.application.getColor(R.color.memory_critical));
- }
-
@Test
public void onBindTile_externalTile_usingRoundedHomepageIcon_shouldNotUpdateIcon() {
final Context context = RuntimeEnvironment.application;
@@ -315,7 +265,7 @@ public class DashboardAdapterTest {
any(RoundedHomepageIcon.class));
}
- private List makeSuggestionsV2(String... pkgNames) {
+ private List makeSuggestions(String... pkgNames) {
final List suggestions = new ArrayList<>();
for (String pkgName : pkgNames) {
final Suggestion suggestion = new Suggestion.Builder(pkgName)
diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardDataTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardDataTest.java
index dfa049446b7..d92cceb62a8 100644
--- a/tests/robotests/src/com/android/settings/dashboard/DashboardDataTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/DashboardDataTest.java
@@ -33,8 +33,8 @@ import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListUpdateCallback;
-import com.android.settings.dashboard.conditional.AirplaneModeCondition;
-import com.android.settings.dashboard.conditional.Condition;
+import com.android.settings.homepage.conditional.AirplaneModeCondition;
+import com.android.settings.homepage.conditional.Condition;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settingslib.drawer.CategoryKey;
import com.android.settingslib.drawer.DashboardCategory;
diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java
index adc1ac0ff6e..22c589c8fd6 100644
--- a/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java
@@ -49,7 +49,7 @@ import android.os.UserManager;
import androidx.fragment.app.FragmentActivity;
import androidx.preference.Preference;
-import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.testutils.FakeFeatureFactory;
@@ -59,7 +59,6 @@ import com.android.settings.testutils.shadow.ShadowTileUtils;
import com.android.settings.testutils.shadow.ShadowUserManager;
import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
import com.android.settingslib.drawer.CategoryKey;
-import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.Tile;
import com.android.settingslib.drawer.TileUtils;
@@ -88,19 +87,19 @@ public class DashboardFeatureProviderImplTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private UserManager mUserManager;
@Mock
- private CategoryManager mCategoryManager;
- @Mock
private PackageManager mPackageManager;
private FakeFeatureFactory mFeatureFactory;
private Context mContext;
private ActivityInfo mActivityInfo;
private DashboardFeatureProviderImpl mImpl;
+ private boolean mForceRoundedIcon;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
+ mForceRoundedIcon = false;
mActivityInfo = new ActivityInfo();
mActivityInfo.packageName = "pkg";
mActivityInfo.name = "class";
@@ -127,7 +126,7 @@ public class DashboardFeatureProviderImplTest {
doReturn(Icon.createWithBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565)))
.when(tile).getIcon(any(Context.class));
mActivityInfo.metaData.putString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS, "HI");
- mImpl.bindPreferenceToTile(mActivity, MetricsProto.MetricsEvent.SETTINGS_GESTURES,
+ mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.SETTINGS_GESTURES,
preference, tile, "123", Preference.DEFAULT_ORDER);
assertThat(preference.getTitle()).isEqualTo(tile.title);
@@ -144,7 +143,7 @@ public class DashboardFeatureProviderImplTest {
mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10);
final Tile tile = new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
- mImpl.bindPreferenceToTile(mActivity, MetricsProto.MetricsEvent.SETTINGS_GESTURES,
+ mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.SETTINGS_GESTURES,
preference, tile, "123", Preference.DEFAULT_ORDER);
assertThat(preference.getFragment()).isNull();
@@ -163,7 +162,7 @@ public class DashboardFeatureProviderImplTest {
when(mActivity.getApplicationContext().getSystemService(Context.USER_SERVICE))
.thenReturn(mUserManager);
- mImpl.bindPreferenceToTile(mActivity, MetricsProto.MetricsEvent.SETTINGS_GESTURES,
+ mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.SETTINGS_GESTURES,
preference, tile, "123", Preference.DEFAULT_ORDER);
preference.getOnPreferenceClickListener().onPreferenceClick(null);
@@ -180,14 +179,14 @@ public class DashboardFeatureProviderImplTest {
when(mActivity.getSystemService(Context.USER_SERVICE))
.thenReturn(mUserManager);
- mImpl.bindPreferenceToTile(mActivity, MetricsProto.MetricsEvent.SETTINGS_GESTURES,
+ mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.SETTINGS_GESTURES,
preference, tile, "123", Preference.DEFAULT_ORDER);
preference.getOnPreferenceClickListener().onPreferenceClick(null);
verify(mFeatureFactory.metricsFeatureProvider).logDashboardStartIntent(
any(Context.class),
any(Intent.class),
- eq(MetricsProto.MetricsEvent.SETTINGS_GESTURES));
+ eq(MetricsEvent.SETTINGS_GESTURES));
verify(mActivity)
.startActivityForResultAsUser(any(Intent.class), anyInt(), any(UserHandle.class));
}
@@ -205,7 +204,7 @@ public class DashboardFeatureProviderImplTest {
when(mActivity.getApplicationContext().getPackageName())
.thenReturn(RuntimeEnvironment.application.getPackageName());
- mImpl.bindPreferenceToTile(mActivity, MetricsProto.MetricsEvent.SETTINGS_GESTURES,
+ mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.SETTINGS_GESTURES,
preference, tile, "123", Preference.DEFAULT_ORDER);
preference.getOnPreferenceClickListener().onPreferenceClick(null);
verify(mFeatureFactory.metricsFeatureProvider).logDashboardStartIntent(
@@ -219,7 +218,7 @@ public class DashboardFeatureProviderImplTest {
@Test
public void bindPreference_nullPreference_shouldIgnore() {
final Tile tile = mock(Tile.class);
- mImpl.bindPreferenceToTile(mActivity, MetricsProto.MetricsEvent.VIEW_UNKNOWN,
+ mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN,
null, tile, "123", Preference.DEFAULT_ORDER);
verifyZeroInteractions(tile);
@@ -229,7 +228,7 @@ public class DashboardFeatureProviderImplTest {
public void bindPreference_withNullKeyNullPriority_shouldGenerateKeyAndPriority() {
final Preference preference = new Preference(RuntimeEnvironment.application);
final Tile tile = new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
- mImpl.bindPreferenceToTile(mActivity, MetricsProto.MetricsEvent.VIEW_UNKNOWN,
+ mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN,
preference, tile, null /*key */, Preference.DEFAULT_ORDER);
assertThat(preference.getKey()).isNotNull();
@@ -240,7 +239,7 @@ public class DashboardFeatureProviderImplTest {
public void bindPreference_noSummary_shouldSetSummaryToPlaceholder() {
final Preference preference = new Preference(RuntimeEnvironment.application);
final Tile tile = new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
- mImpl.bindPreferenceToTile(mActivity, MetricsProto.MetricsEvent.VIEW_UNKNOWN,
+ mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN,
preference, tile, null /*key */, Preference.DEFAULT_ORDER);
assertThat(preference.getSummary())
@@ -252,7 +251,7 @@ public class DashboardFeatureProviderImplTest {
final Preference preference = new Preference(RuntimeEnvironment.application);
final Tile tile = new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
tile.summary = "test";
- mImpl.bindPreferenceToTile(mActivity, MetricsProto.MetricsEvent.VIEW_UNKNOWN,
+ mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN,
preference, tile, null /*key */, Preference.DEFAULT_ORDER);
assertThat(preference.getSummary()).isEqualTo(tile.summary);
@@ -266,7 +265,7 @@ public class DashboardFeatureProviderImplTest {
mActivityInfo.metaData.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY_URI,
"content://com.android.settings/tile_summary");
- mImpl.bindPreferenceToTile(mActivity, MetricsProto.MetricsEvent.VIEW_UNKNOWN,
+ mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN,
preference, tile, null /*key */, Preference.DEFAULT_ORDER);
assertThat(preference.getSummary()).isEqualTo(ShadowTileUtils.MOCK_SUMMARY);
@@ -277,7 +276,7 @@ public class DashboardFeatureProviderImplTest {
final Preference preference = new Preference(RuntimeEnvironment.application);
mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key");
final Tile tile = new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
- mImpl.bindPreferenceToTile(mActivity, MetricsProto.MetricsEvent.VIEW_UNKNOWN,
+ mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN,
preference, tile, null /* key */, Preference.DEFAULT_ORDER);
assertThat(preference.getKey()).isEqualTo(tile.getKey(mContext));
@@ -292,7 +291,7 @@ public class DashboardFeatureProviderImplTest {
mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key");
mActivityInfo.metaData.putString(TileUtils.META_DATA_PREFERENCE_ICON_URI,
"content://com.android.settings/tile_icon");
- mImpl.bindIcon(preference, tile);
+ mImpl.bindIcon(preference, tile, false /* forceRoundedIcon */);
assertThat(preference.getIcon()).isNotNull();
}
@@ -304,7 +303,7 @@ public class DashboardFeatureProviderImplTest {
mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10);
final Tile tile = new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
- mImpl.bindPreferenceToTile(mActivity, MetricsProto.MetricsEvent.VIEW_UNKNOWN,
+ mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN,
preference, tile, "123", baseOrder);
assertThat(preference.getOrder()).isEqualTo(tile.getOrder() + baseOrder);
@@ -317,7 +316,7 @@ public class DashboardFeatureProviderImplTest {
mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10);
final Tile tile = new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, testOrder);
- mImpl.bindPreferenceToTile(mActivity, MetricsProto.MetricsEvent.VIEW_UNKNOWN,
+ mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN,
preference, tile, "123", Preference.DEFAULT_ORDER);
assertThat(preference.getOrder()).isEqualTo(testOrder);
@@ -329,7 +328,7 @@ public class DashboardFeatureProviderImplTest {
final Tile tile = new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
mActivityInfo.metaData.putString(META_DATA_KEY_ORDER, "hello");
- mImpl.bindPreferenceToTile(mActivity, MetricsProto.MetricsEvent.VIEW_UNKNOWN,
+ mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN,
preference, tile, "123", Preference.DEFAULT_ORDER);
assertThat(preference.getOrder()).isEqualTo(Preference.DEFAULT_ORDER);
@@ -343,7 +342,7 @@ public class DashboardFeatureProviderImplTest {
mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key");
mActivityInfo.metaData.putString("com.android.settings.intent.action", "TestAction");
tile.userHandle = null;
- mImpl.bindPreferenceToTile(activity, MetricsProto.MetricsEvent.SETTINGS_GESTURES,
+ mImpl.bindPreferenceToTile(activity, mForceRoundedIcon, MetricsEvent.SETTINGS_GESTURES,
preference, tile, "123", Preference.DEFAULT_ORDER);
preference.performClick();
ShadowActivity shadowActivity = Shadows.shadowOf(activity);
@@ -352,7 +351,7 @@ public class DashboardFeatureProviderImplTest {
assertThat(launchIntent.getAction())
.isEqualTo("TestAction");
assertThat(launchIntent.getIntExtra(VisibilityLoggerMixin.EXTRA_SOURCE_METRICS_CATEGORY, 0))
- .isEqualTo(MetricsProto.MetricsEvent.SETTINGS_GESTURES);
+ .isEqualTo(MetricsEvent.SETTINGS_GESTURES);
}
@Test
@@ -367,7 +366,7 @@ public class DashboardFeatureProviderImplTest {
mActivityInfo.metaData.putString("com.android.settings.intent.action", "TestAction");
tile.userHandle = null;
- mImpl.bindPreferenceToTile(activity, MetricsProto.MetricsEvent.SETTINGS_GESTURES,
+ mImpl.bindPreferenceToTile(activity, mForceRoundedIcon, MetricsEvent.SETTINGS_GESTURES,
preference, tile, "123", Preference.DEFAULT_ORDER);
preference.performClick();
@@ -377,63 +376,11 @@ public class DashboardFeatureProviderImplTest {
assertThat(launchIntent).isNull();
}
- @Test
- public void getPreferences_noCategory_shouldReturnNull() {
- mImpl = new DashboardFeatureProviderImpl(mActivity);
- ReflectionHelpers.setField(mImpl, "mCategoryManager", mCategoryManager);
- when(mCategoryManager.getTilesByCategory(mActivity, CategoryKey.CATEGORY_HOMEPAGE))
- .thenReturn(null);
-
- assertThat(mImpl.getPreferencesForCategory(null, null,
- MetricsProto.MetricsEvent.SETTINGS_GESTURES, CategoryKey.CATEGORY_HOMEPAGE))
- .isNull();
- }
-
- @Test
- public void getPreferences_noTileForCategory_shouldReturnNull() {
- mImpl = new DashboardFeatureProviderImpl(mActivity);
- ReflectionHelpers.setField(mImpl, "mCategoryManager", mCategoryManager);
- when(mCategoryManager.getTilesByCategory(mActivity, CategoryKey.CATEGORY_HOMEPAGE))
- .thenReturn(new DashboardCategory(CategoryKey.CATEGORY_HOMEPAGE));
-
- assertThat(mImpl.getPreferencesForCategory(null, null,
- MetricsProto.MetricsEvent.SETTINGS_GESTURES, CategoryKey.CATEGORY_HOMEPAGE))
- .isNull();
- }
-
- @Test
- public void getPreferences_hasTileForCategory_shouldReturnPrefList() {
- mImpl = new DashboardFeatureProviderImpl(mActivity);
- ReflectionHelpers.setField(mImpl, "mCategoryManager", mCategoryManager);
- final DashboardCategory category = new DashboardCategory(CategoryKey.CATEGORY_HOMEPAGE);
- category.addTile(new Tile(mActivityInfo, category.key));
- when(mCategoryManager
- .getTilesByCategory(any(Context.class), eq(CategoryKey.CATEGORY_HOMEPAGE)))
- .thenReturn(category);
-
- assertThat(mImpl.getPreferencesForCategory(mActivity,
- ShadowApplication.getInstance().getApplicationContext(),
- MetricsProto.MetricsEvent.SETTINGS_GESTURES,
- CategoryKey.CATEGORY_HOMEPAGE).isEmpty())
- .isFalse();
- }
-
@Test
public void testGetExtraIntentAction_shouldReturnNull() {
assertThat(mImpl.getExtraIntentAction()).isNull();
}
- @Test
- public void testShouldTintIcon_enabledInResources_shouldBeTrue() {
- assertThat(mImpl.shouldTintIcon()).isTrue();
- }
-
- @Test
- @Config(qualifiers = "mcc999")
- public void testShouldTintIcon_disabledInResources_shouldBeFalse() {
- assertThat(mImpl.shouldTintIcon()).isFalse();
- }
-
@Test
public void openTileIntent_profileSelectionDialog_shouldShow() {
final Tile tile = new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentRegistryTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentRegistryTest.java
new file mode 100644
index 00000000000..1f68e2f04ec
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentRegistryTest.java
@@ -0,0 +1,33 @@
+/*
+ * 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.settings.dashboard;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class DashboardFragmentRegistryTest {
+ @Test
+ public void pageAndKeyShouldHave1to1Mapping() {
+ assertThat(DashboardFragmentRegistry.CATEGORY_KEY_TO_PARENT_MAP.size())
+ .isEqualTo(DashboardFragmentRegistry.PARENT_TO_CATEGORY_KEY_MAP.size());
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardSummaryTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardSummaryTest.java
index d93cd810caa..4eaeaf23e10 100644
--- a/tests/robotests/src/com/android/settings/dashboard/DashboardSummaryTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/DashboardSummaryTest.java
@@ -30,8 +30,8 @@ import static org.mockito.Mockito.when;
import android.app.Activity;
import android.content.Context;
-import com.android.settings.dashboard.conditional.ConditionManager;
-import com.android.settings.dashboard.conditional.FocusRecyclerView;
+import com.android.settings.homepage.conditional.ConditionManager;
+import com.android.settings.homepage.conditional.FocusRecyclerView;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settingslib.drawer.CategoryKey;
diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java
index bd9db3daba9..6398361fb6e 100644
--- a/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java
@@ -355,7 +355,7 @@ public class DataUsageSummaryPreferenceControllerTest {
mActivity, null, null, null);
final SubscriptionInfo subInfo = new SubscriptionInfo(0, "123456", 0, "name", "carrier",
- 0, 0, "number", 0, null, "123", "456", "ZX");
+ 0, 0, "number", 0, null, "123", "456", "ZX", false, null, null);
when(mSubscriptionManager.getDefaultDataSubscriptionInfo()).thenReturn(subInfo);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/StorageSettingsTest.java b/tests/robotests/src/com/android/settings/deviceinfo/StorageSettingsTest.java
index 943bd9d2630..cb02c76846f 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/StorageSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/StorageSettingsTest.java
@@ -17,7 +17,6 @@
package com.android.settings.deviceinfo;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -25,14 +24,9 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Activity;
-import android.app.usage.StorageStatsManager;
import android.content.Intent;
-import android.icu.text.NumberFormat;
import android.os.storage.VolumeInfo;
-import android.text.format.Formatter;
-import com.android.settings.R;
-import com.android.settings.dashboard.SummaryLoader;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settingslib.deviceinfo.StorageManagerVolumeProvider;
@@ -41,8 +35,6 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
@@ -65,34 +57,6 @@ public class StorageSettingsTest {
when(mStorageManagerVolumeProvider.getVolumes()).thenReturn(mVolumes);
}
- @Test
- public void updateSummary_shouldDisplayUsedPercentAndFreeSpace() throws Exception {
- final SummaryLoader loader = mock(SummaryLoader.class);
- final SummaryLoader.SummaryProvider provider =
- StorageSettings.SUMMARY_PROVIDER_FACTORY.createSummaryProvider(mActivity, loader);
- final VolumeInfo volumeInfo = mVolumes.get(0);
- when(volumeInfo.isMountedReadable()).thenReturn(true);
- when(volumeInfo.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
- when(mStorageManagerVolumeProvider
- .getTotalBytes(nullable(StorageStatsManager.class), nullable(VolumeInfo.class)))
- .thenReturn(500L);
- when(mStorageManagerVolumeProvider
- .getFreeBytes(nullable(StorageStatsManager.class), nullable(VolumeInfo.class)))
- .thenReturn(0L);
-
- ReflectionHelpers
- .setField(provider, "mStorageManagerVolumeProvider", mStorageManagerVolumeProvider);
- ReflectionHelpers.setField(provider, "mContext", RuntimeEnvironment.application);
-
- provider.setListening(true);
-
- final String percentage = NumberFormat.getPercentInstance().format(1);
- final String freeSpace = Formatter.formatFileSize(RuntimeEnvironment.application, 0);
- verify(loader).setSummary(provider,
- RuntimeEnvironment.application.getString(
- R.string.storage_summary, percentage, freeSpace));
- }
-
@Test
public void handlePublicVolumeClick_startsANonNullActivityWhenVolumeHasNoBrowse() {
VolumeInfo volumeInfo = mock(VolumeInfo.class, RETURNS_DEEP_STUBS);
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/TopLevelStoragePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/TopLevelStoragePreferenceControllerTest.java
new file mode 100644
index 00000000000..00484df3efc
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/deviceinfo/TopLevelStoragePreferenceControllerTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.settings.deviceinfo;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.usage.StorageStatsManager;
+import android.content.Context;
+import android.icu.text.NumberFormat;
+import android.os.storage.VolumeInfo;
+import android.text.format.Formatter;
+
+import com.android.settings.R;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.deviceinfo.StorageManagerVolumeProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class TopLevelStoragePreferenceControllerTest {
+
+ @Mock
+ private StorageManagerVolumeProvider mStorageManagerVolumeProvider;
+
+ private Context mContext;
+ private TopLevelStoragePreferenceController mController;
+ private List mVolumes;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = RuntimeEnvironment.application;
+ mVolumes = new ArrayList<>();
+ mVolumes.add(mock(VolumeInfo.class, RETURNS_DEEP_STUBS));
+ when(mStorageManagerVolumeProvider.getVolumes()).thenReturn(mVolumes);
+
+ mController = new TopLevelStoragePreferenceController(mContext, "test_key");
+ }
+
+ @Test
+ public void updateSummary_shouldDisplayUsedPercentAndFreeSpace() throws Exception {
+ final VolumeInfo volumeInfo = mVolumes.get(0);
+ when(volumeInfo.isMountedReadable()).thenReturn(true);
+ when(volumeInfo.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
+ when(mStorageManagerVolumeProvider
+ .getTotalBytes(nullable(StorageStatsManager.class), nullable(VolumeInfo.class)))
+ .thenReturn(500L);
+ when(mStorageManagerVolumeProvider
+ .getFreeBytes(nullable(StorageStatsManager.class), nullable(VolumeInfo.class)))
+ .thenReturn(0L);
+
+ ReflectionHelpers.setField(mController,
+ "mStorageManagerVolumeProvider", mStorageManagerVolumeProvider);
+
+ final String percentage = NumberFormat.getPercentInstance().format(1);
+ final String freeSpace = Formatter.formatFileSize(RuntimeEnvironment.application, 0);
+ assertThat(mController.getSummary()).isEqualTo(
+ RuntimeEnvironment.application.getString(
+ R.string.storage_summary, percentage, freeSpace));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatterySaverControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatterySaverControllerTest.java
index ad5537cf2e2..ac93941a7d0 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatterySaverControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatterySaverControllerTest.java
@@ -56,7 +56,6 @@ public class BatterySaverControllerTest {
mBatterySaverController = spy(new BatterySaverController(mContext));
ReflectionHelpers.setField(mBatterySaverController, "mPowerManager", mPowerManager);
ReflectionHelpers.setField(mBatterySaverController, "mBatterySaverPref", mBatterySaverPref);
- doNothing().when(mBatterySaverController).refreshConditionManager();
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
index 68d9994cf9c..cf1a5f346d7 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
@@ -16,7 +16,9 @@
package com.android.settings.fuelgauge;
import static com.android.settings.fuelgauge.PowerUsageSummary.MENU_ADVANCED_BATTERY;
+
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
@@ -40,6 +42,8 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
+import androidx.loader.app.LoaderManager;
+
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.R;
@@ -50,7 +54,6 @@ import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.XmlTestUtils;
import com.android.settings.testutils.shadow.SettingsShadowResources;
-import com.android.settingslib.core.AbstractPreferenceController;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -69,8 +72,6 @@ import org.robolectric.annotation.Config;
import java.util.ArrayList;
import java.util.List;
-import androidx.loader.app.LoaderManager;
-
// TODO: Improve this test class so that it starts up the real activity and fragment.
@RunWith(SettingsRobolectricTestRunner.class)
@Config(shadows = {
@@ -343,18 +344,6 @@ public class PowerUsageSummaryTest {
verify(mFragment).restartBatteryTipLoader();
}
- @Test
- public void getDashboardLabel_returnsCorrectLabel() {
- BatteryInfo info = new BatteryInfo();
- info.batteryPercentString = "3%";
- assertThat(PowerUsageSummary.getDashboardLabel(mRealContext, info))
- .isEqualTo(info.batteryPercentString);
-
- info.remainingLabel = "Phone will shut down soon";
- assertThat(PowerUsageSummary.getDashboardLabel(mRealContext, info))
- .isEqualTo("3% - Phone will shut down soon");
- }
-
public static class TestFragment extends PowerUsageSummary {
private Context mContext;
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceControllerTest.java
new file mode 100644
index 00000000000..b1bc074eb50
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceControllerTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.settings.fuelgauge;
+
+import static com.android.settings.fuelgauge.TopLevelBatteryPreferenceController.getDashboardLabel;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class TopLevelBatteryPreferenceControllerTest {
+
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ }
+
+ @Test
+ public void getDashboardLabel_returnsCorrectLabel() {
+ BatteryInfo info = new BatteryInfo();
+ info.batteryPercentString = "3%";
+ assertThat(getDashboardLabel(mContext, info))
+ .isEqualTo(info.batteryPercentString);
+
+ info.remainingLabel = "Phone will shut down soon";
+ assertThat(getDashboardLabel(mContext, info))
+ .isEqualTo("3% - Phone will shut down soon");
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/homepage/CardContentProviderTest.java b/tests/robotests/src/com/android/settings/homepage/CardContentProviderTest.java
new file mode 100644
index 00000000000..bf1527a4a42
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/homepage/CardContentProviderTest.java
@@ -0,0 +1,147 @@
+/*
+ * 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.settings.homepage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class CardContentProviderTest {
+
+ private Context mContext;
+ private CardContentProvider mProvider;
+ private Uri mUri;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mProvider = Robolectric.setupContentProvider(CardContentProvider.class);
+ mUri = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(CardContentProvider.CARD_AUTHORITY)
+ .path(CardDatabaseHelper.CARD_TABLE)
+ .build();
+ }
+
+ @After
+ public void cleanUp() {
+ CardDatabaseHelper.getInstance(mContext).close();
+ CardDatabaseHelper.sCardDatabaseHelper = null;
+ }
+
+ @Test
+ public void cardData_insert() {
+ final int cnt_before_instert = getRowCount();
+ mContext.getContentResolver().insert(mUri, insertOneRow());
+ final int cnt_after_instert = getRowCount();
+
+ assertThat(cnt_after_instert - cnt_before_instert).isEqualTo(1);
+ }
+
+ @Test
+ public void cardData_query() {
+ mContext.getContentResolver().insert(mUri, insertOneRow());
+ final int count = getRowCount();
+
+ assertThat(count).isGreaterThan(0);
+ }
+
+ @Test
+ public void cardData_delete() {
+ final ContentResolver contentResolver = mContext.getContentResolver();
+ contentResolver.insert(mUri, insertOneRow());
+ final int del_count = contentResolver.delete(mUri, null, null);
+
+ assertThat(del_count).isGreaterThan(0);
+ }
+
+ @Test
+ public void cardData_update() {
+ final ContentResolver contentResolver = mContext.getContentResolver();
+ contentResolver.insert(mUri, insertOneRow());
+
+ final double updatingScore= 0.87;
+ final ContentValues values = new ContentValues();
+ values.put(CardDatabaseHelper.CardColumns.SCORE, updatingScore);
+ final String strWhere = CardDatabaseHelper.CardColumns.NAME + "=?";
+ final String[] selectionArgs = {"auto_rotate"};
+ final int update_count = contentResolver.update(mUri, values, strWhere, selectionArgs);
+
+ assertThat(update_count).isGreaterThan(0);
+
+ final String[] columns = {CardDatabaseHelper.CardColumns.SCORE};
+ final Cursor cr = contentResolver.query(mUri, columns, strWhere, selectionArgs, null);
+ cr.moveToFirst();
+ final double qryScore = cr.getDouble(0);
+
+ cr.close();
+ assertThat(qryScore).isEqualTo(updatingScore);
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void getType_shouldCrash() {
+ mProvider.getType(null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void invalid_Uri_shouldCrash() {
+ final Uri invalid_Uri = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(CardContentProvider.CARD_AUTHORITY)
+ .path("Invalid_table")
+ .build();
+
+ mProvider.getTableFromMatch(invalid_Uri);
+ }
+
+ private ContentValues insertOneRow() {
+ final ContentValues values = new ContentValues();
+ values.put(CardDatabaseHelper.CardColumns.NAME, "auto_rotate");
+ values.put(CardDatabaseHelper.CardColumns.TYPE, 0);
+ values.put(CardDatabaseHelper.CardColumns.SCORE, 0.9);
+ values.put(CardDatabaseHelper.CardColumns.SLICE_URI,
+ "content://com.android.settings.slices/action/auto_rotate");
+ values.put(CardDatabaseHelper.CardColumns.CATEGORY, 2);
+ values.put(CardDatabaseHelper.CardColumns.PACKAGE_NAME, "com.android.settings");
+ values.put(CardDatabaseHelper.CardColumns.APP_VERSION, "1.0.0");
+
+ return values;
+ }
+
+ private int getRowCount() {
+ final ContentResolver contentResolver = mContext.getContentResolver();
+ final Cursor cr = contentResolver.query(mUri, null, null, null);
+ final int count = cr.getCount();
+ cr.close();
+ return count;
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/homepage/CardDatabaseHelperTest.java b/tests/robotests/src/com/android/settings/homepage/CardDatabaseHelperTest.java
new file mode 100644
index 00000000000..b6ed358a5d5
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/homepage/CardDatabaseHelperTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.settings.homepage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class CardDatabaseHelperTest {
+
+ private Context mContext;
+ private CardDatabaseHelper mCardDatabaseHelper;
+ private SQLiteDatabase mDatabase;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mCardDatabaseHelper = CardDatabaseHelper.getInstance(mContext);
+ mDatabase = mCardDatabaseHelper.getWritableDatabase();
+ }
+
+ @After
+ public void cleanUp() {
+ CardDatabaseHelper.getInstance(mContext).close();
+ CardDatabaseHelper.sCardDatabaseHelper = null;
+ }
+
+ @Test
+ public void testDatabaseSchema() {
+ final Cursor cursor = mDatabase.rawQuery("SELECT * FROM " + CardDatabaseHelper.CARD_TABLE,
+ null);
+ final String[] columnNames = cursor.getColumnNames();
+
+ final String[] expectedNames = {
+ CardDatabaseHelper.CardColumns.NAME,
+ CardDatabaseHelper.CardColumns.TYPE,
+ CardDatabaseHelper.CardColumns.SCORE,
+ CardDatabaseHelper.CardColumns.SLICE_URI,
+ CardDatabaseHelper.CardColumns.CATEGORY,
+ CardDatabaseHelper.CardColumns.AVAILABILITY_URI,
+ CardDatabaseHelper.CardColumns.LOCALIZED_TO_LOCALE,
+ CardDatabaseHelper.CardColumns.PACKAGE_NAME,
+ CardDatabaseHelper.CardColumns.APP_VERSION,
+ CardDatabaseHelper.CardColumns.TITLE_RES_NAME,
+ CardDatabaseHelper.CardColumns.TITLE_TEXT,
+ CardDatabaseHelper.CardColumns.SUMMARY_RES_NAME,
+ CardDatabaseHelper.CardColumns.SUMMARY_TEXT,
+ CardDatabaseHelper.CardColumns.ICON_RES_NAME,
+ CardDatabaseHelper.CardColumns.ICON_RES_ID,
+ CardDatabaseHelper.CardColumns.CARD_ACTION,
+ CardDatabaseHelper.CardColumns.EXPIRE_TIME_MS,
+ };
+
+ assertThat(columnNames).isEqualTo(expectedNames);
+ cursor.close();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/homepage/TopLevelSettingsTest.java b/tests/robotests/src/com/android/settings/homepage/TopLevelSettingsTest.java
new file mode 100644
index 00000000000..83ab948935e
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/homepage/TopLevelSettingsTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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.settings.homepage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class TopLevelSettingsTest {
+ private Context mContext;
+ private TopLevelSettings mSettings;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mSettings = spy(new TopLevelSettings());
+ when(mSettings.getContext()).thenReturn(mContext);
+ }
+
+ @Test
+ public void shouldForceRoundedIcon_true() {
+ assertThat(mSettings.shouldForceRoundedIcon()).isTrue();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/dashboard/conditional/AbnormalRingerConditionBaseTest.java b/tests/robotests/src/com/android/settings/homepage/conditional/AbnormalRingerConditionBaseTest.java
similarity index 98%
rename from tests/robotests/src/com/android/settings/dashboard/conditional/AbnormalRingerConditionBaseTest.java
rename to tests/robotests/src/com/android/settings/homepage/conditional/AbnormalRingerConditionBaseTest.java
index c72131fa8e2..efc5ceab889 100644
--- a/tests/robotests/src/com/android/settings/dashboard/conditional/AbnormalRingerConditionBaseTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/conditional/AbnormalRingerConditionBaseTest.java
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package com.android.settings.dashboard.conditional;
+package com.android.settings.homepage.conditional;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.when;
import android.content.Context;
diff --git a/tests/robotests/src/com/android/settings/dashboard/conditional/BackgroundDataConditionTest.java b/tests/robotests/src/com/android/settings/homepage/conditional/BackgroundDataConditionTest.java
similarity index 97%
rename from tests/robotests/src/com/android/settings/dashboard/conditional/BackgroundDataConditionTest.java
rename to tests/robotests/src/com/android/settings/homepage/conditional/BackgroundDataConditionTest.java
index 725be7962d4..289fa7c22ba 100644
--- a/tests/robotests/src/com/android/settings/dashboard/conditional/BackgroundDataConditionTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/conditional/BackgroundDataConditionTest.java
@@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.settings.dashboard.conditional;
+package com.android.settings.homepage.conditional;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
diff --git a/tests/robotests/src/com/android/settings/dashboard/conditional/BatterySaverConditionTest.java b/tests/robotests/src/com/android/settings/homepage/conditional/BatterySaverConditionTest.java
similarity index 98%
rename from tests/robotests/src/com/android/settings/dashboard/conditional/BatterySaverConditionTest.java
rename to tests/robotests/src/com/android/settings/homepage/conditional/BatterySaverConditionTest.java
index fa05ae030f6..323e6c5ff91 100644
--- a/tests/robotests/src/com/android/settings/dashboard/conditional/BatterySaverConditionTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/conditional/BatterySaverConditionTest.java
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package com.android.settings.dashboard.conditional;
+package com.android.settings.homepage.conditional;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
diff --git a/tests/robotests/src/com/android/settings/dashboard/conditional/ConditionAdapterTest.java b/tests/robotests/src/com/android/settings/homepage/conditional/ConditionAdapterTest.java
similarity index 98%
rename from tests/robotests/src/com/android/settings/dashboard/conditional/ConditionAdapterTest.java
rename to tests/robotests/src/com/android/settings/homepage/conditional/ConditionAdapterTest.java
index 62d1dfaa6af..663c630f3db 100644
--- a/tests/robotests/src/com/android/settings/dashboard/conditional/ConditionAdapterTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/conditional/ConditionAdapterTest.java
@@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.settings.dashboard.conditional;
+package com.android.settings.homepage.conditional;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -24,6 +25,8 @@ import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
+import androidx.recyclerview.widget.RecyclerView;
+
import com.android.settings.R;
import com.android.settings.dashboard.DashboardAdapter;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
@@ -38,8 +41,6 @@ import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.List;
-import androidx.recyclerview.widget.RecyclerView;
-
@RunWith(SettingsRobolectricTestRunner.class)
public class ConditionAdapterTest {
diff --git a/tests/robotests/src/com/android/settings/dashboard/conditional/ConditionTest.java b/tests/robotests/src/com/android/settings/homepage/conditional/ConditionTest.java
similarity index 98%
rename from tests/robotests/src/com/android/settings/dashboard/conditional/ConditionTest.java
rename to tests/robotests/src/com/android/settings/homepage/conditional/ConditionTest.java
index 6d5673151a2..981ef662ef5 100644
--- a/tests/robotests/src/com/android/settings/dashboard/conditional/ConditionTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/conditional/ConditionTest.java
@@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License
*/
-package com.android.settings.dashboard.conditional;
+package com.android.settings.homepage.conditional;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
diff --git a/tests/robotests/src/com/android/settings/dashboard/conditional/DndConditionTest.java b/tests/robotests/src/com/android/settings/homepage/conditional/DndConditionTest.java
similarity index 98%
rename from tests/robotests/src/com/android/settings/dashboard/conditional/DndConditionTest.java
rename to tests/robotests/src/com/android/settings/homepage/conditional/DndConditionTest.java
index 29ad60f713b..9da1f9c3bb5 100644
--- a/tests/robotests/src/com/android/settings/dashboard/conditional/DndConditionTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/conditional/DndConditionTest.java
@@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License
*/
-package com.android.settings.dashboard.conditional;
+package com.android.settings.homepage.conditional;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.never;
diff --git a/tests/robotests/src/com/android/settings/dashboard/conditional/RingerMutedConditionTest.java b/tests/robotests/src/com/android/settings/homepage/conditional/RingerMutedConditionTest.java
similarity index 98%
rename from tests/robotests/src/com/android/settings/dashboard/conditional/RingerMutedConditionTest.java
rename to tests/robotests/src/com/android/settings/homepage/conditional/RingerMutedConditionTest.java
index 66ef5a039b3..408aa137891 100644
--- a/tests/robotests/src/com/android/settings/dashboard/conditional/RingerMutedConditionTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/conditional/RingerMutedConditionTest.java
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-package com.android.settings.dashboard.conditional;
+package com.android.settings.homepage.conditional;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
diff --git a/tests/robotests/src/com/android/settings/dashboard/conditional/RingerVibrateConditionTest.java b/tests/robotests/src/com/android/settings/homepage/conditional/RingerVibrateConditionTest.java
similarity index 97%
rename from tests/robotests/src/com/android/settings/dashboard/conditional/RingerVibrateConditionTest.java
rename to tests/robotests/src/com/android/settings/homepage/conditional/RingerVibrateConditionTest.java
index a134fac5721..e886236e209 100644
--- a/tests/robotests/src/com/android/settings/dashboard/conditional/RingerVibrateConditionTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/conditional/RingerVibrateConditionTest.java
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-package com.android.settings.dashboard.conditional;
+package com.android.settings.homepage.conditional;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.when;
import android.content.Context;
diff --git a/tests/robotests/src/com/android/settings/dashboard/conditional/WorkModeConditionTest.java b/tests/robotests/src/com/android/settings/homepage/conditional/WorkModeConditionTest.java
similarity index 97%
rename from tests/robotests/src/com/android/settings/dashboard/conditional/WorkModeConditionTest.java
rename to tests/robotests/src/com/android/settings/homepage/conditional/WorkModeConditionTest.java
index 8ba6ecc3188..c846be0f854 100644
--- a/tests/robotests/src/com/android/settings/dashboard/conditional/WorkModeConditionTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/conditional/WorkModeConditionTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License
*/
-package com.android.settings.dashboard.conditional;
+package com.android.settings.homepage.conditional;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.spy;
diff --git a/tests/robotests/src/com/android/settings/network/TopLevelNetworkEntryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/TopLevelNetworkEntryPreferenceControllerTest.java
new file mode 100644
index 00000000000..22aaf2a8aaf
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/network/TopLevelNetworkEntryPreferenceControllerTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.settings.network;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.ShadowRestrictedLockUtils;
+import com.android.settings.wifi.WifiMasterSwitchPreferenceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(shadows = ShadowRestrictedLockUtils.class)
+public class TopLevelNetworkEntryPreferenceControllerTest {
+
+ @Mock
+ private WifiMasterSwitchPreferenceController mWifiPreferenceController;
+ @Mock
+ private MobileNetworkPreferenceController mMobileNetworkPreferenceController;
+ @Mock
+ private TetherPreferenceController mTetherPreferenceController;
+
+ private Context mContext;
+ private TopLevelNetworkEntryPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mController = new TopLevelNetworkEntryPreferenceController(mContext, "test_key");
+
+ ReflectionHelpers.setField(mController, "mWifiPreferenceController",
+ mWifiPreferenceController);
+ ReflectionHelpers.setField(mController, "mMobileNetworkPreferenceController",
+ mMobileNetworkPreferenceController);
+ ReflectionHelpers.setField(mController, "mTetherPreferenceController",
+ mTetherPreferenceController);
+
+ }
+
+ @Test
+ public void getSummary_hasMobileAndHotspot_shouldReturnMobileSummary() {
+ when(mWifiPreferenceController.isAvailable()).thenReturn(true);
+ when(mMobileNetworkPreferenceController.isAvailable()).thenReturn(true);
+ when(mTetherPreferenceController.isAvailable()).thenReturn(true);
+
+ assertThat(mController.getSummary())
+ .isEqualTo("Wi\u2011Fi, mobile, data usage, and hotspot");
+ }
+
+ @Test
+ public void getSummary_noMobileOrHotspot_shouldReturnSimpleSummary() {
+ when(mWifiPreferenceController.isAvailable()).thenReturn(true);
+ when(mMobileNetworkPreferenceController.isAvailable()).thenReturn(false);
+ when(mTetherPreferenceController.isAvailable()).thenReturn(false);
+
+ assertThat(mController.getSummary())
+ .isEqualTo("Wi\u2011Fi and data usage");
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/security/SecuritySettingsTest.java b/tests/robotests/src/com/android/settings/security/TopLevelSecurityPreferenceControllerTest.java
similarity index 66%
rename from tests/robotests/src/com/android/settings/security/SecuritySettingsTest.java
rename to tests/robotests/src/com/android/settings/security/TopLevelSecurityPreferenceControllerTest.java
index f3cc4593ff5..17ba6d5f3f2 100644
--- a/tests/robotests/src/com/android/settings/security/SecuritySettingsTest.java
+++ b/tests/robotests/src/com/android/settings/security/TopLevelSecurityPreferenceControllerTest.java
@@ -17,7 +17,6 @@
package com.android.settings.security;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -26,7 +25,6 @@ import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import com.android.settings.R;
-import com.android.settings.dashboard.SummaryLoader;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
@@ -37,17 +35,15 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(SettingsRobolectricTestRunner.class)
-public class SecuritySettingsTest {
+public class TopLevelSecurityPreferenceControllerTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@Mock
- private SummaryLoader mSummaryLoader;
- @Mock
private FingerprintManager mFingerprintManager;
@Mock
private FaceManager mFaceManager;
- private SecuritySettings.SummaryProvider mSummaryProvider;
+ private TopLevelSecurityEntryPreferenceController mController;
@Before
public void setUp() {
@@ -56,87 +52,80 @@ public class SecuritySettingsTest {
.thenReturn(mFingerprintManager);
when(mContext.getSystemService(Context.FACE_SERVICE))
.thenReturn(mFaceManager);
- mSummaryProvider = new SecuritySettings.SummaryProvider(mContext, mSummaryLoader);
+ mController = new TopLevelSecurityEntryPreferenceController(mContext, "test_key");
}
@Test
- public void testSummaryProvider_notListening() {
- mSummaryProvider.setListening(false);
-
- verifyNoMoreInteractions(mSummaryLoader);
- }
-
- @Test
- public void testSummaryProvider_hasFace_hasStaticSummary() {
+ public void geSummary_hasFace_hasStaticSummary() {
when(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE))
.thenReturn(true);
when(mFaceManager.isHardwareDetected()).thenReturn(true);
- mSummaryProvider.setListening(true);
+ mController.getSummary();
- verify(mContext).getString(R.string.security_dashboard_summary_face);
+ verify(mContext).getText(R.string.security_dashboard_summary_face);
}
@Test
- public void testSummaryProvider_hasFingerPrint_hasStaticSummary() {
+ public void geSummary_hasFingerPrint_hasStaticSummary() {
when(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE))
.thenReturn(false);
when(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
.thenReturn(true);
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
- mSummaryProvider.setListening(true);
+ mController.getSummary();
- verify(mContext).getString(R.string.security_dashboard_summary);
+ verify(mContext).getText(R.string.security_dashboard_summary);
}
@Test
- public void testSummaryProvider_noFpFeature_shouldSetSummaryWithNoBiometrics() {
+ public void geSummary_noFpFeature_shouldSetSummaryWithNoBiometrics() {
when(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
.thenReturn(false);
when(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE))
.thenReturn(false);
- mSummaryProvider.setListening(true);
+ mController.getSummary();
- verify(mContext).getString(R.string.security_dashboard_summary_no_fingerprint);
+ verify(mContext).getText(R.string.security_dashboard_summary_no_fingerprint);
}
@Test
- public void testSummaryProvider_noFpHardware_shouldSetSummaryWithNoBiometrics() {
+ public void geSummary_noFpHardware_shouldSetSummaryWithNoBiometrics() {
when(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE))
.thenReturn(false);
when(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
.thenReturn(true);
when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
- mSummaryProvider.setListening(true);
+ mController.getSummary();
- verify(mContext).getString(R.string.security_dashboard_summary_no_fingerprint);
+ verify(mContext).getText(R.string.security_dashboard_summary_no_fingerprint);
}
@Test
- public void testSummaryProvider_noFaceFeature_shouldSetSummaryWithNoBiometrics() {
+ public void geSummary_noFaceFeature_shouldSetSummaryWithNoBiometrics() {
when(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
.thenReturn(false);
when(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE))
.thenReturn(false);
- mSummaryProvider.setListening(true);
+ mController.getSummary();
- verify(mContext).getString(R.string.security_dashboard_summary_no_fingerprint);
+ verify(mContext).getText(R.string.security_dashboard_summary_no_fingerprint);
}
@Test
- public void testSummaryProvider_noFaceHardware_shouldSetSummaryWithNoBiometrics() {
+ public void geSummary_noFaceHardware_shouldSetSummaryWithNoBiometrics() {
when(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE))
.thenReturn(true);
when(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
.thenReturn(false);
when(mFaceManager.isHardwareDetected()).thenReturn(false);
- mSummaryProvider.setListening(true);
+ mController.getSummary();
- verify(mContext).getString(R.string.security_dashboard_summary_no_fingerprint);
+ verify(mContext).getText(R.string.security_dashboard_summary_no_fingerprint);
}
}
diff --git a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
index ea2f2cac6e7..3cb502eee69 100644
--- a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
@@ -66,6 +66,9 @@ import java.util.List;
import java.util.Set;
import androidx.slice.Slice;
+import androidx.slice.SliceProvider;
+import androidx.slice.widget.SliceLiveData;
+import com.android.settings.R;
/**
* TODO Investigate using ShadowContentResolver.registerProviderInternal(String, ContentProvider)
@@ -81,7 +84,7 @@ public class SettingsSliceProviderTest {
private static final String SUMMARY = "summary";
private static final String SCREEN_TITLE = "screen title";
private static final String FRAGMENT_NAME = "fragment name";
- private static final int ICON = 1234; // I declare a thumb war
+ private static final int ICON = R.drawable.ic_settings;
private static final Uri URI = Uri.parse("content://com.android.settings.slices/test");
private static final String PREF_CONTROLLER = FakeToggleController.class.getName();
@@ -117,6 +120,8 @@ public class SettingsSliceProviderTest {
mManager = mock(SliceManager.class);
when(mContext.getSystemService(SliceManager.class)).thenReturn(mManager);
when(mManager.getPinnedSlices()).thenReturn(Collections.emptyList());
+
+ SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
}
@After
@@ -480,7 +485,7 @@ public class SettingsSliceProviderTest {
values.put(SlicesDatabaseHelper.IndexColumns.TITLE, TITLE);
values.put(SlicesDatabaseHelper.IndexColumns.SUMMARY, "s");
values.put(SlicesDatabaseHelper.IndexColumns.SCREENTITLE, "s");
- values.put(SlicesDatabaseHelper.IndexColumns.ICON_RESOURCE, 1234);
+ values.put(SlicesDatabaseHelper.IndexColumns.ICON_RESOURCE, R.drawable.ic_settings);
values.put(SlicesDatabaseHelper.IndexColumns.FRAGMENT, "test");
values.put(SlicesDatabaseHelper.IndexColumns.CONTROLLER, PREF_CONTROLLER);
values.put(SlicesDatabaseHelper.IndexColumns.PLATFORM_SLICE, isPlatformSlice);
diff --git a/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java b/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java
index b96c12886d7..48489f9ba4b 100644
--- a/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java
@@ -64,7 +64,7 @@ public class SliceBuilderUtilsTest {
private final String SCREEN_TITLE = "screen title";
private final String KEYWORDS = "a, b, c";
private final String FRAGMENT_NAME = "fragment name";
- private final int ICON = 1234; // I declare a thumb war
+ private final int ICON = R.drawable.ic_settings;
private final Uri URI = Uri.parse("content://com.android.settings.slices/test");
private final Class TOGGLE_CONTROLLER = FakeToggleController.class;
private final Class SLIDER_CONTROLLER = FakeSliderController.class;
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowBluetoothAdapter.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowBluetoothAdapter.java
index 91afb87021f..30837f41451 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowBluetoothAdapter.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowBluetoothAdapter.java
@@ -30,13 +30,19 @@ public class ShadowBluetoothAdapter extends org.robolectric.shadows.ShadowBlueto
private String mName;
private int mScanMode;
private int mState;
+ private List mSupportedProfiles = new ArrayList();
- /**
- * Do nothing, implement it to avoid null pointer error inside BluetoothAdapter
- */
@Implementation
public List getSupportedProfiles() {
- return new ArrayList();
+ return mSupportedProfiles;
+ }
+
+ public void addSupportedProfiles(int profile) {
+ mSupportedProfiles.add(profile);
+ }
+
+ public void clearSupportedProfiles() {
+ mSupportedProfiles.clear();
}
public void setName(String name) {
diff --git a/tests/robotests/src/com/android/settings/dashboard/RoundedHomepageIconTest.java b/tests/robotests/src/com/android/settings/widget/RoundedHomepageIconTest.java
similarity index 52%
rename from tests/robotests/src/com/android/settings/dashboard/RoundedHomepageIconTest.java
rename to tests/robotests/src/com/android/settings/widget/RoundedHomepageIconTest.java
index 0c90660b114..042341b24db 100644
--- a/tests/robotests/src/com/android/settings/dashboard/RoundedHomepageIconTest.java
+++ b/tests/robotests/src/com/android/settings/widget/RoundedHomepageIconTest.java
@@ -14,22 +14,32 @@
* limitations under the License.
*/
-package com.android.settings.dashboard;
+package com.android.settings.widget;
+
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_HINT;
import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.content.pm.ActivityInfo;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Icon;
import android.graphics.drawable.ShapeDrawable;
+import android.os.Bundle;
import com.android.settings.R;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.drawer.CategoryKey;
+import com.android.settingslib.drawer.Tile;
import org.junit.Before;
import org.junit.Test;
@@ -40,10 +50,15 @@ import org.robolectric.RuntimeEnvironment;
public class RoundedHomepageIconTest {
private Context mContext;
+ private ActivityInfo mActivityInfo;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
+ mActivityInfo = new ActivityInfo();
+ mActivityInfo.packageName = mContext.getPackageName();
+ mActivityInfo.name = "class";
+ mActivityInfo.metaData = new Bundle();
}
@Test
@@ -67,4 +82,34 @@ public class RoundedHomepageIconTest {
verify(background).setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_ATOP);
}
+
+ @Test
+ public void setBackgroundColor_externalTileWithBackgroundColorRawValue_shouldUpdateIcon() {
+ final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
+ mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB, 0xff0000);
+ doReturn(Icon.createWithResource(mContext, R.drawable.ic_settings))
+ .when(tile).getIcon(mContext);
+ final RoundedHomepageIcon icon =
+ new RoundedHomepageIcon(mContext, new ColorDrawable(Color.BLACK));
+
+ icon.setBackgroundColor(mContext, tile);
+ assertThat(icon.mBackgroundColor).isEqualTo(0xff0000);
+ }
+
+ @Test
+ public void onBindTile_externalTileWithBackgroundColorHint_shouldUpdateIcon() {
+ final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
+ mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_HINT,
+ R.color.memory_critical);
+ doReturn(Icon.createWithResource(mContext, R.drawable.ic_settings))
+ .when(tile).getIcon(mContext);
+
+ final RoundedHomepageIcon icon =
+ new RoundedHomepageIcon(mContext, new ColorDrawable(Color.BLACK));
+ icon.setBackgroundColor(mContext, tile);
+
+ assertThat(icon.mBackgroundColor)
+ .isEqualTo(mContext.getColor(R.color.memory_critical));
+ }
+
}