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
.cxx
app/release
/sentry.properties

View File

@ -1,5 +1,6 @@
apply plugin: 'com.android.application'
apply plugin: "androidx.navigation.safeargs"
apply from: 'sentry.gradle'
android {
compileSdkVersion 29
@ -48,8 +49,22 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
flavorDimensions "repo"
productFlavors {
github {
dimension "repo"
versionNameSuffix "-github"
buildConfigField("String", "dsn", SENTRY_DSN)
}
fdroid {
dimension "repo"
versionNameSuffix "-fdroid"
}
}
}
configurations.all {
resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
@ -81,7 +96,7 @@ dependencies {
implementation 'androidx.palette:palette:1.0.0'
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation 'com.google.guava:guava:27.1-jre'
implementation 'com.google.guava:guava:27.0.1-android'
// Room
def room_version = "2.2.6"
@ -116,6 +131,7 @@ dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.6'
githubImplementation 'io.sentry:sentry-android:4.3.0'
testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1'
}

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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
private User getUser(final long userId) {
for (final User user : users) {
@ -1426,58 +1337,6 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
return null;
}
// private void searchUsername(final String text) {
// final Bundle bundle = new Bundle();
// bundle.putString("username", "@" + text);
// NavHostFragment.findNavController(this).navigate(R.id.action_global_profileFragment, bundle);
// }
// class ThreadAction extends AsyncTask<String, 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() {
if (heightProvider != null) return;
heightProvider = new HeightProvider(fragmentActivity).init().setHeightListener(height -> {

View File

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

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_FREQ_UNIT = "enable_dm_auto_refresh_freq_unit";
public static final String PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER = "enable_dm_auto_refresh_freq_number";
public static final String PREF_ENABLE_SENTRY = "enable_sentry";
public static final String PREF_TAB_ORDER = "tab_order";
}

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 awais.instagrabber.repositories.responses.FeedFetchResponse;
import awais.instagrabber.repositories.responses.feed.FeedFetchResponse;
import retrofit2.Call;
import retrofit2.http.FieldMap;
import retrofit2.http.FormUrlEncoded;

View File

@ -16,4 +16,7 @@ public interface GraphQLRepository {
@GET("/explore/tags/{tag}/?__a=1")
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 awais.instagrabber.repositories.responses.LocationFeedResponse;
import awais.instagrabber.repositories.responses.Place;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Path;
import retrofit2.http.QueryMap;
public interface LocationRepository {
@GET("/api/v1/locations/{location}/info/")
Call<Place> fetch(@Path("location") final long locationId);
@GET("/api/v1/feed/location/{location}/")
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;
public final class Hashtag implements Serializable {
private final FollowingType following; // 0 false 1 true
private final FollowingType following; // 0 false 1 true; not on search results
private final long mediaCount;
private final String id;
private final String name;
private final String profilePicUrl; // on app API this is always null (property exists)
private final String searchResultSubtitle; // shows how many posts there are on search results
public Hashtag(final String id,
final String name,
final String profilePicUrl,
final long mediaCount,
final FollowingType following) {
final FollowingType following,
final String searchResultSubtitle) {
this.id = id;
this.name = name;
this.profilePicUrl = profilePicUrl;
this.mediaCount = mediaCount;
this.following = following;
this.searchResultSubtitle = searchResultSubtitle;
}
public String getId() {
@ -31,10 +31,6 @@ public final class Hashtag implements Serializable {
return name;
}
public String getProfilePicUrl() {
return profilePicUrl;
}
public Long getMediaCount() {
return mediaCount;
}
@ -42,4 +38,8 @@ public final class Hashtag implements Serializable {
public FollowingType getFollowing() {
return following;
}
public String getSubtitle() {
return searchResultSubtitle;
}
}

View File

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

View File

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

View File

@ -2,6 +2,9 @@ package awais.instagrabber.repositories.responses;
import java.util.List;
import awais.instagrabber.repositories.responses.notification.Notification;
import awais.instagrabber.repositories.responses.notification.NotificationCounts;
public class NewsInboxResponse {
private final NotificationCounts counts;
private final List<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.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.util.List;
import java.util.Objects;
import awais.instagrabber.repositories.responses.Media;
public class EndOfFeedGroup implements Serializable {
private final String id;
private final String title;

View File

@ -1,4 +1,4 @@
package awais.instagrabber.repositories.responses;
package awais.instagrabber.repositories.responses.feed;
import java.io.Serializable;
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 awais.instagrabber.repositories.responses.Media;
public class FeedFetchResponse {
private final List<Media> items;
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;

View File

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

View File

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

View File

@ -1,4 +1,4 @@
package awais.instagrabber.repositories.responses;
package awais.instagrabber.repositories.responses.notification;
public class NotificationImage {
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.activities.MainActivity;
import awais.instagrabber.repositories.responses.NotificationCounts;
import awais.instagrabber.repositories.responses.notification.NotificationCounts;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.webservices.NewsService;
import awais.instagrabber.webservices.ServiceCallback;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class ActivityCheckerService extends Service {
private static final String TAG = "ActivityCheckerService";
private static final int INITIAL_DELAY_MILLIS = 200;

View File

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

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_UNIT;
import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_NOTIFICATIONS;
import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_SENTRY;
import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_TAB_ORDER;
import static awais.instagrabber.utils.Constants.APP_LANGUAGE;
import static awais.instagrabber.utils.Constants.APP_THEME;
@ -146,6 +147,10 @@ public final class SettingsHelper {
if (sharedPreferences != null) sharedPreferences.edit().putBoolean(key, val).apply();
}
public boolean hasPreference(final String key) {
return sharedPreferences != null && sharedPreferences.contains(key);
}
@StringDef(
{APP_LANGUAGE, APP_THEME, APP_UA, BROWSER_UA, COOKIE, FOLDER_PATH, DATE_TIME_FORMAT, DATE_TIME_SELECTION,
CUSTOM_DATE_TIME_FORMAT, DEVICE_UUID, SKIPPED_VERSION, DEFAULT_TAB, PREF_DARK_THEME, PREF_LIGHT_THEME,
@ -157,7 +162,7 @@ public final class SettingsHelper {
@StringDef({DOWNLOAD_USER_FOLDER, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS,
SHOW_CAPTIONS, CUSTOM_DATE_TIME_FORMAT_ENABLED, MARK_AS_SEEN, DM_MARK_AS_SEEN, CHECK_ACTIVITY,
CHECK_UPDATES, SWAP_DATE_TIME_FORMAT_ENABLED, PREF_ENABLE_DM_NOTIFICATIONS, PREF_ENABLE_DM_AUTO_REFRESH,
FLAG_SECURE, TOGGLE_KEYWORD_FILTER})
FLAG_SECURE, TOGGLE_KEYWORD_FILTER, PREF_ENABLE_SENTRY})
public @interface BooleanSettings {}
@StringDef({PREV_INSTALL_VERSION, BROWSER_UA_CODE, APP_UA_CODE, PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER})

View File

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

View File

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

View File

@ -12,12 +12,12 @@ import java.util.Map;
import java.util.UUID;
import awais.instagrabber.repositories.FeedRepository;
import awais.instagrabber.repositories.responses.EndOfFeedDemarcator;
import awais.instagrabber.repositories.responses.EndOfFeedGroup;
import awais.instagrabber.repositories.responses.EndOfFeedGroupSet;
import awais.instagrabber.repositories.responses.FeedFetchResponse;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.PostsFetchResponse;
import awais.instagrabber.repositories.responses.feed.EndOfFeedDemarcator;
import awais.instagrabber.repositories.responses.feed.EndOfFeedGroup;
import awais.instagrabber.repositories.responses.feed.EndOfFeedGroupSet;
import awais.instagrabber.repositories.responses.feed.FeedFetchResponse;
import awais.instagrabber.utils.TextUtils;
import retrofit2.Call;
import retrofit2.Callback;

View File

@ -19,6 +19,7 @@ import awais.instagrabber.repositories.GraphQLRepository;
import awais.instagrabber.repositories.responses.FriendshipStatus;
import awais.instagrabber.repositories.responses.GraphQLUserListFetchResponse;
import awais.instagrabber.repositories.responses.Hashtag;
import awais.instagrabber.repositories.responses.Location;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.PostsFetchResponse;
import awais.instagrabber.repositories.responses.User;
@ -394,9 +395,53 @@ public class GraphQLService extends BaseService {
callback.onSuccess(new Hashtag(
body.getString(Constants.EXTRAS_ID),
body.getString("name"),
body.getString("profile_pic_url"),
timelineMedia.getLong("count"),
body.optBoolean("is_following") ? FollowingType.FOLLOWING : FollowingType.NOT_FOLLOWING));
body.optBoolean("is_following") ? FollowingType.FOLLOWING : FollowingType.NOT_FOLLOWING,
null));
} catch (JSONException e) {
Log.e(TAG, "onResponse", e);
if (callback != null) {
callback.onFailure(e);
}
}
}
@Override
public void onFailure(@NonNull final Call<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) {
Log.e(TAG, "onResponse", e);
if (callback != null) {

View File

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

View File

@ -1,35 +1,24 @@
package awais.instagrabber.webservices;
import android.util.Log;
import androidx.annotation.NonNull;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.models.enums.NotificationType;
import awais.instagrabber.repositories.NewsRepository;
import awais.instagrabber.repositories.responses.AymlResponse;
import awais.instagrabber.repositories.responses.AymlUser;
import awais.instagrabber.repositories.responses.NotificationCounts;
import awais.instagrabber.repositories.responses.UserSearchResponse;
import awais.instagrabber.repositories.responses.NewsInboxResponse;
import awais.instagrabber.repositories.responses.Notification;
import awais.instagrabber.repositories.responses.NotificationArgs;
import awais.instagrabber.repositories.responses.NotificationImage;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.UserSearchResponse;
import awais.instagrabber.repositories.responses.notification.Notification;
import awais.instagrabber.repositories.responses.notification.NotificationArgs;
import awais.instagrabber.repositories.responses.notification.NotificationCounts;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

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;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Process;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.core.content.FileProvider;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Date;
//import java.util.zip.ZipEntry;
//import java.util.zip.ZipOutputStream;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.utils.Utils;
public final class CrashReporter implements Thread.UncaughtExceptionHandler {
private static final String TAG = CrashReporter.class.getSimpleName();
private static CrashReporter reporterInstance;
private final Application application;
private final String email;
// private final File crashLogsZip;
// private final File crashLogsZip;
private final CrashHandler crashHandler;
private boolean startAttempted = false;
private Thread.UncaughtExceptionHandler defaultEH;
public static CrashReporter get(final Application application) {
if (reporterInstance == null) reporterInstance = new CrashReporter(application);
if (reporterInstance == null) {
reporterInstance = new CrashReporter(application);
}
return reporterInstance;
}
private CrashReporter(@NonNull final Application application) {
this.application = application;
this.email = "barinsta@austinhuang.me";
// this.crashLogsZip = new File(application.getExternalCacheDir(), "crash_logs.zip");
crashHandler = new CrashHandler(application);
// this.crashLogsZip = new File(application.getExternalCacheDir(), "crash_logs.zip");
}
public void start() {
if (!startAttempted) {
defaultEH = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
startAttempted = true;
}
@ -52,140 +37,10 @@ public final class CrashReporter implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(@NonNull final Thread t, @NonNull final Throwable exception) {
final StringBuilder reportBuilder = new StringBuilder();
reportBuilder.append("IMPORTANT: If sending by email, your email address and the entire content will be made public on GitHub issues.");
reportBuilder.append("\r\nIMPORTANT: When possible, please describe the steps leading to this crash. Thank you for your cooperation.");
reportBuilder.append("\r\n\r\nError report collected on: ").append(new Date().toString());
reportBuilder
.append("\r\n\r\nInformation:\r\n==============")
.append("\r\nVERSION : ").append(BuildConfig.VERSION_NAME)
.append("\r\nVERSION_CODE : ").append(BuildConfig.VERSION_CODE)
.append("\r\nPHONE-MODEL : ").append(Build.MODEL)
.append("\r\nANDROID_VERS : ").append(Build.VERSION.RELEASE)
.append("\r\nANDROID_REL : ").append(Build.VERSION.SDK_INT)
.append("\r\nBRAND : ").append(Build.BRAND)
.append("\r\nMANUFACTURER : ").append(Build.MANUFACTURER)
.append("\r\nBOARD : ").append(Build.BOARD)
.append("\r\nDEVICE : ").append(Build.DEVICE)
.append("\r\nPRODUCT : ").append(Build.PRODUCT)
.append("\r\nHOST : ").append(Build.HOST)
.append("\r\nTAGS : ").append(Build.TAGS);
reportBuilder.append("\r\n\r\nStack:\r\n==============\r\n");
final Writer result = new StringWriter();
try (final PrintWriter printWriter = new PrintWriter(result)) {
exception.printStackTrace(printWriter);
reportBuilder.append(result.toString());
reportBuilder.append("\r\nCause:\r\n==============");
// for AsyncTask crashes
Throwable cause = exception.getCause();
while (cause != null) {
cause.printStackTrace(printWriter);
reportBuilder.append(result.toString());
cause = cause.getCause();
}
}
reportBuilder.append("\r\n\r\n**** End of current Report ***");
final String errorContent = reportBuilder.toString();
try (final FileOutputStream trace = application.openFileOutput("stack-" + System.currentTimeMillis() + ".stacktrace", Context.MODE_PRIVATE)) {
trace.write(errorContent.getBytes());
} catch (final Exception ex) {
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", ex);
}
application.startActivity(new Intent(application, ErrorReporterActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
// zipLogs();
Process.killProcess(Process.myPid());
System.exit(10);
}
// public synchronized CrashReporter zipLogs() {
// final File logDir = Utils.logCollector != null ? Utils.logCollector.getLogDir() :
// new File(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? application.getDataDir() : application.getFilesDir(), "crashlogs");
//
// try (final FileOutputStream fos = new FileOutputStream(crashLogsZip);
// final ZipOutputStream zos = new ZipOutputStream(fos)) {
//
// final File[] files = logDir.listFiles();
//
// if (files != null) {
// zos.setLevel(5);
// byte[] buffer;
// for (final File file : files) {
// if (file != null && file.length() > 0) {
// buffer = new byte[1024];
// try (final FileInputStream fis = new FileInputStream(file)) {
// zos.putNextEntry(new ZipEntry(file.getName()));
// int length;
// while ((length = fis.read(buffer)) > 0) zos.write(buffer, 0, length);
// zos.closeEntry();
// }
// }
// }
// }
//
// } catch (final Exception e) {
// if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
// }
//
// return this;
// }
@SuppressWarnings("ResultOfMethodCallIgnored")
public void startCrashEmailIntent(final Context context) {
try {
final String filePath = context.getFilesDir().getAbsolutePath();
String[] errorFileList;
try {
final File dir = new File(filePath);
if (dir.exists() && !dir.isDirectory()) dir.delete();
dir.mkdir();
errorFileList = dir.list((d, name) -> name.endsWith(".stacktrace"));
} catch (final Exception e) {
errorFileList = null;
}
if (errorFileList != null && errorFileList.length > 0) {
final StringBuilder errorStringBuilder;
errorStringBuilder = new StringBuilder("\r\n\r\n");
final int maxSendMail = 5;
int curIndex = 0;
for (final String curString : errorFileList) {
final File file = new File(filePath + '/' + curString);
if (curIndex++ <= maxSendMail) {
errorStringBuilder.append("New Trace collected:\r\n=====================\r\n");
try (final BufferedReader input = new BufferedReader(new FileReader(file))) {
String line;
while ((line = input.readLine()) != null)
errorStringBuilder.append(line).append("\r\n");
}
}
file.delete();
}
errorStringBuilder.append("\r\n\r\n");
context.startActivity(Intent.createChooser(new Intent(Intent.ACTION_SEND).setType("message/rfc822")
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
.putExtra(Intent.EXTRA_EMAIL, new String[]{email})
// .putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(application, BuildConfig.APPLICATION_ID + ".provider", crashLogsZip))
.putExtra(Intent.EXTRA_SUBJECT, "Barinsta Crash Report")
.putExtra(Intent.EXTRA_TEXT, errorStringBuilder.toString()), "Select an email app to send crash logs"));
}
} catch (final Exception e) {
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
if (crashHandler == null) {
defaultEH.uncaughtException(t, exception);
return;
}
crashHandler.uncaughtException(t, exception, defaultEH);
}
}

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
public void onClick(@NonNull final View v) {
if (v == btnReport)
CrashReporter.get(getApplication()).startCrashEmailIntent(this);
if (v == btnReport) {
CrashReporterHelper.startCrashEmailIntent(this);
}
finish();
System.exit(10);
}

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:background="?selectableItemBackgroundBorderless"
app:actualImageScaleType="centerCrop"
app:layout_constraintEnd_toStartOf="@id/mainLocPostCount"
app:layout_constraintEnd_toStartOf="@id/btnMap"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:background="@mipmap/ic_launcher" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/mainLocPostCount"
android:layout_width="0dp"
android:layout_height="0dp"
android:gravity="center_vertical"
android:maxLines="1"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:textAppearance="@style/TextAppearance.AppCompat"
app:layout_constraintBottom_toTopOf="@id/btnMap"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/mainLocationImage"
app:layout_constraintTop_toTopOf="parent"
tools:text="35 Posts" />
<!-- <androidx.appcompat.widget.AppCompatTextView-->
<!-- android:id="@+id/mainLocPostCount"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="0dp"-->
<!-- android:gravity="center_vertical"-->
<!-- android:maxLines="1"-->
<!-- android:paddingStart="12dp"-->
<!-- android:paddingEnd="12dp"-->
<!-- android:textAppearance="@style/TextAppearance.AppCompat"-->
<!-- app:layout_constraintBottom_toTopOf="@id/btnMap"-->
<!-- app:layout_constraintEnd_toEndOf="parent"-->
<!-- app:layout_constraintStart_toEndOf="@id/mainLocationImage"-->
<!-- app:layout_constraintTop_toTopOf="parent"-->
<!-- tools:text="35 Posts" />-->
<com.google.android.material.chip.Chip
android:id="@+id/btnMap"
@ -44,9 +44,8 @@
app:chipBackgroundColor="@null"
app:chipIcon="@drawable/ic_outline_map_24"
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_constraintTop_toBottomOf="@id/mainLocPostCount"
app:rippleColor="@color/grey_500"
tools:visibility="visible" />
@ -60,8 +59,8 @@
app:chipIcon="@drawable/ic_outline_star_plus_24"
app:chipIconTint="@color/yellow_800"
app:layout_constraintBottom_toBottomOf="@id/mainLocationImage"
app:layout_constraintStart_toEndOf="@id/btnMap"
app:layout_constraintTop_toBottomOf="@id/mainLocPostCount"
app:layout_constraintStart_toEndOf="@id/mainLocationImage"
app:layout_constraintTop_toBottomOf="@id/btnMap"
app:rippleColor="@color/yellow_400" />
<androidx.appcompat.widget.AppCompatTextView
@ -90,27 +89,10 @@
android:padding="8dp"
android:background="?android:selectableItemBackground"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
app:layout_constraintBottom_toTopOf="@id/locationUrl"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/locationFullName"
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>

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="forward" type="id" />
<item name="detail" type="id" />
<item name="pending_requests" type="id" />
</resources>

View File

@ -473,6 +473,8 @@
<string name="removed_keywords">Removed keyword: %s from filter list</string>
<string name="marked_as_seen">Marked as seen</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="other_tabs">Other tabs</string>
</resources>

View File

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