diff --git a/.gitignore b/.gitignore index 4d79357f..d849b086 100755 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ .externalNativeBuild .cxx app/release +/sentry.properties \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index e77a6ace..493366c3 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,6 @@ apply plugin: 'com.android.application' apply plugin: "androidx.navigation.safeargs" +apply from: 'sentry.gradle' android { compileSdkVersion 29 @@ -48,8 +49,22 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } -} + flavorDimensions "repo" + + productFlavors { + github { + dimension "repo" + versionNameSuffix "-github" + buildConfigField("String", "dsn", SENTRY_DSN) + } + + fdroid { + dimension "repo" + versionNameSuffix "-fdroid" + } + } +} configurations.all { resolutionStrategy.cacheChangingModulesFor 0, 'seconds' @@ -81,7 +96,7 @@ dependencies { implementation 'androidx.palette:palette:1.0.0' implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" - implementation 'com.google.guava:guava:27.1-jre' + implementation 'com.google.guava:guava:27.0.1-android' // Room def room_version = "2.2.6" @@ -116,6 +131,7 @@ dependencies { debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.6' - + githubImplementation 'io.sentry:sentry-android:4.3.0' + testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1' } diff --git a/app/sentry.gradle b/app/sentry.gradle new file mode 100644 index 00000000..f2b24aa7 --- /dev/null +++ b/app/sentry.gradle @@ -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) +} \ No newline at end of file diff --git a/app/src/fdroid/java/awais/instagrabber/fragments/settings/FlavorSettings.java b/app/src/fdroid/java/awais/instagrabber/fragments/settings/FlavorSettings.java new file mode 100644 index 00000000..b30e263e --- /dev/null +++ b/app/src/fdroid/java/awais/instagrabber/fragments/settings/FlavorSettings.java @@ -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 getPreferences(@NonNull final Context context, + @NonNull final FragmentManager fragmentManager, + @NonNull final SettingCategory settingCategory) { + // switch (settingCategory) { + // default: + // break; + // } + return Collections.emptyList(); + } +} diff --git a/app/src/fdroid/java/awaisomereport/CrashHandler.java b/app/src/fdroid/java/awaisomereport/CrashHandler.java new file mode 100644 index 00000000..2f27d1c3 --- /dev/null +++ b/app/src/fdroid/java/awaisomereport/CrashHandler.java @@ -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; + // } +} diff --git a/app/src/github/AndroidManifest.xml b/app/src/github/AndroidManifest.xml new file mode 100644 index 00000000..4c6a6fe0 --- /dev/null +++ b/app/src/github/AndroidManifest.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/github/java/awais/instagrabber/fragments/settings/FlavorSettings.java b/app/src/github/java/awais/instagrabber/fragments/settings/FlavorSettings.java new file mode 100644 index 00000000..85adb8a9 --- /dev/null +++ b/app/src/github/java/awais/instagrabber/fragments/settings/FlavorSettings.java @@ -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 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 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; + }); + } +} diff --git a/app/src/github/java/awaisomereport/CrashHandler.java b/app/src/github/java/awaisomereport/CrashHandler.java new file mode 100644 index 00000000..cd28eaa2 --- /dev/null +++ b/app/src/github/java/awaisomereport/CrashHandler.java @@ -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); + } +} diff --git a/app/src/github/res/values/strings.xml b/app/src/github/res/values/strings.xml new file mode 100644 index 00000000..6bc4c7a4 --- /dev/null +++ b/app/src/github/res/values/strings.xml @@ -0,0 +1,6 @@ + + + Enable Sentry + Sentry is a listener/handler for errors that asynchronously sends out the error/event to Sentry.io + Sentry will start on next launch + \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/InstaGrabberApplication.java b/app/src/main/java/awais/instagrabber/InstaGrabberApplication.java index b90c8343..a033473d 100644 --- a/app/src/main/java/awais/instagrabber/InstaGrabberApplication.java +++ b/app/src/main/java/awais/instagrabber/InstaGrabberApplication.java @@ -18,14 +18,12 @@ import awais.instagrabber.utils.LocaleUtils; import awais.instagrabber.utils.SettingsHelper; import awais.instagrabber.utils.TextUtils; import awaisomereport.CrashReporter; -//import awaisomereport.LogCollector; import static awais.instagrabber.utils.CookieUtils.NET_COOKIE_MANAGER; import static awais.instagrabber.utils.Utils.applicationHandler; import static awais.instagrabber.utils.Utils.cacheDir; import static awais.instagrabber.utils.Utils.clipboardManager; import static awais.instagrabber.utils.Utils.datetimeParser; -//import static awais.instagrabber.utils.Utils.logCollector; import static awais.instagrabber.utils.Utils.settingsHelper; public final class InstaGrabberApplication extends Application { @@ -34,16 +32,16 @@ public final class InstaGrabberApplication extends Application { @Override public void onCreate() { super.onCreate(); - // final Set requestListeners = new HashSet<>(); - // requestListeners.add(new RequestLoggingListener()); - final ImagePipelineConfig imagePipelineConfig = ImagePipelineConfig - .newBuilder(this) - // .setMainDiskCacheConfig(diskCacheConfig) - // .setRequestListeners(requestListeners) - .setDownsampleEnabled(true) - .build(); - Fresco.initialize(this, imagePipelineConfig); - // FLog.setMinimumLoggingLevel(FLog.VERBOSE); + CookieHandler.setDefault(NET_COOKIE_MANAGER); + + if (settingsHelper == null) { + settingsHelper = new SettingsHelper(this); + } + + if (!BuildConfig.DEBUG) { + CrashReporter.get(this).start(); + } + // logCollector = new LogCollector(this); if (BuildConfig.DEBUG) { try { @@ -55,13 +53,16 @@ public final class InstaGrabberApplication extends Application { } } - if (!BuildConfig.DEBUG) CrashReporter.get(this).start(); -// logCollector = new LogCollector(this); - - CookieHandler.setDefault(NET_COOKIE_MANAGER); - - if (settingsHelper == null) - settingsHelper = new SettingsHelper(this); + // final Set requestListeners = new HashSet<>(); + // requestListeners.add(new RequestLoggingListener()); + final ImagePipelineConfig imagePipelineConfig = ImagePipelineConfig + .newBuilder(this) + // .setMainDiskCacheConfig(diskCacheConfig) + // .setRequestListeners(requestListeners) + .setDownsampleEnabled(true) + .build(); + Fresco.initialize(this, imagePipelineConfig); + // FLog.setMinimumLoggingLevel(FLog.VERBOSE); if (applicationHandler == null) { applicationHandler = new Handler(getApplicationContext().getMainLooper()); diff --git a/app/src/main/java/awais/instagrabber/activities/MainActivity.java b/app/src/main/java/awais/instagrabber/activities/MainActivity.java index d6540585..fe5fa220 100644 --- a/app/src/main/java/awais/instagrabber/activities/MainActivity.java +++ b/app/src/main/java/awais/instagrabber/activities/MainActivity.java @@ -10,7 +10,6 @@ import android.content.Intent; import android.content.ServiceConnection; import android.database.MatrixCursor; import android.net.Uri; -import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Handler; @@ -53,6 +52,7 @@ import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterators; +import java.util.ArrayList; import java.util.Deque; import java.util.List; import java.util.stream.Collectors; @@ -60,18 +60,17 @@ import java.util.stream.Collectors; import awais.instagrabber.R; import awais.instagrabber.adapters.SuggestionsAdapter; import awais.instagrabber.asyncs.PostFetcher; -import awais.instagrabber.asyncs.SuggestionsFetcher; import awais.instagrabber.customviews.emoji.EmojiVariantManager; import awais.instagrabber.databinding.ActivityMainBinding; import awais.instagrabber.fragments.PostViewV2Fragment; import awais.instagrabber.fragments.directmessages.DirectMessageInboxFragmentDirections; import awais.instagrabber.fragments.main.FeedFragment; import awais.instagrabber.fragments.settings.PreferenceKeys; -import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.models.IntentModel; -import awais.instagrabber.models.SuggestionModel; import awais.instagrabber.models.Tab; import awais.instagrabber.models.enums.SuggestionType; +import awais.instagrabber.repositories.responses.search.SearchItem; +import awais.instagrabber.repositories.responses.search.SearchResponse; import awais.instagrabber.services.ActivityCheckerService; import awais.instagrabber.services.DMSyncAlarmReceiver; import awais.instagrabber.utils.AppExecutors; @@ -83,6 +82,10 @@ import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.emoji.EmojiParser; import awais.instagrabber.viewmodels.AppStateViewModel; +import awais.instagrabber.webservices.SearchService; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; import static awais.instagrabber.utils.NavigationExtensions.setupWithNavController; import static awais.instagrabber.utils.Utils.settingsHelper; @@ -105,6 +108,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage private SuggestionsAdapter suggestionAdapter; private AutoCompleteTextView searchAutoComplete; private SearchView searchView; + private SearchService searchService; private boolean showSearch = true; private Handler suggestionsFetchHandler; private int firstFragmentGraphIndex; @@ -167,6 +171,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage EmojiVariantManager.getInstance(); }); initEmojiCompat(); + searchService = SearchService.getInstance(); // initDmService(); } @@ -300,7 +305,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage final Bundle bundle = new Bundle(); switch (type) { case TYPE_LOCATION: - bundle.putString("locationId", query); + bundle.putLong("locationId", Long.valueOf(query)); navController.navigate(R.id.action_global_locationFragment, bundle); break; case TYPE_HASHTAG: @@ -328,51 +333,84 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { private boolean searchUser; private boolean searchHash; - private AsyncTask prevSuggestionAsync; + private Call prevSuggestionAsync; private final String[] COLUMNS = { BaseColumns._ID, Constants.EXTRAS_USERNAME, Constants.EXTRAS_NAME, Constants.EXTRAS_TYPE, + "query", "pfp", "verified" }; private String currentSearchQuery; - private final FetchListener fetchListener = new FetchListener() { + private final Callback cb = new Callback() { @Override - public void doBefore() { - suggestionAdapter.changeCursor(null); - } - - @Override - public void onResult(final SuggestionModel[] result) { + public void onResponse(@NonNull final Call call, + @NonNull final Response response) { final MatrixCursor cursor; - if (result == null) cursor = null; + final SearchResponse body = response.body(); + if (body == null) { + cursor = null; + return; + } + final List result = new ArrayList<>(); + if (isLoggedIn) { + if (body.getList() != null) result.addAll(searchHash ? body.getList() + .stream() + .filter(i -> i.getUser() == null) + .collect(Collectors.toList()) : body.getList()); + } else { - cursor = new MatrixCursor(COLUMNS, 0); - for (int i = 0; i < result.length; i++) { - final SuggestionModel suggestionModel = result[i]; - if (suggestionModel != null) { - final SuggestionType suggestionType = suggestionModel.getSuggestionType(); - final Object[] objects = { - i, - suggestionType == SuggestionType.TYPE_LOCATION ? suggestionModel.getName() : suggestionModel.getUsername(), - suggestionType == SuggestionType.TYPE_LOCATION ? suggestionModel.getUsername() : suggestionModel.getName(), - suggestionType, - suggestionModel.getProfilePic(), - suggestionModel.isVerified()}; - if (!searchHash && !searchUser) cursor.addRow(objects); - else { - final boolean isCurrHash = suggestionType == SuggestionType.TYPE_HASHTAG; - if (searchHash && isCurrHash || !searchHash && !isCurrHash) - cursor.addRow(objects); - } - } + if (body.getUsers() != null && !searchHash) result.addAll(body.getUsers()); + if (body.getHashtags() != null) result.addAll(body.getHashtags()); + if (body.getPlaces() != null) result.addAll(body.getPlaces()); + } + cursor = new MatrixCursor(COLUMNS, 0); + for (int i = 0; i < result.size(); i++) { + final SearchItem suggestionModel = result.get(i); + if (suggestionModel != null) { + Object[] objects = null; + if (suggestionModel.getUser() != null) + objects = new Object[]{ + suggestionModel.getPosition(), + suggestionModel.getUser().getUsername(), + suggestionModel.getUser().getFullName(), + SuggestionType.TYPE_USER, + suggestionModel.getUser().getUsername(), + suggestionModel.getUser().getProfilePicUrl(), + suggestionModel.getUser().isVerified()}; + else if (suggestionModel.getHashtag() != null) + objects = new Object[]{ + suggestionModel.getPosition(), + suggestionModel.getHashtag().getName(), + suggestionModel.getHashtag().getSubtitle(), + SuggestionType.TYPE_HASHTAG, + suggestionModel.getHashtag().getName(), + "res:/" + R.drawable.ic_hashtag, + false}; + else if (suggestionModel.getPlace() != null) + objects = new Object[]{ + suggestionModel.getPosition(), + suggestionModel.getPlace().getTitle(), + suggestionModel.getPlace().getSubtitle(), + SuggestionType.TYPE_LOCATION, + suggestionModel.getPlace().getLocation().getPk(), + "res:/" + R.drawable.ic_location, + false}; + cursor.addRow(objects); } } suggestionAdapter.changeCursor(cursor); } + + @Override + public void onFailure(@NonNull final Call call, + Throwable t) { + if (!call.isCanceled() && t != null) + Log.e(TAG, "Exception on search:", t); + } }; private final Runnable runnable = () -> { @@ -391,17 +429,19 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage if (searchAutoComplete != null) { searchAutoComplete.setThreshold(1); } - prevSuggestionAsync = new SuggestionsFetcher(fetchListener).executeOnExecutor( - AsyncTask.THREAD_POOL_EXECUTOR, - searchUser || searchHash ? currentSearchQuery.substring(1) - : currentSearchQuery); + prevSuggestionAsync = searchService.search(isLoggedIn, + searchUser || searchHash ? currentSearchQuery.substring(1) + : currentSearchQuery, + searchUser ? "user" : (searchHash ? "hashtag" : "blended")); + suggestionAdapter.changeCursor(null); + prevSuggestionAsync.enqueue(cb); } }; private void cancelSuggestionsAsync() { if (prevSuggestionAsync != null) try { - prevSuggestionAsync.cancel(true); + prevSuggestionAsync.cancel(); } catch (final Exception ignored) {} } @@ -728,7 +768,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage final NavController navController = currentNavControllerLiveData.getValue(); if (navController == null) return; final Bundle bundle = new Bundle(); - bundle.putString("locationId", locationId); + bundle.putLong("locationId", Long.valueOf(locationId)); navController.navigate(R.id.action_global_locationFragment, bundle); } diff --git a/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java b/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java index 8a5c3a4b..80ab465b 100644 --- a/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java +++ b/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java @@ -16,7 +16,7 @@ import java.util.stream.Collectors; import awais.instagrabber.adapters.viewholder.NotificationViewHolder; import awais.instagrabber.databinding.ItemNotificationBinding; import awais.instagrabber.models.enums.NotificationType; -import awais.instagrabber.repositories.responses.Notification; +import awais.instagrabber.repositories.responses.notification.Notification; public final class NotificationsAdapter extends ListAdapter { private final OnNotificationClickListener notificationClickListener; diff --git a/app/src/main/java/awais/instagrabber/adapters/SuggestionsAdapter.java b/app/src/main/java/awais/instagrabber/adapters/SuggestionsAdapter.java index 154e83f2..6c51f1f7 100755 --- a/app/src/main/java/awais/instagrabber/adapters/SuggestionsAdapter.java +++ b/app/src/main/java/awais/instagrabber/adapters/SuggestionsAdapter.java @@ -10,7 +10,6 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.cursoradapter.widget.CursorAdapter; -import awais.instagrabber.R; import awais.instagrabber.databinding.ItemSuggestionBinding; import awais.instagrabber.models.enums.SuggestionType; @@ -35,12 +34,12 @@ public final class SuggestionsAdapter extends CursorAdapter { @Override public void bindView(@NonNull final View view, final Context context, @NonNull final Cursor cursor) { - // i, username, fullname, type, picUrl, verified - // 0, 1 , 2 , 3 , 4 , 5 + // i, username, fullname, type, query, picUrl, verified + // 0, 1 , 2 , 3 , 4 , 5 , 6 final String fullName = cursor.getString(2); String username = cursor.getString(1); - String picUrl = cursor.getString(4); - final boolean verified = cursor.getString(5).charAt(0) == 't'; + String picUrl = cursor.getString(5); + final boolean verified = cursor.getString(6).charAt(0) == 't'; final String type = cursor.getString(3); SuggestionType suggestionType = null; @@ -50,22 +49,14 @@ public final class SuggestionsAdapter extends CursorAdapter { Log.e(TAG, "Unknown suggestion type: " + type, e); } if (suggestionType == null) return; - final String query; + String query = cursor.getString(4); switch (suggestionType) { case TYPE_USER: username = '@' + username; - query = username; break; case TYPE_HASHTAG: username = '#' + username; - query = username; break; - case TYPE_LOCATION: - query = fullName; - picUrl = "res:/" + R.drawable.ic_location; - break; - default: - return; // will never come here } if (onSuggestionClickListener != null) { @@ -75,12 +66,8 @@ public final class SuggestionsAdapter extends CursorAdapter { final ItemSuggestionBinding binding = ItemSuggestionBinding.bind(view); binding.isVerified.setVisibility(verified ? View.VISIBLE : View.GONE); binding.tvUsername.setText(username); - if (suggestionType.equals(SuggestionType.TYPE_LOCATION)) { - binding.tvFullName.setVisibility(View.GONE); - } else { - binding.tvFullName.setVisibility(View.VISIBLE); - binding.tvFullName.setText(fullName); - } + binding.tvFullName.setVisibility(View.VISIBLE); + binding.tvFullName.setText(fullName); binding.ivProfilePic.setImageURI(picUrl); } diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java index 1c896770..153837e8 100644 --- a/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java +++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java @@ -9,8 +9,8 @@ import awais.instagrabber.R; import awais.instagrabber.adapters.NotificationsAdapter.OnNotificationClickListener; import awais.instagrabber.databinding.ItemNotificationBinding; import awais.instagrabber.models.enums.NotificationType; -import awais.instagrabber.repositories.responses.Notification; -import awais.instagrabber.repositories.responses.NotificationArgs; +import awais.instagrabber.repositories.responses.notification.Notification; +import awais.instagrabber.repositories.responses.notification.NotificationArgs; public final class NotificationViewHolder extends RecyclerView.ViewHolder { private final ItemNotificationBinding binding; diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemRavenMediaViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemRavenMediaViewHolder.java index a8fb8dda..6198faf1 100644 --- a/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemRavenMediaViewHolder.java +++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemRavenMediaViewHolder.java @@ -16,7 +16,6 @@ import awais.instagrabber.databinding.LayoutDmBaseBinding; import awais.instagrabber.databinding.LayoutDmRavenMediaBinding; import awais.instagrabber.models.enums.MediaItemType; import awais.instagrabber.models.enums.RavenMediaViewMode; -import awais.instagrabber.repositories.responses.ImageVersions2; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.User; import awais.instagrabber.repositories.responses.directmessages.DirectItem; @@ -24,6 +23,7 @@ import awais.instagrabber.repositories.responses.directmessages.DirectItemVisual import awais.instagrabber.repositories.responses.directmessages.DirectThread; import awais.instagrabber.utils.NumberUtils; import awais.instagrabber.utils.ResponseBodyUtils; +import awais.instagrabber.utils.TextUtils; public class DirectItemRavenMediaViewHolder extends DirectItemViewHolder { @@ -48,7 +48,7 @@ public class DirectItemRavenMediaViewHolder extends DirectItemViewHolder { if (media == null) return; setExpiryInfo(visualMedia); setPreview(visualMedia, messageDirection); - final boolean expired = media.getPk() == null; + final boolean expired = TextUtils.isEmpty(media.getId()); if (expired) return; itemView.setOnClickListener(v -> openMedia(media)); /*final boolean isExpired = visualMedia == null || (mediaModel = visualMedia.getMedia()) == null || @@ -118,7 +118,7 @@ public class DirectItemRavenMediaViewHolder extends DirectItemViewHolder { final RavenMediaViewMode viewMode = visualMedia.getViewMode(); if (viewMode != RavenMediaViewMode.PERMANENT) { final MediaItemType mediaType = media.getMediaType(); - final boolean expired = media.getPk() == null; + final boolean expired = TextUtils.isEmpty(media.getId()); final int info; switch (mediaType) { case MEDIA_TYPE_IMAGE: @@ -153,7 +153,7 @@ public class DirectItemRavenMediaViewHolder extends DirectItemViewHolder { private void setPreview(final DirectItemVisualMedia visualMedia, final MessageDirection messageDirection) { final Media media = visualMedia.getMedia(); - final boolean expired = media.getPk() == null; + final boolean expired = TextUtils.isEmpty(media.getId()); if (expired) { binding.preview.setVisibility(View.GONE); binding.typeIcon.setVisibility(View.GONE); diff --git a/app/src/main/java/awais/instagrabber/asyncs/LocationFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/LocationFetcher.java deleted file mode 100644 index 8aabc107..00000000 --- a/app/src/main/java/awais/instagrabber/asyncs/LocationFetcher.java +++ /dev/null @@ -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 { - private static final String TAG = "LocationFetcher"; - - private final FetchListener fetchListener; - private final long id; - - public LocationFetcher(final long id, final FetchListener 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); - } -} diff --git a/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java index 7613c9d2..274b2314 100644 --- a/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java +++ b/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java @@ -4,7 +4,7 @@ import java.util.List; import awais.instagrabber.customviews.helpers.PostFetcher; import awais.instagrabber.interfaces.FetchListener; -import awais.instagrabber.models.LocationModel; +import awais.instagrabber.repositories.responses.Location; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.PostsFetchResponse; import awais.instagrabber.webservices.GraphQLService; @@ -14,12 +14,12 @@ import awais.instagrabber.webservices.ServiceCallback; public class LocationPostFetchService implements PostFetcher.PostFetchService { private final LocationService locationService; private final GraphQLService graphQLService; - private final LocationModel locationModel; + private final Location locationModel; private String nextMaxId; private boolean moreAvailable; private final boolean isLoggedIn; - public LocationPostFetchService(final LocationModel locationModel, final boolean isLoggedIn) { + public LocationPostFetchService(final Location locationModel, final boolean isLoggedIn) { this.locationModel = locationModel; this.isLoggedIn = isLoggedIn; locationService = isLoggedIn ? LocationService.getInstance() : null; @@ -47,8 +47,8 @@ public class LocationPostFetchService implements PostFetcher.PostFetchService { } } }; - if (isLoggedIn) locationService.fetchPosts(locationModel.getId(), nextMaxId, cb); - else graphQLService.fetchLocationPosts(locationModel.getId(), nextMaxId, cb); + if (isLoggedIn) locationService.fetchPosts(locationModel.getPk(), nextMaxId, cb); + else graphQLService.fetchLocationPosts(locationModel.getPk(), nextMaxId, cb); } @Override diff --git a/app/src/main/java/awais/instagrabber/asyncs/SuggestionsFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/SuggestionsFetcher.java deleted file mode 100755 index c40d0406..00000000 --- a/app/src/main/java/awais/instagrabber/asyncs/SuggestionsFetcher.java +++ /dev/null @@ -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 { - private final FetchListener fetchListener; - - public SuggestionsFetcher(final FetchListener 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 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); - } -} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/dialogs/ConfirmDialogFragment.java b/app/src/main/java/awais/instagrabber/dialogs/ConfirmDialogFragment.java index caba0b38..5b8eb3c9 100644 --- a/app/src/main/java/awais/instagrabber/dialogs/ConfirmDialogFragment.java +++ b/app/src/main/java/awais/instagrabber/dialogs/ConfirmDialogFragment.java @@ -8,6 +8,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.Fragment; import com.google.android.material.dialog.MaterialAlertDialogBuilder; @@ -17,6 +18,9 @@ public class ConfirmDialogFragment extends DialogFragment { private Context context; private ConfirmDialogFragmentCallback callback; + private final int defaultPositiveButtonText = R.string.ok; + // private final int defaultNegativeButtonText = R.string.cancel; + @NonNull public static ConfirmDialogFragment newInstance(final int requestCode, @StringRes final int title, @@ -26,11 +30,21 @@ public class ConfirmDialogFragment extends DialogFragment { @StringRes final int neutralText) { Bundle args = new Bundle(); args.putInt("requestCode", requestCode); - args.putInt("title", title); - args.putInt("message", message); - args.putInt("positive", positiveText); - args.putInt("negative", negativeText); - args.putInt("neutral", neutralText); + if (title != 0) { + args.putInt("title", title); + } + if (message != 0) { + args.putInt("message", message); + } + if (positiveText != 0) { + args.putInt("positive", positiveText); + } + if (negativeText != 0) { + args.putInt("negative", negativeText); + } + if (neutralText != 0) { + args.putInt("neutral", neutralText); + } ConfirmDialogFragment fragment = new ConfirmDialogFragment(); fragment.setArguments(args); return fragment; @@ -41,10 +55,9 @@ public class ConfirmDialogFragment extends DialogFragment { @Override public void onAttach(@NonNull final Context context) { super.onAttach(context); - try { - callback = (ConfirmDialogFragmentCallback) getParentFragment(); - } catch (ClassCastException e) { - throw new ClassCastException("Calling fragment must implement ConfirmDialogFragmentCallback interface"); + final Fragment parentFragment = getParentFragment(); + if (parentFragment instanceof ConfirmDialogFragmentCallback) { + callback = (ConfirmDialogFragmentCallback) parentFragment; } this.context = context; } @@ -53,38 +66,42 @@ public class ConfirmDialogFragment extends DialogFragment { @Override public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) { final Bundle arguments = getArguments(); - int title = -1; - int message = -1; - int positiveButtonText = R.string.ok; - int negativeButtonText = R.string.cancel; - int neutralButtonText = -1; + int title = 0; + int message = 0; + int neutralButtonText = 0; + int negativeButtonText = 0; + + final int positiveButtonText; final int requestCode; if (arguments != null) { - title = arguments.getInt("title", -1); - message = arguments.getInt("message", -1); - positiveButtonText = arguments.getInt("positive", R.string.ok); - negativeButtonText = arguments.getInt("negative", R.string.cancel); - neutralButtonText = arguments.getInt("neutral", -1); + title = arguments.getInt("title", 0); + message = arguments.getInt("message", 0); + positiveButtonText = arguments.getInt("positive", defaultPositiveButtonText); + negativeButtonText = arguments.getInt("negative", 0); + neutralButtonText = arguments.getInt("neutral", 0); requestCode = arguments.getInt("requestCode", 0); } else { requestCode = 0; + positiveButtonText = defaultPositiveButtonText; } final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context) .setPositiveButton(positiveButtonText, (d, w) -> { if (callback == null) return; callback.onPositiveButtonClicked(requestCode); - }) - .setNegativeButton(negativeButtonText, (dialog, which) -> { - if (callback == null) return; - callback.onNegativeButtonClicked(requestCode); }); - if (title > 0) { + if (title != 0) { builder.setTitle(title); } - if (message > 0) { + if (message != 0) { builder.setMessage(message); } - if (neutralButtonText > 0) { + if (negativeButtonText != 0) { + builder.setNegativeButton(negativeButtonText, (dialog, which) -> { + if (callback == null) return; + callback.onNegativeButtonClicked(requestCode); + }); + } + if (neutralButtonText != 0) { builder.setNeutralButton(neutralButtonText, (dialog, which) -> { if (callback == null) return; callback.onNeutralButtonClicked(requestCode); diff --git a/app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java index 42fcee57..52b4fed7 100644 --- a/app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.java @@ -2,7 +2,6 @@ package awais.instagrabber.fragments; import android.content.Context; import android.content.res.Resources; -import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; @@ -57,7 +56,6 @@ public final class FollowViewerFragment extends Fragment implements SwipeRefresh private FollowAdapter adapter; private View.OnClickListener clickListener; private FragmentFollowersViewerBinding binding; - private AsyncTask currentlyExecuting; private SwipeRefreshLayout root; private FriendshipService friendshipService; private AppCompatActivity fragmentActivity; diff --git a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java index 75e72949..81530dc5 100644 --- a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java @@ -3,7 +3,6 @@ package awais.instagrabber.fragments; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Typeface; -import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.text.SpannableStringBuilder; @@ -95,7 +94,6 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe private Hashtag hashtagModel = null; private ActionMode actionMode; private StoriesService storiesService; - private AsyncTask currentlyExecuting; private boolean isLoggedIn; private TagsService tagsService; private GraphQLService graphQLService; @@ -474,7 +472,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe hashtag, FavoriteType.HASHTAG, hashtagModel.getName(), - hashtagModel.getProfilePicUrl(), + "res:/" + R.drawable.ic_hashtag, result.getDateAdded() ), new RepositoryCallback() { @Override @@ -518,7 +516,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe hashtag, FavoriteType.HASHTAG, hashtagModel.getName(), - hashtagModel.getProfilePicUrl(), + "res:/" + R.drawable.ic_hashtag, new Date() ), new RepositoryCallback() { @Override @@ -533,7 +531,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe }); } })); - hashtagDetailsBinding.mainHashtagImage.setImageURI(hashtagModel.getProfilePicUrl()); + hashtagDetailsBinding.mainHashtagImage.setImageURI("res:/" + R.drawable.ic_hashtag); final String postCount = String.valueOf(hashtagModel.getMediaCount()); final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline, hashtagModel.getMediaCount() > 2000000000L diff --git a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java index 24fa4b88..c42809c5 100644 --- a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java @@ -3,14 +3,9 @@ package awais.instagrabber.fragments; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; -import android.graphics.Typeface; import android.net.Uri; -import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; -import android.text.SpannableStringBuilder; -import android.text.style.RelativeSizeSpan; -import android.text.style.StyleSpan; import android.util.Log; import android.view.ActionMode; import android.view.LayoutInflater; @@ -45,7 +40,6 @@ import java.util.Set; import awais.instagrabber.R; import awais.instagrabber.activities.MainActivity; import awais.instagrabber.adapters.FeedAdapterV2; -import awais.instagrabber.asyncs.LocationFetcher; import awais.instagrabber.asyncs.LocationPostFetchService; import awais.instagrabber.asyncs.PostFetcher; import awais.instagrabber.customviews.PrimaryActionModeCallback; @@ -56,27 +50,29 @@ import awais.instagrabber.db.entities.Favorite; import awais.instagrabber.db.repositories.FavoriteRepository; import awais.instagrabber.db.repositories.RepositoryCallback; import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment; -import awais.instagrabber.models.LocationModel; import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.models.StoryModel; import awais.instagrabber.models.enums.FavoriteType; import awais.instagrabber.repositories.requests.StoryViewerOptions; +import awais.instagrabber.repositories.responses.Location; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.DownloadUtils; import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.Utils; +import awais.instagrabber.webservices.GraphQLService; +import awais.instagrabber.webservices.LocationService; import awais.instagrabber.webservices.ServiceCallback; import awais.instagrabber.webservices.StoriesService; -//import awaisomereport.LogCollector; import static androidx.core.content.PermissionChecker.checkSelfPermission; -import static awais.instagrabber.fragments.HashTagFragment.ARG_HASHTAG; import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION; -//import static awais.instagrabber.utils.Utils.logCollector; import static awais.instagrabber.utils.Utils.settingsHelper; +//import awaisomereport.LogCollector; +//import static awais.instagrabber.utils.Utils.logCollector; + public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener { private static final String TAG = "LocationFragment"; private static final int STORAGE_PERM_REQUEST_CODE = 8020; @@ -89,10 +85,11 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR private boolean hasStories = false; private boolean opening = false; private long locationId; - private LocationModel locationModel; + private Location locationModel; private ActionMode actionMode; private StoriesService storiesService; - private AsyncTask currentlyExecuting; + private GraphQLService graphQLService; + private LocationService locationService; private boolean isLoggedIn; private boolean storiesFetching; private Set selectedFeedModels; @@ -265,12 +262,29 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR } } }; + private final ServiceCallback cb = new ServiceCallback() { + @Override + public void onSuccess(final Location result) { + locationModel = result; + binding.swipeRefreshLayout.setRefreshing(false); + setupLocationDetails(); + } + + @Override + public void onFailure(final Throwable t) { + setupLocationDetails(); + } + }; @Override public void onCreate(@Nullable final Bundle savedInstanceState) { super.onCreate(savedInstanceState); fragmentActivity = (MainActivity) requireActivity(); + final String cookie = settingsHelper.getString(Constants.COOKIE); + isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0; + locationService = isLoggedIn ? LocationService.getInstance() : null; storiesService = StoriesService.getInstance(null, 0L, null); + graphQLService = isLoggedIn ? null : GraphQLService.getInstance(); setHasOptionsMenu(true); } @@ -354,8 +368,6 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR private void init() { if (getArguments() == null) return; - final String cookie = settingsHelper.getString(Constants.COOKIE); - isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0; final LocationFragmentArgs fragmentArgs = LocationFragmentArgs.fromBundle(getArguments()); locationId = fragmentArgs.getLocationId(); locationDetailsBinding.favChip.setVisibility(View.GONE); @@ -377,42 +389,38 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR } private void fetchLocationModel() { - stopCurrentExecutor(); binding.swipeRefreshLayout.setRefreshing(true); - currentlyExecuting = new LocationFetcher(locationId, result -> { - locationModel = result; - binding.swipeRefreshLayout.setRefreshing(false); - if (locationModel == null) { - final Context context = getContext(); - if (context == null) return; - Toast.makeText(context, R.string.error_loading_location, Toast.LENGTH_SHORT).show(); - binding.swipeRefreshLayout.setEnabled(false); - return; - } - setTitle(); - setupLocationDetails(); - setupPosts(); - fetchStories(); - // fetchPosts(); - }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + if (isLoggedIn) locationService.fetch(locationId, cb); + else graphQLService.fetchLocation(locationId, cb); } private void setupLocationDetails() { - final long locationId = locationModel.getId(); + if (locationModel == null) { + try { + Toast.makeText(getContext(), R.string.error_loading_location, Toast.LENGTH_SHORT).show(); + binding.swipeRefreshLayout.setEnabled(false); + } + catch (Exception ignored) {} + return; + } + setTitle(); + setupPosts(); + fetchStories(); + final long locationId = locationModel.getPk(); // binding.swipeRefreshLayout.setRefreshing(true); - locationDetailsBinding.mainLocationImage.setImageURI(locationModel.getSdProfilePic()); - final String postCount = String.valueOf(locationModel.getPostCount()); - final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline, - locationModel.getPostCount() > 2000000000L - ? 2000000000 - : locationModel.getPostCount().intValue(), - postCount)); - span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0); - span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0); - locationDetailsBinding.mainLocPostCount.setText(span); - locationDetailsBinding.mainLocPostCount.setVisibility(View.VISIBLE); + locationDetailsBinding.mainLocationImage.setImageURI("res:/" + R.drawable.ic_location); +// final String postCount = String.valueOf(locationModel.getCount()); +// final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline, +// locationModel.getPostCount() > 2000000000L +// ? 2000000000 +// : locationModel.getPostCount().intValue(), +// postCount)); +// span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0); +// span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0); +// locationDetailsBinding.mainLocPostCount.setText(span); +// locationDetailsBinding.mainLocPostCount.setVisibility(View.VISIBLE); locationDetailsBinding.locationFullName.setText(locationModel.getName()); - CharSequence biography = locationModel.getBio(); + CharSequence biography = locationModel.getAddress() + "\n" + locationModel.getCity(); // binding.locationBiography.setCaptionIsExpandable(true); // binding.locationBiography.setCaptionIsExpanded(true); @@ -423,22 +431,22 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR } else { locationDetailsBinding.locationBiography.setVisibility(View.VISIBLE); locationDetailsBinding.locationBiography.setText(biography); - locationDetailsBinding.locationBiography.addOnHashtagListener(autoLinkItem -> { - final NavController navController = NavHostFragment.findNavController(this); - final Bundle bundle = new Bundle(); - final String originalText = autoLinkItem.getOriginalText().trim(); - bundle.putString(ARG_HASHTAG, originalText); - navController.navigate(R.id.action_global_hashTagFragment, bundle); - }); - locationDetailsBinding.locationBiography.addOnMentionClickListener(autoLinkItem -> { - final String originalText = autoLinkItem.getOriginalText().trim(); - navigateToProfile(originalText); - }); - locationDetailsBinding.locationBiography.addOnEmailClickListener(autoLinkItem -> Utils.openEmailAddress(context, - autoLinkItem.getOriginalText() - .trim())); - locationDetailsBinding.locationBiography - .addOnURLClickListener(autoLinkItem -> Utils.openURL(context, autoLinkItem.getOriginalText().trim())); +// locationDetailsBinding.locationBiography.addOnHashtagListener(autoLinkItem -> { +// final NavController navController = NavHostFragment.findNavController(this); +// final Bundle bundle = new Bundle(); +// final String originalText = autoLinkItem.getOriginalText().trim(); +// bundle.putString(ARG_HASHTAG, originalText); +// navController.navigate(R.id.action_global_hashTagFragment, bundle); +// }); +// locationDetailsBinding.locationBiography.addOnMentionClickListener(autoLinkItem -> { +// final String originalText = autoLinkItem.getOriginalText().trim(); +// navigateToProfile(originalText); +// }); +// locationDetailsBinding.locationBiography.addOnEmailClickListener(autoLinkItem -> Utils.openEmailAddress(context, +// autoLinkItem.getOriginalText() +// .trim())); +// locationDetailsBinding.locationBiography +// .addOnURLClickListener(autoLinkItem -> Utils.openURL(context, autoLinkItem.getOriginalText().trim())); locationDetailsBinding.locationBiography.setOnLongClickListener(v -> { Utils.copyText(context, biography); return true; @@ -457,16 +465,6 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR locationDetailsBinding.btnMap.setOnClickListener(null); } - final String url = locationModel.getUrl(); - if (TextUtils.isEmpty(url)) { - locationDetailsBinding.locationUrl.setVisibility(View.GONE); - } else if (!url.startsWith("http")) { - locationDetailsBinding.locationUrl.setVisibility(View.VISIBLE); - locationDetailsBinding.locationUrl.setText(TextUtils.getSpannableUrl("http://" + url)); - } else { - locationDetailsBinding.locationUrl.setVisibility(View.VISIBLE); - locationDetailsBinding.locationUrl.setText(TextUtils.getSpannableUrl(url)); - } final FavoriteDataSource dataSource = FavoriteDataSource.getInstance(context); final FavoriteRepository favoriteRepository = FavoriteRepository.getInstance(dataSource); locationDetailsBinding.favChip.setVisibility(View.VISIBLE); @@ -481,7 +479,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR String.valueOf(locationId), FavoriteType.LOCATION, locationModel.getName(), - locationModel.getSdProfilePic(), + "res:/" + R.drawable.ic_location, result.getDateAdded() ), new RepositoryCallback() { @Override @@ -523,7 +521,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR String.valueOf(locationId), FavoriteType.LOCATION, locationModel.getName(), - locationModel.getSdProfilePic(), + "res:/" + R.drawable.ic_location, new Date() ), new RepositoryCallback() { @Override @@ -581,18 +579,6 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR } } - private void stopCurrentExecutor() { - if (currentlyExecuting != null) { - try { - currentlyExecuting.cancel(true); - } catch (final Exception e) { -// if (logCollector != null) logCollector.appendException( -// e, LogCollector.LogFile.MAIN_HELPER, "stopCurrentExecutor"); - Log.e(TAG, "", e); - } - } - } - private void setTitle() { final ActionBar actionBar = fragmentActivity.getSupportActionBar(); if (actionBar != null && locationModel != null) { diff --git a/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java index cae95e09..d9b56ce7 100644 --- a/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java @@ -2,7 +2,6 @@ package awais.instagrabber.fragments; import android.content.Context; import android.content.DialogInterface; -import android.os.AsyncTask; import android.os.Bundle; import android.text.SpannableString; import android.text.Spanned; @@ -37,9 +36,9 @@ import awais.instagrabber.models.enums.NotificationType; import awais.instagrabber.repositories.requests.StoryViewerOptions; import awais.instagrabber.repositories.responses.FriendshipChangeResponse; import awais.instagrabber.repositories.responses.Media; -import awais.instagrabber.repositories.responses.Notification; -import awais.instagrabber.repositories.responses.NotificationArgs; -import awais.instagrabber.repositories.responses.NotificationImage; +import awais.instagrabber.repositories.responses.notification.Notification; +import awais.instagrabber.repositories.responses.notification.NotificationArgs; +import awais.instagrabber.repositories.responses.notification.NotificationImage; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.TextUtils; diff --git a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java index 4b8bece2..34d4ed1c 100644 --- a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java @@ -5,6 +5,7 @@ import android.content.Context; import android.content.IntentFilter; import android.content.res.Configuration; import android.os.Bundle; +import android.os.Handler; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; @@ -104,6 +105,11 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh public void onPause() { super.onPause(); unregisterReceiver(); + isPendingRequestTotalBadgeAttached = false; + if (pendingRequestTotalBadgeDrawable != null) { + BadgeUtils.detachBadgeDrawable(pendingRequestTotalBadgeDrawable, fragmentActivity.getToolbar(), pendingRequestsMenuItem.getItemId()); + pendingRequestTotalBadgeDrawable = null; + } } @Override @@ -124,21 +130,13 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh public void onDestroyView() { super.onDestroyView(); unregisterReceiver(); - isPendingRequestTotalBadgeAttached = false; - if (pendingRequestTotalBadgeDrawable != null) { - BadgeUtils.detachBadgeDrawable(pendingRequestTotalBadgeDrawable, fragmentActivity.getToolbar(), pendingRequestsMenuItem.getItemId()); - pendingRequestTotalBadgeDrawable = null; - } } @Override public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - pendingRequestsMenuItem = menu.add(Menu.NONE, R.id.pending_requests, Menu.NONE, "Pending requests"); - pendingRequestsMenuItem.setIcon(R.drawable.ic_account_clock_24) - .setVisible(false) - .setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS); - attachPendingRequestsBadge(viewModel.getPendingRequestsTotal().getValue()); + inflater.inflate(R.menu.dm_inbox_menu, menu); + pendingRequestsMenuItem = menu.findItem(R.id.pending_requests); + pendingRequestsMenuItem.setVisible(isPendingRequestTotalBadgeAttached); } @Override @@ -213,7 +211,16 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh @SuppressLint("UnsafeExperimentalUsageError") private void attachPendingRequestsBadge(@Nullable final Integer count) { - if (pendingRequestsMenuItem == null) return; + if (pendingRequestsMenuItem == null) { + final Handler handler = new Handler(); + handler.postDelayed(new Runnable() { + @Override + public void run() { + attachPendingRequestsBadge(count); + } + }, 500); + return; + } if (pendingRequestTotalBadgeDrawable == null) { final Context context = getContext(); if (context == null) return; diff --git a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java index bbd174f2..64c7fbca 100644 --- a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java @@ -186,7 +186,7 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi R.string.admin_approval_required_description, R.string.ok, R.string.cancel, - -1 + 0 ); confirmDialogFragment.show(getChildFragmentManager(), "approval_required_dialog"); return; @@ -272,10 +272,10 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi final ConfirmDialogFragment confirmDialogFragment = ConfirmDialogFragment.newInstance( LEAVE_THREAD_REQUEST_CODE, R.string.dms_action_leave_question, - -1, + 0, R.string.yes, R.string.no, - -1 + 0 ); confirmDialogFragment.show(getChildFragmentManager(), "leave_thread_confirmation_dialog"); }); @@ -290,7 +290,7 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi R.string.dms_action_end_description, R.string.yes, R.string.no, - -1 + 0 ); confirmDialogFragment.show(getChildFragmentManager(), "end_thread_confirmation_dialog"); }); diff --git a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java index d1e4b5f9..2962c078 100644 --- a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java @@ -1328,95 +1328,6 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact } } - // private void sendText(final String text, final String itemId, final boolean delete) { - // DirectThreadBroadcaster.TextBroadcastOptions textOptions = null; - // DirectThreadBroadcaster.ReactionBroadcastOptions reactionOptions = null; - // if (text != null) { - // try { - // textOptions = new DirectThreadBroadcaster.TextBroadcastOptions(text); - // } catch (UnsupportedEncodingException e) { - // Log.e(TAG, "Error", e); - // return; - // } - // } else { - // reactionOptions = new DirectThreadBroadcaster.ReactionBroadcastOptions(itemId, delete); - // } - // broadcast(text != null ? textOptions : reactionOptions, result -> { - // final Context context = getContext(); - // if (context == null) return; - // if (result == null || result.getResponseCode() != HttpURLConnection.HTTP_OK) { - // Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - // return; - // } - // if (text != null) { - // // binding.commentText.setText(""); - // } else { - // // final View viewWithTag = binding.messageList.findViewWithTag(directItemModel); - // // if (viewWithTag != null) { - // // final ViewParent dim = viewWithTag.getParent(); - // // if (dim instanceof View) { - // // final View dimView = (View) dim; - // // final View likedContainer = dimView.findViewById(R.id.liked_container); - // // if (likedContainer != null) { - // // likedContainer.setVisibility(delete ? View.GONE : View.VISIBLE); - // // } - // // } - // // } - // // directItemModel.setLiked(); - // } - // context.sendBroadcast(new Intent(DMRefreshBroadcastReceiver.ACTION_REFRESH_DM)); - // hasSentSomething = true; - // // new DirectMessageInboxThreadFetcher(threadId, UserInboxDirection.OLDER, null, fetchListener) - // // .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - // }); - // } - - // private void sendImage(final Uri imageUri) { - // final Context context = getContext(); - // if (context == null) return; - // try (InputStream inputStream = context.getContentResolver().openInputStream(imageUri)) { - // final Bitmap bitmap = BitmapFactory.decodeStream(inputStream); - // Toast.makeText(context, R.string.uploading, Toast.LENGTH_SHORT).show(); - // // Upload Image - // final ImageUploader imageUploader = new ImageUploader(); - // imageUploader.setOnTaskCompleteListener(response -> { - // if (response == null || response.getResponseCode() != HttpURLConnection.HTTP_OK) { - // Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - // if (response != null && response.getResponse() != null) { - // Log.e(TAG, response.getResponse().toString()); - // } - // return; - // } - // final JSONObject responseJson = response.getResponse(); - // try { - // final String uploadId = responseJson.getString("upload_id"); - // // Broadcast - // final DirectThreadBroadcaster.ImageBroadcastOptions options = new DirectThreadBroadcaster.ImageBroadcastOptions(true, uploadId); - // hasSentSomething = true; - // broadcast(options, - // broadcastResponse -> { - // // new DirectMessageInboxThreadFetcher(threadId, UserInboxDirection.OLDER, null, fetchListener) - // // .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - // }); - // } catch (JSONException e) { - // Log.e(TAG, "Error parsing json response", e); - // } - // }); - // final ImageUploadOptions options = ImageUploadOptions.builder(bitmap).build(); - // imageUploader.execute(options); - // } catch (IOException e) { - // Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - // Log.e(TAG, "Error opening file", e); - // } - // } - - // private void broadcast(final DirectThreadBroadcaster.BroadcastOptions broadcastOptions, - // final DirectThreadBroadcaster.OnBroadcastCompleteListener listener) { - // final DirectThreadBroadcaster broadcaster = new DirectThreadBroadcaster(threadId); - // broadcaster.setOnTaskCompleteListener(listener); - // broadcaster.execute(broadcastOptions); - // } - @NonNull private User getUser(final long userId) { for (final User user : users) { @@ -1426,58 +1337,6 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact return null; } - // private void searchUsername(final String text) { - // final Bundle bundle = new Bundle(); - // bundle.putString("username", "@" + text); - // NavHostFragment.findNavController(this).navigate(R.id.action_global_profileFragment, bundle); - // } - - // class ThreadAction extends AsyncTask { - // String action, argument; - // - // protected Void doInBackground(String... rawAction) { - // action = rawAction[0]; - // argument = rawAction[1]; - // final String url = "https://i.instagram.com/api/v1/direct_v2/threads/" + threadId + "/items/" + argument + "/" + action + "/"; - // try { - // String urlParameters = "_csrftoken=" + COOKIE.split("csrftoken=")[1].split(";")[0] - // + "&_uuid=" + Utils.settingsHelper.getString(Constants.DEVICE_UUID); - // final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection(); - // urlConnection.setRequestMethod("POST"); - // urlConnection.setUseCaches(false); - // urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT); - // urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); - // urlConnection.setRequestProperty("Content-Length", Integer.toString(urlParameters.getBytes().length)); - // urlConnection.setDoOutput(true); - // DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream()); - // wr.writeBytes(urlParameters); - // wr.flush(); - // wr.close(); - // urlConnection.connect(); - // if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) { - // if (action.equals("delete")) { - // hasDeletedSomething = true; - // } else if (action.equals("seen")) { - // // context.sendBroadcast(new Intent(DMRefreshBroadcastReceiver.ACTION_REFRESH_DM)); - // } - // } - // urlConnection.disconnect(); - // } catch (Throwable ex) { - // Log.e("austin_debug", action + ": " + ex); - // } - // return null; - // } - // - // @Override - // protected void onPostExecute(Void result) { - // if (hasDeletedSomething) { - // // directItemModel = null; - // // new DirectMessageInboxThreadFetcher(threadId, UserInboxDirection.OLDER, null, fetchListener) - // // .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - // } - // } - // } - private void setupKbHeightProvider() { if (heightProvider != null) return; heightProvider = new HeightProvider(fragmentActivity).init().setHeightListener(height -> { diff --git a/app/src/main/java/awais/instagrabber/fragments/settings/GeneralPreferencesFragment.java b/app/src/main/java/awais/instagrabber/fragments/settings/GeneralPreferencesFragment.java index 99a5d735..62758946 100644 --- a/app/src/main/java/awais/instagrabber/fragments/settings/GeneralPreferencesFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/settings/GeneralPreferencesFragment.java @@ -10,6 +10,8 @@ import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import androidx.preference.SwitchPreferenceCompat; +import java.util.List; + import awais.instagrabber.R; import awais.instagrabber.dialogs.TabOrderPreferenceDialogFragment; import awais.instagrabber.utils.Constants; @@ -32,6 +34,14 @@ public class GeneralPreferencesFragment extends BasePreferencesFragment implemen } screen.addPreference(getUpdateCheckPreference(context)); screen.addPreference(getFlagSecurePreference(context)); + final List preferences = FlavorSettings.getInstance().getPreferences(context, + getChildFragmentManager(), + SettingCategory.GENERAL); + if (preferences != null) { + for (final Preference preference : preferences) { + screen.addPreference(preference); + } + } } private Preference getDefaultTabPreference(@NonNull final Context context) { diff --git a/app/src/main/java/awais/instagrabber/fragments/settings/IFlavorSettings.java b/app/src/main/java/awais/instagrabber/fragments/settings/IFlavorSettings.java new file mode 100644 index 00000000..4b0f1c5a --- /dev/null +++ b/app/src/main/java/awais/instagrabber/fragments/settings/IFlavorSettings.java @@ -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 getPreferences(Context context, + FragmentManager childFragmentManager, + SettingCategory settingCategory); +} diff --git a/app/src/main/java/awais/instagrabber/fragments/settings/PreferenceKeys.java b/app/src/main/java/awais/instagrabber/fragments/settings/PreferenceKeys.java index 6e6b9cf4..5287a1a5 100644 --- a/app/src/main/java/awais/instagrabber/fragments/settings/PreferenceKeys.java +++ b/app/src/main/java/awais/instagrabber/fragments/settings/PreferenceKeys.java @@ -5,5 +5,6 @@ public final class PreferenceKeys { public static final String PREF_ENABLE_DM_AUTO_REFRESH = "enable_dm_auto_refresh"; public static final String PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT = "enable_dm_auto_refresh_freq_unit"; public static final String PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER = "enable_dm_auto_refresh_freq_number"; + public static final String PREF_ENABLE_SENTRY = "enable_sentry"; public static final String PREF_TAB_ORDER = "tab_order"; } diff --git a/app/src/main/java/awais/instagrabber/fragments/settings/SettingCategory.java b/app/src/main/java/awais/instagrabber/fragments/settings/SettingCategory.java new file mode 100644 index 00000000..88ba5e6f --- /dev/null +++ b/app/src/main/java/awais/instagrabber/fragments/settings/SettingCategory.java @@ -0,0 +1,6 @@ +package awais.instagrabber.fragments.settings; + +public enum SettingCategory { + GENERAL, + // add more as and when required +} diff --git a/app/src/main/java/awais/instagrabber/models/LocationModel.java b/app/src/main/java/awais/instagrabber/models/LocationModel.java deleted file mode 100755 index e0e13730..00000000 --- a/app/src/main/java/awais/instagrabber/models/LocationModel.java +++ /dev/null @@ -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; } -} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/models/SuggestionModel.java b/app/src/main/java/awais/instagrabber/models/SuggestionModel.java deleted file mode 100755 index 855b1d4b..00000000 --- a/app/src/main/java/awais/instagrabber/models/SuggestionModel.java +++ /dev/null @@ -1,51 +0,0 @@ -package awais.instagrabber.models; - -import androidx.annotation.NonNull; - -import awais.instagrabber.models.enums.SuggestionType; - -public final class SuggestionModel implements Comparable { - 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()); - } -} diff --git a/app/src/main/java/awais/instagrabber/repositories/FeedRepository.java b/app/src/main/java/awais/instagrabber/repositories/FeedRepository.java index bb7d036c..b5273e35 100644 --- a/app/src/main/java/awais/instagrabber/repositories/FeedRepository.java +++ b/app/src/main/java/awais/instagrabber/repositories/FeedRepository.java @@ -2,7 +2,7 @@ package awais.instagrabber.repositories; import java.util.Map; -import awais.instagrabber.repositories.responses.FeedFetchResponse; +import awais.instagrabber.repositories.responses.feed.FeedFetchResponse; import retrofit2.Call; import retrofit2.http.FieldMap; import retrofit2.http.FormUrlEncoded; diff --git a/app/src/main/java/awais/instagrabber/repositories/GraphQLRepository.java b/app/src/main/java/awais/instagrabber/repositories/GraphQLRepository.java index a6246fe0..efbc1c9a 100644 --- a/app/src/main/java/awais/instagrabber/repositories/GraphQLRepository.java +++ b/app/src/main/java/awais/instagrabber/repositories/GraphQLRepository.java @@ -16,4 +16,7 @@ public interface GraphQLRepository { @GET("/explore/tags/{tag}/?__a=1") Call getTag(@Path("tag") String tag); + + @GET("/explore/locations/{locationId}/?__a=1") + Call getLocation(@Path("locationId") long locationId); } diff --git a/app/src/main/java/awais/instagrabber/repositories/LocationRepository.java b/app/src/main/java/awais/instagrabber/repositories/LocationRepository.java index df999f0b..69e7302e 100644 --- a/app/src/main/java/awais/instagrabber/repositories/LocationRepository.java +++ b/app/src/main/java/awais/instagrabber/repositories/LocationRepository.java @@ -3,12 +3,15 @@ package awais.instagrabber.repositories; import java.util.Map; import awais.instagrabber.repositories.responses.LocationFeedResponse; +import awais.instagrabber.repositories.responses.Place; import retrofit2.Call; import retrofit2.http.GET; import retrofit2.http.Path; import retrofit2.http.QueryMap; public interface LocationRepository { + @GET("/api/v1/locations/{location}/info/") + Call fetch(@Path("location") final long locationId); @GET("/api/v1/feed/location/{location}/") Call fetchPosts(@Path("location") final long locationId, diff --git a/app/src/main/java/awais/instagrabber/repositories/SearchRepository.java b/app/src/main/java/awais/instagrabber/repositories/SearchRepository.java new file mode 100644 index 00000000..148e8f43 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/repositories/SearchRepository.java @@ -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 search(@Url String url, @QueryMap(encoded = true) Map queryParams); +} diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/Hashtag.java b/app/src/main/java/awais/instagrabber/repositories/responses/Hashtag.java index aca5daad..2ce08eb5 100755 --- a/app/src/main/java/awais/instagrabber/repositories/responses/Hashtag.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/Hashtag.java @@ -5,22 +5,22 @@ import java.io.Serializable; import awais.instagrabber.models.enums.FollowingType; public final class Hashtag implements Serializable { - private final FollowingType following; // 0 false 1 true + private final FollowingType following; // 0 false 1 true; not on search results private final long mediaCount; private final String id; private final String name; - private final String profilePicUrl; // on app API this is always null (property exists) + private final String searchResultSubtitle; // shows how many posts there are on search results public Hashtag(final String id, final String name, - final String profilePicUrl, final long mediaCount, - final FollowingType following) { + final FollowingType following, + final String searchResultSubtitle) { this.id = id; this.name = name; - this.profilePicUrl = profilePicUrl; this.mediaCount = mediaCount; this.following = following; + this.searchResultSubtitle = searchResultSubtitle; } public String getId() { @@ -31,10 +31,6 @@ public final class Hashtag implements Serializable { return name; } - public String getProfilePicUrl() { - return profilePicUrl; - } - public Long getMediaCount() { return mediaCount; } @@ -42,4 +38,8 @@ public final class Hashtag implements Serializable { public FollowingType getFollowing() { return following; } + + public String getSubtitle() { + return searchResultSubtitle; + } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/Location.java b/app/src/main/java/awais/instagrabber/repositories/responses/Location.java index ea1e66fe..c6a3d950 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/Location.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/Location.java @@ -9,16 +9,16 @@ public class Location implements Serializable { private final String name; private final String address; private final String city; - private final float lng; - private final float lat; + private final double lng; + private final double lat; public Location(final long pk, final String shortName, final String name, final String address, final String city, - final float lng, - final float lat) { + final double lng, + final double lat) { this.pk = pk; this.shortName = shortName; this.name = name; @@ -48,22 +48,24 @@ public class Location implements Serializable { return city; } - public float getLng() { + public double getLng() { return lng; } - public float getLat() { + public double getLat() { return lat; } + public String getGeo() { return "geo:" + lat + "," + lng + "?z=17&q=" + lat + "," + lng + "(" + name + ")"; } + @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final Location location = (Location) o; return pk == location.pk && - Float.compare(location.lng, lng) == 0 && - Float.compare(location.lat, lat) == 0 && + Double.compare(location.lng, lng) == 0 && + Double.compare(location.lat, lat) == 0 && Objects.equals(shortName, location.shortName) && Objects.equals(name, location.name) && Objects.equals(address, location.address) && diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/Media.java b/app/src/main/java/awais/instagrabber/repositories/responses/Media.java index 1a149a66..c11669a8 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/Media.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/Media.java @@ -9,6 +9,7 @@ import java.util.Map; import java.util.Objects; import awais.instagrabber.models.enums.MediaItemType; +import awais.instagrabber.repositories.responses.feed.EndOfFeedDemarcator; import awais.instagrabber.utils.Utils; public class Media implements Serializable { diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/NewsInboxResponse.java b/app/src/main/java/awais/instagrabber/repositories/responses/NewsInboxResponse.java index 5dcdb487..a6339e20 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/NewsInboxResponse.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/NewsInboxResponse.java @@ -2,6 +2,9 @@ package awais.instagrabber.repositories.responses; import java.util.List; +import awais.instagrabber.repositories.responses.notification.Notification; +import awais.instagrabber.repositories.responses.notification.NotificationCounts; + public class NewsInboxResponse { private final NotificationCounts counts; private final List newStories; diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/Place.java b/app/src/main/java/awais/instagrabber/repositories/responses/Place.java new file mode 100644 index 00000000..c72e1c41 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/repositories/responses/Place.java @@ -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; + } +} diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/EndOfFeedDemarcator.java b/app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedDemarcator.java similarity index 94% rename from app/src/main/java/awais/instagrabber/repositories/responses/EndOfFeedDemarcator.java rename to app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedDemarcator.java index 44b9936d..2408bc89 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/EndOfFeedDemarcator.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedDemarcator.java @@ -1,4 +1,4 @@ -package awais.instagrabber.repositories.responses; +package awais.instagrabber.repositories.responses.feed; import java.io.Serializable; import java.util.Objects; diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/EndOfFeedGroup.java b/app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedGroup.java similarity index 92% rename from app/src/main/java/awais/instagrabber/repositories/responses/EndOfFeedGroup.java rename to app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedGroup.java index 43384a41..e675691e 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/EndOfFeedGroup.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedGroup.java @@ -1,9 +1,11 @@ -package awais.instagrabber.repositories.responses; +package awais.instagrabber.repositories.responses.feed; import java.io.Serializable; import java.util.List; import java.util.Objects; +import awais.instagrabber.repositories.responses.Media; + public class EndOfFeedGroup implements Serializable { private final String id; private final String title; diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/EndOfFeedGroupSet.java b/app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedGroupSet.java similarity index 97% rename from app/src/main/java/awais/instagrabber/repositories/responses/EndOfFeedGroupSet.java rename to app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedGroupSet.java index 3da29e03..cfda0f39 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/EndOfFeedGroupSet.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/feed/EndOfFeedGroupSet.java @@ -1,4 +1,4 @@ -package awais.instagrabber.repositories.responses; +package awais.instagrabber.repositories.responses.feed; import java.io.Serializable; import java.util.List; diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/FeedFetchResponse.java b/app/src/main/java/awais/instagrabber/repositories/responses/feed/FeedFetchResponse.java similarity index 90% rename from app/src/main/java/awais/instagrabber/repositories/responses/FeedFetchResponse.java rename to app/src/main/java/awais/instagrabber/repositories/responses/feed/FeedFetchResponse.java index 2d91a9f7..9d8659af 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/FeedFetchResponse.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/feed/FeedFetchResponse.java @@ -1,7 +1,9 @@ -package awais.instagrabber.repositories.responses; +package awais.instagrabber.repositories.responses.feed; import java.util.List; +import awais.instagrabber.repositories.responses.Media; + public class FeedFetchResponse { private final List items; private final int numResults; diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/Notification.java b/app/src/main/java/awais/instagrabber/repositories/responses/notification/Notification.java similarity index 91% rename from app/src/main/java/awais/instagrabber/repositories/responses/Notification.java rename to app/src/main/java/awais/instagrabber/repositories/responses/notification/Notification.java index c784d52b..6da18866 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/Notification.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/notification/Notification.java @@ -1,4 +1,4 @@ -package awais.instagrabber.repositories.responses; +package awais.instagrabber.repositories.responses.notification; import awais.instagrabber.models.enums.NotificationType; diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/NotificationArgs.java b/app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationArgs.java similarity index 97% rename from app/src/main/java/awais/instagrabber/repositories/responses/NotificationArgs.java rename to app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationArgs.java index 87116896..27fde081 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/NotificationArgs.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationArgs.java @@ -1,4 +1,4 @@ -package awais.instagrabber.repositories.responses; +package awais.instagrabber.repositories.responses.notification; import androidx.annotation.NonNull; @@ -7,8 +7,6 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import android.util.Log; - import awais.instagrabber.utils.Utils; public class NotificationArgs { diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/NotificationCounts.java b/app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationCounts.java similarity index 94% rename from app/src/main/java/awais/instagrabber/repositories/responses/NotificationCounts.java rename to app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationCounts.java index 38fb70f9..dfad97e0 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/NotificationCounts.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationCounts.java @@ -1,6 +1,4 @@ -package awais.instagrabber.repositories.responses; - -import androidx.annotation.NonNull; +package awais.instagrabber.repositories.responses.notification; public class NotificationCounts { private final int commentLikes; diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/NotificationImage.java b/app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationImage.java similarity index 84% rename from app/src/main/java/awais/instagrabber/repositories/responses/NotificationImage.java rename to app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationImage.java index 3ba92ce2..e3d31bc9 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/NotificationImage.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/notification/NotificationImage.java @@ -1,4 +1,4 @@ -package awais.instagrabber.repositories.responses; +package awais.instagrabber.repositories.responses.notification; public class NotificationImage { private final String id; diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/search/SearchItem.java b/app/src/main/java/awais/instagrabber/repositories/responses/search/SearchItem.java new file mode 100644 index 00000000..4aca79da --- /dev/null +++ b/app/src/main/java/awais/instagrabber/repositories/responses/search/SearchItem.java @@ -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; + } +} diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/search/SearchResponse.java b/app/src/main/java/awais/instagrabber/repositories/responses/search/SearchResponse.java new file mode 100644 index 00000000..d3f0e143 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/repositories/responses/search/SearchResponse.java @@ -0,0 +1,46 @@ +package awais.instagrabber.repositories.responses.search; + +import java.util.List; + +public class SearchResponse { + // app + private final List list; + // browser + private final List users; + private final List places; + private final List hashtags; + // universal + private final String status; + + public SearchResponse(final List list, + final List users, + final List places, + final List hashtags, + final String status) { + this.list = list; + this.users = users; + this.places = places; + this.hashtags = hashtags; + this.status = status; + } + + public List getList() { + return list; + } + + public List getUsers() { + return users; + } + + public List getPlaces() { + return places; + } + + public List getHashtags() { + return hashtags; + } + + public String getStatus() { + return status; + } +} diff --git a/app/src/main/java/awais/instagrabber/services/ActivityCheckerService.java b/app/src/main/java/awais/instagrabber/services/ActivityCheckerService.java index 5160f9aa..1845188e 100644 --- a/app/src/main/java/awais/instagrabber/services/ActivityCheckerService.java +++ b/app/src/main/java/awais/instagrabber/services/ActivityCheckerService.java @@ -18,13 +18,11 @@ import java.util.List; import awais.instagrabber.R; import awais.instagrabber.activities.MainActivity; -import awais.instagrabber.repositories.responses.NotificationCounts; +import awais.instagrabber.repositories.responses.notification.NotificationCounts; import awais.instagrabber.utils.Constants; import awais.instagrabber.webservices.NewsService; import awais.instagrabber.webservices.ServiceCallback; -import static awais.instagrabber.utils.Utils.settingsHelper; - public class ActivityCheckerService extends Service { private static final String TAG = "ActivityCheckerService"; private static final int INITIAL_DELAY_MILLIS = 200; diff --git a/app/src/main/java/awais/instagrabber/utils/Constants.java b/app/src/main/java/awais/instagrabber/utils/Constants.java index 0c92f0fd..969f5d6a 100644 --- a/app/src/main/java/awais/instagrabber/utils/Constants.java +++ b/app/src/main/java/awais/instagrabber/utils/Constants.java @@ -1,6 +1,8 @@ package awais.instagrabber.utils; public final class Constants { + public static final String CRASH_REPORT_EMAIL = "barinsta@austinhuang.me"; + // string prefs public static final String FOLDER_PATH = "custom_path"; public static final String DATE_TIME_FORMAT = "date_time_format"; diff --git a/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java b/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java index ce80b3d9..9ae6cfd4 100755 --- a/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java +++ b/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java @@ -15,6 +15,7 @@ import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_D import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER; import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT; import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_NOTIFICATIONS; +import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_SENTRY; import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_TAB_ORDER; import static awais.instagrabber.utils.Constants.APP_LANGUAGE; import static awais.instagrabber.utils.Constants.APP_THEME; @@ -146,6 +147,10 @@ public final class SettingsHelper { if (sharedPreferences != null) sharedPreferences.edit().putBoolean(key, val).apply(); } + public boolean hasPreference(final String key) { + return sharedPreferences != null && sharedPreferences.contains(key); + } + @StringDef( {APP_LANGUAGE, APP_THEME, APP_UA, BROWSER_UA, COOKIE, FOLDER_PATH, DATE_TIME_FORMAT, DATE_TIME_SELECTION, CUSTOM_DATE_TIME_FORMAT, DEVICE_UUID, SKIPPED_VERSION, DEFAULT_TAB, PREF_DARK_THEME, PREF_LIGHT_THEME, @@ -157,7 +162,7 @@ public final class SettingsHelper { @StringDef({DOWNLOAD_USER_FOLDER, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS, SHOW_CAPTIONS, CUSTOM_DATE_TIME_FORMAT_ENABLED, MARK_AS_SEEN, DM_MARK_AS_SEEN, CHECK_ACTIVITY, CHECK_UPDATES, SWAP_DATE_TIME_FORMAT_ENABLED, PREF_ENABLE_DM_NOTIFICATIONS, PREF_ENABLE_DM_AUTO_REFRESH, - FLAG_SECURE, TOGGLE_KEYWORD_FILTER}) + FLAG_SECURE, TOGGLE_KEYWORD_FILTER, PREF_ENABLE_SENTRY}) public @interface BooleanSettings {} @StringDef({PREV_INSTALL_VERSION, BROWSER_UA_CODE, APP_UA_CODE, PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER}) diff --git a/app/src/main/java/awais/instagrabber/utils/TextUtils.java b/app/src/main/java/awais/instagrabber/utils/TextUtils.java index e26bd8cf..448c2a90 100644 --- a/app/src/main/java/awais/instagrabber/utils/TextUtils.java +++ b/app/src/main/java/awais/instagrabber/utils/TextUtils.java @@ -2,8 +2,6 @@ package awais.instagrabber.utils; import android.content.Context; import android.text.SpannableString; -import android.text.SpannableStringBuilder; -import android.text.Spanned; import android.text.format.DateFormat; import android.text.format.DateUtils; import android.text.style.URLSpan; @@ -11,17 +9,12 @@ import android.util.Patterns; import androidx.annotation.NonNull; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import awais.instagrabber.customviews.CommentMentionClickSpan; public final class TextUtils { // extracted from String class @@ -69,7 +62,7 @@ public final class TextUtils { str = str.trim(); return "".equals(str) || "null".equals(str) || str.isEmpty(); } - return "null".contentEquals(charSequence) || "".contentEquals(charSequence) || charSequence.length() < 1; + return "null".contentEquals(charSequence) || "".contentEquals(charSequence); } public static String millisToTimeString(final long millis) { diff --git a/app/src/main/java/awais/instagrabber/viewmodels/NotificationViewModel.java b/app/src/main/java/awais/instagrabber/viewmodels/NotificationViewModel.java index 59a8bb64..1e3e9845 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/NotificationViewModel.java +++ b/app/src/main/java/awais/instagrabber/viewmodels/NotificationViewModel.java @@ -5,7 +5,7 @@ import androidx.lifecycle.ViewModel; import java.util.List; -import awais.instagrabber.repositories.responses.Notification; +import awais.instagrabber.repositories.responses.notification.Notification; public class NotificationViewModel extends ViewModel { private MutableLiveData> list; diff --git a/app/src/main/java/awais/instagrabber/webservices/FeedService.java b/app/src/main/java/awais/instagrabber/webservices/FeedService.java index 2bd91690..3b4baddd 100644 --- a/app/src/main/java/awais/instagrabber/webservices/FeedService.java +++ b/app/src/main/java/awais/instagrabber/webservices/FeedService.java @@ -12,12 +12,12 @@ import java.util.Map; import java.util.UUID; import awais.instagrabber.repositories.FeedRepository; -import awais.instagrabber.repositories.responses.EndOfFeedDemarcator; -import awais.instagrabber.repositories.responses.EndOfFeedGroup; -import awais.instagrabber.repositories.responses.EndOfFeedGroupSet; -import awais.instagrabber.repositories.responses.FeedFetchResponse; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.PostsFetchResponse; +import awais.instagrabber.repositories.responses.feed.EndOfFeedDemarcator; +import awais.instagrabber.repositories.responses.feed.EndOfFeedGroup; +import awais.instagrabber.repositories.responses.feed.EndOfFeedGroupSet; +import awais.instagrabber.repositories.responses.feed.FeedFetchResponse; import awais.instagrabber.utils.TextUtils; import retrofit2.Call; import retrofit2.Callback; diff --git a/app/src/main/java/awais/instagrabber/webservices/GraphQLService.java b/app/src/main/java/awais/instagrabber/webservices/GraphQLService.java index 4cd1c1b6..b4ca9293 100644 --- a/app/src/main/java/awais/instagrabber/webservices/GraphQLService.java +++ b/app/src/main/java/awais/instagrabber/webservices/GraphQLService.java @@ -19,6 +19,7 @@ import awais.instagrabber.repositories.GraphQLRepository; import awais.instagrabber.repositories.responses.FriendshipStatus; import awais.instagrabber.repositories.responses.GraphQLUserListFetchResponse; import awais.instagrabber.repositories.responses.Hashtag; +import awais.instagrabber.repositories.responses.Location; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.PostsFetchResponse; import awais.instagrabber.repositories.responses.User; @@ -394,9 +395,53 @@ public class GraphQLService extends BaseService { callback.onSuccess(new Hashtag( body.getString(Constants.EXTRAS_ID), body.getString("name"), - body.getString("profile_pic_url"), timelineMedia.getLong("count"), - body.optBoolean("is_following") ? FollowingType.FOLLOWING : FollowingType.NOT_FOLLOWING)); + body.optBoolean("is_following") ? FollowingType.FOLLOWING : FollowingType.NOT_FOLLOWING, + null)); + } catch (JSONException e) { + Log.e(TAG, "onResponse", e); + if (callback != null) { + callback.onFailure(e); + } + } + } + + @Override + public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { + if (callback != null) { + callback.onFailure(t); + } + } + }); + } + + public void fetchLocation(final long locationId, + final ServiceCallback callback) { + final Call request = repository.getLocation(locationId); + request.enqueue(new Callback() { + @Override + public void onResponse(@NonNull final Call call, @NonNull final Response response) { + final String rawBody = response.body(); + if (rawBody == null) { + Log.e(TAG, "Error occurred while fetching gql location of " + locationId); + callback.onSuccess(null); + return; + } + try { + final JSONObject body = new JSONObject(rawBody) + .getJSONObject("graphql") + .getJSONObject(Constants.EXTRAS_LOCATION); + final JSONObject timelineMedia = body.getJSONObject("edge_location_to_media"); + final JSONObject address = new JSONObject(body.getString("address_json")); + callback.onSuccess(new Location( + body.getLong(Constants.EXTRAS_ID), + body.getString("slug"), + body.getString("name"), + address.optString("street_address"), + address.optString("city_name"), + body.optDouble("lng", 0d), + body.optDouble("lat", 0d) + )); } catch (JSONException e) { Log.e(TAG, "onResponse", e); if (callback != null) { diff --git a/app/src/main/java/awais/instagrabber/webservices/LocationService.java b/app/src/main/java/awais/instagrabber/webservices/LocationService.java index 381ba440..7b3648f4 100644 --- a/app/src/main/java/awais/instagrabber/webservices/LocationService.java +++ b/app/src/main/java/awais/instagrabber/webservices/LocationService.java @@ -5,7 +5,9 @@ import androidx.annotation.NonNull; import com.google.common.collect.ImmutableMap; import awais.instagrabber.repositories.LocationRepository; +import awais.instagrabber.repositories.responses.Location; import awais.instagrabber.repositories.responses.LocationFeedResponse; +import awais.instagrabber.repositories.responses.Place; import awais.instagrabber.repositories.responses.PostsFetchResponse; import awais.instagrabber.utils.TextUtils; import retrofit2.Call; @@ -69,34 +71,24 @@ public class LocationService extends BaseService { }); } - // private PostsFetchResponse parseResponse(@NonNull final String body) throws JSONException { - // final JSONObject root = new JSONObject(body); - // final boolean moreAvailable = root.optBoolean("more_available"); - // final String nextMaxId = root.optString("next_max_id"); - // final JSONArray itemsJson = root.optJSONArray("items"); - // final List items = parseItems(itemsJson); - // return new PostsFetchResponse( - // items, - // moreAvailable, - // nextMaxId - // ); - // } + public void fetch(@NonNull final long locationId, + final ServiceCallback callback) { + final Call request = repository.fetch(locationId); + request.enqueue(new Callback() { + @Override + public void onResponse(@NonNull final Call call, @NonNull final Response response) { + if (callback == null) { + return; + } + callback.onSuccess(response.body() == null ? null : response.body().getLocation()); + } - // private List parseItems(final JSONArray items) throws JSONException { - // if (items == null) { - // return Collections.emptyList(); - // } - // final List feedModels = new ArrayList<>(); - // for (int i = 0; i < items.length(); i++) { - // final JSONObject itemJson = items.optJSONObject(i); - // if (itemJson == null) { - // continue; - // } - // final FeedModel feedModel = ResponseBodyUtils.parseItem(itemJson); - // if (feedModel != null) { - // feedModels.add(feedModel); - // } - // } - // return feedModels; - // } + @Override + public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { + if (callback != null) { + callback.onFailure(t); + } + } + }); + } } diff --git a/app/src/main/java/awais/instagrabber/webservices/NewsService.java b/app/src/main/java/awais/instagrabber/webservices/NewsService.java index fe54c13d..63fede99 100644 --- a/app/src/main/java/awais/instagrabber/webservices/NewsService.java +++ b/app/src/main/java/awais/instagrabber/webservices/NewsService.java @@ -1,35 +1,24 @@ package awais.instagrabber.webservices; -import android.util.Log; - import androidx.annotation.NonNull; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.stream.Collectors; -import awais.instagrabber.BuildConfig; -import awais.instagrabber.models.enums.NotificationType; import awais.instagrabber.repositories.NewsRepository; import awais.instagrabber.repositories.responses.AymlResponse; import awais.instagrabber.repositories.responses.AymlUser; -import awais.instagrabber.repositories.responses.NotificationCounts; -import awais.instagrabber.repositories.responses.UserSearchResponse; import awais.instagrabber.repositories.responses.NewsInboxResponse; -import awais.instagrabber.repositories.responses.Notification; -import awais.instagrabber.repositories.responses.NotificationArgs; -import awais.instagrabber.repositories.responses.NotificationImage; import awais.instagrabber.repositories.responses.User; +import awais.instagrabber.repositories.responses.UserSearchResponse; +import awais.instagrabber.repositories.responses.notification.Notification; +import awais.instagrabber.repositories.responses.notification.NotificationArgs; +import awais.instagrabber.repositories.responses.notification.NotificationCounts; import awais.instagrabber.utils.Constants; -import awais.instagrabber.utils.Utils; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; diff --git a/app/src/main/java/awais/instagrabber/webservices/SearchService.java b/app/src/main/java/awais/instagrabber/webservices/SearchService.java new file mode 100644 index 00000000..c9efa68f --- /dev/null +++ b/app/src/main/java/awais/instagrabber/webservices/SearchService.java @@ -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 search(final boolean isLoggedIn, + final String query, + final String context) { + final ImmutableMap.Builder 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()); + } +} diff --git a/app/src/main/java/awaisomereport/CrashReporter.java b/app/src/main/java/awaisomereport/CrashReporter.java index f5f22143..8b61e952 100755 --- a/app/src/main/java/awaisomereport/CrashReporter.java +++ b/app/src/main/java/awaisomereport/CrashReporter.java @@ -1,50 +1,35 @@ package awaisomereport; 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.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 { + private static final String TAG = CrashReporter.class.getSimpleName(); + 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 Thread.UncaughtExceptionHandler defaultEH; public static CrashReporter get(final Application application) { - if (reporterInstance == null) reporterInstance = new CrashReporter(application); + if (reporterInstance == null) { + reporterInstance = new CrashReporter(application); + } return reporterInstance; } private CrashReporter(@NonNull final Application application) { - this.application = application; - this.email = "barinsta@austinhuang.me"; -// 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() { if (!startAttempted) { + defaultEH = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler(this); startAttempted = true; } @@ -52,140 +37,10 @@ public final class CrashReporter implements Thread.UncaughtExceptionHandler { @Override 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); } } diff --git a/app/src/main/java/awaisomereport/CrashReporterHelper.java b/app/src/main/java/awaisomereport/CrashReporterHelper.java new file mode 100644 index 00000000..83f17f3e --- /dev/null +++ b/app/src/main/java/awaisomereport/CrashReporterHelper.java @@ -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); + } + } +} diff --git a/app/src/main/java/awaisomereport/ErrorReporterActivity.java b/app/src/main/java/awaisomereport/ErrorReporterActivity.java index 9fb1ce40..31dc3817 100755 --- a/app/src/main/java/awaisomereport/ErrorReporterActivity.java +++ b/app/src/main/java/awaisomereport/ErrorReporterActivity.java @@ -43,8 +43,9 @@ public final class ErrorReporterActivity extends Activity implements View.OnClic @Override public void onClick(@NonNull final View v) { - if (v == btnReport) - CrashReporter.get(getApplication()).startCrashEmailIntent(this); + if (v == btnReport) { + CrashReporterHelper.startCrashEmailIntent(this); + } finish(); System.exit(10); } diff --git a/app/src/main/java/awaisomereport/ICrashHandler.java b/app/src/main/java/awaisomereport/ICrashHandler.java new file mode 100644 index 00000000..103987d9 --- /dev/null +++ b/app/src/main/java/awaisomereport/ICrashHandler.java @@ -0,0 +1,7 @@ +package awaisomereport; + +public interface ICrashHandler { + void uncaughtException(Thread t, + Throwable exception, + Thread.UncaughtExceptionHandler defaultEH); +} diff --git a/app/src/main/res/drawable/ic_hashtag.png b/app/src/main/res/drawable/ic_hashtag.png new file mode 100644 index 00000000..ce135fc0 Binary files /dev/null and b/app/src/main/res/drawable/ic_hashtag.png differ diff --git a/app/src/main/res/layout/layout_location_details.xml b/app/src/main/res/layout/layout_location_details.xml index 912c225c..bc880ebf 100644 --- a/app/src/main/res/layout/layout_location_details.xml +++ b/app/src/main/res/layout/layout_location_details.xml @@ -15,25 +15,25 @@ android:layout_height="@dimen/profile_picture_size" android:background="?selectableItemBackgroundBorderless" app:actualImageScaleType="centerCrop" - app:layout_constraintEnd_toStartOf="@id/mainLocPostCount" + app:layout_constraintEnd_toStartOf="@id/btnMap" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:background="@mipmap/ic_launcher" /> - + + + + + + + + + + + + + + @@ -60,8 +59,8 @@ app:chipIcon="@drawable/ic_outline_star_plus_24" app:chipIconTint="@color/yellow_800" app:layout_constraintBottom_toBottomOf="@id/mainLocationImage" - app:layout_constraintStart_toEndOf="@id/btnMap" - app:layout_constraintTop_toBottomOf="@id/mainLocPostCount" + app:layout_constraintStart_toEndOf="@id/mainLocationImage" + app:layout_constraintTop_toBottomOf="@id/btnMap" app:rippleColor="@color/yellow_400" /> - - \ No newline at end of file diff --git a/app/src/main/res/menu/dm_inbox_menu.xml b/app/src/main/res/menu/dm_inbox_menu.xml new file mode 100644 index 00000000..041ebda7 --- /dev/null +++ b/app/src/main/res/menu/dm_inbox_menu.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml index ecb381cf..7319700f 100644 --- a/app/src/main/res/values/ids.xml +++ b/app/src/main/res/values/ids.xml @@ -4,5 +4,4 @@ - \ 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 ac04be66..25bddb2c 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -473,6 +473,8 @@ Removed keyword: %s from filter list Marked as seen Delete unsuccessful + Barinsta Crash Report + Select an email app to send crash logs Screen order Other tabs diff --git a/crowdin.yml b/crowdin.yml index 8f046a46..7cae19af 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -1,3 +1,5 @@ files: - source: '/app/src/main/res/values/[arrays][strings][!styles]' 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%