Finished MovieProvider and corresponding tests

This commit is contained in:
Aargonian
2016-02-16 03:33:00 -05:00
parent 443340e8e1
commit 8aa12527a4
12 changed files with 780 additions and 681 deletions

2
.idea/misc.xml generated
View File

@@ -37,7 +37,7 @@
<ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View File

@@ -1,5 +1,6 @@
package com.example.android.popularmovies.data;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.test.AndroidTestCase;
@@ -14,18 +15,23 @@ public class TestDb extends AndroidTestCase {
public static final String LOG_TAG = TestDb.class.getSimpleName();
// Since we want each test to start with a clean slate
void deleteTheDatabase() {
mContext.deleteDatabase(MovieDbHelper.DB_NAME);
}
@Override
public void setUp() {
deleteTheDatabase();
}
@Override
public void tearDown() {
deleteTheDatabase();
}
public void testCreateDb() throws Throwable {
Log.d(LOG_TAG, "Test: testCreateDb()");
final HashSet<String> tableNameHashSet = new HashSet<String>();
final HashSet<String> tableNameHashSet = new HashSet<>();
tableNameHashSet.add(MovieContract.MovieEntry.TABLE_NAME);
mContext.deleteDatabase(MovieDbHelper.DB_NAME);
@@ -45,7 +51,7 @@ public class TestDb extends AndroidTestCase {
} while( c.moveToNext() );
// if this fails, it means that your database doesn't contain both the location entry
// and weather entry tables
// and Movie entry tables
assertTrue("Error: Your database was created without the Movie Table",
tableNameHashSet.isEmpty());
@@ -57,7 +63,7 @@ public class TestDb extends AndroidTestCase {
c.moveToFirst());
// Build a HashSet of all of the column names we want to look for
final HashSet<String> movieColumnHashSet = new HashSet<String>();
final HashSet<String> movieColumnHashSet = new HashSet<>();
movieColumnHashSet.add(MovieContract.MovieEntry._ID);
movieColumnHashSet.add(MovieContract.MovieEntry.COLUMN_TMDB_ID);
movieColumnHashSet.add(MovieContract.MovieEntry.COLUMN_TITLE);
@@ -66,6 +72,7 @@ public class TestDb extends AndroidTestCase {
movieColumnHashSet.add(MovieContract.MovieEntry.COLUMN_RELEASE);
movieColumnHashSet.add(MovieContract.MovieEntry.COLUMN_RUNTIME);
movieColumnHashSet.add(MovieContract.MovieEntry.COLUMN_RATING);
movieColumnHashSet.add(MovieContract.MovieEntry.COLUMN_FAVORITE);
movieColumnHashSet.add(MovieContract.MovieEntry.COLUMN_VOTE_CNT);
movieColumnHashSet.add(MovieContract.MovieEntry.COLUMN_GENRES);
@@ -82,8 +89,28 @@ public class TestDb extends AndroidTestCase {
db.close();
}
//TODO: Add Test for Movie Table
//TODO: Add Test for Popular Table
//TODO: Add Test for Rating Table
//TODO: Add Test for Favorites Table
public void testMovieTable() {
Log.d(LOG_TAG, "TEST: TestDb.testMovieTable()");
MovieDbHelper dbHelper = new MovieDbHelper(mContext);
SQLiteDatabase db = dbHelper.getWritableDatabase();
String filePath = TestUtilities.storeTestImage(mContext);
ContentValues testValues = TestUtilities.createFightClubMovieValues(filePath);
assertTrue(db.insert(MovieContract.MovieEntry.TABLE_NAME, null, testValues) != -1);
Cursor cursor = db.query(MovieContract.MovieEntry.TABLE_NAME,
null, null, null, null, null, null);
assertTrue("Error: No Movies Returned From Query!", cursor.moveToFirst());
TestUtilities.validateCurrentRecord("Error: Movie Not Validated!", cursor, testValues);
//Test bitmap
String f =cursor.getString(cursor.getColumnIndex(MovieContract.MovieEntry.COLUMN_IMG_PATH));
assertTrue(f.equals(filePath));
assertTrue(TestUtilities.getTestImage(mContext).sameAs(FileUtils.getImage(f)));
assertFalse( "Error: More than one record!", cursor.moveToNext());
cursor.close();
db.close();
}
}

View File

@@ -0,0 +1,68 @@
package com.example.android.popularmovies.data;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.test.AndroidTestCase;
import android.util.Log;
import com.example.android.popularmovies.R;
/**
* Created by Aaron Helton on 2/15/2016
*/
public class TestFileUtils extends AndroidTestCase
{
private static final String LOG_TAG = TestFileUtils.class.getSimpleName();
private Bitmap bm;
private String filePath;
@Override
public void setUp()
{
bm = BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.ic_launcher);
}
public void testStoreAndGetImage()
{
Log.d(LOG_TAG, "Test: TestFileUtils.testStoreAndGetImage()");
String filePath = FileUtils.storeImage(mContext, bm, "ic_launcher");
assertNotNull("Improperly Returned Filepath!", filePath);
assertFalse("Filepath is Empty!", filePath.isEmpty());
assertTrue("Default PNG Encoding Not Applied!", filePath.contains(".png"));
Bitmap ret = FileUtils.getImage(filePath);
assertNotNull("Returned Bitmap is Null!", ret);
assertTrue("Bitmap Returned is not the Same!", bm.sameAs(ret));
String filePath2 = FileUtils.storeImage(mContext, bm, "someImage");
filePath = FileUtils.storeImage(null, bm, "ic_launcher");
assertNull(filePath);
filePath = FileUtils.storeImage(mContext, bm, null);
assertNull(filePath);
filePath = FileUtils.storeImage(mContext, bm, "");
assertNull(filePath);
filePath = FileUtils.storeImage(mContext, bm, "ic_launcher.jpg");
String ext = FileUtils.getExtension(filePath);
assertNotNull(ext);
assertTrue(ext.equals("jpg"));
ret = FileUtils.getImage(filePath);
assertFalse("JPG Compression Should Make This False", bm.sameAs(ret));
filePath = FileUtils.storeImage(mContext, bm, "someFileName");
assertTrue(FileUtils.getImage(filePath).sameAs(bm));
}
public void testGetExtension()
{
Log.d(LOG_TAG, "TEST: TestFileUtils.testGetExtension()");
final String TEST_EXTENSION = "someExtension";
final String TEST_FILE_STEM = "test";
String fileName = TEST_FILE_STEM + "." + TEST_EXTENSION;
assertTrue(TEST_EXTENSION.equals(FileUtils.getExtension(fileName)));
assertNull(FileUtils.getExtension(TEST_FILE_STEM));
}
}

View File

@@ -1,507 +1,253 @@
package com.example.android.popularmovies.data;
import android.content.ComponentName;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.test.AndroidTestCase;
//
//import android.content.ContentUris;
//import android.content.ContentValues;
//import android.database.Cursor;
//import android.database.sqlite.SQLiteDatabase;
//import android.net.Uri;
//import android.os.Build;
//import android.test.AndroidTestCase;
//import android.util.Log;
//
//import android.content.ContentUris;
//import android.content.ContentValues;
//import android.database.Cursor;
//import android.database.sqlite.SQLiteDatabase;
//import android.net.Uri;
//import android.os.Build;
//import android.test.AndroidTestCase;
//import android.util.Log;
//
//import com.example.android.sunshine.app.data.WeatherContract.LocationEntry;
//import com.example.android.sunshine.app.data.WeatherContract.WeatherEntry;
//
///*
// Note: This is not a complete set of tests of the Sunshine ContentProvider, but it does test
// that at least the basic functionality has been implemented correctly.
//
// Students: Uncomment the tests in this class as you implement the functionality in your
// ContentProvider to make sure that you've implemented things reasonably correctly.
// */
import android.util.Log;
import com.example.android.popularmovies.data.MovieContract.MovieEntry;
public class TestProvider extends AndroidTestCase {
//
// public static final String LOG_TAG = TestProvider.class.getSimpleName();
//
// /*
// This helper function deletes all records from both database tables using the ContentProvider.
// It also queries the ContentProvider to make sure that the database has been successfully
// deleted, so it cannot be used until the Query and Delete functions have been written
// in the ContentProvider.
//
// Students: Replace the calls to deleteAllRecordsFromDB with this one after you have written
// the delete functionality in the ContentProvider.
// */
// public void deleteAllRecordsFromProvider() {
// mContext.getContentResolver().delete(
// WeatherEntry.CONTENT_URI,
// null,
// null
// );
// mContext.getContentResolver().delete(
// LocationEntry.CONTENT_URI,
// null,
// null
// );
//
// Cursor cursor = mContext.getContentResolver().query(
// WeatherEntry.CONTENT_URI,
// null,
// null,
// null,
// null
// );
// assertEquals("Error: Records not deleted from Weather table during delete", 0, cursor.getCount());
// cursor.close();
//
// cursor = mContext.getContentResolver().query(
// LocationEntry.CONTENT_URI,
// null,
// null,
// null,
// null
// );
// assertEquals("Error: Records not deleted from Location table during delete", 0, cursor.getCount());
// cursor.close();
// }
//
// /*
// Student: Refactor this function to use the deleteAllRecordsFromProvider functionality once
// you have implemented delete functionality there.
// */
// public void deleteAllRecords() {
// deleteAllRecordsFromProvider();
// }
//
// // Since we want each test to start with a clean slate, run deleteAllRecords
// // in setUp (called by the test runner before each test).
// @Override
// protected void setUp() throws Exception {
// super.setUp();
// deleteAllRecords();
// }
//
// /*
// This test checks to make sure that the content provider is registered correctly.
// Students: Uncomment this test to make sure you've correctly registered the WeatherProvider.
// */
//// public void testProviderRegistry() {
//// PackageManager pm = mContext.getPackageManager();
////
//// // We define the component name based on the package name from the context and the
//// // WeatherProvider class.
//// ComponentName componentName = new ComponentName(mContext.getPackageName(),
//// WeatherProvider.class.getName());
//// try {
//// // Fetch the provider info using the component name from the PackageManager
//// // This throws an exception if the provider isn't registered.
//// ProviderInfo providerInfo = pm.getProviderInfo(componentName, 0);
////
//// // Make sure that the registered authority matches the authority from the Contract.
//// assertEquals("Error: WeatherProvider registered with authority: " + providerInfo.authority +
//// " instead of authority: " + WeatherContract.CONTENT_AUTHORITY,
//// providerInfo.authority, WeatherContract.CONTENT_AUTHORITY);
//// } catch (PackageManager.NameNotFoundException e) {
//// // I guess the provider isn't registered correctly.
//// assertTrue("Error: WeatherProvider not registered at " + mContext.getPackageName(),
//// false);
//// }
//// }
//
// /*
// This test doesn't touch the database. It verifies that the ContentProvider returns
// the correct type for each type of URI that it can handle.
// Students: Uncomment this test to verify that your implementation of GetType is
// functioning correctly.
// */
// public void testGetType() {
// // content://com.example.android.sunshine.app/weather/
// String type = mContext.getContentResolver().getType(WeatherEntry.CONTENT_URI);
// // vnd.android.cursor.dir/com.example.android.sunshine.app/weather
// assertEquals("Error: the WeatherEntry CONTENT_URI should return WeatherEntry.CONTENT_TYPE",
// WeatherEntry.CONTENT_TYPE, type);
//
// String testLocation = "94074";
// // content://com.example.android.sunshine.app/weather/94074
// type = mContext.getContentResolver().getType(
// WeatherEntry.buildWeatherLocation(testLocation));
// // vnd.android.cursor.dir/com.example.android.sunshine.app/weather
// assertEquals("Error: the WeatherEntry CONTENT_URI with location should return WeatherEntry.CONTENT_TYPE",
// WeatherEntry.CONTENT_TYPE, type);
//
// long testDate = 1419120000L; // December 21st, 2014
// // content://com.example.android.sunshine.app/weather/94074/20140612
// type = mContext.getContentResolver().getType(
// WeatherEntry.buildWeatherLocationWithDate(testLocation, testDate));
// // vnd.android.cursor.item/com.example.android.sunshine.app/weather/1419120000
// assertEquals("Error: the WeatherEntry CONTENT_URI with location and date should return WeatherEntry.CONTENT_ITEM_TYPE",
// WeatherEntry.CONTENT_ITEM_TYPE, type);
//
// // content://com.example.android.sunshine.app/location/
// type = mContext.getContentResolver().getType(LocationEntry.CONTENT_URI);
// // vnd.android.cursor.dir/com.example.android.sunshine.app/location
// assertEquals("Error: the LocationEntry CONTENT_URI should return LocationEntry.CONTENT_TYPE",
// LocationEntry.CONTENT_TYPE, type);
// }
//
//
// /*
// This test uses the database directly to insert and then uses the ContentProvider to
// read out the data. Uncomment this test to see if the basic weather query functionality
// given in the ContentProvider is working correctly.
// */
// public void testBasicWeatherQuery() {
// // insert our test records into the database
// WeatherDbHelper dbHelper = new WeatherDbHelper(mContext);
// SQLiteDatabase db = dbHelper.getWritableDatabase();
//
// ContentValues testValues = TestUtilities.createNorthPoleLocationValues();
// long locationRowId = TestUtilities.insertNorthPoleLocationValues(mContext);
//
// // Fantastic. Now that we have a location, add some weather!
// ContentValues weatherValues = TestUtilities.createWeatherValues(locationRowId);
//
// long weatherRowId = db.insert(WeatherEntry.TABLE_NAME, null, weatherValues);
// assertTrue("Unable to Insert WeatherEntry into the Database", weatherRowId != -1);
//
// db.close();
//
// // Test the basic content provider query
// Cursor weatherCursor = mContext.getContentResolver().query(
// WeatherEntry.CONTENT_URI,
// null,
// null,
// null,
// null
// );
//
// // Make sure we get the correct cursor out of the database
// TestUtilities.validateCursor("testBasicWeatherQuery", weatherCursor, weatherValues);
// }
//
// /*
// This test uses the database directly to insert and then uses the ContentProvider to
// read out the data. Uncomment this test to see if your location queries are
// performing correctly.
// */
// public void testBasicLocationQueries() {
// // insert our test records into the database
// WeatherDbHelper dbHelper = new WeatherDbHelper(mContext);
// SQLiteDatabase db = dbHelper.getWritableDatabase();
//
// ContentValues testValues = TestUtilities.createNorthPoleLocationValues();
// long locationRowId = TestUtilities.insertNorthPoleLocationValues(mContext);
//
// // Test the basic content provider query
// Cursor locationCursor = mContext.getContentResolver().query(
// LocationEntry.CONTENT_URI,
// null,
// null,
// null,
// null
// );
//
// // Make sure we get the correct cursor out of the database
// TestUtilities.validateCursor("testBasicLocationQueries, location query", locationCursor, testValues);
//
// // Has the NotificationUri been set correctly? --- we can only test this easily against API
// // level 19 or greater because getNotificationUri was added in API level 19.
// if ( Build.VERSION.SDK_INT >= 19 ) {
// assertEquals("Error: Location Query did not properly set NotificationUri",
// locationCursor.getNotificationUri(), LocationEntry.CONTENT_URI);
// }
// }
//
// /*
// This test uses the provider to insert and then update the data. Uncomment this test to
// see if your update location is functioning correctly.
// */
// public void testUpdateLocation() {
// // Create a new map of values, where column names are the keys
// ContentValues values = TestUtilities.createNorthPoleLocationValues();
//
// Uri locationUri = mContext.getContentResolver().
// insert(LocationEntry.CONTENT_URI, values);
// long locationRowId = ContentUris.parseId(locationUri);
//
// // Verify we got a row back.
// assertTrue(locationRowId != -1);
// Log.d(LOG_TAG, "New row id: " + locationRowId);
//
// ContentValues updatedValues = new ContentValues(values);
// updatedValues.put(LocationEntry._ID, locationRowId);
// updatedValues.put(LocationEntry.COLUMN_CITY_NAME, "Santa's Village");
//
// // Create a cursor with observer to make sure that the content provider is notifying
// // the observers as expected
// Cursor locationCursor = mContext.getContentResolver().query(LocationEntry.CONTENT_URI, null, null, null, null);
//
// TestUtilities.TestContentObserver tco = TestUtilities.getTestContentObserver();
// locationCursor.registerContentObserver(tco);
//
// int count = mContext.getContentResolver().update(
// LocationEntry.CONTENT_URI, updatedValues, LocationEntry._ID + "= ?",
// new String[] { Long.toString(locationRowId)});
// assertEquals(count, 1);
//
// // Test to make sure our observer is called. If not, we throw an assertion.
// //
// // Students: If your code is failing here, it means that your content provider
// // isn't calling getContext().getContentResolver().notifyChange(uri, null);
// tco.waitForNotificationOrFail();
//
// locationCursor.unregisterContentObserver(tco);
// locationCursor.close();
//
// // A cursor is your primary interface to the query results.
// Cursor cursor = mContext.getContentResolver().query(
// LocationEntry.CONTENT_URI,
// null, // projection
// LocationEntry._ID + " = " + locationRowId,
// null, // Values for the "where" clause
// null // sort order
// );
//
// TestUtilities.validateCursor("testUpdateLocation. Error validating location entry update.",
// cursor, updatedValues);
//
// cursor.close();
// }
//
//
// // Make sure we can still delete after adding/updating stuff
// //
// // Student: Uncomment this test after you have completed writing the insert functionality
// // in your provider. It relies on insertions with testInsertReadProvider, so insert and
// // query functionality must also be complete before this test can be used.
// public void testInsertReadProvider() {
// ContentValues testValues = TestUtilities.createNorthPoleLocationValues();
//
// // Register a content observer for our insert. This time, directly with the content resolver
// TestUtilities.TestContentObserver tco = TestUtilities.getTestContentObserver();
// mContext.getContentResolver().registerContentObserver(LocationEntry.CONTENT_URI, true, tco);
// Uri locationUri = mContext.getContentResolver().insert(LocationEntry.CONTENT_URI, testValues);
//
// // Did our content observer get called? Students: If this fails, your insert location
// // isn't calling getContext().getContentResolver().notifyChange(uri, null);
// tco.waitForNotificationOrFail();
// mContext.getContentResolver().unregisterContentObserver(tco);
//
// long locationRowId = ContentUris.parseId(locationUri);
//
// // Verify we got a row back.
// assertTrue(locationRowId != -1);
//
// // Data's inserted. IN THEORY. Now pull some out to stare at it and verify it made
// // the round trip.
//
// // A cursor is your primary interface to the query results.
// Cursor cursor = mContext.getContentResolver().query(
// LocationEntry.CONTENT_URI,
// null, // leaving "columns" null just returns all the columns.
// null, // cols for "where" clause
// null, // values for "where" clause
// null // sort order
// );
//
// TestUtilities.validateCursor("testInsertReadProvider. Error validating LocationEntry.",
// cursor, testValues);
//
// // Fantastic. Now that we have a location, add some weather!
// ContentValues weatherValues = TestUtilities.createWeatherValues(locationRowId);
// // The TestContentObserver is a one-shot class
// tco = TestUtilities.getTestContentObserver();
//
// mContext.getContentResolver().registerContentObserver(WeatherEntry.CONTENT_URI, true, tco);
//
// Uri weatherInsertUri = mContext.getContentResolver()
// .insert(WeatherEntry.CONTENT_URI, weatherValues);
// assertTrue(weatherInsertUri != null);
//
// // Did our content observer get called? Students: If this fails, your insert weather
// // in your ContentProvider isn't calling
// // getContext().getContentResolver().notifyChange(uri, null);
// tco.waitForNotificationOrFail();
// mContext.getContentResolver().unregisterContentObserver(tco);
//
// // A cursor is your primary interface to the query results.
// Cursor weatherCursor = mContext.getContentResolver().query(
// WeatherEntry.CONTENT_URI, // Table to Query
// null, // leaving "columns" null just returns all the columns.
// null, // cols for "where" clause
// null, // values for "where" clause
// null // columns to group by
// );
//
// TestUtilities.validateCursor("testInsertReadProvider. Error validating WeatherEntry insert.",
// weatherCursor, weatherValues);
//
// // Add the location values in with the weather data so that we can make
// // sure that the join worked and we actually get all the values back
// weatherValues.putAll(testValues);
//
// // Get the joined Weather and Location data
// weatherCursor = mContext.getContentResolver().query(
// WeatherEntry.buildWeatherLocation(TestUtilities.TEST_LOCATION),
// null, // leaving "columns" null just returns all the columns.
// null, // cols for "where" clause
// null, // values for "where" clause
// null // sort order
// );
// TestUtilities.validateCursor("testInsertReadProvider. Error validating joined Weather and Location Data.",
// weatherCursor, weatherValues);
//
// // Get the joined Weather and Location data with a start date
// weatherCursor = mContext.getContentResolver().query(
// WeatherEntry.buildWeatherLocationWithStartDate(
// TestUtilities.TEST_LOCATION, TestUtilities.TEST_DATE),
// null, // leaving "columns" null just returns all the columns.
// null, // cols for "where" clause
// null, // values for "where" clause
// null // sort order
// );
// TestUtilities.validateCursor("testInsertReadProvider. Error validating joined Weather and Location Data with start date.",
// weatherCursor, weatherValues);
//
// // Get the joined Weather data for a specific date
// weatherCursor = mContext.getContentResolver().query(
// WeatherEntry.buildWeatherLocationWithDate(TestUtilities.TEST_LOCATION, TestUtilities.TEST_DATE),
// null,
// null,
// null,
// null
// );
// TestUtilities.validateCursor("testInsertReadProvider. Error validating joined Weather and Location data for a specific date.",
// weatherCursor, weatherValues);
// }
//
// // Make sure we can still delete after adding/updating stuff
// //
// // Student: Uncomment this test after you have completed writing the delete functionality
// // in your provider. It relies on insertions with testInsertReadProvider, so insert and
// // query functionality must also be complete before this test can be used.
// public void testDeleteRecords() {
// testInsertReadProvider();
//
// // Register a content observer for our location delete.
// TestUtilities.TestContentObserver locationObserver = TestUtilities.getTestContentObserver();
// mContext.getContentResolver().registerContentObserver(LocationEntry.CONTENT_URI, true, locationObserver);
//
// // Register a content observer for our weather delete.
// TestUtilities.TestContentObserver weatherObserver = TestUtilities.getTestContentObserver();
// mContext.getContentResolver().registerContentObserver(WeatherEntry.CONTENT_URI, true, weatherObserver);
//
// deleteAllRecordsFromProvider();
//
// // Students: If either of these fail, you most-likely are not calling the
// // getContext().getContentResolver().notifyChange(uri, null); in the ContentProvider
// // delete. (only if the insertReadProvider is succeeding)
// locationObserver.waitForNotificationOrFail();
// weatherObserver.waitForNotificationOrFail();
//
// mContext.getContentResolver().unregisterContentObserver(locationObserver);
// mContext.getContentResolver().unregisterContentObserver(weatherObserver);
// }
//
//
// static private final int BULK_INSERT_RECORDS_TO_INSERT = 10;
// static ContentValues[] createBulkInsertWeatherValues(long locationRowId) {
// long currentTestDate = TestUtilities.TEST_DATE;
// long millisecondsInADay = 1000*60*60*24;
// ContentValues[] returnContentValues = new ContentValues[BULK_INSERT_RECORDS_TO_INSERT];
//
// for ( int i = 0; i < BULK_INSERT_RECORDS_TO_INSERT; i++, currentTestDate+= millisecondsInADay ) {
// ContentValues weatherValues = new ContentValues();
// weatherValues.put(WeatherContract.WeatherEntry.COLUMN_LOC_KEY, locationRowId);
// weatherValues.put(WeatherContract.WeatherEntry.COLUMN_DATE, currentTestDate);
// weatherValues.put(WeatherContract.WeatherEntry.COLUMN_DEGREES, 1.1);
// weatherValues.put(WeatherContract.WeatherEntry.COLUMN_HUMIDITY, 1.2 + 0.01 * (float) i);
// weatherValues.put(WeatherContract.WeatherEntry.COLUMN_PRESSURE, 1.3 - 0.01 * (float) i);
// weatherValues.put(WeatherContract.WeatherEntry.COLUMN_MAX_TEMP, 75 + i);
// weatherValues.put(WeatherContract.WeatherEntry.COLUMN_MIN_TEMP, 65 - i);
// weatherValues.put(WeatherContract.WeatherEntry.COLUMN_SHORT_DESC, "Asteroids");
// weatherValues.put(WeatherContract.WeatherEntry.COLUMN_WIND_SPEED, 5.5 + 0.2 * (float) i);
// weatherValues.put(WeatherContract.WeatherEntry.COLUMN_WEATHER_ID, 321);
// returnContentValues[i] = weatherValues;
// }
// return returnContentValues;
// }
//
// // Student: Uncomment this test after you have completed writing the BulkInsert functionality
// // in your provider. Note that this test will work with the built-in (default) provider
// // implementation, which just inserts records one-at-a-time, so really do implement the
// // BulkInsert ContentProvider function.
//// public void testBulkInsert() {
//// // first, let's create a location value
//// ContentValues testValues = TestUtilities.createNorthPoleLocationValues();
//// Uri locationUri = mContext.getContentResolver().insert(LocationEntry.CONTENT_URI, testValues);
//// long locationRowId = ContentUris.parseId(locationUri);
////
//// // Verify we got a row back.
//// assertTrue(locationRowId != -1);
////
//// // Data's inserted. IN THEORY. Now pull some out to stare at it and verify it made
//// // the round trip.
////
//// // A cursor is your primary interface to the query results.
//// Cursor cursor = mContext.getContentResolver().query(
//// LocationEntry.CONTENT_URI,
//// null, // leaving "columns" null just returns all the columns.
//// null, // cols for "where" clause
//// null, // values for "where" clause
//// null // sort order
//// );
////
//// TestUtilities.validateCursor("testBulkInsert. Error validating LocationEntry.",
//// cursor, testValues);
////
//// // Now we can bulkInsert some weather. In fact, we only implement BulkInsert for weather
//// // entries. With ContentProviders, you really only have to implement the features you
//// // use, after all.
//// ContentValues[] bulkInsertContentValues = createBulkInsertWeatherValues(locationRowId);
////
//// // Register a content observer for our bulk insert.
//// TestUtilities.TestContentObserver weatherObserver = TestUtilities.getTestContentObserver();
//// mContext.getContentResolver().registerContentObserver(WeatherEntry.CONTENT_URI, true, weatherObserver);
////
//// int insertCount = mContext.getContentResolver().bulkInsert(WeatherEntry.CONTENT_URI, bulkInsertContentValues);
////
//// // Students: If this fails, it means that you most-likely are not calling the
//// // getContext().getContentResolver().notifyChange(uri, null); in your BulkInsert
//// // ContentProvider method.
//// weatherObserver.waitForNotificationOrFail();
//// mContext.getContentResolver().unregisterContentObserver(weatherObserver);
////
//// assertEquals(insertCount, BULK_INSERT_RECORDS_TO_INSERT);
////
//// // A cursor is your primary interface to the query results.
//// cursor = mContext.getContentResolver().query(
//// WeatherEntry.CONTENT_URI,
//// null, // leaving "columns" null just returns all the columns.
//// null, // cols for "where" clause
//// null, // values for "where" clause
//// WeatherEntry.COLUMN_DATE + " ASC" // sort order == by DATE ASCENDING
//// );
////
//// // we should have as many records in the database as we've inserted
//// assertEquals(cursor.getCount(), BULK_INSERT_RECORDS_TO_INSERT);
////
//// // and let's make sure they match the ones we created
//// cursor.moveToFirst();
//// for ( int i = 0; i < BULK_INSERT_RECORDS_TO_INSERT; i++, cursor.moveToNext() ) {
//// TestUtilities.validateCurrentRecord("testBulkInsert. Error validating WeatherEntry " + i,
//// cursor, bulkInsertContentValues[i]);
//// }
//// cursor.close();
//// }
public static final String LOG_TAG = TestProvider.class.getSimpleName();
public void deleteAllRecords() {
Log.d(LOG_TAG, "TEST: TestProvider.deleteAllRecords()");
mContext.getContentResolver().delete(
MovieEntry.CONTENT_URI,
null,
null
);
Cursor cursor = mContext.getContentResolver().query(
MovieEntry.CONTENT_URI,
null,
null,
null,
null
);
assertNotNull(cursor);
if(cursor.moveToFirst())
{
int columns = cursor.getColumnCount();
Log.e(LOG_TAG, "DETECTED VALUES:\n");
for(int i = 0; i < columns; i++)
{
Log.e(LOG_TAG, cursor.getColumnName(i) + ": ");
}
Log.e(LOG_TAG, "SUSPECT FAVORITE? " + cursor.getInt(cursor.getColumnIndex(MovieEntry.COLUMN_FAVORITE)));
}
assertEquals("Error: Movie Records Not Deleted!", 0, cursor.getCount());
cursor.close();
}
@Override
protected void setUp() throws Exception {
super.setUp();
deleteAllRecords();
}
/*
This test checks to make sure that the content provider is registered correctly.
Students: Uncomment this test to make sure you've correctly registered the MovieProvider.
*/
public void testProviderRegistry() {
Log.d(LOG_TAG, "TEST: TestProvider.testProviderRegistry()");
PackageManager pm = mContext.getPackageManager();
ComponentName componentName =
new ComponentName(mContext.getPackageName(), MovieProvider.class.getName());
try {
ProviderInfo providerInfo = pm.getProviderInfo(componentName, 0);
assertEquals("Error: MovieProvider registered with authority: " + providerInfo.authority +
" instead of authority: " + MovieContract.CONTENT_AUTHORITY,
providerInfo.authority, MovieContract.CONTENT_AUTHORITY);
} catch (PackageManager.NameNotFoundException e) {
assertTrue("Error: MovieProvider not registered at " + mContext.getPackageName(),false);
}
}
public void testGetType() {
Log.d(LOG_TAG, "TEST: TestProvider.testGetType()");
String type = mContext.getContentResolver().getType(MovieEntry.CONTENT_URI);
assertEquals("Error: the MovieEntry CONTENT_URI should return MovieEntry.CONTENT_TYPE",
MovieEntry.CONTENT_TYPE, type);
long testId = 550;
type = mContext.getContentResolver().getType(MovieEntry.buildMovieUriWithId(testId));
assertEquals("Error: the MovieEntry CONTENT_URI WITH ID " +
"should return MovieEntry.CONTENT_ITEM_TYPE", MovieEntry.CONTENT_ITEM_TYPE, type);
}
public void testBasicMovieQuery() {
Log.d(LOG_TAG, "TEST: TestProvider.testBasicMovieQuery()");
MovieDbHelper dbHelper = new MovieDbHelper(mContext);
SQLiteDatabase db = dbHelper.getWritableDatabase();
String filePath = TestUtilities.storeTestImage(mContext);
ContentValues MovieValues = TestUtilities.createFightClubMovieValues(filePath);
long MovieRowId = db.insert(MovieEntry.TABLE_NAME, null, MovieValues);
assertTrue("Unable to Insert MovieEntry into the Database", MovieRowId != -1);
db.close();
Cursor MovieCursor = mContext.getContentResolver().query(
MovieEntry.CONTENT_URI,
null,
null,
null,
null
);
TestUtilities.validateCursor("testBasicMovieQuery", MovieCursor, MovieValues);
}
public void testUpdateMovie() {
Log.d(LOG_TAG, "TEST: TestProvider.testUpdateMovie()");
String filePath = TestUtilities.storeTestImage(mContext);
ContentValues values = TestUtilities.createFightClubMovieValues(filePath);
Uri movieUri = mContext.getContentResolver().insert(MovieEntry.CONTENT_URI, values);
long movieTMDBId = ContentUris.parseId(movieUri);
Log.d(LOG_TAG, "Movie ID: " + movieTMDBId);
ContentValues updatedValues = new ContentValues(values);
updatedValues.put(MovieEntry.COLUMN_POPULARITY, 25.3);
// Create a cursor with observer to make sure that the content provider is notifying
// the observers as expected
Cursor movieCursor = mContext.getContentResolver().query(MovieEntry.CONTENT_URI,
null, null, null, null);
assertNotNull(movieCursor);
TestUtilities.TestContentObserver tco = TestUtilities.getTestContentObserver();
movieCursor.registerContentObserver(tco);
int count = mContext.getContentResolver().update(
MovieEntry.CONTENT_URI, updatedValues, MovieEntry.COLUMN_TMDB_ID + " = ?",
new String[] { Long.toString(movieTMDBId)});
assertEquals(1, count);
tco.waitForNotificationOrFail();
movieCursor.unregisterContentObserver(tco);
movieCursor.close();
Cursor cursor = mContext.getContentResolver().query(
MovieEntry.CONTENT_URI,
null, // projection
MovieEntry.COLUMN_TMDB_ID + " = " + movieTMDBId,
null, // Values for the "where" clause
null // sort order
);
assertNotNull(cursor);
TestUtilities.validateCursor("testUpdateLocation. Error validating location entry update.",
cursor, updatedValues);
cursor.close();
}
public void testInsertReadProvider() {
Log.d(LOG_TAG, "TEST: TestProvider.testInsertReadProvider()");
String filePath = TestUtilities.storeTestImage(mContext);
ContentValues testValues = TestUtilities.createFightClubMovieValues(filePath);
TestUtilities.TestContentObserver tco = TestUtilities.getTestContentObserver();
mContext.getContentResolver().registerContentObserver(MovieEntry.CONTENT_URI, true, tco);
Uri movieUri = mContext.getContentResolver().insert(MovieEntry.CONTENT_URI, testValues);
tco.waitForNotificationOrFail();
mContext.getContentResolver().unregisterContentObserver(tco);
long movieRowId = ContentUris.parseId(movieUri);
assertTrue(movieRowId != -1);
Cursor cursor = mContext.getContentResolver().query(
MovieEntry.CONTENT_URI,
null,
null,
null,
null
);
TestUtilities.validateCursor("testInsertReadProvider. Error validating MovieEntry.",
cursor, testValues);
}
public void testDeleteRecords() {
testInsertReadProvider();
TestUtilities.TestContentObserver MovieObserver = TestUtilities.getTestContentObserver();
mContext.getContentResolver().registerContentObserver(MovieEntry.CONTENT_URI, true, MovieObserver);
deleteAllRecords();
MovieObserver.waitForNotificationOrFail();
mContext.getContentResolver().unregisterContentObserver(MovieObserver);
}
static private final int BULK_INSERT_RECORDS_TO_INSERT = 10;
static ContentValues[] createBulkInsertMovieValues(Context context) {
String movie = TestUtilities.storeTestImage(context);
ContentValues[] returnContentValues = new ContentValues[BULK_INSERT_RECORDS_TO_INSERT];
long startID = 1;
for ( int i = 0; i < BULK_INSERT_RECORDS_TO_INSERT; i++) {
ContentValues MovieValues = new ContentValues();
MovieValues.put(MovieContract.MovieEntry.COLUMN_TMDB_ID, startID++);
MovieValues.put(MovieContract.MovieEntry.COLUMN_TITLE, "Game Grumps: The Movie");
MovieValues.put(MovieContract.MovieEntry.COLUMN_DESC, "Two men, one game, 10 minutes");
MovieValues.put(MovieContract.MovieEntry.COLUMN_POPULARITY, 9001);
MovieValues.put(MovieContract.MovieEntry.COLUMN_IMG_PATH, movie);
MovieValues.put(MovieContract.MovieEntry.COLUMN_FAVORITE, 1);
MovieValues.put(MovieContract.MovieEntry.COLUMN_RELEASE, "2012-07-18");
MovieValues.put(MovieContract.MovieEntry.COLUMN_RUNTIME, 18);
MovieValues.put(MovieContract.MovieEntry.COLUMN_VOTE_CNT, 3047171);
MovieValues.put(MovieContract.MovieEntry.COLUMN_RATING, 11.2);
MovieValues.put(MovieContract.MovieEntry.COLUMN_GENRES, "Comedy_Romance_Drama");
returnContentValues[i] = MovieValues;
}
return returnContentValues;
}
public void testBulkInsert() {
ContentValues[] bulkInsertContentValues = createBulkInsertMovieValues(mContext);
// Register a content observer for our bulk insert.
TestUtilities.TestContentObserver movieObserver = TestUtilities.getTestContentObserver();
mContext.getContentResolver()
.registerContentObserver(MovieEntry.CONTENT_URI, true, movieObserver);
int insertCount = mContext.getContentResolver().bulkInsert(MovieEntry.CONTENT_URI,
bulkInsertContentValues);
movieObserver.waitForNotificationOrFail();
mContext.getContentResolver().unregisterContentObserver(movieObserver);
assertEquals(insertCount, BULK_INSERT_RECORDS_TO_INSERT);
Cursor cursor = mContext.getContentResolver().query(
MovieEntry.CONTENT_URI,
null,
null,
null,
null
);
assertNotNull(cursor);
assertEquals(cursor.getCount(), BULK_INSERT_RECORDS_TO_INSERT);
cursor.moveToFirst();
for ( int i = 0; i < BULK_INSERT_RECORDS_TO_INSERT; i++, cursor.moveToNext() ) {
TestUtilities.validateCurrentRecord("testBulkInsert. Error validating MovieEntry " + i,
cursor, bulkInsertContentValues[i]);
}
cursor.close();
}
}

View File

@@ -15,41 +15,24 @@
*/
package com.example.android.popularmovies.data;
import android.content.UriMatcher;
import android.net.Uri;
import android.test.AndroidTestCase;
import android.util.Log;
/*
Uncomment this class when you are ready to test your UriMatcher. Note that this class utilizes
constants that are declared with package protection inside of the UriMatcher, which is why
the test must be in the same data package as the Android app code. Doing the test this way is
a nice compromise between data hiding and testability.
*/
public class TestUriMatcher extends AndroidTestCase {
// private static final String LOCATION_QUERY = "London, UK";
// private static final long TEST_DATE = 1419033600L; // December 20th, 2014
// private static final long TEST_LOCATION_ID = 10L;
//
// // content://com.example.android.sunshine.app/weather"
// private static final Uri TEST_WEATHER_DIR = WeatherContract.WeatherEntry.CONTENT_URI;
// private static final Uri TEST_WEATHER_WITH_LOCATION_DIR = WeatherContract.WeatherEntry.buildWeatherLocation(LOCATION_QUERY);
// private static final Uri TEST_WEATHER_WITH_LOCATION_AND_DATE_DIR = WeatherContract.WeatherEntry.buildWeatherLocationWithDate(LOCATION_QUERY, TEST_DATE);
// // content://com.example.android.sunshine.app/location"
// private static final Uri TEST_LOCATION_DIR = WeatherContract.LocationEntry.CONTENT_URI;
//
// /*
// Students: This function tests that your UriMatcher returns the correct integer value
// for each of the Uri types that our ContentProvider can handle. Uncomment this when you are
// ready to test your UriMatcher.
// */
// public void testUriMatcher() {
// UriMatcher testMatcher = WeatherProvider.buildUriMatcher();
//
// assertEquals("Error: The WEATHER URI was matched incorrectly.",
// testMatcher.match(TEST_WEATHER_DIR), WeatherProvider.WEATHER);
// assertEquals("Error: The WEATHER WITH LOCATION URI was matched incorrectly.",
// testMatcher.match(TEST_WEATHER_WITH_LOCATION_DIR), WeatherProvider.WEATHER_WITH_LOCATION);
// assertEquals("Error: The WEATHER WITH LOCATION AND DATE URI was matched incorrectly.",
// testMatcher.match(TEST_WEATHER_WITH_LOCATION_AND_DATE_DIR), WeatherProvider.WEATHER_WITH_LOCATION_AND_DATE);
// assertEquals("Error: The LOCATION URI was matched incorrectly.",
// testMatcher.match(TEST_LOCATION_DIR), WeatherProvider.LOCATION);
// }
private static final String LOG_TAG = TestUriMatcher.class.getSimpleName();
private static final long MOVIE_ID = 550;
private static final Uri TEST_MOVIE_DIR = MovieContract.MovieEntry.CONTENT_URI;
private static final Uri TEST_MOVIE_WITH_ID_DIR = MovieContract.MovieEntry.buildMovieUriWithId(MOVIE_ID);
public void testUriMatcher() {
Log.d(LOG_TAG, "TEST: TestUriMatcher.testUriMatcher()");
UriMatcher testMatcher = MovieProvider.buildUriMatcher();
assertEquals("Error: The MOVIES URI was matched incorrectly.",
testMatcher.match(TEST_MOVIE_DIR), MovieProvider.MOVIES);
assertEquals("Error: The MOVIE WITH ID URI was matched incorrectly.",
testMatcher.match(TEST_MOVIE_WITH_ID_DIR), MovieProvider.MOVIE_WITH_ID);
}
}

View File

@@ -1,138 +1,117 @@
package com.example.android.popularmovies.data;
import android.content.ContentValues;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import android.test.AndroidTestCase;
/*
Students: These are functions and some test data to make it easier to test your database and
Content Provider. Note that you'll want your WeatherContract class to exactly match the one
in our solution to use these as-given.
*/
import com.example.android.popularmovies.R;
import com.example.android.popularmovies.utils.PollingCheck;
import java.util.Map;
import java.util.Set;
public class TestUtilities extends AndroidTestCase {
// static final String TEST_LOCATION = "99705";
// static final long TEST_DATE = 1419033600L; // December 20th, 2014
//
// static void validateCursor(String error, Cursor valueCursor, ContentValues expectedValues) {
// assertTrue("Empty cursor returned. " + error, valueCursor.moveToFirst());
// validateCurrentRecord(error, valueCursor, expectedValues);
// valueCursor.close();
// }
//
// static void validateCurrentRecord(String error, Cursor valueCursor, ContentValues expectedValues) {
// Set<Map.Entry<String, Object>> valueSet = expectedValues.valueSet();
// for (Map.Entry<String, Object> entry : valueSet) {
// String columnName = entry.getKey();
// int idx = valueCursor.getColumnIndex(columnName);
// assertFalse("Column '" + columnName + "' not found. " + error, idx == -1);
// String expectedValue = entry.getValue().toString();
// assertEquals("Value '" + entry.getValue().toString() +
// "' did not match the expected value '" +
// expectedValue + "'. " + error, expectedValue, valueCursor.getString(idx));
// }
// }
//
// /*
// Students: Use this to create some default weather values for your database tests.
// */
// static ContentValues createWeatherValues(long locationRowId) {
// ContentValues weatherValues = new ContentValues();
// weatherValues.put(WeatherContract.WeatherEntry.COLUMN_LOC_KEY, locationRowId);
// weatherValues.put(WeatherContract.WeatherEntry.COLUMN_DATE, TEST_DATE);
// weatherValues.put(WeatherContract.WeatherEntry.COLUMN_DEGREES, 1.1);
// weatherValues.put(WeatherContract.WeatherEntry.COLUMN_HUMIDITY, 1.2);
// weatherValues.put(WeatherContract.WeatherEntry.COLUMN_PRESSURE, 1.3);
// weatherValues.put(WeatherContract.WeatherEntry.COLUMN_MAX_TEMP, 75);
// weatherValues.put(WeatherContract.WeatherEntry.COLUMN_MIN_TEMP, 65);
// weatherValues.put(WeatherContract.WeatherEntry.COLUMN_SHORT_DESC, "Asteroids");
// weatherValues.put(WeatherContract.WeatherEntry.COLUMN_WIND_SPEED, 5.5);
// weatherValues.put(WeatherContract.WeatherEntry.COLUMN_WEATHER_ID, 321);
//
// return weatherValues;
// }
//
// /*
// Students: You can uncomment this helper function once you have finished creating the
// LocationEntry part of the WeatherContract.
// */
// static ContentValues createNorthPoleLocationValues() {
// // Create a new map of values, where column names are the keys
// ContentValues testValues = new ContentValues();
// testValues.put(WeatherContract.LocationEntry.COLUMN_LOCATION_SETTING, TEST_LOCATION);
// testValues.put(WeatherContract.LocationEntry.COLUMN_CITY_NAME, "North Pole");
// testValues.put(WeatherContract.LocationEntry.COLUMN_COORD_LAT, 64.7488);
// testValues.put(WeatherContract.LocationEntry.COLUMN_COORD_LONG, -147.353);
//
// return testValues;
// }
//
// /*
// Students: You can uncomment this function once you have finished creating the
// LocationEntry part of the WeatherContract as well as the WeatherDbHelper.
// */
// static long insertNorthPoleLocationValues(Context context) {
// // insert our test records into the database
// WeatherDbHelper dbHelper = new WeatherDbHelper(context);
// SQLiteDatabase db = dbHelper.getWritableDatabase();
// ContentValues testValues = TestUtilities.createNorthPoleLocationValues();
//
// long locationRowId;
// locationRowId = db.insert(WeatherContract.LocationEntry.TABLE_NAME, null, testValues);
//
// // Verify we got a row back.
// assertTrue("Error: Failure to insert North Pole Location Values", locationRowId != -1);
//
// return locationRowId;
// }
//
// /*
// Students: The functions we provide inside of TestProvider use this utility class to test
// the ContentObserver callbacks using the PollingCheck class that we grabbed from the Android
// CTS tests.
//
// Note that this only tests that the onChange function is called; it does not test that the
// correct Uri is returned.
// */
// static class TestContentObserver extends ContentObserver {
// final HandlerThread mHT;
// boolean mContentChanged;
//
// static TestContentObserver getTestContentObserver() {
// HandlerThread ht = new HandlerThread("ContentObserverThread");
// ht.start();
// return new TestContentObserver(ht);
// }
//
// private TestContentObserver(HandlerThread ht) {
// super(new Handler(ht.getLooper()));
// mHT = ht;
// }
//
// // On earlier versions of Android, this onChange method is called
// @Override
// public void onChange(boolean selfChange) {
// onChange(selfChange, null);
// }
//
// @Override
// public void onChange(boolean selfChange, Uri uri) {
// mContentChanged = true;
// }
//
// public void waitForNotificationOrFail() {
// // Note: The PollingCheck class is taken from the Android CTS (Compatibility Test Suite).
// // It's useful to look at the Android CTS source for ideas on how to test your Android
// // applications. The reason that PollingCheck works is that, by default, the JUnit
// // testing framework is not running on the main Android application thread.
// new PollingCheck(5000) {
// @Override
// protected boolean check() {
// return mContentChanged;
// }
// }.run();
// mHT.quit();
// }
// }
//
// static TestContentObserver getTestContentObserver() {
// return TestContentObserver.getTestContentObserver();
// }
static String storeTestImage(Context context) {
Bitmap bm = BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher);
return FileUtils.storeImage(context, bm, "FightClub.png");
}
static Bitmap getTestImage(Context context) {
return BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher);
}
static void validateCursor(String error, Cursor valueCursor, ContentValues expectedValues) {
assertTrue("Empty cursor returned. " + error, valueCursor.moveToFirst());
validateCurrentRecord(error, valueCursor, expectedValues);
valueCursor.close();
}
static void validateCurrentRecord(String error, Cursor valueCursor,ContentValues expectedValues)
{
Set<Map.Entry<String, Object>> valueSet = expectedValues.valueSet();
for (Map.Entry<String, Object> entry : valueSet) {
String columnName = entry.getKey();
int idx = valueCursor.getColumnIndex(columnName);
assertFalse("Column '" + columnName + "' not found. " + error, idx == -1);
String expectedValue = entry.getValue().toString();
assertEquals("Value '" + entry.getValue().toString() +
"' did not match the expected value '" +
expectedValue + "'. " + error, expectedValue, valueCursor.getString(idx));
}
}
public static ContentValues createFightClubMovieValues(String imgPath) {
ContentValues testValues = new ContentValues();
testValues.put(MovieContract.MovieEntry.COLUMN_TMDB_ID, 550);
testValues.put(MovieContract.MovieEntry.COLUMN_TITLE,"Fight Club");
testValues.put(MovieContract.MovieEntry.COLUMN_DESC, "A ticking-time-bomb insomniac and a"+
" slippery soap salesman channel primal male aggression into a shocking new form " +
"of therapy. Their concept catches on, with underground \"fight clubs\" forming " +
"in every town, until an eccentric gets in the way and ignites an out-of-control " +
"spiral toward oblivion.");
testValues.put(MovieContract.MovieEntry.COLUMN_POPULARITY, 2.5);
//Can't use real double because of comparison problems.
testValues.put(MovieContract.MovieEntry.COLUMN_IMG_PATH, imgPath);
testValues.put(MovieContract.MovieEntry.COLUMN_RELEASE, "1999-10-14");
testValues.put(MovieContract.MovieEntry.COLUMN_RUNTIME, 139);
testValues.put(MovieContract.MovieEntry.COLUMN_FAVORITE, 1);
testValues.put(MovieContract.MovieEntry.COLUMN_VOTE_CNT, 3185);
testValues.put(MovieContract.MovieEntry.COLUMN_RATING, 7.7);
testValues.put(MovieContract.MovieEntry.COLUMN_GENRES, "Drama");
return testValues;
}
static class TestContentObserver extends ContentObserver {
final HandlerThread mHT;
boolean mContentChanged;
static TestContentObserver getTestContentObserver() {
HandlerThread ht = new HandlerThread("ContentObserverThread");
ht.start();
return new TestContentObserver(ht);
}
private TestContentObserver(HandlerThread ht) {
super(new Handler(ht.getLooper()));
mHT = ht;
}
// On earlier versions of Android, this onChange method is called
@Override
public void onChange(boolean selfChange) {
onChange(selfChange, null);
}
@Override
public void onChange(boolean selfChange, Uri uri) {
mContentChanged = true;
}
public void waitForNotificationOrFail() {
// Note: The PollingCheck class is taken from the Android CTS (Compatibility Test Suite).
// It's useful to look at the Android CTS source for ideas on how to test your Android
// applications. The reason that PollingCheck works is that, by default, the JUnit
// testing framework is not running on the main Android application thread.
new PollingCheck(5000) {
@Override
protected boolean check() {
return mContentChanged;
}
}.run();
mHT.quit();
}
}
static TestContentObserver getTestContentObserver() {
return TestContentObserver.getTestContentObserver();
}
}

