MP1_CalebFontenot

This commit is contained in:
2024-01-23 18:03:09 -06:00
parent 4587fe1846
commit 75ab169397
2010 changed files with 48871 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
package com.bignerdranch.android.locatr;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumentation test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.bignerdranch.android.locatr", appContext.getPackageName());
}
}

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bignerdranch.android.locatr">
<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" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".LocatrActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,135 @@
package com.bignerdranch.android.locatr;
import android.location.Location;
import android.net.Uri;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
public class FlickrFetchr {
private static final String TAG = "FlickrFetchr";
private static final String API_KEY = "REPLACE_ME_WITH_A_REAL_KEY";
private static final String FETCH_RECENTS_METHOD = "flickr.photos.getRecent";
private static final String SEARCH_METHOD = "flickr.photos.search";
private static final Uri ENDPOINT = Uri
.parse("https://api.flickr.com/services/rest/")
.buildUpon()
.appendQueryParameter("api_key", API_KEY)
.appendQueryParameter("format", "json")
.appendQueryParameter("nojsoncallback", "1")
.appendQueryParameter("extras", "url_s")
.build();
public byte[] getUrlBytes(String urlSpec) throws IOException {
URL url = new URL(urlSpec);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
InputStream in = connection.getInputStream();
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
throw new IOException(connection.getResponseMessage() +
": with " +
urlSpec);
}
int bytesRead = 0;
byte[] buffer = new byte[1024];
while ((bytesRead = in.read(buffer)) > 0) {
out.write(buffer, 0, bytesRead);
}
out.close();
return out.toByteArray();
} finally {
connection.disconnect();
}
}
public String getUrlString(String urlSpec) throws IOException {
return new String(getUrlBytes(urlSpec));
}
public List<GalleryItem> fetchRecentPhotos() {
String url = buildUrl(FETCH_RECENTS_METHOD, null);
return downloadGalleryItems(url);
}
public List<GalleryItem> searchPhotos(String query) {
String url = buildUrl(SEARCH_METHOD, query);
return downloadGalleryItems(url);
}
public List<GalleryItem> searchPhotos(Location location) {
String url = buildUrl(location);
return downloadGalleryItems(url);
}
private List<GalleryItem> downloadGalleryItems(String url) {
List<GalleryItem> items = new ArrayList<>();
try {
String jsonString = getUrlString(url);
Log.i(TAG, "Received JSON: " + jsonString);
JSONObject jsonBody = new JSONObject(jsonString);
parseItems(items, jsonBody);
} catch (IOException ioe) {
Log.e(TAG, "Failed to fetch items", ioe);
} catch (JSONException je) {
Log.e(TAG, "Failed to parse JSON", je);
}
return items;
}
private String buildUrl(String method, String query) {
Uri.Builder uriBuilder = ENDPOINT.buildUpon()
.appendQueryParameter("method", method);
if (method.equals(SEARCH_METHOD)) {
uriBuilder.appendQueryParameter("text", query);
}
return uriBuilder.build().toString();
}
private String buildUrl(Location location) {
return ENDPOINT.buildUpon()
.appendQueryParameter("method", SEARCH_METHOD)
.appendQueryParameter("lat", "" + location.getLatitude())
.appendQueryParameter("lon", "" + location.getLongitude())
.build().toString();
}
private void parseItems(List<GalleryItem> items, JSONObject jsonBody)
throws IOException, JSONException {
JSONObject photosJsonObject = jsonBody.getJSONObject("photos");
JSONArray photoJsonArray = photosJsonObject.getJSONArray("photo");
for (int i = 0; i < photoJsonArray.length(); i++) {
JSONObject photoJsonObject = photoJsonArray.getJSONObject(i);
GalleryItem item = new GalleryItem();
item.setId(photoJsonObject.getString("id"));
item.setCaption(photoJsonObject.getString("title"));
if (!photoJsonObject.has("url_s")) {
continue;
}
item.setUrl(photoJsonObject.getString("url_s"));
item.setOwner(photoJsonObject.getString("owner"));
items.add(item);
}
}
}

View File

@@ -0,0 +1,55 @@
package com.bignerdranch.android.locatr;
import android.net.Uri;
public class GalleryItem {
private String mCaption;
private String mId;
private String mUrl;
private String mOwner;
public String getCaption() {
return mCaption;
}
public void setCaption(String caption) {
mCaption = caption;
}
public String getId() {
return mId;
}
public void setId(String id) {
mId = id;
}
public String getUrl() {
return mUrl;
}
public void setUrl(String url) {
mUrl = url;
}
public String getOwner() {
return mOwner;
}
public void setOwner(String owner) {
mOwner = owner;
}
public Uri getPhotoPageUri() {
return Uri.parse("http://www.flickr.com/photos/")
.buildUpon()
.appendPath(mOwner)
.appendPath(mId)
.build();
}
@Override
public String toString() {
return mCaption;
}
}

View File

