diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..afbdab3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..25ce038 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +GridImageSearch \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..217af47 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..e7bedf3 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..e206d70 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..fe865d3 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + + diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..609cf44 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,11 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..3b31283 --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..59436c9 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..6da9557 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml new file mode 100644 index 0000000..922003b --- /dev/null +++ b/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..def6a6a --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/GridImageSearch.iml b/GridImageSearch.iml new file mode 100644 index 0000000..2a02201 --- /dev/null +++ b/GridImageSearch.iml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/app.iml b/app/app.iml new file mode 100644 index 0000000..2c31785 --- /dev/null +++ b/app/app.iml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..f3c613f --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,31 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 21 + buildToolsVersion "21.1.2" + + defaultConfig { + applicationId "com.example.vikramjeet.gridimagesearch" + minSdkVersion 16 + targetSdkVersion 21 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile 'com.android.support:appcompat-v7:21.0.3' + // Sends network requests for JSON + compile 'com.loopj.android:android-async-http:1.4.5' + // Load remote images from the network into image views + compile 'com.squareup.picasso:picasso:2.4.0' + // For SwipeToRefresh View + compile 'com.android.support:support-v4:21.0.3' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..d663060 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/Vikramjeet/Library/Android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/app/src/androidTest/java/com/example/vikramjeet/gridimagesearch/ApplicationTest.java b/app/src/androidTest/java/com/example/vikramjeet/gridimagesearch/ApplicationTest.java new file mode 100644 index 0000000..4324c49 --- /dev/null +++ b/app/src/androidTest/java/com/example/vikramjeet/gridimagesearch/ApplicationTest.java @@ -0,0 +1,13 @@ +package com.example.vikramjeet.gridimagesearch; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..fe328c8 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/example/vikramjeet/activities/ImageDetailActivity.java b/app/src/main/java/com/example/vikramjeet/activities/ImageDetailActivity.java new file mode 100644 index 0000000..080630a --- /dev/null +++ b/app/src/main/java/com/example/vikramjeet/activities/ImageDetailActivity.java @@ -0,0 +1,71 @@ +package com.example.vikramjeet.activities; + +import android.os.Bundle; +import android.support.v7.app.ActionBarActivity; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.ImageView; + +import com.example.vikramjeet.gridimagesearch.R; +import com.example.vikramjeet.models.Image; +import com.squareup.picasso.Picasso; + +public class ImageDetailActivity extends ActionBarActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_image_detail); + // Pull image out of the intent + Image image = (Image) getIntent().getSerializableExtra("image"); + // Get image Url + String imageUrl = image.getUrl(); + // Find imageView + ImageView ivImageDetail = (ImageView) findViewById(R.id.ivImageDetail); + // Load the image into image view using picasso +// Picasso.with(this) +// .load(imageUrl) +// .resize(200, 200) +// .centerCrop() +// .placeholder(R.drawable.image_placeholder) +// .error(R.drawable.image_error) +// .into(ivImageDetail); + + Picasso.with(this) + .load(imageUrl) + .placeholder(R.drawable.image_placeholder) + .error(R.drawable.image_error) + .resize(Integer.parseInt(image.getWidth()), Integer.parseInt(image.getHeight())) + .into(ivImageDetail); + + + } + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_image_detail, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + return true; + } + +// if (id == R.id.home) { +// onBackPressed(); +// return true; +// } + + return super.onOptionsItemSelected(item); + } +} diff --git a/app/src/main/java/com/example/vikramjeet/activities/ImageSearchActivity.java b/app/src/main/java/com/example/vikramjeet/activities/ImageSearchActivity.java new file mode 100644 index 0000000..66c5f1f --- /dev/null +++ b/app/src/main/java/com/example/vikramjeet/activities/ImageSearchActivity.java @@ -0,0 +1,272 @@ +package com.example.vikramjeet.activities; + +import android.content.Context; +import android.content.Intent; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.Bundle; +import android.support.v4.view.MenuItemCompat; +import android.support.v7.app.ActionBarActivity; +import android.support.v7.widget.SearchView; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; +import android.widget.EditText; +import android.widget.GridView; + +import com.example.vikramjeet.adapters.ImageAdapter; +import com.example.vikramjeet.gridimagesearch.R; +import com.example.vikramjeet.helpers.EndlessScrollListener; +import com.example.vikramjeet.models.Filter; +import com.example.vikramjeet.models.Image; +import com.loopj.android.http.AsyncHttpClient; +import com.loopj.android.http.JsonHttpResponseHandler; + +import org.apache.http.Header; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; + + +public class ImageSearchActivity extends ActionBarActivity { + private final int REQUEST_CODE = 200; + public static final String url = "https://ajax.googleapis.com/ajax/services/search/images?v=1.0&rsz=8&q="; + private EditText etQuery; + private GridView gvResults; + private ArrayList images; + private ImageAdapter adapter; + private String searchURL; + private AsyncHttpClient httpClient; + private SearchView searchView; + private Filter searchFilter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_image_search); + + // Set up views + setupViews(); + // Create Images ArrayList + images = new ArrayList(); + // Attach datasource to adapter + adapter = new ImageAdapter(this, images); + // Link adapter to gridview + gvResults.setAdapter(adapter); + + // Attach the listener to the AdapterView onCreate + gvResults.setOnScrollListener(new EndlessScrollListener() { + @Override + public void onLoadMore(int page, int totalItemsCount) { + // Triggered only when new data needs to be appended to the list + // Add whatever code is needed to append new items to your AdapterView + customLoadMoreDataFromApi(page); + // or customLoadMoreDataFromApi(totalItemsCount); + } + }); + } + + // Append more data into the adapter + public void customLoadMoreDataFromApi(int page) { + // This method probably sends out a network request and appends new data items to your adapter. + // Use the offset value and add it as a parameter to your API request to retrieve paginated data. + // Deserialize API response and then construct new objects to append to the adapter + + if (isNetworkAvailable()) { + fetchImagesWhileScrolling(searchURL, page); + } + } + + + private void setupViews() { + gvResults = (GridView) findViewById(R.id.gvResults); + // Set up onClick handler + gvResults.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + // Create an Intent + Intent imageIntent = new Intent(ImageSearchActivity.this, ImageDetailActivity.class); + // Get the image + Image image = images.get(position); + // Pass image result into the Intent + imageIntent.putExtra("image", image); + // Start the activity + startActivity(imageIntent); + } + }); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode == RESULT_OK && requestCode == REQUEST_CODE) { + // Extract name value from result extras + searchFilter = (Filter) data.getSerializableExtra("Filter"); +// // Toast the name to display temporarily on screen +// Toast.makeText(this, filter.getImageSize(), Toast.LENGTH_SHORT).show(); + } + } + + public String applyFilters(String url, String query) { + url = url + query; + if (searchFilter != null) { + if (searchFilter.getImageType() != null) { + url = url + "&imgtype=" + searchFilter.getImageType(); + } + + if (searchFilter.getImageColor() != null) { + url = url + "&imgcolor=" + searchFilter.getImageColor(); + } + + if (searchFilter.getImageSize() != null) { + url = url + "&imgsz=" + searchFilter.getImageSize(); + } + + if (!searchFilter.getSiteFilter().equals("")) { + url = url + "&as_sitesearch=" + searchFilter.getSiteFilter(); + } + } + +// Toast.makeText(ImageSearchActivity.this, url, Toast.LENGTH_SHORT).show(); + return url; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_image_search, menu); + + MenuItem searchItem = menu.findItem(R.id.action_search); + searchView = (SearchView) MenuItemCompat.getActionView(searchItem); + searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String query) { + // Create Network client + httpClient = new AsyncHttpClient(); + + // Create search url +// searchURL = url + query; + searchURL = applyFilters(url, query); + + + if(isNetworkAvailable()) { + // Fetch images + fetchImages(searchURL); + } + + + return true; + } + + @Override + public boolean onQueryTextChange(String newText) { + return false; + } + }); + + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + switch (id) { + //noinspection SimplifiableIfStatement + case R.id.action_settings: + return true; + case R.id.miFilters: + showSettings(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + // Helper methods + + private void showSettings() { + // Create an Intent + Intent settingsIntent = new Intent(ImageSearchActivity.this, SettingsActivity.class); +// settingsIntent.putExtra("Filter", searchFilter); + // Start the activity + startActivityForResult(settingsIntent, REQUEST_CODE); + } + + public void fetchImages(String url) { + // Make request + httpClient.get(url, new JsonHttpResponseHandler() { + // successful response + @Override + public void onSuccess(int statusCode, Header[] headers, JSONObject response) { + Log.i("Response", response.toString()); + JSONArray imageListJSON = null; + try { + // Getting imageList json + imageListJSON = response.getJSONObject("responseData").getJSONArray("results"); + // Cleat list for the initial search (not for pagination) + images.clear(); +// Add images to the list +// images.addAll(Image.fromJSONArray(imageListJSON)); +//// Refresh data by notifying adapter +// adapter.notifyDataSetChanged(); + + // Using another approach of updating adapter itself and hence it will trigger the change to datasource + adapter.addAll(Image.fromJSONArray(imageListJSON)); + + } catch (JSONException e) { +// Log.e("ImageSearchActivity", "Failed to parse response json"); + e.printStackTrace(); + } + } + + // failed response + @Override + public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) { + super.onFailure(statusCode, headers, responseString, throwable); + } + }); + } + + public void fetchImagesWhileScrolling(String url, int page) { + int offset = 8*(page-1); + String finalURL = url + "&start=" + offset; + + httpClient.get(finalURL, new JsonHttpResponseHandler() { + + public void onSuccess(int statusCode, Header[] headers, JSONObject response) { + Log.i("Response", response.toString()); + JSONArray imageListJSON = null; + try { + // Getting imageList json + imageListJSON = response.getJSONObject("responseData").getJSONArray("results"); + // Using another approach of updating adapter itself and hence it will trigger the change to datasource + adapter.addAll(Image.fromJSONArray(imageListJSON)); + + } catch (JSONException e) { + Log.e("ImageSearchActivity", "Failed to parse response json"); + e.printStackTrace(); + } + } + + // failed response + @Override + public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) { + super.onFailure(statusCode, headers, responseString, throwable); + } + }); + } + + private Boolean isNetworkAvailable() { + ConnectivityManager connectivityManager + = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); + return activeNetworkInfo != null && activeNetworkInfo.isConnectedOrConnecting(); + } +} diff --git a/app/src/main/java/com/example/vikramjeet/activities/SettingsActivity.java b/app/src/main/java/com/example/vikramjeet/activities/SettingsActivity.java new file mode 100644 index 0000000..985c243 --- /dev/null +++ b/app/src/main/java/com/example/vikramjeet/activities/SettingsActivity.java @@ -0,0 +1,145 @@ +package com.example.vikramjeet.activities; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.ActionBarActivity; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.EditText; +import android.widget.Spinner; + +import com.example.vikramjeet.gridimagesearch.R; +import com.example.vikramjeet.models.Filter; + +public class SettingsActivity extends ActionBarActivity { + + private String imageSize; + private String imageColor; + private String imageType; + private EditText etSiteFilter; + private Button saveButton; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_settings); + + etSiteFilter = (EditText) findViewById(R.id.etSiteFilter); + saveButton = (Button) findViewById(R.id.btnSave); + + + + // Load spinner view + Spinner spinnerImageSize = (Spinner) findViewById(R.id.spinnerImageSize); + // Create an ArrayAdapter using the string array using custom spinner layout + ArrayAdapter adapter = ArrayAdapter.createFromResource(this, + R.array.image_size_array, R.layout.custom_spinner_item); + // Specify the layout to use when the list of choices appears + adapter.setDropDownViewResource(R.layout.custom_spinner_item); + // Apply the adapter to the spinner + spinnerImageSize.setAdapter(adapter); + + spinnerImageSize.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + if (position > 0) { + imageSize = (String) parent.getItemAtPosition(position); + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + + } + }); + + + // Load spinner view + Spinner spinnerColorFilter = (Spinner) findViewById(R.id.spinnerColorFilter); + // Create an ArrayAdapter using the string array using custom spinner layout + ArrayAdapter adapter2 = ArrayAdapter.createFromResource(this, + R.array.image_color_array, R.layout.custom_spinner_item); + // Specify the layout to use when the list of choices appears + adapter.setDropDownViewResource(R.layout.custom_spinner_item); + // Apply the adapter to the spinner + spinnerColorFilter.setAdapter(adapter2); + + spinnerColorFilter.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + if (position > 0) { + imageColor = (String) parent.getItemAtPosition(position); + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + + } + }); + + // Load spinner view + Spinner spinnerImageType = (Spinner) findViewById(R.id.spinnerImageType); + // Create an ArrayAdapter using the string array using custom spinner layout + ArrayAdapter adapter3 = ArrayAdapter.createFromResource(this, + R.array.image_type_array, R.layout.custom_spinner_item); + // Specify the layout to use when the list of choices appears + adapter.setDropDownViewResource(R.layout.custom_spinner_item); + // Apply the adapter to the spinner + spinnerImageType.setAdapter(adapter3); + + spinnerImageSize.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + if (position > 0) { + imageType = (String) parent.getItemAtPosition(position); + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + + } + }); + } + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_settings, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + return true; + } + + return super.onOptionsItemSelected(item); + } + + public void onClickSaveButton(View v) { + String siteFilter = etSiteFilter.getText().toString(); + // Create Filter object + Filter filter = new Filter(imageSize, imageColor, imageType, siteFilter); + // Prepare data Intent + Intent dataIntent = new Intent(); + // Put Filter object in Intent + dataIntent.putExtra("Filter", filter); + // Set and Give the result back + setResult(RESULT_OK, dataIntent); + finish(); + } +} diff --git a/app/src/main/java/com/example/vikramjeet/adapters/ImageAdapter.java b/app/src/main/java/com/example/vikramjeet/adapters/ImageAdapter.java new file mode 100644 index 0000000..048601e --- /dev/null +++ b/app/src/main/java/com/example/vikramjeet/adapters/ImageAdapter.java @@ -0,0 +1,67 @@ +package com.example.vikramjeet.adapters; + +import android.content.Context; +import android.text.Html; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.TextView; + +import com.example.vikramjeet.gridimagesearch.R; +import com.example.vikramjeet.models.Image; +import com.squareup.picasso.Picasso; + +import java.util.List; + +/** + * Created by Vikramjeet on 2/10/15. + */ +public class ImageAdapter extends ArrayAdapter { + + private static class ViewHolder { + ImageView ivImage; + TextView tvImageTitle; + } + + public ImageAdapter(Context context, List images) { + super(context, R.layout.item_image, images); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + // Get the image object + Image image = getItem(position); + + //View look up cache stored in tag; + ViewHolder viewHolder = null; + if (convertView == null) { //If recycled view is not available + viewHolder = new ViewHolder(); + // create new convert view + convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_image, parent, false); + // Lookup the views + viewHolder.ivImage = (ImageView) convertView.findViewById(R.id.ivImage); + viewHolder.tvImageTitle = (TextView) convertView.findViewById(R.id.tvImageTitle); + convertView.setTag(viewHolder); + } else { + viewHolder = (ViewHolder) convertView.getTag(); + } + + // Clear out the user photo imageview (in case previous image is still in there) + viewHolder.ivImage.setImageResource(0); + + // Insert the image using Picasso (asynchronous) + Picasso.with(getContext()) + .load(image.getThumbnailUrl()) + .placeholder(R.drawable.image_placeholder) + .error(R.drawable.image_error) + .fit().centerCrop() + .into(viewHolder.ivImage); + + // Set image title + viewHolder.tvImageTitle.setText(Html.fromHtml(image.getTitle())); + + return convertView; + } +} diff --git a/app/src/main/java/com/example/vikramjeet/helpers/EndlessScrollListener.java b/app/src/main/java/com/example/vikramjeet/helpers/EndlessScrollListener.java new file mode 100644 index 0000000..3ad6992 --- /dev/null +++ b/app/src/main/java/com/example/vikramjeet/helpers/EndlessScrollListener.java @@ -0,0 +1,75 @@ +package com.example.vikramjeet.helpers; + +import android.widget.AbsListView; + +/** + * Created by Vikramjeet on 2/12/15. + */ +public abstract class EndlessScrollListener implements AbsListView.OnScrollListener { + + // The minimum amount of items to have below your current scroll position + // before loading more. + private int visibleThreshold = 5; + // The current offset index of data you have loaded + private int currentPage = 0; + // The total number of items in the dataset after the last load + private int previousTotalItemCount = 0; + // True if we are still waiting for the last set of data to load. + private boolean loading = true; + // Sets the starting page index + private int startingPageIndex = 0; + + public EndlessScrollListener() { + } + + public EndlessScrollListener(int visibleThreshold) { + this.visibleThreshold = visibleThreshold; + } + + public EndlessScrollListener(int visibleThreshold, int startPage) { + this.visibleThreshold = visibleThreshold; + this.startingPageIndex = startPage; + this.currentPage = startPage; + } + + // This happens many times a second during a scroll, so be wary of the code you place here. + // We are given a few useful parameters to help us work out if we need to load some more data, + // but first we check if we are waiting for the previous load to finish. + @Override + public void onScroll(AbsListView view,int firstVisibleItem,int visibleItemCount,int totalItemCount) + { + // If the total item count is zero and the previous isn't, assume the + // list is invalidated and should be reset back to initial state + if (totalItemCount < previousTotalItemCount) { + this.currentPage = this.startingPageIndex; + this.previousTotalItemCount = 0; + if (totalItemCount == 0) { this.loading = true; } + } + // If it’s still loading, we check to see if the dataset count has + // changed, if so we conclude it has finished loading and update the current page + // number and total item count. + if (loading && (totalItemCount > previousTotalItemCount)) { + loading = false; + previousTotalItemCount = totalItemCount; + currentPage++; + } + + // If it isn’t currently loading, we check to see if we have breached + // the visibleThreshold and need to reload more data. + // If we do need to reload some more data, we execute onLoadMore to fetch the data. + if (!loading && (totalItemCount - visibleItemCount)<=(firstVisibleItem + visibleThreshold)) { + onLoadMore(currentPage + 1, totalItemCount); + loading = true; + } + } + + // Defines the process for actually loading more data based on page + public abstract void onLoadMore(int page, int totalItemsCount); + + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + // Don't take any action on changed + } + + +} diff --git a/app/src/main/java/com/example/vikramjeet/models/Filter.java b/app/src/main/java/com/example/vikramjeet/models/Filter.java new file mode 100644 index 0000000..4d01a6b --- /dev/null +++ b/app/src/main/java/com/example/vikramjeet/models/Filter.java @@ -0,0 +1,38 @@ +package com.example.vikramjeet.models; + +import java.io.Serializable; + +/** + * Created by Vikramjeet on 2/12/15. + */ +public class Filter implements Serializable { + private String imageSize; + private String imageColor; + private String imageType; + private String siteFilter; + + public Filter(String size, String color, String type, String site) { + imageSize = size; + imageColor = color; + imageType = type; + siteFilter = site; + + } + + public String getImageSize() { + return imageSize; + } + + public String getImageColor() { + return imageColor; + } + + public String getImageType() { + return imageType; + } + + public String getSiteFilter() { + return siteFilter; + } + +} diff --git a/app/src/main/java/com/example/vikramjeet/models/Image.java b/app/src/main/java/com/example/vikramjeet/models/Image.java new file mode 100644 index 0000000..26353cf --- /dev/null +++ b/app/src/main/java/com/example/vikramjeet/models/Image.java @@ -0,0 +1,73 @@ +package com.example.vikramjeet.models; + +import android.util.Log; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.Serializable; +import java.util.ArrayList; + +/** + * Created by Vikramjeet on 2/10/15. + */ +public class Image implements Serializable { + + private String Url; + private String thumbnailUrl; + private String title; + private String height; + private String width; + + public Image(JSONObject json) { + try { + Url = json.getString("unescapedUrl"); + thumbnailUrl = json.getString("tbUrl"); + title = json.getString("title"); + height = json.getString("height"); + width = json.getString("width"); + } catch(JSONException e) { + Log.e("Image", "Failed to parse json in constructor"); + e.printStackTrace(); + } + } + + public static ArrayList fromJSONArray(JSONArray array) { + ArrayList images = new ArrayList(); + int length = array.length(); + // Iterate over the imageList json + for (int index = 0 ; index < length; index++) { + try { + // Retrieve each imageJSON object + JSONObject imageJSON = array.getJSONObject(index); + // Add it to the images list + images.add(new Image(imageJSON)); + } catch (JSONException e) { + Log.e("Image", "Failed to parse json array"); + } + } + return images; + } + + public String getUrl() { + return Url; + } + + public String getThumbnailUrl() { + return thumbnailUrl; + } + + public String getTitle() { + return title; + } + + public String getHeight() { + return height; + } + + public String getWidth() { + return width; + } + +} diff --git a/app/src/main/res/drawable-hdpi/ic_launcher.png b/app/src/main/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000..96a442e Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_launcher.png b/app/src/main/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000..359047d Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_launcher.png b/app/src/main/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000..71c6d76 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/app/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..4df1894 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-xxhdpi/image_error.png b/app/src/main/res/drawable-xxhdpi/image_error.png new file mode 100644 index 0000000..e1eace9 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/image_error.png differ diff --git a/app/src/main/res/drawable-xxhdpi/image_placeholder.png b/app/src/main/res/drawable-xxhdpi/image_placeholder.png new file mode 100644 index 0000000..00e527b Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/image_placeholder.png differ diff --git a/app/src/main/res/layout/activity_image_detail.xml b/app/src/main/res/layout/activity_image_detail.xml new file mode 100644 index 0000000..7f50987 --- /dev/null +++ b/app/src/main/res/layout/activity_image_detail.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/layout/activity_image_search.xml b/app/src/main/res/layout/activity_image_search.xml new file mode 100644 index 0000000..604587f --- /dev/null +++ b/app/src/main/res/layout/activity_image_search.xml @@ -0,0 +1,24 @@ + + + + + diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml new file mode 100644 index 0000000..b96a92f --- /dev/null +++ b/app/src/main/res/layout/activity_settings.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + +