This commit is contained in:
Austin Huang 2020-08-06 12:48:52 -04:00
parent 9850b19fcf
commit 14d3ea5c6c
No known key found for this signature in database
GPG Key ID: 84C23AA04587A91F
24 changed files with 376 additions and 96 deletions

View File

@ -9,8 +9,8 @@ android {
minSdkVersion 16
targetSdkVersion 29
versionCode 39
versionName '17.3'
versionCode 40
versionName '17.4'
multiDexEnabled true

View File

@ -114,38 +114,15 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
main.mainBinding.mainPosts.setVisibility(View.VISIBLE);
});
final String username;
final String postFix;
if (!isHashtag && !isLocation) {
username = "@"+main.profileModel.getUsername();
postFix = "/" + main.profileModel.getPostCount() + ')';
} else {
username = null;
postFix = null;
}
if (isHashtag)
main.mainBinding.toolbar.toolbar.setTitle(main.userQuery);
else if (isLocation)
main.mainBinding.toolbar.toolbar.setTitle(main.locationModel.getName());
else main.mainBinding.toolbar.toolbar.setTitle(username + " (" + main.allItems.size() + postFix);
else main.mainBinding.toolbar.toolbar.setTitle("@"+main.profileModel.getUsername());
final PostModel model = result[result.length - 1];
if (model != null) {
endCursor = model.getEndCursor();
if (endCursor == null && !isHashtag) {
main.mainBinding.toolbar.toolbar.setTitle(username + " (" + main.profileModel.getPostCount() + postFix);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
main.mainBinding.toolbar.toolbar.setTitle(username);
handler.removeCallbacks(this);
}
}, 1000);
}
hasNextPage = model.hasNextPage();
if (autoloadPosts && hasNextPage)
currentlyExecuting = new PostsFetcher(main.profileModel.getId(), endCursor, this)
@ -273,8 +250,6 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
);
else Toast.makeText(main, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
});
@ -1254,7 +1229,7 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener {
Long.parseLong(Utils.dataBox.getFavorite(main.userQuery).split("/")[1]),
main.locationModel != null ? main.locationModel.getName() : main.userQuery.replaceAll("^@", "")));
onRefresh();
} else if (!isLoggedIn && v == main.mainBinding.btnFollow) {
} else if (!isLoggedIn && (v == main.mainBinding.btnFollow || v == main.mainBinding.btnFollowTag)) {
Utils.dataBox.addFavorite(new DataBox.FavoriteModel(main.userQuery, System.currentTimeMillis(),
main.locationModel != null ? main.locationModel.getName() : main.userQuery.replaceAll("^@", "")));
onRefresh();

View File

@ -279,7 +279,6 @@ public final class CommentsViewer extends BaseLanguageActivity implements SwipeR
commentsBinding.commentText.clearFocus();
}
}
else Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
urlConnection.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", action+": " + ex);
@ -296,10 +295,9 @@ public final class CommentsViewer extends BaseLanguageActivity implements SwipeR
commentModel = null;
focus = null;
}
//imm.hideSoftInputFromWindow(commentsBinding.getView().getRootView().getWindowToken(), 0);
onRefresh();
}
else Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
}
}

View File

@ -9,10 +9,12 @@ import androidx.annotation.Nullable;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import android.view.View;
import java.util.ArrayList;
import java.util.Arrays;
import awais.instagrabber.R;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.adapters.DirectMessagesAdapter;
import awais.instagrabber.asyncs.direct_messages.InboxFetcher;
@ -72,6 +74,9 @@ public final class DirectMessages extends BaseLanguageActivity implements SwipeR
setContentView(dmsBinding.getRoot());
dmsBinding.swipeRefreshLayout.setOnRefreshListener(this);
dmsBinding.toolbar.toolbar.setTitle(R.string.action_dms);
dmsBinding.commentText.setVisibility(View.GONE);
dmsBinding.commentSend.setVisibility(View.GONE);
final LinearLayoutManager layoutManager = new LinearLayoutManager(this);
dmsBinding.rvDirectMessages.setLayoutManager(layoutManager);

View File

