Merge branch 'master' into bottombar_redesign

This commit is contained in:
Ammar Githam 2021-03-24 23:40:31 +09:00
commit b9d75af03c
71 changed files with 1087 additions and 967 deletions

1
.gitignore vendored
View File

@ -17,3 +17,4 @@
.externalNativeBuild .externalNativeBuild
.cxx .cxx
app/release app/release
/sentry.properties

View File

@ -1,5 +1,6 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: "androidx.navigation.safeargs" apply plugin: "androidx.navigation.safeargs"
apply from: 'sentry.gradle'
android { android {
compileSdkVersion 29 compileSdkVersion 29
@ -48,8 +49,22 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 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 { configurations.all {
resolutionStrategy.cacheChangingModulesFor 0, 'seconds' resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
@ -81,7 +96,7 @@ dependencies {
implementation 'androidx.palette:palette:1.0.0' implementation 'androidx.palette:palette:1.0.0'
implementation "androidx.lifecycle:lifecycle-extensions:2.2.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 // Room
def room_version = "2.2.6" def room_version = "2.2.6"
@ -116,6 +131,7 @@ dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.6' 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' testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1'
} }

13
app/sentry.gradle Normal file
View File

@ -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)
}

View File

@ -0,0 +1,40 @@
package awais.instagrabber.fragments.settings;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentManager;
import androidx.preference.Preference;
import java.util.Collections;
import java.util.List;
import awais.instagrabber.fragments.settings.IFlavorSettings;
import awais.instagrabber.fragments.settings.SettingCategory;
public final class FlavorSettings implements IFlavorSettings {
private static FlavorSettings instance;
private FlavorSettings() {
}
public static FlavorSettings getInstance() {
if (instance == null) {
instance = new FlavorSettings();
}
return instance;
}
@NonNull
@Override
public List<Preference> getPreferences(@NonNull final Context context,
@NonNull final FragmentManager fragmentManager,
@NonNull final SettingCategory settingCategory) {
// switch (settingCategory) {
// default:
// break;
// }
return Collections.emptyList();
}
}

View File

@ -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;
// }
}

View File

@ -0,0 +1,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="awais.instagrabber">
<application>
<meta-data
android:name="io.sentry.auto-init"
android:value="false" />
</application>
</manifest>

View File

@ -0,0 +1,83 @@
package awais.instagrabber.fragments.settings;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentManager;
import androidx.preference.Preference;
import com.google.common.collect.ImmutableList;
import java.util.Collections;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.dialogs.ConfirmDialogFragment;
import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_SENTRY;
import static awais.instagrabber.utils.Utils.settingsHelper;
public final class FlavorSettings implements IFlavorSettings {
private static FlavorSettings instance;
private FlavorSettings() {
}
public static FlavorSettings getInstance() {
if (instance == null) {
instance = new FlavorSettings();
}
return instance;
}
@NonNull
@Override
public List<Preference> getPreferences(@NonNull final Context context,
@NonNull final FragmentManager fragmentManager,
@NonNull final SettingCategory settingCategory) {
switch (settingCategory) {
case GENERAL:
return getGeneralPrefs(context, fragmentManager);
default:
break;
}
return Collections.emptyList();
}
private List<Preference> getGeneralPrefs(@NonNull final Context context,
@NonNull final FragmentManager fragmentManager) {
return ImmutableList.of(
getSentryPreference(context, fragmentManager)
);
}
private Preference getSentryPreference(@NonNull final Context context,
@NonNull final FragmentManager fragmentManager) {
if (!settingsHelper.hasPreference(PREF_ENABLE_SENTRY)) {
// disabled by default
settingsHelper.putBoolean(PREF_ENABLE_SENTRY, false);
}
return PreferenceHelper.getSwitchPreference(
context,
PREF_ENABLE_SENTRY,
R.string.enable_sentry,
R.string.sentry_summary,
false,
(preference, newValue) -> {
if (!(newValue instanceof Boolean)) return true;
final boolean enabled = (Boolean) newValue;
if (enabled) {
final ConfirmDialogFragment dialogFragment = ConfirmDialogFragment.newInstance(
111,
0,
R.string.sentry_start_next_launch,
R.string.ok,
0,
0);
dialogFragment.show(fragmentManager, "sentry_dialog");
}
return true;
});
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="enable_sentry">Enable Sentry</string>
<string name="sentry_summary">Sentry is a listener/handler for errors that asynchronously sends out the error/event to Sentry.io</string>
<string name="sentry_start_next_launch">Sentry will start on next launch</string>
</resources>

View File

@ -18,14 +18,12 @@ import awais.instagrabber.utils.LocaleUtils;
import awais.instagrabber.utils.SettingsHelper; import awais.instagrabber.utils.SettingsHelper;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import awaisomereport.CrashReporter; import awaisomereport.CrashReporter;
//import awaisomereport.LogCollector;
import static awais.instagrabber.utils.CookieUtils.NET_COOKIE_MANAGER; import static awais.instagrabber.utils.CookieUtils.NET_COOKIE_MANAGER;
import static awais.instagrabber.utils.Utils.applicationHandler; import static awais.instagrabber.utils.Utils.applicationHandler;
import static awais.instagrabber.utils.Utils.cacheDir; import static awais.instagrabber.utils.Utils.cacheDir;
import static awais.instagrabber.utils.Utils.clipboardManager; import static awais.instagrabber.utils.Utils.clipboardManager;
import static awais.instagrabber.utils.Utils.datetimeParser; import static awais.instagrabber.utils.Utils.datetimeParser;
//import static awais.instagrabber.utils.Utils.logCollector;
import static awais.instagrabber.utils.Utils.settingsHelper; import static awais.instagrabber.utils.Utils.settingsHelper;
public final class InstaGrabberApplication extends Application { public final class InstaGrabberApplication extends Application {
@ -34,16 +32,16 @@ public final class InstaGrabberApplication extends Application {
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
// final Set<RequestListener> requestListeners = new HashSet<>(); CookieHandler.setDefault(NET_COOKIE_MANAGER);
// requestListeners.add(new RequestLoggingListener());
final ImagePipelineConfig imagePipelineConfig = ImagePipelineConfig if (settingsHelper == null) {
.newBuilder(this) settingsHelper = new SettingsHelper(this);
// .setMainDiskCacheConfig(diskCacheConfig) }
// .setRequestListeners(requestListeners)
.setDownsampleEnabled(true) if (!BuildConfig.DEBUG) {
.build(); CrashReporter.get(this).start();
Fresco.initialize(this, imagePipelineConfig); }
// FLog.setMinimumLoggingLevel(FLog.VERBOSE); // logCollector = new LogCollector(this);
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
try { try {
@ -55,13 +53,16 @@ public final class InstaGrabberApplication extends Application {
} }
} }
if (!BuildConfig.DEBUG) CrashReporter.get(this).start(); // final Set<RequestListener> requestListeners = new HashSet<>();
// logCollector = new LogCollector(this); // requestListeners.add(new RequestLoggingListener());
final ImagePipelineConfig imagePipelineConfig = ImagePipelineConfig
CookieHandler.setDefault(NET_COOKIE_MANAGER); .newBuilder(this)
// .setMainDiskCacheConfig(diskCacheConfig)
if (settingsHelper == null) // .setRequestListeners(requestListeners)
settingsHelper = new SettingsHelper(this); .setDownsampleEnabled(true)
.build();
Fresco.initialize(this, imagePipelineConfig);
// FLog.setMinimumLoggingLevel(FLog.VERBOSE);
if (applicationHandler == null) { if (applicationHandler == null) {
applicationHandler = new Handler(getApplicationContext().getMainLooper()); applicationHandler = new Handler(getApplicationContext().getMainLooper());

View File

@ -10,7 +10,6 @@ import android.content.Intent;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.database.MatrixCursor; import android.database.MatrixCursor;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; 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.ImmutableList;
import com.google.common.collect.Iterators; import com.google.common.collect.Iterators;
import java.util.ArrayList;
import java.util.Deque; import java.util.Deque;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -60,18 +60,17 @@ import java.util.stream.Collectors;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.adapters.SuggestionsAdapter; import awais.instagrabber.adapters.SuggestionsAdapter;
import awais.instagrabber.asyncs.PostFetcher; import awais.instagrabber.asyncs.PostFetcher;
import awais.instagrabber.asyncs.SuggestionsFetcher;
import awais.instagrabber.customviews.emoji.EmojiVariantManager; import awais.instagrabber.customviews.emoji.EmojiVariantManager;
import awais.instagrabber.databinding.ActivityMainBinding; import awais.instagrabber.databinding.ActivityMainBinding;
import awais.instagrabber.fragments.PostViewV2Fragment; import awais.instagrabber.fragments.PostViewV2Fragment;
import awais.instagrabber.fragments.directmessages.DirectMessageInboxFragmentDirections; import awais.instagrabber.fragments.directmessages.DirectMessageInboxFragmentDirections;
import awais.instagrabber.fragments.main.FeedFragment; import awais.instagrabber.fragments.main.FeedFragment;
import awais.instagrabber.fragments.settings.PreferenceKeys; import awais.instagrabber.fragments.settings.PreferenceKeys;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.IntentModel; import awais.instagrabber.models.IntentModel;
import awais.instagrabber.models.SuggestionModel;
import awais.instagrabber.models.Tab; import awais.instagrabber.models.Tab;
import awais.instagrabber.models.enums.SuggestionType; 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.ActivityCheckerService;
import awais.instagrabber.services.DMSyncAlarmReceiver; import awais.instagrabber.services.DMSyncAlarmReceiver;
import awais.instagrabber.utils.AppExecutors; import awais.instagrabber.utils.AppExecutors;
@ -83,6 +82,10 @@ import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.emoji.EmojiParser; import awais.instagrabber.utils.emoji.EmojiParser;
import awais.instagrabber.viewmodels.AppStateViewModel; 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.NavigationExtensions.setupWithNavController;
import static awais.instagrabber.utils.Utils.settingsHelper; import static awais.instagrabber.utils.Utils.settingsHelper;
@ -105,6 +108,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
private SuggestionsAdapter suggestionAdapter; private SuggestionsAdapter suggestionAdapter;
private AutoCompleteTextView searchAutoComplete; private AutoCompleteTextView searchAutoComplete;
private SearchView searchView; private SearchView searchView;
private SearchService searchService;
private boolean showSearch = true; private boolean showSearch = true;
private Handler suggestionsFetchHandler; private Handler suggestionsFetchHandler;
private int firstFragmentGraphIndex; private int firstFragmentGraphIndex;
@ -167,6 +171,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
EmojiVariantManager.getInstance(); EmojiVariantManager.getInstance();
}); });
initEmojiCompat(); initEmojiCompat();
searchService = SearchService.getInstance();
// initDmService(); // initDmService();
} }
@ -300,7 +305,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
final Bundle bundle = new Bundle(); final Bundle bundle = new Bundle();
switch (type) { switch (type) {
case TYPE_LOCATION: case TYPE_LOCATION:
bundle.putString("locationId", query); bundle.putLong("locationId", Long.valueOf(query));
navController.navigate(R.id.action_global_locationFragment, bundle); navController.navigate(R.id.action_global_locationFragment, bundle);
break; break;
case TYPE_HASHTAG: case TYPE_HASHTAG:
@ -328,51 +333,84 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
private boolean searchUser; private boolean searchUser;
private boolean searchHash; private boolean searchHash;
private AsyncTask<?, ?, ?> prevSuggestionAsync; private Call<SearchResponse> prevSuggestionAsync;
private final String[] COLUMNS = { private final String[] COLUMNS = {
BaseColumns._ID, BaseColumns._ID,
Constants.EXTRAS_USERNAME, Constants.EXTRAS_USERNAME,
Constants.EXTRAS_NAME, Constants.EXTRAS_NAME,
Constants.EXTRAS_TYPE, Constants.EXTRAS_TYPE,
"query",
"pfp", "pfp",
"verified" "verified"
}; };
private String currentSearchQuery; private String currentSearchQuery;
private final FetchListener<SuggestionModel[]> fetchListener = new FetchListener<SuggestionModel[]>() { private final Callback<SearchResponse> cb = new Callback<SearchResponse>() {
@Override @Override
public void doBefore() { public void onResponse(@NonNull final Call<SearchResponse> call,
suggestionAdapter.changeCursor(null); @NonNull final Response<SearchResponse> response) {
}
@Override
public void onResult(final SuggestionModel[] result) {
final MatrixCursor cursor; final MatrixCursor cursor;
if (result == null) cursor = null; final SearchResponse body = response.body();
if (body == null) {
cursor = null;
return;
}
final List<SearchItem> 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 { else {
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); cursor = new MatrixCursor(COLUMNS, 0);
for (int i = 0; i < result.length; i++) { for (int i = 0; i < result.size(); i++) {
final SuggestionModel suggestionModel = result[i]; final SearchItem suggestionModel = result.get(i);
if (suggestionModel != null) { if (suggestionModel != null) {
final SuggestionType suggestionType = suggestionModel.getSuggestionType(); Object[] objects = null;
final Object[] objects = { if (suggestionModel.getUser() != null)
i, objects = new Object[]{
suggestionType == SuggestionType.TYPE_LOCATION ? suggestionModel.getName() : suggestionModel.getUsername(), suggestionModel.getPosition(),
suggestionType == SuggestionType.TYPE_LOCATION ? suggestionModel.getUsername() : suggestionModel.getName(), suggestionModel.getUser().getUsername(),
suggestionType, suggestionModel.getUser().getFullName(),
suggestionModel.getProfilePic(), SuggestionType.TYPE_USER,
suggestionModel.isVerified()}; suggestionModel.getUser().getUsername(),
if (!searchHash && !searchUser) cursor.addRow(objects); suggestionModel.getUser().getProfilePicUrl(),
else { suggestionModel.getUser().isVerified()};
final boolean isCurrHash = suggestionType == SuggestionType.TYPE_HASHTAG; else if (suggestionModel.getHashtag() != null)
if (searchHash && isCurrHash || !searchHash && !isCurrHash) 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); cursor.addRow(objects);
} }
} }
}
}
suggestionAdapter.changeCursor(cursor); suggestionAdapter.changeCursor(cursor);
} }
@Override
public void onFailure(@NonNull final Call<SearchResponse> call,
Throwable t) {
if (!call.isCanceled() && t != null)
Log.e(TAG, "Exception on search:", t);
}
}; };
private final Runnable runnable = () -> { private final Runnable runnable = () -> {
@ -391,17 +429,19 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
if (searchAutoComplete != null) { if (searchAutoComplete != null) {
searchAutoComplete.setThreshold(1); searchAutoComplete.setThreshold(1);
} }
prevSuggestionAsync = new SuggestionsFetcher(fetchListener).executeOnExecutor( prevSuggestionAsync = searchService.search(isLoggedIn,
AsyncTask.THREAD_POOL_EXECUTOR,
searchUser || searchHash ? currentSearchQuery.substring(1) searchUser || searchHash ? currentSearchQuery.substring(1)
: currentSearchQuery); : currentSearchQuery,
searchUser ? "user" : (searchHash ? "hashtag" : "blended"));
suggestionAdapter.changeCursor(null);
prevSuggestionAsync.enqueue(cb);
} }
}; };
private void cancelSuggestionsAsync() { private void cancelSuggestionsAsync() {
if (prevSuggestionAsync != null) if (prevSuggestionAsync != null)
try { try {
prevSuggestionAsync.cancel(true); prevSuggestionAsync.cancel();
} catch (final Exception ignored) {} } catch (final Exception ignored) {}
} }
@ -728,7 +768,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
final NavController navController = currentNavControllerLiveData.getValue(); final NavController navController = currentNavControllerLiveData.getValue();
if (navController == null) return; if (navController == null) return;
final Bundle bundle = new Bundle(); final Bundle bundle = new Bundle();
bundle.putString("locationId", locationId); bundle.putLong("locationId", Long.valueOf(locationId));
navController.navigate(R.id.action_global_locationFragment, bundle); navController.navigate(R.id.action_global_locationFragment, bundle);
} }

