diff --git a/Lab10_LocationLab/LocationLab.pdf b/Lab10_LocationLab/LocationLab.pdf new file mode 100644 index 0000000000000000000000000000000000000000..92b6e0d0de6583bd213129fac51411f460b7239b Binary files /dev/null and b/Lab10_LocationLab/LocationLab.pdf differ diff --git a/Lab10_LocationLab/app/build.gradle b/Lab10_LocationLab/app/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..4ca11faedf43c08c002cbe3c495fe3f8a7c697d9 --- /dev/null +++ b/Lab10_LocationLab/app/build.gradle @@ -0,0 +1,35 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 26 + buildToolsVersion "26.0.1" + + defaultConfig { + applicationId "course.labs.locationlab" + minSdkVersion 21 + targetSdkVersion 26 + + testApplicationId "course.labs.locationlab.test" + testInstrumentationRunner "android.test.InstrumentationTestRunner" + testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner' + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' + } + } +} + +dependencies { + androidTestCompile 'com.jayway.android.robotium:robotium-solo:5.6.0' + androidTestCompile 'com.android.support.test:rules:0.5' + androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.1' + compile 'com.android.support:support-annotations:26.0.0' + compile 'com.android.support:support-v4:26.0.1' +} + +configurations.all { + resolutionStrategy.force 'com.android.support:support-annotations:26.0.0' +} \ No newline at end of file diff --git a/Lab10_LocationLab/app/libs/robotium-solo-5.2.1.jar b/Lab10_LocationLab/app/libs/robotium-solo-5.2.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..2875fc4de9de5172a9d0f9f6faa7da1a11fcce79 Binary files /dev/null and b/Lab10_LocationLab/app/libs/robotium-solo-5.2.1.jar differ diff --git a/Lab10_LocationLab/app/src/androidTest/java/course/labs/locationlab/tests/PermGrantedTest.java b/Lab10_LocationLab/app/src/androidTest/java/course/labs/locationlab/tests/PermGrantedTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d16c557078bcb719c0cac057407d77837829b29c --- /dev/null +++ b/Lab10_LocationLab/app/src/androidTest/java/course/labs/locationlab/tests/PermGrantedTest.java @@ -0,0 +1,80 @@ +package course.labs.locationlab.tests; + +import android.content.Context; +import android.content.Intent; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SdkSuppress; +import android.support.test.runner.AndroidJUnit4; +import android.support.test.uiautomator.By; +import android.support.test.uiautomator.UiDevice; +import android.support.test.uiautomator.UiObject; +import android.support.test.uiautomator.UiObject2; +import android.support.test.uiautomator.UiObjectNotFoundException; +import android.support.test.uiautomator.UiSelector; +import android.support.test.uiautomator.Until; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.regex.Pattern; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +@RunWith(AndroidJUnit4.class) +@SdkSuppress(minSdkVersion = 21) + +public class PermGrantedTest { + + private static final int LAUNCH_TIMEOUT = 10000; + private UiDevice mDevice; + private static final String BASIC_SAMPLE_PACKAGE + = "course.labs.locationlab"; + @Before + public void startMainActivityFromHomeScreen() { + // Initialize UiDevice instance + mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + + // Start from the home screen + mDevice.pressHome(); + + // Wait for launcher + final String launcherPackage = mDevice.getLauncherPackageName(); + assertThat(launcherPackage, is(notNullValue())); + mDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), + LAUNCH_TIMEOUT); + + // Launch the app + Context context = InstrumentationRegistry.getContext(); + final Intent intent = context.getPackageManager() + .getLaunchIntentForPackage(BASIC_SAMPLE_PACKAGE); + + if (null == intent) fail(); + + // Clear out any previous instances + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + context.startActivity(intent); + + // Wait for the app to appear + mDevice.wait(Until.hasObject(By.pkg(BASIC_SAMPLE_PACKAGE).depth(0)), + LAUNCH_TIMEOUT); + //Give permission if it is not given already + // + UiObject2 PermissionDialog = mDevice.wait(Until.findObject(By.text("ALLOW")), 2000); + if (PermissionDialog != null) + PermissionDialog.click(); + + } + + @Test + public void testPermissionRequested() { + UiObject2 getNewPlace = mDevice.wait(Until.findObject(By.text("Get New Place")), 2000); + assertNotNull("Permission wasn't granted", getNewPlace); + getNewPlace.click(); + + } +} diff --git a/Lab10_LocationLab/app/src/androidTest/java/course/labs/locationlab/tests/TestNoCountryLocation.java b/Lab10_LocationLab/app/src/androidTest/java/course/labs/locationlab/tests/TestNoCountryLocation.java new file mode 100644 index 0000000000000000000000000000000000000000..beb341d053c3138638ca11ea2fedd1e6d319373b --- /dev/null +++ b/Lab10_LocationLab/app/src/androidTest/java/course/labs/locationlab/tests/TestNoCountryLocation.java @@ -0,0 +1,52 @@ +package course.labs.locationlab.tests; + +import android.test.ActivityInstrumentationTestCase2; + +import com.robotium.solo.Solo; + +import course.labs.locationlab.PlaceViewActivity; + +public class TestNoCountryLocation extends + ActivityInstrumentationTestCase2<PlaceViewActivity> { + private Solo solo; + + public TestNoCountryLocation() { + super(PlaceViewActivity.class); + } + + public void setUp() throws Exception { + solo = new Solo(getInstrumentation(), getActivity()); + PlaceViewActivity.sHasNetwork = false; + } + + @Override + public void tearDown() throws Exception { + solo.finishOpenedActivities(); + } + + public void testRun() { + + int delay = 2000; + int longDelay = 5000; + + // Wait for activity: 'course.labs.locationlab.PlaceViewActivity' + solo.waitForActivity(course.labs.locationlab.PlaceViewActivity.class, + delay); + + // Click on action bar item + solo.clickOnActionBarItem(course.labs.locationlab.R.id.place_no_country); + + solo.sleep(delay); + + // Click on Get New Place + solo.clickOnView(solo.getView(course.labs.locationlab.R.id.footer)); + + String noCountryString = solo + .getString(course.labs.locationlab.R.string.no_country_string); + + // Assert that no country toast is shown + assertTrue(noCountryString + " is not shown!", + solo.waitForText(noCountryString, 1, longDelay)); + + } +} diff --git a/Lab10_LocationLab/app/src/androidTest/java/course/labs/locationlab/tests/TestOneValidLocation.java b/Lab10_LocationLab/app/src/androidTest/java/course/labs/locationlab/tests/TestOneValidLocation.java new file mode 100644 index 0000000000000000000000000000000000000000..a504148393083764d74878bd32350c22388819ba --- /dev/null +++ b/Lab10_LocationLab/app/src/androidTest/java/course/labs/locationlab/tests/TestOneValidLocation.java @@ -0,0 +1,56 @@ +package course.labs.locationlab.tests; + +import android.test.ActivityInstrumentationTestCase2; +import android.util.Log; + +import com.robotium.solo.Solo; + +import course.labs.locationlab.PlaceViewActivity; +import course.labs.locationlab.R; + +public class TestOneValidLocation extends + ActivityInstrumentationTestCase2<PlaceViewActivity> { + private Solo solo; + + public TestOneValidLocation() { + super(PlaceViewActivity.class); + } + + public void setUp() throws Exception { + solo = new Solo(getInstrumentation(), getActivity()); + PlaceViewActivity.sHasNetwork = false; + } + + @Override + public void tearDown() throws Exception { + solo.finishOpenedActivities(); + } + + public void testRun() { + + int delay = 5000; + int longDelay = 8000; + + // Wait for activity: 'course.labs.locationlab.PlaceViewActivity' + solo.waitForActivity(course.labs.locationlab.PlaceViewActivity.class, + 2000); + + // Click on action bar item + solo.clickOnActionBarItem(course.labs.locationlab.R.id.place_one); + + solo.sleep(delay); + + // Click on Get New Place + solo.clickOnView(solo.getView(course.labs.locationlab.R.id.footer)); + + // Assert that PlaceBadge is shown + assertTrue("PlaceBadge is not shown!", solo.waitForText( + solo.getString(R.string.the_greenhouse_string), 1, longDelay)); + // Click on PlaceBadge + solo.clickOnText(solo.getString(R.string.the_greenhouse_string)); + + solo.sleep(delay); + + assertTrue("Detail view not shown!", solo.waitForText("Date")); + } +} diff --git a/Lab10_LocationLab/app/src/androidTest/java/course/labs/locationlab/tests/TestSameLocation.java b/Lab10_LocationLab/app/src/androidTest/java/course/labs/locationlab/tests/TestSameLocation.java new file mode 100644 index 0000000000000000000000000000000000000000..a53e098f1be5e6530143eaad66cbf6dd8ba80141 --- /dev/null +++ b/Lab10_LocationLab/app/src/androidTest/java/course/labs/locationlab/tests/TestSameLocation.java @@ -0,0 +1,66 @@ +package course.labs.locationlab.tests; + +import android.test.ActivityInstrumentationTestCase2; + +import com.robotium.solo.Solo; + +import course.labs.locationlab.PlaceViewActivity; +import course.labs.locationlab.R; + + +public class TestSameLocation extends ActivityInstrumentationTestCase2<PlaceViewActivity> { + private Solo solo; + + public TestSameLocation() { + super(PlaceViewActivity.class); + } + + public void setUp() throws Exception { + solo = new Solo(getInstrumentation(), getActivity()); + PlaceViewActivity.sHasNetwork = false; + } + + @Override + public void tearDown() throws Exception { + solo.finishOpenedActivities(); + } + + public void testRun() { + + int delay = 2000; + int longDelay = 5000; + + // Wait for activity: 'course.labs.locationlab.PlaceViewActivity' + solo.waitForActivity(course.labs.locationlab.PlaceViewActivity.class, delay); + + // Click on action bar item + solo.clickOnActionBarItem(course.labs.locationlab.R.id.place_one); + + solo.sleep(delay); + + // Click on Get New Place + solo.clickOnView(solo.getView(course.labs.locationlab.R.id.footer)); + + solo.sleep(2000); + + // Assert that PlaceBadge is shown + assertTrue("PlaceBadge is not shown!", solo.waitForText( + solo.getString(R.string.the_greenhouse_string), 1, longDelay)); + + // Click on action bar item + solo.clickOnActionBarItem(course.labs.locationlab.R.id.place_one); + + solo.sleep(delay); + + // Click on Get New Place + solo.clickOnView(solo.getView(course.labs.locationlab.R.id.footer)); + + String samePlaceString = solo + .getString(course.labs.locationlab.R.string.duplicate_location_string); + + // Assert that duplicate location Toast is shown + assertTrue(samePlaceString + " is not shown!", + solo.waitForText(samePlaceString, 1, longDelay)); + + } +} diff --git a/Lab10_LocationLab/app/src/androidTest/java/course/labs/locationlab/tests/TestTwoValidLocations.java b/Lab10_LocationLab/app/src/androidTest/java/course/labs/locationlab/tests/TestTwoValidLocations.java new file mode 100644 index 0000000000000000000000000000000000000000..057e0ebd31660210fe8507e4e30a4da0d7810d0e --- /dev/null +++ b/Lab10_LocationLab/app/src/androidTest/java/course/labs/locationlab/tests/TestTwoValidLocations.java @@ -0,0 +1,75 @@ +package course.labs.locationlab.tests; + +import course.labs.locationlab.PlaceViewActivity; +import course.labs.locationlab.R; + +import com.robotium.solo.*; + +import android.test.ActivityInstrumentationTestCase2; + +import org.junit.Test; + +public class TestTwoValidLocations extends + ActivityInstrumentationTestCase2<PlaceViewActivity> { + private Solo solo; + + public TestTwoValidLocations() { + super(PlaceViewActivity.class); + } + + public void setUp() throws Exception { + solo = new Solo(getInstrumentation(), getActivity()); + PlaceViewActivity.sHasNetwork = false; + } + + @Override + public void tearDown() throws Exception { + solo.finishOpenedActivities(); + } + @Test + public void testRun() { + + int delay = 2000; + int longDelay = 5000; + + // Wait for activity: 'course.labs.locationlab.PlaceViewActivity' + solo.waitForActivity(course.labs.locationlab.PlaceViewActivity.class, + 2000); + + // Click on action bar item + solo.clickOnActionBarItem(course.labs.locationlab.R.id.place_one); + + solo.sleep(delay); + + // Click on Get New Place + solo.clickOnView(solo.getView(course.labs.locationlab.R.id.footer)); + + solo.sleep(delay); + + // Assert that PlaceBadge is shown + assertTrue("PlaceBadge is not shown!", solo.waitForText( + solo.getString(R.string.the_greenhouse_string), 1, longDelay)); + + // Click on action bar item + solo.clickOnActionBarItem(course.labs.locationlab.R.id.place_two); + + solo.sleep(delay); + + // Click on Get New Place + solo.clickOnView(solo.getView(course.labs.locationlab.R.id.footer)); + + solo.sleep(delay); + + // Assert that PlaceBadge is shown + assertTrue("PlaceBadge is not shown!", solo.waitForText( + solo.getString(R.string.berwyn_string), 1, longDelay)); + + // Click on PlaceBadge + solo.clickOnText(solo.getString(R.string.berwyn_string)); + + solo.sleep(delay); + + assertTrue("Detail view not shown!", solo.waitForText("Date")); + + } +} diff --git a/Lab10_LocationLab/app/src/androidTest/res/drawable/.gitignore b/Lab10_LocationLab/app/src/androidTest/res/drawable/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Lab10_LocationLab/app/src/debug/AndroidManifest.xml b/Lab10_LocationLab/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000000000000000000000000000000000000..5e0fdf89160a005bfdb63447341985b69fdb0908 --- /dev/null +++ b/Lab10_LocationLab/app/src/debug/AndroidManifest.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="course.labs.locationlab" + android:versionCode="1" + android:versionName="1.0" > + + <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> + <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> + <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" /> + <uses-permission android:name="android.permission.INTERNET" /> + + <uses-sdk + android:minSdkVersion="17" + android:targetSdkVersion="19" /> + + <application + android:allowBackup="true" + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/AppTheme" > + <activity + android:name="course.labs.locationlab.PlaceViewActivity" + android:label="@string/app_name" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <activity + android:name="course.labs.locationlab.PlaceBadgeDetailActivity" + android:label="@string/badge_detail" > + </activity> + + </application> + +</manifest> \ No newline at end of file diff --git a/Lab10_LocationLab/app/src/main/AndroidManifest.xml b/Lab10_LocationLab/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000000000000000000000000000000000..8d577a59d4a01c7febe7c0185ca889166b757a87 --- /dev/null +++ b/Lab10_LocationLab/app/src/main/AndroidManifest.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="course.labs.locationlab" + android:versionCode="1" + android:versionName="1.0" > + + <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> + <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> + <uses-permission android:name="android.permission.INTERNET" /> + + <uses-sdk + android:minSdkVersion="17" + android:targetSdkVersion="19" /> + + <application + android:allowBackup="true" + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/AppTheme" > + <activity + android:name="course.labs.locationlab.PlaceViewActivity" + android:label="@string/app_name" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <activity + android:name="course.labs.locationlab.PlaceBadgeDetailActivity" + android:label="@string/badge_detail" > + </activity> + + </application> + +</manifest> \ No newline at end of file diff --git a/Lab10_LocationLab/app/src/main/java/course/labs/locationlab/MockLocationProvider.java b/Lab10_LocationLab/app/src/main/java/course/labs/locationlab/MockLocationProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..361cf4349ff4172178c7b13b8c39f0d5e4dcc427 --- /dev/null +++ b/Lab10_LocationLab/app/src/main/java/course/labs/locationlab/MockLocationProvider.java @@ -0,0 +1,46 @@ + package course.labs.locationlab; + +// Adapted from code found at: +// http://mobiarch.wordpress.com/2012/07/17/testing-with-mock-location-data-in-android/ + +import android.content.Context; +import android.location.Location; +import android.location.LocationManager; +import android.os.SystemClock; + +public class MockLocationProvider { + + private String mProviderName; + private LocationManager mLocationManager; + + private static float mockAccuracy = 5; + + public MockLocationProvider(String name, Context ctx) { + this.mProviderName = name; + + mLocationManager = (LocationManager) ctx + .getSystemService(Context.LOCATION_SERVICE); + mLocationManager.addTestProvider(mProviderName, false, false, false, + false, true, true, true, 0, 5); + mLocationManager.setTestProviderEnabled(mProviderName, true); + } + + public void pushLocation(double lat, double lon) { + + Location mockLocation = new Location(mProviderName); + mockLocation.setLatitude(lat); + mockLocation.setLongitude(lon); + mockLocation.setAltitude(0); + mockLocation.setTime(System.currentTimeMillis()); + mockLocation + .setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos()); + mockLocation.setAccuracy(mockAccuracy); + + mLocationManager.setTestProviderLocation(mProviderName, mockLocation); + + } + + public void shutdown() { + mLocationManager.removeTestProvider(mProviderName); + } +} diff --git a/Lab10_LocationLab/app/src/main/java/course/labs/locationlab/PlaceBadgeDetailActivity.java b/Lab10_LocationLab/app/src/main/java/course/labs/locationlab/PlaceBadgeDetailActivity.java new file mode 100644 index 0000000000000000000000000000000000000000..bf94160afcff52194eebce417e7442b16f2cadbf --- /dev/null +++ b/Lab10_LocationLab/app/src/main/java/course/labs/locationlab/PlaceBadgeDetailActivity.java @@ -0,0 +1,19 @@ +package course.labs.locationlab; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import android.widget.ImageView; +import android.widget.TextView; + +public class PlaceBadgeDetailActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // TODO - implement the Activity + //Hint: You will get the PlacePadge info from the intent + //Hint: The layout file for this activity is the place_badge detail + } +} diff --git a/Lab10_LocationLab/app/src/main/java/course/labs/locationlab/PlaceDownloaderTask.java b/Lab10_LocationLab/app/src/main/java/course/labs/locationlab/PlaceDownloaderTask.java new file mode 100644 index 0000000000000000000000000000000000000000..cc6f235a0959e686464132867477459531c86df9 --- /dev/null +++ b/Lab10_LocationLab/app/src/main/java/course/labs/locationlab/PlaceDownloaderTask.java @@ -0,0 +1,250 @@ +package course.labs.locationlab; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringReader; +import java.lang.ref.WeakReference; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Locale; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.location.Location; +import android.location.LocationManager; +import android.os.AsyncTask; +import android.util.Log; + +public class PlaceDownloaderTask extends AsyncTask<Location, Void, PlaceRecord> { + + // False if you don't have network access + private boolean mHasNetwork = false; + + // TODO - put your www.geonames.org account name here instead of "aporter". + private static String USERNAME = "aporter"; + + private HttpURLConnection mHttpUrl; + private WeakReference<PlaceViewActivity> mParent; + private static final Location sMockLoc1 = new Location( + LocationManager.NETWORK_PROVIDER); + private static final Location sMockLoc2 = new Location( + LocationManager.NETWORK_PROVIDER); + private static final Location sMockLoc3 = new Location( + LocationManager.NETWORK_PROVIDER); + private static String sMockCountryName1, sMockCountryNameInvalid, + sMockPlaceName1, sMockPlaceName2, sMockPlaceNameInvalid; + private static Bitmap sStubBitmap = null; + + public PlaceDownloaderTask(PlaceViewActivity parent, boolean hasNetwork) { + super(); + mParent = new WeakReference<PlaceViewActivity>(parent); + mHasNetwork = hasNetwork; + + if (null != parent) { + sStubBitmap = BitmapFactory.decodeResource(parent.getResources(), + R.drawable.stub); + + sMockLoc1.setLatitude(37.422); + sMockLoc1.setLongitude(-122.084); + sMockCountryName1 = parent + .getString(R.string.mock_name_united_states_string); + sMockPlaceName1 = parent.getString(R.string.the_greenhouse_string); + + sMockLoc2.setLatitude(38.996667); + sMockLoc2.setLongitude(-76.9275); + sMockPlaceName2 = parent.getString(R.string.berwyn_string); + + sMockLoc3.setLatitude(0); + sMockLoc3.setLongitude(0); + sMockCountryNameInvalid = ""; + sMockPlaceNameInvalid = ""; + } + } + + @Override + protected PlaceRecord doInBackground(Location... location) { + + PlaceRecord place = null; + + if (mHasNetwork) { + + // Get the PlaceBadge information + place = getPlaceFromURL(generateURL(USERNAME, location[0])); + place.setLocation(location[0]); + + if ("" != place.getCountryName()) { + place.setFlagBitmap(getFlagFromURL(place.getFlagUrl())); + } else { + place.setFlagBitmap(sStubBitmap); + } + } else { + + place = new PlaceRecord(); + place.setLocation(location[0]); + place.setFlagBitmap(sStubBitmap); + + if (place.intersects(sMockLoc1)) { + place.setCountryName(sMockCountryName1); + place.setPlace(sMockPlaceName1); + } else if (place.intersects(sMockLoc2)) { + place.setCountryName(sMockCountryName1); + place.setPlace(sMockPlaceName2); + } else { + place.setCountryName(sMockCountryNameInvalid); + place.setPlace(sMockPlaceNameInvalid); + } + } + + return place; + + } + + @Override + protected void onPostExecute(PlaceRecord result) { + + if (null != result && null != mParent.get()) { + mParent.get().addNewPlace(result); + } + } + + private PlaceRecord getPlaceFromURL(String... params) { + String result = null; + BufferedReader in = null; + + try { + + URL url = new URL(params[0]); + mHttpUrl = (HttpURLConnection) url.openConnection(); + in = new BufferedReader(new InputStreamReader( + mHttpUrl.getInputStream())); + + StringBuffer sb = new StringBuffer(""); + String line = ""; + while ((line = in.readLine()) != null) { + sb.append(line + "\n"); + } + result = sb.toString(); + + } catch (MalformedURLException e) { + + } catch (IOException e) { + + } finally { + try { + if (null != in) { + in.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + mHttpUrl.disconnect(); + } + + return placeDataFromXml(result); + } + + private Bitmap getFlagFromURL(String flagUrl) { + + InputStream in = null; + + Log.i("temp", flagUrl); + + try { + URL url = new URL(flagUrl); + mHttpUrl = (HttpURLConnection) url.openConnection(); + in = mHttpUrl.getInputStream(); + + return BitmapFactory.decodeStream(in); + + } catch (MalformedURLException e) { + Log.e("DEBUG", e.toString()); + } catch (IOException e) { + Log.e("DEBUG", e.toString()); + } finally { + try { + if (null != in) { + in.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + mHttpUrl.disconnect(); + } + + return BitmapFactory.decodeResource(mParent.get().getResources(), + R.drawable.stub); + } + + private static PlaceRecord placeDataFromXml(String xmlString) { + DocumentBuilder builder; + String countryName = ""; + String countryCode = ""; + String placeName = ""; + + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + + try { + builder = factory.newDocumentBuilder(); + Document document = builder.parse(new InputSource(new StringReader( + xmlString))); + NodeList list = document.getDocumentElement().getChildNodes(); + for (int i = 0; i < list.getLength(); i++) { + Node curr = list.item(i); + + NodeList list2 = curr.getChildNodes(); + + for (int j = 0; j < list2.getLength(); j++) { + + Node curr2 = list2.item(j); + if (curr2.getNodeName() != null) { + + if (curr2.getNodeName().equals("countryName")) { + countryName = curr2.getTextContent(); + } else if (curr2.getNodeName().equals("countryCode")) { + countryCode = curr2.getTextContent(); + } else if (curr2.getNodeName().equals("name")) { + placeName = curr2.getTextContent(); + } + } + } + } + } catch (DOMException e) { + e.printStackTrace(); + } catch (ParserConfigurationException e) { + e.printStackTrace(); + } catch (SAXException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + return new PlaceRecord(generateFlagURL(countryCode.toLowerCase(Locale.US)), + countryName, placeName); + } + + private static String generateURL(String username, Location location) { + + return "http://www.geonames.org/findNearbyPlaceName?username=" + + username + "&style=full&lat=" + location.getLatitude() + + "&lng=" + location.getLongitude(); + } + + private static String generateFlagURL(String countryCode) { + return "http://www.geonames.org/flags/x/" + countryCode + ".gif"; + } + +} diff --git a/Lab10_LocationLab/app/src/main/java/course/labs/locationlab/PlaceRecord.java b/Lab10_LocationLab/app/src/main/java/course/labs/locationlab/PlaceRecord.java new file mode 100644 index 0000000000000000000000000000000000000000..8d9114f8c4342ce18f67a1c09655b130501421f3 --- /dev/null +++ b/Lab10_LocationLab/app/src/main/java/course/labs/locationlab/PlaceRecord.java @@ -0,0 +1,114 @@ +package course.labs.locationlab; + +import java.util.Date; + +import android.content.Intent; +import android.graphics.Bitmap; +import android.location.Location; +import android.util.Log; + +public class PlaceRecord { + + private String mFlagUrl; + private String mCountryName; + private String mPlaceName; + private Bitmap mFlagBitmap; + private Location mLocation; + private Date mDateVisited; + + public PlaceRecord(String flagUrl, String country, String place) { + this.mFlagUrl = flagUrl; + this.mCountryName = country; + this.mPlaceName = place; + } + + public PlaceRecord(Location location) { + mLocation = location; + } + + public PlaceRecord() { + + } + + public PlaceRecord(Intent intent) { + this.mFlagUrl = intent.getStringExtra("mFlagUrl"); + this.mCountryName = intent.getStringExtra("mCountryName"); + this.mPlaceName = intent.getStringExtra("mPlaceName"); + this.mFlagBitmap = (Bitmap) intent.getParcelableExtra("mFlagBitmap"); + this.mLocation = (Location) intent.getParcelableExtra("mLocation"); + this.mDateVisited = (Date) intent.getSerializableExtra("mDateVisited"); + } + + public Intent packageToIntent() { + Intent intent = new Intent(); + intent.putExtra("mFlagUrl", mFlagUrl); + intent.putExtra("mCountryName", mCountryName); + intent.putExtra("mPlaceName", mPlaceName); + intent.putExtra("mFlagBitmap", mFlagBitmap); + intent.putExtra("mLocation", mLocation); + intent.putExtra("mDateVisited", mDateVisited); + return intent; + } + + public String getFlagUrl() { + return mFlagUrl; + } + + public void setFlagUrl(String flagUrl) { + this.mFlagUrl = flagUrl; + } + + public String getCountryName() { + return mCountryName; + } + + public void setCountryName(String country) { + this.mCountryName = country; + } + + public String getPlace() { + return mPlaceName; + } + + public void setPlace(String place) { + this.mPlaceName = place; + } + + public Bitmap getFlagBitmap() { + return mFlagBitmap; + } + + public void setFlagBitmap(Bitmap mFlagBitmap) { + this.mFlagBitmap = mFlagBitmap; + } + + public boolean intersects(Location location) { + + double tolerance = 1000; + + return (mLocation.distanceTo(location) <= tolerance); + + } + + public void setLocation(Location location) { + mLocation = location; + } + + public Location getLocation() { + return mLocation; + } + + public Date getDateVisited() { + return mDateVisited; + } + + public void setDateVisited(Date mDateVisited) { + this.mDateVisited = mDateVisited; + } + + @Override + public String toString() { + return "Place: " + mPlaceName + " Country: " + mCountryName; + + } +} diff --git a/Lab10_LocationLab/app/src/main/java/course/labs/locationlab/PlaceViewActivity.java b/Lab10_LocationLab/app/src/main/java/course/labs/locationlab/PlaceViewActivity.java new file mode 100644 index 0000000000000000000000000000000000000000..d1ca01bc366f7d678395e3185747529d20157725 --- /dev/null +++ b/Lab10_LocationLab/app/src/main/java/course/labs/locationlab/PlaceViewActivity.java @@ -0,0 +1,275 @@ +package course.labs.locationlab; + +import java.util.Date; + +import android.app.ListActivity; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.os.Build; +import android.os.Bundle; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; +import android.util.Log; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.ListView; +import android.widget.Toast; + +public class PlaceViewActivity extends ListActivity implements LocationListener { + private static final long FIVE_MINS = 5 * 60 * 1000; + + private static String TAG = "Lab-Location"; + + public static String PACKAGE_NAME = "course.labs.locationlab.placerecord.PlaceDetail"; + public static String PLACE_DETAIL = "place_detail"; + public static String INTENT_DATA = "course.labs.locationlab.placerecord.IntentData"; + + private Location mLastLocationReading; + private PlaceViewAdapter mAdapter; + + // False if you don't have network access + public static boolean sHasNetwork = true; + + // default minimum time between new readings + private long mMinTime = 5000; + + // default minimum distance between old and new readings. + private float mMinDistance = 1000.0f; + + private LocationManager mLocationManager; + + // A fake location provider used for testing + private MockLocationProvider mMockLocationProvider; + + private boolean mockLocationOn = false; + private boolean returningFromActivity = false; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Set up the app's user interface + // This class is a ListActivity, so it has its own ListView + // ListView's adapter should be a PlaceViewAdapter + + // TODO - acquire reference to the LocationManager + mLocationManager = null; + + ListView placesListView = getListView(); + + // TODO - Set an OnItemClickListener on the placeListView to open a detail view when the user + // clicks on a Place Badge. + + + // TODO - add a footerView to the ListView + // You can use footer_view.xml to define the footer + + View footerView = null; + + // Can remove once footerView is implemented + if (null == footerView) { + return; + } + + + + footerView.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View arg0) { + // TODO - When the footerView's onClick() method is called, it must + // issue the + // following log call + // Log.i(TAG,"Entered footerView.OnClickListener.onClick()"); + + // footerView must respond to user clicks. + // Must handle 3 cases: + // 1) The current location is new - download new Place Badge. Issue the + // following log call: + // Log.i(TAG,"Starting Place Download"); + + // 2) The current location has been seen before - issue Toast message. + // Issue the following log call: + // Log.i(TAG,"You already have this location badge"); + + // 3) There is no current location - response is up to you. The best + // solution is to disable the footerView until you have a location. + // Issue the following log call: + // Log.i(TAG,"Location data is not available"); + + + } + + }); + + placesListView.addFooterView(footerView); + mAdapter = new PlaceViewAdapter(getApplicationContext()); + setListAdapter(mAdapter); + } + public final static int MY_PERMISSIONS_LOCATION= 4; + + @Override + protected void onResume() { + super.onResume(); + if (Build.VERSION.SDK_INT >= 23 && + ContextCompat.checkSelfPermission(getApplicationContext(), "android.permission.ACCESS_FINE_LOCATION") != PackageManager.PERMISSION_GRANTED + ||ContextCompat.checkSelfPermission(getApplicationContext(), "android.permission.ACCESS_COARSE_LOCATION") != PackageManager.PERMISSION_GRANTED) { + + ActivityCompat.requestPermissions(PlaceViewActivity.this, + new String[]{"android.permission.ACCESS_FINE_LOCATION", + "android.permission.ACCESS_COARSE_LOCATION"}, + MY_PERMISSIONS_LOCATION); + }else + getLocationUpdates(); + + } + + private void getLocationUpdates() + { + try { + startMockLocationManager(); + + // TODO - Check NETWORK_PROVIDER and GPS_PROVIDER for an existing + // location reading. + // Only keep this last reading if it is fresh - less than 5 minutes old. + + + + // TODO - register to receive location updates from NETWORK_PROVIDER + + }catch (SecurityException e) + { + Log.d(TAG,e.getLocalizedMessage()); + } + } + @Override + protected void onPause() { + shutdownMockLocationManager(); + + // TODO - unregister for location updates + + super.onPause(); + } + + // Callback method used by PlaceDownloaderTask + public void addNewPlace(PlaceRecord place) { + + Log.i(TAG, "Entered addNewPlace()"); + if (place.getCountryName() == null || place.getCountryName().isEmpty()) { + showToast(getString(R.string.no_country_string)); + } else if (!mAdapter.intersects(place.getLocation())) { + place.setDateVisited(new Date()); + mAdapter.add(place); + mAdapter.notifyDataSetChanged(); + } else { + showToast(getString(R.string.duplicate_location_string)); + } + } + + @Override + public void onLocationChanged(Location currentLocation) { + + // TODO - Handle location updates + // Cases to consider + // 1) If there is no last location, keep the current location. + // 2) If the current location is older than the last location, ignore + // the current location + // 3) If the current location is newer than the last locations, keep the + // current location. + + + } + + @Override + public void onProviderDisabled(String provider) { + // not implemented + } + + @Override + public void onProviderEnabled(String provider) { + // not implemented + } + + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { + // not implemented + } + + private long ageInMilliseconds(Location location) { + return System.currentTimeMillis() - location.getTime(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.delete_badges: + mAdapter.removeAllViews(); + return true; + case R.id.place_one: + mMockLocationProvider.pushLocation(37.422, -122.084); + return true; + case R.id.place_no_country: + mMockLocationProvider.pushLocation(0, 0); + return true; + case R.id.place_two: + mMockLocationProvider.pushLocation(38.996667, -76.9275); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + private void showToast(final String toast) { + Toast.makeText(PlaceViewActivity.this, toast, Toast.LENGTH_LONG) + .show(); + } + + private void shutdownMockLocationManager() { + if (mockLocationOn) { + mMockLocationProvider.shutdown(); + mockLocationOn = false; + } + } + + private void startMockLocationManager() { + if (!mockLocationOn) { + mMockLocationProvider = new MockLocationProvider( + LocationManager.NETWORK_PROVIDER, this); + mockLocationOn = true; + } + } + @Override + public void onRequestPermissionsResult(int requestCode, + String permissions[], int[] grantResults) { + switch (requestCode) { + case MY_PERMISSIONS_LOCATION: + int g = 0; + Log.d(TAG,"Perm?: "+permissions.length+" -? "+grantResults.length); + for(String perm: permissions) + Log.d(TAG,"Perm: "+perm+" --> "+grantResults[g++]); + if (grantResults.length > 0 + && grantResults[0] == PackageManager.PERMISSION_GRANTED) + getLocationUpdates(); + else { + Log.i(TAG, "Permission was not granted to access location"); + finish(); + } + } + } +} \ No newline at end of file diff --git a/Lab10_LocationLab/app/src/main/java/course/labs/locationlab/PlaceViewAdapter.java b/Lab10_LocationLab/app/src/main/java/course/labs/locationlab/PlaceViewAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..c152778f1478032517c250fde77eac66ff5cff59 --- /dev/null +++ b/Lab10_LocationLab/app/src/main/java/course/labs/locationlab/PlaceViewAdapter.java @@ -0,0 +1,93 @@ +package course.labs.locationlab; + +import java.util.ArrayList; + +import android.content.Context; +import android.location.Location; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.TextView; + +public class PlaceViewAdapter extends BaseAdapter { + + private ArrayList<PlaceRecord> list = new ArrayList<PlaceRecord>(); + private static LayoutInflater inflater = null; + private Context mContext; + + public PlaceViewAdapter(Context context) { + mContext = context; + inflater = LayoutInflater.from(mContext); + } + + @Override + public int getCount() { + return list.size(); + } + + @Override + public Object getItem(int position) { + return list.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + + View newView = convertView; + ViewHolder holder; + + PlaceRecord curr = list.get(position); + + if (null == convertView) { + holder = new ViewHolder(); + newView = inflater.inflate(R.layout.place_badge_view, parent, false); + holder.flag = (ImageView) newView.findViewById(R.id.flag); + holder.country = (TextView) newView.findViewById(R.id.country_name); + holder.place = (TextView) newView.findViewById(R.id.place_name); + newView.setTag(holder); + + } else { + holder = (ViewHolder) newView.getTag(); + } + + holder.flag.setImageBitmap(curr.getFlagBitmap()); + holder.country.setText(curr.getCountryName()); + holder.place.setText(curr.getPlace()); + + return newView; + } + + static class ViewHolder { + + ImageView flag; + TextView country; + TextView place; + + } + + public boolean intersects (Location location) { + for (PlaceRecord item : list) { + if (item.intersects(location)) { + return true; + } + } + return false; + } + + public void add(PlaceRecord listItem) { + list.add(listItem); + notifyDataSetChanged(); + } + + public void removeAllViews(){ + list.clear(); + this.notifyDataSetChanged(); + } +} diff --git a/Lab10_LocationLab/app/src/main/res/drawable-hdpi/ic_launcher.png b/Lab10_LocationLab/app/src/main/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..288b66551d1efd1f13dd06f20a67534d2df57946 Binary files /dev/null and b/Lab10_LocationLab/app/src/main/res/drawable-hdpi/ic_launcher.png differ diff --git a/Lab10_LocationLab/app/src/main/res/drawable-hdpi/stub.jpg b/Lab10_LocationLab/app/src/main/res/drawable-hdpi/stub.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b82ead519ea0d11528c10797883cbf5d9e97e1ad Binary files /dev/null and b/Lab10_LocationLab/app/src/main/res/drawable-hdpi/stub.jpg differ diff --git a/Lab10_LocationLab/app/src/main/res/drawable-mdpi/ic_launcher.png b/Lab10_LocationLab/app/src/main/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..6ae570b4db4da165fada0650079061cb56aa8793 Binary files /dev/null and b/Lab10_LocationLab/app/src/main/res/drawable-mdpi/ic_launcher.png differ diff --git a/Lab10_LocationLab/app/src/main/res/drawable-xhdpi/ic_launcher.png b/Lab10_LocationLab/app/src/main/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d4fb7cd9d868f1d7d9964f1686dcbc018ef9495a Binary files /dev/null and b/Lab10_LocationLab/app/src/main/res/drawable-xhdpi/ic_launcher.png differ diff --git a/Lab10_LocationLab/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/Lab10_LocationLab/app/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..85a6081587e2c2b9793d796ee7b07e12fdf860db Binary files /dev/null and b/Lab10_LocationLab/app/src/main/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/Lab10_LocationLab/app/src/main/res/layout/footer_view.xml b/Lab10_LocationLab/app/src/main/res/layout/footer_view.xml new file mode 100644 index 0000000000000000000000000000000000000000..bf95517b9b36b2efe441430c69ed7e4d0a4d15f6 --- /dev/null +++ b/Lab10_LocationLab/app/src/main/res/layout/footer_view.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/footer" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center_horizontal" + android:text="@string/footer_text" + android:textColor="@color/black_text_87" + android:textSize="20sp" /> diff --git a/Lab10_LocationLab/app/src/main/res/layout/place_badge_detail.xml b/Lab10_LocationLab/app/src/main/res/layout/place_badge_detail.xml new file mode 100644 index 0000000000000000000000000000000000000000..b2a5237ac7770a6564c8704429059913d625c83b --- /dev/null +++ b/Lab10_LocationLab/app/src/main/res/layout/place_badge_detail.xml @@ -0,0 +1,110 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_margin="8dp" > + + <ImageView + android:id="@+id/flag" + android:layout_width="300dp" + android:layout_height="158dp" + android:padding="8dp" + android:layout_alignParentTop="true" + android:layout_centerHorizontal="true" + android:contentDescription="@string/image_descriptor_string" /> + + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@+id/flag" + android:layout_margin="8dp" > + + <TextView + android:id="@+id/place_string" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/place_name_string" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="@color/black_text_87" /> + + <TextView + android:id="@+id/place_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignBaseline="@+id/place_string" + android:layout_toRightOf="@+id/place_string" + android:ellipsize="end" + android:singleLine="true" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="@color/black_text_87" /> + + <TextView + android:id="@+id/country_string" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@+id/place_name" + android:ellipsize="end" + android:singleLine="true" + android:text="@string/country_name_string" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="@color/black_text_87" /> + + <TextView + android:id="@+id/country_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignBaseline="@+id/country_string" + android:layout_below="@+id/place_name" + android:layout_toRightOf="@+id/country_string" + android:ellipsize="end" + android:singleLine="true" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="@color/black_text_87" /> + + <TextView + android:id="@+id/date_visited_string" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@+id/country_name" + android:ellipsize="end" + android:singleLine="true" + android:text="@string/date_visited_string" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="@color/black_text_87" /> + + <TextView + android:id="@+id/date_visited" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignBaseline="@+id/date_visited_string" + android:layout_below="@+id/country_name" + android:layout_toRightOf="@+id/date_visited_string" + android:ellipsize="end" + android:singleLine="true" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="@color/black_text_87" /> + + <TextView + android:id="@+id/gps_coordinates_string" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@+id/date_visited_string" + android:ellipsize="end" + android:singleLine="true" + android:text="@string/gps_coordinates_string" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="@color/black_text_87" /> + + <TextView + android:id="@+id/gps_coordinates" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignBaseline="@+id/gps_coordinates_string" + android:layout_toRightOf="@+id/gps_coordinates_string" + android:ellipsize="end" + android:singleLine="true" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="@color/black_text_87" /> + </RelativeLayout> + +</RelativeLayout> \ No newline at end of file diff --git a/Lab10_LocationLab/app/src/main/res/layout/place_badge_view.xml b/Lab10_LocationLab/app/src/main/res/layout/place_badge_view.xml new file mode 100644 index 0000000000000000000000000000000000000000..e6958a6b23ed97955a861c0a59c46582333b0e69 --- /dev/null +++ b/Lab10_LocationLab/app/src/main/res/layout/place_badge_view.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" > + + <ImageView + android:id="@+id/flag" + android:layout_width="160dp" + android:layout_height="85dp" + android:contentDescription="@string/image_descriptor_string" + android:padding="8dp" + android:scaleType="fitXY" /> + + <RelativeLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:layout_toRightOf="@+id/flag" > + + <TextView + android:id="@+id/place_string" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/place_name_string" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="@color/black_text_87" /> + + <TextView + android:id="@+id/place_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignBaseline="@+id/place_string" + android:layout_toRightOf="@+id/place_string" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="@color/black_text_87" /> + + <TextView + android:id="@+id/country_string" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@+id/place_string" + android:text="@string/country_name_string" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="@color/black_text_87" /> + + <TextView + android:id="@+id/country_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignBaseline="@+id/country_string" + android:layout_toRightOf="@+id/country_string" + android:ellipsize="end" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="@color/black_text_87" /> + </RelativeLayout> + +</RelativeLayout> \ No newline at end of file diff --git a/Lab10_LocationLab/app/src/main/res/menu/main.xml b/Lab10_LocationLab/app/src/main/res/menu/main.xml new file mode 100644 index 0000000000000000000000000000000000000000..4451357749531faad6707bc2cbafa9f827e23a88 --- /dev/null +++ b/Lab10_LocationLab/app/src/main/res/menu/main.xml @@ -0,0 +1,18 @@ +<menu xmlns:android="http://schemas.android.com/apk/res/android" > + + <item + android:id="@+id/delete_badges" + android:title="@string/delete"/> + + <item + android:id="@+id/place_one" + android:title="@string/place_one"/> + + <item + android:id="@+id/place_no_country" + android:title="@string/place_no_country"/> + + <item + android:id="@+id/place_two" + android:title="@string/place_two"/> +</menu> diff --git a/Lab10_LocationLab/app/src/main/res/values-sw600dp/dimens.xml b/Lab10_LocationLab/app/src/main/res/values-sw600dp/dimens.xml new file mode 100644 index 0000000000000000000000000000000000000000..44f01db75f0fef18081132a9e86517f8d5efa8f6 --- /dev/null +++ b/Lab10_LocationLab/app/src/main/res/values-sw600dp/dimens.xml @@ -0,0 +1,8 @@ +<resources> + + <!-- + Customize dimensions originally defined in res/values/dimens.xml (such as + screen margins) for sw600dp devices (e.g. 7" tablets) here. + --> + +</resources> diff --git a/Lab10_LocationLab/app/src/main/res/values-sw720dp-land/dimens.xml b/Lab10_LocationLab/app/src/main/res/values-sw720dp-land/dimens.xml new file mode 100644 index 0000000000000000000000000000000000000000..61e3fa8fbca01c469f05fb488bdb8f5ffdb9da3c --- /dev/null +++ b/Lab10_LocationLab/app/src/main/res/values-sw720dp-land/dimens.xml @@ -0,0 +1,9 @@ +<resources> + + <!-- + Customize dimensions originally defined in res/values/dimens.xml (such as + screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here. + --> + <dimen name="activity_horizontal_margin">128dp</dimen> + +</resources> diff --git a/Lab10_LocationLab/app/src/main/res/values-v11/styles.xml b/Lab10_LocationLab/app/src/main/res/values-v11/styles.xml new file mode 100644 index 0000000000000000000000000000000000000000..3c02242ad044be9b8c7c09e7c90c5d427763fe97 --- /dev/null +++ b/Lab10_LocationLab/app/src/main/res/values-v11/styles.xml @@ -0,0 +1,11 @@ +<resources> + + <!-- + Base application theme for API 11+. This theme completely replaces + AppBaseTheme from res/values/styles.xml on API 11+ devices. + --> + <style name="AppBaseTheme" parent="android:Theme.Holo.Light"> + <!-- API 11 theme customizations can go here. --> + </style> + +</resources> diff --git a/Lab10_LocationLab/app/src/main/res/values-v14/styles.xml b/Lab10_LocationLab/app/src/main/res/values-v14/styles.xml new file mode 100644 index 0000000000000000000000000000000000000000..a91fd0372b20f85e284fc2fa2ce949176dfdf6e5 --- /dev/null +++ b/Lab10_LocationLab/app/src/main/res/values-v14/styles.xml @@ -0,0 +1,12 @@ +<resources> + + <!-- + Base application theme for API 14+. This theme completely replaces + AppBaseTheme from BOTH res/values/styles.xml and + res/values-v11/styles.xml on API 14+ devices. + --> + <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar"> + <!-- API 14 theme customizations can go here. --> + </style> + +</resources> diff --git a/Lab10_LocationLab/app/src/main/res/values/color.xml b/Lab10_LocationLab/app/src/main/res/values/color.xml new file mode 100644 index 0000000000000000000000000000000000000000..e4aaf650d571c86360e616b4872c7c071f19ba5d --- /dev/null +++ b/Lab10_LocationLab/app/src/main/res/values/color.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <color name="black_text_87">#DE000000</color> +</resources> diff --git a/Lab10_LocationLab/app/src/main/res/values/dimens.xml b/Lab10_LocationLab/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000000000000000000000000000000000000..55c1e5908c7e0f157fe815acd6d5cd7358463390 --- /dev/null +++ b/Lab10_LocationLab/app/src/main/res/values/dimens.xml @@ -0,0 +1,7 @@ +<resources> + + <!-- Default screen margins, per the Android Design guidelines. --> + <dimen name="activity_horizontal_margin">16dp</dimen> + <dimen name="activity_vertical_margin">16dp</dimen> + +</resources> diff --git a/Lab10_LocationLab/app/src/main/res/values/strings.xml b/Lab10_LocationLab/app/src/main/res/values/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..52e02ba5ce742b32611b750c73820b878a0f8bd4 --- /dev/null +++ b/Lab10_LocationLab/app/src/main/res/values/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="app_name">LocationLab</string> + <string name="footer_text">Get New Place</string> + <string name="delete">Delete</string> + <string name="badge_detail">Badge Details</string> + <string name="place_name_string">Place: </string> + <string name="country_name_string">Country: </string> + <string name="date_visited_string">Date Visited: </string> + <string name="gps_coordinates_string">Location: </string> + <string name="image_descriptor_string">Flag of visited country</string> + <string name="place_one">Place One</string> + <string name="place_no_country">Place No Country</string> + <string name="place_two">Place Two</string> + <string name="no_country_string">There is no country at this location</string> + <string name="mock_name_united_states_string">United States</string> + <string name="the_greenhouse_string">The Greenhouse</string> + <string name="berwyn_string">Berwyn</string> + <string name="duplicate_location_string">You already have this location badge</string> + +</resources> \ No newline at end of file diff --git a/Lab10_LocationLab/app/src/main/res/values/styles.xml b/Lab10_LocationLab/app/src/main/res/values/styles.xml new file mode 100644 index 0000000000000000000000000000000000000000..5cf0386a144ff6248609832f751aa29c590c6454 --- /dev/null +++ b/Lab10_LocationLab/app/src/main/res/values/styles.xml @@ -0,0 +1,21 @@ +<resources> + + <!-- + Base application theme, dependent on API level. This theme is replaced + by AppBaseTheme from res/values-vXX/styles.xml on newer devices. + --> + <style name="AppBaseTheme" parent="android:Theme.Light"> + <!-- + Theme customizations available in newer API levels can go in + res/values-vXX/styles.xml, while customizations related to + backward-compatibility can go here. + --> + </style> + + <!-- Application theme. --> + <style name="AppTheme" parent="AppBaseTheme"> + <!-- All customizations that are NOT specific to a particular API-level can go here. --> + <item name="android:textColor">@color/black_text_87</item> + </style> + +</resources> diff --git a/Lab10_LocationLab/build.gradle b/Lab10_LocationLab/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..d287fe6fa142cbd023a024b5649b4ab9f0d1e688 --- /dev/null +++ b/Lab10_LocationLab/build.gradle @@ -0,0 +1,18 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.3.3' + } +} + +allprojects { + repositories { + jcenter() + maven { + url "https://maven.google.com" + } + } +} diff --git a/Lab10_LocationLab/gradle/wrapper/gradle-wrapper.jar b/Lab10_LocationLab/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..13372aef5e24af05341d49695ee84e5f9b594659 Binary files /dev/null and b/Lab10_LocationLab/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Lab10_LocationLab/gradle/wrapper/gradle-wrapper.properties b/Lab10_LocationLab/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000000000000000000000000000000000..1e1b6b6189b2b3b4c2ead065c617a2acc978cb98 --- /dev/null +++ b/Lab10_LocationLab/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sun Nov 12 18:15:56 EST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip diff --git a/Lab10_LocationLab/gradlew b/Lab10_LocationLab/gradlew new file mode 100755 index 0000000000000000000000000000000000000000..9d82f78915133e1c35a6ea51252590fb38efac2f --- /dev/null +++ b/Lab10_LocationLab/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/Lab10_LocationLab/gradlew.bat b/Lab10_LocationLab/gradlew.bat new file mode 100644 index 0000000000000000000000000000000000000000..8a0b282aa6885fb573c106b3551f7275c5f17e8e --- /dev/null +++ b/Lab10_LocationLab/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Lab10_LocationLab/settings.gradle b/Lab10_LocationLab/settings.gradle new file mode 100644 index 0000000000000000000000000000000000000000..e7b4def49cb53d9aa04228dd3edb14c9e635e003 --- /dev/null +++ b/Lab10_LocationLab/settings.gradle @@ -0,0 +1 @@ +include ':app'