1
0
mirror of https://github.com/KokaKiwi/BarInsta synced 2024-11-22 14:47:29 +00:00

Preferences! Check description

1. Added preferences screens.
2. Some DM changes.
3. Init profile actions using services
This commit is contained in:
Ammar Githam 2020-08-30 15:47:04 +09:00
parent 3f6c74d671
commit 40e810e88c
63 changed files with 1484 additions and 478 deletions

View File

@ -39,6 +39,7 @@ android {
dependencies {
def appcompat_version = "1.2.0"
def nav_version = "2.3.0"
def preference_version = "1.1.1"
implementation "androidx.appcompat:appcompat:$appcompat_version"
// For loading and tinting drawables on older versions of the platform
@ -50,6 +51,8 @@ dependencies {
implementation "androidx.navigation:navigation-fragment:$nav_version"
implementation "androidx.navigation:navigation-ui:$nav_version"
implementation "androidx.constraintlayout:constraintlayout:2.0.0"
implementation "androidx.preference:preference:$preference_version"
implementation 'org.jsoup:jsoup:1.13.1'
implementation 'com.github.bumptech.glide:glide:4.11.0'
@ -59,6 +62,7 @@ dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
}

View File

@ -1372,12 +1372,15 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
public void onClick(final View v) {
final String userIdFromCookie = Utils.getUserIdFromCookie(MainHelper.this.cookie);
final boolean isSelf = (isLoggedIn && mainActivity.profileModel != null) && userIdFromCookie != null && userIdFromCookie.equals(mainActivity.profileModel.getId());
if (!isLoggedIn && Utils.dataBox.getFavorite(mainActivity.userQuery) != null && v == mainActivity.mainBinding.profileView.btnFollow) {
if (!isLoggedIn
&& Utils.dataBox.getFavorite(mainActivity.userQuery) != null
&& v == mainActivity.mainBinding.profileView.btnFollow) {
Utils.dataBox.delFavorite(new DataBox.FavoriteModel(mainActivity.userQuery,
Long.parseLong(Utils.dataBox.getFavorite(mainActivity.userQuery).split("/")[1]),
mainActivity.locationModel != null ? mainActivity.locationModel.getName() : mainActivity.userQuery.replaceAll("^@", "")));
onRefresh();
} else if (!isLoggedIn && (v == mainActivity.mainBinding.profileView.btnFollow || v == mainActivity.mainBinding.profileView.btnFollowTag)) {
} else if (!isLoggedIn
&& (v == mainActivity.mainBinding.profileView.btnFollow || v == mainActivity.mainBinding.profileView.btnFollowTag)) {
Utils.dataBox.addFavorite(new DataBox.FavoriteModel(mainActivity.userQuery, System.currentTimeMillis(),
mainActivity.locationModel != null ? mainActivity.locationModel.getName() : mainActivity.userQuery.replaceAll("^@", "")));
onRefresh();
@ -1389,7 +1392,7 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
new ProfileAction().execute("block");
} else if (v == mainActivity.mainBinding.profileView.btnFollowTag) {
new ProfileAction().execute("followtag");
} else if (v == mainActivity.mainBinding.profileView.btnTagged || (v == mainActivity.mainBinding.profileView.btnRestrict && !isLoggedIn)) {
} else if (v == mainActivity.mainBinding.profileView.btnTagged || v == mainActivity.mainBinding.profileView.btnRestrict) {
mainActivity.startActivity(new Intent(mainActivity, SavedViewer.class)
.putExtra(Constants.EXTRAS_INDEX, "%" + mainActivity.profileModel.getId())
.putExtra(Constants.EXTRAS_USER, "@" + mainActivity.profileModel.getUsername())

View File

@ -15,7 +15,7 @@ public abstract class BaseLanguageActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
Utils.changeTheme(this);
Utils.changeTheme(getApplicationContext());
super.onCreate(savedInstanceState);
}
}

View File

@ -1,7 +1,9 @@
package awais.instagrabber.activities;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
@ -36,9 +38,9 @@ public final class Login extends BaseLanguageActivity implements View.OnClickLis
final String mainCookie = Utils.getCookie(url);
if (Utils.isEmpty(mainCookie) || !mainCookie.contains("; ds_user_id=")) ready = true;
else if (mainCookie.contains("; ds_user_id=") && ready) {
Utils.setupCookies(mainCookie);
settingsHelper.putString(Constants.COOKIE, mainCookie);
Toast.makeText(getApplicationContext(), R.string.login_success_loading_cookies, Toast.LENGTH_SHORT).show();
final Intent intent = new Intent();
intent.putExtra("cookie", mainCookie);
setResult(Constants.LOGIN_RESULT_CODE, intent);
finish();
}
}
@ -95,7 +97,6 @@ public final class Login extends BaseLanguageActivity implements View.OnClickLis
}
@SuppressLint("SetJavaScriptEnabled")
@SuppressWarnings("deprecation")
private void initWebView() {
if (loginBinding != null) {
loginBinding.webView.setWebChromeClient(webChromeClient);

View File

@ -31,6 +31,19 @@ import static awais.instagrabber.utils.Utils.settingsHelper;
public class MainActivity extends BaseLanguageActivity {
private static final String TAG = "MainActivity";
private static final List<Integer> SHOW_BOTTOM_VIEW_DESTINATIONS = Arrays.asList(
R.id.directMessagesInboxFragment,
R.id.feedFragment,
R.id.profileFragment,
R.id.discoverFragment,
R.id.morePreferencesFragment);
private static final List<Integer> KEEP_SCROLL_BEHAVIOUR_DESTINATIONS = Arrays.asList(
R.id.directMessagesInboxFragment,
R.id.feedFragment,
R.id.profileFragment,
R.id.discoverFragment,
R.id.morePreferencesFragment,
R.id.settingsPreferencesFragment);
private ActivityMainBinding binding;
private LiveData<NavController> currentNavControllerLiveData;
@ -69,17 +82,16 @@ public class MainActivity extends BaseLanguageActivity {
R.navigation.direct_messages_nav_graph,
R.navigation.feed_nav_graph,
R.navigation.profile_nav_graph,
R.navigation.discover_nav_graph
R.navigation.discover_nav_graph,
R.navigation.more_nav_graph
));
binding.bottomNavView.setSelectedItemId(R.id.feed_nav_graph);
final LiveData<NavController> navControllerLiveData = setupWithNavController(
binding.bottomNavView,
navList,
getSupportFragmentManager(),
R.id.main_nav_host,
getIntent(),
1);
0);
navControllerLiveData.observe(this, this::setupNavigation);
currentNavControllerLiveData = navControllerLiveData;
}
@ -89,19 +101,12 @@ public class MainActivity extends BaseLanguageActivity {
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
binding.appBarLayout.setExpanded(true, true);
final int destinationId = destination.getId();
final List<Integer> showBottomView = Arrays.asList(
R.id.directMessagesInboxFragment,
R.id.feedFragment,
R.id.profileFragment,
R.id.discoverFragment);
if (showBottomView.contains(destinationId)) {
binding.bottomNavView.setVisibility(SHOW_BOTTOM_VIEW_DESTINATIONS.contains(destinationId) ? View.VISIBLE : View.GONE);
if (KEEP_SCROLL_BEHAVIOUR_DESTINATIONS.contains(destinationId)) {
setScrollingBehaviour();
binding.bottomNavView.setVisibility(View.VISIBLE);
return;
} else {
removeScrollingBehaviour();
}
removeScrollingBehaviour();
binding.bottomNavView.setVisibility(View.GONE);
});
}

View File

@ -41,7 +41,6 @@ import awais.instagrabber.asyncs.SuggestionsFetcher;
import awais.instagrabber.asyncs.UsernameFetcher;
import awais.instagrabber.asyncs.i.iStoryStatusFetcher;
import awais.instagrabber.customviews.MouseDrawer;
import awais.instagrabber.databinding.ActivityMainBinding;
import awais.instagrabber.databinding.ActivityMainbackupBinding;
import awais.instagrabber.dialogs.AboutDialog;
import awais.instagrabber.dialogs.QuickAccessDialog;
@ -95,8 +94,7 @@ public final class MainActivityBackup extends BaseLanguageActivity {
// .putExtra(Constants.EXTRAS_HIGHLIGHT, highlightModel.getTitle())
// .putExtra(Constants.EXTRAS_STORIES, result)
// );
}
else
} else
Toast.makeText(MainActivityBackup.this, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
@ -105,7 +103,8 @@ public final class MainActivityBackup extends BaseLanguageActivity {
private SuggestionsAdapter suggestionAdapter;
private MenuItem searchAction;
public @NonNull ActivityMainbackupBinding mainBinding;
public @NonNull
ActivityMainbackupBinding mainBinding;
public SearchView searchView;
public MenuItem downloadAction, settingsAction, dmsAction, notifAction;
public StoryModel[] storyModels;
@ -253,7 +252,7 @@ public final class MainActivityBackup extends BaseLanguageActivity {
final boolean isQueryNull = userQuery == null;
if (isQueryNull) {
allItems.clear();
mainBinding.profileView.privatePage1.setImageResource(R.drawable.ic_info);
mainBinding.profileView.privatePage1.setImageResource(R.drawable.ic_outline_info_24);
mainBinding.profileView.privatePage2.setTextSize(20);
mainBinding.profileView.privatePage2.setText(isLoggedIn ? R.string.no_acc_logged_in : R.string.no_acc);
mainBinding.profileView.privatePage.setVisibility(View.VISIBLE);

View File

@ -8,7 +8,7 @@ import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import awais.instagrabber.adapters.viewholder.DirectMessageInboxItemViewHolder;
import awais.instagrabber.databinding.LayoutIncludeSimpleItemBinding;
import awais.instagrabber.databinding.LayoutDmInboxItemBinding;
import awais.instagrabber.models.direct_messages.InboxThreadModel;
public final class DirectMessageInboxAdapter extends ListAdapter<InboxThreadModel, DirectMessageInboxItemViewHolder> {
@ -35,7 +35,7 @@ public final class DirectMessageInboxAdapter extends ListAdapter<InboxThreadMode
@Override
public DirectMessageInboxItemViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final LayoutIncludeSimpleItemBinding binding = LayoutIncludeSimpleItemBinding.inflate(layoutInflater, parent, false);
final LayoutDmInboxItemBinding binding = LayoutDmInboxItemBinding.inflate(layoutInflater, parent, false);
return new DirectMessageInboxItemViewHolder(binding);
}

View File

@ -2,18 +2,16 @@ package awais.instagrabber.adapters.viewholder;
import android.content.Context;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.core.text.HtmlCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestManager;
import com.facebook.drawee.view.SimpleDraweeView;
import awais.instagrabber.R;
import awais.instagrabber.databinding.LayoutIncludeSimpleItemBinding;
import awais.instagrabber.databinding.LayoutDmInboxItemBinding;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.models.direct_messages.InboxThreadModel;
@ -21,19 +19,18 @@ import awais.instagrabber.models.enums.DirectItemType;
public final class DirectMessageInboxItemViewHolder extends RecyclerView.ViewHolder {
private final LinearLayout multipleProfilePicsContainer;
private final ImageView[] multipleProfilePics;
private final LayoutIncludeSimpleItemBinding binding;
private final SimpleDraweeView[] multipleProfilePics;
private final LayoutDmInboxItemBinding binding;
public DirectMessageInboxItemViewHolder(@NonNull final LayoutIncludeSimpleItemBinding binding) {
public DirectMessageInboxItemViewHolder(@NonNull final LayoutDmInboxItemBinding binding) {
super(binding.getRoot());
this.binding = binding;
binding.tvLikes.setVisibility(View.GONE);
multipleProfilePicsContainer = binding.container;
multipleProfilePicsContainer = binding.multiPicContainer;
final LinearLayout containerChild = (LinearLayout) multipleProfilePicsContainer.getChildAt(1);
multipleProfilePics = new ImageView[]{
(ImageView) multipleProfilePicsContainer.getChildAt(0),
(ImageView) containerChild.getChildAt(0),
(ImageView) containerChild.getChildAt(1)
multipleProfilePics = new SimpleDraweeView[]{
(SimpleDraweeView) multipleProfilePicsContainer.getChildAt(0),
(SimpleDraweeView) containerChild.getChildAt(0),
(SimpleDraweeView) containerChild.getChildAt(1)
};
binding.tvDate.setSelected(true);
binding.tvUsername.setSelected(true);
@ -45,17 +42,17 @@ public final class DirectMessageInboxItemViewHolder extends RecyclerView.ViewHol
return;
}
itemView.setTag(model);
final RequestManager glideRequestManager = Glide.with(itemView);
final ProfileModel[] users = model.getUsers();
if (users.length > 1) {
binding.ivProfilePic.setVisibility(View.GONE);
multipleProfilePicsContainer.setVisibility(View.VISIBLE);
for (int i = 0; i < Math.min(3, users.length); ++i)
glideRequestManager.load(users[i].getSdProfilePic()).into(multipleProfilePics[i]);
for (int i = 0; i < Math.min(3, users.length); ++i) {
multipleProfilePics[i].setImageURI(users[i].getSdProfilePic());
}
} else {
binding.ivProfilePic.setVisibility(View.VISIBLE);
multipleProfilePicsContainer.setVisibility(View.GONE);
glideRequestManager.load(users.length == 1 ? users[0].getSdProfilePic() : null).into(binding.ivProfilePic);
binding.ivProfilePic.setImageURI(users.length == 1 ? users[0].getSdProfilePic() : null);
}
binding.tvUsername.setText(model.getThreadTitle());
final DirectItemModel lastItemModel = itemModels[itemModels.length - 1];

View File

@ -25,6 +25,8 @@ import awaisomereport.LogCollector;
import static awais.instagrabber.utils.Utils.logCollector;
public final class FeedFetcher extends AsyncTask<Void, Void, FeedModel[]> {
private static final String TAG = "FeedFetcher";
private static final int maxItemsToLoad = 25; // max is 50, but that's too many posts, setting more than 30 is gay
private final String endCursor;
private final FetchListener<FeedModel[]> fetchListener;
@ -61,8 +63,10 @@ public final class FeedFetcher extends AsyncTask<Void, Void, FeedModel[]> {
final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
final JSONObject timelineFeed = new JSONObject(Utils.readFromConnection(urlConnection)).getJSONObject("data")
.getJSONObject(Constants.EXTRAS_USER).getJSONObject("edge_web_feed_timeline");
final String json = Utils.readFromConnection(urlConnection);
Log.d(TAG, json);
final JSONObject timelineFeed = new JSONObject(json).getJSONObject("data")
.getJSONObject(Constants.EXTRAS_USER).getJSONObject("edge_web_feed_timeline");
final String endCursor;
final boolean hasNextPage;
@ -83,7 +87,8 @@ public final class FeedFetcher extends AsyncTask<Void, Void, FeedModel[]> {
for (int i = 0; i < feedLen; ++i) {
final JSONObject feedItem = feedItems.getJSONObject(i).getJSONObject("node");
final String mediaType = feedItem.optString("__typename");
if (mediaType.isEmpty() || "GraphSuggestedUserFeedUnit".equals(mediaType)) continue;
if (mediaType.isEmpty() || "GraphSuggestedUserFeedUnit".equals(mediaType))
continue;
final boolean isVideo = feedItem.optBoolean("is_video");
final long videoViews = feedItem.optLong("video_view_count", 0);
@ -93,7 +98,8 @@ public final class FeedFetcher extends AsyncTask<Void, Void, FeedModel[]> {
final String resourceUrl;
if (isVideo) resourceUrl = feedItem.getString("video_url");
else resourceUrl = feedItem.has("display_resources") ? Utils.getHighQualityImage(feedItem) : displayUrl;
else
resourceUrl = feedItem.has("display_resources") ? Utils.getHighQualityImage(feedItem) : displayUrl;
ProfileModel profileModel = null;
if (feedItem.has("owner")) {

View File

@ -8,6 +8,9 @@ import androidx.annotation.Nullable;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
@ -23,6 +26,8 @@ import static awais.instagrabber.utils.Utils.logCollector;
import static awaisomereport.LogCollector.LogFile;
public final class InboxFetcher extends AsyncTask<Void, Void, InboxModel> {
private static final String TAG = "InboxFetcher";
private final String endCursor;
private final FetchListener<InboxModel> fetchListener;
@ -43,46 +48,59 @@ public final class InboxFetcher extends AsyncTask<Void, Void, InboxModel> {
conn.setRequestProperty("Accept-Language", LocaleUtils.getCurrentLocale().getLanguage() + ",en-US;q=0.8");
conn.setUseCaches(false);
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
JSONObject data = new JSONObject(Utils.readFromConnection(conn));
// try (FileWriter fileWriter = new FileWriter(new File("/sdcard/test.json"))) {
// fileWriter.write(data.toString(2));
// }
final long seqId = data.optLong("seq_id");
final int pendingRequestsCount = data.optInt("pending_requests_total");
final boolean hasPendingTopRequests = data.optBoolean("has_pending_top_requests");
data = data.getJSONObject("inbox");
final boolean blendedInboxEnabled = data.optBoolean("blended_inbox_enabled");
final boolean hasOlder = data.optBoolean("has_older");
final int unseenCount = data.optInt("unseen_count");
final long unseenCountTimestamp = data.optLong("unseen_count_ts");
final String oldestCursor = data.optString("oldest_cursor");
InboxThreadModel[] inboxThreadModels = null;
final JSONArray threadsArray = data.optJSONArray("threads");
if (threadsArray != null) {
final int threadsLen = threadsArray.length();
inboxThreadModels = new InboxThreadModel[threadsLen];
for (int i = 0; i < threadsLen; ++i)
inboxThreadModels[i] = Utils.createInboxThreadModel(threadsArray.getJSONObject(i), false);
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
final InputStream responseInputStream = conn.getErrorStream();
final BufferedReader r = new BufferedReader(new InputStreamReader(responseInputStream));
final StringBuilder builder = new StringBuilder();
for (String line = r.readLine(); line != null; line = r.readLine()) {
if (builder.length() != 0) {
builder.append("\n");
}
builder.append(line);
}
result = new InboxModel(hasOlder, hasPendingTopRequests,
blendedInboxEnabled, unseenCount, pendingRequestsCount,
seqId, unseenCountTimestamp, oldestCursor, inboxThreadModels);
Log.e(TAG, "Error response: " + conn.getResponseCode() + ", " + builder.toString());
r.close();
conn.disconnect();
return null;
}
JSONObject data = new JSONObject(Utils.readFromConnection(conn));
// try (FileWriter fileWriter = new FileWriter(new File("/sdcard/test.json"))) {
// fileWriter.write(data.toString(2));
// }
final long seqId = data.optLong("seq_id");
final int pendingRequestsCount = data.optInt("pending_requests_total");
final boolean hasPendingTopRequests = data.optBoolean("has_pending_top_requests");
data = data.getJSONObject("inbox");
final boolean blendedInboxEnabled = data.optBoolean("blended_inbox_enabled");
final boolean hasOlder = data.optBoolean("has_older");
final int unseenCount = data.optInt("unseen_count");
final long unseenCountTimestamp = data.optLong("unseen_count_ts");
final String oldestCursor = data.optString("oldest_cursor");
InboxThreadModel[] inboxThreadModels = null;
final JSONArray threadsArray = data.optJSONArray("threads");
if (threadsArray != null) {
final int threadsLen = threadsArray.length();
inboxThreadModels = new InboxThreadModel[threadsLen];
for (int i = 0; i < threadsLen; ++i)
inboxThreadModels[i] = Utils.createInboxThreadModel(threadsArray.getJSONObject(i), false);
}
result = new InboxModel(hasOlder, hasPendingTopRequests,
blendedInboxEnabled, unseenCount, pendingRequestsCount,
seqId, unseenCountTimestamp, oldestCursor, inboxThreadModels);
conn.disconnect();
} catch (final Exception e) {
result = null;
if (logCollector != null)
logCollector.appendException(e, LogFile.ASYNC_DMS, "doInBackground");
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
}
return result;

View File

@ -24,6 +24,8 @@ import androidx.fragment.app.FragmentManager;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import java.text.SimpleDateFormat;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.R;
import awais.instagrabber.activities.Login;
@ -50,6 +52,7 @@ import static awais.instagrabber.utils.Constants.MUTED_VIDEOS;
import static awais.instagrabber.utils.Constants.STORIESIG;
import static awais.instagrabber.utils.Utils.settingsHelper;
@Deprecated
public final class SettingsDialog extends BottomSheetDialogFragment implements View.OnClickListener, AdapterView.OnItemSelectedListener,
CompoundButton.OnCheckedChangeListener {
private Activity activity;
@ -65,7 +68,8 @@ public final class SettingsDialog extends BottomSheetDialogFragment implements V
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
if (requestCode != 6200) return;
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) showDirectoryChooser();
else Toast.makeText(activity, R.string.direct_download_perms_ask, Toast.LENGTH_SHORT).show();
else
Toast.makeText(activity, R.string.direct_download_perms_ask, Toast.LENGTH_SHORT).show();
}
private void showDirectoryChooser() {
@ -73,10 +77,10 @@ public final class SettingsDialog extends BottomSheetDialogFragment implements V
if (fragmentManager == null) fragmentManager = getChildFragmentManager();
new DirectoryChooser().setInitialDirectory(settingsHelper.getString(FOLDER_PATH))
.setInteractionListener(path -> {
settingsHelper.putString(FOLDER_PATH, path);
somethingChanged = true;
}).show(fragmentManager, null);
.setInteractionListener(path -> {
settingsHelper.putString(FOLDER_PATH, path);
somethingChanged = true;
}).show(fragmentManager, null);
}
@NonNull
@ -115,12 +119,12 @@ public final class SettingsDialog extends BottomSheetDialogFragment implements V
if (Utils.isEmpty(settingsHelper.getString(Constants.COOKIE))) btnLogout.setEnabled(false);
spAppTheme = contentView.findViewById(R.id.spAppTheme);
currentTheme = settingsHelper.getInteger(APP_THEME);
currentTheme = Integer.parseInt(settingsHelper.getString(APP_THEME));
spAppTheme.setSelection(currentTheme);
spAppTheme.setOnItemSelectedListener(this);
spLanguage = contentView.findViewById(R.id.spLanguage);
currentLanguage = settingsHelper.getInteger(APP_LANGUAGE);
currentLanguage = Integer.parseInt(settingsHelper.getString(APP_LANGUAGE));
spLanguage.setSelection(currentLanguage);
spLanguage.setOnItemSelectedListener(this);
@ -178,13 +182,13 @@ public final class SettingsDialog extends BottomSheetDialogFragment implements V
public void onItemSelected(final AdapterView<?> spinner, final View view, final int position, final long id) {
if (spinner == spAppTheme) {
if (position != currentTheme) {
settingsHelper.putInteger(APP_THEME, position);
settingsHelper.putString(APP_THEME, String.valueOf(position));
somethingChanged = true;
}
} else if (spinner == spLanguage) {
selectedLanguage = position;
if (position != currentLanguage) {
settingsHelper.putInteger(APP_LANGUAGE, position);
settingsHelper.putString(APP_LANGUAGE, String.valueOf(position));
somethingChanged = true;
}
}
@ -205,7 +209,28 @@ public final class SettingsDialog extends BottomSheetDialogFragment implements V
requestPermissions(Utils.PERMS, 6007);
else Utils.showImportExportDialog(activity);
} else if (v == btnTimeSettings) {
new TimeSettingsDialog().show(fragmentManager, null);
new TimeSettingsDialog(settingsHelper.getBoolean(Constants.CUSTOM_DATE_TIME_FORMAT_ENABLED),
settingsHelper.getString(Constants.CUSTOM_DATE_TIME_FORMAT),
settingsHelper.getString(Constants.DATE_TIME_SELECTION),
(isCustomFormat,
formatSelection,
spTimeFormatSelectedItemPosition,
spSeparatorSelectedItemPosition,
spDateFormatSelectedItemPosition,
selectedFormat, currentFormat) -> {
if (isCustomFormat) {
settingsHelper.putString(Constants.CUSTOM_DATE_TIME_FORMAT, formatSelection);
} else {
final String formatSelectionUpdated = spTimeFormatSelectedItemPosition + ";"
+ spSeparatorSelectedItemPosition + ';'
+ spDateFormatSelectedItemPosition; // time;separator;date
settingsHelper.putString(Constants.DATE_TIME_FORMAT, selectedFormat);
settingsHelper.putString(Constants.DATE_TIME_SELECTION, formatSelectionUpdated);
}
settingsHelper.putBoolean(Constants.CUSTOM_DATE_TIME_FORMAT_ENABLED, isCustomFormat);
Utils.datetimeParser = (SimpleDateFormat) currentFormat.clone();
}
).show(fragmentManager, null);
} else if (v == btnReport) {
CrashReporter.get(activity.getApplication()).zipLogs().startCrashEmailIntent(activity, true);
} else if (v == btnSaveTo) {

View File

@ -6,6 +6,9 @@ import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.CompoundButton;
@ -19,38 +22,42 @@ import java.util.Date;
import java.util.GregorianCalendar;
import awais.instagrabber.databinding.DialogTimeSettingsBinding;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.LocaleUtils;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper;
public final class TimeSettingsDialog extends DialogFragment implements AdapterView.OnItemSelectedListener, CompoundButton.OnCheckedChangeListener,
View.OnClickListener, TextWatcher {
private DialogTimeSettingsBinding timeSettingsBinding;
private final Date magicDate;
private SimpleDateFormat currentFormat;
private String selectedFormat;
private boolean customDateTimeFormatEnabled;
private String customDateTimeFormat;
private String dateTimeSelection;
private final OnConfirmListener onConfirmListener;
public TimeSettingsDialog() {
super();
public TimeSettingsDialog(final boolean customDateTimeFormatEnabled,
final String customDateTimeFormat,
final String dateTimeSelection,
final OnConfirmListener onConfirmListener) {
this.customDateTimeFormatEnabled = customDateTimeFormatEnabled;
this.customDateTimeFormat = customDateTimeFormat;
this.dateTimeSelection = dateTimeSelection;
this.onConfirmListener = onConfirmListener;
final Calendar instance = GregorianCalendar.getInstance();
instance.set(2020, 5, 22, 8, 17, 13);
magicDate = instance.getTime();
}
@NonNull
@Override
public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {
final Dialog dialog = super.onCreateDialog(savedInstanceState);
timeSettingsBinding = DialogTimeSettingsBinding.inflate(LayoutInflater.from(getContext()));
public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {
timeSettingsBinding = DialogTimeSettingsBinding.inflate(inflater, container, false);
timeSettingsBinding.cbCustomFormat.setOnCheckedChangeListener(this);
timeSettingsBinding.cbCustomFormat.setChecked(customDateTimeFormatEnabled);
timeSettingsBinding.etCustomFormat.setText(customDateTimeFormat);
timeSettingsBinding.cbCustomFormat.setChecked(settingsHelper.getBoolean(Constants.CUSTOM_DATE_TIME_FORMAT_ENABLED));
timeSettingsBinding.etCustomFormat.setText(settingsHelper.getString(Constants.CUSTOM_DATE_TIME_FORMAT));
final String[] dateTimeFormat = settingsHelper.getString(Constants.DATE_TIME_SELECTION).split(";"); // output = time;separator;date
final String[] dateTimeFormat = dateTimeSelection.split(";"); // output = time;separator;date
timeSettingsBinding.spTimeFormat.setSelection(Integer.parseInt(dateTimeFormat[0]));
timeSettingsBinding.spSeparator.setSelection(Integer.parseInt(dateTimeFormat[1]));
timeSettingsBinding.spDateFormat.setSelection(Integer.parseInt(dateTimeFormat[2]));
@ -67,8 +74,7 @@ public final class TimeSettingsDialog extends DialogFragment implements AdapterV
timeSettingsBinding.btnConfirm.setOnClickListener(this);
timeSettingsBinding.btnInfo.setOnClickListener(this);
dialog.setContentView(timeSettingsBinding.getRoot());
return dialog;
return timeSettingsBinding.getRoot();
}
private void refreshTimeFormat() {
@ -87,7 +93,8 @@ public final class TimeSettingsDialog extends DialogFragment implements AdapterV
+ (isSwapTime ? dateStr : timeStr);
timeSettingsBinding.btnConfirm.setEnabled(true);
timeSettingsBinding.timePreview.setText((currentFormat = new SimpleDateFormat(selectedFormat, LocaleUtils.getCurrentLocale())).format(magicDate));
currentFormat = new SimpleDateFormat(selectedFormat, LocaleUtils.getCurrentLocale());
timeSettingsBinding.timePreview.setText(currentFormat.format(magicDate));
}
}
@ -96,8 +103,8 @@ public final class TimeSettingsDialog extends DialogFragment implements AdapterV
//noinspection ConstantConditions
final String string = timeSettingsBinding.etCustomFormat.getText().toString();
if (Utils.isEmpty(string)) throw new NullPointerException();
final String format = (currentFormat = new SimpleDateFormat(string, LocaleUtils.getCurrentLocale())).format(magicDate);
currentFormat = new SimpleDateFormat(string, LocaleUtils.getCurrentLocale());
final String format = currentFormat.format(magicDate);
timeSettingsBinding.timePreview.setText(format);
timeSettingsBinding.btnConfirm.setEnabled(true);
@ -115,6 +122,8 @@ public final class TimeSettingsDialog extends DialogFragment implements AdapterV
@Override
public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
if (buttonView == timeSettingsBinding.cbCustomFormat) {
final View parent = (View) timeSettingsBinding.etCustomFormat.getParent();
parent.setVisibility(isChecked ? View.VISIBLE : View.GONE);
timeSettingsBinding.etCustomFormat.setEnabled(isChecked);
timeSettingsBinding.btnInfo.setEnabled(isChecked);
@ -134,26 +143,16 @@ public final class TimeSettingsDialog extends DialogFragment implements AdapterV
@Override
public void onClick(final View v) {
if (v == timeSettingsBinding.btnConfirm) {
final String formatSelection;
final boolean isCustomFormat = timeSettingsBinding.cbCustomFormat.isChecked();
if (isCustomFormat) {
//noinspection ConstantConditions
formatSelection = timeSettingsBinding.etCustomFormat.getText().toString();
settingsHelper.putString(Constants.CUSTOM_DATE_TIME_FORMAT, formatSelection);
} else {
formatSelection = timeSettingsBinding.spTimeFormat.getSelectedItemPosition() + ";"
+ timeSettingsBinding.spSeparator.getSelectedItemPosition() + ';'
+ timeSettingsBinding.spDateFormat.getSelectedItemPosition(); // time;separator;date
settingsHelper.putString(Constants.DATE_TIME_FORMAT, selectedFormat);
settingsHelper.putString(Constants.DATE_TIME_SELECTION, formatSelection);
final Editable etCustomFormatText = timeSettingsBinding.etCustomFormat.getText();
if (onConfirmListener != null) {
onConfirmListener.onConfirm(timeSettingsBinding.cbCustomFormat.isChecked(),
etCustomFormatText == null ? null : etCustomFormatText.toString(),
timeSettingsBinding.spTimeFormat.getSelectedItemPosition(),
timeSettingsBinding.spSeparator.getSelectedItemPosition(),
timeSettingsBinding.spDateFormat.getSelectedItemPosition(),
selectedFormat,
currentFormat);
}
settingsHelper.putBoolean(Constants.CUSTOM_DATE_TIME_FORMAT_ENABLED, isCustomFormat);
Utils.datetimeParser = (SimpleDateFormat) currentFormat.clone();
dismiss();
} else if (v == timeSettingsBinding.btnInfo) {
timeSettingsBinding.customPanel.setVisibility(timeSettingsBinding.customPanel
@ -162,6 +161,14 @@ public final class TimeSettingsDialog extends DialogFragment implements AdapterV
}
}
public interface OnConfirmListener {
void onConfirm(boolean isCustomFormat,
String formatSelection,
int spTimeFormatSelectedItemPosition,
int spSeparatorSelectedItemPosition,
int spDateFormatSelectedItemPosition, final String selectedFormat, final SimpleDateFormat currentFormat);
}
@Override
public void onNothingSelected(final AdapterView<?> parent) { }
@ -170,4 +177,17 @@ public final class TimeSettingsDialog extends DialogFragment implements AdapterV
@Override
public void afterTextChanged(final Editable s) { }
@Override
public void onResume() {
super.onResume();
final Dialog dialog = getDialog();
if (dialog == null) return;
final Window window = dialog.getWindow();
if (window == null) return;
final WindowManager.LayoutParams params = window.getAttributes();
params.width = ViewGroup.LayoutParams.MATCH_PARENT;
params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
window.setAttributes(params);
}
}

View File

@ -11,6 +11,8 @@ import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
@ -19,11 +21,11 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.AppCompatImageView;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentContainerView;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
@ -49,7 +51,6 @@ import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.activities.PostViewer;
import awais.instagrabber.activities.ProfileViewer;
import awais.instagrabber.adapters.DirectMessageItemsAdapter;
import awais.instagrabber.asyncs.ImageUploader;
import awais.instagrabber.asyncs.direct_messages.DirectMessageInboxThreadFetcher;
@ -61,7 +62,6 @@ import awais.instagrabber.interfaces.MentionClickListener;
import awais.instagrabber.models.ImageUploadOptions;
import awais.instagrabber.models.PostModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.direct_messages.DirectItemModel;
import awais.instagrabber.models.direct_messages.InboxThreadModel;
import awais.instagrabber.models.enums.DirectItemType;
@ -74,7 +74,7 @@ public class DirectMessageThreadFragment extends Fragment {
private static final String TAG = "DirectMessagesThreadFmt";
private static final int PICK_IMAGE = 100;
private FragmentActivity fragmentActivity;
private AppCompatActivity fragmentActivity;
private String threadId, threadTitle;
private String cursor;
private final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
@ -83,7 +83,7 @@ public class DirectMessageThreadFragment extends Fragment {
private DirectItemModelListViewModel listViewModel;
private DirectItemModel directItemModel;
private RecyclerView messageList;
private AppCompatImageView dmInfo;
// private AppCompatImageView dmInfo;
private boolean hasSentSomething, hasDeletedSomething;
private boolean hasOlder = true;
@ -157,7 +157,8 @@ public class DirectMessageThreadFragment extends Fragment {
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fragmentActivity = requireActivity();
fragmentActivity = (AppCompatActivity) requireActivity();
setHasOptionsMenu(true);
}
@Override
@ -165,8 +166,8 @@ public class DirectMessageThreadFragment extends Fragment {
final ViewGroup container,
final Bundle savedInstanceState) {
binding = FragmentDirectMessagesThreadBinding.inflate(inflater, container, false);
CoordinatorLayout containerTwo = (CoordinatorLayout) container.getParent();
dmInfo = containerTwo.findViewById(R.id.dmInfo);
final FragmentContainerView containerTwo = (FragmentContainerView) container.getParent();
// dmInfo = containerTwo.findViewById(R.id.dmInfo);
final LinearLayout root = binding.getRoot();
listViewModel = new ViewModelProvider(fragmentActivity).get(DirectItemModelListViewModel.class);
if (getArguments() == null) {
@ -177,6 +178,10 @@ public class DirectMessageThreadFragment extends Fragment {
threadId = DirectMessageThreadFragmentArgs.fromBundle(getArguments()).getThreadId();
}
threadTitle = DirectMessageThreadFragmentArgs.fromBundle(getArguments()).getTitle();
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
if (actionBar != null) {
actionBar.setTitle(threadTitle);
}
binding.swipeRefreshLayout.setEnabled(false);
messageList = binding.messageList;
messageList.setHasFixedSize(true);
@ -192,11 +197,11 @@ public class DirectMessageThreadFragment extends Fragment {
}
new DirectMessageInboxThreadFetcher(threadId, UserInboxDirection.OLDER, cursor, fetchListener).execute(); // serial because we don't want messages to be randomly ordered
}));
dmInfo.setOnClickListener(v -> {
final NavDirections action =
DirectMessageThreadFragmentDirections.actionDMThreadFragmentToDMSettingsFragment(threadId, threadTitle);
NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(action);
});
// dmInfo.setOnClickListener(v -> {
// final NavDirections action =
// DirectMessageThreadFragmentDirections.actionDMThreadFragmentToDMSettingsFragment(threadId, threadTitle);
// NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(action);
// });
final DialogInterface.OnClickListener onDialogListener = (dialogInterface, which) -> {
if (which == 0) {
@ -250,12 +255,11 @@ public class DirectMessageThreadFragment extends Fragment {
default:
Log.d("austin_debug", "unsupported type " + itemType);
}
}
else if (which == 1) {
} else if (which == 1) {
sendText(null, directItemModel.getItemId(), directItemModel.isLiked());
}
else if (which == 2) {
if (String.valueOf(directItemModel.getUserId()).equals(myId)) new Unsend().execute();
} else if (which == 2) {
if (String.valueOf(directItemModel.getUserId()).equals(myId))
new Unsend().execute();
else searchUsername(getUser(directItemModel.getUserId()).getUsername());
}
};
@ -320,6 +324,12 @@ public class DirectMessageThreadFragment extends Fragment {
return root;
}
@Override
public void onPrepareOptionsMenu(@NonNull final Menu menu) {
final MenuItem item = menu.findItem(R.id.favourites);
item.setVisible(false);
}
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
@ -423,7 +433,9 @@ public class DirectMessageThreadFragment extends Fragment {
}
private void searchUsername(final String text) {
startActivity(new Intent(requireContext(), ProfileViewer.class).putExtra(Constants.EXTRAS_USERNAME, text));
// startActivity(new Intent(requireContext(), ProfileViewer.class).putExtra(Constants.EXTRAS_USERNAME, text));
final NavDirections action = DirectMessageThreadFragmentDirections.actionDirectMessagesThreadFragmentToProfileFragment("@" + text);
NavHostFragment.findNavController(this).navigate(action);
}
public static class DirectItemModelListViewModel extends ViewModel {
@ -434,8 +446,7 @@ public class DirectMessageThreadFragment extends Fragment {
if (list == null) {
list = new MutableLiveData<>();
isEmpty = true;
}
else isEmpty = false;
} else isEmpty = false;
return list;
}
@ -451,10 +462,10 @@ public class DirectMessageThreadFragment extends Fragment {
class Unsend extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... lmao) {
final String url = "https://i.instagram.com/api/v1/direct_v2/threads/"+threadId+"/items/"+directItemModel.getItemId()+"/delete/";
final String url = "https://i.instagram.com/api/v1/direct_v2/threads/" + threadId + "/items/" + directItemModel.getItemId() + "/delete/";
try {
String urlParameters = "_csrftoken=" + cookie.split("csrftoken=")[1].split(";")[0]
+"&_uuid=" + Utils.settingsHelper.getString(Constants.DEVICE_UUID);
+ "&_uuid=" + Utils.settingsHelper.getString(Constants.DEVICE_UUID);
final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setUseCaches(false);

View File

@ -60,6 +60,7 @@ public class DiscoverFragment extends Fragment {
private String discoverEndMaxId;
private ActionMode actionMode;
private DiscoverItemViewModel discoverItemViewModel;
private boolean shouldRefresh = true;
private final FetchListener<DiscoverTopicModel> topicFetchListener = new FetchListener<DiscoverTopicModel>() {
@Override
@ -154,14 +155,20 @@ public class DiscoverFragment extends Fragment {
final ViewGroup container,
final Bundle savedInstanceState) {
if (root != null) {
shouldRefresh = false;
return root;
}
binding = FragmentDiscoverBinding.inflate(inflater, container, false);
root = binding.getRoot();
setupExplore();
return root;
}
@Override
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
if (!shouldRefresh) return;
setupExplore();
}
private void setupExplore() {
discoverItemViewModel = new ViewModelProvider(fragmentActivity).get(DiscoverItemViewModel.class);
final GridAutofitLayoutManager layoutManager = new GridAutofitLayoutManager(requireContext(), Utils.convertDpToPx(110));

View File

@ -80,6 +80,7 @@ public class FeedFragment extends Fragment {
private String feedEndCursor = null;
private FeedViewModel feedViewModel;
private VideoAwareRecyclerScroller videoAwareRecyclerScroller;
private boolean shouldRefresh = true;
private final FetchListener<FeedModel[]> feedFetchListener = new FetchListener<FeedModel[]>() {
@Override
@ -164,14 +165,21 @@ public class FeedFragment extends Fragment {
final ViewGroup container,
final Bundle savedInstanceState) {
if (root != null) {
shouldRefresh = false;
return root;
}
binding = FragmentFeedBinding.inflate(inflater, container, false);
root = binding.getRoot();
return root;
}
@Override
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
if (!shouldRefresh) return;
// setupActionBar();
setupFeedStories();
setupFeed();
return root;
shouldRefresh = false;
}
@Override

View File

@ -10,6 +10,7 @@ import android.os.Looper;
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;
import android.view.MenuItem;
@ -29,8 +30,10 @@ import androidx.core.view.ViewCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.activities.FollowViewer;
@ -47,17 +50,24 @@ import awais.instagrabber.customviews.PrimaryActionModeCallback;
import awais.instagrabber.customviews.PrimaryActionModeCallback.CallbacksHelper;
import awais.instagrabber.customviews.helpers.GridAutofitLayoutManager;
import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
import awais.instagrabber.databinding.FragmentProfileBinding;
import awais.instagrabber.fragments.main.viewmodels.ProfilePostsViewModel;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.PostModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.StoryModel;
import awais.instagrabber.models.enums.DownloadMethod;
import awais.instagrabber.models.enums.ItemGetType;
import awais.instagrabber.services.ProfileService;
import awais.instagrabber.repositories.responses.FriendshipRepositoryChangeResponseRootObject;
import awais.instagrabber.services.FriendshipService;
import awais.instagrabber.services.ServiceCallback;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.DataBox;
import awais.instagrabber.utils.Utils;
import awaisomereport.LogCollector;
import static awais.instagrabber.utils.Utils.logCollector;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class ProfileFragment extends Fragment {
@ -74,8 +84,13 @@ public class ProfileFragment extends Fragment {
private PostsAdapter postsAdapter;
private ActionMode actionMode;
private Handler usernameSettingHandler;
private ProfileService profileService;
private FriendshipService friendshipService;
private boolean shouldRefresh = true;
private StoryModel[] storyModels;
private boolean hasNextPage;
private String endCursor;
private AsyncTask<Void, Void, PostModel[]> currentlyExecuting;
;
private final Runnable usernameSettingRunnable = () -> {
final ActionBar actionBar = fragmentActivity.getSupportActionBar();
if (actionBar != null) {
@ -118,11 +133,38 @@ public class ProfileFragment extends Fragment {
}
});
private final FetchListener<PostModel[]> postsFetchListener = new FetchListener<PostModel[]>() {
@Override
public void onResult(final PostModel[] result) {
binding.swipeRefreshLayout.setRefreshing(false);
if (result != null) {
binding.mainPosts.post(() -> binding.mainPosts.setVisibility(View.VISIBLE));
// final int oldSize = mainActivity.allItems.size();
final List<PostModel> postModels = profilePostsViewModel.getList().getValue();
final List<PostModel> finalList = postModels == null || postModels.isEmpty() ? new ArrayList<>() : new ArrayList<>(postModels);
finalList.addAll(Arrays.asList(result));
profilePostsViewModel.getList().postValue(finalList);
PostModel model = null;
if (result.length != 0) {
model = result[result.length - 1];
}
if (model == null) return;
endCursor = model.getEndCursor();
hasNextPage = model.hasNextPage();
model.setPageCursor(false, null);
return;
}
binding.privatePage1.setImageResource(R.drawable.ic_cancel);
binding.privatePage2.setText(R.string.empty_acc);
binding.privatePage.setVisibility(View.VISIBLE);
}
};
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fragmentActivity = (MainActivity) requireActivity();
profileService = ProfileService.getInstance();
friendshipService = FriendshipService.getInstance();
}
@Override
@ -130,6 +172,15 @@ public class ProfileFragment extends Fragment {
final ViewGroup container,
final Bundle savedInstanceState) {
if (root != null) {
if (getArguments() != null) {
final ProfileFragmentArgs fragmentArgs = ProfileFragmentArgs.fromBundle(getArguments());
if (!fragmentArgs.getUsername().equals(username)) {
shouldRefresh = true;
return root;
}
}
setUsernameDelayed();
shouldRefresh = false;
return root;
}
binding = FragmentProfileBinding.inflate(inflater, container, false);
@ -139,7 +190,9 @@ public class ProfileFragment extends Fragment {
@Override
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
if (!shouldRefresh) return;
init();
shouldRefresh = false;
}
@Override
@ -162,12 +215,13 @@ public class ProfileFragment extends Fragment {
setUsernameDelayed();
}
if (!isLoggedIn) {
binding.privatePage1.setImageResource(R.drawable.ic_info);
binding.privatePage1.setImageResource(R.drawable.ic_outline_info_24);
binding.privatePage2.setText(R.string.no_acc);
binding.privatePage.setVisibility(View.VISIBLE);
return;
}
setupPosts();
setupCommonListeners();
fetchProfile();
}
@ -205,18 +259,12 @@ public class ProfileFragment extends Fragment {
private void fetchProfileDetails() {
new ProfileFetcher(username.substring(1), profileModel -> {
this.profileModel = profileModel;
new PostsFetcher(profileModel.getId(),
null,
result -> profilePostsViewModel.getList().postValue(Arrays.asList(result)))
.setUsername(profileModel.getUsername())
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
setProfileDetails();
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private void setProfileDetails() {
setupCommonListeners();
if (profileModel == null) {
binding.swipeRefreshLayout.setRefreshing(false);
Toast.makeText(requireContext(), R.string.error_loading_profile, Toast.LENGTH_SHORT).show();
@ -225,12 +273,18 @@ public class ProfileFragment extends Fragment {
binding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE);
final String profileId = profileModel.getId();
if (settingsHelper.getBoolean(Constants.STORIESIG)) {
new iStoryStatusFetcher(profileId, profileModel.getUsername(), false, false,
(!isLoggedIn && settingsHelper.getBoolean(Constants.STORIESIG)), false,
new iStoryStatusFetcher(
profileId,
profileModel.getUsername(),
false,
false,
(!isLoggedIn && settingsHelper.getBoolean(Constants.STORIESIG)),
false,
result -> {
// mainActivity.storyModels = result;
// if (result != null && result.length > 0)
// binding.mainProfileImage.setStoriesBorder();
storyModels = result;
if (result != null && result.length > 0) {
binding.mainProfileImage.setStoriesBorder();
}
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
new HighlightsFetcher(profileId, (!isLoggedIn && settingsHelper.getBoolean(Constants.STORIESIG)), result -> {
@ -412,9 +466,7 @@ public class ProfileFragment extends Fragment {
} else {
binding.swipeRefreshLayout.setRefreshing(true);
binding.mainPosts.setVisibility(View.VISIBLE);
// currentlyExecuting = new PostsFetcher(profileId, postsFetchListener)
// .setUsername(profileModel.getUsername())
// .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
fetchPosts();
}
} else {
binding.mainFollowers.setClickable(false);
@ -451,14 +503,53 @@ public class ProfileFragment extends Fragment {
new DataBox.FavoriteModel(username, System.currentTimeMillis(),
username.replaceAll("^@", "")));
}
// onRefresh();
fetchProfileDetails();
return;
}
profileService.followProfile(username);
if (profileModel.getFollowing() || profileModel.getRequested()) {
friendshipService.unfollow(
userIdFromCookie,
profileModel.getId(),
Utils.getCsrfTokenFromCookie(cookie),
new ServiceCallback<FriendshipRepositoryChangeResponseRootObject>() {
@Override
public void onSuccess(final FriendshipRepositoryChangeResponseRootObject result) {
Log.d(TAG, "Unfollow success: " + result);
}
@Override
public void onFailure(final Throwable t) {
Log.e(TAG, "Error unfollowing", t);
}
});
} else {
friendshipService.follow(
userIdFromCookie,
profileModel.getId(),
Utils.getCsrfTokenFromCookie(cookie),
new ServiceCallback<FriendshipRepositoryChangeResponseRootObject>() {
@Override
public void onSuccess(final FriendshipRepositoryChangeResponseRootObject result) {
Log.d(TAG, "Follow success: " + result);
}
@Override
public void onFailure(final Throwable t) {
Log.e(TAG, "Error following", t);
}
});
}
});
// binding.btnRestrict.setOnClickListener(profileActionListener);
// binding.btnBlock.setOnClickListener(profileActionListener);
binding.btnRestrict.setOnClickListener(v -> {
if (!isLoggedIn) return;
// restrict
// new ProfileAction().execute("restrict");
});
binding.btnBlock.setOnClickListener(v -> {
if (!isLoggedIn) return;
// new MainHelper.ProfileAction().execute("block");
});
binding.btnSaved.setOnClickListener(v -> startActivity(new Intent(requireContext(), SavedViewer.class)
.putExtra(Constants.EXTRAS_INDEX, "$" + profileModel.getId())
.putExtra(Constants.EXTRAS_USER, "@" + profileModel.getUsername())
@ -516,8 +607,34 @@ public class ProfileFragment extends Fragment {
onBackPressedDispatcher.addCallback(onBackPressedCallback);
return true;
});
binding.mainPosts.setAdapter(postsAdapter);
profilePostsViewModel.getList().observe(fragmentActivity, postsAdapter::submitList);
binding.mainPosts.setAdapter(postsAdapter);
final RecyclerLazyLoader lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
if (!hasNextPage) return;
binding.swipeRefreshLayout.setRefreshing(true);
fetchPosts();
endCursor = null;
});
binding.mainPosts.addOnScrollListener(lazyLoader);
}
private void fetchPosts() {
stopCurrentExecutor();
currentlyExecuting = new PostsFetcher(profileModel.getId(), endCursor, postsFetchListener)
.setUsername(profileModel.getUsername())
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
public 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 boolean checkAndResetAction() {

View File

@ -0,0 +1,44 @@
package awais.instagrabber.fragments.settings;
import android.content.SharedPreferences;
import android.os.Bundle;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import awais.instagrabber.activities.MainActivity;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.LocaleUtils;
public abstract class BasePreferencesFragment extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener {
private boolean shouldRecreate = false;
@Override
public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
final PreferenceManager preferenceManager = getPreferenceManager();
preferenceManager.setSharedPreferencesName("settings");
preferenceManager.getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
final PreferenceScreen screen = preferenceManager.createPreferenceScreen(requireContext());
setupPreferenceScreen(screen);
setPreferenceScreen(screen);
}
abstract void setupPreferenceScreen(PreferenceScreen screen);
protected void shouldRecreate() {
this.shouldRecreate = true;
}
@Override
public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences, final String key) {
if (!shouldRecreate) return;
final MainActivity activity = (MainActivity) getActivity();
if (activity == null) return;
if (key.equals(Constants.APP_LANGUAGE)) {
LocaleUtils.setLocale(activity.getBaseContext());
}
shouldRecreate = false;
activity.recreate();
}
}

View File

@ -0,0 +1,105 @@
package awais.instagrabber.fragments.settings;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import awais.instagrabber.R;
import awais.instagrabber.activities.Login;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class MorePreferencesFragment extends BasePreferencesFragment {
private final String cookie = settingsHelper.getString(Constants.COOKIE);
@Override
void setupPreferenceScreen(final PreferenceScreen screen) {
screen.addPreference(new MoreHeaderPreference(requireContext()));
final PreferenceCategory accountCategory = new PreferenceCategory(requireContext());
accountCategory.setTitle("Account");
accountCategory.setIconSpaceReserved(false);
screen.addPreference(accountCategory);
final boolean isLoggedIn = !Utils.isEmpty(cookie) && Utils.getUserIdFromCookie(cookie) != null;
screen.addPreference(getPreference(isLoggedIn ? R.string.relogin : R.string.login,
isLoggedIn ? R.string.relogin_summary : -1,
-1,
preference -> {
startActivityForResult(new Intent(requireContext(), Login.class), Constants.LOGIN_RESULT_CODE);
return true;
}));
if (isLoggedIn) {
screen.addPreference(getPreference(R.string.logout, -1, preference -> {
Utils.setupCookies("LOGOUT");
shouldRecreate();
Toast.makeText(requireContext(), R.string.logout_success, Toast.LENGTH_SHORT).show();
settingsHelper.putString(Constants.COOKIE, "");
return true;
}));
}
final PreferenceCategory defaultCategory = new PreferenceCategory(requireContext());
screen.addPreference(defaultCategory);
defaultCategory.addPreference(getPreference(R.string.action_notif, R.drawable.ic_not_liked, preference -> false));
defaultCategory.addPreference(getPreference(R.string.action_settings, R.drawable.ic_outline_settings_24, preference -> {
final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToSettingsPreferencesFragment();
NavHostFragment.findNavController(this).navigate(navDirections);
return true;
}));
defaultCategory.addPreference(getPreference(R.string.action_about, R.drawable.ic_outline_info_24, preference -> false));
}
@Override
public void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {
if (resultCode == Constants.LOGIN_RESULT_CODE) {
if (data == null) return;
final String cookie = data.getStringExtra("cookie");
Utils.setupCookies(cookie);
shouldRecreate();
Toast.makeText(requireContext(), R.string.login_success_loading_cookies, Toast.LENGTH_SHORT).show();
settingsHelper.putString(Constants.COOKIE, cookie);
}
}
@NonNull
private Preference getPreference(final int title,
final int icon,
final Preference.OnPreferenceClickListener clickListener) {
return getPreference(title, -1, icon, clickListener);
}
@NonNull
private Preference getPreference(final int title,
final int summary,
final int icon,
final Preference.OnPreferenceClickListener clickListener) {
final Preference preference = new Preference(requireContext());
if (icon <= 0) preference.setIconSpaceReserved(false);
if (icon > 0) preference.setIcon(icon);
preference.setTitle(title);
if (summary > 0) {
preference.setSummary(summary);
}
preference.setOnPreferenceClickListener(clickListener);
return preference;
}
public static class MoreHeaderPreference extends Preference {
public MoreHeaderPreference(final Context context) {
super(context);
setLayoutResource(R.layout.pref_more_header);
setSelectable(false);
}
}
}

View File

@ -0,0 +1,285 @@
package awais.instagrabber.fragments.settings;
import android.content.Context;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatButton;
import androidx.appcompat.widget.AppCompatTextView;
import androidx.preference.DropDownPreference;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import androidx.preference.PreferenceViewHolder;
import androidx.preference.SwitchPreferenceCompat;
import com.google.android.material.switchmaterial.SwitchMaterial;
import java.text.SimpleDateFormat;
import awais.instagrabber.R;
import awais.instagrabber.dialogs.TimeSettingsDialog;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.DirectoryChooser;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Constants.FOLDER_PATH;
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class SettingsPreferencesFragment extends BasePreferencesFragment {
private static final String TAG = "SettingsPrefsFrag";
private static AppCompatTextView customPathTextView;
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public void onPrepareOptionsMenu(@NonNull final Menu menu) {
super.onPrepareOptionsMenu(menu);
final MenuItem item = menu.findItem(R.id.favourites);
item.setVisible(false);
}
@Override
void setupPreferenceScreen(final PreferenceScreen screen) {
screen.addPreference(getLanguagePreference());
screen.addPreference(getThemePreference());
screen.addPreference(getAmoledThemePreference());
screen.addPreference(getDownloadUserFolderPreference());
screen.addPreference(getSaveToCustomFolderPreference());
screen.addPreference(getAutoPlayVideosPreference());
screen.addPreference(getAlwaysMuteVideosPreference());
screen.addPreference(getPostTimePreference());
final PreferenceCategory loggedInUsersPreferenceCategory = new PreferenceCategory(requireContext());
loggedInUsersPreferenceCategory.setIconSpaceReserved(false);
screen.addPreference(loggedInUsersPreferenceCategory);
loggedInUsersPreferenceCategory.setTitle(R.string.login_settings);
loggedInUsersPreferenceCategory.addPreference(getMarkStoriesSeenPreference());
loggedInUsersPreferenceCategory.addPreference(getEnableActivityNotificationsPreference());
final PreferenceCategory anonUsersPreferenceCategory = new PreferenceCategory(requireContext());
anonUsersPreferenceCategory.setIconSpaceReserved(false);
screen.addPreference(anonUsersPreferenceCategory);
anonUsersPreferenceCategory.setTitle(R.string.anonymous_settings);
anonUsersPreferenceCategory.addPreference(getUseInstaDpPreference());
anonUsersPreferenceCategory.addPreference(getUseStoriesIgPreference());
}
@NonNull
private DropDownPreference getLanguagePreference() {
final DropDownPreference preference = new DropDownPreference(requireContext());
preference.setSummaryProvider(ListPreference.SimpleSummaryProvider.getInstance());
final int length = getResources().getStringArray(R.array.languages).length;
final String[] values = new String[length];
for (int i = 0; i < length; i++) {
values[i] = String.valueOf(i);
}
preference.setKey(Constants.APP_LANGUAGE);
preference.setTitle(R.string.select_language);
preference.setEntries(R.array.languages);
preference.setIconSpaceReserved(false);
preference.setEntryValues(values);
preference.setOnPreferenceChangeListener((preference1, newValue) -> {
shouldRecreate();
return true;
});
return preference;
}
@NonNull
private DropDownPreference getThemePreference() {
final DropDownPreference preference = new DropDownPreference(requireContext());
preference.setSummaryProvider(ListPreference.SimpleSummaryProvider.getInstance());
final int length = getResources().getStringArray(R.array.theme_presets).length;
final String[] values = new String[length];
for (int i = 0; i < length; i++) {
values[i] = String.valueOf(i);
}
preference.setKey(Constants.APP_THEME);
preference.setTitle(R.string.theme_settings);
preference.setEntries(R.array.theme_presets);
preference.setIconSpaceReserved(false);
preference.setEntryValues(values);
preference.setOnPreferenceChangeListener((preference1, newValue) -> {
shouldRecreate();
return true;
});
return preference;
}
private SwitchPreferenceCompat getAmoledThemePreference() {
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(requireContext());
preference.setKey(Constants.AMOLED_THEME);
preference.setTitle(R.string.use_amoled_dark_theme);
preference.setIconSpaceReserved(false);
preference.setOnPreferenceChangeListener((preference1, newValue) -> {
final boolean isNight = Utils.isNight(requireContext(), settingsHelper.getThemeCode(true));
if (isNight) shouldRecreate();
return true;
});
return preference;
}
private Preference getDownloadUserFolderPreference() {
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(requireContext());
preference.setKey(Constants.DOWNLOAD_USER_FOLDER);
preference.setTitle("Download to username folder");
preference.setSummary(R.string.download_user_folder);
preference.setIconSpaceReserved(false);
return preference;
}
private Preference getSaveToCustomFolderPreference() {
return new SaveToCustomFolderPreference(requireContext(), (resultCallback) -> {
new DirectoryChooser()
.setInitialDirectory(settingsHelper.getString(FOLDER_PATH))
.setInteractionListener(path -> {
settingsHelper.putString(FOLDER_PATH, path);
resultCallback.onResult(path);
})
.show(getParentFragmentManager(), null);
});
}
private Preference getAutoPlayVideosPreference() {
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(requireContext());
preference.setKey(Constants.AUTOPLAY_VIDEOS);
preference.setTitle(R.string.post_viewer_autoplay_video);
preference.setIconSpaceReserved(false);
return preference;
}
private Preference getAlwaysMuteVideosPreference() {
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(requireContext());
preference.setKey(Constants.MUTED_VIDEOS);
preference.setTitle(R.string.post_viewer_muted_autoplay);
preference.setIconSpaceReserved(false);
return preference;
}
private Preference getMarkStoriesSeenPreference() {
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(requireContext());
preference.setKey(Constants.MARK_AS_SEEN);
preference.setTitle(R.string.mark_as_seen_setting);
preference.setSummary(R.string.mark_as_seen_setting_summary);
preference.setIconSpaceReserved(false);
return preference;
}
private Preference getEnableActivityNotificationsPreference() {
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(requireContext());
preference.setKey(Constants.CHECK_ACTIVITY);
preference.setTitle(R.string.activity_setting);
preference.setIconSpaceReserved(false);
return preference;
}
private Preference getUseInstaDpPreference() {
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(requireContext());
preference.setKey(Constants.INSTADP);
preference.setTitle(R.string.instadp_settings);
preference.setIconSpaceReserved(false);
return preference;
}
private Preference getUseStoriesIgPreference() {
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(requireContext());
preference.setKey(Constants.STORIESIG);
preference.setTitle(R.string.storiesig_settings);
preference.setIconSpaceReserved(false);
return preference;
}
private Preference getPostTimePreference() {
final Preference preference = new Preference(requireContext());
preference.setTitle(R.string.time_settings);
preference.setIconSpaceReserved(false);
preference.setOnPreferenceClickListener(preference1 -> {
new TimeSettingsDialog(settingsHelper.getBoolean(Constants.CUSTOM_DATE_TIME_FORMAT_ENABLED),
settingsHelper.getString(Constants.CUSTOM_DATE_TIME_FORMAT),
settingsHelper.getString(Constants.DATE_TIME_SELECTION),
(isCustomFormat,
formatSelection,
spTimeFormatSelectedItemPosition,
spSeparatorSelectedItemPosition,
spDateFormatSelectedItemPosition,
selectedFormat, currentFormat) -> {
if (isCustomFormat) {
settingsHelper.putString(Constants.CUSTOM_DATE_TIME_FORMAT, formatSelection);
} else {
final String formatSelectionUpdated = spTimeFormatSelectedItemPosition + ";"
+ spSeparatorSelectedItemPosition + ';'
+ spDateFormatSelectedItemPosition; // time;separator;date
settingsHelper.putString(Constants.DATE_TIME_FORMAT, selectedFormat);
settingsHelper.putString(Constants.DATE_TIME_SELECTION, formatSelectionUpdated);
}
settingsHelper.putBoolean(Constants.CUSTOM_DATE_TIME_FORMAT_ENABLED, isCustomFormat);
Utils.datetimeParser = (SimpleDateFormat) currentFormat.clone();
}
).show(getParentFragmentManager(), null);
return true;
});
return preference;
}
public static class SaveToCustomFolderPreference extends Preference {
private final OnSelectFolderButtonClickListener onSelectFolderButtonClickListener;
private String key;
public SaveToCustomFolderPreference(final Context context, final OnSelectFolderButtonClickListener onSelectFolderButtonClickListener) {
super(context);
this.onSelectFolderButtonClickListener = onSelectFolderButtonClickListener;
key = Constants.FOLDER_SAVE_TO;
setLayoutResource(R.layout.pref_custom_folder);
setKey(key);
setTitle(R.string.save_to_folder);
setIconSpaceReserved(false);
}
@Override
public void onBindViewHolder(final PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
final SwitchMaterial cbSaveTo = (SwitchMaterial) holder.findViewById(R.id.cbSaveTo);
final View buttonContainer = holder.findViewById(R.id.button_container);
customPathTextView = (AppCompatTextView) holder.findViewById(R.id.custom_path);
cbSaveTo.setOnCheckedChangeListener((buttonView, isChecked) -> {
settingsHelper.putBoolean(FOLDER_SAVE_TO, isChecked);
buttonContainer.setVisibility(isChecked ? View.VISIBLE : View.GONE);
final String customPath = settingsHelper.getString(FOLDER_PATH);
customPathTextView.setText(customPath);
});
final boolean savedToEnabled = settingsHelper.getBoolean(key);
holder.itemView.setOnClickListener(v -> cbSaveTo.toggle());
cbSaveTo.setChecked(savedToEnabled);
buttonContainer.setVisibility(savedToEnabled ? View.VISIBLE : View.GONE);
final AppCompatButton btnSaveTo = (AppCompatButton) holder.findViewById(R.id.btnSaveTo);
btnSaveTo.setOnClickListener(v -> {
if (onSelectFolderButtonClickListener == null) return;
onSelectFolderButtonClickListener.onClick(result -> {
if (Utils.isEmpty(result)) return;
customPathTextView.setText(result);
});
});
}
public interface ResultCallback {
void onResult(String result);
}
public interface OnSelectFolderButtonClickListener {
void onClick(ResultCallback resultCallback);
}
}
}

View File

@ -0,0 +1,4 @@
package awais.instagrabber.fragments.settings.helpers;
public class AutoSummaryDropDownPreference {
}

View File

@ -0,0 +1,19 @@
package awais.instagrabber.repositories;
import java.util.Map;
import awais.instagrabber.repositories.responses.FriendshipRepositoryChangeResponseRootObject;
import retrofit2.Call;
import retrofit2.http.FieldMap;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.POST;
import retrofit2.http.Path;
public interface FriendshipRepository {
@FormUrlEncoded
@POST("/api/v1/friendships/{action}/{id}/")
Call<FriendshipRepositoryChangeResponseRootObject> change(@Path("action") String action,
@Path("id") String id,
@FieldMap Map<String, String> form);
}

View File

@ -1,4 +0,0 @@
package awais.instagrabber.repositories;
public interface ProfileRepository {
}

View File

@ -0,0 +1,76 @@
package awais.instagrabber.repositories.responses;
public class FriendshipRepositoryChangeResponseFriendshipStatus {
private boolean following;
private boolean followedBy;
private boolean blocking;
private boolean muting;
private boolean isPrivate;
private boolean incomingRequest;
private boolean outgoingRequest;
private boolean isBestie;
public FriendshipRepositoryChangeResponseFriendshipStatus(final boolean following,
final boolean followedBy,
final boolean blocking,
final boolean muting,
final boolean isPrivate,
final boolean incomingRequest,
final boolean outgoingRequest,
final boolean isBestie) {
this.following = following;
this.followedBy = followedBy;
this.blocking = blocking;
this.muting = muting;
this.isPrivate = isPrivate;
this.incomingRequest = incomingRequest;
this.outgoingRequest = outgoingRequest;
this.isBestie = isBestie;
}
public boolean isFollowing() {
return following;
}
public boolean isFollowedBy() {
return followedBy;
}
public boolean isBlocking() {
return blocking;
}
public boolean isMuting() {
return muting;
}
public boolean isPrivate() {
return isPrivate;
}
public boolean isIncomingRequest() {
return incomingRequest;
}
public boolean isOutgoingRequest() {
return outgoingRequest;
}
public boolean isBestie() {
return isBestie;
}
@Override
public String toString() {
return "FriendshipRepositoryChangeResponseFriendshipStatus{" +
"following=" + following +
", followedBy=" + followedBy +
", blocking=" + blocking +
", muting=" + muting +
", isPrivate=" + isPrivate +
", incomingRequest=" + incomingRequest +
", outgoingRequest=" + outgoingRequest +
", isBestie=" + isBestie +
'}';
}
}

View File

@ -0,0 +1,27 @@
package awais.instagrabber.repositories.responses;
public class FriendshipRepositoryChangeResponseRootObject {
private FriendshipRepositoryChangeResponseFriendshipStatus friendshipStatus;
private String status;
public FriendshipRepositoryChangeResponseRootObject(final FriendshipRepositoryChangeResponseFriendshipStatus friendshipStatus, final String status) {
this.friendshipStatus = friendshipStatus;
this.status = status;
}
public FriendshipRepositoryChangeResponseFriendshipStatus getFriendshipStatus() {
return friendshipStatus;
}
public String getStatus() {
return status;
}
@Override
public String toString() {
return "FriendshipRepositoryChangeResponseRootObject{" +
"friendshipStatus=" + friendshipStatus +
", status='" + status + '\'' +
'}';
}
}

View File

@ -2,6 +2,7 @@ package awais.instagrabber.services;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.converter.scalars.ScalarsConverterFactory;
public abstract class BaseService {
@ -17,6 +18,7 @@ public abstract class BaseService {
.build();
builder = new Retrofit.Builder()
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(client);
}
return builder;

View File

@ -0,0 +1,82 @@
package awais.instagrabber.services;
import androidx.annotation.NonNull;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import awais.instagrabber.repositories.FriendshipRepository;
import awais.instagrabber.repositories.responses.FriendshipRepositoryChangeResponseRootObject;
import awais.instagrabber.utils.Utils;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
public class FriendshipService extends BaseService {
private static final String TAG = "ProfileService";
private final FriendshipRepository repository;
private static FriendshipService instance;
private FriendshipService() {
final Retrofit retrofit = getRetrofitBuilder()
.baseUrl("https://i.instagram.com")
.build();
repository = retrofit.create(FriendshipRepository.class);
}
public static FriendshipService getInstance() {
if (instance == null) {
instance = new FriendshipService();
}
return instance;
}
public void follow(final String userId,
final String targetUserId,
final String crsfToken,
final ServiceCallback<FriendshipRepositoryChangeResponseRootObject> callback) {
change("create", userId, targetUserId, crsfToken, callback);
}
public void unfollow(final String userId,
final String targetUserId,
final String crsfToken,
final ServiceCallback<FriendshipRepositoryChangeResponseRootObject> callback) {
change("destroy", userId, targetUserId, crsfToken, callback);
}
private void change(final String action,
final String userId,
final String targetUserId,
final String crsfToken,
final ServiceCallback<FriendshipRepositoryChangeResponseRootObject> callback) {
final Map<String, Object> form = new HashMap<>(5);
form.put("_csrftoken", crsfToken);
form.put("_uid", userId);
form.put("_uuid", UUID.randomUUID().toString());
form.put("user_id", targetUserId);
final Map<String, String> signedForm = Utils.sign(form);
final Call<FriendshipRepositoryChangeResponseRootObject> request = repository.change(action, targetUserId, signedForm);
request.enqueue(new Callback<FriendshipRepositoryChangeResponseRootObject>() {
@Override
public void onResponse(@NonNull final Call<FriendshipRepositoryChangeResponseRootObject> call,
@NonNull final Response<FriendshipRepositoryChangeResponseRootObject> response) {
if (callback != null) {
callback.onSuccess(response.body());
}
}
@Override
public void onFailure(@NonNull final Call<FriendshipRepositoryChangeResponseRootObject> call,
@NonNull final Throwable t) {
if (callback != null) {
callback.onFailure(t);
}
}
});
}
}

View File

@ -1,35 +0,0 @@
package awais.instagrabber.services;
import awais.instagrabber.repositories.ProfileRepository;
import retrofit2.Retrofit;
public class ProfileService extends BaseService {
private static final String TAG = "ProfileService";
private final ProfileRepository repository;
private static ProfileService instance;
private ProfileService() {
final Retrofit retrofit = getRetrofitBuilder()
.baseUrl("https://i.instagram.com")
.build();
repository = retrofit.create(ProfileRepository.class);
}
public static ProfileService getInstance() {
if (instance == null) {
instance = new ProfileService();
}
return instance;
}
public void followProfile(final String username) {
// final String url = "https://www.instagram.com/web/" + (action.equals("followtag") && mainActivity.hashtagModel != null ? "tags/" + (mainActivity.hashtagModel.getFollowing() ? "unfollow/" : "follow/") + mainActivity.hashtagModel.getName() + "/" : (action.equals("restrict") && mainActivity.profileModel != null ? "restrict_action" : "friendships/" + mainActivity.profileModel.getId()) + "/" + (action.equals("follow") ?
// mainActivity.profileModel.getFollowing() || mainActivity.profileModel.getRequested()
// ? "unfollow/" : "follow/" :
// action.equals("restrict") ?
// mainActivity.profileModel.getRestricted() ? "unrestrict/" : "restrict/" :
// mainActivity.profileModel.getBlocked() ? "unblock/" : "block/"));
}
}

View File

@ -64,4 +64,5 @@ public final class Constants {
"\"gyroscope\", \"value\": \"gyroscope_enabled\" } ]";
public static final String SIGNATURE_VERSION = "4";
public static final String SIGNATURE_KEY = "9193488027538fd3450b83b7d05286d4ca9599a0f7eeed90d8c85925698a05dc";
public static final int LOGIN_RESULT_CODE = 5000;
}

View File

@ -217,8 +217,8 @@ public final class ExportImportUtils {
if (settingsHelper != null) {
try {
final JSONObject json = new JSONObject();
json.put(Constants.APP_THEME, settingsHelper.getInteger(Constants.APP_THEME));
json.put(Constants.APP_LANGUAGE, settingsHelper.getInteger(Constants.APP_LANGUAGE));
json.put(Constants.APP_THEME, settingsHelper.getString(Constants.APP_THEME));
json.put(Constants.APP_LANGUAGE, settingsHelper.getString(Constants.APP_LANGUAGE));
String str = settingsHelper.getString(Constants.FOLDER_PATH);
if (!Utils.isEmpty(str)) json.put(Constants.FOLDER_PATH, str);

View File

@ -55,7 +55,7 @@ public final class LocaleUtils {
if (Utils.settingsHelper == null)
Utils.settingsHelper = new SettingsHelper(baseContext);
final int appLanguageIndex = Utils.settingsHelper.getInteger(Constants.APP_LANGUAGE);
final int appLanguageIndex = Integer.parseInt(Utils.settingsHelper.getString(Constants.APP_LANGUAGE));
if (appLanguageIndex == 1) return "en";
if (appLanguageIndex == 2) return "fr";

View File

@ -1,5 +1,6 @@
package awais.instagrabber.utils;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
@ -83,10 +84,18 @@ public final class SettingsHelper {
int themeCode = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM;
if (!fromHelper && sharedPreferences != null) {
themeCode = sharedPreferences.getInt(APP_THEME, themeCode);
if (themeCode == 1) themeCode = AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY;
else if (themeCode == 3) themeCode = AppCompatDelegate.MODE_NIGHT_NO;
else if (themeCode == 0) themeCode = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM;
themeCode = Integer.parseInt(sharedPreferences.getString(APP_THEME, String.valueOf(themeCode)));
switch (themeCode) {
case 1:
themeCode = AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY;
break;
case 3:
themeCode = AppCompatDelegate.MODE_NIGHT_NO;
break;
case 0:
themeCode = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM;
break;
}
}
if (themeCode == AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM && Build.VERSION.SDK_INT < 29)
@ -107,13 +116,13 @@ public final class SettingsHelper {
if (sharedPreferences != null) sharedPreferences.edit().putBoolean(key, val).apply();
}
@StringDef({COOKIE, FOLDER_PATH, DATE_TIME_FORMAT, DATE_TIME_SELECTION, CUSTOM_DATE_TIME_FORMAT, DEVICE_UUID})
@StringDef({APP_LANGUAGE, APP_THEME, COOKIE, FOLDER_PATH, DATE_TIME_FORMAT, DATE_TIME_SELECTION, CUSTOM_DATE_TIME_FORMAT, DEVICE_UUID})
public @interface StringSettings {}
@StringDef({DOWNLOAD_USER_FOLDER, BOTTOM_TOOLBAR, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS,
AUTOLOAD_POSTS, CUSTOM_DATE_TIME_FORMAT_ENABLED, MARK_AS_SEEN, INSTADP, STORIESIG, AMOLED_THEME, CHECK_ACTIVITY})
public @interface BooleanSettings {}
@StringDef({APP_THEME, APP_LANGUAGE, PREV_INSTALL_VERSION})
@StringDef({PREV_INSTALL_VERSION})
public @interface IntegerSettings {}
}

View File

@ -59,6 +59,7 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@ -820,24 +821,26 @@ public final class Utils {
}
AppCompatDelegate.setDefaultNightMode(themeCode);
// use amoled theme only if enabled in settings
if (isAmoledEnabled) {
// check if setting is set to 'Dark'
boolean isNight = themeCode == AppCompatDelegate.MODE_NIGHT_YES;
// if not dark check if themeCode is MODE_NIGHT_FOLLOW_SYSTEM or MODE_NIGHT_AUTO_BATTERY
if (!isNight && (themeCode == AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM || themeCode == AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY)) {
// check if resulting theme would be NIGHT
final int uiMode = context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
isNight = uiMode == Configuration.UI_MODE_NIGHT_YES;
}
if (isNight) {
// set amoled theme
Log.d("InstaGrabber", "settings amoled theme");
context.setTheme(R.style.Theme_Amoled);
return;
}
if (isAmoledEnabled && isNight(context, themeCode)) {
// set amoled theme
Log.d(TAG, "settings amoled theme");
context.setTheme(R.style.Theme_Amoled);
}
}
public static boolean isNight(final Context context, final int themeCode) {
// check if setting is set to 'Dark'
boolean isNight = themeCode == AppCompatDelegate.MODE_NIGHT_YES;
// if not dark check if themeCode is MODE_NIGHT_FOLLOW_SYSTEM or MODE_NIGHT_AUTO_BATTERY
if (!isNight && (themeCode == AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM || themeCode == AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY)) {
// check if resulting theme would be NIGHT
final int uiMode = context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
isNight = uiMode == Configuration.UI_MODE_NIGHT_YES;
}
return isNight;
}
public static void setTooltipText(final View view, @StringRes final int tooltipTextRes) {
if (view != null && tooltipTextRes != 0 && tooltipTextRes != -1) {
final Context context = view.getContext();
@ -1208,6 +1211,20 @@ public final class Utils {
dialog[0] = new AlertDialog.Builder(context).setView(importExportBinding.getRoot()).show();
}
public static Map<String, String> sign(final Map<String, Object> form) {
final String signed = sign(new JSONObject(form).toString());
if (signed == null) {
return null;
}
final String[] parts = signed.split("&");
final Map<String, String> map = new HashMap<>();
for (final String part : parts) {
final String[] partSplit = part.split("=");
map.put(partSplit[0], partSplit[1]);
}
return map;
}
public static String sign(final String message) {
try {
final Mac hasher = Mac.getInstance("HmacSHA256");
@ -1440,4 +1457,11 @@ public final class Utils {
public static int getResultingWidth(final int requiredHeight, final int height, final int width) {
return requiredHeight * width / height;
}
public static String getCsrfTokenFromCookie(final String cookie) {
if (cookie == null) {
return null;
}
return cookie.split("csrftoken=")[1].split(";")[0];
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 451 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 333 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 435 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 239 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 311 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 597 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 811 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 570 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 815 B

View File

@ -1,10 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:alpha="0.8"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M11,7h2v2h-2zM11,11h2v6h-2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98 0,-0.34 -0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.09,-0.16 -0.26,-0.25 -0.44,-0.25 -0.06,0 -0.12,0.01 -0.17,0.03l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.06,-0.02 -0.12,-0.03 -0.18,-0.03 -0.17,0 -0.34,0.09 -0.43,0.25l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98 0,0.33 0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.09,0.16 0.26,0.25 0.44,0.25 0.06,0 0.12,-0.01 0.17,-0.03l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.06,0.02 0.12,0.03 0.18,0.03 0.17,0 0.34,-0.09 0.43,-0.25l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM17.45,11.27c0.04,0.31 0.05,0.52 0.05,0.73 0,0.21 -0.02,0.43 -0.05,0.73l-0.14,1.13 0.89,0.7 1.08,0.84 -0.7,1.21 -1.27,-0.51 -1.04,-0.42 -0.9,0.68c-0.43,0.32 -0.84,0.56 -1.25,0.73l-1.06,0.43 -0.16,1.13 -0.2,1.35h-1.4l-0.19,-1.35 -0.16,-1.13 -1.06,-0.43c-0.43,-0.18 -0.83,-0.41 -1.23,-0.71l-0.91,-0.7 -1.06,0.43 -1.27,0.51 -0.7,-1.21 1.08,-0.84 0.89,-0.7 -0.14,-1.13c-0.03,-0.31 -0.05,-0.54 -0.05,-0.74s0.02,-0.43 0.05,-0.73l0.14,-1.13 -0.89,-0.7 -1.08,-0.84 0.7,-1.21 1.27,0.51 1.04,0.42 0.9,-0.68c0.43,-0.32 0.84,-0.56 1.25,-0.73l1.06,-0.43 0.16,-1.13 0.2,-1.35h1.39l0.19,1.35 0.16,1.13 1.06,0.43c0.43,0.18 0.83,0.41 1.23,0.71l0.91,0.7 1.06,-0.43 1.27,-0.51 0.7,1.21 -1.07,0.85 -0.89,0.7 0.14,1.13zM12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zM12,14c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
</vector>

View File

@ -63,7 +63,7 @@
android:paddingEnd="8dp"
android:paddingRight="8dp"
android:paddingBottom="4dp"
app:srcCompat="@drawable/ic_info" />
app:srcCompat="@drawable/ic_outline_info_24" />
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>

View File

@ -1,13 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="4dp"
android:paddingLeft="4dp"
android:paddingTop="12dp"
android:paddingEnd="4dp"
android:paddingRight="4dp">
android:padding="16dp">
<LinearLayout
android:layout_width="match_parent"
@ -25,9 +22,8 @@
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/time_settings_title_custom" />
<CheckBox
@ -40,7 +36,8 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:orientation="horizontal"
android:visibility="gone">
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/etCustomFormat"
@ -55,7 +52,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:src="@android:drawable/ic_menu_info_details" />
app:srcCompat="@drawable/ic_outline_info_24" />
</LinearLayout>
<FrameLayout
@ -68,68 +65,6 @@
</FrameLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:baselineAligned="false"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.84"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/time_settings_title_time_format" />
<androidx.appcompat.widget.AppCompatSpinner
android:id="@+id/spTimeFormat"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="@array/time_presets" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.7"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/time_settings_title_separator" />
<androidx.appcompat.widget.AppCompatSpinner
android:id="@+id/spSeparator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="@array/separator_presets" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/time_settings_title_date_format" />
<androidx.appcompat.widget.AppCompatSpinner
android:id="@+id/spDateFormat"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="@array/date_presets" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -145,18 +80,75 @@
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_weight="1"
android:text="@string/time_settings_title_preview" />
android:text="@string/time_settings_title_time_format" />
<CheckBox
android:id="@+id/cbSwapTimeDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/time_settings_swap_time" />
<androidx.appcompat.widget.AppCompatSpinner
android:id="@+id/spTimeFormat"
android:layout_width="0dp"
android:layout_height="36dp"
android:layout_weight="1"
android:entries="@array/time_presets" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/time_settings_title_separator" />
<androidx.appcompat.widget.AppCompatSpinner
android:id="@+id/spSeparator"
android:layout_width="0dp"
android:layout_height="36dp"
android:layout_weight="1"
android:entries="@array/separator_presets" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/time_settings_title_date_format" />
<androidx.appcompat.widget.AppCompatSpinner
android:id="@+id/spDateFormat"
android:layout_width="0dp"
android:layout_height="36dp"
android:layout_weight="1"
android:entries="@array/date_presets" />
</LinearLayout>
</LinearLayout>
<CheckBox
android:id="@+id/cbSwapTimeDate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/time_settings_swap_time" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:baselineAligned="false"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:text="@string/time_settings_title_preview" />
<TextView
android:id="@+id/timePreview"
android:layout_width="match_parent"

View File

@ -16,6 +16,6 @@
android:id="@+id/inbox_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/layout_include_simple_item" />
tools:listitem="@layout/layout_dm_inbox_item" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</awais.instagrabber.customviews.helpers.NestedCoordinatorLayout>

View File

@ -9,7 +9,7 @@
<include
android:id="@+id/container"
layout="@layout/layout_include_simple_item" />
layout="@layout/layout_dm_inbox_item" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvChildComments"

View File

@ -13,7 +13,7 @@
android:layout_width="match_parent"
android:layout_height="1dip"
android:layout_alignParentTop="true"
android:background="?android:attr/dividerVertical" />
android:background="@android:color/darker_gray" />
<View
android:id="@+id/horizontalDivider"
@ -23,9 +23,9 @@
android:layout_centerHorizontal="true"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:background="?android:attr/dividerVertical" />
android:background="@android:color/darker_gray" />
<Button
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnCancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -38,7 +38,7 @@
android:background="?android:selectableItemBackground"
android:text="@string/cancel" />
<Button
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnConfirm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -103,7 +103,7 @@
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_below="@id/btnNavUp"
android:background="#33B5E5" />
android:background="@android:color/darker_gray" />
</RelativeLayout>
<androidx.recyclerview.widget.RecyclerView

View File

@ -0,0 +1,121 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:orientation="horizontal">
<FrameLayout
android:layout_width="@dimen/simple_item_picture_size"
android:layout_height="@dimen/simple_item_picture_size"
android:gravity="center"
android:padding="4dp">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/ivProfilePic"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:roundAsCircle="true" />
<LinearLayout
android:id="@+id/multi_pic_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<com.facebook.drawee.view.SimpleDraweeView
android:layout_width="@dimen/simple_item_picture_size_half"
android:layout_height="match_parent"
app:roundAsCircle="true" />
<LinearLayout
android:layout_width="@dimen/simple_item_picture_size_half"
android:layout_height="match_parent"
android:orientation="vertical">
<com.facebook.drawee.view.SimpleDraweeView
android:layout_width="@dimen/simple_item_picture_size_half"
android:layout_height="@dimen/simple_item_picture_size_half"
app:roundAsCircle="true" />
<com.facebook.drawee.view.SimpleDraweeView
android:layout_width="@dimen/simple_item_picture_size_half"
android:layout_height="@dimen/simple_item_picture_size_half"
app:roundAsCircle="true" />
</LinearLayout>
</LinearLayout>
</FrameLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="8dp"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvUsername"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:ellipsize="marquee"
android:gravity="center_vertical"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="?android:textColorPrimary"
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@id/tvDate"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="username" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvDate"
android:layout_width="0dp"
android:layout_height="0dp"
android:ellipsize="marquee"
android:gravity="center_vertical|end"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
android:textStyle="italic"
app:layout_constraintBottom_toTopOf="@id/comment_container"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/tvUsername"
app:layout_constraintTop_toTopOf="parent"
tools:text="long date................................" />
<LinearLayout
android:id="@+id/comment_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvUsername">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/notTextType"
android:layout_width="4dp"
android:layout_height="4dp"
android:layout_gravity="center_vertical"
android:layout_marginEnd="4dp"
android:layout_marginRight="4dp"
android:visibility="gone"
app:srcCompat="@android:drawable/ic_notification_overlay"
app:tint="@color/feed_text_primary_color" />
<awais.instagrabber.customviews.RamboTextView
android:id="@+id/tvComment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:autoLink="web|email"
android:ellipsize="end"
android:linksClickable="true"
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
tools:text="comment" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>

View File

@ -1,138 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:orientation="horizontal"
android:paddingStart="0dp"
android:paddingLeft="0dp"
android:paddingEnd="4dp"
android:paddingRight="4dp">
<FrameLayout
android:layout_width="@dimen/simple_item_picture_size"
android:layout_height="@dimen/simple_item_picture_size"
android:gravity="center">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/ivProfilePic"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<LinearLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="@dimen/simple_item_picture_size_half"
android:layout_height="match_parent" />
<LinearLayout
android:layout_width="@dimen/simple_item_picture_size_half"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="@dimen/simple_item_picture_size_half"
android:layout_height="@dimen/simple_item_picture_size_half" />
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="@dimen/simple_item_picture_size_half"
android:layout_height="@dimen/simple_item_picture_size_half" />
</LinearLayout>
</LinearLayout>
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvUsername"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_weight="1.0"
android:ellipsize="marquee"
android:paddingStart="8dp"
android:paddingLeft="8dp"
android:paddingTop="4dp"
android:paddingEnd="4dp"
android:paddingRight="4dp"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="?android:textColorPrimary"
android:textStyle="bold" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingLeft="8dp"
android:paddingTop="4dp"
android:paddingRight="8dp"
android:paddingBottom="16dp">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/notTextType"
android:layout_width="4dp"
android:layout_height="4dp"
android:layout_gravity="center_vertical"
android:layout_marginEnd="4dp"
android:layout_marginRight="4dp"
android:visibility="gone"
app:srcCompat="@android:drawable/ic_notification_overlay"
app:tint="@color/feed_text_primary_color" />
<awais.instagrabber.customviews.RamboTextView
android:id="@+id/tvComment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:autoLink="web|email"
android:ellipsize="end"
android:linksClickable="true"
android:textAppearance="@style/TextAppearance.AppCompat" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:weightSum="3">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvLikes"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="marquee"
android:paddingStart="4dp"
android:paddingLeft="4dp"
android:paddingTop="4dp"
android:paddingEnd="8dp"
android:paddingRight="8dp"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textSize="14sp" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvDate"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:layout_gravity="start"
android:ellipsize="marquee"
android:paddingStart="4dp"
android:paddingLeft="4dp"
android:paddingTop="4dp"
android:paddingEnd="8dp"
android:paddingRight="8dp"
android:singleLine="true"
android:textStyle="italic"
android:gravity="right"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeight"
android:orientation="vertical"
android:paddingEnd="?android:attr/scrollbarSize"
android:paddingRight="?android:attr/scrollbarSize">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="false"
android:gravity="center_vertical">
<RelativeLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dip"
android:layout_marginLeft="15dip"
android:layout_marginTop="6dip"
android:layout_marginEnd="6dip"
android:layout_marginRight="6dip"
android:layout_marginBottom="6dip"
android:layout_weight="1">
<TextView
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceListItem"
android:textColor="?android:attr/textColorPrimary"
tools:text="Test" />
<TextView
android:id="@android:id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@android:id/title"
android:layout_alignStart="@android:id/title"
android:layout_alignLeft="@android:id/title"
android:maxLines="4"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
android:textColor="?android:attr/textColorSecondary"
tools:text="summary" />
</RelativeLayout>
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/cbSaveTo"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:contentDescription="@string/save_to_folder"
android:paddingEnd="10dp"
android:paddingRight="10dp" />
</LinearLayout>
<LinearLayout
android:id="@+id/button_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="15dip"
android:layout_marginLeft="15dip"
android:gravity="center_vertical"
android:orientation="horizontal"
android:visibility="gone">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnSaveTo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/select_folder" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/custom_path"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
android:textColor="?android:attr/textColorSecondary"
tools:text="test path" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="match_parent"
android:layout_height="200dp"
android:scaleType="fitCenter"
app:srcCompat="@mipmap/ic_launcher" />
</LinearLayout>

View File

@ -24,7 +24,7 @@
<!-- android:title="@string/title_favorites"/>-->
<item
android:id="@+id/moreFragment"
android:id="@+id/more_nav_graph"
android:icon="@drawable/ic_more_horiz_24"
android:title="@string/more" />
</menu>

View File

@ -28,6 +28,9 @@
<action
android:id="@+id/action_dMThreadFragment_to_dMSettingsFragment"
app:destination="@id/directMessagesSettingsFragment" />
<action
android:id="@+id/action_directMessagesThreadFragment_to_profileFragment"
app:destination="@id/profileFragment" />
</fragment>
<fragment
android:id="@+id/directMessagesSettingsFragment"
@ -41,4 +44,14 @@
android:name="title"
app:argType="string" />
</fragment>
<fragment
android:id="@+id/profileFragment"
android:name="awais.instagrabber.fragments.main.ProfileFragment"
android:label="ProfileFragment"
tools:layout="@layout/fragment_profile">
<argument
android:name="username"
app:argType="string"
app:nullable="false" />
</fragment>
</navigation>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/more_nav_graph"
app:startDestination="@id/morePreferencesFragment">
<fragment
android:id="@+id/morePreferencesFragment"
android:name="awais.instagrabber.fragments.settings.MorePreferencesFragment"
android:label="More">
<action
android:id="@+id/action_morePreferencesFragment_to_settingsPreferencesFragment"
app:destination="@id/settingsPreferencesFragment" />
</fragment>
<fragment
android:id="@+id/settingsPreferencesFragment"
android:name="awais.instagrabber.fragments.settings.SettingsPreferencesFragment"
android:label="@string/action_settings" />
</navigation>

View File

@ -13,12 +13,31 @@
<item>Turkish [Thanks to @faydin90 (Telegram)]</item>
<item>Brazilian Portuguese [Thanks to @wagnim (GitHub)]</item>
</string-array>
<string-array name="languages_values">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>6</item>
<item>7</item>
<item>8</item>
<item>9</item>
<item>10</item>
</string-array>
<string-array name="theme_presets">
<item>Auto / Follow System</item>
<item>Auto / Follow Battery</item>
<item>Dark</item>
<item>Light</item>
</string-array>
<string-array name="theme_presets_values">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
</string-array>
<string-array name="separator_presets">
<item>None</item>
<item>\@</item>

View File

@ -12,8 +12,8 @@
<dimen name="story_item_height">80dp</dimen>
<dimen name="story_item_width">45dp</dimen>
<dimen name="simple_item_picture_size">@dimen/story_item_height</dimen>
<dimen name="simple_item_picture_size_half">@dimen/story_item_width</dimen>
<dimen name="simple_item_picture_size">80dp</dimen>
<dimen name="simple_item_picture_size_half">40dp</dimen>
<dimen name="message_item_size">@dimen/simple_item_picture_size</dimen>
<dimen name="message_item_profile_size">@dimen/feed_profile_size</dimen>

View File

@ -36,7 +36,8 @@
<string name="bottom_toolbar">Show toolbar at bottom</string>
<string name="download_user_folder">Download posts to username folder in Downloads</string>
<string name="autoload_posts">Auto-load all posts from user</string>
<string name="mark_as_seen_setting">Mark stories as seen after viewing\n(Story author will know you viewed it)</string>
<string name="mark_as_seen_setting">Mark stories as seen after viewing</string>
<string name="mark_as_seen_setting_summary">Story author will know you viewed it</string>
<string name="activity_setting">Enable activity notifications</string>
<string name="error_loading_profile">Error loading profile!\nTry logging in and search again.</string>
<string name="error_creating_folders">Error creating Download folder(s).</string>
@ -129,12 +130,12 @@
<string name="refresh">Refresh</string>
<string name="get_cookies">Get cookies</string>
<string name="desktop_2fa">Desktop Mode</string>
<string name="time_settings_title_custom">Custom Format</string>
<string name="time_settings_title_custom">Use custom format</string>
<string name="time_settings_title_separator">Separator</string>
<string name="time_settings_title_time_format">Time Format</string>
<string name="time_settings_title_date_format">Date Format</string>
<string name="time_settings_title_preview">Preview</string>
<string name="time_settings_swap_time">Swap Time and\nDate positions</string>
<string name="time_settings_swap_time">Swap Time and Date positions</string>
<string name="quick_access_info_dialog">Favorites panel is for adding your favorite hashtags and/or usernames.\n\nAnd the Quick Access panel is for quickly switching between accounts.\n\nNote 1: Make sure to Login into each account [Settings &gt; Login] to add account to the list!\n\nNote 2: Log out of the current account and then log into the other account.</string>
<string name="quick_access_cannot_delete_curr">Cannot delete currently in use account</string>
<string name="quick_access_confirm_delete">Are you sure you want to delete %s?</string>
@ -239,4 +240,7 @@
<string name="more">More</string>
<string name="title_dm">DM</string>
<string name="number_selected">%d selected</string>
<string name="relogin">Relogin</string>
<string name="relogin_summary">Refresh your cookies if facing any issues</string>
<string name="logout_success">Successfully logged out!</string>
</resources>