This commit is contained in:
Austin Huang 2020-09-02 13:31:09 -04:00
commit b62dc66032
No known key found for this signature in database
GPG Key ID: 84C23AA04587A91F
29 changed files with 409 additions and 94 deletions

View File

@ -10,8 +10,8 @@ android {
minSdkVersion 16
targetSdkVersion 29
versionCode 47
versionName '18.1'
versionCode 48
versionName '18.2'
multiDexEnabled true

View File

@ -1414,6 +1414,7 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
? mainActivity.locationModel.getName()
: mainActivity.userQuery.replaceAll("^@", "")));
onRefresh();
<<<<<<< HEAD
} else if (v == mainActivity.mainBinding.profileView.btnFollow) {
if (mainActivity.profileModel.isPrivate() && mainActivity.profileModel.getFollowing()) {
new AlertDialog.Builder(main)
@ -1425,6 +1426,25 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
}
else new ProfileAction().execute("follow");
} else if (v == mainActivity.mainBinding.profileView.btnRestrict && isLoggedIn) {
||||||| merged common ancestors
} else if (v == main.mainBinding.profileView.btnFollow) {
new ProfileAction().execute("follow");
} else if (v == main.mainBinding.profileView.btnRestrict && isLoggedIn) {
=======
} else if (v == main.mainBinding.profileView.btnFollow) {
if (main.profileModel.isPrivate() && main.profileModel.getFollowing()) {
new AlertDialog.Builder(main)
.setTitle(R.string.priv_acc)
.setMessage(R.string.priv_acc_confirm)
.setNegativeButton(R.string.no, null)
.setPositiveButton(R.string.yes, (dialog, which) -> new ProfileAction().execute("follow"))
.show();
}
else {
new ProfileAction().execute("follow");
}
} else if (v == main.mainBinding.profileView.btnRestrict && isLoggedIn) {
>>>>>>> 44db2db57f4461bbffa160e26cd734fb07f3b930
new ProfileAction().execute("restrict");
} else if (v == mainActivity.mainBinding.profileView.btnSaved && !isSelf) {
new ProfileAction().execute("block");
@ -1455,6 +1475,7 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
protected Void doInBackground(String... rawAction) {
action = rawAction[0];
<<<<<<< HEAD
final String url = "https://www.instagram.com/web/" + (action.equals("followtag") && mainActivity.hashtagModel != null
? "tags/" + (mainActivity.hashtagModel.getFollowing()
? "unfollow/"
@ -1480,6 +1501,31 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
.getBlocked()
? "unblock/"
: "block/"));
||||||| merged common ancestors
final String url = "https://www.instagram.com/web/"+
((action == "followtag" && main.hashtagModel != null) ? ("tags/"+
(main.hashtagModel.getFollowing() == true ? "unfollow/" : "follow/")+main.hashtagModel.getName()+"/") : (
((action == "restrict" && main.profileModel != null) ? "restrict_action" : ("friendships/"+main.profileModel.getId()))+"/"+
((action == "follow" && main.profileModel != null) ?
((main.profileModel.getFollowing() == true ||
(main.profileModel.getFollowing() == false && main.profileModel.getRequested() == true))
? "unfollow/" : "follow/") :
((action == "restrict" && main.profileModel != null) ?
(main.profileModel.getRestricted() == true ? "unrestrict/" : "restrict/") :
(main.profileModel.getBlocked() == true ? "unblock/" : "block/")))));
=======
final String url = "https://www.instagram.com/web/"+
((action == "followtag" && main.hashtagModel != null) ? ("tags/"+
(main.hashtagModel.getFollowing() ? "unfollow/" : "follow/")+main.hashtagModel.getName()+"/") : (
((action == "restrict" && main.profileModel != null) ? "restrict_action" : ("friendships/"+main.profileModel.getId()))+"/"+
((action == "follow" && main.profileModel != null) ?
((main.profileModel.getFollowing() ||
(main.profileModel.getFollowing() == false && main.profileModel.getRequested() == true))
? "unfollow/" : "follow/") :
((action == "restrict" && main.profileModel != null) ?
(main.profileModel.getRestricted() ? "unrestrict/" : "restrict/") :
(main.profileModel.getBlocked() ? "unblock/" : "block/")))));
>>>>>>> 44db2db57f4461bbffa160e26cd734fb07f3b930
try {
final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST");
@ -1515,4 +1561,4 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
}
}
}
}
}

View File

@ -18,11 +18,13 @@ import androidx.navigation.ui.NavigationUI;
import awais.instagrabber.R;
import awais.instagrabber.databinding.ActivityDirectMessagesBinding;
import awais.instagrabber.fragments.directmessages.DirectMessageThreadFragmentArgs;
import awais.instagrabber.utils.Constants;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class DirectMessagesActivity extends BaseLanguageActivity implements NavController.OnDestinationChangedListener {
private TextView toolbarTitle;
private AppCompatImageView infoButton;
private AppCompatImageView dmInfo, dmSeen;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -36,7 +38,8 @@ public class DirectMessagesActivity extends BaseLanguageActivity implements NavC
final Toolbar toolbar = binding.toolbar;
setSupportActionBar(toolbar);
infoButton = binding.dmInfo;
dmInfo = binding.dmInfo;
dmSeen = binding.dmSeen;
final NavController navController = Navigation.findNavController(this, R.id.direct_messages_nav_host_fragment);
navController.addOnDestinationChangedListener(this);
@ -51,7 +54,8 @@ public class DirectMessagesActivity extends BaseLanguageActivity implements NavC
switch (destination.getId()) {
case R.id.directMessagesInboxFragment:
setToolbarTitle(R.string.action_dms);
infoButton.setVisibility(View.GONE);
dmInfo.setVisibility(View.GONE);
dmSeen.setVisibility(View.GONE);
return;
case R.id.directMessagesThreadFragment:
if (arguments == null) {
@ -59,14 +63,16 @@ public class DirectMessagesActivity extends BaseLanguageActivity implements NavC
}
final String title = DirectMessageThreadFragmentArgs.fromBundle(arguments).getTitle();
setToolbarTitle(title);
infoButton.setVisibility(View.VISIBLE);
dmInfo.setVisibility(View.VISIBLE);
dmSeen.setVisibility(settingsHelper.getBoolean(Constants.DM_MARK_AS_SEEN) ? View.GONE : View.VISIBLE);
return;
case R.id.directMessagesSettingsFragment:
if (arguments == null) {
return;
}
setToolbarTitle(R.string.action_settings);
infoButton.setVisibility(View.GONE);
dmInfo.setVisibility(View.GONE);
dmSeen.setVisibility(View.GONE);
return;
}
}

View File

@ -129,7 +129,7 @@ public final class MainActivityBackup extends BaseLanguageActivity {
mainBinding = ActivityMainbackupBinding.inflate(getLayoutInflater());
setContentView(mainBinding.getRoot());
FlavorTown.updateCheck(this);
if (settingsHelper.getBoolean(Constants.CHECK_UPDATES)) FlavorTown.updateCheck(this);
FlavorTown.changelogCheck(this);
cookie = settingsHelper.getString(Constants.COOKIE);

View File

@ -17,6 +17,7 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import java.io.DataOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
@ -169,8 +170,16 @@ public final class NotificationsViewer extends BaseLanguageActivity implements S
urlConnection.setRequestProperty("User-Agent", Constants.USER_AGENT);
urlConnection.setRequestProperty("x-csrftoken",
Utils.settingsHelper.getString(Constants.COOKIE).split("csrftoken=")[1].split(";")[0]);
final String urlParameters = "timestamp="+(System.currentTimeMillis()/1000);
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
urlConnection.setRequestProperty("Content-Length", "" +
urlParameters.getBytes().length);
urlConnection.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
urlConnection.connect();
urlConnection.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", "seen: " + ex);
}

View File

@ -103,9 +103,6 @@ public final class ProfilePicViewer extends BaseLanguageActivity {
errorHandled = true;
new ProfilePictureFetcher(username, id, fetchListener, profilePicUrl, (hashtagModel != null || locationModel != null))
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else {
glideRequestManager.load(profilePicUrl).into(profileBinding.imageViewer);
showImageInfo();
}
profileBinding.progressView.setVisibility(View.GONE);
return false;
@ -153,7 +150,7 @@ public final class ProfilePicViewer extends BaseLanguageActivity {
profileBinding.imageInfo.setVisibility(View.VISIBLE);
}
}
}).into(profileBinding.imageViewer);
}).error(glideRequestManager.load(profilePicUrl)).into(profileBinding.imageViewer);
};
new ProfilePictureFetcher(username, id, fetchListener, profilePicUrl, (hashtagModel != null || locationModel != null))

View File

@ -10,17 +10,22 @@ import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.adapters.viewholder.FollowsViewHolder;
import awais.instagrabber.models.ProfileModel;
public final class DirectMessageMembersAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final ProfileModel[] profileModels;
private final List<Long> admins;
private final View.OnClickListener onClickListener;
private final LayoutInflater layoutInflater;
public DirectMessageMembersAdapter(final ProfileModel[] profileModels, final Context context, final View.OnClickListener onClickListener) {
public DirectMessageMembersAdapter(final ProfileModel[] profileModels, final List<Long> admins,
final Context context, final View.OnClickListener onClickListener) {
this.profileModels = profileModels;
this.admins = admins;
this.layoutInflater = LayoutInflater.from(context);
this.onClickListener = onClickListener;
}
@ -44,6 +49,9 @@ public final class DirectMessageMembersAdapter extends RecyclerView.Adapter<Recy
followHolder.tvUsername.setText(model.getUsername());
followHolder.tvFullName.setText(model.getName());
if (admins != null && admins.contains(Long.parseLong(model.getId())))
followHolder.isAdmin.setVisibility(View.VISIBLE);
Glide.with(layoutInflater.getContext()).load(model.getSdProfilePic()).into(followHolder.profileImage);
}
}

View File

@ -101,5 +101,6 @@ public final class DirectMessageInboxItemViewHolder extends RecyclerView.ViewHol
}
binding.tvComment.setText(HtmlCompat.fromHtml(messageText.toString(), HtmlCompat.FROM_HTML_MODE_COMPACT));
binding.tvDate.setText(lastItemModel.getDateTime());
binding.unread.setVisibility(model.getUnreadCount() > 0L ? View.VISIBLE : View.GONE);
}
}

View File

@ -10,7 +10,7 @@ import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.R;
public final class FollowsViewHolder extends RecyclerView.ViewHolder {
public final ImageView profileImage;
public final ImageView profileImage, isAdmin;
public final TextView tvFullName, tvUsername;
public FollowsViewHolder(@NonNull final View itemView) {
@ -18,5 +18,6 @@ public final class FollowsViewHolder extends RecyclerView.ViewHolder {
profileImage = itemView.findViewById(R.id.ivProfilePic);
tvFullName = itemView.findViewById(R.id.tvFullName);
tvUsername = itemView.findViewById(R.id.tvUsername);
isAdmin = itemView.findViewById(R.id.isAdmin);
}
}

View File

@ -86,6 +86,7 @@ public final class ProfilePictureFetcher extends AsyncTask<Void, Void, String> {
}
}
}
if (Utils.isEmpty(out)) out = picUrl;
} catch (final Exception e) {
if (logCollector != null)
logCollector.appendException(e, LogCollector.LogFile.ASYNC_PROFILE_PICTURE_FETCHER, "doInBackground");

View File

@ -161,7 +161,7 @@ public class DirectThreadBroadcaster extends AsyncTask<DirectThreadBroadcaster.B
super(ItemType.TEXT);
this.text = URLEncoder.encode(text, "UTF-8")
.replaceAll("\\+", "%20").replaceAll("%21", "!").replaceAll("%27", "'")
.replaceAll("%28", "(").replaceAll("%29", ")").replaceAll("%7E", "~");
.replaceAll("%28", "(").replaceAll("%29", ")").replaceAll("%7E", "~").replaceAll("%0A", "\n");
}
@Override
@ -197,7 +197,7 @@ public class DirectThreadBroadcaster extends AsyncTask<DirectThreadBroadcaster.B
super(ItemType.REELSHARE);
this.text = URLEncoder.encode(text, "UTF-8")
.replaceAll("\\+", "%20").replaceAll("%21", "!").replaceAll("%27", "'")
.replaceAll("%28", "(").replaceAll("%29", ")").replaceAll("%7E", "~");
.replaceAll("%28", "(").replaceAll("%29", ")").replaceAll("%7E", "~").replaceAll("%0A", "\n");
this.mediaId = mediaId;
this.reelId = reelId; // or user id, usually same
}

View File