@ -13,15 +13,19 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.io.DataOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import awais.instagrabber.R;
import awais.instagrabber.adapters.MessageItemsAdapter;
import awais.instagrabber.asyncs.direct_messages.UserInboxFetcher;
import awais.instagrabber.asyncs.UsernameFetcher;
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
import awais.instagrabber.databinding.ActivityDmsBinding;
import awais.instagrabber.interfaces.FetchListener;
@ -29,22 +33,22 @@ 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.DirectItemModel.DirectItemMediaModel;
import awais.instagrabber.models.direct_messages.DirectItemModel.DirectItemRavenMediaModel;
import awais.instagrabber.models.direct_messages.InboxThreadModel;
import awais.instagrabber.models.enums.DirectItemType;
import awais.instagrabber.models.enums.DownloadMethod;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.models.enums.UserInboxDirection;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper;
public final class DirectMessagesUserInbox extends AppCompatActivity {
private DirectItemModel directItemModel;
private final ProfileModel myProfileHolder =
new ProfileModel(false, false, false, null, null, null, null, null, null, null, 0, 0, 0, false, false, false, false);
private final ArrayList<ProfileModel> users = new ArrayList<>(), leftusers = new ArrayList<>();
private final ArrayList<DirectItemModel> directItemModels = new ArrayList<>();
private String threadid;
private final FetchListener<InboxThreadModel> fetchListener = new FetchListener<InboxThreadModel>() {
@Override
public void doBefore() {
@ -66,6 +70,14 @@ public final class DirectMessagesUserInbox extends AppCompatActivity {
leftusers.clear();
leftusers.addAll(Arrays.asList(result.getLeftUsers()));
threadid = result.getThreadId();
dmsBinding.toolbar.toolbar.setTitle(result.getThreadTitle());
String[] users = new String[result.getUsers().length];
for (int i = 0; i < users.length; ++i) {
users[i] = result.getUsers()[i].getUsername();
}
dmsBinding.toolbar.toolbar.setSubtitle(String.join(", ", users));
final int oldSize = directItemModels.size();
final List<DirectItemModel> itemModels = Arrays.asList(result.getItems());
directItemModels.addAll(itemModels);
@ -94,6 +106,9 @@ public final class DirectMessagesUserInbox extends AppCompatActivity {
}
dmsBinding.swipeRefreshLayout.setEnabled(false);
dmsBinding.commentText.setVisibility(View.VISIBLE);
dmsBinding.commentSend.setVisibility(View.VISIBLE);
dmsBinding.commentSend.setOnClickListener(newCommentListener);
final LinearLayoutManager layoutManager = new LinearLayoutManager(this, RecyclerView.VERTICAL, true);
dmsBinding.rvDirectMessages.setLayoutManager(layoutManager);
@ -138,7 +153,9 @@ public final class DirectMessagesUserInbox extends AppCompatActivity {
directItemModel.getReelShare().getMedia().getVideoUrl(),
directItemModel.getReelShare().getMedia().getMediaType(),
directItemModel.getTimestamp(),
directItemModel.getReelShare().getReelOwnerName()
directItemModel.getReelShare().getReelOwnerName(),
String.valueOf(directItemModel.getReelShare().getReelOwnerId()),
false
);
sm.setVideoUrl(directItemModel.getReelShare().getMedia().getVideoUrl());
StoryModel[] sms = {sm};
@ -196,4 +213,66 @@ public final class DirectMessagesUserInbox extends AppCompatActivity {
startActivity(intent);
}
}
private final View.OnClickListener newCommentListener = v -> {
if (Utils.isEmpty(dmsBinding.commentText.getText().toString()) && v == dmsBinding.commentSend)
Toast.makeText(getApplicationContext(), R.string.comment_send_empty_comment, Toast.LENGTH_SHORT).show();
else if (v == dmsBinding.commentSend) new CommentAction().execute();
};
class CommentAction extends AsyncTask<Void, Void, Void> {
boolean ok = false;
protected Void doInBackground(Void... lmao) {
final String url = "https://i.instagram.com/api/v1/direct_v2/create_group_thread/";
final String cookie = settingsHelper.getString(Constants.COOKIE);
try {
final String url2 = "https://i.instagram.com/api/v1/direct_v2/threads/broadcast/text/";
final HttpURLConnection urlConnection2 = (HttpURLConnection) new URL(url2).openConnection();
urlConnection2.setRequestMethod("POST");
urlConnection2.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
urlConnection2.setUseCaches(false);
final String commentText = URLEncoder.encode(dmsBinding.commentText.getText().toString(), "UTF-8")
.replaceAll("\\+", "%20").replaceAll("\\%21", "!").replaceAll("\\%27", "'")
.replaceAll("\\%28", "(").replaceAll("\\%29", ")").replaceAll("\\%7E", "~");
final String cc = UUID.randomUUID().toString();
final String urlParameters2 = Utils.sign("{\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0]
+"\",\"_uid\":\"" + Utils.getUserIdFromCookie(cookie)
+"\",\"__uuid\":\"" + settingsHelper.getString(Constants.DEVICE_UUID)
+"\",\"client_context\":\"" + cc
+"\",\"mutation_token\":\"" + cc
+"\",\"text\":\"" + commentText
+"\",\"thread_ids\":\"["+threadid
+"]\",\"action\":\"send_item\"}");
urlConnection2.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
urlConnection2.setRequestProperty("Content-Length", "" + Integer.toString(urlParameters2.getBytes().length));
urlConnection2.setDoOutput(true);
DataOutputStream wr2 = new DataOutputStream(urlConnection2.getOutputStream());
wr2.writeBytes(urlParameters2);
wr2.flush();
wr2.close();
urlConnection2.connect();
Log.d("austin_debug", urlConnection2.getResponseCode() + " " + urlParameters2 + " " + cookie);
if (urlConnection2.getResponseCode() == HttpURLConnection.HTTP_OK) {
ok = true;
}
urlConnection2.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", "dm send: " + ex);
}
return null;
}
@Override
protected void onPostExecute(Void result) {
if (!ok) Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
else {
dmsBinding.commentText.setText("");
dmsBinding.commentText.clearFocus();
directItemModels.clear();
messageItemsAdapter.notifyDataSetChanged();
new UserInboxFetcher(threadid, UserInboxDirection.OLDER, null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
}
}

View File

@ -158,7 +158,6 @@ public final class NotificationsViewer extends BaseLanguageActivity implements S
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
ok = true;
}
else Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
urlConnection.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", action+": " + ex);
@ -171,6 +170,7 @@ public final class NotificationsViewer extends BaseLanguageActivity implements S
if (ok == true) {
onRefresh();
}
else Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
}
}

View File

@ -88,7 +88,7 @@ public final class PostViewer extends BaseLanguageActivity {
private SwipeEvent swipeEvent;
private CharSequence postCaption = null, postShortCode, postUserId;
private Resources resources;
private boolean session = false, isFromShare;
private boolean session = false, isFromShare, liked, saved, ok = false;
private int slidePos = 0, lastSlidePos = 0;
private ItemGetType itemGetType;
@SuppressLint("ClickableViewAccessibility")
@ -237,6 +237,8 @@ public final class PostViewer extends BaseLanguageActivity {
viewerBinding.bottomPanel.commentsCount.setText(String.valueOf(commentsCount));
viewerBinding.bottomPanel.btnComments.setVisibility(View.VISIBLE);
postShortCode = postModel.getShortCode();
viewerBinding.bottomPanel.btnComments.setOnClickListener(v ->
startActivityForResult(new Intent(this, CommentsViewer.class)
.putExtra(Constants.EXTRAS_SHORTCODE, postShortCode)
@ -250,8 +252,10 @@ public final class PostViewer extends BaseLanguageActivity {
postModel.setPostId(viewerPostModel.getPostId());
postModel.setTimestamp(viewerPostModel.getTimestamp());
postModel.setPostCaption(viewerPostModel.getPostCaption());
postModel.setLike(viewerPostModel.getLike());
postModel.setBookmark(viewerPostModel.getBookmark());
if (!ok) {
liked = viewerPostModel.getLike();
saved = viewerPostModel.getBookmark();
}
}
setupPostInfoBar("@"+viewerPostModel.getUsername(), viewerPostModel.getItemType(),
@ -390,6 +394,7 @@ public final class PostViewer extends BaseLanguageActivity {
if (isMainSwipe) {
slidePos = 0;
ok = false;
Log.d("AWAISKING_APP", "swipe left <<< post[" + position + "]: " + postModel + " -- " + slides);
postModel = itemGetterItems.get(position);
postModel.setPosition(position);
@ -628,19 +633,19 @@ public final class PostViewer extends BaseLanguageActivity {
postModel.setPostId(viewerPostModel.getPostId());
postModel.setTimestamp(viewerPostModel.getTimestamp());
postModel.setPostCaption(viewerPostModel.getPostCaption());
postModel.setLike(viewerPostModel.getLike());
postModel.setBookmark(viewerPostModel.getBookmark());
if (viewerPostModel.getLike() == true) {
viewerBinding.btnLike.setText(resources.getString(R.string.unlike, viewerPostModel.getLikes()));
if (liked == true) {
viewerBinding.btnLike.setText(resources.getString(R.string.unlike, viewerPostModel.getLikes()
+ ((ok && viewerPostModel.getLike() != liked) ? (liked ? 1L : -1L) : 0L)));
viewerBinding.btnLike.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor(
getApplicationContext(), R.color.btn_pink_background)));
}
else {
viewerBinding.btnLike.setText(resources.getString(R.string.like, viewerPostModel.getLikes()));
viewerBinding.btnLike.setText(resources.getString(R.string.like, viewerPostModel.getLikes()
+ ((ok && viewerPostModel.getLike() != liked) ? (liked ? 1L : -1L) : 0L)));
viewerBinding.btnLike.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor(
getApplicationContext(), R.color.btn_lightpink_background)));
}
if (viewerPostModel.getBookmark() == true) {
if (saved == true) {
viewerBinding.btnBookmark.setText(R.string.unbookmark);
viewerBinding.btnBookmark.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor(
getApplicationContext(), R.color.btn_orange_background)));
@ -755,20 +760,14 @@ public final class PostViewer extends BaseLanguageActivity {
decorView.setSystemUiVisibility(newUiOptions);
}
/*
Recommended for PERSONAL use only
Don't ever think about running a like farm with this
*/
class PostAction extends AsyncTask<String, Void, Void> {
boolean ok = false;
String action;
protected Void doInBackground(String... rawAction) {
action = rawAction[0];
final String url = "https://www.instagram.com/web/"+action+"/"+postModel.getPostId()+"/"+ (action == "save" ?
(postModel.getBookmark() == true ? "unsave/" : "save/") :
(postModel.getLike() == true ? "unlike/" : "like/"));
(saved ? "unsave/" : "save/") :
(liked ? "unlike/" : "like/"));
try {
final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST");
@ -780,7 +779,6 @@ public final class PostViewer extends BaseLanguageActivity {
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
ok = true;
}
else Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
urlConnection.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", action+": " + ex);
@ -791,15 +789,14 @@ public final class PostViewer extends BaseLanguageActivity {
@Override
protected void onPostExecute(Void result) {
if (ok == true && action == "likes") {
postModel.setLike(!viewerPostModel.getLike());
viewerPostModel.setManualLike(!viewerPostModel.getLike());
liked = !liked;
refreshPost();
}
else if (ok == true && action == "save") {
viewerPostModel.setBookmark(!viewerPostModel.getBookmark());
postModel.setBookmark(!viewerPostModel.getBookmark());
saved = !saved;
refreshPost();
}
else Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
}
}

View File

@ -47,6 +47,7 @@ import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Date;
import java.util.UUID;
import org.json.JSONObject;
@ -74,6 +75,7 @@ import static awais.instagrabber.customviews.helpers.SwipeGestureListener.SWIPE_
import static awais.instagrabber.customviews.helpers.SwipeGestureListener.SWIPE_VELOCITY_THRESHOLD;
import static awais.instagrabber.utils.Constants.FOLDER_PATH;
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
import static awais.instagrabber.utils.Constants.MARK_AS_SEEN;
import static awais.instagrabber.utils.Utils.logCollector;
import static awais.instagrabber.utils.Utils.settingsHelper;
@ -94,7 +96,7 @@ public final class StoryViewer extends BaseLanguageActivity {
private GestureDetectorCompat gestureDetector;
private SimpleExoPlayer player;
private SwipeEvent swipeEvent;
private MenuItem menuDownload;
private MenuItem menuDownload, menuDm;
private PollModel poll;
private QuestionModel question;
private String[] mentions;
@ -149,11 +151,11 @@ public final class StoryViewer extends BaseLanguageActivity {
if ((isRightSwipe == true && index == 0) || (isRightSwipe == false && index == storyFeed.length - 1))
Toast.makeText(getApplicationContext(), R.string.no_more_stories, Toast.LENGTH_SHORT).show();
else {
boolean fetching = false;
final FeedStoryModel feedStoryModel = isRightSwipe ?
(index == 0 ? null : storyFeed[index - 1]) :
(storyFeed.length == index + 1 ? null : storyFeed[index + 1]);
if (feedStoryModel != null) {
boolean fetching = false;
if (fetching) {
Toast.makeText(getApplicationContext(), R.string.be_patient, Toast.LENGTH_SHORT).show();
} else {
@ -179,7 +181,6 @@ public final class StoryViewer extends BaseLanguageActivity {
if (--slidePos <= 0) slidePos = 0;
} else if (++slidePos >= storiesLen) slidePos = storiesLen - 1;
currentStory = storyModels[slidePos];
//slidePos = currentStory.getPosition();
refreshStory();
}
}
@ -197,6 +198,7 @@ public final class StoryViewer extends BaseLanguageActivity {
storiesAdapter.setData(null);
if (menuDownload != null) menuDownload.setVisible(false);
if (menuDm != null) menuDm.setVisible(false);
storyViewerBinding.playerView.setOnTouchListener((v, event) -> gestureDetector.onTouchEvent(event));
storyViewerBinding.imageViewer.setOnSingleFlingListener((e1, e2, velocityX, velocityY) -> {
@ -318,12 +320,14 @@ public final class StoryViewer extends BaseLanguageActivity {
@Override
public void onLoadCompleted(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) {
if (menuDownload != null) menuDownload.setVisible(true);
if (currentStory.canReply() && menuDm != null) menuDm.setVisible(true);
storyViewerBinding.progressView.setVisibility(View.GONE);
}
@Override
public void onLoadStarted(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) {
if (menuDownload != null) menuDownload.setVisible(true);
if (currentStory.canReply() && menuDm != null) menuDm.setVisible(true);
storyViewerBinding.progressView.setVisibility(View.VISIBLE);
}
@ -335,6 +339,7 @@ public final class StoryViewer extends BaseLanguageActivity {
@Override
public void onLoadError(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData, final IOException error, final boolean wasCanceled) {
if (menuDownload != null) menuDownload.setVisible(false);
if (menuDm != null) menuDm.setVisible(false);
storyViewerBinding.progressView.setVisibility(View.GONE);
}
});
@ -368,6 +373,7 @@ public final class StoryViewer extends BaseLanguageActivity {
@Override
public boolean onResourceReady(final Drawable resource, final Object model, final Target<Drawable> target, final DataSource dataSource, final boolean isFirstResource) {
if (menuDownload != null) menuDownload.setVisible(true);
if (currentStory.canReply() && menuDm != null) menuDm.setVisible(true);
storyViewerBinding.progressView.setVisibility(View.GONE);
return false;
}
@ -382,7 +388,9 @@ public final class StoryViewer extends BaseLanguageActivity {
menu.findItem(R.id.action_search).setVisible(false);
menuDownload = menu.findItem(R.id.action_download);
menuDm = menu.findItem(R.id.action_dms);
menuDownload.setVisible(true);
menuDm.setVisible(false);
menuDownload.setOnMenuItemClickListener(item -> {
if (ContextCompat.checkSelfPermission(this, Utils.PERMS[0]) == PackageManager.PERMISSION_GRANTED)
downloadStory();
@ -390,6 +398,18 @@ public final class StoryViewer extends BaseLanguageActivity {
ActivityCompat.requestPermissions(this, Utils.PERMS, 8020);
return true;
});
menuDm.setOnMenuItemClickListener(item -> {
final EditText input = new EditText(this);
input.setHint(R.string.reply_hint);
new AlertDialog.Builder(this).setTitle(R.string.reply_story)
.setView(input)
.setPositiveButton(R.string.ok, (d,w) -> {
new CommentAction().execute(input.getText().toString());
})
.setNegativeButton(R.string.cancel, null)
.show();
return true;
});
return true;
}
@ -488,6 +508,8 @@ public final class StoryViewer extends BaseLanguageActivity {
storyViewerBinding.quiz.setVisibility(quiz != null ? View.VISIBLE : View.GONE);
storyViewerBinding.quiz.setTag(quiz);
storyViewerBinding.toolbar.toolbar.setSubtitle(Utils.datetimeParser.format(new Date(currentStory.getTimestamp() * 1000L)));
releasePlayer();
final Intent intent = getIntent();
if (intent.getBooleanExtra(Constants.EXTRAS_HASHTAG, false)) {
@ -498,6 +520,8 @@ public final class StoryViewer extends BaseLanguageActivity {
}
if (itemType == MediaItemType.MEDIA_TYPE_VIDEO) setupVideo();
else setupImage();
if (settingsHelper.getBoolean(MARK_AS_SEEN)) new SeenAction().execute();
}
private void searchUsername(final String text) {
@ -535,7 +559,7 @@ public final class StoryViewer extends BaseLanguageActivity {
protected Void doInBackground(Integer... rawchoice) {
int choice = rawchoice[0];
final String url = "https://www.instagram.com/media/"+currentStory.getStoryMediaId()+"/"+poll.getId()+"/story_poll_vote/";
final String url = "https://www.instagram.com/media/"+currentStory.getStoryMediaId().split("_")[0]+"/"+poll.getId()+"/story_poll_vote/";
try {
final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST");
@ -554,7 +578,6 @@ public final class StoryViewer extends BaseLanguageActivity {
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
ok = choice;
}
else Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
urlConnection.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", "vote: " + ex);
@ -568,6 +591,7 @@ public final class StoryViewer extends BaseLanguageActivity {
poll.setMyChoice(ok);
Toast.makeText(getApplicationContext(), R.string.votef_story_poll, Toast.LENGTH_SHORT).show();
}
else Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
}
@ -578,7 +602,7 @@ public final class StoryViewer extends BaseLanguageActivity {
protected Void doInBackground(Integer... rawchoice) {
int choice = rawchoice[0];
final String cookie = settingsHelper.getString(Constants.COOKIE);
final String url = "https://i.instagram.com/api/v1/media/"+currentStory.getStoryMediaId()+"/"+quiz.getId()+"/story_quiz_answer/";
final String url = "https://i.instagram.com/api/v1/media/"+currentStory.getStoryMediaId().split("_")[0]+"/"+quiz.getId()+"/story_quiz_answer/";
try {
JSONObject ogbody = new JSONObject("{\"client_context\":\"" + UUID.randomUUID().toString()
+"\",\"mutation_token\":\"" + UUID.randomUUID().toString()
@ -604,7 +628,6 @@ public final class StoryViewer extends BaseLanguageActivity {
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
ok = choice;
}
else Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
urlConnection.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", "quiz: " + ex);
@ -618,6 +641,7 @@ public final class StoryViewer extends BaseLanguageActivity {
quiz.setMyChoice(ok);
Toast.makeText(getApplicationContext(), R.string.answered_story, Toast.LENGTH_SHORT).show();
}
else Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
}
@ -628,7 +652,7 @@ public final class StoryViewer extends BaseLanguageActivity {
protected Void doInBackground(String... rawchoice) {
final String cookie = settingsHelper.getString(Constants.COOKIE);
final String url = "https://i.instagram.com/api/v1/media/"
+currentStory.getStoryMediaId()+"/"+question.getId()+"/story_question_response/";
+currentStory.getStoryMediaId().split("_")[0]+"/"+question.getId()+"/story_question_response/";
try {
JSONObject ogbody = new JSONObject("{\"client_context\":\"" + UUID.randomUUID().toString()
+"\",\"mutation_token\":\"" + UUID.randomUUID().toString()
@ -654,7 +678,6 @@ public final class StoryViewer extends BaseLanguageActivity {
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
ok = true;
}
else Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
urlConnection.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", "respond: " + ex);
@ -667,6 +690,117 @@ public final class StoryViewer extends BaseLanguageActivity {
if (ok) {
Toast.makeText(getApplicationContext(), R.string.answered_story, Toast.LENGTH_SHORT).show();
}
else Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
}
class SeenAction extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... lmao) {
final String cookie = settingsHelper.getString(Constants.COOKIE);
final String url = "https://www.instagram.com/stories/reel/seen";
try {
String urlParameters = "reelMediaId="+currentStory.getStoryMediaId().split("_")[0]
+"&reelMediaOwnerId="+currentStory.getUserId()
+"&reelId="+currentStory.getUserId()
+"&reelMediaTakenAt="+String.valueOf(currentStory.getTimestamp())
+"&viewSeenAt="+String.valueOf(currentStory.getTimestamp());
final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("x-csrftoken",
settingsHelper.getString(Constants.COOKIE).split("csrftoken=")[1].split(";")[0]);
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
urlConnection.setRequestProperty("Content-Length", Integer.toString(urlParameters.getBytes().length));
urlConnection.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
urlConnection.connect();
urlConnection.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", "seen: " + ex);
}
return null;
}
}
class CommentAction extends AsyncTask<String, Void, Void> {
boolean ok = false;
protected Void doInBackground(String... rawAction) {
final String action = rawAction[0];
final String url = "https://i.instagram.com/api/v1/direct_v2/create_group_thread/";
final String cookie = settingsHelper.getString(Constants.COOKIE);
try {
final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
urlConnection.setUseCaches(false);
final String urlParameters = Utils.sign("{\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0]
+"\",\"_uid\":\"" + Utils.getUserIdFromCookie(cookie)
+"\",\"__uuid\":\"" + settingsHelper.getString(Constants.DEVICE_UUID)
+"\",\"recipient_users\":\"["+currentStory.getUserId() // <- string of array of number (not joking)
+"]\"}");
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
urlConnection.setRequestProperty("Content-Length", "" + Integer.toString(urlParameters.getBytes().length));
urlConnection.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
urlConnection.connect();
if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
try {
final String threadid = new JSONObject(Utils.readFromConnection(urlConnection)).getString("thread_id");
final String url2 = "https://i.instagram.com/api/v1/direct_v2/threads/broadcast/reel_share/";
final HttpURLConnection urlConnection2 = (HttpURLConnection) new URL(url2).openConnection();
urlConnection2.setRequestMethod("POST");
urlConnection2.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
urlConnection2.setUseCaches(false);
final String commentText = URLEncoder.encode(action, "UTF-8")
.replaceAll("\\+", "%20").replaceAll("\\%21", "!").replaceAll("\\%27", "'")
.replaceAll("\\%28", "(").replaceAll("\\%29", ")").replaceAll("\\%7E", "~");
final String cc = UUID.randomUUID().toString();
final String urlParameters2 = Utils.sign("{\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0]
+"\",\"_uid\":\"" + Utils.getUserIdFromCookie(cookie)
+"\",\"__uuid\":\"" + settingsHelper.getString(Constants.DEVICE_UUID)
+"\",\"client_context\":\"" + cc
+"\",\"mutation_token\":\"" + cc
+"\",\"text\":\"" + commentText
+"\",\"media_id\":\"" + currentStory.getStoryMediaId()
+"\",\"reel_id\":\"" + currentStory.getUserId()
+"\",\"thread_ids\":\"["+threadid
+"]\",\"action\":\"send_item\",\"entry\":\"reel\"}");
urlConnection2.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
urlConnection2.setRequestProperty("Content-Length", "" + Integer.toString(urlParameters2.getBytes().length));
urlConnection2.setDoOutput(true);
DataOutputStream wr2 = new DataOutputStream(urlConnection2.getOutputStream());
wr2.writeBytes(urlParameters2);
wr2.flush();
wr2.close();
urlConnection2.connect();
Log.d("austin_debug", urlConnection2.getResponseCode() + " " + urlParameters2 + " " + cookie);
if (urlConnection2.getResponseCode() == HttpURLConnection.HTTP_OK) {
ok = true;
}
urlConnection2.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", "reply (B): " + ex);
}
}
urlConnection.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", "reply (CT): " + ex);
}
return null;
}
@Override
protected void onPostExecute(Void result) {
Toast.makeText(getApplicationContext(),
ok ? R.string.answered_story : R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
}
}
}

View File

@ -42,6 +42,8 @@ public final class FeedStoriesAdapter extends RecyclerView.Adapter<HighlightView
holder.title.setText(profileModel.getUsername());
Glide.with(layoutInflater.getContext()).load(profileModel.getSdProfilePic()).into(holder.icon);
holder.icon.setAlpha(feedStoryModel.getFullyRead() ? 0.5F : 1.0F);
holder.title.setAlpha(feedStoryModel.getFullyRead() ? 0.5F : 1.0F);
}
}

View File

@ -64,8 +64,9 @@ public final class FeedStoriesFetcher extends AsyncTask<Void, Void, FeedStoryMod
null, 0, 0, 0, false, false, false, false);
final String id = node.getString("id");
final boolean fullyRead = !node.isNull("seen") && node.getLong("seen") == node.getLong("latest_reel_media");
feedStoryIDs[i] = id;
feedStoryModels[i] = new FeedStoryModel(id, profileModel);
feedStoryModels[i] = new FeedStoryModel(id, profileModel, fullyRead);
}
result = feedStoryModels;
}

View File

@ -39,9 +39,8 @@ public final class iStoryStatusFetcher extends AsyncTask<Void, Void, StoryModel[
@Override
protected StoryModel[] doInBackground(final Void... voids) {
StoryModel[] result = null;
final String url = "https://i.instagram.com/api/v1/" + (isLoc ? "locations/" : (isHashtag ? "tags/" : "feed/reels_media/?reel_ids="))
+ id + ((isLoc || isHashtag) ? "/story/" : "");
final String url = "https://i.instagram.com/api/v1/" + (isLoc ? "locations/" : (isHashtag ? "tags/" : "feed/user/"))
+ id + "/story/";
try {
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setInstanceFollowRedirects(false);
@ -50,9 +49,7 @@ public final class iStoryStatusFetcher extends AsyncTask<Void, Void, StoryModel[
conn.connect();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
JSONObject data = new JSONObject(Utils.readFromConnection(conn)).getJSONObject((isLoc || isHashtag) ? "story" : "reels");
if (!isLoc && !isHashtag && data.isNull(id)) return null;
else if (!isLoc && !isHashtag) data = data.getJSONObject(id);
JSONObject data = new JSONObject(Utils.readFromConnection(conn)).getJSONObject((isLoc || isHashtag) ? "story" : "reel");
JSONArray media;
if ((media = data.optJSONArray("items")) != null && media.length() > 0 &&
@ -65,11 +62,12 @@ public final class iStoryStatusFetcher extends AsyncTask<Void, Void, StoryModel[
data = media.getJSONObject(i);
final boolean isVideo = data.has("video_duration");
models[i] = new StoryModel(data.getString("pk"),
models[i] = new StoryModel(data.getString("id"),
data.getJSONObject("image_versions2").getJSONArray("candidates").getJSONObject(0).getString("url"),
isVideo ? MediaItemType.MEDIA_TYPE_VIDEO : MediaItemType.MEDIA_TYPE_IMAGE,
data.optLong("taken_at", 0),
(isLoc || isHashtag) ? data.getJSONObject("user").getString("username") : username);
(isLoc || isHashtag) ? data.getJSONObject("user").getString("username") : username,
data.getJSONObject("user").getString("pk"), data.getBoolean("can_reply"));
final JSONArray videoResources = data.optJSONArray("video_versions");
if (isVideo && videoResources != null)

View File

@ -40,6 +40,7 @@ import static awais.instagrabber.utils.Constants.BOTTOM_TOOLBAR;
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;
import static awais.instagrabber.utils.Constants.MARK_AS_SEEN;
import static awais.instagrabber.utils.Constants.MUTED_VIDEOS;
import static awais.instagrabber.utils.Constants.SHOW_FEED;
import static awais.instagrabber.utils.Utils.settingsHelper;
@ -121,11 +122,13 @@ public final class SettingsDialog extends BottomSheetDialogFragment implements V
final AppCompatCheckBox cbAutoloadPosts = contentView.findViewById(R.id.cbAutoloadPosts);
final AppCompatCheckBox cbAutoplayVideos = contentView.findViewById(R.id.cbAutoplayVideos);
final AppCompatCheckBox cbDownloadUsername = contentView.findViewById(R.id.cbDownloadUsername);
final AppCompatCheckBox cbMarkAsSeen = contentView.findViewById(R.id.cbMarkAsSeen);
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));
cbAutoloadPosts.setChecked(settingsHelper.getBoolean(AUTOLOAD_POSTS));
cbDownloadUsername.setChecked(settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER));
@ -136,6 +139,7 @@ public final class SettingsDialog extends BottomSheetDialogFragment implements V
setupListener(cbAutoloadPosts);
setupListener(cbAutoplayVideos);
setupListener(cbDownloadUsername);
setupListener(cbMarkAsSeen);
btnSaveTo.setEnabled(cbSaveTo.isChecked());
@ -203,6 +207,7 @@ public final class SettingsDialog extends BottomSheetDialogFragment implements V
else if (id == R.id.cbAutoplayVideos) settingsHelper.putBoolean(AUTOPLAY_VIDEOS, checked);
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.cbSaveTo) {
settingsHelper.putBoolean(FOLDER_SAVE_TO, checked);
btnSaveTo.setEnabled(checked);

View File

@ -28,14 +28,6 @@ public abstract class BasePostModel implements Serializable {
return bookmarked;
}
public boolean setLike(final boolean like) {
liked = like; return liked;
}
public boolean setBookmark(final boolean bookmark) {
bookmarked = bookmark; return bookmarked;
}
public MediaItemType getItemType() {
return itemType;
}

View File

@ -6,10 +6,12 @@ public final class FeedStoryModel implements Serializable {
private final String storyMediaId;
private final ProfileModel profileModel;
private StoryModel[] storyModels;
private boolean fullyRead;
public FeedStoryModel(final String storyMediaId, final ProfileModel profileModel) {
public FeedStoryModel(final String storyMediaId, final ProfileModel profileModel, final boolean fullyRead) {
this.storyMediaId = storyMediaId;
this.profileModel = profileModel;
this.fullyRead = fullyRead;
}
public String getStoryMediaId() {
@ -27,4 +29,8 @@ public final class FeedStoryModel implements Serializable {
public StoryModel[] getStoryModels() {
return storyModels;
}
public boolean getFullyRead() {
return fullyRead;
}
}

View File

@ -8,7 +8,7 @@ import awais.instagrabber.models.stickers.QuestionModel;
import awais.instagrabber.models.stickers.QuizModel;
public final class StoryModel implements Serializable {
private final String storyMediaId, storyUrl, username;
private final String storyMediaId, storyUrl, username, userId;
private final MediaItemType itemType;
private final long timestamp;
private String videoUrl, tappableShortCode, tappableId, spotify;
@ -17,14 +17,17 @@ public final class StoryModel implements Serializable {
private QuizModel quiz;
private String[] mentions;
private int position;
private boolean isCurrentSlide = false;
private boolean isCurrentSlide = false, canReply = false;
public StoryModel(final String storyMediaId, final String storyUrl, final MediaItemType itemType, final long timestamp, final String username) {
public StoryModel(final String storyMediaId, final String storyUrl, final MediaItemType itemType,
final long timestamp, final String username, final String userId, final boolean canReply) {
this.storyMediaId = storyMediaId;
this.storyUrl = storyUrl;
this.itemType = itemType;
this.timestamp = timestamp;
this.username = username;
this.userId = userId;
this.canReply = canReply;
}
public String getStoryUrl() {
@ -115,7 +118,15 @@ public final class StoryModel implements Serializable {
return isCurrentSlide;
}
public boolean canReply() {
return canReply;
}
public String getUsername() {
return username;
}
public String getUserId() {
return userId;
}
}

View File

@ -19,6 +19,7 @@ public final class Constants {
public static final String AUTOLOAD_POSTS = "autoload_posts";
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";
// never Export
public static final String COOKIE = "cookie";
public static final String SHOW_QUICK_ACCESS_DIALOG = "show_quick_dlg";

View File

@ -166,7 +166,7 @@ public final class DataBox extends SQLiteOpenHelper {
try (final SQLiteDatabase db = getReadableDatabase();
final Cursor cursor = db.rawQuery("SELECT query_text, date_added FROM favorites WHERE "
+KEY_QUERY_DISPLAY+"='"+query+"' ORDER BY date_added DESC", null)) {
+KEY_QUERY_TEXT+"='"+query+"' ORDER BY date_added DESC", null)) {
if (cursor != null && cursor.moveToFirst()) {
return cursor.getString(0) + "/" + String.valueOf(cursor.getLong(1));
}

View File

@ -22,6 +22,7 @@ import static awais.instagrabber.utils.Constants.DEVICE_UUID;
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;
import static awais.instagrabber.utils.Constants.MARK_AS_SEEN;
import static awais.instagrabber.utils.Constants.MUTED_VIDEOS;
import static awais.instagrabber.utils.Constants.PREV_INSTALL_VERSION;
import static awais.instagrabber.utils.Constants.SHOW_QUICK_ACCESS_DIALOG;
@ -106,7 +107,7 @@ 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})
AUTOLOAD_POSTS, CUSTOM_DATE_TIME_FORMAT_ENABLED, MARK_AS_SEEN})
public @interface BooleanSettings {}
@StringDef({APP_THEME, APP_LANGUAGE, PREV_INSTALL_VERSION})

View File

@ -1224,7 +1224,8 @@ public final class Utils {
storyModels[j] = new StoryModel(data.getString(Constants.EXTRAS_ID), data.getString("display_url"),
isVideo ? MediaItemType.MEDIA_TYPE_VIDEO : MediaItemType.MEDIA_TYPE_IMAGE,
data.getLong("taken_at_timestamp"), data.getJSONObject("owner").getString("username"));
data.getLong("taken_at_timestamp"), data.getJSONObject("owner").getString("username"),
data.getJSONObject("owner").getString("id"), data.getBoolean("can_reply"));
if (isVideo && data.has("video_resources"))
storyModels[j].setVideoUrl(Utils.getHighQualityPost(data.getJSONArray("video_resources"), true, false, false));

View File

@ -5,8 +5,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".activities.CommentsViewer"
android:weightSum="8">
tools:context=".activities.CommentsViewer">
<include
android:id="@+id/toolbar"

View File

@ -1,19 +1,56 @@
<?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="match_parent"
android:orientation="vertical"
tools:context=".activities.DirectMessages">
<include
android:id="@+id/toolbar"
android:layout_weight="1"
layout="@layout/layout_include_toolbar" />
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_weight="8"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvDirectMessages"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/commentText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="2"
android:paddingLeft="8dp"
android:gravity="bottom"
android:hint="@string/dm_hint"
android:inputType="textMultiLine"
android:maxLength="2200"
android:maxLines="10"
android:visibility="gone"
android:scrollHorizontally="false" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/commentSend"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?selectableItemBackgroundBorderless"
android:clickable="true"
android:paddingRight="8dp"
android:visibility="gone"
app:srcCompat="@android:drawable/ic_menu_send" />
</LinearLayout>
</LinearLayout>

View File

@ -259,6 +259,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/cbMarkAsSeen"
android:layout_width="30dp"
android:layout_height="30dp"
android:contentDescription="@string/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/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"

View File

@ -9,6 +9,7 @@
<string name="action_setting">Settings (v%s)</string>
<string name="action_settings">Settings</string>
<string name="action_download">Download</string>
<string name="action_seen">Mark as Seen</string>
<string name="action_github" translatable="false">GitHub</string>
<string name="action_fdroid" translatable="false">F-Droid</string>
<string name="action_search">Search username…</string>
@ -37,6 +38,7 @@
<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="error_loading_profile">Error loading profile!\nTry logging in and search again.</string>
<string name="error_creating_folders">Error creating Download folder(s).</string>
<string name="show_feed">Show user feed (Works only when user is logged in)</string>
@ -71,6 +73,8 @@
<string name="respond_story">Respond</string>
<string name="answer_hint">Answer...</string>
<string name="answered_story">Answer successful!</string>
<string name="reply_story">Reply to story</string>
<string name="reply_hint">Reply...</string>
<string name="story_quiz">Quiz</string>
<string name="story_quizzed">You have already answered!</string>
<string name="story_mentions">Mentions</string>
@ -189,6 +193,7 @@
<string name="login_success_loading_cookies">Successfully loaded cookies!\nIf you still can\'t open private pages/posts, re-login!</string>
<string name="comment_hint">Write a new comment...</string>
<string name="dm_hint">Write a new message...</string>
<string name="liked_notif">Liked your post</string>
<string name="comment_notif">Commented on your post:</string>

View File

@ -0,0 +1,8 @@
* You can now send text DMs
* You can now reply DM to stories
* DM title and (some) members are now shown on DM screen
* Seen stories (API) are now displayed transparently
* Story creation date is now shown on the story screen
* A new setting allows you to mark stories as seen (Default disabled)
* Fixed a bug related to the favourite button on profile
* Fixed a bug where liked/saved status does not carry over across slides