View File

@ -16,7 +16,7 @@ import java.util.stream.Collectors;
import awais.instagrabber.adapters.viewholder.NotificationViewHolder; import awais.instagrabber.adapters.viewholder.NotificationViewHolder;
import awais.instagrabber.databinding.ItemNotificationBinding; import awais.instagrabber.databinding.ItemNotificationBinding;
import awais.instagrabber.models.enums.NotificationType; 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<Notification, NotificationViewHolder> { public final class NotificationsAdapter extends ListAdapter<Notification, NotificationViewHolder> {
private final OnNotificationClickListener notificationClickListener; private final OnNotificationClickListener notificationClickListener;

View File

@ -10,7 +10,6 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.cursoradapter.widget.CursorAdapter; import androidx.cursoradapter.widget.CursorAdapter;
import awais.instagrabber.R;
import awais.instagrabber.databinding.ItemSuggestionBinding; import awais.instagrabber.databinding.ItemSuggestionBinding;
import awais.instagrabber.models.enums.SuggestionType; import awais.instagrabber.models.enums.SuggestionType;
@ -35,12 +34,12 @@ public final class SuggestionsAdapter extends CursorAdapter {
@Override @Override
public void bindView(@NonNull final View view, final Context context, @NonNull final Cursor cursor) { public void bindView(@NonNull final View view, final Context context, @NonNull final Cursor cursor) {
// i, username, fullname, type, picUrl, verified // i, username, fullname, type, query, picUrl, verified
// 0, 1 , 2 , 3 , 4 , 5 // 0, 1 , 2 , 3 , 4 , 5 , 6
final String fullName = cursor.getString(2); final String fullName = cursor.getString(2);
String username = cursor.getString(1); String username = cursor.getString(1);
String picUrl = cursor.getString(4); String picUrl = cursor.getString(5);
final boolean verified = cursor.getString(5).charAt(0) == 't'; final boolean verified = cursor.getString(6).charAt(0) == 't';
final String type = cursor.getString(3); final String type = cursor.getString(3);
SuggestionType suggestionType = null; SuggestionType suggestionType = null;
@ -50,22 +49,14 @@ public final class SuggestionsAdapter extends CursorAdapter {
Log.e(TAG, "Unknown suggestion type: " + type, e); Log.e(TAG, "Unknown suggestion type: " + type, e);
} }
if (suggestionType == null) return; if (suggestionType == null) return;
final String query; String query = cursor.getString(4);
switch (suggestionType) { switch (suggestionType) {
case TYPE_USER: case TYPE_USER:
username = '@' + username; username = '@' + username;
query = username;
break; break;
case TYPE_HASHTAG: case TYPE_HASHTAG:
username = '#' + username; username = '#' + username;
query = username;
break; break;
case TYPE_LOCATION:
query = fullName;
picUrl = "res:/" + R.drawable.ic_location;
break;
default:
return; // will never come here
} }
if (onSuggestionClickListener != null) { if (onSuggestionClickListener != null) {
@ -75,12 +66,8 @@ public final class SuggestionsAdapter extends CursorAdapter {
final ItemSuggestionBinding binding = ItemSuggestionBinding.bind(view); final ItemSuggestionBinding binding = ItemSuggestionBinding.bind(view);
binding.isVerified.setVisibility(verified ? View.VISIBLE : View.GONE); binding.isVerified.setVisibility(verified ? View.VISIBLE : View.GONE);
binding.tvUsername.setText(username); binding.tvUsername.setText(username);
if (suggestionType.equals(SuggestionType.TYPE_LOCATION)) {
binding.tvFullName.setVisibility(View.GONE);
} else {
binding.tvFullName.setVisibility(View.VISIBLE); binding.tvFullName.setVisibility(View.VISIBLE);
binding.tvFullName.setText(fullName); binding.tvFullName.setText(fullName);
}
binding.ivProfilePic.setImageURI(picUrl); binding.ivProfilePic.setImageURI(picUrl);
} }

View File

@ -9,8 +9,8 @@ import awais.instagrabber.R;
import awais.instagrabber.adapters.NotificationsAdapter.OnNotificationClickListener; import awais.instagrabber.adapters.NotificationsAdapter.OnNotificationClickListener;
import awais.instagrabber.databinding.ItemNotificationBinding; import awais.instagrabber.databinding.ItemNotificationBinding;
import awais.instagrabber.models.enums.NotificationType; import awais.instagrabber.models.enums.NotificationType;
import awais.instagrabber.repositories.responses.Notification; import awais.instagrabber.repositories.responses.notification.Notification;
import awais.instagrabber.repositories.responses.NotificationArgs; import awais.instagrabber.repositories.responses.notification.NotificationArgs;
public final class NotificationViewHolder extends RecyclerView.ViewHolder { public final class NotificationViewHolder extends RecyclerView.ViewHolder {
private final ItemNotificationBinding binding; private final ItemNotificationBinding binding;

View File

@ -16,7 +16,6 @@ import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmRavenMediaBinding; import awais.instagrabber.databinding.LayoutDmRavenMediaBinding;
import awais.instagrabber.models.enums.MediaItemType; import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.models.enums.RavenMediaViewMode; import awais.instagrabber.models.enums.RavenMediaViewMode;
import awais.instagrabber.repositories.responses.ImageVersions2;
import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User; import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem; 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.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.NumberUtils; import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.ResponseBodyUtils; import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.TextUtils;
public class DirectItemRavenMediaViewHolder extends DirectItemViewHolder { public class DirectItemRavenMediaViewHolder extends DirectItemViewHolder {
@ -48,7 +48,7 @@ public class DirectItemRavenMediaViewHolder extends DirectItemViewHolder {
if (media == null) return; if (media == null) return;
setExpiryInfo(visualMedia); setExpiryInfo(visualMedia);
setPreview(visualMedia, messageDirection); setPreview(visualMedia, messageDirection);
final boolean expired = media.getPk() == null; final boolean expired = TextUtils.isEmpty(media.getId());
if (expired) return; if (expired) return;
itemView.setOnClickListener(v -> openMedia(media)); itemView.setOnClickListener(v -> openMedia(media));
/*final boolean isExpired = visualMedia == null || (mediaModel = visualMedia.getMedia()) == null || /*final boolean isExpired = visualMedia == null || (mediaModel = visualMedia.getMedia()) == null ||
@ -118,7 +118,7 @@ public class DirectItemRavenMediaViewHolder extends DirectItemViewHolder {
final RavenMediaViewMode viewMode = visualMedia.getViewMode(); final RavenMediaViewMode viewMode = visualMedia.getViewMode();
if (viewMode != RavenMediaViewMode.PERMANENT) { if (viewMode != RavenMediaViewMode.PERMANENT) {
final MediaItemType mediaType = media.getMediaType(); final MediaItemType mediaType = media.getMediaType();
final boolean expired = media.getPk() == null; final boolean expired = TextUtils.isEmpty(media.getId());
final int info; final int info;
switch (mediaType) { switch (mediaType) {
case MEDIA_TYPE_IMAGE: case MEDIA_TYPE_IMAGE:
@ -153,7 +153,7 @@ public class DirectItemRavenMediaViewHolder extends DirectItemViewHolder {
private void setPreview(final DirectItemVisualMedia visualMedia, private void setPreview(final DirectItemVisualMedia visualMedia,
final MessageDirection messageDirection) { final MessageDirection messageDirection) {
final Media media = visualMedia.getMedia(); final Media media = visualMedia.getMedia();
final boolean expired = media.getPk() == null; final boolean expired = TextUtils.isEmpty(media.getId());
if (expired) { if (expired) {
binding.preview.setVisibility(View.GONE); binding.preview.setVisibility(View.GONE);
binding.typeIcon.setVisibility(View.GONE); binding.typeIcon.setVisibility(View.GONE);

View File

@ -1,81 +0,0 @@
package awais.instagrabber.asyncs;
import android.os.AsyncTask;
import android.util.Log;
import androidx.annotation.Nullable;
import org.json.JSONObject;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.URL;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.LocationModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.NetworkUtils;
//import awaisomereport.LogCollector;
//import static awais.instagrabber.utils.Utils.logCollector;
public final class LocationFetcher extends AsyncTask<Void, Void, LocationModel> {
private static final String TAG = "LocationFetcher";
private final FetchListener<LocationModel> fetchListener;
private final long id;
public LocationFetcher(final long id, final FetchListener<LocationModel> fetchListener) {
// idSlug = id + "/" + slug UPDATE: slug can be ignored tbh
this.id = id;
this.fetchListener = fetchListener;
}
@Nullable
@Override
protected LocationModel doInBackground(final Void... voids) {
LocationModel result = null;
try {
final HttpURLConnection conn = (HttpURLConnection) new URL("https://www.instagram.com/explore/locations/" + id + "/?__a=1")
.openConnection();
conn.setUseCaches(true);
conn.connect();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
final JSONObject location = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("graphql")
.getJSONObject(Constants.EXTRAS_LOCATION);
final JSONObject timelineMedia = location.getJSONObject("edge_location_to_media");
// if (timelineMedia.has("edges")) {
// final JSONArray edges = timelineMedia.getJSONArray("edges");
// }
result = new LocationModel(
location.getLong(Constants.EXTRAS_ID),
location.getString("name"),
location.getString("blurb"),
location.getString("website"),
location.getString("profile_pic_url"),
timelineMedia.getLong("count"),
BigDecimal.valueOf(location.optDouble("lat", 0d)).toString(),
BigDecimal.valueOf(location.optDouble("lng", 0d)).toString()
);
}
conn.disconnect();
} catch (final Exception e) {
// if (logCollector != null)
// logCollector.appendException(e, LogCollector.LogFile.ASYNC_LOCATION_FETCHER, "doInBackground");
if (BuildConfig.DEBUG) {
Log.e(TAG, "", e);
}
}
return result;
}
@Override
protected void onPostExecute(final LocationModel result) {
if (fetchListener != null) fetchListener.onResult(result);
}
}

View File

@ -4,7 +4,7 @@ import java.util.List;
import awais.instagrabber.customviews.helpers.PostFetcher; import awais.instagrabber.customviews.helpers.PostFetcher;
import awais.instagrabber.interfaces.FetchListener; 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.Media;
import awais.instagrabber.repositories.responses.PostsFetchResponse; import awais.instagrabber.repositories.responses.PostsFetchResponse;
import awais.instagrabber.webservices.GraphQLService; import awais.instagrabber.webservices.GraphQLService;
@ -14,12 +14,12 @@ import awais.instagrabber.webservices.ServiceCallback;
public class LocationPostFetchService implements PostFetcher.PostFetchService { public class LocationPostFetchService implements PostFetcher.PostFetchService {
private final LocationService locationService; private final LocationService locationService;
private final GraphQLService graphQLService; private final GraphQLService graphQLService;
private final LocationModel locationModel; private final Location locationModel;
private String nextMaxId; private String nextMaxId;
private boolean moreAvailable; private boolean moreAvailable;
private final boolean isLoggedIn; private final boolean isLoggedIn;
public LocationPostFetchService(final LocationModel locationModel, final boolean isLoggedIn) { public LocationPostFetchService(final Location locationModel, final boolean isLoggedIn) {
this.locationModel = locationModel; this.locationModel = locationModel;
this.isLoggedIn = isLoggedIn; this.isLoggedIn = isLoggedIn;
locationService = isLoggedIn ? LocationService.getInstance() : null; locationService = isLoggedIn ? LocationService.getInstance() : null;
@ -47,8 +47,8 @@ public class LocationPostFetchService implements PostFetcher.PostFetchService {
} }
} }
}; };
if (isLoggedIn) locationService.fetchPosts(locationModel.getId(), nextMaxId, cb); if (isLoggedIn) locationService.fetchPosts(locationModel.getPk(), nextMaxId, cb);
else graphQLService.fetchLocationPosts(locationModel.getId(), nextMaxId, cb); else graphQLService.fetchLocationPosts(locationModel.getPk(), nextMaxId, cb);
} }
@Override @Override

View File

@ -1,113 +0,0 @@
package awais.instagrabber.asyncs;
import android.os.AsyncTask;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.InterruptedIOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.SuggestionModel;
import awais.instagrabber.models.enums.SuggestionType;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.NetworkUtils;
import awais.instagrabber.utils.UrlEncoder;
public final class SuggestionsFetcher extends AsyncTask<String, String, SuggestionModel[]> {
private final FetchListener<SuggestionModel[]> fetchListener;
public SuggestionsFetcher(final FetchListener<SuggestionModel[]> fetchListener) {
this.fetchListener = fetchListener;
}
@Override
protected void onPreExecute() {
if (fetchListener != null) fetchListener.doBefore();
}
@Override
protected SuggestionModel[] doInBackground(final String... params) {
SuggestionModel[] result = null;
try {
final HttpURLConnection conn = (HttpURLConnection) new URL("https://www.instagram.com/web/search/topsearch/?context=blended&count=50&query="
+ UrlEncoder.encodeUrl(params[0])).openConnection();
conn.setUseCaches(false);
conn.connect();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
final JSONObject jsonObject = new JSONObject(NetworkUtils.readFromConnection(conn));
conn.disconnect();
final JSONArray usersArray = jsonObject.getJSONArray("users");
final JSONArray hashtagsArray = jsonObject.getJSONArray("hashtags");
final JSONArray placesArray = jsonObject.getJSONArray("places");
final int usersLen = usersArray.length();
final int hashtagsLen = hashtagsArray.length();
final int placesLen = placesArray.length();
final ArrayList<SuggestionModel> suggestionModels = new ArrayList<>(usersLen + hashtagsLen);
for (int i = 0; i < hashtagsLen; i++) {
final JSONObject hashtagsArrayJSONObject = hashtagsArray.getJSONObject(i);
final JSONObject hashtag = hashtagsArrayJSONObject.getJSONObject("hashtag");
suggestionModels.add(new SuggestionModel(false,
hashtag.getString(Constants.EXTRAS_NAME),
null,
hashtag.optString("profile_pic_url", Constants.DEFAULT_HASH_TAG_PIC),
SuggestionType.TYPE_HASHTAG,
hashtagsArrayJSONObject.optInt("position", suggestionModels.size() - 1)));
}
for (int i = 0; i < placesLen; i++) {
final JSONObject placesArrayJSONObject = placesArray.getJSONObject(i);
final JSONObject place = placesArrayJSONObject.getJSONObject("place");
// name
suggestionModels.add(new SuggestionModel(false,
place.getJSONObject("location").getString("pk"), // +"/"+place.getString("slug"),
place.getString("title"),
place.optString("profile_pic_url"),
SuggestionType.TYPE_LOCATION,
placesArrayJSONObject.optInt("position", suggestionModels.size() - 1)));
}
for (int i = 0; i < usersLen; i++) {
final JSONObject usersArrayJSONObject = usersArray.getJSONObject(i);
final JSONObject user = usersArrayJSONObject.getJSONObject(Constants.EXTRAS_USER);
suggestionModels.add(new SuggestionModel(user.getBoolean("is_verified"),
user.getString(Constants.EXTRAS_USERNAME),
user.getString("full_name"),
user.getString("profile_pic_url"),
SuggestionType.TYPE_USER,
usersArrayJSONObject.optInt("position", suggestionModels.size() - 1)));
}
suggestionModels.trimToSize();
Collections.sort(suggestionModels);
result = suggestionModels.toArray(new SuggestionModel[0]);
}
} catch (final Exception e) {
if (BuildConfig.DEBUG && !(e instanceof InterruptedIOException)) Log.e("AWAISKING_APP", "", e);
}
return result;
}
@Override
protected void onPostExecute(final SuggestionModel[] result) {
if (fetchListener != null) fetchListener.onResult(result);
}
}

View File

@ -8,6 +8,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
@ -17,6 +18,9 @@ public class ConfirmDialogFragment extends DialogFragment {
private Context context; private Context context;
private ConfirmDialogFragmentCallback callback; private ConfirmDialogFragmentCallback callback;
private final int defaultPositiveButtonText = R.string.ok;
// private final int defaultNegativeButtonText = R.string.cancel;
@NonNull @NonNull
public static ConfirmDialogFragment newInstance(final int requestCode, public static ConfirmDialogFragment newInstance(final int requestCode,
@StringRes final int title, @StringRes final int title,
@ -26,11 +30,21 @@ public class ConfirmDialogFragment extends DialogFragment {
@StringRes final int neutralText) { @StringRes final int neutralText) {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putInt("requestCode", requestCode); args.putInt("requestCode", requestCode);
if (title != 0) {
args.putInt("title", title); args.putInt("title", title);
}
if (message != 0) {
args.putInt("message", message); args.putInt("message", message);
}
if (positiveText != 0) {
args.putInt("positive", positiveText); args.putInt("positive", positiveText);
}
if (negativeText != 0) {
args.putInt("negative", negativeText); args.putInt("negative", negativeText);
}
if (neutralText != 0) {
args.putInt("neutral", neutralText); args.putInt("neutral", neutralText);
}
ConfirmDialogFragment fragment = new ConfirmDialogFragment(); ConfirmDialogFragment fragment = new ConfirmDialogFragment();
fragment.setArguments(args); fragment.setArguments(args);
return fragment; return fragment;
@ -41,10 +55,9 @@ public class ConfirmDialogFragment extends DialogFragment {
@Override @Override
public void onAttach(@NonNull final Context context) { public void onAttach(@NonNull final Context context) {
super.onAttach(context); super.onAttach(context);
try { final Fragment parentFragment = getParentFragment();
callback = (ConfirmDialogFragmentCallback) getParentFragment(); if (parentFragment instanceof ConfirmDialogFragmentCallback) {
} catch (ClassCastException e) { callback = (ConfirmDialogFragmentCallback) parentFragment;
throw new ClassCastException("Calling fragment must implement ConfirmDialogFragmentCallback interface");
} }
this.context = context; this.context = context;
} }
@ -53,38 +66,42 @@ public class ConfirmDialogFragment extends DialogFragment {
@Override @Override
public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) { public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {
final Bundle arguments = getArguments(); final Bundle arguments = getArguments();
int title = -1; int title = 0;
int message = -1; int message = 0;
int positiveButtonText = R.string.ok; int neutralButtonText = 0;
int negativeButtonText = R.string.cancel; int negativeButtonText = 0;
int neutralButtonText = -1;
final int positiveButtonText;
final int requestCode; final int requestCode;
if (arguments != null) { if (arguments != null) {
title = arguments.getInt("title", -1); title = arguments.getInt("title", 0);
message = arguments.getInt("message", -1); message = arguments.getInt("message", 0);
positiveButtonText = arguments.getInt("positive", R.string.ok); positiveButtonText = arguments.getInt("positive", defaultPositiveButtonText);
negativeButtonText = arguments.getInt("negative", R.string.cancel); negativeButtonText = arguments.getInt("negative", 0);
neutralButtonText = arguments.getInt("neutral", -1); neutralButtonText = arguments.getInt("neutral", 0);
requestCode = arguments.getInt("requestCode", 0); requestCode = arguments.getInt("requestCode", 0);
} else { } else {
requestCode = 0; requestCode = 0;
positiveButtonText = defaultPositiveButtonText;
} }
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context) final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context)
.setPositiveButton(positiveButtonText, (d, w) -> { .setPositiveButton(positiveButtonText, (d, w) -> {
if (callback == null) return; if (callback == null) return;
callback.onPositiveButtonClicked(requestCode); callback.onPositiveButtonClicked(requestCode);
}) });
.setNegativeButton(negativeButtonText, (dialog, which) -> { if (title != 0) {
builder.setTitle(title);
}
if (message != 0) {
builder.setMessage(message);
}
if (negativeButtonText != 0) {
builder.setNegativeButton(negativeButtonText, (dialog, which) -> {
if (callback == null) return; if (callback == null) return;
callback.onNegativeButtonClicked(requestCode); callback.onNegativeButtonClicked(requestCode);
}); });
if (title > 0) {
builder.setTitle(title);
} }
if (message > 0) { if (neutralButtonText != 0) {
builder.setMessage(message);
}
if (neutralButtonText > 0) {
builder.setNeutralButton(neutralButtonText, (dialog, which) -> { builder.setNeutralButton(neutralButtonText, (dialog, which) -> {
if (callback == null) return; if (callback == null) return;
callback.onNeutralButtonClicked(requestCode); callback.onNeutralButtonClicked(requestCode);

View File

@ -2,7 +2,6 @@ package awais.instagrabber.fragments;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -57,7 +56,6 @@ public final class FollowViewerFragment extends Fragment implements SwipeRefresh
private FollowAdapter adapter; private FollowAdapter adapter;
private View.OnClickListener clickListener; private View.OnClickListener clickListener;
private FragmentFollowersViewerBinding binding; private FragmentFollowersViewerBinding binding;
private AsyncTask<Void, Void, FollowModel[]> currentlyExecuting;
private SwipeRefreshLayout root; private SwipeRefreshLayout root;
private FriendshipService friendshipService; private FriendshipService friendshipService;
private AppCompatActivity fragmentActivity; private AppCompatActivity fragmentActivity;

View File

@ -3,7 +3,6 @@ package awais.instagrabber.fragments;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.text.SpannableStringBuilder; import android.text.SpannableStringBuilder;
@ -95,7 +94,6 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
private Hashtag hashtagModel = null; private Hashtag hashtagModel = null;
private ActionMode actionMode; private ActionMode actionMode;
private StoriesService storiesService; private StoriesService storiesService;
private AsyncTask<?, ?, ?> currentlyExecuting;
private boolean isLoggedIn; private boolean isLoggedIn;
private TagsService tagsService; private TagsService tagsService;
private GraphQLService graphQLService; private GraphQLService graphQLService;
@ -474,7 +472,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
hashtag, hashtag,
FavoriteType.HASHTAG, FavoriteType.HASHTAG,
hashtagModel.getName(), hashtagModel.getName(),
hashtagModel.getProfilePicUrl(), "res:/" + R.drawable.ic_hashtag,
result.getDateAdded() result.getDateAdded()
), new RepositoryCallback<Void>() { ), new RepositoryCallback<Void>() {
@Override @Override
@ -518,7 +516,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
hashtag, hashtag,
FavoriteType.HASHTAG, FavoriteType.HASHTAG,
hashtagModel.getName(), hashtagModel.getName(),
hashtagModel.getProfilePicUrl(), "res:/" + R.drawable.ic_hashtag,
new Date() new Date()
), new RepositoryCallback<Void>() { ), new RepositoryCallback<Void>() {
@Override @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 String postCount = String.valueOf(hashtagModel.getMediaCount());
final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline, final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline,
hashtagModel.getMediaCount() > 2000000000L hashtagModel.getMediaCount() > 2000000000L

View File

@ -3,14 +3,9 @@ package awais.instagrabber.fragments;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.Typeface;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.text.SpannableStringBuilder;
import android.text.style.RelativeSizeSpan;
import android.text.style.StyleSpan;
import android.util.Log; import android.util.Log;
import android.view.ActionMode; import android.view.ActionMode;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -45,7 +40,6 @@ import java.util.Set;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.activities.MainActivity; import awais.instagrabber.activities.MainActivity;
import awais.instagrabber.adapters.FeedAdapterV2; import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.asyncs.LocationFetcher;
import awais.instagrabber.asyncs.LocationPostFetchService; import awais.instagrabber.asyncs.LocationPostFetchService;
import awais.instagrabber.asyncs.PostFetcher; import awais.instagrabber.asyncs.PostFetcher;
import awais.instagrabber.customviews.PrimaryActionModeCallback; 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.FavoriteRepository;
import awais.instagrabber.db.repositories.RepositoryCallback; import awais.instagrabber.db.repositories.RepositoryCallback;
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment; import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
import awais.instagrabber.models.LocationModel;
import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.models.PostsLayoutPreferences;
import awais.instagrabber.models.StoryModel; import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.enums.FavoriteType; import awais.instagrabber.models.enums.FavoriteType;
import awais.instagrabber.repositories.requests.StoryViewerOptions; import awais.instagrabber.repositories.requests.StoryViewerOptions;
import awais.instagrabber.repositories.responses.Location;
import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.DownloadUtils; import awais.instagrabber.utils.DownloadUtils;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
import awais.instagrabber.webservices.GraphQLService;
import awais.instagrabber.webservices.LocationService;
import awais.instagrabber.webservices.ServiceCallback; import awais.instagrabber.webservices.ServiceCallback;
import awais.instagrabber.webservices.StoriesService; import awais.instagrabber.webservices.StoriesService;
//import awaisomereport.LogCollector;
import static androidx.core.content.PermissionChecker.checkSelfPermission; 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.DownloadUtils.WRITE_PERMISSION;
//import static awais.instagrabber.utils.Utils.logCollector;
import static awais.instagrabber.utils.Utils.settingsHelper; 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 { public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = "LocationFragment"; private static final String TAG = "LocationFragment";
private static final int STORAGE_PERM_REQUEST_CODE = 8020; 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 hasStories = false;
private boolean opening = false; private boolean opening = false;
private long locationId; private long locationId;
private LocationModel locationModel; private Location locationModel;
private ActionMode actionMode; private ActionMode actionMode;
private StoriesService storiesService; private StoriesService storiesService;
private AsyncTask<?, ?, ?> currentlyExecuting; private GraphQLService graphQLService;
private LocationService locationService;
private boolean isLoggedIn; private boolean isLoggedIn;
private boolean storiesFetching; private boolean storiesFetching;
private Set<Media> selectedFeedModels; private Set<Media> selectedFeedModels;
@ -265,12 +262,29 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
} }
} }
}; };
private final ServiceCallback<Location> cb = new ServiceCallback<Location>() {
@Override
public void onSuccess(final Location result) {
locationModel = result;
binding.swipeRefreshLayout.setRefreshing(false);
setupLocationDetails();
}
@Override
public void onFailure(final Throwable t) {
setupLocationDetails();
}
};
@Override @Override
public void onCreate(@Nullable final Bundle savedInstanceState) { public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
fragmentActivity = (MainActivity) requireActivity(); 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); storiesService = StoriesService.getInstance(null, 0L, null);
graphQLService = isLoggedIn ? null : GraphQLService.getInstance();
setHasOptionsMenu(true); setHasOptionsMenu(true);
} }
@ -354,8 +368,6 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
private void init() { private void init() {
if (getArguments() == null) return; 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()); final LocationFragmentArgs fragmentArgs = LocationFragmentArgs.fromBundle(getArguments());
locationId = fragmentArgs.getLocationId(); locationId = fragmentArgs.getLocationId();
locationDetailsBinding.favChip.setVisibility(View.GONE); locationDetailsBinding.favChip.setVisibility(View.GONE);
@ -377,42 +389,38 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
} }
private void fetchLocationModel() { private void fetchLocationModel() {
stopCurrentExecutor();
binding.swipeRefreshLayout.setRefreshing(true); binding.swipeRefreshLayout.setRefreshing(true);
currentlyExecuting = new LocationFetcher(locationId, result -> { if (isLoggedIn) locationService.fetch(locationId, cb);
locationModel = result; else graphQLService.fetchLocation(locationId, cb);
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);
} }
private void setupLocationDetails() { 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); // binding.swipeRefreshLayout.setRefreshing(true);
locationDetailsBinding.mainLocationImage.setImageURI(locationModel.getSdProfilePic()); locationDetailsBinding.mainLocationImage.setImageURI("res:/" + R.drawable.ic_location);
final String postCount = String.valueOf(locationModel.getPostCount()); // final String postCount = String.valueOf(locationModel.getCount());
final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline, // final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline,
locationModel.getPostCount() > 2000000000L // locationModel.getPostCount() > 2000000000L
? 2000000000 // ? 2000000000
: locationModel.getPostCount().intValue(), // : locationModel.getPostCount().intValue(),
postCount)); // postCount));
span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0); // span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0);
span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0); // span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0);
locationDetailsBinding.mainLocPostCount.setText(span); // locationDetailsBinding.mainLocPostCount.setText(span);
locationDetailsBinding.mainLocPostCount.setVisibility(View.VISIBLE); // locationDetailsBinding.mainLocPostCount.setVisibility(View.VISIBLE);
locationDetailsBinding.locationFullName.setText(locationModel.getName()); locationDetailsBinding.locationFullName.setText(locationModel.getName());
CharSequence biography = locationModel.getBio(); CharSequence biography = locationModel.getAddress() + "\n" + locationModel.getCity();
// binding.locationBiography.setCaptionIsExpandable(true); // binding.locationBiography.setCaptionIsExpandable(true);
// binding.locationBiography.setCaptionIsExpanded(true); // binding.locationBiography.setCaptionIsExpanded(true);
@ -423,22 +431,22 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
} else { } else {
locationDetailsBinding.locationBiography.setVisibility(View.VISIBLE); locationDetailsBinding.locationBiography.setVisibility(View.VISIBLE);
locationDetailsBinding.locationBiography.setText(biography); locationDetailsBinding.locationBiography.setText(biography);
locationDetailsBinding.locationBiography.addOnHashtagListener(autoLinkItem -> { // locationDetailsBinding.locationBiography.addOnHashtagListener(autoLinkItem -> {
final NavController navController = NavHostFragment.findNavController(this); // final NavController navController = NavHostFragment.findNavController(this);
final Bundle bundle = new Bundle(); // final Bundle bundle = new Bundle();
final String originalText = autoLinkItem.getOriginalText().trim(); // final String originalText = autoLinkItem.getOriginalText().trim();
bundle.putString(ARG_HASHTAG, originalText); // bundle.putString(ARG_HASHTAG, originalText);
navController.navigate(R.id.action_global_hashTagFragment, bundle); // navController.navigate(R.id.action_global_hashTagFragment, bundle);
}); // });
locationDetailsBinding.locationBiography.addOnMentionClickListener(autoLinkItem -> { // locationDetailsBinding.locationBiography.addOnMentionClickListener(autoLinkItem -> {
final String originalText = autoLinkItem.getOriginalText().trim(); // final String originalText = autoLinkItem.getOriginalText().trim();
navigateToProfile(originalText); // navigateToProfile(originalText);
}); // });
locationDetailsBinding.locationBiography.addOnEmailClickListener(autoLinkItem -> Utils.openEmailAddress(context, // locationDetailsBinding.locationBiography.addOnEmailClickListener(autoLinkItem -> Utils.openEmailAddress(context,
autoLinkItem.getOriginalText() // autoLinkItem.getOriginalText()
.trim())); // .trim()));
locationDetailsBinding.locationBiography // locationDetailsBinding.locationBiography
.addOnURLClickListener(autoLinkItem -> Utils.openURL(context, autoLinkItem.getOriginalText().trim())); // .addOnURLClickListener(autoLinkItem -> Utils.openURL(context, autoLinkItem.getOriginalText().trim()));
locationDetailsBinding.locationBiography.setOnLongClickListener(v -> { locationDetailsBinding.locationBiography.setOnLongClickListener(v -> {
Utils.copyText(context, biography); Utils.copyText(context, biography);
return true; return true;
@ -457,16 +465,6 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
locationDetailsBinding.btnMap.setOnClickListener(null); 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 FavoriteDataSource dataSource = FavoriteDataSource.getInstance(context);
final FavoriteRepository favoriteRepository = FavoriteRepository.getInstance(dataSource); final FavoriteRepository favoriteRepository = FavoriteRepository.getInstance(dataSource);
locationDetailsBinding.favChip.setVisibility(View.VISIBLE); locationDetailsBinding.favChip.setVisibility(View.VISIBLE);
@ -481,7 +479,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
String.valueOf(locationId), String.valueOf(locationId),
FavoriteType.LOCATION, FavoriteType.LOCATION,
locationModel.getName(), locationModel.getName(),
locationModel.getSdProfilePic(), "res:/" + R.drawable.ic_location,
result.getDateAdded() result.getDateAdded()
), new RepositoryCallback<Void>() { ), new RepositoryCallback<Void>() {
@Override @Override
@ -523,7 +521,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
String.valueOf(locationId), String.valueOf(locationId),
FavoriteType.LOCATION, FavoriteType.LOCATION,
locationModel.getName(), locationModel.getName(),
locationModel.getSdProfilePic(), "res:/" + R.drawable.ic_location,
new Date() new Date()
), new RepositoryCallback<Void>() { ), new RepositoryCallback<Void>() {
@Override @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() { private void setTitle() {
final ActionBar actionBar = fragmentActivity.getSupportActionBar(); final ActionBar actionBar = fragmentActivity.getSupportActionBar();
if (actionBar != null && locationModel != null) { if (actionBar != null && locationModel != null) {

View File

@ -2,7 +2,6 @@ package awais.instagrabber.fragments;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.Spanned; import android.text.Spanned;
@ -37,9 +36,9 @@ import awais.instagrabber.models.enums.NotificationType;
import awais.instagrabber.repositories.requests.StoryViewerOptions; import awais.instagrabber.repositories.requests.StoryViewerOptions;
import awais.instagrabber.repositories.responses.FriendshipChangeResponse; import awais.instagrabber.repositories.responses.FriendshipChangeResponse;
import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.Notification; import awais.instagrabber.repositories.responses.notification.Notification;
import awais.instagrabber.repositories.responses.NotificationArgs; import awais.instagrabber.repositories.responses.notification.NotificationArgs;
import awais.instagrabber.repositories.responses.NotificationImage; import awais.instagrabber.repositories.responses.notification.NotificationImage;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;

View File

@ -5,6 +5,7 @@ import android.content.Context;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
@ -104,6 +105,11 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
public void onPause() { public void onPause() {
super.onPause(); super.onPause();
unregisterReceiver(); unregisterReceiver();
isPendingRequestTotalBadgeAttached = false;
if (pendingRequestTotalBadgeDrawable != null) {
BadgeUtils.detachBadgeDrawable(pendingRequestTotalBadgeDrawable, fragmentActivity.getToolbar(), pendingRequestsMenuItem.getItemId());
pendingRequestTotalBadgeDrawable = null;
}
} }
@Override @Override
@ -124,21 +130,13 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();
unregisterReceiver(); unregisterReceiver();
isPendingRequestTotalBadgeAttached = false;
if (pendingRequestTotalBadgeDrawable != null) {
BadgeUtils.detachBadgeDrawable(pendingRequestTotalBadgeDrawable, fragmentActivity.getToolbar(), pendingRequestsMenuItem.getItemId());
pendingRequestTotalBadgeDrawable = null;
}
} }
@Override @Override
public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) { public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.dm_inbox_menu, menu);
pendingRequestsMenuItem = menu.add(Menu.NONE, R.id.pending_requests, Menu.NONE, "Pending requests"); pendingRequestsMenuItem = menu.findItem(R.id.pending_requests);
pendingRequestsMenuItem.setIcon(R.drawable.ic_account_clock_24) pendingRequestsMenuItem.setVisible(isPendingRequestTotalBadgeAttached);
.setVisible(false)
.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS);
attachPendingRequestsBadge(viewModel.getPendingRequestsTotal().getValue());
} }
@Override @Override
@ -213,7 +211,16 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
@SuppressLint("UnsafeExperimentalUsageError") @SuppressLint("UnsafeExperimentalUsageError")
private void attachPendingRequestsBadge(@Nullable final Integer count) { 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) { if (pendingRequestTotalBadgeDrawable == null) {
final Context context = getContext(); final Context context = getContext();
if (context == null) return; if (context == null) return;

View File

@ -186,7 +186,7 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi
R.string.admin_approval_required_description, R.string.admin_approval_required_description,
R.string.ok, R.string.ok,
R.string.cancel, R.string.cancel,
-1 0
); );
confirmDialogFragment.show(getChildFragmentManager(), "approval_required_dialog"); confirmDialogFragment.show(getChildFragmentManager(), "approval_required_dialog");
return; return;
@ -272,10 +272,10 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi
final ConfirmDialogFragment confirmDialogFragment = ConfirmDialogFragment.newInstance( final ConfirmDialogFragment confirmDialogFragment = ConfirmDialogFragment.newInstance(
LEAVE_THREAD_REQUEST_CODE, LEAVE_THREAD_REQUEST_CODE,
R.string.dms_action_leave_question, R.string.dms_action_leave_question,
-1, 0,
R.string.yes, R.string.yes,
R.string.no, R.string.no,
-1 0
); );
confirmDialogFragment.show(getChildFragmentManager(), "leave_thread_confirmation_dialog"); 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.dms_action_end_description,
R.string.yes, R.string.yes,
R.string.no, R.string.no,
-1 0
); );
confirmDialogFragment.show(getChildFragmentManager(), "end_thread_confirmation_dialog"); confirmDialogFragment.show(getChildFragmentManager(), "end_thread_confirmation_dialog");
}); });

View File

@ -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 @NonNull
private User getUser(final long userId) { private User getUser(final long userId) {
for (final User user : users) { for (final User user : users) {
@ -1426,58 +1337,6 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
return null; 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, Void, Void> {
// 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() { private void setupKbHeightProvider() {
if (heightProvider != null) return; if (heightProvider != null) return;
heightProvider = new HeightProvider(fragmentActivity).init().setHeightListener(height -> { heightProvider = new HeightProvider(fragmentActivity).init().setHeightListener(height -> {

View File

@ -10,6 +10,8 @@ import androidx.preference.Preference;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreferenceCompat; import androidx.preference.SwitchPreferenceCompat;
import java.util.List;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.dialogs.TabOrderPreferenceDialogFragment; import awais.instagrabber.dialogs.TabOrderPreferenceDialogFragment;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
@ -32,6 +34,14 @@ public class GeneralPreferencesFragment extends BasePreferencesFragment implemen
} }
screen.addPreference(getUpdateCheckPreference(context)); screen.addPreference(getUpdateCheckPreference(context));
screen.addPreference(getFlagSecurePreference(context)); screen.addPreference(getFlagSecurePreference(context));
final List<Preference> 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) { private Preference getDefaultTabPreference(@NonNull final Context context) {

View File

@ -0,0 +1,14 @@
package awais.instagrabber.fragments.settings;
import android.content.Context;
import androidx.fragment.app.FragmentManager;
import androidx.preference.Preference;
import java.util.List;
public interface IFlavorSettings {
List<Preference> getPreferences(Context context,
FragmentManager childFragmentManager,
SettingCategory settingCategory);
}

View File

@ -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 = "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_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_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"; public static final String PREF_TAB_ORDER = "tab_order";
} }

View File

@ -0,0 +1,6 @@
package awais.instagrabber.fragments.settings;
public enum SettingCategory {
GENERAL,
// add more as and when required
}

View File

@ -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; }
}

View File

@ -1,51 +0,0 @@
package awais.instagrabber.models;
import androidx.annotation.NonNull;
import awais.instagrabber.models.enums.SuggestionType;
public final class SuggestionModel implements Comparable<SuggestionModel> {
private final int position;
private final boolean isVerified;
private final String username, name, profilePic;
private final SuggestionType suggestionType;
public SuggestionModel(final boolean isVerified, final String username, final String name, final String profilePic,
final SuggestionType suggestionType, final int position) {
this.isVerified = isVerified;
this.username = username;
this.name = name;
this.profilePic = profilePic;
this.suggestionType = suggestionType;
this.position = position;
}
public boolean isVerified() {
return isVerified;
}
public String getUsername() {
return username;
}
public String getName() {
return name;
}
public String getProfilePic() {
return profilePic;
}
public SuggestionType getSuggestionType() {
return suggestionType;
}
public int getPosition() {
return position;
}
@Override
public int compareTo(@NonNull final SuggestionModel model) {
return Integer.compare(getPosition(), model.getPosition());
}
}

View File

@ -2,7 +2,7 @@ package awais.instagrabber.repositories;
import java.util.Map; import java.util.Map;
import awais.instagrabber.repositories.responses.FeedFetchResponse; import awais.instagrabber.repositories.responses.feed.FeedFetchResponse;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.FieldMap; import retrofit2.http.FieldMap;
import retrofit2.http.FormUrlEncoded; import retrofit2.http.FormUrlEncoded;

View File

@ -16,4 +16,7 @@ public interface GraphQLRepository {
@GET("/explore/tags/{tag}/?__a=1") @GET("/explore/tags/{tag}/?__a=1")
Call<String> getTag(@Path("tag") String tag); Call<String> getTag(@Path("tag") String tag);
@GET("/explore/locations/{locationId}/?__a=1")
Call<String> getLocation(@Path("locationId") long locationId);
} }

View File

@ -3,12 +3,15 @@ package awais.instagrabber.repositories;
import java.util.Map; import java.util.Map;
import awais.instagrabber.repositories.responses.LocationFeedResponse; import awais.instagrabber.repositories.responses.LocationFeedResponse;
import awais.instagrabber.repositories.responses.Place;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.GET; import retrofit2.http.GET;
import retrofit2.http.Path; import retrofit2.http.Path;
import retrofit2.http.QueryMap; import retrofit2.http.QueryMap;
public interface LocationRepository { public interface LocationRepository {
@GET("/api/v1/locations/{location}/info/")
Call<Place> fetch(@Path("location") final long locationId);
@GET("/api/v1/feed/location/{location}/") @GET("/api/v1/feed/location/{location}/")
Call<LocationFeedResponse> fetchPosts(@Path("location") final long locationId, Call<LocationFeedResponse> fetchPosts(@Path("location") final long locationId,

View File

@ -0,0 +1,14 @@
package awais.instagrabber.repositories;
import java.util.Map;
import awais.instagrabber.repositories.responses.search.SearchResponse;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.QueryMap;
import retrofit2.http.Url;
public interface SearchRepository {
@GET
Call<SearchResponse> search(@Url String url, @QueryMap(encoded = true) Map<String, String> queryParams);
}

View File

@ -5,22 +5,22 @@ import java.io.Serializable;
import awais.instagrabber.models.enums.FollowingType; import awais.instagrabber.models.enums.FollowingType;
public final class Hashtag implements Serializable { 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 long mediaCount;
private final String id; private final String id;
private final String name; 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, public Hashtag(final String id,
final String name, final String name,
final String profilePicUrl,
final long mediaCount, final long mediaCount,
final FollowingType following) { final FollowingType following,
final String searchResultSubtitle) {
this.id = id; this.id = id;
this.name = name; this.name = name;
this.profilePicUrl = profilePicUrl;
this.mediaCount = mediaCount; this.mediaCount = mediaCount;
this.following = following; this.following = following;
this.searchResultSubtitle = searchResultSubtitle;
} }
public String getId() { public String getId() {
@ -31,10 +31,6 @@ public final class Hashtag implements Serializable {
return name; return name;
} }
public String getProfilePicUrl() {
return profilePicUrl;
}
public Long getMediaCount() { public Long getMediaCount() {
return mediaCount; return mediaCount;
} }
@ -42,4 +38,8 @@ public final class Hashtag implements Serializable {
public FollowingType getFollowing() { public FollowingType getFollowing() {
return following; return following;
} }
public String getSubtitle() {
return searchResultSubtitle;
}
} }

View File

@ -9,16 +9,16 @@ public class Location implements Serializable {
private final String name; private final String name;
private final String address; private final String address;
private final String city; private final String city;
private final float lng; private final double lng;
private final float lat; private final double lat;
public Location(final long pk, public Location(final long pk,
final String shortName, final String shortName,
final String name, final String name,
final String address, final String address,
final String city, final String city,
final float lng, final double lng,
final float lat) { final double lat) {
this.pk = pk; this.pk = pk;
this.shortName = shortName; this.shortName = shortName;
this.name = name; this.name = name;
@ -48,22 +48,24 @@ public class Location implements Serializable {
return city; return city;
} }
public float getLng() { public double getLng() {
return lng; return lng;
} }
public float getLat() { public double getLat() {
return lat; return lat;
} }
public String getGeo() { return "geo:" + lat + "," + lng + "?z=17&q=" + lat + "," + lng + "(" + name + ")"; }
@Override @Override
public boolean equals(final Object o) { public boolean equals(final Object o) {
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
final Location location = (Location) o; final Location location = (Location) o;
return pk == location.pk && return pk == location.pk &&
Float.compare(location.lng, lng) == 0 && Double.compare(location.lng, lng) == 0 &&
Float.compare(location.lat, lat) == 0 && Double.compare(location.lat, lat) == 0 &&
Objects.equals(shortName, location.shortName) && Objects.equals(shortName, location.shortName) &&
Objects.equals(name, location.name) && Objects.equals(name, location.name) &&
Objects.equals(address, location.address) && Objects.equals(address, location.address) &&

View File

@ -9,6 +9,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import awais.instagrabber.models.enums.MediaItemType; import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.repositories.responses.feed.EndOfFeedDemarcator;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
public class Media implements Serializable { public class Media implements Serializable {

View File

@ -2,6 +2,9 @@ package awais.instagrabber.repositories.responses;
import java.util.List; import java.util.List;
import awais.instagrabber.repositories.responses.notification.Notification;
import awais.instagrabber.repositories.responses.notification.NotificationCounts;
public class NewsInboxResponse { public class NewsInboxResponse {
private final NotificationCounts counts; private final NotificationCounts counts;
private final List<Notification> newStories; private final List<Notification> newStories;

View File

@ -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;
}
}

View File

@ -1,4 +1,4 @@
package awais.instagrabber.repositories.responses; package awais.instagrabber.repositories.responses.feed;
import java.io.Serializable; import java.io.Serializable;
import java.util.Objects; import java.util.Objects;

View File

@ -1,9 +1,11 @@
package awais.instagrabber.repositories.responses; package awais.instagrabber.repositories.responses.feed;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import awais.instagrabber.repositories.responses.Media;
public class EndOfFeedGroup implements Serializable { public class EndOfFeedGroup implements Serializable {
private final String id; private final String id;
private final String title; private final String title;

View File

@ -1,4 +1,4 @@
package awais.instagrabber.repositories.responses; package awais.instagrabber.repositories.responses.feed;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;

View File

@ -1,7 +1,9 @@
package awais.instagrabber.repositories.responses; package awais.instagrabber.repositories.responses.feed;
import java.util.List; import java.util.List;
import awais.instagrabber.repositories.responses.Media;
public class FeedFetchResponse { public class FeedFetchResponse {
private final List<Media> items; private final List<Media> items;
private final int numResults; private final int numResults;

View File

@ -1,4 +1,4 @@
package awais.instagrabber.repositories.responses; package awais.instagrabber.repositories.responses.notification;
import awais.instagrabber.models.enums.NotificationType; import awais.instagrabber.models.enums.NotificationType;

View File

@ -1,4 +1,4 @@
package awais.instagrabber.repositories.responses; package awais.instagrabber.repositories.responses.notification;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -7,8 +7,6 @@ import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import android.util.Log;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
public class NotificationArgs { public class NotificationArgs {

View File

@ -1,6 +1,4 @@
package awais.instagrabber.repositories.responses; package awais.instagrabber.repositories.responses.notification;
import androidx.annotation.NonNull;
public class NotificationCounts { public class NotificationCounts {
private final int commentLikes; private final int commentLikes;

View File

@ -1,4 +1,4 @@
package awais.instagrabber.repositories.responses; package awais.instagrabber.repositories.responses.notification;
public class NotificationImage { public class NotificationImage {
private final String id; private final String id;

View File

@ -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;
}
}

View File

@ -0,0 +1,46 @@
package awais.instagrabber.repositories.responses.search;
import java.util.List;
public class SearchResponse {
// app
private final List<SearchItem> list;
// browser
private final List<SearchItem> users;
private final List<SearchItem> places;
private final List<SearchItem> hashtags;
// universal
private final String status;
public SearchResponse(final List<SearchItem> list,
final List<SearchItem> users,
final List<SearchItem> places,
final List<SearchItem> hashtags,
final String status) {
this.list = list;
this.users = users;
this.places = places;
this.hashtags = hashtags;
this.status = status;
}
public List<SearchItem> getList() {
return list;
}
public List<SearchItem> getUsers() {
return users;
}
public List<SearchItem> getPlaces() {
return places;
}
public List<SearchItem> getHashtags() {
return hashtags;
}
public String getStatus() {
return status;
}
}

View File

@ -18,13 +18,11 @@ import java.util.List;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.activities.MainActivity; 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.utils.Constants;
import awais.instagrabber.webservices.NewsService; import awais.instagrabber.webservices.NewsService;
import awais.instagrabber.webservices.ServiceCallback; import awais.instagrabber.webservices.ServiceCallback;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class ActivityCheckerService extends Service { public class ActivityCheckerService extends Service {
private static final String TAG = "ActivityCheckerService"; private static final String TAG = "ActivityCheckerService";
private static final int INITIAL_DELAY_MILLIS = 200; private static final int INITIAL_DELAY_MILLIS = 200;

View File

@ -1,6 +1,8 @@
package awais.instagrabber.utils; package awais.instagrabber.utils;
public final class Constants { public final class Constants {
public static final String CRASH_REPORT_EMAIL = "barinsta@austinhuang.me";
// string prefs // string prefs
public static final String FOLDER_PATH = "custom_path"; public static final String FOLDER_PATH = "custom_path";
public static final String DATE_TIME_FORMAT = "date_time_format"; public static final String DATE_TIME_FORMAT = "date_time_format";

View File

@ -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_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_AUTO_REFRESH_FREQ_UNIT;
import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_NOTIFICATIONS; 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.fragments.settings.PreferenceKeys.PREF_TAB_ORDER;
import static awais.instagrabber.utils.Constants.APP_LANGUAGE; import static awais.instagrabber.utils.Constants.APP_LANGUAGE;
import static awais.instagrabber.utils.Constants.APP_THEME; 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(); if (sharedPreferences != null) sharedPreferences.edit().putBoolean(key, val).apply();
} }
public boolean hasPreference(final String key) {
return sharedPreferences != null && sharedPreferences.contains(key);
}
@StringDef( @StringDef(
{APP_LANGUAGE, APP_THEME, APP_UA, BROWSER_UA, COOKIE, FOLDER_PATH, DATE_TIME_FORMAT, DATE_TIME_SELECTION, {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, 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, @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, 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, 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 {} public @interface BooleanSettings {}
@StringDef({PREV_INSTALL_VERSION, BROWSER_UA_CODE, APP_UA_CODE, PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER}) @StringDef({PREV_INSTALL_VERSION, BROWSER_UA_CODE, APP_UA_CODE, PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER})

View File

@ -2,8 +2,6 @@ package awais.instagrabber.utils;
import android.content.Context; import android.content.Context;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.format.DateFormat; import android.text.format.DateFormat;
import android.text.format.DateUtils; import android.text.format.DateUtils;
import android.text.style.URLSpan; import android.text.style.URLSpan;
@ -11,17 +9,12 @@ import android.util.Patterns;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern;
import awais.instagrabber.customviews.CommentMentionClickSpan;
public final class TextUtils { public final class TextUtils {
// extracted from String class // extracted from String class
@ -69,7 +62,7 @@ public final class TextUtils {
str = str.trim(); str = str.trim();
return "".equals(str) || "null".equals(str) || str.isEmpty(); 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) { public static String millisToTimeString(final long millis) {

View File

@ -5,7 +5,7 @@ import androidx.lifecycle.ViewModel;
import java.util.List; import java.util.List;
import awais.instagrabber.repositories.responses.Notification; import awais.instagrabber.repositories.responses.notification.Notification;
public class NotificationViewModel extends ViewModel { public class NotificationViewModel extends ViewModel {
private MutableLiveData<List<Notification>> list; private MutableLiveData<List<Notification>> list;

View File

@ -12,12 +12,12 @@ import java.util.Map;
import java.util.UUID; import java.util.UUID;
import awais.instagrabber.repositories.FeedRepository; 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.Media;
import awais.instagrabber.repositories.responses.PostsFetchResponse; 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 awais.instagrabber.utils.TextUtils;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.Callback; import retrofit2.Callback;

View File

@ -19,6 +19,7 @@ import awais.instagrabber.repositories.GraphQLRepository;
import awais.instagrabber.repositories.responses.FriendshipStatus; import awais.instagrabber.repositories.responses.FriendshipStatus;
import awais.instagrabber.repositories.responses.GraphQLUserListFetchResponse; import awais.instagrabber.repositories.responses.GraphQLUserListFetchResponse;
import awais.instagrabber.repositories.responses.Hashtag; import awais.instagrabber.repositories.responses.Hashtag;
import awais.instagrabber.repositories.responses.Location;
import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.PostsFetchResponse; import awais.instagrabber.repositories.responses.PostsFetchResponse;
import awais.instagrabber.repositories.responses.User; import awais.instagrabber.repositories.responses.User;
@ -394,9 +395,53 @@ public class GraphQLService extends BaseService {
callback.onSuccess(new Hashtag( callback.onSuccess(new Hashtag(
body.getString(Constants.EXTRAS_ID), body.getString(Constants.EXTRAS_ID),
body.getString("name"), body.getString("name"),
body.getString("profile_pic_url"),
timelineMedia.getLong("count"), 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<String> call, @NonNull final Throwable t) {
if (callback != null) {
callback.onFailure(t);
}
}
});
}
public void fetchLocation(final long locationId,
final ServiceCallback<Location> callback) {
final Call<String> request = repository.getLocation(locationId);
request.enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> 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) { } catch (JSONException e) {
Log.e(TAG, "onResponse", e); Log.e(TAG, "onResponse", e);
if (callback != null) { if (callback != null) {

View File

@ -5,7 +5,9 @@ import androidx.annotation.NonNull;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import awais.instagrabber.repositories.LocationRepository; import awais.instagrabber.repositories.LocationRepository;
import awais.instagrabber.repositories.responses.Location;
import awais.instagrabber.repositories.responses.LocationFeedResponse; import awais.instagrabber.repositories.responses.LocationFeedResponse;
import awais.instagrabber.repositories.responses.Place;
import awais.instagrabber.repositories.responses.PostsFetchResponse; import awais.instagrabber.repositories.responses.PostsFetchResponse;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import retrofit2.Call; import retrofit2.Call;
@ -69,34 +71,24 @@ public class LocationService extends BaseService {
}); });
} }
// private PostsFetchResponse parseResponse(@NonNull final String body) throws JSONException { public void fetch(@NonNull final long locationId,
// final JSONObject root = new JSONObject(body); final ServiceCallback<Location> callback) {
// final boolean moreAvailable = root.optBoolean("more_available"); final Call<Place> request = repository.fetch(locationId);
// final String nextMaxId = root.optString("next_max_id"); request.enqueue(new Callback<Place>() {
// final JSONArray itemsJson = root.optJSONArray("items"); @Override
// final List<FeedModel> items = parseItems(itemsJson); public void onResponse(@NonNull final Call<Place> call, @NonNull final Response<Place> response) {
// return new PostsFetchResponse( if (callback == null) {
// items, return;
// moreAvailable, }
// nextMaxId callback.onSuccess(response.body() == null ? null : response.body().getLocation());
// ); }
// }
// private List<FeedModel> parseItems(final JSONArray items) throws JSONException { @Override
// if (items == null) { public void onFailure(@NonNull final Call<Place> call, @NonNull final Throwable t) {
// return Collections.emptyList(); if (callback != null) {
// } callback.onFailure(t);
// final List<FeedModel> 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;
// }
} }

View File

@ -1,35 +1,24 @@
package awais.instagrabber.webservices; package awais.instagrabber.webservices;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.models.enums.NotificationType;
import awais.instagrabber.repositories.NewsRepository; import awais.instagrabber.repositories.NewsRepository;
import awais.instagrabber.repositories.responses.AymlResponse; import awais.instagrabber.repositories.responses.AymlResponse;
import awais.instagrabber.repositories.responses.AymlUser; 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.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.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.Constants;
import awais.instagrabber.utils.Utils;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.Callback; import retrofit2.Callback;
import retrofit2.Response; import retrofit2.Response;

View File

@ -0,0 +1,45 @@
package awais.instagrabber.webservices;
import com.google.common.collect.ImmutableMap;
import awais.instagrabber.repositories.SearchRepository;
import awais.instagrabber.repositories.responses.search.SearchResponse;
import retrofit2.Call;
import retrofit2.Retrofit;
public class SearchService extends BaseService {
private static final String TAG = "LocationService";
private final SearchRepository repository;
private static SearchService instance;
private SearchService() {
final Retrofit retrofit = getRetrofitBuilder()
.baseUrl("https://www.instagram.com")
.build();
repository = retrofit.create(SearchRepository.class);
}
public static SearchService getInstance() {
if (instance == null) {
instance = new SearchService();
}
return instance;
}
public Call<SearchResponse> search(final boolean isLoggedIn,
final String query,
final String context) {
final ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
builder.put("query", query);
// context is one of: "blended", "user", "place", "hashtag"
// note that "place" and "hashtag" can contain ONE user result, who knows why
builder.put("context", context);
builder.put("count", "50");
return repository.search(isLoggedIn
? "https://i.instagram.com/api/v1/fbsearch/topsearch_flat/"
: "https://www.instagram.com/web/search/topsearch/",
builder.build());
}
}

View File

@ -1,50 +1,35 @@
package awaisomereport; package awaisomereport;
import android.app.Application; import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Process;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.content.FileProvider;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Date;
//import java.util.zip.ZipEntry;
//import java.util.zip.ZipOutputStream;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.utils.Utils;
public final class CrashReporter implements Thread.UncaughtExceptionHandler { public final class CrashReporter implements Thread.UncaughtExceptionHandler {
private static final String TAG = CrashReporter.class.getSimpleName();
private static CrashReporter reporterInstance; private static CrashReporter reporterInstance;
private final Application application;
private final String email; // private final File crashLogsZip;
// private final File crashLogsZip; private final CrashHandler crashHandler;
private boolean startAttempted = false; private boolean startAttempted = false;
private Thread.UncaughtExceptionHandler defaultEH;
public static CrashReporter get(final Application application) { public static CrashReporter get(final Application application) {
if (reporterInstance == null) reporterInstance = new CrashReporter(application); if (reporterInstance == null) {
reporterInstance = new CrashReporter(application);
}
return reporterInstance; return reporterInstance;
} }
private CrashReporter(@NonNull final Application application) { private CrashReporter(@NonNull final Application application) {
this.application = application; crashHandler = new CrashHandler(application);
this.email = "barinsta@austinhuang.me"; // this.crashLogsZip = new File(application.getExternalCacheDir(), "crash_logs.zip");
// this.crashLogsZip = new File(application.getExternalCacheDir(), "crash_logs.zip");
} }
public void start() { public void start() {
if (!startAttempted) { if (!startAttempted) {
defaultEH = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this); Thread.setDefaultUncaughtExceptionHandler(this);
startAttempted = true; startAttempted = true;
} }
@ -52,140 +37,10 @@ public final class CrashReporter implements Thread.UncaughtExceptionHandler {
@Override @Override
public void uncaughtException(@NonNull final Thread t, @NonNull final Throwable exception) { public void uncaughtException(@NonNull final Thread t, @NonNull final Throwable exception) {
final StringBuilder reportBuilder = new StringBuilder(); if (crashHandler == null) {
reportBuilder.append("IMPORTANT: If sending by email, your email address and the entire content will be made public on GitHub issues."); defaultEH.uncaughtException(t, exception);
reportBuilder.append("\r\nIMPORTANT: When possible, please describe the steps leading to this crash. Thank you for your cooperation."); return;
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);
} }
crashHandler.uncaughtException(t, exception, defaultEH);
} }
} }

View File

@ -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);
}
}
}

View File

@ -43,8 +43,9 @@ public final class ErrorReporterActivity extends Activity implements View.OnClic
@Override @Override
public void onClick(@NonNull final View v) { public void onClick(@NonNull final View v) {
if (v == btnReport) if (v == btnReport) {
CrashReporter.get(getApplication()).startCrashEmailIntent(this); CrashReporterHelper.startCrashEmailIntent(this);
}
finish(); finish();
System.exit(10); System.exit(10);
} }

View File

@ -0,0 +1,7 @@
package awaisomereport;
public interface ICrashHandler {
void uncaughtException(Thread t,
Throwable exception,
Thread.UncaughtExceptionHandler defaultEH);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -15,25 +15,25 @@
android:layout_height="@dimen/profile_picture_size" android:layout_height="@dimen/profile_picture_size"
android:background="?selectableItemBackgroundBorderless" android:background="?selectableItemBackgroundBorderless"
app:actualImageScaleType="centerCrop" app:actualImageScaleType="centerCrop"
app:layout_constraintEnd_toStartOf="@id/mainLocPostCount" app:layout_constraintEnd_toStartOf="@id/btnMap"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:background="@mipmap/ic_launcher" /> tools:background="@mipmap/ic_launcher" />
<androidx.appcompat.widget.AppCompatTextView <!-- <androidx.appcompat.widget.AppCompatTextView-->
android:id="@+id/mainLocPostCount" <!-- android:id="@+id/mainLocPostCount"-->
android:layout_width="0dp" <!-- android:layout_width="0dp"-->
android:layout_height="0dp" <!-- android:layout_height="0dp"-->
android:gravity="center_vertical" <!-- android:gravity="center_vertical"-->
android:maxLines="1" <!-- android:maxLines="1"-->
android:paddingStart="12dp" <!-- android:paddingStart="12dp"-->
android:paddingEnd="12dp" <!-- android:paddingEnd="12dp"-->
android:textAppearance="@style/TextAppearance.AppCompat" <!-- android:textAppearance="@style/TextAppearance.AppCompat"-->
app:layout_constraintBottom_toTopOf="@id/btnMap" <!-- app:layout_constraintBottom_toTopOf="@id/btnMap"-->
app:layout_constraintEnd_toEndOf="parent" <!-- app:layout_constraintEnd_toEndOf="parent"-->
app:layout_constraintStart_toEndOf="@id/mainLocationImage" <!-- app:layout_constraintStart_toEndOf="@id/mainLocationImage"-->
app:layout_constraintTop_toTopOf="parent" <!-- app:layout_constraintTop_toTopOf="parent"-->
tools:text="35 Posts" /> <!-- tools:text="35 Posts" />-->
<com.google.android.material.chip.Chip <com.google.android.material.chip.Chip
android:id="@+id/btnMap" android:id="@+id/btnMap"
@ -44,9 +44,8 @@
app:chipBackgroundColor="@null" app:chipBackgroundColor="@null"
app:chipIcon="@drawable/ic_outline_map_24" app:chipIcon="@drawable/ic_outline_map_24"
app:chipIconTint="@color/green_500" app:chipIconTint="@color/green_500"
app:layout_constraintBottom_toTopOf="@id/locationFullName" app:layout_constraintTop_toTopOf="@id/mainLocationImage"
app:layout_constraintStart_toEndOf="@id/mainLocationImage" app:layout_constraintStart_toEndOf="@id/mainLocationImage"
app:layout_constraintTop_toBottomOf="@id/mainLocPostCount"
app:rippleColor="@color/grey_500" app:rippleColor="@color/grey_500"
tools:visibility="visible" /> tools:visibility="visible" />
@ -60,8 +59,8 @@
app:chipIcon="@drawable/ic_outline_star_plus_24" app:chipIcon="@drawable/ic_outline_star_plus_24"
app:chipIconTint="@color/yellow_800" app:chipIconTint="@color/yellow_800"
app:layout_constraintBottom_toBottomOf="@id/mainLocationImage" app:layout_constraintBottom_toBottomOf="@id/mainLocationImage"
app:layout_constraintStart_toEndOf="@id/btnMap" app:layout_constraintStart_toEndOf="@id/mainLocationImage"
app:layout_constraintTop_toBottomOf="@id/mainLocPostCount" app:layout_constraintTop_toBottomOf="@id/btnMap"
app:rippleColor="@color/yellow_400" /> app:rippleColor="@color/yellow_400" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
@ -90,27 +89,10 @@
android:padding="8dp" android:padding="8dp"
android:background="?android:selectableItemBackground" android:background="?android:selectableItemBackground"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" android:textAppearance="@style/TextAppearance.AppCompat.Body1"
app:layout_constraintBottom_toTopOf="@id/locationUrl" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/locationFullName" app:layout_constraintTop_toBottomOf="@id/locationFullName"
tools:text="IN THE MIDDLE OF OUR STREET" /> tools:text="IN THE MIDDLE OF OUR STREET" />
<awais.instagrabber.customviews.RamboTextViewV2
android:id="@+id/locationUrl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/locationBiography"
android:ellipsize="marquee"
android:padding="8dp"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/locationBiography"
tools:text="https://austinhuang.me/"
tools:textColor="@android:color/holo_blue_dark"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/pending_requests"
android:icon="@drawable/ic_account_clock_24"
android:title="@string/pending_requests"
android:visible="false"
app:showAsAction="always" />
</menu>

View File

@ -4,5 +4,4 @@
<item name="unsend" type="id" /> <item name="unsend" type="id" />
<item name="forward" type="id" /> <item name="forward" type="id" />
<item name="detail" type="id" /> <item name="detail" type="id" />
<item name="pending_requests" type="id" />
</resources> </resources>

View File

@ -473,6 +473,8 @@
<string name="removed_keywords">Removed keyword: %s from filter list</string> <string name="removed_keywords">Removed keyword: %s from filter list</string>
<string name="marked_as_seen">Marked as seen</string> <string name="marked_as_seen">Marked as seen</string>
<string name="delete_unsuccessful">Delete unsuccessful</string> <string name="delete_unsuccessful">Delete unsuccessful</string>
<string name="crash_report_subject">Barinsta Crash Report</string>
<string name="crash_report_title">Select an email app to send crash logs</string>
<string name="tab_order">Screen order</string> <string name="tab_order">Screen order</string>
<string name="other_tabs">Other tabs</string> <string name="other_tabs">Other tabs</string>
</resources> </resources>

View File

@ -1,3 +1,5 @@
files: files:
- source: '/app/src/main/res/values/[arrays][strings][!styles]' - source: '/app/src/main/res/values/[arrays][strings][!styles]'
translation: /app/src/main/res/values-%two_letters_code%/%original_file_name% translation: /app/src/main/res/values-%two_letters_code%/%original_file_name%
- source: '/app/src/github/res/values/[arrays][strings][!styles]'
translation: /app/src/github/res/values-%two_letters_code%/%original_file_name%