mirror of
https://github.com/KokaKiwi/BarInsta
synced 2024-11-22 06:37:30 +00:00
Merge branch 'master' into bottombar_redesign
This commit is contained in:
commit
b9d75af03c
1
.gitignore
vendored
1
.gitignore
vendored
@ -17,3 +17,4 @@
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
app/release
|
||||
/sentry.properties
|
@ -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
13
app/sentry.gradle
Normal 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)
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
56
app/src/fdroid/java/awaisomereport/CrashHandler.java
Normal file
56
app/src/fdroid/java/awaisomereport/CrashHandler.java
Normal 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;
|
||||
// }
|
||||
}
|
10
app/src/github/AndroidManifest.xml
Normal file
10
app/src/github/AndroidManifest.xml
Normal 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>
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
59
app/src/github/java/awaisomereport/CrashHandler.java
Normal file
59
app/src/github/java/awaisomereport/CrashHandler.java
Normal 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);
|
||||
}
|
||||
}
|
6
app/src/github/res/values/strings.xml
Normal file
6
app/src/github/res/values/strings.xml
Normal 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>
|
@ -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());
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
});
|
||||
|
@ -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 -> {
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
@ -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";
|
||||
}
|
||||
|
@ -0,0 +1,6 @@
|
||||
package awais.instagrabber.fragments.settings;
|
||||
|
||||
public enum SettingCategory {
|
||||
GENERAL,
|
||||
// add more as and when required
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
package awais.instagrabber.models;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public final class LocationModel implements Serializable {
|
||||
private final long postCount;
|
||||
private final long id;
|
||||
private final String name;
|
||||
private final String bio;
|
||||
private final String url;
|
||||
private final String sdProfilePic;
|
||||
private final String lat;
|
||||
private final String lng;
|
||||
|
||||
public LocationModel(final long id,
|
||||
final String name,
|
||||
final String bio,
|
||||
final String url,
|
||||
final String sdProfilePic,
|
||||
final long postCount,
|
||||
final String lat,
|
||||
final String lng) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.bio = bio;
|
||||
this.url = url;
|
||||
this.sdProfilePic = sdProfilePic;
|
||||
this.postCount = postCount;
|
||||
this.lat = lat;
|
||||
this.lng = lng;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getBio() {
|
||||
return bio;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public String getGeo() { return "geo:" + lat + "," + lng + "?z=17&q=" + lat + "," + lng + "(" + name + ")"; }
|
||||
|
||||
public String getSdProfilePic() {
|
||||
return sdProfilePic;
|
||||
}
|
||||
|
||||
public Long getPostCount() { return postCount; }
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
package awais.instagrabber.models;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import awais.instagrabber.models.enums.SuggestionType;
|
||||
|
||||
public final class SuggestionModel implements Comparable<SuggestionModel> {
|
||||
private final int position;
|
||||
private final boolean isVerified;
|
||||
private final String username, name, profilePic;
|
||||
private final SuggestionType suggestionType;
|
||||
|
||||
public SuggestionModel(final boolean isVerified, final String username, final String name, final String profilePic,
|
||||
final SuggestionType suggestionType, final int position) {
|
||||
this.isVerified = isVerified;
|
||||
this.username = username;
|
||||
this.name = name;
|
||||
this.profilePic = profilePic;
|
||||
this.suggestionType = suggestionType;
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public boolean isVerified() {
|
||||
return isVerified;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getProfilePic() {
|
||||
return profilePic;
|
||||
}
|
||||
|
||||
public SuggestionType getSuggestionType() {
|
||||
return suggestionType;
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NonNull final SuggestionModel model) {
|
||||
return Integer.compare(getPosition(), model.getPosition());
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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) &&
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -0,0 +1,43 @@
|
||||
package awais.instagrabber.repositories.responses;
|
||||
|
||||
public class Place {
|
||||
private final Location location;
|
||||
// for search
|
||||
private final String title; // those are repeated within location
|
||||
private final String subtitle; // address
|
||||
private final String slug; // browser only; for end of address
|
||||
// for location info
|
||||
private final String status;
|
||||
|
||||
public Place(final Location location,
|
||||
final String title,
|
||||
final String subtitle,
|
||||
final String slug,
|
||||
final String status) {
|
||||
this.location = location;
|
||||
this.title = title;
|
||||
this.subtitle = subtitle;
|
||||
this.slug = slug;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Location getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getSubtitle() {
|
||||
return subtitle;
|
||||
}
|
||||
|
||||
public String getSlug() {
|
||||
return slug;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package awais.instagrabber.repositories.responses;
|
||||
package awais.instagrabber.repositories.responses.feed;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
@ -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;
|
@ -1,4 +1,4 @@
|
||||
package awais.instagrabber.repositories.responses;
|
||||
package awais.instagrabber.repositories.responses.feed;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
@ -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;
|
@ -1,4 +1,4 @@
|
||||
package awais.instagrabber.repositories.responses;
|
||||
package awais.instagrabber.repositories.responses.notification;
|
||||
|
||||
import awais.instagrabber.models.enums.NotificationType;
|
||||
|
@ -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 {
|
@ -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;
|
@ -1,4 +1,4 @@
|
||||
package awais.instagrabber.repositories.responses;
|
||||
package awais.instagrabber.repositories.responses.notification;
|
||||
|
||||
public class NotificationImage {
|
||||
private final String id;
|
@ -0,0 +1,38 @@
|
||||
package awais.instagrabber.repositories.responses.search;
|
||||
|
||||
import awais.instagrabber.repositories.responses.Hashtag;
|
||||
import awais.instagrabber.repositories.responses.Place;
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
|
||||
public class SearchItem {
|
||||
private final User user;
|
||||
private final Place place;
|
||||
private final Hashtag hashtag;
|
||||
private final int position;
|
||||
|
||||
public SearchItem(final User user,
|
||||
final Place place,
|
||||
final Hashtag hashtag,
|
||||
final int position) {
|
||||
this.user = user;
|
||||
this.place = place;
|
||||
this.hashtag = hashtag;
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public Place getPlace() {
|
||||
return place;
|
||||
}
|
||||
|
||||
public Hashtag getHashtag() {
|
||||
return hashtag;
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package awais.instagrabber.repositories.responses.search;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SearchResponse {
|
||||
// app
|
||||
private final List<SearchItem> list;
|
||||
// browser
|
||||
private final List<SearchItem> users;
|
||||
private final List<SearchItem> places;
|
||||
private final List<SearchItem> hashtags;
|
||||
// universal
|
||||
private final String status;
|
||||
|
||||
public SearchResponse(final List<SearchItem> list,
|
||||
final List<SearchItem> users,
|
||||
final List<SearchItem> places,
|
||||
final List<SearchItem> hashtags,
|
||||
final String status) {
|
||||
this.list = list;
|
||||
this.users = users;
|
||||
this.places = places;
|
||||
this.hashtags = hashtags;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public List<SearchItem> getList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<SearchItem> getUsers() {
|
||||
return users;
|
||||
}
|
||||
|
||||
public List<SearchItem> getPlaces() {
|
||||
return places;
|
||||
}
|
||||
|
||||
public List<SearchItem> getHashtags() {
|
||||
return hashtags;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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";
|
||||
|
@ -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})
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -0,0 +1,45 @@
|
||||
package awais.instagrabber.webservices;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import awais.instagrabber.repositories.SearchRepository;
|
||||
import awais.instagrabber.repositories.responses.search.SearchResponse;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Retrofit;
|
||||
|
||||
public class SearchService extends BaseService {
|
||||
private static final String TAG = "LocationService";
|
||||
|
||||
private final SearchRepository repository;
|
||||
|
||||
private static SearchService instance;
|
||||
|
||||
private SearchService() {
|
||||
final Retrofit retrofit = getRetrofitBuilder()
|
||||
.baseUrl("https://www.instagram.com")
|
||||
.build();
|
||||
repository = retrofit.create(SearchRepository.class);
|
||||
}
|
||||
|
||||
public static SearchService getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new SearchService();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public Call<SearchResponse> search(final boolean isLoggedIn,
|
||||
final String query,
|
||||
final String context) {
|
||||
final ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
|
||||
builder.put("query", query);
|
||||
// context is one of: "blended", "user", "place", "hashtag"
|
||||
// note that "place" and "hashtag" can contain ONE user result, who knows why
|
||||
builder.put("context", context);
|
||||
builder.put("count", "50");
|
||||
return repository.search(isLoggedIn
|
||||
? "https://i.instagram.com/api/v1/fbsearch/topsearch_flat/"
|
||||
: "https://www.instagram.com/web/search/topsearch/",
|
||||
builder.build());
|
||||
}
|
||||
}
|
@ -1,50 +1,35 @@
|
||||
package awaisomereport;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
134
app/src/main/java/awaisomereport/CrashReporterHelper.java
Normal file
134
app/src/main/java/awaisomereport/CrashReporterHelper.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
7
app/src/main/java/awaisomereport/ICrashHandler.java
Normal file
7
app/src/main/java/awaisomereport/ICrashHandler.java
Normal file
@ -0,0 +1,7 @@
|
||||
package awaisomereport;
|
||||
|
||||
public interface ICrashHandler {
|
||||
void uncaughtException(Thread t,
|
||||
Throwable exception,
|
||||
Thread.UncaughtExceptionHandler defaultEH);
|
||||
}
|
BIN
app/src/main/res/drawable/ic_hashtag.png
Normal file
BIN
app/src/main/res/drawable/ic_hashtag.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.5 KiB |
@ -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>
|
10
app/src/main/res/menu/dm_inbox_menu.xml
Normal file
10
app/src/main/res/menu/dm_inbox_menu.xml
Normal 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>
|
@ -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>
|
@ -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>
|
||||
|
@ -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%
|
||||
|
Loading…
Reference in New Issue
Block a user