diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 6ccb8d1b08d..0418d51dfc0 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -3312,13 +3312,13 @@ - + android:name=".slices.SliceBroadcastReceiver" > diff --git a/src/com/android/settings/search/DatabaseIndexingUtils.java b/src/com/android/settings/search/DatabaseIndexingUtils.java index 207d09fdb37..94ec65096e3 100644 --- a/src/com/android/settings/search/DatabaseIndexingUtils.java +++ b/src/com/android/settings/search/DatabaseIndexingUtils.java @@ -43,7 +43,7 @@ public class DatabaseIndexingUtils { private static final String TAG = "IndexingUtil"; - private static final String FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER = + public static final String FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER = "SEARCH_INDEX_DATA_PROVIDER"; /** diff --git a/src/com/android/settings/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java similarity index 67% rename from src/com/android/settings/SettingsSliceProvider.java rename to src/com/android/settings/slices/SettingsSliceProvider.java index 845dacddca3..22035d2195b 100644 --- a/src/com/android/settings/SettingsSliceProvider.java +++ b/src/com/android/settings/slices/SettingsSliceProvider.java @@ -14,28 +14,31 @@ * limitations under the License */ -package com.android.settings; +package com.android.settings.slices; import android.app.PendingIntent; -import android.app.slice.Slice; -import android.app.slice.SliceProvider; + import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.graphics.drawable.Icon; import android.net.Uri; -import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; +import com.android.settings.R; + +import androidx.app.slice.Slice; +import androidx.app.slice.SliceProvider; +import androidx.app.slice.builders.ListBuilder; + public class SettingsSliceProvider extends SliceProvider { public static final String SLICE_AUTHORITY = "com.android.settings.slices"; public static final String PATH_WIFI = "wifi"; public static final String ACTION_WIFI_CHANGED = "com.android.settings.slice.action.WIFI_CHANGED"; - // TODO -- Associate slice URI with search result instead of separate hardcoded thing - public static final String[] WIFI_SEARCH_TERMS = {"wi-fi", "wifi", "internet"}; + // TODO -- Associate slice URI with search result instead of separate hardcoded thing public static Uri getUri(String path) { return new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) @@ -44,7 +47,7 @@ public class SettingsSliceProvider extends SliceProvider { } @Override - public boolean onCreate() { + public boolean onCreateSliceProvider() { return true; } @@ -53,15 +56,15 @@ public class SettingsSliceProvider extends SliceProvider { String path = sliceUri.getPath(); switch (path) { case "/" + PATH_WIFI: - return createWifi(sliceUri); - + return createWifiSlice(sliceUri); } throw new IllegalArgumentException("Unrecognized slice uri: " + sliceUri); } - private Slice createWifi(Uri uri) { + + // TODO (b/70622039) remove this when the proper wifi slice is enabled. + private Slice createWifiSlice(Uri sliceUri) { // Get wifi state - String[] toggleHints; WifiManager wifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE); int wifiState = wifiManager.getWifiState(); boolean wifiEnabled = false; @@ -74,7 +77,6 @@ public class SettingsSliceProvider extends SliceProvider { case WifiManager.WIFI_STATE_ENABLED: case WifiManager.WIFI_STATE_ENABLING: state = wifiManager.getConnectionInfo().getSSID(); - WifiInfo.removeDoubleQuotes(state); wifiEnabled = true; break; case WifiManager.WIFI_STATE_UNKNOWN: @@ -82,28 +84,17 @@ public class SettingsSliceProvider extends SliceProvider { state = ""; // just don't show anything? break; } - if (wifiEnabled) { - toggleHints = new String[] {Slice.HINT_TOGGLE, Slice.HINT_SELECTED}; - } else { - toggleHints = new String[] {Slice.HINT_TOGGLE}; - } - // Construct the slice - Slice.Builder b = new Slice.Builder(uri); - b.addSubSlice(new Slice.Builder(b) - .addAction(getIntent("android.settings.WIFI_SETTINGS"), - new Slice.Builder(b) - .addText(getContext().getString(R.string.wifi_settings), null) - .addText(state, null) - .addIcon(Icon.createWithResource(getContext(), - R.drawable.ic_settings_wireless), null, Slice.HINT_HIDDEN) - .addHints(Slice.HINT_TITLE) - .build()) - .addAction(getBroadcastIntent(ACTION_WIFI_CHANGED), - new Slice.Builder(b) - .addHints(toggleHints) - .build()) - .build()); - return b.build(); + + boolean finalWifiEnabled = wifiEnabled; + return new ListBuilder(sliceUri) + .setColor(R.color.material_blue_500) + .add(b -> b + .setTitle(getContext().getString(R.string.wifi_settings)) + .setTitleItem(Icon.createWithResource(getContext(), R.drawable.wifi_signal)) + .setSubtitle(state) + .addToggle(getBroadcastIntent(ACTION_WIFI_CHANGED), finalWifiEnabled) + .setContentIntent(getIntent(Intent.ACTION_MAIN))) + .build(); } private PendingIntent getIntent(String action) { diff --git a/src/com/android/settings/SliceBroadcastReceiver.java b/src/com/android/settings/slices/SliceBroadcastReceiver.java similarity index 85% rename from src/com/android/settings/SliceBroadcastReceiver.java rename to src/com/android/settings/slices/SliceBroadcastReceiver.java index f43e3a39206..b6f2ab945ca 100644 --- a/src/com/android/settings/SliceBroadcastReceiver.java +++ b/src/com/android/settings/slices/SliceBroadcastReceiver.java @@ -14,9 +14,9 @@ * limitations under the License */ -package com.android.settings; +package com.android.settings.slices; -import static com.android.settings.SettingsSliceProvider.ACTION_WIFI_CHANGED; +import static com.android.settings.slices.SettingsSliceProvider.ACTION_WIFI_CHANGED; import android.app.slice.Slice; import android.content.BroadcastReceiver; @@ -42,8 +42,8 @@ public class SliceBroadcastReceiver extends BroadcastReceiver { // Wait a bit for wifi to update (TODO: is there a better way to do this?) Handler h = new Handler(); h.postDelayed(() -> { - Uri uri = SettingsSliceProvider.getUri(SettingsSliceProvider.PATH_WIFI); - context.getContentResolver().notifyChange(uri, null); + Uri uri = SettingsSliceProvider.getUri(SettingsSliceProvider.PATH_WIFI); + context.getContentResolver().notifyChange(uri, null); }, 1000); break; } diff --git a/src/com/android/settings/slices/SliceData.java b/src/com/android/settings/slices/SliceData.java new file mode 100644 index 00000000000..528f23c9133 --- /dev/null +++ b/src/com/android/settings/slices/SliceData.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2017 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.slices; + +import android.net.Uri; +import android.text.TextUtils; + +/** + * TODO (b/67996923) Add SlicesIndexingManager + * Data class representing a slice stored by {@link SlicesIndexingManager}. + * Note that {@link #key} is treated as a primary key for this class and determines equality. + */ +public class SliceData { + + private final String key; + + private final String title; + + private final String summary; + + private final String screenTitle; + + private final int iconResource; + + private final String fragmentClassName; + + private final Uri uri; + + private final String preferenceController; + + public String getKey() { + return key; + } + + public String getTitle() { + return title; + } + + public String getSummary() { + return summary; + } + + public String getScreenTitle() { + return screenTitle; + } + + public int getIconResource() { + return iconResource; + } + + public String getFragmentClassName() { + return fragmentClassName; + } + + public Uri getUri() { + return uri; + } + + public String getPreferenceController() { + return preferenceController; + } + + private SliceData(Builder builder) { + key = builder.mKey; + title = builder.mTitle; + summary = builder.mSummary; + screenTitle = builder.mScreenTitle; + iconResource = builder.mIconResource; + fragmentClassName = builder.mFragmentClassName; + uri = builder.mUri; + preferenceController = builder.mPrefControllerClassName; + } + + @Override + public int hashCode() { + return key.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof SliceData)) { + return false; + } + SliceData newObject = (SliceData) obj; + return TextUtils.equals(key, newObject.key); + } + + static class Builder { + private String mKey; + + private String mTitle; + + private String mSummary; + + private String mScreenTitle; + + private int mIconResource; + + private String mFragmentClassName; + + private Uri mUri; + + private String mPrefControllerClassName; + + public Builder setKey(String key) { + mKey = key; + return this; + } + + public Builder setTitle(String title) { + mTitle = title; + return this; + } + + public Builder setSummary(String summary) { + mSummary = summary; + return this; + } + + public Builder setScreenTitle(String screenTitle) { + mScreenTitle = screenTitle; + return this; + } + + public Builder setIcon(int iconResource) { + mIconResource = iconResource; + return this; + } + + public Builder setPreferenceControllerClassName(String controllerClassName) { + mPrefControllerClassName = controllerClassName; + return this; + } + + public Builder setFragmentName(String fragmentClassName) { + mFragmentClassName = fragmentClassName; + return this; + } + + public Builder setUri(Uri uri) { + mUri = uri; + return this; + } + + public SliceData build() { + if (TextUtils.isEmpty(mKey)) { + throw new IllegalStateException("Key cannot be empty"); + } + + if (TextUtils.isEmpty(mTitle)) { + throw new IllegalStateException("Title cannot be empty"); + } + + if (TextUtils.isEmpty(mFragmentClassName)) { + throw new IllegalStateException("Fragment Name cannot be empty"); + } + + if (TextUtils.isEmpty(mPrefControllerClassName)) { + throw new IllegalStateException("Preference Controller cannot be empty"); + } + + if (mUri == null) { + throw new IllegalStateException("Uri cannot be null"); + } + + return new SliceData(this); + } + + public String getKey() { + return mKey; + } + } + +} \ No newline at end of file diff --git a/src/com/android/settings/slices/SlicesDatabaseHelper.java b/src/com/android/settings/slices/SlicesDatabaseHelper.java new file mode 100644 index 00000000000..a74fc812d61 --- /dev/null +++ b/src/com/android/settings/slices/SlicesDatabaseHelper.java @@ -0,0 +1,122 @@ +package com.android.settings.slices; + +import android.content.Context; + +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * Defines the schema for the Slices database. + */ +public class SlicesDatabaseHelper extends SQLiteOpenHelper { + + private static final String TAG = "SlicesDatabaseHelper"; + + private static final String DATABASE_NAME = "slices_index.db"; + private static final String SHARED_PREFS_TAG = "slices_shared_prefs"; + + private static final int DATABASE_VERSION = 1; + + public interface Tables { + String TABLE_SLICES_INDEX = "slices_index"; + } + + public interface IndexColumns { + /** + * Primary key of the DB. Preference key from preference controllers. + */ + String KEY = "key"; + + /** + * Title of the Setting. + */ + String TITLE = "title"; + + /** + * Summary / Subtitle for the setting. + */ + String SUBTITLE = "subtitle"; + + /** + * Title of the Setting screen on which the Setting lives. + */ + String SCREENTITLE = "screentitle"; + + /** + * Resource ID for the icon of the setting. Should be 0 for no icon. + */ + String ICON_RESOURCE = "icon"; + + /** + * Classname of the fragment name of the page that hosts the setting. + */ + String FRAGMENT = "fragment"; + + /** + * Class name of the controller backing the setting. Must be a + * {@link com.android.settings.core.BasePreferenceController}. + */ + String CONTROLLER = "controller"; + } + + private static final String CREATE_SLICES_TABLE = + "CREATE VIRTUAL TABLE " + Tables.TABLE_SLICES_INDEX + " USING fts4" + + "(" + + IndexColumns.KEY + + ", " + + IndexColumns.TITLE + + ", " + + IndexColumns.SUBTITLE + + ", " + + IndexColumns.SCREENTITLE + + ", " + + IndexColumns.ICON_RESOURCE + + ", " + + IndexColumns.FRAGMENT + + ", " + + IndexColumns.CONTROLLER + + ");"; + + private final Context mContext; + + public SlicesDatabaseHelper(Context context) { + super(context, DATABASE_NAME, null /* CursorFactor */, DATABASE_VERSION); + mContext = context; + } + + @Override + public void onCreate(SQLiteDatabase db) { + createDatabases(db); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + if (oldVersion < DATABASE_VERSION) { + Log.d(TAG, "Reconstructing DB from " + oldVersion + "to " + newVersion); + reconstruct(db); + } + } + + @VisibleForTesting + void reconstruct(SQLiteDatabase db) { + mContext.getSharedPreferences(SHARED_PREFS_TAG, Context.MODE_PRIVATE) + .edit() + .clear() + .commit(); + dropTables(db); + createDatabases(db); + } + + private void createDatabases(SQLiteDatabase db) { + db.execSQL(CREATE_SLICES_TABLE); + Log.d(TAG, "Created databases"); + } + + + private void dropTables(SQLiteDatabase db) { + db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_SLICES_INDEX); + } +} \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java b/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java index a8372d93d26..f84f9a2240d 100644 --- a/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java +++ b/tests/robotests/src/com/android/settings/search/SearchIndexProviderCodeInspector.java @@ -43,13 +43,13 @@ public class SearchIndexProviderCodeInspector extends CodeInspector { "SettingsPreferenceFragment should implement Indexable, but these do not:\n"; private static final String NOT_CONTAINING_PROVIDER_OBJECT_ERROR = "Indexable should have public field " - + DatabaseIndexingManager.FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + + DatabaseIndexingUtils.FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + " but these are not:\n"; private static final String NOT_SHARING_PREF_CONTROLLERS_BETWEEN_FRAG_AND_PROVIDER = "DashboardFragment should share pref controllers with its SearchIndexProvider, but " + " these are not: \n"; private static final String NOT_IN_INDEXABLE_PROVIDER_REGISTRY = - "Class containing " + DatabaseIndexingManager.FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + "Class containing " + DatabaseIndexingUtils.FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER + " must be added to " + SearchIndexableResources.class.getName() + " but these are not: \n"; private static final String NOT_PROVIDING_VALID_RESOURCE_ERROR = @@ -173,7 +173,7 @@ public class SearchIndexProviderCodeInspector extends CodeInspector { private boolean hasSearchIndexProvider(Class clazz) { try { final Field f = clazz.getField( - DatabaseIndexingManager.FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER); + DatabaseIndexingUtils.FIELD_NAME_SEARCH_INDEX_DATA_PROVIDER); return f != null; } catch (NoClassDefFoundError e) { // Cannot find class def, ignore diff --git a/tests/robotests/src/com/android/settings/slices/SliceDataTest.java b/tests/robotests/src/com/android/settings/slices/SliceDataTest.java new file mode 100644 index 00000000000..0e4accabe5e --- /dev/null +++ b/tests/robotests/src/com/android/settings/slices/SliceDataTest.java @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2017 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.slices; + + +import static com.google.common.truth.Truth.assertThat; + +import android.net.Uri; + +import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class SliceDataTest { + + private final String KEY = "KEY"; + private final String TITLE = "title"; + private final String SUMMARY = "summary"; + private final String SCREEN_TITLE = "screen title"; + private final String FRAGMENT_NAME = "fragment name"; + private final int ICON = 1234; // I declare a thumb war + private final Uri URI = Uri.parse("content://com.android.settings.slices/test"); + private final String PREF_CONTROLLER = "com.android.settings.slices.tester"; + + @Test + public void testBuilder_buildsMatchingObject() { + SliceData.Builder builder = new SliceData.Builder() + .setKey(KEY) + .setTitle(TITLE) + .setSummary(SUMMARY) + .setScreenTitle(SCREEN_TITLE) + .setIcon(ICON) + .setFragmentName(FRAGMENT_NAME) + .setUri(URI) + .setPreferenceControllerClassName(PREF_CONTROLLER); + + SliceData data = builder.build(); + + assertThat(data.getKey()).isEqualTo(KEY); + assertThat(data.getTitle()).isEqualTo(TITLE); + assertThat(data.getSummary()).isEqualTo(SUMMARY); + assertThat(data.getScreenTitle()).isEqualTo(SCREEN_TITLE); + assertThat(data.getIconResource()).isEqualTo(ICON); + assertThat(data.getFragmentClassName()).isEqualTo(FRAGMENT_NAME); + assertThat(data.getUri()).isEqualTo(URI); + assertThat(data.getPreferenceController()).isEqualTo(PREF_CONTROLLER); + } + + @Test(expected = IllegalStateException.class) + public void testBuilder_noKey_throwsIllegalStateException() { + new SliceData.Builder() + .setTitle(TITLE) + .setSummary(SUMMARY) + .setScreenTitle(SCREEN_TITLE) + .setIcon(ICON) + .setFragmentName(FRAGMENT_NAME) + .setUri(URI) + .setPreferenceControllerClassName(PREF_CONTROLLER) + .build(); + } + + @Test(expected = IllegalStateException.class) + public void testBuilder_noTitle_throwsIllegalStateException() { + new SliceData.Builder() + .setKey(KEY) + .setSummary(SUMMARY) + .setScreenTitle(SCREEN_TITLE) + .setIcon(ICON) + .setFragmentName(FRAGMENT_NAME) + .setUri(URI) + .setPreferenceControllerClassName(PREF_CONTROLLER) + .build(); + } + + @Test(expected = IllegalStateException.class) + public void testBuilder_noFragment_throwsIllegalStateException() { + new SliceData.Builder() + .setKey(KEY) + .setFragmentName(FRAGMENT_NAME) + .setSummary(SUMMARY) + .setScreenTitle(SCREEN_TITLE) + .setIcon(ICON) + .setUri(URI) + .setPreferenceControllerClassName(PREF_CONTROLLER) + .build(); + } + + @Test(expected = IllegalStateException.class) + public void testBuilder_noUri_throwsIllegalStateException() { + new SliceData.Builder() + .setKey(KEY) + .setTitle(TITLE) + .setSummary(SUMMARY) + .setScreenTitle(SCREEN_TITLE) + .setIcon(ICON) + .setFragmentName(FRAGMENT_NAME) + .setPreferenceControllerClassName(PREF_CONTROLLER) + .build(); + } + + @Test(expected = IllegalStateException.class) + public void testBuilder_noPrefController_throwsIllegalStateException() { + new SliceData.Builder() + .setKey(KEY) + .setTitle(TITLE) + .setSummary(SUMMARY) + .setScreenTitle(SCREEN_TITLE) + .setIcon(ICON) + .setUri(URI) + .setFragmentName(FRAGMENT_NAME) + .build(); + } + + @Test + public void testBuilder_noSubtitle_buildsMatchingObject() { + SliceData.Builder builder = new SliceData.Builder() + .setKey(KEY) + .setTitle(TITLE) + .setScreenTitle(SCREEN_TITLE) + .setIcon(ICON) + .setFragmentName(FRAGMENT_NAME) + .setUri(URI) + .setPreferenceControllerClassName(PREF_CONTROLLER); + + SliceData data = builder.build(); + + assertThat(data.getKey()).isEqualTo(KEY); + assertThat(data.getTitle()).isEqualTo(TITLE); + assertThat(data.getSummary()).isNull(); + assertThat(data.getScreenTitle()).isEqualTo(SCREEN_TITLE); + assertThat(data.getIconResource()).isEqualTo(ICON); + assertThat(data.getFragmentClassName()).isEqualTo(FRAGMENT_NAME); + assertThat(data.getUri()).isEqualTo(URI); + assertThat(data.getPreferenceController()).isEqualTo(PREF_CONTROLLER); + } + + @Test + public void testBuilder_noScreenTitle_buildsMatchingObject() { + SliceData.Builder builder = new SliceData.Builder() + .setKey(KEY) + .setTitle(TITLE) + .setSummary(SUMMARY) + .setIcon(ICON) + .setFragmentName(FRAGMENT_NAME) + .setUri(URI) + .setPreferenceControllerClassName(PREF_CONTROLLER); + + SliceData data = builder.build(); + + assertThat(data.getKey()).isEqualTo(KEY); + assertThat(data.getTitle()).isEqualTo(TITLE); + assertThat(data.getSummary()).isEqualTo(SUMMARY); + assertThat(data.getScreenTitle()).isNull(); + assertThat(data.getIconResource()).isEqualTo(ICON); + assertThat(data.getFragmentClassName()).isEqualTo(FRAGMENT_NAME); + assertThat(data.getUri()).isEqualTo(URI); + assertThat(data.getPreferenceController()).isEqualTo(PREF_CONTROLLER); + } + + @Test + public void testBuilder_noIcon_buildsMatchingObject() { + SliceData.Builder builder = new SliceData.Builder() + .setKey(KEY) + .setTitle(TITLE) + .setSummary(SUMMARY) + .setScreenTitle(SCREEN_TITLE) + .setFragmentName(FRAGMENT_NAME) + .setUri(URI) + .setPreferenceControllerClassName(PREF_CONTROLLER); + + SliceData data = builder.build(); + + assertThat(data.getKey()).isEqualTo(KEY); + assertThat(data.getTitle()).isEqualTo(TITLE); + assertThat(data.getSummary()).isEqualTo(SUMMARY); + assertThat(data.getScreenTitle()).isEqualTo(SCREEN_TITLE); + assertThat(data.getIconResource()).isEqualTo(0); + assertThat(data.getFragmentClassName()).isEqualTo(FRAGMENT_NAME); + assertThat(data.getUri()).isEqualTo(URI); + assertThat(data.getPreferenceController()).isEqualTo(PREF_CONTROLLER); + } + + @Test + public void testEquality_identicalObjects() { + SliceData.Builder builder = new SliceData.Builder() + .setKey(KEY) + .setTitle(TITLE) + .setSummary(SUMMARY) + .setScreenTitle(SCREEN_TITLE) + .setIcon(ICON) + .setFragmentName(FRAGMENT_NAME) + .setUri(URI) + .setPreferenceControllerClassName(PREF_CONTROLLER); + + SliceData dataOne = builder.build(); + SliceData dataTwo = builder.build(); + + assertThat(dataOne.hashCode()).isEqualTo(dataTwo.hashCode()); + assertThat(dataOne).isEqualTo(dataTwo); + } + + @Test + public void testEquality_matchingKey_EqualObjects() { + SliceData.Builder builder = new SliceData.Builder() + .setKey(KEY) + .setTitle(TITLE) + .setSummary(SUMMARY) + .setScreenTitle(SCREEN_TITLE) + .setIcon(ICON) + .setFragmentName(FRAGMENT_NAME) + .setUri(URI) + .setPreferenceControllerClassName(PREF_CONTROLLER); + + SliceData dataOne = builder.build(); + + builder.setTitle(TITLE + " diff") + .setSummary(SUMMARY + " diff") + .setScreenTitle(SCREEN_TITLE + " diff") + .setIcon(ICON + 1) + .setFragmentName(FRAGMENT_NAME + " diff") + .setUri(URI) + .setPreferenceControllerClassName(PREF_CONTROLLER + " diff"); + + SliceData dataTwo = builder.build(); + + assertThat(dataOne.hashCode()).isEqualTo(dataTwo.hashCode()); + assertThat(dataOne).isEqualTo(dataTwo); + } + + @Test + public void testEquality_differentKey_differentObjects() { + SliceData.Builder builder = new SliceData.Builder() + .setKey(KEY) + .setTitle(TITLE) + .setSummary(SUMMARY) + .setScreenTitle(SCREEN_TITLE) + .setIcon(ICON) + .setFragmentName(FRAGMENT_NAME) + .setUri(URI) + .setPreferenceControllerClassName(PREF_CONTROLLER); + + SliceData dataOne = builder.build(); + + builder.setKey("not key"); + SliceData dataTwo = builder.build(); + + assertThat(dataOne.hashCode()).isNotEqualTo(dataTwo.hashCode()); + assertThat(dataOne).isNotEqualTo(dataTwo); + } +} diff --git a/tests/robotests/src/com/android/settings/slices/SlicesDatabaseHelperTest.java b/tests/robotests/src/com/android/settings/slices/SlicesDatabaseHelperTest.java new file mode 100644 index 00000000000..a4020dd3d31 --- /dev/null +++ b/tests/robotests/src/com/android/settings/slices/SlicesDatabaseHelperTest.java @@ -0,0 +1,87 @@ +package com.android.settings.slices; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; + +import com.android.settings.TestConfig; +import com.android.settings.slices.SlicesDatabaseHelper.IndexColumns; +import com.android.settings.testutils.DatabaseTestUtils; +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; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class SlicesDatabaseHelperTest { + + private Context mContext; + private SlicesDatabaseHelper mSlicesDatabaseHelper; + private SQLiteDatabase mDatabase; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + mSlicesDatabaseHelper = new SlicesDatabaseHelper(mContext); + mDatabase = mSlicesDatabaseHelper.getWritableDatabase(); + } + + @After + public void cleanUp() { + DatabaseTestUtils.clearDb(mContext); + } + + @Test + public void testDatabaseSchema() { + Cursor cursor = mDatabase.rawQuery("SELECT * FROM slices_index", null); + String[] columnNames = cursor.getColumnNames(); + + String[] expectedNames = new String[]{ + IndexColumns.KEY, + IndexColumns.TITLE, + IndexColumns.SUBTITLE, + IndexColumns.SCREENTITLE, + IndexColumns.ICON_RESOURCE, + IndexColumns.FRAGMENT, + IndexColumns.CONTROLLER + }; + + assertThat(columnNames).isEqualTo(expectedNames); + } + + @Test + public void testUpgrade_dropsOldData() { + ContentValues dummyValues = getDummyRow(); + + mDatabase.replaceOrThrow(SlicesDatabaseHelper.Tables.TABLE_SLICES_INDEX, null, dummyValues); + Cursor baseline = mDatabase.rawQuery("SELECT * FROM slices_index", null); + assertThat(baseline.getCount()).isEqualTo(1); + + mSlicesDatabaseHelper.onUpgrade(mDatabase, 0, 1); + + Cursor newCursor = mDatabase.rawQuery("SELECT * FROM slices_index", null); + assertThat(newCursor.getCount()).isEqualTo(0); + } + + private ContentValues getDummyRow() { + ContentValues values; + + values = new ContentValues(); + values.put(IndexColumns.KEY, "key"); + values.put(IndexColumns.TITLE, "title"); + values.put(IndexColumns.SUBTITLE, "subtitle"); + values.put(IndexColumns.ICON_RESOURCE, 99); + values.put(IndexColumns.FRAGMENT, "fragmentClassName"); + values.put(IndexColumns.CONTROLLER, "preferenceController"); + + return values; + } +}