Refine the SchedulesProvider
Bug: 148995400
Test: make RunSettingsLibRoboTests -j56
Change-Id: I84dfa571eefffa74f49ed861fba7a49d1b50c659
diff --git a/packages/SettingsLib/SchedulesProvider/res/values/config.xml b/packages/SettingsLib/SchedulesProvider/res/values/config.xml
new file mode 100644
index 0000000..48f3e3e
--- /dev/null
+++ b/packages/SettingsLib/SchedulesProvider/res/values/config.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<resources>
+ <!-- Package name for the caller of the Schedules provider. -->
+ <string name="config_schedules_provider_caller_package" translatable="false">com.android.settings</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/ScheduleInfo.java b/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/ScheduleInfo.java
index 7d2b8e2..26bcd54 100644
--- a/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/ScheduleInfo.java
+++ b/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/ScheduleInfo.java
@@ -24,9 +24,9 @@
import androidx.annotation.NonNull;
/**
- * This is a schedule data item. It contains the schedule title text, the summary text which
- * displays on the summary of the Settings preference and an {@link Intent}. Intent is able to
- * launch the editing page of the schedule data when user clicks this item (preference).
+ * Schedule data item containing the schedule title text, the summary text which is displayed on the
+ * summary of the Settings preference and an {@link Intent} which Settings will launch when the
+ * user clicks on the preference.
*/
public class ScheduleInfo implements Parcelable {
private static final String TAG = "ScheduleInfo";
@@ -40,7 +40,7 @@
mIntent = builder.mIntent;
}
- protected ScheduleInfo(Parcel in) {
+ private ScheduleInfo(Parcel in) {
mTitle = in.readString();
mSummary = in.readString();
mIntent = in.readParcelable(Intent.class.getClassLoader());
@@ -48,8 +48,6 @@
/**
* Returns the title text.
- *
- * @return The title.
*/
public String getTitle() {
return mTitle;
@@ -57,15 +55,14 @@
/**
* Returns the summary text.
- *
- * @return The summary.
*/
public String getSummary() {
return mSummary;
}
/**
- * Returns an {@link Intent}.
+ * Returns an {@link Intent} which Settings will launch when the user clicks on a schedule
+ * preference.
*/
public Intent getIntent() {
return mIntent;
@@ -107,19 +104,15 @@
@NonNull
@Override
public String toString() {
- return "title : " + mTitle + " summary : " + mSummary + (mIntent == null
- ? " and intent is null." : ".");
+ return "title: " + mTitle + ", summary: " + mSummary + ", intent: " + mIntent;
}
/**
* A simple builder for {@link ScheduleInfo}.
*/
public static class Builder {
- @NonNull
private String mTitle;
- @NonNull
private String mSummary;
- @NonNull
private Intent mIntent;
/**
diff --git a/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/SchedulesProvider.java b/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/SchedulesProvider.java
index a423e47..28d5f07 100644
--- a/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/SchedulesProvider.java
+++ b/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/SchedulesProvider.java
@@ -21,19 +21,18 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.SystemProperties;
+import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.ArrayList;
-import java.util.List;
import java.util.stream.Collectors;
/**
- * This provider is a bridge for client apps to provide the schedule data.
- * Client provider needs to implement their {@link #getScheduleInfoList()} and returns a list of
- * {@link ScheduleInfo}.
+ * A bridge for client apps to provide the schedule data. Client provider needs to implement
+ * {@link #getScheduleInfoList()} returning a list of {@link ScheduleInfo}.
*/
public abstract class SchedulesProvider extends ContentProvider {
public static final String METHOD_GENERATE_SCHEDULE_INFO_LIST = "generateScheduleInfoList";
@@ -46,9 +45,8 @@
}
@Override
- public final Cursor query(
- Uri uri, String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
+ public final Cursor query(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
throw new UnsupportedOperationException("Query operation is not supported currently.");
}
@@ -74,18 +72,24 @@
}
/**
- * Return the list of the schedule information.
- *
- * @return a list of the {@link ScheduleInfo}.
+ * Returns the list of the schedule information.
*/
public abstract ArrayList<ScheduleInfo> getScheduleInfoList();
/**
- * Returns a bundle which contains a list of {@link ScheduleInfo} and data types:
- * scheduleInfoList : ArrayList<ScheduleInfo>
+ * Returns a bundle which contains a list of {@link ScheduleInfo}s:
+ *
+ * <ul>
+ * <li>scheduleInfoList: ArrayList<ScheduleInfo>
+ * </ul>
*/
@Override
public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
+ if (!TextUtils.equals(getCallingPackage(),
+ getContext().getText(R.string.config_schedules_provider_caller_package))) {
+ return null;
+ }
+
final Bundle bundle = new Bundle();
if (METHOD_GENERATE_SCHEDULE_INFO_LIST.equals(method)) {
final ArrayList<ScheduleInfo> scheduleInfoList = filterInvalidData(
@@ -98,36 +102,40 @@
}
/**
- * To filter the invalid schedule info.
+ * Filters our invalid schedule infos from {@code schedulesInfoList}.
*
- * @param scheduleInfoList The list of the {@link ScheduleInfo}.
- * @return The valid list of the {@link ScheduleInfo}.
+ * @return valid {@link SchedulesInfo}s if {@code schedulesInfoList} is not null. Otherwise,
+ * null.
*/
- private ArrayList<ScheduleInfo> filterInvalidData(ArrayList<ScheduleInfo> scheduleInfoList) {
+ @Nullable
+ private ArrayList<ScheduleInfo> filterInvalidData(
+ @Nullable ArrayList<ScheduleInfo> scheduleInfoList) {
if (scheduleInfoList == null) {
Log.d(TAG, "package : " + getContext().getPackageName() + " has no scheduling data.");
return null;
}
// Dump invalid data in debug mode.
if (SystemProperties.getInt("ro.debuggable", 0) == 1) {
- new Thread(() -> {
- dumpInvalidData(scheduleInfoList);
- }).start();
+ dumpInvalidData(scheduleInfoList);
}
- final List<ScheduleInfo> filteredList = scheduleInfoList
+ return scheduleInfoList
.stream()
- .filter(scheduleInfo -> scheduleInfo.isValid())
- .collect(Collectors.toList());
-
- return new ArrayList<>(filteredList);
+ .filter(ScheduleInfo::isValid)
+ .collect(Collectors.toCollection(ArrayList::new));
}
private void dumpInvalidData(ArrayList<ScheduleInfo> scheduleInfoList) {
- Log.d(TAG, "package : " + getContext().getPackageName()
- + " provided some scheduling data are invalid.");
- scheduleInfoList
+ final boolean hasInvalidData = scheduleInfoList
.stream()
- .filter(scheduleInfo -> !scheduleInfo.isValid())
- .forEach(scheduleInfo -> Log.d(TAG, scheduleInfo.toString()));
+ .anyMatch(scheduleInfo -> !scheduleInfo.isValid());
+
+ if (hasInvalidData) {
+ Log.w(TAG, "package : " + getContext().getPackageName()
+ + " provided some scheduling data that are invalid.");
+ scheduleInfoList
+ .stream()
+ .filter(scheduleInfo -> !scheduleInfo.isValid())
+ .forEach(scheduleInfo -> Log.w(TAG, scheduleInfo.toString()));
+ }
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/schedulesprovider/ScheduleInfoTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/schedulesprovider/ScheduleInfoTest.java
new file mode 100644
index 0000000..5ec89ed
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/schedulesprovider/ScheduleInfoTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2020 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.settingslib.schedulesprovider;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Intent;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class ScheduleInfoTest {
+ private static final String TEST_TITLE = "Night Light";
+ private static final String TEST_SUMMARY = "Night Light summary";
+ private static final String TEST_EMPTY_SUMMARY = "";
+
+ @Test
+ public void builder_usedValidArguments_isValid() {
+ final Intent intent = createTestIntent();
+ final ScheduleInfo info = createTestScheduleInfo(TEST_TITLE, TEST_SUMMARY, intent);
+
+ assertThat(info).isNotNull();
+ assertThat(info.isValid()).isTrue();
+ }
+
+ @Test
+ public void builder_useEmptySummary_isInvalid() {
+ final Intent intent = createTestIntent();
+ final ScheduleInfo info = createTestScheduleInfo(TEST_TITLE, TEST_EMPTY_SUMMARY, intent);
+
+ assertThat(info).isNotNull();
+ assertThat(info.isValid()).isFalse();
+ }
+
+ @Test
+ public void builder_intentIsNull_isInvalid() {
+ final ScheduleInfo info = new ScheduleInfo.Builder()
+ .setTitle(TEST_TITLE)
+ .setSummary(TEST_SUMMARY)
+ .build();
+
+ assertThat(info).isNotNull();
+ assertThat(info.isValid()).isFalse();
+ }
+
+ @Test
+ public void getTitle_setValidTitle_shouldReturnSameCorrectTitle() {
+ final Intent intent = createTestIntent();
+ final ScheduleInfo info = createTestScheduleInfo(TEST_TITLE, TEST_SUMMARY, intent);
+
+ assertThat(info.getTitle()).isEqualTo(TEST_TITLE);
+ }
+
+ @Test
+ public void getSummary_setValidSummary_shouldReturnSameCorrectSummary() {
+ final Intent intent = createTestIntent();
+ final ScheduleInfo info = createTestScheduleInfo(TEST_TITLE, TEST_SUMMARY, intent);
+
+ assertThat(info.getSummary()).isEqualTo(TEST_SUMMARY);
+ }
+
+ @Test
+ public void getIntent_setValidIntent_shouldReturnSameCorrectIntent() {
+ final Intent intent = createTestIntent();
+ final ScheduleInfo info = createTestScheduleInfo(TEST_TITLE, TEST_SUMMARY, intent);
+
+ assertThat(info.getIntent()).isEqualTo(intent);
+ }
+
+ private static Intent createTestIntent() {
+ return new Intent("android.settings.NIGHT_DISPLAY_SETTINGS").addCategory(
+ Intent.CATEGORY_DEFAULT);
+ }
+
+ private static ScheduleInfo createTestScheduleInfo(String title, String summary,
+ Intent intent) {
+ return new ScheduleInfo.Builder()
+ .setTitle(title)
+ .setSummary(summary)
+ .setIntent(intent)
+ .build();
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/schedulesprovider/SchedulesProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/schedulesprovider/SchedulesProviderTest.java
new file mode 100644
index 0000000..eb2e8e0
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/schedulesprovider/SchedulesProviderTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2020 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.settingslib.schedulesprovider;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.robolectric.Shadows.shadowOf;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.ArrayList;
+
+@RunWith(RobolectricTestRunner.class)
+public class SchedulesProviderTest {
+ private static final String INVALID_PACKAGE = "com.android.sunny";
+ private static final String VALID_PACKAGE = "com.android.settings";
+ private static final String INVALID_METHOD = "queryTestData";
+ private TestSchedulesProvider mProvider;
+
+ @Before
+ public void setUp() {
+ mProvider = Robolectric.setupContentProvider(TestSchedulesProvider.class);
+ shadowOf(mProvider).setCallingPackage(VALID_PACKAGE);
+ mProvider.setScheduleInfos(TestSchedulesProvider.createOneValidScheduleInfo());
+ }
+
+ @Test
+ public void call_invalidCallingPkg_returnNull() {
+ shadowOf(mProvider).setCallingPackage(INVALID_PACKAGE);
+
+ final Bundle bundle = mProvider.call(SchedulesProvider.METHOD_GENERATE_SCHEDULE_INFO_LIST,
+ null /* arg */, null /* extras */);
+
+ assertThat(bundle).isNull();
+ }
+
+ @Test
+ public void call_invalidMethod_returnBundleWithoutScheduleInfoData() {
+ final Bundle bundle = mProvider.call(INVALID_METHOD, null /* arg */, null /* extras */);
+
+ assertThat(bundle).isNotNull();
+ assertThat(bundle.containsKey(SchedulesProvider.BUNDLE_SCHEDULE_INFO_LIST)).isFalse();
+ }
+
+ @Test
+ public void call_validMethod_returnScheduleInfoData() {
+ final Bundle bundle = mProvider.call(SchedulesProvider.METHOD_GENERATE_SCHEDULE_INFO_LIST,
+ null /* arg */, null /* extras */);
+
+ assertThat(bundle).isNotNull();
+ assertThat(bundle.containsKey(SchedulesProvider.BUNDLE_SCHEDULE_INFO_LIST)).isTrue();
+ final ArrayList<ScheduleInfo> infos = bundle.getParcelableArrayList(
+ SchedulesProvider.BUNDLE_SCHEDULE_INFO_LIST);
+ assertThat(infos).hasSize(1);
+ }
+
+ @Test
+ public void call_addTwoValidData_returnScheduleInfoData() {
+ mProvider.setScheduleInfos(TestSchedulesProvider.createTwoValidScheduleInfos());
+ final Bundle bundle = mProvider.call(SchedulesProvider.METHOD_GENERATE_SCHEDULE_INFO_LIST,
+ null /* arg */, null /* extras */);
+
+ assertThat(bundle).isNotNull();
+ assertThat(bundle.containsKey(SchedulesProvider.BUNDLE_SCHEDULE_INFO_LIST)).isTrue();
+ final ArrayList<ScheduleInfo> infos = bundle.getParcelableArrayList(
+ SchedulesProvider.BUNDLE_SCHEDULE_INFO_LIST);
+ assertThat(infos).hasSize(2);
+ }
+
+ @Test
+ public void call_addTwoValidDataAndOneInvalidData_returnTwoScheduleInfoData() {
+ mProvider.setScheduleInfos(TestSchedulesProvider.createTwoValidAndOneInvalidScheduleInfo());
+ final Bundle bundle = mProvider.call(SchedulesProvider.METHOD_GENERATE_SCHEDULE_INFO_LIST,
+ null /* arg */, null /* extras */);
+
+ assertThat(bundle).isNotNull();
+ assertThat(bundle.containsKey(SchedulesProvider.BUNDLE_SCHEDULE_INFO_LIST)).isTrue();
+ final ArrayList<ScheduleInfo> infos = bundle.getParcelableArrayList(
+ SchedulesProvider.BUNDLE_SCHEDULE_INFO_LIST);
+ assertThat(infos).hasSize(2);
+ }
+
+ private static class TestSchedulesProvider extends SchedulesProvider {
+ private ArrayList<ScheduleInfo> mScheduleInfos = new ArrayList<>();
+
+ @Override
+ public ArrayList<ScheduleInfo> getScheduleInfoList() {
+ return mScheduleInfos;
+ }
+
+ void setScheduleInfos(ArrayList<ScheduleInfo> scheduleInfos) {
+ mScheduleInfos = scheduleInfos;
+ }
+
+ private static ArrayList<ScheduleInfo> createOneValidScheduleInfo() {
+ final ArrayList<ScheduleInfo> scheduleInfos = new ArrayList<>();
+ final Intent intent = new Intent("android.settings.NIGHT_DISPLAY_SETTINGS").addCategory(
+ Intent.CATEGORY_DEFAULT);
+ final ScheduleInfo info = new ScheduleInfo.Builder().setTitle(
+ "Night Light").setSummary("This a sunny test").setIntent(intent).build();
+ scheduleInfos.add(info);
+
+ return scheduleInfos;
+ }
+
+ private static ArrayList<ScheduleInfo> createTwoValidScheduleInfos() {
+ final ArrayList<ScheduleInfo> scheduleInfos = new ArrayList<>();
+ Intent intent = new Intent("android.settings.NIGHT_DISPLAY_SETTINGS").addCategory(
+ Intent.CATEGORY_DEFAULT);
+ ScheduleInfo info = new ScheduleInfo.Builder().setTitle(
+ "Night Light").setSummary("This a sunny test").setIntent(intent).build();
+ scheduleInfos.add(info);
+
+ intent = new Intent("android.settings.DISPLAY_SETTINGS").addCategory(
+ Intent.CATEGORY_DEFAULT);
+ info = new ScheduleInfo.Builder().setTitle("Display").setSummary(
+ "Display summary").setIntent(intent).build();
+ scheduleInfos.add(info);
+
+ return scheduleInfos;
+ }
+
+ private static ArrayList<ScheduleInfo> createTwoValidAndOneInvalidScheduleInfo() {
+ final ArrayList<ScheduleInfo> scheduleInfos = new ArrayList<>();
+ Intent intent = new Intent("android.settings.NIGHT_DISPLAY_SETTINGS").addCategory(
+ Intent.CATEGORY_DEFAULT);
+ ScheduleInfo info = new ScheduleInfo.Builder().setTitle(
+ "Night Light").setSummary("This a sunny test").setIntent(intent).build();
+ scheduleInfos.add(info);
+
+ intent = new Intent("android.settings.DISPLAY_SETTINGS").addCategory(
+ Intent.CATEGORY_DEFAULT);
+ info = new ScheduleInfo.Builder().setTitle("Display").setSummary(
+ "Display summary").setIntent(intent).build();
+ scheduleInfos.add(info);
+
+ intent = new Intent("android.settings.DISPLAY_SETTINGS").addCategory(
+ Intent.CATEGORY_DEFAULT);
+ info = new ScheduleInfo.Builder().setTitle("").setSummary("Display summary").setIntent(
+ intent).build();
+ scheduleInfos.add(info);
+
+ return scheduleInfos;
+ }
+ }
+}