Browse Source
Merge branch 'master' into bottombar_redesign
renovate/org.robolectric-robolectric-4.x
Merge branch 'master' into bottombar_redesign
renovate/org.robolectric-robolectric-4.x
71 changed files with 1087 additions and 967 deletions
-
1.gitignore
-
20app/build.gradle
-
13app/sentry.gradle
-
40app/src/fdroid/java/awais/instagrabber/fragments/settings/FlavorSettings.java
-
56app/src/fdroid/java/awaisomereport/CrashHandler.java
-
10app/src/github/AndroidManifest.xml
-
83app/src/github/java/awais/instagrabber/fragments/settings/FlavorSettings.java
-
59app/src/github/java/awaisomereport/CrashHandler.java
-
6app/src/github/res/values/strings.xml
-
39app/src/main/java/awais/instagrabber/InstaGrabberApplication.java
-
118app/src/main/java/awais/instagrabber/activities/MainActivity.java
-
2app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java
-
27app/src/main/java/awais/instagrabber/adapters/SuggestionsAdapter.java
-
4app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java
-
8app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemRavenMediaViewHolder.java
-
81app/src/main/java/awais/instagrabber/asyncs/LocationFetcher.java
-
10app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java
-
113app/src/main/java/awais/instagrabber/asyncs/SuggestionsFetcher.java
-
69app/src/main/java/awais/instagrabber/dialogs/ConfirmDialogFragment.java
-
2app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java
-
8app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java
-
154app/src/main/java/awais/instagrabber/fragments/LocationFragment.java
-
7app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java
-
31app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java
-
8app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java
-
141app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java
-
10app/src/main/java/awais/instagrabber/fragments/settings/GeneralPreferencesFragment.java
-
14app/src/main/java/awais/instagrabber/fragments/settings/IFlavorSettings.java
-
1app/src/main/java/awais/instagrabber/fragments/settings/PreferenceKeys.java
-
6app/src/main/java/awais/instagrabber/fragments/settings/SettingCategory.java
-
56app/src/main/java/awais/instagrabber/models/LocationModel.java
-
51app/src/main/java/awais/instagrabber/models/SuggestionModel.java
-
2app/src/main/java/awais/instagrabber/repositories/FeedRepository.java
-
3app/src/main/java/awais/instagrabber/repositories/GraphQLRepository.java
-
3app/src/main/java/awais/instagrabber/repositories/LocationRepository.java
-
14app/src/main/java/awais/instagrabber/repositories/SearchRepository.java
-
18app/src/main/java/awais/instagrabber/repositories/responses/Hashtag.java
-
18app/src/main/java/awais/instagrabber/repositories/responses/Location.java
-
1app/src/main/java/awais/instagrabber/repositories/responses/Media.java
-
3app/src/main/java/awais/instagrabber/repositories/responses/NewsInboxResponse.java
-
43app/src/main/java/awais/instagrabber/repositories/responses/Place.java
-
2app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedDemarcator.java
-
4app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedGroup.java
-
2app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedGroupSet.java
-
4app/src/main/java/awais/instagrabber/repositories/responses/feed/FeedFetchResponse.java
-
2app/src/main/java/awais/instagrabber/repositories/responses/notification/Notification.java
-
4app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationArgs.java
-
4app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationCounts.java
-
2app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationImage.java
-
38app/src/main/java/awais/instagrabber/repositories/responses/search/SearchItem.java
-
46app/src/main/java/awais/instagrabber/repositories/responses/search/SearchResponse.java
-
4app/src/main/java/awais/instagrabber/services/ActivityCheckerService.java
-
2app/src/main/java/awais/instagrabber/utils/Constants.java
-
7app/src/main/java/awais/instagrabber/utils/SettingsHelper.java
-
9app/src/main/java/awais/instagrabber/utils/TextUtils.java
-
2app/src/main/java/awais/instagrabber/viewmodels/NotificationViewModel.java
-
8app/src/main/java/awais/instagrabber/webservices/FeedService.java
-
49app/src/main/java/awais/instagrabber/webservices/GraphQLService.java
-
50app/src/main/java/awais/instagrabber/webservices/LocationService.java
-
19app/src/main/java/awais/instagrabber/webservices/NewsService.java
-
45app/src/main/java/awais/instagrabber/webservices/SearchService.java
-
179app/src/main/java/awaisomereport/CrashReporter.java
-
134app/src/main/java/awaisomereport/CrashReporterHelper.java
-
5app/src/main/java/awaisomereport/ErrorReporterActivity.java
-
7app/src/main/java/awaisomereport/ICrashHandler.java
-
BINapp/src/main/res/drawable/ic_hashtag.png
-
56app/src/main/res/layout/layout_location_details.xml
-
10app/src/main/res/menu/dm_inbox_menu.xml
-
1app/src/main/res/values/ids.xml
-
2app/src/main/res/values/strings.xml
-
2crowdin.yml
@ -0,0 +1,13 @@ |
|||||
|
def dsnKey = 'DSN' |
||||
|
def defaultDsn = '\"\"' |
||||
|
|
||||
|
final Properties properties = new Properties() |
||||
|
File propertiesFile = rootProject.file('sentry.properties') |
||||
|
if (!propertiesFile.exists()) { |
||||
|
propertiesFile.createNewFile() |
||||
|
} |
||||
|
properties.load(new FileInputStream(propertiesFile)) |
||||
|
|
||||
|
ext{ |
||||
|
SENTRY_DSN = properties.getProperty(dsnKey, defaultDsn) |
||||
|
} |
@ -0,0 +1,40 @@ |
|||||
|
package awais.instagrabber.fragments.settings; |
||||
|
|
||||
|
import android.content.Context; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
import androidx.fragment.app.FragmentManager; |
||||
|
import androidx.preference.Preference; |
||||
|
|
||||
|
import java.util.Collections; |
||||
|
import java.util.List; |
||||
|
|
||||
|
import awais.instagrabber.fragments.settings.IFlavorSettings; |
||||
|
import awais.instagrabber.fragments.settings.SettingCategory; |
||||
|
|
||||
|
public final class FlavorSettings implements IFlavorSettings { |
||||
|
|
||||
|
private static FlavorSettings instance; |
||||
|
|
||||
|
private FlavorSettings() { |
||||
|
} |
||||
|
|
||||
|
public static FlavorSettings getInstance() { |
||||
|
if (instance == null) { |
||||
|
instance = new FlavorSettings(); |
||||
|
} |
||||
|
return instance; |
||||
|
} |
||||
|
|
||||
|
@NonNull |
||||
|
@Override |
||||
|
public List<Preference> getPreferences(@NonNull final Context context, |
||||
|
@NonNull final FragmentManager fragmentManager, |
||||
|
@NonNull final SettingCategory settingCategory) { |
||||
|
// switch (settingCategory) { |
||||
|
// default: |
||||
|
// break; |
||||
|
// } |
||||
|
return Collections.emptyList(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,56 @@ |
|||||
|
package awaisomereport; |
||||
|
|
||||
|
import android.app.Application; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
|
||||
|
public class CrashHandler implements ICrashHandler { |
||||
|
private static final String TAG = CrashHandler.class.getSimpleName(); |
||||
|
|
||||
|
private final Application application; |
||||
|
|
||||
|
public CrashHandler(@NonNull final Application application) { |
||||
|
this.application = application; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void uncaughtException(@NonNull final Thread t, |
||||
|
@NonNull final Throwable exception, |
||||
|
@NonNull final Thread.UncaughtExceptionHandler defaultEH) { |
||||
|
CrashReporterHelper.startErrorReporterActivity(application, exception); |
||||
|
// zipLogs(); |
||||
|
defaultEH.uncaughtException(t, exception); |
||||
|
} |
||||
|
|
||||
|
// public synchronized CrashReporter zipLogs() { |
||||
|
// final File logDir = Utils.logCollector != null ? Utils.logCollector.getLogDir() : |
||||
|
// new File(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? application.getDataDir() : application.getFilesDir(), "crashlogs"); |
||||
|
// |
||||
|
// try (final FileOutputStream fos = new FileOutputStream(crashLogsZip); |
||||
|
// final ZipOutputStream zos = new ZipOutputStream(fos)) { |
||||
|
// |
||||
|
// final File[] files = logDir.listFiles(); |
||||
|
// |
||||
|
// if (files != null) { |
||||
|
// zos.setLevel(5); |
||||
|
// byte[] buffer; |
||||
|
// for (final File file : files) { |
||||
|
// if (file != null && file.length() > 0) { |
||||
|
// buffer = new byte[1024]; |
||||
|
// try (final FileInputStream fis = new FileInputStream(file)) { |
||||
|
// zos.putNextEntry(new ZipEntry(file.getName())); |
||||
|
// int length; |
||||
|
// while ((length = fis.read(buffer)) > 0) zos.write(buffer, 0, length); |
||||
|
// zos.closeEntry(); |
||||
|
// } |
||||
|
// } |
||||
|
// } |
||||
|
// } |
||||
|
// |
||||
|
// } catch (final Exception e) { |
||||
|
// if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); |
||||
|
// } |
||||
|
// |
||||
|
// return this; |
||||
|
// } |
||||
|
} |
@ -0,0 +1,10 @@ |
|||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
package="awais.instagrabber"> |
||||
|
|
||||
|
<application> |
||||
|
<meta-data |
||||
|
android:name="io.sentry.auto-init" |
||||
|
android:value="false" /> |
||||
|
</application> |
||||
|
|
||||
|
</manifest> |
@ -0,0 +1,83 @@ |
|||||
|
package awais.instagrabber.fragments.settings; |
||||
|
|
||||
|
import android.content.Context; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
import androidx.fragment.app.FragmentManager; |
||||
|
import androidx.preference.Preference; |
||||
|
|
||||
|
import com.google.common.collect.ImmutableList; |
||||
|
|
||||
|
import java.util.Collections; |
||||
|
import java.util.List; |
||||
|
|
||||
|
import awais.instagrabber.R; |
||||
|
import awais.instagrabber.dialogs.ConfirmDialogFragment; |
||||
|
|
||||
|
import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_SENTRY; |
||||
|
import static awais.instagrabber.utils.Utils.settingsHelper; |
||||
|
|
||||
|
public final class FlavorSettings implements IFlavorSettings { |
||||
|
|
||||
|
private static FlavorSettings instance; |
||||
|
|
||||
|
private FlavorSettings() { |
||||
|
} |
||||
|
|
||||
|
public static FlavorSettings getInstance() { |
||||
|
if (instance == null) { |
||||
|
instance = new FlavorSettings(); |
||||
|
} |
||||
|
return instance; |
||||
|
} |
||||
|
|
||||
|
@NonNull |
||||
|
@Override |
||||
|
public List<Preference> getPreferences(@NonNull final Context context, |
||||
|
@NonNull final FragmentManager fragmentManager, |
||||
|
@NonNull final SettingCategory settingCategory) { |
||||
|
switch (settingCategory) { |
||||
|
case GENERAL: |
||||
|
return getGeneralPrefs(context, fragmentManager); |
||||
|
default: |
||||
|
break; |
||||
|
} |
||||
|
return Collections.emptyList(); |
||||
|
} |
||||
|
|
||||
|
private List<Preference> getGeneralPrefs(@NonNull final Context context, |
||||
|
@NonNull final FragmentManager fragmentManager) { |
||||
|
return ImmutableList.of( |
||||
|
getSentryPreference(context, fragmentManager) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
private Preference getSentryPreference(@NonNull final Context context, |
||||
|
@NonNull final FragmentManager fragmentManager) { |
||||
|
if (!settingsHelper.hasPreference(PREF_ENABLE_SENTRY)) { |
||||
|
// disabled by default |
||||
|
settingsHelper.putBoolean(PREF_ENABLE_SENTRY, false); |
||||
|
} |
||||
|
return PreferenceHelper.getSwitchPreference( |
||||
|
context, |
||||
|
PREF_ENABLE_SENTRY, |
||||
|
R.string.enable_sentry, |
||||
|
R.string.sentry_summary, |
||||
|
false, |
||||
|
(preference, newValue) -> { |
||||
|
if (!(newValue instanceof Boolean)) return true; |
||||
|
final boolean enabled = (Boolean) newValue; |
||||
|
if (enabled) { |
||||
|
final ConfirmDialogFragment dialogFragment = ConfirmDialogFragment.newInstance( |
||||
|
111, |
||||
|
0, |
||||
|
R.string.sentry_start_next_launch, |
||||
|
R.string.ok, |
||||
|
0, |
||||
|
0); |
||||
|
dialogFragment.show(fragmentManager, "sentry_dialog"); |
||||
|
} |
||||
|
return true; |
||||
|
}); |
||||
|
} |
||||
|
} |
@ -0,0 +1,59 @@ |
|||||
|
package awaisomereport; |
||||
|
|
||||
|
import android.app.Application; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
|
||||
|
import awais.instagrabber.BuildConfig; |
||||
|
import awais.instagrabber.fragments.settings.PreferenceKeys; |
||||
|
import io.sentry.SentryLevel; |
||||
|
import io.sentry.android.core.SentryAndroid; |
||||
|
import io.sentry.protocol.Contexts; |
||||
|
import io.sentry.protocol.Device; |
||||
|
|
||||
|
import static awais.instagrabber.utils.Utils.settingsHelper; |
||||
|
|
||||
|
public class CrashHandler implements ICrashHandler { |
||||
|
private static final String TAG = CrashHandler.class.getSimpleName(); |
||||
|
|
||||
|
private final Application application; |
||||
|
private final boolean enabled; |
||||
|
|
||||
|
public CrashHandler(@NonNull final Application application) { |
||||
|
this.application = application; |
||||
|
if (!settingsHelper.hasPreference(PreferenceKeys.PREF_ENABLE_SENTRY)) { |
||||
|
// disabled by default (change to true if we need enabled by default) |
||||
|
enabled = false; |
||||
|
} else { |
||||
|
enabled = settingsHelper.getBoolean(PreferenceKeys.PREF_ENABLE_SENTRY); |
||||
|
} |
||||
|
if (!enabled) return; |
||||
|
SentryAndroid.init(application, options -> { |
||||
|
options.setDsn(BuildConfig.dsn); |
||||
|
options.setDiagnosticLevel(SentryLevel.ERROR); |
||||
|
options.setBeforeSend((event, hint) -> { |
||||
|
// Removing unneeded info from event |
||||
|
final Contexts contexts = event.getContexts(); |
||||
|
final Device device = contexts.getDevice(); |
||||
|
device.setName(null); |
||||
|
device.setTimezone(null); |
||||
|
device.setCharging(null); |
||||
|
device.setBootTime(null); |
||||
|
device.setFreeStorage(null); |
||||
|
device.setBatteryTemperature(null); |
||||
|
return event; |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void uncaughtException(@NonNull final Thread t, |
||||
|
@NonNull final Throwable exception, |
||||
|
@NonNull final Thread.UncaughtExceptionHandler defaultEH) { |
||||
|
// When enabled, Sentry auto captures unhandled exceptions |
||||
|
if (!enabled) { |
||||
|
CrashReporterHelper.startErrorReporterActivity(application, exception); |
||||
|
} |
||||
|
defaultEH.uncaughtException(t, exception); |
||||
|
} |
||||
|
} |
@ -0,0 +1,6 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<resources> |
||||
|
<string name="enable_sentry">Enable Sentry</string> |
||||
|
<string name="sentry_summary">Sentry is a listener/handler for errors that asynchronously sends out the error/event to Sentry.io</string> |
||||
|
<string name="sentry_start_next_launch">Sentry will start on next launch</string> |
||||
|
</resources> |
@ -1,81 +0,0 @@ |
|||||
package awais.instagrabber.asyncs; |
|
||||
|
|
||||
import android.os.AsyncTask; |
|
||||
import android.util.Log; |
|
||||
|
|
||||
import androidx.annotation.Nullable; |
|
||||
|
|
||||
import org.json.JSONObject; |
|
||||
|
|
||||
import java.math.BigDecimal; |
|
||||
import java.net.HttpURLConnection; |
|
||||
import java.net.URL; |
|
||||
|
|
||||
import awais.instagrabber.BuildConfig; |
|
||||
import awais.instagrabber.interfaces.FetchListener; |
|
||||
import awais.instagrabber.models.LocationModel; |
|
||||
import awais.instagrabber.utils.Constants; |
|
||||
import awais.instagrabber.utils.NetworkUtils; |
|
||||
//import awaisomereport.LogCollector; |
|
||||
|
|
||||
//import static awais.instagrabber.utils.Utils.logCollector; |
|
||||
|
|
||||
public final class LocationFetcher extends AsyncTask<Void, Void, LocationModel> { |
|
||||
private static final String TAG = "LocationFetcher"; |
|
||||
|
|
||||
private final FetchListener<LocationModel> fetchListener; |
|
||||
private final long id; |
|
||||
|
|
||||
public LocationFetcher(final long id, final FetchListener<LocationModel> fetchListener) { |
|
||||
// idSlug = id + "/" + slug UPDATE: slug can be ignored tbh |
|
||||
this.id = id; |
|
||||
this.fetchListener = fetchListener; |
|
||||
} |
|
||||
|
|
||||
@Nullable |
|
||||
@Override |
|
||||
protected LocationModel doInBackground(final Void... voids) { |
|
||||
LocationModel result = null; |
|
||||
|
|
||||
try { |
|
||||
final HttpURLConnection conn = (HttpURLConnection) new URL("https://www.instagram.com/explore/locations/" + id + "/?__a=1") |
|
||||
.openConnection(); |
|
||||
conn.setUseCaches(true); |
|
||||
conn.connect(); |
|
||||
|
|
||||
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { |
|
||||
final JSONObject location = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("graphql") |
|
||||
.getJSONObject(Constants.EXTRAS_LOCATION); |
|
||||
|
|
||||
final JSONObject timelineMedia = location.getJSONObject("edge_location_to_media"); |
|
||||
// if (timelineMedia.has("edges")) { |
|
||||
// final JSONArray edges = timelineMedia.getJSONArray("edges"); |
|
||||
// } |
|
||||
result = new LocationModel( |
|
||||
location.getLong(Constants.EXTRAS_ID), |
|
||||
location.getString("name"), |
|
||||
location.getString("blurb"), |
|
||||
location.getString("website"), |
|
||||
location.getString("profile_pic_url"), |
|
||||
timelineMedia.getLong("count"), |
|
||||
BigDecimal.valueOf(location.optDouble("lat", 0d)).toString(), |
|
||||
BigDecimal.valueOf(location.optDouble("lng", 0d)).toString() |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
conn.disconnect(); |
|
||||
} catch (final Exception e) { |
|
||||
// if (logCollector != null) |
|
||||
// logCollector.appendException(e, LogCollector.LogFile.ASYNC_LOCATION_FETCHER, "doInBackground"); |
|
||||
if (BuildConfig.DEBUG) { |
|
||||
Log.e(TAG, "", e); |
|
||||
} |
|
||||
} |
|
||||
return result; |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
protected void onPostExecute(final LocationModel result) { |
|
||||
if (fetchListener != null) fetchListener.onResult(result); |
|
||||
} |
|
||||
} |
|
@ -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,14 @@ |
|||||
|
package awais.instagrabber.fragments.settings; |
||||
|
|
||||
|
import android.content.Context; |
||||
|
|
||||
|
import androidx.fragment.app.FragmentManager; |
||||
|
import androidx.preference.Preference; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
public interface IFlavorSettings { |
||||
|
List<Preference> getPreferences(Context context, |
||||
|
FragmentManager childFragmentManager, |
||||
|
SettingCategory settingCategory); |
||||
|
} |
@ -0,0 +1,6 @@ |
|||||
|
package awais.instagrabber.fragments.settings; |
||||
|
|
||||
|
public enum SettingCategory { |
||||
|
GENERAL, |
||||
|
// add more as and when required |
||||
|
} |
@ -1,56 +0,0 @@ |
|||||
package awais.instagrabber.models; |
|
||||
|
|
||||
import java.io.Serializable; |
|
||||
|
|
||||
public final class LocationModel implements Serializable { |
|
||||
private final long postCount; |
|
||||
private final long id; |
|
||||
private final String name; |
|
||||
private final String bio; |
|
||||
private final String url; |
|
||||
private final String sdProfilePic; |
|
||||
private final String lat; |
|
||||
private final String lng; |
|
||||
|
|
||||
public LocationModel(final long id, |
|
||||
final String name, |
|
||||
final String bio, |
|
||||
final String url, |
|
||||
final String sdProfilePic, |
|
||||
final long postCount, |
|
||||
final String lat, |
|
||||
final String lng) { |
|
||||
this.id = id; |
|
||||
this.name = name; |
|
||||
this.bio = bio; |
|
||||
this.url = url; |
|
||||
this.sdProfilePic = sdProfilePic; |
|
||||
this.postCount = postCount; |
|
||||
this.lat = lat; |
|
||||
this.lng = lng; |
|
||||
} |
|
||||
|
|
||||
public long getId() { |
|
||||
return id; |
|
||||
} |
|
||||
|
|
||||
public String getName() { |
|
||||
return name; |
|
||||
} |
|
||||
|
|
||||
public String getBio() { |
|
||||
return bio; |
|
||||
} |
|
||||
|
|
||||
public String getUrl() { |
|
||||
return url; |
|
||||
} |
|
||||
|
|
||||
public String getGeo() { return "geo:" + lat + "," + lng + "?z=17&q=" + lat + "," + lng + "(" + name + ")"; } |
|
||||
|
|
||||
public String getSdProfilePic() { |
|
||||
return sdProfilePic; |
|
||||
} |
|
||||
|
|
||||
public Long getPostCount() { return postCount; } |
|
||||
} |
|
@ -1,51 +0,0 @@ |
|||||
package awais.instagrabber.models; |
|
||||
|
|
||||
import androidx.annotation.NonNull; |
|
||||
|
|
||||
import awais.instagrabber.models.enums.SuggestionType; |
|
||||
|
|
||||
public final class SuggestionModel implements Comparable<SuggestionModel> { |
|
||||
private final int position; |
|
||||
private final boolean isVerified; |
|
||||
private final String username, name, profilePic; |
|
||||
private final SuggestionType suggestionType; |
|
||||
|
|
||||
public SuggestionModel(final boolean isVerified, final String username, final String name, final String profilePic, |
|
||||
final SuggestionType suggestionType, final int position) { |
|
||||
this.isVerified = isVerified; |
|
||||
this.username = username; |
|
||||
this.name = name; |
|
||||
this.profilePic = profilePic; |
|
||||
this.suggestionType = suggestionType; |
|
||||
this.position = position; |
|
||||
} |
|
||||
|
|
||||
public boolean isVerified() { |
|
||||
return isVerified; |
|
||||
} |
|
||||
|
|
||||
public String getUsername() { |
|
||||
return username; |
|
||||
} |
|
||||
|
|
||||
public String getName() { |
|
||||
return name; |
|
||||
} |
|
||||
|
|
||||
public String getProfilePic() { |
|
||||
return profilePic; |
|
||||
} |
|
||||
|
|
||||
public SuggestionType getSuggestionType() { |
|
||||
return suggestionType; |
|
||||
} |
|
||||
|
|
||||
public int getPosition() { |
|
||||
return position; |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public int compareTo(@NonNull final SuggestionModel model) { |
|
||||
return Integer.compare(getPosition(), model.getPosition()); |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,14 @@ |
|||||
|
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,43 @@ |
|||||
|
package awais.instagrabber.repositories.responses; |
||||
|
|
||||
|
public class Place { |
||||
|
private final Location location; |
||||
|
// for search |
||||
|
private final String title; // those are repeated within location |
||||
|
private final String subtitle; // address |
||||
|
private final String slug; // browser only; for end of address |
||||
|
// for location info |
||||
|
private final String status; |
||||
|
|
||||
|
public Place(final Location location, |
||||
|
final String title, |
||||
|
final String subtitle, |
||||
|
final String slug, |
||||
|
final String status) { |
||||
|
this.location = location; |
||||
|
this.title = title; |
||||
|
this.subtitle = subtitle; |
||||
|
this.slug = slug; |
||||
|
this.status = status; |
||||
|
} |
||||
|
|
||||
|
public Location getLocation() { |
||||
|
return location; |
||||
|
} |
||||
|
|
||||
|
public String getTitle() { |
||||
|
return title; |
||||
|
} |
||||
|
|
||||
|
public String getSubtitle() { |
||||
|
return subtitle; |
||||
|
} |
||||
|
|
||||
|
public String getSlug() { |
||||
|
return slug; |
||||
|
} |
||||
|
|
||||
|
public String getStatus() { |
||||
|
return status; |
||||
|
} |
||||
|
} |
@ -1,4 +1,4 @@ |
|||||
package awais.instagrabber.repositories.responses; |
|
||||
|
package awais.instagrabber.repositories.responses.feed; |
||||
|
|
||||
import java.io.Serializable; |
import java.io.Serializable; |
||||
import java.util.Objects; |
import java.util.Objects; |
@ -1,9 +1,11 @@ |
|||||
package awais.instagrabber.repositories.responses; |
|
||||
|
package awais.instagrabber.repositories.responses.feed; |
||||
|
|
||||
import java.io.Serializable; |
import java.io.Serializable; |
||||
import java.util.List; |
import java.util.List; |
||||
import java.util.Objects; |
import java.util.Objects; |
||||
|
|
||||
|
import awais.instagrabber.repositories.responses.Media; |
||||
|
|
||||
public class EndOfFeedGroup implements Serializable { |
public class EndOfFeedGroup implements Serializable { |
||||
private final String id; |
private final String id; |
||||
private final String title; |
private final String title; |
@ -1,4 +1,4 @@ |
|||||
package awais.instagrabber.repositories.responses; |
|
||||
|
package awais.instagrabber.repositories.responses.feed; |
||||
|
|
||||
import java.io.Serializable; |
import java.io.Serializable; |
||||
import java.util.List; |
import java.util.List; |
@ -1,7 +1,9 @@ |
|||||
package awais.instagrabber.repositories.responses; |
|
||||
|
package awais.instagrabber.repositories.responses.feed; |
||||
|
|
||||
import java.util.List; |
import java.util.List; |
||||
|
|
||||
|
import awais.instagrabber.repositories.responses.Media; |
||||
|
|
||||
public class FeedFetchResponse { |
public class FeedFetchResponse { |
||||
private final List<Media> items; |
private final List<Media> items; |
||||
private final int numResults; |
private final int numResults; |
@ -1,4 +1,4 @@ |
|||||
package awais.instagrabber.repositories.responses; |
|
||||
|
package awais.instagrabber.repositories.responses.notification; |
||||
|
|
||||
import awais.instagrabber.models.enums.NotificationType; |
import awais.instagrabber.models.enums.NotificationType; |
||||
|
|
@ -1,6 +1,4 @@ |
|||||
package awais.instagrabber.repositories.responses; |
|
||||
|
|
||||
import androidx.annotation.NonNull; |
|
||||
|
package awais.instagrabber.repositories.responses.notification; |
||||
|
|
||||
public class NotificationCounts { |
public class NotificationCounts { |
||||
private final int commentLikes; |
private final int commentLikes; |
@ -1,4 +1,4 @@ |
|||||
package awais.instagrabber.repositories.responses; |
|
||||
|
package awais.instagrabber.repositories.responses.notification; |
||||
|
|
||||
public class NotificationImage { |
public class NotificationImage { |
||||
private final String id; |
private final String id; |
@ -0,0 +1,38 @@ |
|||||
|
package awais.instagrabber.repositories.responses.search; |
||||
|
|
||||
|
import awais.instagrabber.repositories.responses.Hashtag; |
||||
|
import awais.instagrabber.repositories.responses.Place; |
||||
|
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,46 @@ |
|||||
|
package awais.instagrabber.repositories.responses.search; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
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; |
||||
|
} |
||||
|
} |
@ -1,6 +1,8 @@ |
|||||
package awais.instagrabber.utils; |
package awais.instagrabber.utils; |
||||
|
|
||||
public final class Constants { |
public final class Constants { |
||||
|
public static final String CRASH_REPORT_EMAIL = "[email protected]"; |
||||
|
|
||||
// string prefs |
// string prefs |
||||
public static final String FOLDER_PATH = "custom_path"; |
public static final String FOLDER_PATH = "custom_path"; |
||||
public static final String DATE_TIME_FORMAT = "date_time_format"; |
public static final String DATE_TIME_FORMAT = "date_time_format"; |
||||
|
@ -0,0 +1,45 @@ |
|||||
|
package awais.instagrabber.webservices; |
||||
|
|
||||
|
import com.google.common.collect.ImmutableMap; |
||||
|
|
||||
|
import awais.instagrabber.repositories.SearchRepository; |
||||
|
import awais.instagrabber.repositories.responses.search.SearchResponse; |
||||
|
import retrofit2.Call; |
||||
|
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()); |
||||
|
} |
||||
|
} |
@ -1,50 +1,35 @@ |
|||||
package awaisomereport; |
package awaisomereport; |
||||
|
|
||||
import android.app.Application; |
import android.app.Application; |
||||
import android.content.Context; |
|
||||
import android.content.Intent; |
|
||||
import android.os.Build; |
|
||||
import android.os.Process; |
|
||||
import android.util.Log; |
|
||||
|
|
||||
import androidx.annotation.NonNull; |
import androidx.annotation.NonNull; |
||||
import androidx.core.content.FileProvider; |
|
||||
|
|
||||
import java.io.BufferedReader; |
|
||||
import java.io.File; |
|
||||
import java.io.FileInputStream; |
|
||||
import java.io.FileOutputStream; |
|
||||
import java.io.FileReader; |
|
||||
import java.io.PrintWriter; |
|
||||
import java.io.StringWriter; |
|
||||
import java.io.Writer; |
|
||||
import java.util.Date; |
|
||||
//import java.util.zip.ZipEntry; |
|
||||
//import java.util.zip.ZipOutputStream; |
|
||||
|
|
||||
import awais.instagrabber.BuildConfig; |
|
||||
import awais.instagrabber.utils.Utils; |
|
||||
|
|
||||
public final class CrashReporter implements Thread.UncaughtExceptionHandler { |
public final class CrashReporter implements Thread.UncaughtExceptionHandler { |
||||
|
private static final String TAG = CrashReporter.class.getSimpleName(); |
||||
|
|
||||
private static CrashReporter reporterInstance; |
private static CrashReporter reporterInstance; |
||||
private final Application application; |
|
||||
private final String email; |
|
||||
// private final File crashLogsZip; |
|
||||
|
|
||||
|
// private final File crashLogsZip; |
||||
|
private final CrashHandler crashHandler; |
||||
|
|
||||
private boolean startAttempted = false; |
private boolean startAttempted = false; |
||||
|
private Thread.UncaughtExceptionHandler defaultEH; |
||||
|
|
||||
public static CrashReporter get(final Application application) { |
public static CrashReporter get(final Application application) { |
||||
if (reporterInstance == null) reporterInstance = new CrashReporter(application); |
|
||||
|
if (reporterInstance == null) { |
||||
|
reporterInstance = new CrashReporter(application); |
||||
|
} |
||||
return reporterInstance; |
return reporterInstance; |
||||
} |
} |
||||
|
|
||||
private CrashReporter(@NonNull final Application application) { |
private CrashReporter(@NonNull final Application application) { |
||||
this.application = application; |
|
||||
this.email = "[email protected]"; |
|
||||
// this.crashLogsZip = new File(application.getExternalCacheDir(), "crash_logs.zip"); |
|
||||
|
crashHandler = new CrashHandler(application); |
||||
|
// this.crashLogsZip = new File(application.getExternalCacheDir(), "crash_logs.zip"); |
||||
} |
} |
||||
|
|
||||
public void start() { |
public void start() { |
||||
if (!startAttempted) { |
if (!startAttempted) { |
||||
|
defaultEH = Thread.getDefaultUncaughtExceptionHandler(); |
||||
Thread.setDefaultUncaughtExceptionHandler(this); |
Thread.setDefaultUncaughtExceptionHandler(this); |
||||
startAttempted = true; |
startAttempted = true; |
||||
} |
} |
||||
@ -52,140 +37,10 @@ public final class CrashReporter implements Thread.UncaughtExceptionHandler { |
|||||
|
|
||||
@Override |
@Override |
||||
public void uncaughtException(@NonNull final Thread t, @NonNull final Throwable exception) { |
public void uncaughtException(@NonNull final Thread t, @NonNull final Throwable exception) { |
||||
final StringBuilder reportBuilder = new StringBuilder(); |
|
||||
reportBuilder.append("IMPORTANT: If sending by email, your email address and the entire content will be made public on GitHub issues."); |
|
||||
reportBuilder.append("\r\nIMPORTANT: When possible, please describe the steps leading to this crash. Thank you for your cooperation."); |
|
||||
reportBuilder.append("\r\n\r\nError report collected on: ").append(new Date().toString()); |
|
||||
|
|
||||
reportBuilder |
|
||||
.append("\r\n\r\nInformation:\r\n==============") |
|
||||
.append("\r\nVERSION : ").append(BuildConfig.VERSION_NAME) |
|
||||
.append("\r\nVERSION_CODE : ").append(BuildConfig.VERSION_CODE) |
|
||||
.append("\r\nPHONE-MODEL : ").append(Build.MODEL) |
|
||||
.append("\r\nANDROID_VERS : ").append(Build.VERSION.RELEASE) |
|
||||
.append("\r\nANDROID_REL : ").append(Build.VERSION.SDK_INT) |
|
||||
.append("\r\nBRAND : ").append(Build.BRAND) |
|
||||
.append("\r\nMANUFACTURER : ").append(Build.MANUFACTURER) |
|
||||
.append("\r\nBOARD : ").append(Build.BOARD) |
|
||||
.append("\r\nDEVICE : ").append(Build.DEVICE) |
|
||||
.append("\r\nPRODUCT : ").append(Build.PRODUCT) |
|
||||
.append("\r\nHOST : ").append(Build.HOST) |
|
||||
.append("\r\nTAGS : ").append(Build.TAGS); |
|
||||
|
|
||||
reportBuilder.append("\r\n\r\nStack:\r\n==============\r\n"); |
|
||||
final Writer result = new StringWriter(); |
|
||||
try (final PrintWriter printWriter = new PrintWriter(result)) { |
|
||||
exception.printStackTrace(printWriter); |
|
||||
reportBuilder.append(result.toString()); |
|
||||
|
|
||||
reportBuilder.append("\r\nCause:\r\n=============="); |
|
||||
|
|
||||
// for AsyncTask crashes |
|
||||
Throwable cause = exception.getCause(); |
|
||||
while (cause != null) { |
|
||||
cause.printStackTrace(printWriter); |
|
||||
reportBuilder.append(result.toString()); |
|
||||
cause = cause.getCause(); |
|
||||
} |
|
||||
} |
|
||||
reportBuilder.append("\r\n\r\n**** End of current Report ***"); |
|
||||
|
|
||||
final String errorContent = reportBuilder.toString(); |
|
||||
try (final FileOutputStream trace = application.openFileOutput("stack-" + System.currentTimeMillis() + ".stacktrace", Context.MODE_PRIVATE)) { |
|
||||
trace.write(errorContent.getBytes()); |
|
||||
} catch (final Exception ex) { |
|
||||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", ex); |
|
||||
} |
|
||||
|
|
||||
application.startActivity(new Intent(application, ErrorReporterActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); |
|
||||
|
|
||||
// zipLogs(); |
|
||||
|
|
||||
Process.killProcess(Process.myPid()); |
|
||||
System.exit(10); |
|
||||
} |
|
||||
|
|
||||
// public synchronized CrashReporter zipLogs() { |
|
||||
// final File logDir = Utils.logCollector != null ? Utils.logCollector.getLogDir() : |
|
||||
// new File(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? application.getDataDir() : application.getFilesDir(), "crashlogs"); |
|
||||
// |
|
||||
// try (final FileOutputStream fos = new FileOutputStream(crashLogsZip); |
|
||||
// final ZipOutputStream zos = new ZipOutputStream(fos)) { |
|
||||
// |
|
||||
// final File[] files = logDir.listFiles(); |
|
||||
// |
|
||||
// if (files != null) { |
|
||||
// zos.setLevel(5); |
|
||||
// byte[] buffer; |
|
||||
// for (final File file : files) { |
|
||||
// if (file != null && file.length() > 0) { |
|
||||
// buffer = new byte[1024]; |
|
||||
// try (final FileInputStream fis = new FileInputStream(file)) { |
|
||||
// zos.putNextEntry(new ZipEntry(file.getName())); |
|
||||
// int length; |
|
||||
// while ((length = fis.read(buffer)) > 0) zos.write(buffer, 0, length); |
|
||||
// zos.closeEntry(); |
|
||||
// } |
|
||||
// } |
|
||||
// } |
|
||||
// } |
|
||||
// |
|
||||
// } catch (final Exception e) { |
|
||||
// if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); |
|
||||
// } |
|
||||
// |
|
||||
// return this; |
|
||||
// } |
|
||||
|
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored") |
|
||||
public void startCrashEmailIntent(final Context context) { |
|
||||
try { |
|
||||
final String filePath = context.getFilesDir().getAbsolutePath(); |
|
||||
|
|
||||
String[] errorFileList; |
|
||||
|
|
||||
try { |
|
||||
final File dir = new File(filePath); |
|
||||
if (dir.exists() && !dir.isDirectory()) dir.delete(); |
|
||||
dir.mkdir(); |
|
||||
errorFileList = dir.list((d, name) -> name.endsWith(".stacktrace")); |
|
||||
} catch (final Exception e) { |
|
||||
errorFileList = null; |
|
||||
} |
|
||||
|
|
||||
if (errorFileList != null && errorFileList.length > 0) { |
|
||||
final StringBuilder errorStringBuilder; |
|
||||
|
|
||||
errorStringBuilder = new StringBuilder("\r\n\r\n"); |
|
||||
final int maxSendMail = 5; |
|
||||
|
|
||||
int curIndex = 0; |
|
||||
for (final String curString : errorFileList) { |
|
||||
final File file = new File(filePath + '/' + curString); |
|
||||
|
|
||||
if (curIndex++ <= maxSendMail) { |
|
||||
errorStringBuilder.append("New Trace collected:\r\n=====================\r\n"); |
|
||||
try (final BufferedReader input = new BufferedReader(new FileReader(file))) { |
|
||||
String line; |
|
||||
while ((line = input.readLine()) != null) |
|
||||
errorStringBuilder.append(line).append("\r\n"); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
file.delete(); |
|
||||
} |
|
||||
|
|
||||
errorStringBuilder.append("\r\n\r\n"); |
|
||||
|
|
||||
context.startActivity(Intent.createChooser(new Intent(Intent.ACTION_SEND).setType("message/rfc822") |
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION) |
|
||||
.putExtra(Intent.EXTRA_EMAIL, new String[]{email}) |
|
||||
// .putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(application, BuildConfig.APPLICATION_ID + ".provider", crashLogsZip)) |
|
||||
.putExtra(Intent.EXTRA_SUBJECT, "Barinsta Crash Report") |
|
||||
.putExtra(Intent.EXTRA_TEXT, errorStringBuilder.toString()), "Select an email app to send crash logs")); |
|
||||
} |
|
||||
} catch (final Exception e) { |
|
||||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); |
|
||||
|
if (crashHandler == null) { |
||||
|
defaultEH.uncaughtException(t, exception); |
||||
|
return; |
||||
} |
} |
||||
|
crashHandler.uncaughtException(t, exception, defaultEH); |
||||
} |
} |
||||
} |
} |
@ -0,0 +1,134 @@ |
|||||
|
package awaisomereport; |
||||
|
|
||||
|
import android.app.Application; |
||||
|
import android.content.Context; |
||||
|
import android.content.Intent; |
||||
|
import android.content.res.Resources; |
||||
|
import android.os.Build; |
||||
|
import android.util.Log; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
|
||||
|
import java.io.BufferedReader; |
||||
|
import java.io.File; |
||||
|
import java.io.FileOutputStream; |
||||
|
import java.io.FileReader; |
||||
|
import java.io.PrintWriter; |
||||
|
import java.io.StringWriter; |
||||
|
import java.io.Writer; |
||||
|
import java.util.Date; |
||||
|
|
||||
|
import awais.instagrabber.BuildConfig; |
||||
|
import awais.instagrabber.R; |
||||
|
import awais.instagrabber.utils.Constants; |
||||
|
|
||||
|
public final class CrashReporterHelper { |
||||
|
private static final String TAG = CrashReporterHelper.class.getSimpleName(); |
||||
|
|
||||
|
public static void startErrorReporterActivity(@NonNull final Application application, |
||||
|
@NonNull final Throwable exception) { |
||||
|
final StringBuilder reportBuilder = new StringBuilder(); |
||||
|
reportBuilder.append("IMPORTANT: If sending by email, your email address and the entire content will be made public on GitHub issues.") |
||||
|
.append("\r\nIMPORTANT: When possible, please describe the steps leading to this crash. Thank you for your cooperation.") |
||||
|
.append("\r\n\r\nError report collected on: ").append(new Date().toString()) |
||||
|
.append("\r\n\r\nInformation:\r\n==============") |
||||
|
.append("\r\nVERSION : ").append(BuildConfig.VERSION_NAME) |
||||
|
.append("\r\nVERSION_CODE : ").append(BuildConfig.VERSION_CODE) |
||||
|
.append("\r\nPHONE-MODEL : ").append(Build.MODEL) |
||||
|
.append("\r\nANDROID_VERS : ").append(Build.VERSION.RELEASE) |
||||
|
.append("\r\nANDROID_REL : ").append(Build.VERSION.SDK_INT) |
||||
|
.append("\r\nBRAND : ").append(Build.BRAND) |
||||
|
.append("\r\nMANUFACTURER : ").append(Build.MANUFACTURER) |
||||
|
.append("\r\nBOARD : ").append(Build.BOARD) |
||||
|
.append("\r\nDEVICE : ").append(Build.DEVICE) |
||||
|
.append("\r\nPRODUCT : ").append(Build.PRODUCT) |
||||
|
.append("\r\nHOST : ").append(Build.HOST) |
||||
|
.append("\r\nTAGS : ").append(Build.TAGS); |
||||
|
|
||||
|
reportBuilder.append("\r\n\r\nStack:\r\n==============\r\n"); |
||||
|
final Writer result = new StringWriter(); |
||||
|
try (final PrintWriter printWriter = new PrintWriter(result)) { |
||||
|
exception.printStackTrace(printWriter); |
||||
|
reportBuilder.append(result.toString()); |
||||
|
reportBuilder.append("\r\nCause:\r\n=============="); |
||||
|
// for AsyncTask crashes |
||||
|
Throwable cause = exception.getCause(); |
||||
|
while (cause != null) { |
||||
|
cause.printStackTrace(printWriter); |
||||
|
reportBuilder.append(result.toString()); |
||||
|
cause = cause.getCause(); |
||||
|
} |
||||
|
} |
||||
|
reportBuilder.append("\r\n\r\n**** End of current Report ***"); |
||||
|
|
||||
|
final String errorContent = reportBuilder.toString(); |
||||
|
try (final FileOutputStream trace = application.openFileOutput("stack-" + System.currentTimeMillis() + ".stacktrace", Context.MODE_PRIVATE)) { |
||||
|
trace.write(errorContent.getBytes()); |
||||
|
} catch (final Exception ex) { |
||||
|
if (BuildConfig.DEBUG) Log.e(TAG, "", ex); |
||||
|
} |
||||
|
|
||||
|
application.startActivity(new Intent(application, ErrorReporterActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); |
||||
|
} |
||||
|
|
||||
|
public static void startCrashEmailIntent(final Context context) { |
||||
|
try { |
||||
|
final String filePath = context.getFilesDir().getAbsolutePath(); |
||||
|
|
||||
|
String[] errorFileList; |
||||
|
|
||||
|
try { |
||||
|
final File dir = new File(filePath); |
||||
|
if (dir.exists() && !dir.isDirectory()) { |
||||
|
//noinspection ResultOfMethodCallIgnored |
||||
|
dir.delete(); |
||||
|
} |
||||
|
//noinspection ResultOfMethodCallIgnored |
||||
|
dir.mkdirs(); |
||||
|
errorFileList = dir.list((d, name) -> name.endsWith(".stacktrace")); |
||||
|
} catch (final Exception e) { |
||||
|
errorFileList = null; |
||||
|
} |
||||
|
|
||||
|
if (errorFileList == null || errorFileList.length <= 0) { |
||||
|
return; |
||||
|
} |
||||
|
final StringBuilder errorStringBuilder; |
||||
|
|
||||
|
errorStringBuilder = new StringBuilder("\r\n\r\n"); |
||||
|
final int maxSendMail = 5; |
||||
|
int curIndex = 0; |
||||
|
for (final String curString : errorFileList) { |
||||
|
final File file = new File(filePath + '/' + curString); |
||||
|
|
||||
|
if (curIndex++ <= maxSendMail) { |
||||
|
errorStringBuilder.append("New Trace collected:\r\n=====================\r\n"); |
||||
|
try (final BufferedReader input = new BufferedReader(new FileReader(file))) { |
||||
|
String line; |
||||
|
while ((line = input.readLine()) != null) |
||||
|
errorStringBuilder.append(line).append("\r\n"); |
||||
|
} |
||||
|
} |
||||
|
//noinspection ResultOfMethodCallIgnored |
||||
|
file.delete(); |
||||
|
} |
||||
|
|
||||
|
errorStringBuilder.append("\r\n\r\n"); |
||||
|
final Resources resources = context.getResources(); |
||||
|
context.startActivity(Intent.createChooser( |
||||
|
new Intent(Intent.ACTION_SEND) |
||||
|
.setType("message/rfc822") |
||||
|
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
||||
|
| Intent.FLAG_GRANT_READ_URI_PERMISSION |
||||
|
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION) |
||||
|
.putExtra(Intent.EXTRA_EMAIL, new String[]{Constants.CRASH_REPORT_EMAIL}) |
||||
|
// .putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(application, BuildConfig.APPLICATION_ID + ".provider", crashLogsZip)) |
||||
|
.putExtra(Intent.EXTRA_SUBJECT, resources.getString(R.string.crash_report_subject)) |
||||
|
.putExtra(Intent.EXTRA_TEXT, errorStringBuilder.toString()), |
||||
|
context.getResources().getString(R.string.crash_report_title)) |
||||
|
); |
||||
|
} catch (final Exception e) { |
||||
|
Log.e(TAG, "", e); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,7 @@ |
|||||
|
package awaisomereport; |
||||
|
|
||||
|
public interface ICrashHandler { |
||||
|
void uncaughtException(Thread t, |
||||
|
Throwable exception, |
||||
|
Thread.UncaughtExceptionHandler defaultEH); |
||||
|
} |
After Width: 132 | Height: 132 | Size: 5.5 KiB |
@ -0,0 +1,10 @@ |
|||||
|
<?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/pending_requests" |
||||
|
android:icon="@drawable/ic_account_clock_24" |
||||
|
android:title="@string/pending_requests" |
||||
|
android:visible="false" |
||||
|
app:showAsAction="always" /> |
||||
|
</menu> |
@ -1,3 +1,5 @@ |
|||||
files: |
files: |
||||
- source: '/app/src/main/res/values/[arrays][strings][!styles]' |
- source: '/app/src/main/res/values/[arrays][strings][!styles]' |
||||
translation: /app/src/main/res/values-%two_letters_code%/%original_file_name% |
translation: /app/src/main/res/values-%two_letters_code%/%original_file_name% |
||||
|
- source: '/app/src/github/res/values/[arrays][strings][!styles]' |
||||
|
translation: /app/src/github/res/values-%two_letters_code%/%original_file_name% |
Write
Preview
Loading…
Cancel
Save
Reference in new issue