View File

@@ -3,7 +3,6 @@
package="com.example.android.popularmovies">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
@@ -31,6 +30,9 @@
android:name="android.support.PARENT_ACTIVITY"
android:value="com.example.android.popularmovies.MainActivity"/>
</activity>
<provider
android:authorities="com.example.android.popularmovies"
android:name=".data.MovieProvider"/>
</application>
</manifest>

View File

@@ -73,7 +73,6 @@ public class Movie implements Comparable<Movie>
}
}
//TODO: Implementation tied explicitly to Picasso. Check out other libraries!
public static Bitmap getPoster(Context context, String posterPath) throws IOException
{
if(context == null || posterPath == null || posterPath.isEmpty())

View File

@@ -0,0 +1,84 @@
package com.example.android.popularmovies.data;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* Created by Aaron Helton on 2/15/2016
*/
public final class FileUtils
{
private static final String LOG_TAG = FileUtils.class.getSimpleName();
/**
* Stores an image in the internal cache directory.
* @param context the context for storing the file
* @param image the image to be stored
* @param fileName the name of the file to be stored
* @return the filepath for the stored image. Null if the save was unsuccessful.
*/
public static String storeImage(Context context, Bitmap image, String fileName)
{
//Sanity checks
if(image == null || fileName == null || fileName.isEmpty() || context == null)
return null;
File pictureFile;
String extension = getExtension(fileName);
if(extension == null) {
extension = "png";
pictureFile = new File(context.getFilesDir(), fileName + "." + extension);
}
else {
pictureFile = new File(context.getFilesDir(), fileName);
}
Bitmap.CompressFormat format;
switch(extension.toLowerCase()) {
case "png":
format = Bitmap.CompressFormat.PNG; break;
case "jpg":case "jpeg":
format = Bitmap.CompressFormat.JPEG; break;
case "webp":
format = Bitmap.CompressFormat.WEBP; break;
default:
throw new UnsupportedOperationException("Unsupported Format: "
+ extension.toUpperCase());
}
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
image.compress(format, 90, fos);
fos.close();
return pictureFile.getAbsolutePath();
} catch (FileNotFoundException e) {
Log.d(LOG_TAG, "File not found: " + e.getMessage());
return null;
} catch (IOException e) {
Log.d(LOG_TAG, "Error accessing file: " + e.getMessage());
return null;
}
}
public static Bitmap getImage(String file)
{
return BitmapFactory.decodeFile(file);
}
public static String getExtension(String fileName)
{
int dotIndex = fileName.lastIndexOf(".");
if(dotIndex == -1) {
return null;
} else {
return fileName.substring(dotIndex+1);
}
}
}