@@ -0,0 +1,42 @@
package com.bignerdranch.android.locatr;
import android.app.Dialog;
import android.content.DialogInterface;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
public class LocatrActivity extends SingleFragmentActivity {
private static final int REQUEST_ERROR = 0;
@Override
protected Fragment createFragment() {
return LocatrFragment.newInstance();
}
@Override
protected void onResume() {
super.onResume();
GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance();
int errorCode = apiAvailability.isGooglePlayServicesAvailable(this);
if (errorCode != ConnectionResult.SUCCESS) {
Dialog errorDialog = apiAvailability.getErrorDialog(this,
errorCode,
REQUEST_ERROR,
new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialogInterface) {
// Leave if services are unavailable.
finish();
}
});
errorDialog.show();
}
}
}

View File

@@ -0,0 +1,178 @@
package com.bignerdranch.android.locatr;
import android.Manifest;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.location.Location;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import java.io.IOException;
import java.util.List;
public class LocatrFragment extends Fragment {
private static final String TAG = "LocatrFragment";
private static final String[] LOCATION_PERMISSIONS = new String[]{
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
};
private static final int REQUEST_LOCATION_PERMISSIONS = 0;
private ImageView mImageView;
private GoogleApiClient mClient;
public static LocatrFragment newInstance() {
return new LocatrFragment();
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
mClient = new GoogleApiClient.Builder(getActivity()).addApi(LocationServices.API)
.addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
@Override
public void onConnected(@Nullable Bundle bundle) {
getActivity().invalidateOptionsMenu();
}
@Override
public void onConnectionSuspended(int i) {
}
})
.build();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_locatr, container, false);
mImageView = (ImageView) v.findViewById(R.id.image);
return v;
}
@Override
public void onStart() {
super.onStart();
getActivity().invalidateOptionsMenu();
mClient.connect();
}
@Override
public void onStop() {
super.onStop();
mClient.disconnect();
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.fragment_locatr, menu);
MenuItem searchItem = menu.findItem(R.id.action_locate);
searchItem.setEnabled(mClient.isConnected());
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_locate:
if (hasLocationPermission()) {
findImage();
} else {
requestPermissions(LOCATION_PERMISSIONS,
REQUEST_LOCATION_PERMISSIONS);
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
switch (requestCode) {
case REQUEST_LOCATION_PERMISSIONS:
if (hasLocationPermission()) {
findImage();
}
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
private void findImage() {
LocationRequest request = LocationRequest.create();
request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
request.setNumUpdates(1);
request.setInterval(0);
LocationServices.FusedLocationApi
.requestLocationUpdates(mClient, request, new LocationListener() {
@Override
public void onLocationChanged(Location location) {
Log.i(TAG, "Got a fix: " + location);
new SearchTask().execute(location);
}
});
}
private boolean hasLocationPermission() {
int result = ContextCompat
.checkSelfPermission(getActivity(), LOCATION_PERMISSIONS[0]);
return result == PackageManager.PERMISSION_GRANTED;
}
private class SearchTask extends AsyncTask<Location,Void,Void> {
private GalleryItem mGalleryItem;
private Bitmap mBitmap;
@Override
protected Void doInBackground(Location... params) {
FlickrFetchr fetchr = new FlickrFetchr();
List<GalleryItem> items = fetchr.searchPhotos(params[0]);
if (items.size() == 0) {
return null;
}
mGalleryItem = items.get(0);
try {
byte[] bytes = fetchr.getUrlBytes(mGalleryItem.getUrl());
mBitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
} catch (IOException ioe) {
Log.i(TAG, "Unable to decode bitmap", ioe);
}
return null;
}
@Override
protected void onPostExecute(Void result) {
mImageView.setImageBitmap(mBitmap);
}
}
}

View File

@@ -0,0 +1,28 @@
package com.bignerdranch.android.locatr;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;
public abstract class SingleFragmentActivity extends AppCompatActivity {
protected abstract Fragment createFragment();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment);
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragment_container);
if (fragment == null) {
fragment = createFragment();
fm.beginTransaction()
.add(R.id.fragment_container, fragment)
.commit();
}
}
}

View File

@@ -0,0 +1,5 @@
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_locatr"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.bignerdranch.android.locatr.LocatrActivity">
<ImageView
android:id="@+id/image"
android:layout_width="150dp"
android:layout_height="150dp"
android:background="@android:color/white"
android:layout_centerInParent="true"
/>
</RelativeLayout>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/action_locate"
android:icon="@android:drawable/ic_menu_compass"
android:title="@string/search"
android:enabled="false"
app:showAsAction="ifRoom"/>
</menu>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -0,0 +1,6 @@
<resources>
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
(such as screen margins) for screens with more than 820dp of available width. This
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
<dimen name="activity_horizontal_margin">64dp</dimen>
</resources>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>

View File

@@ -0,0 +1,5 @@
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>

View File

@@ -0,0 +1,5 @@
<resources>
<string name="app_name">Locatr</string>
<string name="search">Find an image near you</string>
</resources>

View File

@@ -0,0 +1,11 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>

View File

@@ -0,0 +1,17 @@
package com.bignerdranch.android.locatr;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}