@ -42,7 +42,9 @@ import static awais.instagrabber.utils.Constants.AUTOLOAD_POSTS;
import static awais.instagrabber.utils.Constants.AUTOPLAY_VIDEOS;
import static awais.instagrabber.utils.Constants.BOTTOM_TOOLBAR;
import static awais.instagrabber.utils.Constants.CHECK_ACTIVITY;
import static awais.instagrabber.utils.Constants.CHECK_UPDATES;
import static awais.instagrabber.utils.Constants.COOKIE;
import static awais.instagrabber.utils.Constants.DM_MARK_AS_SEEN;
import static awais.instagrabber.utils.Constants.DOWNLOAD_USER_FOLDER;
import static awais.instagrabber.utils.Constants.FOLDER_PATH;
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
@ -137,22 +139,26 @@ public final class SettingsDialog extends BottomSheetDialogFragment implements V
final AppCompatCheckBox cbAutoplayVideos = contentView.findViewById(R.id.cbAutoplayVideos);
final AppCompatCheckBox cbDownloadUsername = contentView.findViewById(R.id.cbDownloadUsername);
final AppCompatCheckBox cbMarkAsSeen = contentView.findViewById(R.id.cbMarkAsSeen);
final AppCompatCheckBox cbMarkDmAsSeen = contentView.findViewById(R.id.cbMarkDmAsSeen);
final AppCompatCheckBox cbActivity = contentView.findViewById(R.id.cbActivity);
final AppCompatCheckBox cbInstadp = contentView.findViewById(R.id.cbInstadp);
final AppCompatCheckBox cbStoriesig = contentView.findViewById(R.id.cbStoriesig);
final AppCompatCheckBox cbAmoledTheme = contentView.findViewById(R.id.cbAmoledTheme);
final AppCompatCheckBox cbUpdates = contentView.findViewById(R.id.cbUpdates);
cbSaveTo.setChecked(settingsHelper.getBoolean(FOLDER_SAVE_TO));
cbMuteVideos.setChecked(settingsHelper.getBoolean(MUTED_VIDEOS));
cbBottomToolbar.setChecked(settingsHelper.getBoolean(BOTTOM_TOOLBAR));
cbAutoplayVideos.setChecked(settingsHelper.getBoolean(AUTOPLAY_VIDEOS));
cbMarkAsSeen.setChecked(settingsHelper.getBoolean(MARK_AS_SEEN));
cbMarkDmAsSeen.setChecked(settingsHelper.getBoolean(DM_MARK_AS_SEEN));
cbInstadp.setChecked(settingsHelper.getBoolean(INSTADP));
cbStoriesig.setChecked(settingsHelper.getBoolean(STORIESIG));
cbAmoledTheme.setChecked(settingsHelper.getBoolean(AMOLED_THEME));
cbActivity.setChecked(settingsHelper.getBoolean(CHECK_ACTIVITY));
cbAutoloadPosts.setChecked(settingsHelper.getBoolean(AUTOLOAD_POSTS));
cbDownloadUsername.setChecked(settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER));
cbUpdates.setChecked(settingsHelper.getBoolean(CHECK_UPDATES));
setupListener(cbSaveTo);
setupListener(cbMuteVideos);
@ -161,10 +167,12 @@ public final class SettingsDialog extends BottomSheetDialogFragment implements V
setupListener(cbAutoplayVideos);
setupListener(cbDownloadUsername);
setupListener(cbMarkAsSeen);
setupListener(cbMarkDmAsSeen);
setupListener(cbInstadp);
setupListener(cbStoriesig);
setupListener(cbAmoledTheme);
setupListener(cbActivity);
setupListener(cbUpdates);
btnSaveTo.setEnabled(cbSaveTo.isChecked());
@ -254,10 +262,12 @@ public final class SettingsDialog extends BottomSheetDialogFragment implements V
else if (id == R.id.cbMuteVideos) settingsHelper.putBoolean(MUTED_VIDEOS, checked);
else if (id == R.id.cbAutoloadPosts) settingsHelper.putBoolean(AUTOLOAD_POSTS, checked);
else if (id == R.id.cbMarkAsSeen) settingsHelper.putBoolean(MARK_AS_SEEN, checked);
else if (id == R.id.cbMarkDmAsSeen) settingsHelper.putBoolean(DM_MARK_AS_SEEN, checked);
else if (id == R.id.cbInstadp) settingsHelper.putBoolean(INSTADP, checked);
else if (id == R.id.cbStoriesig) settingsHelper.putBoolean(STORIESIG, checked);
else if (id == R.id.cbAmoledTheme) settingsHelper.putBoolean(AMOLED_THEME, checked);
else if (id == R.id.cbActivity) settingsHelper.putBoolean(CHECK_ACTIVITY, checked);
else if (id == R.id.cbUpdates) settingsHelper.putBoolean(CHECK_UPDATES, checked);
else if (id == R.id.cbSaveTo) {
settingsHelper.putBoolean(FOLDER_SAVE_TO, checked);
btnSaveTo.setEnabled(checked);

View File

@ -9,6 +9,7 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Toast;
@ -29,6 +30,8 @@ import java.io.DataOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.List;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.R;
@ -46,16 +49,18 @@ public class DirectMessageSettingsFragment extends Fragment implements SwipeRefr
private static final String TAG = "DirectMessagesSettingsFrag";
private FragmentActivity fragmentActivity;
private RecyclerView userList;
private RecyclerView userList, leftUserList;
private EditText titleText;
private View leftTitle;
private AppCompatImageView titleSend;
private AppCompatButton btnLeave;
private LinearLayoutManager layoutManager;
private LinearLayoutManager layoutManager, layoutManagerDos;
private String threadId, threadTitle;
private final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
private boolean amAdmin;
private AsyncTask<Void, Void, InboxThreadModel> currentlyRunning;
private DirectMessageMembersAdapter memberAdapter;
private View.OnClickListener clickListener;
private DirectMessageMembersAdapter memberAdapter, leftAdapter;
private View.OnClickListener clickListener, basicClickListener;
private final FetchListener<InboxThreadModel> fetchListener = new FetchListener<InboxThreadModel>() {
@Override
@ -63,8 +68,15 @@ public class DirectMessageSettingsFragment extends Fragment implements SwipeRefr
@Override
public void onResult(final InboxThreadModel threadModel) {
memberAdapter = new DirectMessageMembersAdapter(threadModel.getUsers(), requireContext(), clickListener);
final List<Long> adminList = Arrays.asList(threadModel.getAdmins());
amAdmin = adminList.contains(Long.parseLong(Utils.getUserIdFromCookie(cookie)));
memberAdapter = new DirectMessageMembersAdapter(threadModel.getUsers(), adminList, requireContext(), amAdmin ? clickListener : basicClickListener);
userList.setAdapter(memberAdapter);
if (threadModel.getLeftUsers() != null && threadModel.getLeftUsers().length > 0) {
leftTitle.setVisibility(View.VISIBLE);
leftAdapter = new DirectMessageMembersAdapter(threadModel.getLeftUsers(), null, requireContext(), basicClickListener);
leftUserList.setAdapter(leftAdapter);
}
}
};
@ -73,10 +85,9 @@ public class DirectMessageSettingsFragment extends Fragment implements SwipeRefr
super.onCreate(savedInstanceState);
fragmentActivity = requireActivity();
clickListener = v -> {
basicClickListener = v -> {
final Object tag = v.getTag();
if (tag instanceof ProfileModel) {
// TODO: kick dialog
ProfileModel model = (ProfileModel) tag;
startActivity(
new Intent(requireContext(), ProfileViewer.class)
@ -84,6 +95,37 @@ public class DirectMessageSettingsFragment extends Fragment implements SwipeRefr
);
}
};
clickListener = v -> {
final Object tag = v.getTag();
if (tag instanceof ProfileModel) {
ProfileModel model = (ProfileModel) tag;
if (!amAdmin || model.getId().equals(Utils.getUserIdFromCookie(cookie))) {
startActivity(
new Intent(requireContext(), ProfileViewer.class)
.putExtra(Constants.EXTRAS_USERNAME, model.getUsername())
);
}
else {
new AlertDialog.Builder(requireContext()).setAdapter(
new ArrayAdapter<>(requireContext(), android.R.layout.simple_list_item_1, new String[]{
getString(R.string.open_profile),
getString(R.string.dms_action_kick),
}),
(d,w) -> {
if (w == 0)
startActivity(
new Intent(requireContext(), ProfileViewer.class)
.putExtra(Constants.EXTRAS_USERNAME, model.getUsername())
);
else if (w == 1) {
new ChangeSettings().execute("remove_users", model.getId());
onRefresh();
}
}).show();
}
}
};
}
@Override
@ -92,7 +134,18 @@ public class DirectMessageSettingsFragment extends Fragment implements SwipeRefr
final Bundle savedInstanceState) {
final FragmentDirectMessagesSettingsBinding binding = FragmentDirectMessagesSettingsBinding.inflate(inflater, container, false);
final LinearLayout root = binding.getRoot();
layoutManager = new LinearLayoutManager(requireContext());
layoutManager = new LinearLayoutManager(requireContext()) {
@Override
public boolean canScrollVertically() {
return false;
}
};
layoutManagerDos = new LinearLayoutManager(requireContext()) {
@Override
public boolean canScrollVertically() {
return false;
}
};
threadId = DirectMessageSettingsFragmentArgs.fromBundle(getArguments()).getThreadId();
threadTitle = DirectMessageSettingsFragmentArgs.fromBundle(getArguments()).getTitle();
@ -102,6 +155,12 @@ public class DirectMessageSettingsFragment extends Fragment implements SwipeRefr
userList.setHasFixedSize(true);
userList.setLayoutManager(layoutManager);
leftUserList = binding.leftUserList;
leftUserList.setHasFixedSize(true);
leftUserList.setLayoutManager(layoutManagerDos);
leftTitle = binding.leftTitle;
titleText = binding.titleText;
titleText.setText(threadTitle);
@ -154,11 +213,12 @@ public class DirectMessageSettingsFragment extends Fragment implements SwipeRefr
}
class ChangeSettings extends AsyncTask<String, Void, Void> {
String action;
String action, argument;
boolean ok = false;
protected Void doInBackground(String... rawAction) {
action = rawAction[0];
if (rawAction.length == 2) argument = rawAction[1];
final String url = "https://i.instagram.com/api/v1/direct_v2/threads/"+threadId+"/"+action+"/";
try {
String urlParameters = "_csrftoken=" + cookie.split("csrftoken=")[1].split(";")[0]
@ -167,6 +227,8 @@ public class DirectMessageSettingsFragment extends Fragment implements SwipeRefr
urlParameters += "&title=" + URLEncoder.encode(titleText.getText().toString(), "UTF-8")
.replaceAll("\\+", "%20").replaceAll("%21", "!").replaceAll("%27", "'")
.replaceAll("%28", "(").replaceAll("%29", ")").replaceAll("%7E", "~");
else if (action.startsWith("remove_users"))
urlParameters += ("&user_ids=[" + argument + "]");
final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setUseCaches(false);
@ -197,11 +259,15 @@ public class DirectMessageSettingsFragment extends Fragment implements SwipeRefr
threadTitle = titleText.getText().toString();
titleSend.setVisibility(View.GONE);
titleText.clearFocus();
DirectMessageThreadFragment.hasSentSomething = true;
}
else if (action.equals("leave")) {
DirectMessageInboxFragment.refreshPlease = true;
NavHostFragment.findNavController(DirectMessageSettingsFragment.this).popBackStack(R.id.directMessagesInboxFragment, false);
}
else {
DirectMessageThreadFragment.hasSentSomething = true;
}
}
else Toast.makeText(requireContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}

View File

@ -76,17 +76,17 @@ public class DirectMessageThreadFragment extends Fragment {
private static final int PICK_IMAGE = 100;
private AppCompatActivity fragmentActivity;
private String threadId, threadTitle;
private String cursor;
private String threadId, threadTitle, cursor, lastMessage;
private final String cookie = Utils.settingsHelper.getString(Constants.COOKIE);
private final String myId = Utils.getUserIdFromCookie(cookie);
private FragmentDirectMessagesThreadBinding binding;
private DirectItemModelListViewModel listViewModel;
private DirectItemModel directItemModel;
private RecyclerView messageList;
// private AppCompatImageView dmInfo;
// private AppCompatImageView dmInfo, dmSeen;
private boolean hasSentSomething, hasDeletedSomething;
private boolean hasOlder = true;
public static boolean hasSentSomething;
private final ProfileModel myProfileHolder = ProfileModel.getDefaultProfileModel();
private final List<ProfileModel> users = new ArrayList<>();
@ -150,6 +150,10 @@ public class DirectMessageThreadFragment extends Fragment {
list.addAll(newList);
}
listViewModel.getList().postValue(list);
lastMessage = result.getNewestCursor();
if (Utils.settingsHelper.getBoolean(Constants.DM_MARK_AS_SEEN)) new ThreadAction().execute("seen", lastMessage);
}
binding.swipeRefreshLayout.setRefreshing(false);
}
@ -162,6 +166,14 @@ public class DirectMessageThreadFragment extends Fragment {
setHasOptionsMenu(true);
}
@Override
public void onResume() {
super.onResume();
if (hasSentSomething) {
new DirectMessageInboxThreadFetcher(threadId, UserInboxDirection.OLDER, null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
@Override
public View onCreateView(@NonNull final LayoutInflater inflater,
final ViewGroup container,
@ -169,6 +181,7 @@ public class DirectMessageThreadFragment extends Fragment {
binding = FragmentDirectMessagesThreadBinding.inflate(inflater, container, false);
final FragmentContainerView containerTwo = (FragmentContainerView) container.getParent();
// dmInfo = containerTwo.findViewById(R.id.dmInfo);
// dmSeen = containerTwo.findViewById(R.id.dmSeen);
final LinearLayout root = binding.getRoot();
listViewModel = new ViewModelProvider(fragmentActivity).get(DirectItemModelListViewModel.class);
if (getArguments() == null) {
@ -204,6 +217,11 @@ public class DirectMessageThreadFragment extends Fragment {
// NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(action);
// });
// dmSeen.setOnClickListener(v -> {
// new ThreadAction().execute("seen", lastMessage);
// dmSeen.setVisibility(View.GONE);
// });
final DialogInterface.OnClickListener onDialogListener = (dialogInterface, which) -> {
if (which == 0) {
final DirectItemType itemType = directItemModel.getItemType();
@ -271,7 +289,7 @@ public class DirectMessageThreadFragment extends Fragment {
sendText(null, directItemModel.getItemId(), directItemModel.isLiked());
} else if (which == 2) {
if (String.valueOf(directItemModel.getUserId()).equals(myId))
new Unsend().execute();
new ThreadAction().execute("delete", directItemModel.getItemId());
else searchUsername(getUser(directItemModel.getUserId()).getUsername());
}
};
@ -474,9 +492,13 @@ 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/";
class ThreadAction extends AsyncTask<String, Void, Void> {
String action, argument;
protected Void doInBackground(String... rawAction) {
action = rawAction[0];
argument = rawAction[1];
final String url = "https://i.instagram.com/api/v1/direct_v2/threads/" + threadId + "/items/" + argument + "/" + action + "/";
try {
String urlParameters = "_csrftoken=" + cookie.split("csrftoken=")[1].split(";")[0]
+ "&_uuid=" + Utils.settingsHelper.getString(Constants.DEVICE_UUID);
@ -493,11 +515,12 @@ public class DirectMessageThreadFragment extends Fragment {
wr.close();
urlConnection.connect();
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
hasDeletedSomething = true;
if (action == "delete") hasDeletedSomething = true;
else if (action == "seen") DirectMessageInboxFragment.refreshPlease = true;
}
urlConnection.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", "unsend: " + ex);
Log.e("austin_debug", action + ": " + ex);
}
return null;
}

View File

@ -12,16 +12,17 @@ public final class InboxThreadModel implements Serializable {
private final String threadId, threadV2Id, threadType, threadTitle, newestCursor, oldestCursor, nextCursor, prevCursor;
private final ProfileModel inviter;
private final ProfileModel[] users, leftUsers;
private final Long[] admins;
private final DirectItemModel[] items;
private final boolean muted, isPin, isSpam, isGroup, named, pending, archived, canonical, hasOlder, hasNewer;
private final long lastActivityAt;
private final boolean muted, isPin, isSpam, isGroup, named, pending, archived, canonical, hasOlder;
private final long unreadCount, lastActivityAt;
public InboxThreadModel(final InboxReadState readState, final String threadId, final String threadV2Id, final String threadType, final String threadTitle,
final String newestCursor, final String oldestCursor, final String nextCursor, final String prevCursor,
final ProfileModel inviter, final ProfileModel[] users,
final ProfileModel[] leftUsers, final DirectItemModel[] items, final boolean muted,
final ProfileModel inviter, final ProfileModel[] users, final ProfileModel[] leftUsers,
final Long[] admins, final DirectItemModel[] items, final boolean muted,
final boolean isPin, final boolean named, final boolean canonical, final boolean pending,
final boolean hasOlder, final boolean hasNewer, final boolean isSpam, final boolean isGroup,
final boolean hasOlder, final long unreadCount, final boolean isSpam, final boolean isGroup,
final boolean archived, final long lastActivityAt) {
this.readState = readState;
this.threadId = threadId;
@ -35,6 +36,7 @@ public final class InboxThreadModel implements Serializable {
this.inviter = inviter;
this.users = users;
this.leftUsers = leftUsers;
this.admins = admins;
this.items = items; // todo
this.muted = muted;
this.isPin = isPin;
@ -42,7 +44,7 @@ public final class InboxThreadModel implements Serializable {
this.canonical = canonical;
this.pending = pending;
this.hasOlder = hasOlder;
this.hasNewer = hasNewer;
this.unreadCount = unreadCount;
this.isSpam = isSpam;
this.isGroup = isGroup;
this.archived = archived;
@ -97,6 +99,8 @@ public final class InboxThreadModel implements Serializable {
return leftUsers;
}
public Long[] getAdmins() { return admins; }
public DirectItemModel[] getItems() {
return items;
}
@ -129,9 +133,7 @@ public final class InboxThreadModel implements Serializable {
return hasOlder;
}
public boolean isHasNewer() {
return hasNewer;
}
public long getUnreadCount() { return unreadCount; }
public boolean isSpam() {
return isSpam;

View File

@ -20,10 +20,12 @@ public final class Constants {
public static final String SHOW_FEED = "show_feed";
public static final String CUSTOM_DATE_TIME_FORMAT_ENABLED = "data_time_custom_enabled";
public static final String MARK_AS_SEEN = "mark_as_seen";
public static final String DM_MARK_AS_SEEN = "dm_mark_as_seen";
public static final String INSTADP = "instadp";
public static final String STORIESIG = "storiesig";
public static final String AMOLED_THEME = "amoled_theme";
public static final String CHECK_ACTIVITY = "check_activity";
public static final String CHECK_UPDATES = "check_updates";
// never Export
public static final String COOKIE = "cookie";
public static final String SHOW_QUICK_ACCESS_DIALOG = "show_quick_dlg";
@ -50,9 +52,9 @@ public final class Constants {
// spoof
public static final String USER_AGENT = "Mozilla/5.0 (Linux; Android 8.1.0; motorola one Build/OPKS28.63-18-3; wv) " +
"AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36 " +
"Instagram 154.0.0.32.123 Android (27/8.1.0; 320dpi; 720x1362; motorola; motorola one; deen_sprout; qcom; pt_BR; 238093938)";
"Instagram 156.0.0.26.109 Android (27/8.1.0; 320dpi; 720x1362; motorola; motorola one; deen_sprout; qcom; pt_BR; 240726452)";
public static final String I_USER_AGENT =
"Instagram 154.0.0.32.123 Android (27/8.1.0; 320dpi; 720x1362; motorola; motorola one; deen_sprout; qcom; pt_BR; 238093938)";
"Instagram 156.0.0.26.109 Android (27/8.1.0; 320dpi; 720x1362; motorola; motorola one; deen_sprout; qcom; pt_BR; 240726452)";
public static final String A_USER_AGENT = "https://InstaGrabber.AustinHuang.me / mailto:InstaGrabber@AustinHuang.me";
// see https://github.com/dilame/instagram-private-api/blob/master/src/core/constants.ts
public static final String SUPPORTED_CAPABILITIES = "[ { \"name\": \"SUPPORTED_SDK_VERSIONS\", \"value\":" +

View File

@ -16,12 +16,14 @@ import static awais.instagrabber.utils.Constants.AUTOLOAD_POSTS;
import static awais.instagrabber.utils.Constants.AUTOPLAY_VIDEOS;
import static awais.instagrabber.utils.Constants.BOTTOM_TOOLBAR;
import static awais.instagrabber.utils.Constants.CHECK_ACTIVITY;
import static awais.instagrabber.utils.Constants.CHECK_UPDATES;
import static awais.instagrabber.utils.Constants.COOKIE;
import static awais.instagrabber.utils.Constants.CUSTOM_DATE_TIME_FORMAT;
import static awais.instagrabber.utils.Constants.CUSTOM_DATE_TIME_FORMAT_ENABLED;
import static awais.instagrabber.utils.Constants.DATE_TIME_FORMAT;
import static awais.instagrabber.utils.Constants.DATE_TIME_SELECTION;
import static awais.instagrabber.utils.Constants.DEVICE_UUID;
import static awais.instagrabber.utils.Constants.DM_MARK_AS_SEEN;
import static awais.instagrabber.utils.Constants.DOWNLOAD_USER_FOLDER;
import static awais.instagrabber.utils.Constants.FOLDER_PATH;
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
@ -77,7 +79,8 @@ public final class SettingsHelper {
return BOTTOM_TOOLBAR.equals(key) ||
AUTOPLAY_VIDEOS.equals(key) ||
SHOW_QUICK_ACCESS_DIALOG.equals(key) ||
MUTED_VIDEOS.equals(key);
MUTED_VIDEOS.equals(key) ||
CHECK_UPDATES.equals(key);
}
public int getThemeCode(final boolean fromHelper) {
@ -120,7 +123,8 @@ public final class SettingsHelper {
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})
AUTOLOAD_POSTS, CUSTOM_DATE_TIME_FORMAT_ENABLED, MARK_AS_SEEN, DM_MARK_AS_SEEN,
INSTADP, STORIESIG, AMOLED_THEME, CHECK_ACTIVITY, CHECK_UPDATES})
public @interface BooleanSettings {}
@StringDef({PREV_INSTALL_VERSION})

View File

@ -475,7 +475,7 @@ public final class Utils {
@NonNull
public static InboxThreadModel createInboxThreadModel(@NonNull final JSONObject data, final boolean inThreadView) throws Exception {
final InboxReadState readState = data.getInt("read_state") == 0 ? InboxReadState.STATE_READ : InboxReadState.STATE_UNREAD;
final String threadType = data.getString("thread_type");// private = dms, [??] = group
final String threadType = data.getString("thread_type"); // they're all "private", group is identified by boolean "is_group"
final String threadId = data.getString("thread_id");
final String threadV2Id = data.getString("thread_v2_id");
@ -487,7 +487,7 @@ public final class Utils {
final String threadPrevCursor = data.has("prev_cursor") ? data.getString("prev_cursor") : null;
final boolean threadHasOlder = data.getBoolean("has_older");
final boolean threadHasNewer = data.getBoolean("has_newer");
final long unreadCount = data.optLong("read_state", 0);
final long lastActivityAt = data.optLong("last_activity_at");
final boolean named = data.optBoolean("named");
@ -503,6 +503,8 @@ public final class Utils {
final int usersLen = users.length();
final JSONArray leftusers = data.getJSONArray("left_users");
final int leftusersLen = leftusers.length();
final JSONArray admins = data.getJSONArray("admin_user_ids");
final int adminsLen = admins.length();
final ProfileModel[] userModels = new ProfileModel[usersLen];
for (int j = 0; j < usersLen; ++j) {
@ -532,6 +534,11 @@ public final class Utils {
null, 0, 0, 0, false, false, false, false);
}
final Long[] adminIDs = new Long[adminsLen];
for (int j = 0; j < adminsLen; ++j) {
adminIDs[j] = admins.getLong(j);
}
final JSONArray items = data.getJSONArray("items");
final int itemsLen = items.length();
@ -710,7 +717,6 @@ public final class Utils {
break;
case CLIP:
Log.d("austin_debug", "clip: "+itemObject.getJSONObject("clip").getJSONObject("clip"));
directMedia = getDirectMediaModel(itemObject.getJSONObject("clip").getJSONObject("clip"));
break;
@ -779,10 +785,10 @@ public final class Utils {
return new InboxThreadModel(readState, threadId, threadV2Id, threadType, threadTitle,
threadNewestCursor, threadOldestCursor, threadNextCursor, threadPrevCursor,
null, // todo
userModels, leftuserModels,
userModels, leftuserModels, adminIDs,
itemModels.toArray(new DirectItemModel[0]),
muted, isPin, named, canonical,
pending, threadHasOlder, threadHasNewer, isSpam, isGroup, archived, lastActivityAt);
pending, threadHasOlder, unreadCount, isSpam, isGroup, archived, lastActivityAt);
}
private static RavenExpiringMediaType getExpiringMediaType(final String type) {

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 943 B

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid
android:color="#ff0000"/>
<size
android:width="120dp"
android:height="120dp"/>
</shape>

View File

@ -44,11 +44,27 @@
<TextView
android:id="@+id/toolbar_title"
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
android:layout_width="wrap_content"
android:layout_width="200dp"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="@string/app_name" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/dmSeen"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
android:background="?selectableItemBackgroundBorderless"
android:clickable="true"
android:focusable="true"
android:paddingStart="4dp"
android:paddingLeft="4dp"
android:paddingTop="4dp"
android:paddingEnd="8dp"
android:paddingRight="8dp"
android:paddingBottom="4dp"
app:srcCompat="@android:drawable/ic_menu_view" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/dmInfo"
android:layout_width="wrap_content"

View File

@ -46,6 +46,42 @@
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:background="?android:selectableItemBackground"
android:orientation="horizontal"
android:weightSum="2">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnLogin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_weight="1"
android:text="@string/login"
android:textColor="@color/btn_green_text_color"
android:textSize="20sp"
app:backgroundTint="@color/btn_green_background" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnLogout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_weight="1"
android:text="@string/logout"
android:textColor="@color/btn_red_text_color"
android:textSize="20sp"
app:backgroundTint="@color/btn_red_background" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -209,6 +245,31 @@
android:textSize="16sp" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginLeft="5dp"
android:background="?android:selectableItemBackground"
android:orientation="horizontal"
android:padding="5dp">
<androidx.appcompat.widget.AppCompatCheckBox
android:id="@+id/cbUpdates"
android:layout_width="30dp"
android:layout_height="30dp"
android:contentDescription="@string/bottom_toolbar" />
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:padding="5dp"
android:text="@string/update_check"
android:textColor="?android:textColorPrimary"
android:textSize="16sp" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -320,6 +381,31 @@
android:textSize="16sp" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginLeft="5dp"
android:background="?android:selectableItemBackground"
android:orientation="horizontal"
android:padding="5dp">
<androidx.appcompat.widget.AppCompatCheckBox
android:id="@+id/cbMarkDmAsSeen"
android:layout_width="30dp"
android:layout_height="30dp"
android:contentDescription="@string/dm_mark_as_seen_setting" />
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:padding="5dp"
android:text="@string/dm_mark_as_seen_setting"
android:textColor="?android:textColorPrimary"
android:textSize="16sp" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -419,42 +505,6 @@
android:textSize="16sp" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:background="?android:selectableItemBackground"
android:orientation="horizontal"
android:weightSum="2">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnLogin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_weight="1"
android:text="@string/login"
android:textColor="@color/btn_green_text_color"
android:textSize="20sp"
app:backgroundTint="@color/btn_green_background" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnLogout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_weight="1"
android:text="@string/logout"
android:textColor="@color/btn_red_text_color"
android:textSize="20sp"
app:backgroundTint="@color/btn_red_background" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnTimeSettings"
android:layout_width="match_parent"

View File

@ -60,9 +60,35 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/userList"
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/userList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:nestedScrollingEnabled="false"/>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/leftTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:gravity="center_vertical"
android:padding="5dp"
android:text="@string/dms_left_users"
android:textColor="?android:textColorPrimary"
android:textSize="24sp"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/leftUserList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:nestedScrollingEnabled="false"/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</LinearLayout>

View File

@ -20,6 +20,7 @@
android:layout_height="60dp"
android:layout_marginStart="66dp"
android:layout_marginLeft="66dp"
android:layout_marginEnd="26dp"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatTextView
@ -41,4 +42,13 @@
android:gravity="center_vertical"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
</LinearLayout>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/isAdmin"
android:layout_width="20dp"
android:layout_height="60dp"
android:layout_gravity="right"
android:scaleType="fitCenter"
app:srcCompat="@drawable/ic_star"
android:visibility="gone"/>
</FrameLayout>

View File

@ -0,0 +1,9 @@
* Proper login support for those under Android 8
* You are alerted before unfollowing a private account
* Update checker can now be toggled
* "Swipe up" story sticker type is now supported
* Unread DM are now indicated
* You can now mark DMs as seen, either automatically (settings) or manually
* You can now kick DM members
* "Clip" message type (basically sharing reels) is now supported
* Bug fixes, see GitHub