Skip to content

Commit

Permalink
Reverse image search is finally working :3 TODO: Add default applicat…
Browse files Browse the repository at this point in the history
…ion activity and settings
  • Loading branch information
ScrewTSW committed Oct 31, 2018
1 parent b33fc8a commit e96a0dc
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 71 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
*.iml
.gradle
/local.properties
/.idea
/.idea/
.DS_Store
/build
/captures
Expand Down
6 changes: 5 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
133 changes: 66 additions & 67 deletions app/src/main/java/eu/dasancti/reversee/MainActivity.java
Original file line number Diff line number Diff line change
@@ -1,118 +1,117 @@
package eu.dasancti.reversee;

import android.Manifest;
import android.content.ContentProviderClient;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
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;

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<String> 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<String> 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<String> 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<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put("sch", "sch");
params.put("encoded_image", imageData.get());
return params;
}
public Map<String, String> getHeaders() {
Map<String, String> 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() {
Expand Down
35 changes: 33 additions & 2 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,41 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:text="Please be patient"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.351"
tools:ignore="HardcodedText"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="We are processing your queue."
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.39"
android:id="@+id/textView"
tools:ignore="HardcodedText"/>
<ProgressBar
style="?android:attr/progressBarStyle"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:layout_editor_absoluteY="230dp" tools:layout_editor_absoluteX="167dp" android:id="@+id/progressBar" />
<TextView
app:layout_constraintTop_toBottomOf="@id/progressBar"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:text="..."
android:layout_width="wrap_content"
android:layout_height="wrap_content" tools:layout_editor_absoluteY="280dp"
tools:layout_editor_absoluteX="187dp" android:id="@+id/progressStatus" android:paddingTop="42dp"
tools:ignore="HardcodedText"/>

</android.support.constraint.ConstraintLayout>
4 changes: 4 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
<resources>
<string name="app_name">Reversee</string>
<string name="progress_status_recieved_intent">Recieved intent data (image share)</string>
<string name="progress_status_handling_image">Handling google reverse image search.</string>
<string name="progress_status_response_success">Google request successful.</string>
<string name="progress_status_redirect_found">Found redirect URL, opening browser intent.</string>
</resources>

0 comments on commit e96a0dc

Please sign in to comment.