Browse Source
improve search
improve search
1. separate logged-in and anonymous endpoints 2. migrate to retrofit + gson, retire SuggestionsFetcher 3. prefixing search with @ or # will only return users or hashtags, respectively 4. add subtitles for locations (address) and hashtags (rough post count)renovate/org.robolectric-robolectric-4.x
Austin Huang
4 years ago
No known key found for this signature in database
GPG Key ID: 84C23AA04587A91F
10 changed files with 283 additions and 172 deletions
-
113app/src/main/java/awais/instagrabber/activities/MainActivity.java
-
26app/src/main/java/awais/instagrabber/adapters/SuggestionsAdapter.java
-
113app/src/main/java/awais/instagrabber/asyncs/SuggestionsFetcher.java
-
15app/src/main/java/awais/instagrabber/repositories/SearchRepository.java
-
11app/src/main/java/awais/instagrabber/repositories/responses/Hashtag.java
-
36app/src/main/java/awais/instagrabber/repositories/responses/search/Place.java
-
39app/src/main/java/awais/instagrabber/repositories/responses/search/SearchItem.java
-
48app/src/main/java/awais/instagrabber/repositories/responses/search/SearchResponse.java
-
4app/src/main/java/awais/instagrabber/webservices/GraphQLService.java
-
50app/src/main/java/awais/instagrabber/webservices/SearchService.java
@ -1,113 +0,0 @@ |
|||
package awais.instagrabber.asyncs; |
|||
|
|||
import android.os.AsyncTask; |
|||
import android.util.Log; |
|||
|
|||
import org.json.JSONArray; |
|||
import org.json.JSONObject; |
|||
|
|||
import java.io.InterruptedIOException; |
|||
import java.net.HttpURLConnection; |
|||
import java.net.URL; |
|||
import java.util.ArrayList; |
|||
import java.util.Collections; |
|||
|
|||
import awais.instagrabber.BuildConfig; |
|||
import awais.instagrabber.interfaces.FetchListener; |
|||
import awais.instagrabber.models.SuggestionModel; |
|||
import awais.instagrabber.models.enums.SuggestionType; |
|||
import awais.instagrabber.utils.Constants; |
|||
import awais.instagrabber.utils.NetworkUtils; |
|||
import awais.instagrabber.utils.UrlEncoder; |
|||
|
|||
public final class SuggestionsFetcher extends AsyncTask<String, String, SuggestionModel[]> { |
|||
private final FetchListener<SuggestionModel[]> fetchListener; |
|||
|
|||
public SuggestionsFetcher(final FetchListener<SuggestionModel[]> fetchListener) { |
|||
this.fetchListener = fetchListener; |
|||
} |
|||
|
|||
@Override |
|||
protected void onPreExecute() { |
|||
if (fetchListener != null) fetchListener.doBefore(); |
|||
} |
|||
|
|||
@Override |
|||
protected SuggestionModel[] doInBackground(final String... params) { |
|||
SuggestionModel[] result = null; |
|||
try { |
|||
final HttpURLConnection conn = (HttpURLConnection) new URL("https://www.instagram.com/web/search/topsearch/?context=blended&count=50&query=" |
|||
+ UrlEncoder.encodeUrl(params[0])).openConnection(); |
|||
conn.setUseCaches(false); |
|||
conn.connect(); |
|||
|
|||
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { |
|||
final JSONObject jsonObject = new JSONObject(NetworkUtils.readFromConnection(conn)); |
|||
conn.disconnect(); |
|||
|
|||
final JSONArray usersArray = jsonObject.getJSONArray("users"); |
|||
final JSONArray hashtagsArray = jsonObject.getJSONArray("hashtags"); |
|||
final JSONArray placesArray = jsonObject.getJSONArray("places"); |
|||
|
|||
final int usersLen = usersArray.length(); |
|||
final int hashtagsLen = hashtagsArray.length(); |
|||
final int placesLen = placesArray.length(); |
|||
|
|||
final ArrayList<SuggestionModel> suggestionModels = new ArrayList<>(usersLen + hashtagsLen); |
|||
for (int i = 0; i < hashtagsLen; i++) { |
|||
final JSONObject hashtagsArrayJSONObject = hashtagsArray.getJSONObject(i); |
|||
|
|||
final JSONObject hashtag = hashtagsArrayJSONObject.getJSONObject("hashtag"); |
|||
|
|||
suggestionModels.add(new SuggestionModel(false, |
|||
hashtag.getString(Constants.EXTRAS_NAME), |
|||
null, |
|||
hashtag.optString("profile_pic_url", Constants.DEFAULT_HASH_TAG_PIC), |
|||
SuggestionType.TYPE_HASHTAG, |
|||
hashtagsArrayJSONObject.optInt("position", suggestionModels.size() - 1))); |
|||
} |
|||
|
|||
for (int i = 0; i < placesLen; i++) { |
|||
final JSONObject placesArrayJSONObject = placesArray.getJSONObject(i); |
|||
|
|||
final JSONObject place = placesArrayJSONObject.getJSONObject("place"); |
|||
|
|||
// name |
|||
suggestionModels.add(new SuggestionModel(false, |
|||
place.getJSONObject("location").getString("pk"), // +"/"+place.getString("slug"), |
|||
place.getString("title"), |
|||
place.optString("profile_pic_url"), |
|||
SuggestionType.TYPE_LOCATION, |
|||
placesArrayJSONObject.optInt("position", suggestionModels.size() - 1))); |
|||
} |
|||
|
|||
for (int i = 0; i < usersLen; i++) { |
|||
final JSONObject usersArrayJSONObject = usersArray.getJSONObject(i); |
|||
|
|||
final JSONObject user = usersArrayJSONObject.getJSONObject(Constants.EXTRAS_USER); |
|||
|
|||
suggestionModels.add(new SuggestionModel(user.getBoolean("is_verified"), |
|||
user.getString(Constants.EXTRAS_USERNAME), |
|||
user.getString("full_name"), |
|||
user.getString("profile_pic_url"), |
|||
SuggestionType.TYPE_USER, |
|||
usersArrayJSONObject.optInt("position", suggestionModels.size() - 1))); |
|||
} |
|||
|
|||
suggestionModels.trimToSize(); |
|||
|
|||
Collections.sort(suggestionModels); |
|||
|
|||
result = suggestionModels.toArray(new SuggestionModel[0]); |
|||
} |
|||
} catch (final Exception e) { |
|||
if (BuildConfig.DEBUG && !(e instanceof InterruptedIOException)) Log.e("AWAISKING_APP", "", e); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
@Override |
|||
protected void onPostExecute(final SuggestionModel[] result) { |
|||
if (fetchListener != null) fetchListener.onResult(result); |
|||
} |
|||
} |
@ -0,0 +1,15 @@ |
|||
package awais.instagrabber.repositories; |
|||
|
|||
import java.util.Map; |
|||
|
|||
import awais.instagrabber.repositories.responses.search.SearchResponse; |
|||
|
|||
import retrofit2.Call; |
|||
import retrofit2.http.GET; |
|||
import retrofit2.http.QueryMap; |
|||
import retrofit2.http.Url; |
|||
|
|||
public interface SearchRepository { |
|||
@GET |
|||
Call<SearchResponse> search(@Url String url, @QueryMap(encoded = true) Map<String, String> queryParams); |
|||
} |
@ -0,0 +1,36 @@ |
|||
package awais.instagrabber.repositories.responses.search; |
|||
|
|||
import awais.instagrabber.repositories.responses.Location; |
|||
|
|||
public class Place { |
|||
private final Location location; |
|||
private final String title; // those are repeated within location |
|||
private final String subtitle; // address |
|||
private final String slug; // browser only; for end of address |
|||
|
|||
public Place(final Location location, |
|||
final String title, |
|||
final String subtitle, |
|||
final String slug) { |
|||
this.location = location; |
|||
this.title = title; |
|||
this.subtitle = subtitle; |
|||
this.slug = slug; |
|||
} |
|||
|
|||
public Location getLocation() { |
|||
return location; |
|||
} |
|||
|
|||
public String getTitle() { |
|||
return title; |
|||
} |
|||
|
|||
public String getSubtitle() { |
|||
return subtitle; |
|||
} |
|||
|
|||
public String getSlug() { |
|||
return slug; |
|||
} |
|||
} |
@ -0,0 +1,39 @@ |
|||
package awais.instagrabber.repositories.responses.search; |
|||
|
|||
import java.util.List; |
|||
|
|||
import awais.instagrabber.repositories.responses.Hashtag; |
|||
import awais.instagrabber.repositories.responses.User; |
|||
|
|||
public class SearchItem { |
|||
private final User user; |
|||
private final Place place; |
|||
private final Hashtag hashtag; |
|||
private final int position; |
|||
|
|||
public SearchItem(final User user, |
|||
final Place place, |
|||
final Hashtag hashtag, |
|||
final int position) { |
|||
this.user = user; |
|||
this.place = place; |
|||
this.hashtag = hashtag; |
|||
this.position = position; |
|||
} |
|||
|
|||
public User getUser() { |
|||
return user; |
|||
} |
|||
|
|||
public Place getPlace() { |
|||
return place; |
|||
} |
|||
|
|||
public Hashtag getHashtag() { |
|||
return hashtag; |
|||
} |
|||
|
|||
public int getPosition() { |
|||
return position; |
|||
} |
|||
} |
@ -0,0 +1,48 @@ |
|||
package awais.instagrabber.repositories.responses.search; |
|||
|
|||
import java.util.List; |
|||
|
|||
import awais.instagrabber.repositories.responses.User; |
|||
|
|||
public class SearchResponse { |
|||
// app |
|||
private final List<SearchItem> list; |
|||
// browser |
|||
private final List<SearchItem> users; |
|||
private final List<SearchItem> places; |
|||
private final List<SearchItem> hashtags; |
|||
// universal |
|||
private final String status; |
|||
|
|||
public SearchResponse(final List<SearchItem> list, |
|||
final List<SearchItem> users, |
|||
final List<SearchItem> places, |
|||
final List<SearchItem> hashtags, |
|||
final String status) { |
|||
this.list = list; |
|||
this.users = users; |
|||
this.places = places; |
|||
this.hashtags = hashtags; |
|||
this.status = status; |
|||
} |
|||
|
|||
public List<SearchItem> getList() { |
|||
return list; |
|||
} |
|||
|
|||
public List<SearchItem> getUsers() { |
|||
return users; |
|||
} |
|||
|
|||
public List<SearchItem> getPlaces() { |
|||
return places; |
|||
} |
|||
|
|||
public List<SearchItem> getHashtags() { |
|||
return hashtags; |
|||
} |
|||
|
|||
public String getStatus() { |
|||
return status; |
|||
} |
|||
} |
@ -0,0 +1,50 @@ |
|||
package awais.instagrabber.webservices; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
|
|||
import com.google.common.collect.ImmutableMap; |
|||
|
|||
import awais.instagrabber.repositories.SearchRepository; |
|||
import awais.instagrabber.repositories.responses.search.SearchResponse; |
|||
import awais.instagrabber.utils.TextUtils; |
|||
import retrofit2.Call; |
|||
import retrofit2.Callback; |
|||
import retrofit2.Response; |
|||
import retrofit2.Retrofit; |
|||
|
|||
public class SearchService extends BaseService { |
|||
private static final String TAG = "LocationService"; |
|||
|
|||
private final SearchRepository repository; |
|||
|
|||
private static SearchService instance; |
|||
|
|||
private SearchService() { |
|||
final Retrofit retrofit = getRetrofitBuilder() |
|||
.baseUrl("https://www.instagram.com") |
|||
.build(); |
|||
repository = retrofit.create(SearchRepository.class); |
|||
} |
|||
|
|||
public static SearchService getInstance() { |
|||
if (instance == null) { |
|||
instance = new SearchService(); |
|||
} |
|||
return instance; |
|||
} |
|||
|
|||
public Call<SearchResponse> search(final boolean isLoggedIn, |
|||
final String query, |
|||
final String context) { |
|||
final ImmutableMap.Builder<String, String> builder = ImmutableMap.builder(); |
|||
builder.put("query", query); |
|||
// context is one of: "blended", "user", "place", "hashtag" |
|||
// note that "place" and "hashtag" can contain ONE user result, who knows why |
|||
builder.put("context", context); |
|||
builder.put("count", "50"); |
|||
return repository.search(isLoggedIn |
|||
? "https://i.instagram.com/api/v1/fbsearch/topsearch_flat/" |
|||
: "https://www.instagram.com/web/search/topsearch/", |
|||
builder.build()); |
|||
} |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue