diff --git a/.gitignore b/.gitignore index 09b993d..0df7064 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ *.iml .gradle /local.properties -/.idea +/.idea/ .DS_Store /build /captures diff --git a/app/build.gradle b/app/build.gradle index caa745d..b0aefae 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -34,9 +34,13 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' - implementation 'com.android.volley:volley:1.1.1' +// implementation 'com.android.volley:volley:1.1.1' + implementation 'com.loopj.android:android-async-http:1.4.9' implementation 'org.jsoup:jsoup:1.7.2' implementation group: 'commons-io', name: 'commons-io', version: '2.4' +// implementation 'com.squareup.retrofit2:retrofit:2.1.0' +// implementation 'com.google.code.gson:gson:2.8.2' +// implementation 'com.squareup.retrofit2:converter-gson:2.1.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' diff --git a/app/src/main/java/eu/dasancti/reversee/MainActivity.java b/app/src/main/java/eu/dasancti/reversee/MainActivity.java index 9a1e830..6eacf12 100644 --- a/app/src/main/java/eu/dasancti/reversee/MainActivity.java +++ b/app/src/main/java/eu/dasancti/reversee/MainActivity.java @@ -1,7 +1,6 @@ package eu.dasancti.reversee; import android.Manifest; -import android.content.ContentProviderClient; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; @@ -9,23 +8,20 @@ import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.util.Log; -import android.util.Xml; +import android.widget.TextView; import android.widget.Toast; -import com.android.volley.Request; -import com.android.volley.RequestQueue; -import com.android.volley.Response; -import com.android.volley.VolleyError; -import com.android.volley.toolbox.StringRequest; -import com.android.volley.toolbox.Volley; -import org.apache.commons.io.IOUtils; +import com.loopj.android.http.AsyncHttpClient; +import com.loopj.android.http.AsyncHttpResponseHandler; +import com.loopj.android.http.RequestHandle; +import com.loopj.android.http.RequestParams; +import cz.msebera.android.httpclient.Header; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; -import java.io.*; -import java.util.HashMap; -import java.util.Map; +import java.io.FileNotFoundException; +import java.util.Arrays; import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; @@ -33,86 +29,89 @@ public class MainActivity extends AppCompatActivity { private static final String GOOGLE_REVERSE_IMAGE_SEARCH_URL = "https://www.google.com/searchbyimage/upload"; private static final String FAKE_USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11"; + private static final String API_SCH_KEY = "sch"; + private static final String API_SCH_VALUE = "sch"; + private static final String API_ENCODED_IMAGE_KEY = "encoded_image"; + private static final String API_USER_AGENT_KEY = "User-Agent"; + + private TextView progressStatus; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); + progressStatus = findViewById(R.id.progressStatus); + Intent intent = getIntent(); String action = intent.getAction(); if (isPermissionGranted()) { if (action.equals(Intent.ACTION_SEND)) { - handleImageSearch(intent); + progressStatus.setText(getString(R.string.progress_status_recieved_intent)); + try { + handleImageSearch(intent); + } catch (FileNotFoundException e) { + String err = "Failed to handle Share image intent:"+e.getMessage(); + Log.e("INTENT_HANDLE",err); + Toast.makeText(getApplicationContext(), err, Toast.LENGTH_SHORT).show(); + } } } else { Toast.makeText(this, "The app wasn't allowed permissions to manage files.", Toast.LENGTH_LONG).show(); } } - private void handleImageSearch(Intent intent) { + private void handleImageSearch(Intent intent) throws FileNotFoundException { Uri imageUri = intent.getParcelableExtra(Intent.EXTRA_STREAM); - Toast.makeText(this, "Received image URI:"+imageUri.getPath(), Toast.LENGTH_SHORT).show(); - AtomicReference imageData = new AtomicReference<>(); - try { - imageData.set(IOUtils.toString(Objects.requireNonNull(getContentResolver().openInputStream(imageUri)))); - } catch (FileNotFoundException e) { - String err = "Couldn't open the shared image as an input stream."; - Toast.makeText(this, err, Toast.LENGTH_LONG).show(); - Log.e("GET_IMAGE_FILE", err); - this.finish(); - return; - } catch (IOException e) { - String err = "Couldn't read input stream of image."; - Toast.makeText(this, err, Toast.LENGTH_LONG).show(); - Log.e("GET_IMAGE_FILE", err); - this.finish(); - return; - } - Intent searchIntent = new Intent(); - searchIntent.setAction(Intent.ACTION_VIEW); - //TODO: Implement browser intent with POST method - RequestQueue requestQueue = Volley.newRequestQueue(this); - final AtomicReference browserRedirect = new AtomicReference<>(); - StringRequest getGoogleRedirectUrl = new StringRequest( - Request.Method.POST, - GOOGLE_REVERSE_IMAGE_SEARCH_URL, - response -> { - Document doc = Jsoup.parse(response); - Element content = doc.getElementById("content"); - Elements redirects = content.getElementsByTag("meta"); + progressStatus.setText(getString(R.string.progress_status_handling_image)); + AtomicReference reverseSearchRedirectURL = new AtomicReference<>(); + AsyncHttpClient asyncHttpClient = new AsyncHttpClient(); + asyncHttpClient.addHeader(API_USER_AGENT_KEY, FAKE_USER_AGENT); + RequestParams requestParams = new RequestParams(); + requestParams.put(API_SCH_KEY, API_SCH_VALUE); + requestParams.put(API_ENCODED_IMAGE_KEY, Objects.requireNonNull(getContentResolver().openInputStream(imageUri))); + RequestHandle requestHandle = asyncHttpClient.post(getApplicationContext(),GOOGLE_REVERSE_IMAGE_SEARCH_URL, requestParams, new AsyncHttpResponseHandler() { + @Override + public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) { + Log.e("UNEXPECTED", "Based on the stupendous behavior of this async library, we should never be able to get here. 3xx HTML codes are considered a failure."); + MainActivity.this.finish(); + } + + @Override + public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) { + if (statusCode == 302) { + progressStatus.setText(getString(R.string.progress_status_response_success)); + Log.i("RESPONSE_SUCCESS", "Recieved 302 redirect, processing HTML response."); + String responseString = new String(responseBody); + Document doc = Jsoup.parse(responseString); + Elements redirects = doc.getElementsByTag("a"); for (Element redirect : redirects) { - String redirectUrl = redirect.attr("url"); + String redirectUrl = redirect.attr("href"); if (redirectUrl.contains("/search?tbs=sbi:")) { - browserRedirect.set(redirectUrl); + reverseSearchRedirectURL.set(redirectUrl); + progressStatus.setText(getString(R.string.progress_status_redirect_found)); break; } } - if (browserRedirect.get().isEmpty()) { - Toast.makeText(this, "Failed to get redirect URL", Toast.LENGTH_LONG).show(); + if (reverseSearchRedirectURL.get().isEmpty()) { + Toast.makeText(MainActivity.this, "Failed to get redirect URL", Toast.LENGTH_LONG).show(); } else { - //TODO: Launch browser intent with the redirect - Toast.makeText(this, "Found redirect URL for reverse search.", Toast.LENGTH_SHORT).show(); - Log.i("REVERSE_SEARCH_URL", browserRedirect.get()); + Toast.makeText(MainActivity.this, "Found redirect URL for reverse search, opening browser.", Toast.LENGTH_SHORT).show(); + Log.i("REVERSE_SEARCH_URL", reverseSearchRedirectURL.get()); + Intent searchIntent = new Intent(); + searchIntent.setAction(Intent.ACTION_VIEW); + searchIntent.setData(Uri.parse(reverseSearchRedirectURL.get())); + startActivity(searchIntent); + MainActivity.this.finish(); } - }, - error -> Toast.makeText(this, "POST call to reverse search failed: "+error.getMessage(), Toast.LENGTH_LONG).show() - ) { - protected Map getParams() { - Map params = new HashMap<>(); - params.put("sch", "sch"); - params.put("encoded_image", imageData.get()); - return params; - } - public Map getHeaders() { - Map headers = new HashMap<>(); - headers.put("User-Agent", FAKE_USER_AGENT); - return headers; + } else { + String err = "POST call to reverse search failed [" + statusCode + "] error message: " + error.getMessage() + "\n body:" + Arrays.toString(responseBody); + Log.e("POST_CALL_FAILED", err); + Toast.makeText(MainActivity.this, err, Toast.LENGTH_LONG).show(); + } } - }; - requestQueue.add(getGoogleRedirectUrl); - Toast.makeText(this, "Handling google reverse image search.", Toast.LENGTH_LONG).show(); - this.finish(); + }); + // TODO: Display spinner until requestHandle.isFinished() is true } private boolean isPermissionGranted() { diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 980638c..c8fc93d 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -10,10 +10,41 @@ + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="0.351" + tools:ignore="HardcodedText"/> + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ee747d3..6a73b8b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,7 @@ Reversee + Recieved intent data (image share) + Handling google reverse image search. + Google request successful. + Found redirect URL, opening browser intent.