View File

@@ -29,15 +29,22 @@ public class MovieContract {
public static final String COLUMN_TMDB_ID = "tmdb_id";
public static final String COLUMN_TITLE = "title";
public static final String COLUMN_DESC = "overview";
public static final String COLUMN_POPULARITY = "popularity";
public static final String COLUMN_IMG_PATH = "img_path";
public static final String COLUMN_FAVORITE = "favorite";
public static final String COLUMN_RELEASE = "release_date";
public static final String COLUMN_RUNTIME = "runtime";
public static final String COLUMN_VOTE_CNT = "vote_count";
public static final String COLUMN_RATING = "rating";
public static final String COLUMN_GENRES = "genres";
public static final Uri buildMovieUri(long tmdb_id) {
public static Uri buildMovieUriWithId(long tmdb_id) {
return ContentUris.withAppendedId(CONTENT_URI, tmdb_id);
}
public static long getMovieIdFromUri(Uri uri)
{
return Long.parseLong(uri.getPathSegments().get(uri.getPathSegments().size()-1));
}
}
}

View File

@@ -17,22 +17,23 @@ public class MovieDbHelper extends SQLiteOpenHelper {
super(context, DB_NAME, null, DB_VER);
}
//TODO: Create the Favorites, Rating, and Popular Tables
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
final String SQL_CREATE_FAVORITES_TABLE = "CREATE TABLE " + MovieEntry.TABLE_NAME + " (" +
final String SQL_CREATE_MOVIES_TABLE = "CREATE TABLE " + MovieEntry.TABLE_NAME + " (" +
MovieEntry._ID + " INTEGER PRIMARY KEY," +
MovieEntry.COLUMN_TMDB_ID + " INTEGER UNIQUE NOT NULL," +
MovieEntry.COLUMN_TITLE + " TEXT NOT NULL," +
MovieEntry.COLUMN_DESC + " TEXT NOT NULL," +
MovieEntry.COLUMN_POPULARITY + " REAL NOT NULL," +
MovieEntry.COLUMN_IMG_PATH + " TEXT NOT NULL," +
MovieEntry.COLUMN_RELEASE + " TEXT NOT NULL," +
MovieEntry.COLUMN_RUNTIME + " INTEGER NOT NULL," +
MovieEntry.COLUMN_FAVORITE + " INTEGER NOT NULL DEFAULT 0," +
MovieEntry.COLUMN_VOTE_CNT + " INTEGER NOT NULL," +
MovieEntry.COLUMN_RATING + " REAL NOT NULL," +
MovieEntry.COLUMN_GENRES + " TEXT NOT NULL" +
" );";
sqLiteDatabase.execSQL(SQL_CREATE_FAVORITES_TABLE);
sqLiteDatabase.execSQL(SQL_CREATE_MOVIES_TABLE);
}
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {

View File

@@ -0,0 +1,203 @@
package com.example.android.popularmovies.data;
import android.annotation.TargetApi;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.support.annotation.NonNull;
/**
* Created by Aaron Helton on 2/16/2016
*/
public class MovieProvider extends ContentProvider
{
@SuppressWarnings("unused")
private static final String LOG_TAG = MovieProvider.class.getSimpleName();
private static final UriMatcher sUriMatcher = buildUriMatcher();
private MovieDbHelper mOpenHelper;
public static final int MOVIES = 100;
public static final int MOVIE_WITH_ID = 101;
public static UriMatcher buildUriMatcher() {
final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
final String authority = MovieContract.CONTENT_AUTHORITY;
matcher.addURI(authority, MovieContract.PATH_MOVIES, MOVIES);
matcher.addURI(authority, MovieContract.PATH_MOVIES + "/#", MOVIE_WITH_ID);
return matcher;
}
@Override
public boolean onCreate() {
mOpenHelper = new MovieDbHelper(getContext());
return true;
}
@Override
public String getType(@NonNull Uri uri)
{
final int match = sUriMatcher.match(uri);
switch(match) {
case MOVIES:
return MovieContract.MovieEntry.CONTENT_TYPE;
case MOVIE_WITH_ID:
return MovieContract.MovieEntry.CONTENT_ITEM_TYPE;
default:
throw new UnsupportedOperationException("Unknown Uri: " + uri);
}
}
@Override
public Cursor query(@NonNull Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder)
{
Cursor retCursor;
switch(sUriMatcher.match(uri)) {
case MOVIES:
retCursor = mOpenHelper.getReadableDatabase().query(
MovieContract.MovieEntry.TABLE_NAME,
projection, selection, selectionArgs, null, null, sortOrder);
break;
case MOVIE_WITH_ID:
retCursor = getMovieById(uri, projection);
break;
default:
throw new UnsupportedOperationException("Unknown URI: " + uri);
}
//noinspection ConstantConditions
retCursor.setNotificationUri(getContext().getContentResolver(), uri);
return retCursor;
}
@Override
public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs)
{
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
int rowsModified;
switch(match) {
case MOVIES: {
rowsModified = db.update(MovieContract.MovieEntry.TABLE_NAME, values,
selection, selectionArgs);
break;
}
default: {
throw new UnsupportedOperationException("Invalid URI: " + uri);
}
}
if(rowsModified > 0)
//noinspection ConstantConditions
getContext().getContentResolver().notifyChange(uri, null);
db.close();
return rowsModified;
}
@Override
public Uri insert(@NonNull Uri uri, ContentValues values) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
Uri returnUri;
switch(match)
{
case MOVIES: {
long id = values.getAsInteger(MovieContract.MovieEntry.COLUMN_TMDB_ID);
long rowId = db.insert(MovieContract.MovieEntry.TABLE_NAME, null, values);
if(rowId > 0)
returnUri = MovieContract.MovieEntry.buildMovieUriWithId(id);
else
throw new SQLException("Failed to insert row into " + uri);
break;
}
default:
throw new UnsupportedOperationException("Unknown URI: " + uri);
}
//noinspection ConstantConditions
getContext().getContentResolver().notifyChange(uri, null);
db.close();
return returnUri;
}
@Override
public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
if(selection == null)
selection = "1";
int rowsModified;
switch(match) {
case MOVIES: {
rowsModified = db.delete(MovieContract.MovieEntry.TABLE_NAME,
selection, selectionArgs);
break;
}
default:
throw new UnsupportedOperationException("Invalid URI: " + uri);
}
if(rowsModified > 0)
//noinspection ConstantConditions
getContext().getContentResolver().notifyChange(uri, null);
db.close();
return rowsModified;
}
private Cursor getMovieById(Uri uri, String[] projection)
{
long id = MovieContract.MovieEntry.getMovieIdFromUri(uri);
return mOpenHelper.getReadableDatabase().query(
MovieContract.MovieEntry.TABLE_NAME,
projection,
MovieContract.MovieEntry.COLUMN_TMDB_ID + " = ?",
new String[]{Long.toString(id)},
null,
null,
null
);
}
@Override
public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
switch (match) {
case MOVIES:
db.beginTransaction();
int returnCount = 0;
try {
for (ContentValues value : values) {
long _id = db.insert(MovieContract.MovieEntry.TABLE_NAME, null, value);
if (_id != -1) {
returnCount++;
}
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
//noinspection ConstantConditions
getContext().getContentResolver().notifyChange(uri, null);
db.close();
return returnCount;
default:
int count = super.bulkInsert(uri, values);
db.close();
return count;
}
}
@Override
@TargetApi(11)
public void shutdown() {
mOpenHelper.close();
super.shutdown();
}
}