Loading AndroidManifest.xml +6 −0 Original line number Diff line number Diff line Loading @@ -3181,6 +3181,12 @@ <service android:name=".fuelgauge.batterytip.AnomalyDetectionJobService" android:permission="android.permission.BIND_JOB_SERVICE" /> <provider android:name=".homepage.CardContentProvider" android:authorities="com.android.settings.homepage.CardContentProvider" android:exported="true" android:permission="android.permission.WRITE_SETTINGS_HOMEPAGE_DATA" /> <!-- This is the longest AndroidManifest.xml ever. --> </application> </manifest> src/com/android/settings/homepage/CardContentProvider.java 0 → 100644 +166 −0 Original line number Diff line number Diff line /* * 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; } } src/com/android/settings/homepage/CardDatabaseHelper.java 0 → 100644 +190 −0 Original line number Diff line number Diff line /* * 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; } } tests/robotests/src/com/android/settings/homepage/CardContentProviderTest.java 0 → 100644 +147 −0 Original line number Diff line number Diff line /* * 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; } } tests/robotests/src/com/android/settings/homepage/CardDatabaseHelperTest.java 0 → 100644 +83 −0 Original line number Diff line number Diff line /* * 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(); } } Loading
AndroidManifest.xml +6 −0 Original line number Diff line number Diff line Loading @@ -3181,6 +3181,12 @@ <service android:name=".fuelgauge.batterytip.AnomalyDetectionJobService" android:permission="android.permission.BIND_JOB_SERVICE" /> <provider android:name=".homepage.CardContentProvider" android:authorities="com.android.settings.homepage.CardContentProvider" android:exported="true" android:permission="android.permission.WRITE_SETTINGS_HOMEPAGE_DATA" /> <!-- This is the longest AndroidManifest.xml ever. --> </application> </manifest>
src/com/android/settings/homepage/CardContentProvider.java 0 → 100644 +166 −0 Original line number Diff line number Diff line /* * 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; } }
src/com/android/settings/homepage/CardDatabaseHelper.java 0 → 100644 +190 −0 Original line number Diff line number Diff line /* * 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; } }
tests/robotests/src/com/android/settings/homepage/CardContentProviderTest.java 0 → 100644 +147 −0 Original line number Diff line number Diff line /* * 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; } }
tests/robotests/src/com/android/settings/homepage/CardDatabaseHelperTest.java 0 → 100644 +83 −0 Original line number Diff line number Diff line /* * 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(); } }