Browse Source
Merge pull request #57 from ammargitham/feature/share-photos-dm
Merge pull request #57 from ammargitham/feature/share-photos-dm
Feature/share photos dmlegacy
Austin Huang
4 years ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 1907 additions and 1119 deletions
-
2.gitignore
-
7app/build.gradle
-
11app/src/main/AndroidManifest.xml
-
11app/src/main/java/awais/instagrabber/InstaApp.java
-
118app/src/main/java/awais/instagrabber/activities/DirectMessages.java
-
73app/src/main/java/awais/instagrabber/activities/DirectMessagesActivity.java
-
269app/src/main/java/awais/instagrabber/activities/DirectMessagesUserInbox.java
-
31app/src/main/java/awais/instagrabber/activities/Main.java
-
54app/src/main/java/awais/instagrabber/adapters/DirectMessageInboxAdapter.java
-
131app/src/main/java/awais/instagrabber/adapters/DirectMessagesAdapter.java
-
400app/src/main/java/awais/instagrabber/adapters/MessageItemsAdapter.java
-
107app/src/main/java/awais/instagrabber/adapters/viewholder/DirectMessageInboxItemViewHolder.java
-
43app/src/main/java/awais/instagrabber/adapters/viewholder/DirectMessageViewHolder.java
-
383app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageViewHolder.java
-
91app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/TextMessageViewHolder.java
-
176app/src/main/java/awais/instagrabber/asyncs/ImageUploader.java
-
32app/src/main/java/awais/instagrabber/asyncs/direct_messages/DirectMessageInboxThreadFetcher.java
-
207app/src/main/java/awais/instagrabber/asyncs/direct_messages/DirectThreadBroadcaster.java
-
149app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java
-
351app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java
-
62app/src/main/java/awais/instagrabber/models/ImageUploadOptions.java
-
24app/src/main/java/awais/instagrabber/models/ProfileModel.java
-
18app/src/main/java/awais/instagrabber/models/direct_messages/InboxThreadModel.java
-
14app/src/main/java/awais/instagrabber/models/enums/UserInboxDirection.java
-
53app/src/main/java/awais/instagrabber/utils/Utils.java
-
10app/src/main/res/drawable/ic_image_24.xml
-
10app/src/main/res/drawable/ic_send_24.xml
-
54app/src/main/res/layout/activity_direct_messages.xml
-
10app/src/main/res/layout/fragment_direct_messages_inbox.xml
-
48app/src/main/res/layout/fragment_direct_messages_thread.xml
-
3app/src/main/res/layout/item_message_item.xml
-
26app/src/main/res/navigation/direct_messages_nav_graph.xml
-
3app/src/main/res/values/strings.xml
-
3build.gradle
@ -1,118 +0,0 @@ |
|||||
package awais.instagrabber.activities; |
|
||||
|
|
||||
import android.content.Intent; |
|
||||
import android.os.AsyncTask; |
|
||||
import android.os.Bundle; |
|
||||
import android.util.Log; |
|
||||
|
|
||||
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; |
|
||||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader; |
|
||||
import awais.instagrabber.databinding.ActivityDmsBinding; |
|
||||
import awais.instagrabber.interfaces.FetchListener; |
|
||||
import awais.instagrabber.models.direct_messages.InboxModel; |
|
||||
import awais.instagrabber.models.direct_messages.InboxThreadModel; |
|
||||
import awais.instagrabber.utils.Constants; |
|
||||
import awais.instagrabber.utils.Utils; |
|
||||
|
|
||||
public final class DirectMessages extends BaseLanguageActivity implements SwipeRefreshLayout.OnRefreshListener { |
|
||||
private final ArrayList<InboxThreadModel> inboxThreadModelList = new ArrayList<>(); |
|
||||
private final DirectMessagesAdapter messagesAdapter = new DirectMessagesAdapter(inboxThreadModelList, v -> { |
|
||||
final Object tag = v.getTag(); |
|
||||
if (tag instanceof InboxThreadModel) { |
|
||||
startActivity(new Intent(this, DirectMessagesUserInbox.class) |
|
||||
.putExtra(Constants.EXTRAS_THREAD_MODEL, (InboxThreadModel) tag) |
|
||||
); |
|
||||
} |
|
||||
}); |
|
||||
private final FetchListener<InboxModel> fetchListener = new FetchListener<InboxModel>() { |
|
||||
@Override |
|
||||
public void doBefore() { |
|
||||
dmsBinding.swipeRefreshLayout.setRefreshing(true); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onResult(final InboxModel inboxModel) { |
|
||||
if (inboxModel != null) { |
|
||||
endCursor = inboxModel.getOldestCursor(); |
|
||||
if ("MINCURSOR".equals(endCursor) || "MAXCURSOR".equals(endCursor)) endCursor = null; |
|
||||
// todo get request / unseen count from inboxModel |
|
||||
|
|
||||
final InboxThreadModel[] threads = inboxModel.getThreads(); |
|
||||
if (threads != null && threads.length > 0) { |
|
||||
final int oldSize = inboxThreadModelList.size(); |
|
||||
inboxThreadModelList.addAll(Arrays.asList(threads)); |
|
||||
|
|
||||
messagesAdapter.notifyItemRangeInserted(oldSize, threads.length); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
dmsBinding.swipeRefreshLayout.setRefreshing(false); |
|
||||
stopCurrentExecutor(); |
|
||||
} |
|
||||
}; |
|
||||
private String endCursor; |
|
||||
private RecyclerLazyLoader lazyLoader; |
|
||||
private AsyncTask<Void, Void, InboxModel> currentlyRunning; |
|
||||
private ActivityDmsBinding dmsBinding; |
|
||||
|
|
||||
@Override |
|
||||
protected void onCreate(@Nullable final Bundle savedInstanceState) { |
|
||||
super.onCreate(savedInstanceState); |
|
||||
dmsBinding = ActivityDmsBinding.inflate(getLayoutInflater()); |
|
||||
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); |
|
||||
dmsBinding.rvDirectMessages.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL)); |
|
||||
dmsBinding.rvDirectMessages.setAdapter(messagesAdapter); |
|
||||
|
|
||||
lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> { |
|
||||
if (!Utils.isEmpty(endCursor)) |
|
||||
currentlyRunning = new InboxFetcher(endCursor, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
|
||||
endCursor = null; |
|
||||
}); |
|
||||
|
|
||||
dmsBinding.rvDirectMessages.addOnScrollListener(lazyLoader); |
|
||||
|
|
||||
stopCurrentExecutor(); |
|
||||
currentlyRunning = new InboxFetcher(null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onRefresh() { |
|
||||
endCursor = null; |
|
||||
lazyLoader.resetState(); |
|
||||
inboxThreadModelList.clear(); |
|
||||
messagesAdapter.notifyDataSetChanged(); |
|
||||
|
|
||||
stopCurrentExecutor(); |
|
||||
currentlyRunning = new InboxFetcher(null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
|
||||
} |
|
||||
|
|
||||
private void stopCurrentExecutor() { |
|
||||
if (currentlyRunning != null) { |
|
||||
try { |
|
||||
currentlyRunning.cancel(true); |
|
||||
} catch (final Exception e) { |
|
||||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,73 @@ |
|||||
|
package awais.instagrabber.activities; |
||||
|
|
||||
|
import android.os.Bundle; |
||||
|
import android.widget.TextView; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
import androidx.annotation.Nullable; |
||||
|
import androidx.appcompat.widget.Toolbar; |
||||
|
import androidx.coordinatorlayout.widget.CoordinatorLayout; |
||||
|
import androidx.navigation.NavController; |
||||
|
import androidx.navigation.NavDestination; |
||||
|
import androidx.navigation.Navigation; |
||||
|
import androidx.navigation.ui.AppBarConfiguration; |
||||
|
import androidx.navigation.ui.NavigationUI; |
||||
|
|
||||
|
import awais.instagrabber.R; |
||||
|
import awais.instagrabber.databinding.ActivityDirectMessagesBinding; |
||||
|
import awais.instagrabber.fragments.directmessages.DirectMessageThreadFragmentArgs; |
||||
|
|
||||
|
public class DirectMessagesActivity extends BaseLanguageActivity implements NavController.OnDestinationChangedListener { |
||||
|
|
||||
|
private TextView toolbarTitle; |
||||
|
|
||||
|
@Override |
||||
|
protected void onCreate(Bundle savedInstanceState) { |
||||
|
super.onCreate(savedInstanceState); |
||||
|
final ActivityDirectMessagesBinding binding = ActivityDirectMessagesBinding.inflate(getLayoutInflater()); |
||||
|
final CoordinatorLayout root = binding.getRoot(); |
||||
|
setContentView(root); |
||||
|
|
||||
|
toolbarTitle = binding.toolbarTitle; |
||||
|
|
||||
|
final Toolbar toolbar = binding.toolbar; |
||||
|
setSupportActionBar(toolbar); |
||||
|
|
||||
|
final NavController navController = Navigation.findNavController(this, R.id.direct_messages_nav_host_fragment); |
||||
|
navController.addOnDestinationChangedListener(this); |
||||
|
final AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build(); |
||||
|
NavigationUI.setupWithNavController(toolbar, navController, appBarConfiguration); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onDestinationChanged(@NonNull final NavController controller, |
||||
|
@NonNull final NavDestination destination, |
||||
|
@Nullable final Bundle arguments) { |
||||
|
switch (destination.getId()) { |
||||
|
case R.id.directMessagesInboxFragment: |
||||
|
setToolbarTitle(R.string.action_dms); |
||||
|
return; |
||||
|
case R.id.directMessagesThreadFragment: |
||||
|
if (arguments == null) { |
||||
|
return; |
||||
|
} |
||||
|
final String title = DirectMessageThreadFragmentArgs.fromBundle(arguments).getTitle(); |
||||
|
setToolbarTitle(title); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void setToolbarTitle(final String text) { |
||||
|
if (toolbarTitle == null) { |
||||
|
return; |
||||
|
} |
||||
|
toolbarTitle.setText(text); |
||||
|
} |
||||
|
|
||||
|
private void setToolbarTitle(final int resourceId) { |
||||
|
if (toolbarTitle == null) { |
||||
|
return; |
||||
|
} |
||||
|
toolbarTitle.setText(resourceId); |
||||
|
} |
||||
|
} |
@ -1,269 +0,0 @@ |
|||||
package awais.instagrabber.activities; |
|
||||
|
|
||||
import android.content.Intent; |
|
||||
import android.net.Uri; |
|
||||
import android.os.AsyncTask; |
|
||||
import android.os.Bundle; |
|
||||
import android.util.Log; |
|
||||
import android.view.View; |
|
||||
import android.widget.Toast; |
|
||||
|
|
||||
import androidx.annotation.Nullable; |
|
||||
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.customviews.helpers.RecyclerLazyLoader; |
|
||||
import awais.instagrabber.databinding.ActivityDmsBinding; |
|
||||
import awais.instagrabber.interfaces.FetchListener; |
|
||||
import awais.instagrabber.models.PostModel; |
|
||||
import awais.instagrabber.models.ProfileModel; |
|
||||
import awais.instagrabber.models.StoryModel; |
|
||||
import awais.instagrabber.models.direct_messages.DirectItemModel; |
|
||||
import awais.instagrabber.models.direct_messages.InboxThreadModel; |
|
||||
import awais.instagrabber.models.enums.DirectItemType; |
|
||||
import awais.instagrabber.models.enums.DownloadMethod; |
|
||||
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 BaseLanguageActivity { |
|
||||
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() { |
|
||||
dmsBinding.swipeRefreshLayout.setRefreshing(true); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onResult(final InboxThreadModel result) { |
|
||||
if (result == null && ("MINCURSOR".equals(endCursor) || "MAXCURSOR".equals(endCursor) || Utils.isEmpty(endCursor))) |
|
||||
Toast.makeText(DirectMessagesUserInbox.this, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); |
|
||||
|
|
||||
if (result != null) { |
|
||||
endCursor = result.getPrevCursor(); |
|
||||
if ("MINCURSOR".equals(endCursor) || "MAXCURSOR".equals(endCursor)) endCursor = null; |
|
||||
|
|
||||
users.clear(); |
|
||||
users.addAll(Arrays.asList(result.getUsers())); |
|
||||
|
|
||||
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); |
|
||||
messageItemsAdapter.notifyItemRangeInserted(oldSize, itemModels.size()); |
|
||||
} |
|
||||
|
|
||||
dmsBinding.swipeRefreshLayout.setRefreshing(false); |
|
||||
} |
|
||||
}; |
|
||||
private String endCursor; |
|
||||
private ActivityDmsBinding dmsBinding; |
|
||||
private MessageItemsAdapter messageItemsAdapter; |
|
||||
|
|
||||
@Override |
|
||||
protected void onCreate(@Nullable final Bundle savedInstanceState) { |
|
||||
super.onCreate(savedInstanceState); |
|
||||
dmsBinding = ActivityDmsBinding.inflate(getLayoutInflater()); |
|
||||
setContentView(dmsBinding.getRoot()); |
|
||||
|
|
||||
final InboxThreadModel threadModel; |
|
||||
final Intent intent = getIntent(); |
|
||||
if (intent == null || !intent.hasExtra(Constants.EXTRAS_THREAD_MODEL) || |
|
||||
(threadModel = (InboxThreadModel) intent.getSerializableExtra(Constants.EXTRAS_THREAD_MODEL)) == null) { |
|
||||
Utils.errorFinish(this); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
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); |
|
||||
|
|
||||
dmsBinding.rvDirectMessages.addOnScrollListener(new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> { |
|
||||
if (!Utils.isEmpty(endCursor)) { |
|
||||
new UserInboxFetcher(threadModel.getThreadId(), UserInboxDirection.OLDER, |
|
||||
endCursor, fetchListener).execute(); // serial because we don't want messages to be randomly ordered |
|
||||
} |
|
||||
})); |
|
||||
|
|
||||
messageItemsAdapter = new MessageItemsAdapter(directItemModels, users, leftusers, v -> { |
|
||||
Object tag = v.getTag(); |
|
||||
if (tag instanceof DirectItemModel) { |
|
||||
directItemModel = (DirectItemModel) tag; |
|
||||
final DirectItemType itemType = directItemModel.getItemType(); |
|
||||
switch (itemType) { |
|
||||
case MEDIA_SHARE: |
|
||||
startActivity(new Intent(this, PostViewer.class) |
|
||||
.putExtra(Constants.EXTRAS_POST, new PostModel(directItemModel.getMediaModel().getCode(), false))); |
|
||||
break; |
|
||||
case LINK: |
|
||||
Intent linkIntent = new Intent(Intent.ACTION_VIEW); |
|
||||
linkIntent.setData(Uri.parse(directItemModel.getLinkModel().getLinkContext().getLinkUrl())); |
|
||||
startActivity(linkIntent); |
|
||||
break; |
|
||||
case TEXT: |
|
||||
case REEL_SHARE: |
|
||||
Utils.copyText(v.getContext(), directItemModel.getText()); |
|
||||
Toast.makeText(v.getContext(), R.string.clipboard_copied, Toast.LENGTH_SHORT).show(); |
|
||||
break; |
|
||||
case RAVEN_MEDIA: |
|
||||
case MEDIA: |
|
||||
Utils.dmDownload(this, getUser(directItemModel.getUserId()).getUsername(), DownloadMethod.DOWNLOAD_DIRECT, |
|
||||
Collections.singletonList(itemType == DirectItemType.MEDIA ? directItemModel.getMediaModel() : directItemModel.getRavenMediaModel().getMedia())); |
|
||||
Toast.makeText(v.getContext(), R.string.downloader_downloading_media, Toast.LENGTH_SHORT).show(); |
|
||||
break; |
|
||||
case STORY_SHARE: |
|
||||
if (directItemModel.getReelShare() != null) { |
|
||||
StoryModel sm = new StoryModel( |
|
||||
directItemModel.getReelShare().getReelId(), |
|
||||
directItemModel.getReelShare().getMedia().getVideoUrl(), |
|
||||
directItemModel.getReelShare().getMedia().getMediaType(), |
|
||||
directItemModel.getTimestamp(), |
|
||||
directItemModel.getReelShare().getReelOwnerName(), |
|
||||
String.valueOf(directItemModel.getReelShare().getReelOwnerId()), |
|
||||
false |
|
||||
); |
|
||||
sm.setVideoUrl(directItemModel.getReelShare().getMedia().getVideoUrl()); |
|
||||
StoryModel[] sms = {sm}; |
|
||||
startActivity(new Intent(this, StoryViewer.class) |
|
||||
.putExtra(Constants.EXTRAS_USERNAME, directItemModel.getReelShare().getReelOwnerName()) |
|
||||
.putExtra(Constants.EXTRAS_STORIES, sms) |
|
||||
); |
|
||||
} |
|
||||
else if (directItemModel.getText() != null && directItemModel.getText().toString().contains("@")) { |
|
||||
searchUsername(directItemModel.getText().toString().split("@")[1].split(" ")[0]); |
|
||||
} |
|
||||
break; |
|
||||
case PLACEHOLDER: |
|
||||
if (directItemModel.getText().toString().contains("@")) |
|
||||
searchUsername(directItemModel.getText().toString().split("@")[1].split(" ")[0]); |
|
||||
break; |
|
||||
default: |
|
||||
Log.d("austin_debug", "unsupported type "+itemType); |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
(view, text, isHashtag) -> { |
|
||||
searchUsername(text); |
|
||||
}); |
|
||||
|
|
||||
dmsBinding.rvDirectMessages.setAdapter(messageItemsAdapter); |
|
||||
|
|
||||
new UserInboxFetcher(threadModel.getThreadId(), UserInboxDirection.OLDER, null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
|
||||
} |
|
||||
|
|
||||
@Nullable |
|
||||
private ProfileModel getUser(final long userId) { |
|
||||
if (users != null) { |
|
||||
ProfileModel result = myProfileHolder; |
|
||||
for (final ProfileModel user : users) { |
|
||||
if (Long.toString(userId).equals(user.getId())) result = user; |
|
||||
} |
|
||||
if (leftusers != null) |
|
||||
for (final ProfileModel leftuser : leftusers) { |
|
||||
if (Long.toString(userId).equals(leftuser.getId())) result = leftuser; |
|
||||
} |
|
||||
return result; |
|
||||
} |
|
||||
return null; |
|
||||
} |
|
||||
|
|
||||
private void searchUsername(final String text) { |
|
||||
startActivity(new Intent(getApplicationContext(), ProfileViewer.class).putExtra(Constants.EXTRAS_USERNAME, text)); |
|
||||
} |
|
||||
|
|
||||
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 url2 = "https://i.instagram.com/api/v1/direct_v2/threads/broadcast/text/"; |
|
||||
final String cookie = settingsHelper.getString(Constants.COOKIE); |
|
||||
try { |
|
||||
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); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,54 @@ |
|||||
|
package awais.instagrabber.adapters; |
||||
|
|
||||
|
import android.view.LayoutInflater; |
||||
|
import android.view.ViewGroup; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
import androidx.recyclerview.widget.DiffUtil; |
||||
|
import androidx.recyclerview.widget.ListAdapter; |
||||
|
|
||||
|
import awais.instagrabber.adapters.viewholder.DirectMessageInboxItemViewHolder; |
||||
|
import awais.instagrabber.databinding.LayoutIncludeSimpleItemBinding; |
||||
|
import awais.instagrabber.models.direct_messages.InboxThreadModel; |
||||
|
|
||||
|
public final class DirectMessageInboxAdapter extends ListAdapter<InboxThreadModel, DirectMessageInboxItemViewHolder> { |
||||
|
private final OnItemClickListener onClickListener; |
||||
|
|
||||
|
private static final DiffUtil.ItemCallback<InboxThreadModel> diffCallback = new DiffUtil.ItemCallback<InboxThreadModel>() { |
||||
|
@Override |
||||
|
public boolean areItemsTheSame(@NonNull final InboxThreadModel oldItem, @NonNull final InboxThreadModel newItem) { |
||||
|
return oldItem.getThreadId().equals(newItem.getThreadId()); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean areContentsTheSame(@NonNull final InboxThreadModel oldItem, @NonNull final InboxThreadModel newItem) { |
||||
|
return oldItem.equals(newItem); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
public DirectMessageInboxAdapter(final OnItemClickListener onClickListener) { |
||||
|
super(diffCallback); |
||||
|
this.onClickListener = onClickListener; |
||||
|
} |
||||
|
|
||||
|
@NonNull |
||||
|
@Override |
||||
|
public DirectMessageInboxItemViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) { |
||||
|
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); |
||||
|
final LayoutIncludeSimpleItemBinding binding = LayoutIncludeSimpleItemBinding.inflate(layoutInflater, parent, false); |
||||
|
return new DirectMessageInboxItemViewHolder(binding); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onBindViewHolder(@NonNull final DirectMessageInboxItemViewHolder holder, final int position) { |
||||
|
final InboxThreadModel threadModel = getItem(position); |
||||
|
if (onClickListener != null) { |
||||
|
holder.itemView.setOnClickListener((v) -> onClickListener.onItemClick(threadModel)); |
||||
|
} |
||||
|
holder.bind(threadModel); |
||||
|
} |
||||
|
|
||||
|
public interface OnItemClickListener { |
||||
|
void onItemClick(final InboxThreadModel inboxThreadModel); |
||||
|
} |
||||
|
} |
@ -1,131 +0,0 @@ |
|||||
package awais.instagrabber.adapters; |
|
||||
|
|
||||
import android.content.Context; |
|
||||
import android.view.LayoutInflater; |
|
||||
import android.view.View; |
|
||||
import android.view.ViewGroup; |
|
||||
|
|
||||
import androidx.annotation.NonNull; |
|
||||
import androidx.core.text.HtmlCompat; |
|
||||
import androidx.recyclerview.widget.RecyclerView; |
|
||||
|
|
||||
import com.bumptech.glide.Glide; |
|
||||
import com.bumptech.glide.RequestManager; |
|
||||
|
|
||||
import java.util.ArrayList; |
|
||||
|
|
||||
import awais.instagrabber.R; |
|
||||
import awais.instagrabber.adapters.viewholder.DirectMessageViewHolder; |
|
||||
import awais.instagrabber.models.ProfileModel; |
|
||||
import awais.instagrabber.models.direct_messages.DirectItemModel; |
|
||||
import awais.instagrabber.models.direct_messages.DirectItemModel.DirectItemActionLogModel; |
|
||||
import awais.instagrabber.models.direct_messages.DirectItemModel.DirectItemReelShareModel; |
|
||||
import awais.instagrabber.models.direct_messages.InboxThreadModel; |
|
||||
import awais.instagrabber.models.enums.DirectItemType; |
|
||||
|
|
||||
public final class DirectMessagesAdapter extends RecyclerView.Adapter<DirectMessageViewHolder> { |
|
||||
private final ArrayList<InboxThreadModel> inboxThreadModels; |
|
||||
private final View.OnClickListener onClickListener; |
|
||||
private LayoutInflater layoutInflater; |
|
||||
|
|
||||
public DirectMessagesAdapter(final ArrayList<InboxThreadModel> inboxThreadModels, final View.OnClickListener onClickListener) { |
|
||||
this.inboxThreadModels = inboxThreadModels; |
|
||||
this.onClickListener = onClickListener; |
|
||||
} |
|
||||
|
|
||||
@NonNull |
|
||||
@Override |
|
||||
public DirectMessageViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) { |
|
||||
if (layoutInflater == null) layoutInflater = LayoutInflater.from(parent.getContext()); |
|
||||
return new DirectMessageViewHolder(layoutInflater.inflate(R.layout.layout_include_simple_item, parent, false), |
|
||||
onClickListener); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onBindViewHolder(@NonNull final DirectMessageViewHolder holder, final int position) { |
|
||||
final InboxThreadModel threadModel = inboxThreadModels.get(position); |
|
||||
final DirectItemModel[] itemModels; |
|
||||
|
|
||||
holder.itemView.setTag(threadModel); |
|
||||
|
|
||||
final RequestManager glideRequestManager = Glide.with(holder.itemView); |
|
||||
|
|
||||
if (threadModel != null && (itemModels = threadModel.getItems()) != null) { |
|
||||
final ProfileModel[] users = threadModel.getUsers(); |
|
||||
|
|
||||
if (users.length > 1) { |
|
||||
holder.ivProfilePic.setVisibility(View.GONE); |
|
||||
holder.multipleProfilePicsContainer.setVisibility(View.VISIBLE); |
|
||||
|
|
||||
for (int i = 0; i < Math.min(3, users.length); ++i) |
|
||||
glideRequestManager.load(users[i].getSdProfilePic()).into(holder.multipleProfilePics[i]); |
|
||||
|
|
||||
} else { |
|
||||
holder.ivProfilePic.setVisibility(View.VISIBLE); |
|
||||
holder.multipleProfilePicsContainer.setVisibility(View.GONE); |
|
||||
|
|
||||
glideRequestManager.load(users.length == 1 ? users[0].getSdProfilePic() : null).into(holder.ivProfilePic); |
|
||||
} |
|
||||
|
|
||||
holder.tvUsername.setText(threadModel.getThreadTitle()); |
|
||||
|
|
||||
final DirectItemModel lastItemModel = itemModels[itemModels.length - 1]; |
|
||||
final DirectItemType itemType = lastItemModel.getItemType(); |
|
||||
|
|
||||
holder.notTextType.setVisibility(itemType != DirectItemType.TEXT ? View.VISIBLE : View.GONE); |
|
||||
|
|
||||
final Context context = layoutInflater.getContext(); |
|
||||
|
|
||||
final CharSequence messageText; |
|
||||
switch (itemType) { |
|
||||
case TEXT: |
|
||||
case LIKE: |
|
||||
messageText = lastItemModel.getText(); |
|
||||
break; |
|
||||
case LINK: |
|
||||
messageText = context.getString(R.string.direct_messages_sent_link); |
|
||||
break; |
|
||||
case MEDIA: |
|
||||
case MEDIA_SHARE: |
|
||||
messageText = context.getString(R.string.direct_messages_sent_media); |
|
||||
break; |
|
||||
case ACTION_LOG: |
|
||||
final DirectItemActionLogModel logModel = lastItemModel.getActionLogModel(); |
|
||||
messageText = logModel != null ? logModel.getDescription() : "..."; |
|
||||
break; |
|
||||
case REEL_SHARE: |
|
||||
final DirectItemReelShareModel reelShare = lastItemModel.getReelShare(); |
|
||||
if (reelShare == null) |
|
||||
messageText = context.getString(R.string.direct_messages_sent_media); |
|
||||
else { |
|
||||
final String reelType = reelShare.getType(); |
|
||||
final int textRes; |
|
||||
if ("reply".equals(reelType)) |
|
||||
textRes = R.string.direct_messages_replied_story; |
|
||||
else if ("mention".equals(reelType)) |
|
||||
textRes = R.string.direct_messages_mention_story; |
|
||||
else if ("reaction".equals(reelType)) |
|
||||
textRes = R.string.direct_messages_reacted_story; |
|
||||
else textRes = R.string.direct_messages_sent_media; |
|
||||
|
|
||||
messageText = context.getString(textRes) + " : " + reelShare.getText(); |
|
||||
} |
|
||||
break; |
|
||||
case RAVEN_MEDIA: |
|
||||
messageText = context.getString(R.string.direct_messages_sent_media); |
|
||||
break; |
|
||||
default: |
|
||||
messageText = "<i>Unsupported message</i>"; |
|
||||
} |
|
||||
|
|
||||
holder.tvMessage.setText(HtmlCompat.fromHtml(messageText.toString(), 63)); |
|
||||
|
|
||||
holder.tvDate.setText(lastItemModel.getDateTime()); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public int getItemCount() { |
|
||||
return inboxThreadModels == null ? 0 : inboxThreadModels.size(); |
|
||||
} |
|
||||
} |
|
@ -1,410 +1,66 @@ |
|||||
package awais.instagrabber.adapters; |
package awais.instagrabber.adapters; |
||||
|
|
||||
import android.content.Context; |
|
||||
import android.content.res.Resources; |
|
||||
import android.text.Spanned; |
|
||||
import android.util.Log; |
|
||||
import android.view.LayoutInflater; |
import android.view.LayoutInflater; |
||||
import android.view.View; |
import android.view.View; |
||||
import android.view.ViewGroup; |
import android.view.ViewGroup; |
||||
import android.widget.ArrayAdapter; |
|
||||
import android.widget.ImageView; |
|
||||
import android.widget.TextView; |
|
||||
import android.widget.Toast; |
|
||||
|
|
||||
import androidx.annotation.NonNull; |
import androidx.annotation.NonNull; |
||||
import androidx.annotation.Nullable; |
|
||||
import androidx.core.text.HtmlCompat; |
|
||||
import androidx.recyclerview.widget.RecyclerView; |
|
||||
|
import androidx.recyclerview.widget.DiffUtil; |
||||
|
import androidx.recyclerview.widget.ListAdapter; |
||||
|
|
||||
import com.bumptech.glide.Glide; |
|
||||
import com.bumptech.glide.RequestManager; |
|
||||
|
import java.util.List; |
||||
|
|
||||
import java.util.ArrayList; |
|
||||
|
|
||||
import awais.instagrabber.R; |
|
||||
import awais.instagrabber.activities.Main; |
|
||||
import awais.instagrabber.adapters.viewholder.directmessages.TextMessageViewHolder; |
|
||||
|
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageViewHolder; |
||||
|
import awais.instagrabber.databinding.ItemMessageItemBinding; |
||||
import awais.instagrabber.interfaces.MentionClickListener; |
import awais.instagrabber.interfaces.MentionClickListener; |
||||
import awais.instagrabber.models.ProfileModel; |
import awais.instagrabber.models.ProfileModel; |
||||
import awais.instagrabber.models.direct_messages.DirectItemModel; |
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.enums.DirectItemType; |
|
||||
import awais.instagrabber.models.enums.MediaItemType; |
|
||||
import awais.instagrabber.models.enums.RavenExpiringMediaType; |
|
||||
import awais.instagrabber.models.enums.RavenMediaViewType; |
|
||||
import awais.instagrabber.utils.Constants; |
|
||||
import awais.instagrabber.utils.Utils; |
|
||||
|
|
||||
import static awais.instagrabber.models.direct_messages.DirectItemModel.DirectItemLinkContext; |
|
||||
import static awais.instagrabber.models.direct_messages.DirectItemModel.DirectItemLinkModel; |
|
||||
import static awais.instagrabber.models.direct_messages.DirectItemModel.DirectItemReelShareModel; |
|
||||
import static awais.instagrabber.models.direct_messages.DirectItemModel.DirectItemVoiceMediaModel; |
|
||||
import static awais.instagrabber.models.direct_messages.DirectItemModel.RavenExpiringMediaActionSummaryModel; |
|
||||
|
|
||||
public final class MessageItemsAdapter extends RecyclerView.Adapter<TextMessageViewHolder> { |
|
||||
private static final int MESSAGE_INCOMING = 69, MESSAGE_OUTGOING = 420; |
|
||||
private final ProfileModel myProfileHolder = |
|
||||
new ProfileModel(false, false, false, |
|
||||
Utils.getUserIdFromCookie(Utils.settingsHelper.getString(Constants.COOKIE)), |
|
||||
null, null, null, null, null, null, 0, 0, 0, false, false, false, false); |
|
||||
private final ArrayList<DirectItemModel> directItemModels; |
|
||||
private final ArrayList<ProfileModel> users, leftusers; |
|
||||
|
public final class MessageItemsAdapter extends ListAdapter<DirectItemModel, DirectMessageViewHolder> { |
||||
|
private final List<ProfileModel> users; |
||||
|
private final List<ProfileModel> leftUsers; |
||||
private final View.OnClickListener onClickListener; |
private final View.OnClickListener onClickListener; |
||||
private final MentionClickListener mentionClickListener; |
private final MentionClickListener mentionClickListener; |
||||
private final View.OnClickListener openProfileClickListener = v -> { |
|
||||
final Object tag = v.getTag(); |
|
||||
if (tag instanceof ProfileModel) { |
|
||||
// todo do profile stuff |
|
||||
final ProfileModel profileModel = (ProfileModel) tag; |
|
||||
Log.d("AWAISKING_APP", "--> " + profileModel); |
|
||||
} |
|
||||
}; |
|
||||
private final int itemMargin; |
|
||||
private DirectItemVoiceMediaModel prevVoiceModel; |
|
||||
private ImageView prevPlayIcon; |
|
||||
private final View.OnClickListener voicePlayClickListener = v -> { |
|
||||
final Object tag = v.getTag(); |
|
||||
if (v instanceof ViewGroup && tag instanceof DirectItemVoiceMediaModel) { |
|
||||
final ImageView playIcon = (ImageView) ((ViewGroup) v).getChildAt(0); |
|
||||
final DirectItemVoiceMediaModel voiceMediaModel = (DirectItemVoiceMediaModel) tag; |
|
||||
final boolean voicePlaying = voiceMediaModel.isPlaying(); |
|
||||
voiceMediaModel.setPlaying(!voicePlaying); |
|
||||
|
|
||||
if (voiceMediaModel == prevVoiceModel) { |
|
||||
// todo pause / resume |
|
||||
} else { |
|
||||
// todo release prev audio, start new voice |
|
||||
if (prevVoiceModel != null) prevVoiceModel.setPlaying(false); |
|
||||
if (prevPlayIcon != null) prevPlayIcon.setImageResource(android.R.drawable.ic_media_play); |
|
||||
} |
|
||||
|
|
||||
if (voicePlaying) { |
|
||||
playIcon.setImageResource(android.R.drawable.ic_media_play); |
|
||||
} else { |
|
||||
playIcon.setImageResource(android.R.drawable.ic_media_pause); |
|
||||
|
private static final DiffUtil.ItemCallback<DirectItemModel> diffCallback = new DiffUtil.ItemCallback<DirectItemModel>() { |
||||
|
@Override |
||||
|
public boolean areItemsTheSame(@NonNull final DirectItemModel oldItem, @NonNull final DirectItemModel newItem) { |
||||
|
return oldItem.getItemId().equals(newItem.getItemId()); |
||||
} |
} |
||||
|
|
||||
prevVoiceModel = voiceMediaModel; |
|
||||
prevPlayIcon = playIcon; |
|
||||
|
@Override |
||||
|
public boolean areContentsTheSame(@NonNull final DirectItemModel oldItem, @NonNull final DirectItemModel newItem) { |
||||
|
return oldItem.getItemId().equals(newItem.getItemId()); |
||||
} |
} |
||||
}; |
}; |
||||
private Context context; |
|
||||
private LayoutInflater layoutInflater; |
|
||||
private String strDmYou; |
|
||||
|
|
||||
public MessageItemsAdapter(final ArrayList<DirectItemModel> directItemModels, final ArrayList<ProfileModel> users, |
|
||||
final ArrayList<ProfileModel> leftusers, final View.OnClickListener onClickListener, |
|
||||
|
public MessageItemsAdapter(final List<ProfileModel> users, |
||||
|
final List<ProfileModel> leftUsers, |
||||
|
final View.OnClickListener onClickListener, |
||||
final MentionClickListener mentionClickListener) { |
final MentionClickListener mentionClickListener) { |
||||
|
super(diffCallback); |
||||
this.users = users; |
this.users = users; |
||||
this.leftusers = leftusers; |
|
||||
this.directItemModels = directItemModels; |
|
||||
|
this.leftUsers = leftUsers; |
||||
this.onClickListener = onClickListener; |
this.onClickListener = onClickListener; |
||||
this.mentionClickListener = mentionClickListener; |
this.mentionClickListener = mentionClickListener; |
||||
this.itemMargin = Utils.displayMetrics.widthPixels / 5; |
|
||||
} |
} |
||||
|
|
||||
@NonNull |
@NonNull |
||||
@Override |
@Override |
||||
public TextMessageViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) { |
|
||||
if (context == null) context = parent.getContext(); |
|
||||
if (strDmYou == null) strDmYou = context.getString(R.string.direct_messages_you); |
|
||||
if (layoutInflater == null) layoutInflater = LayoutInflater.from(context); |
|
||||
return new TextMessageViewHolder(layoutInflater.inflate(R.layout.item_message_item, parent, false), |
|
||||
onClickListener, mentionClickListener); |
|
||||
|
public DirectMessageViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) { |
||||
|
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); |
||||
|
final ItemMessageItemBinding binding = ItemMessageItemBinding.inflate(layoutInflater, parent, false); |
||||
|
return new DirectMessageViewHolder(binding, users, leftUsers); |
||||
} |
} |
||||
|
|
||||
@Override |
@Override |
||||
public void onBindViewHolder(@NonNull final TextMessageViewHolder holder, final int position) { |
|
||||
final DirectItemModel directItemModel = directItemModels.get(position); |
|
||||
holder.itemView.setTag(directItemModel); |
|
||||
|
|
||||
if (directItemModel != null) { |
|
||||
final DirectItemType itemType = directItemModel.getItemType(); |
|
||||
|
|
||||
final ProfileModel user = getUser(directItemModel.getUserId()); |
|
||||
final int type = user == myProfileHolder ? MESSAGE_OUTGOING : MESSAGE_INCOMING; |
|
||||
|
|
||||
final RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) holder.itemView.getLayoutParams(); |
|
||||
layoutParams.setMargins(type == MESSAGE_OUTGOING ? itemMargin : 0, 0, |
|
||||
type == MESSAGE_INCOMING ? itemMargin : 0, 0); |
|
||||
|
|
||||
holder.tvMessage.setVisibility(View.GONE); |
|
||||
holder.voiceMessageContainer.setVisibility(View.GONE); |
|
||||
holder.ivAnimatedMessage.setVisibility(View.GONE); |
|
||||
holder.linkMessageContainer.setVisibility(View.GONE); |
|
||||
|
|
||||
holder.mediaMessageContainer.setVisibility(View.GONE); |
|
||||
holder.mediaTypeIcon.setVisibility(View.GONE); |
|
||||
holder.mediaExpiredIcon.setVisibility(View.GONE); |
|
||||
|
|
||||
holder.profileMessageContainer.setVisibility(View.GONE); |
|
||||
holder.isVerified.setVisibility(View.GONE); |
|
||||
|
|
||||
holder.btnOpenProfile.setVisibility(View.GONE); |
|
||||
holder.btnOpenProfile.setOnClickListener(null); |
|
||||
holder.btnOpenProfile.setTag(null); |
|
||||
|
|
||||
CharSequence text = "?"; |
|
||||
if (user != null && user != myProfileHolder) text = user.getUsername(); |
|
||||
else if (user == myProfileHolder) text = strDmYou; |
|
||||
text = text + " - " + directItemModel.getDateTime(); |
|
||||
|
|
||||
holder.tvUsername.setText(text); |
|
||||
|
|
||||
holder.ivProfilePic.setVisibility(type == MESSAGE_INCOMING ? View.VISIBLE : View.GONE); |
|
||||
|
|
||||
final RequestManager glideRequestManager = Glide.with(holder.itemView); |
|
||||
|
|
||||
if (type == MESSAGE_INCOMING && user != null) |
|
||||
glideRequestManager.load(user.getSdProfilePic()).into(holder.ivProfilePic); |
|
||||
|
|
||||
DirectItemMediaModel mediaModel = directItemModel.getMediaModel(); |
|
||||
switch (itemType) { |
|
||||
case PLACEHOLDER: |
|
||||
holder.tvMessage.setText(HtmlCompat.fromHtml(directItemModel.getText().toString(), 63)); |
|
||||
holder.tvMessage.setVisibility(View.VISIBLE); |
|
||||
break; |
|
||||
case TEXT: |
|
||||
case LIKE: |
|
||||
text = directItemModel.getText(); |
|
||||
text = Utils.getSpannableUrl(text.toString()); // for urls |
|
||||
if (Utils.hasMentions(text)) text = Utils.getMentionText(text); // for mentions |
|
||||
|
|
||||
if (text instanceof Spanned) holder.tvMessage.setText(text, TextView.BufferType.SPANNABLE); |
|
||||
else if (text == "") holder.tvMessage.setText(context.getText(R.string.dms_inbox_raven_message_unknown)); |
|
||||
else holder.tvMessage.setText(text); |
|
||||
|
|
||||
holder.tvMessage.setVisibility(View.VISIBLE); |
|
||||
break; |
|
||||
|
|
||||
case LINK: { |
|
||||
final DirectItemLinkModel link = directItemModel.getLinkModel(); |
|
||||
final DirectItemLinkContext linkContext = link.getLinkContext(); |
|
||||
|
|
||||
final String linkImageUrl = linkContext.getLinkImageUrl(); |
|
||||
if (!Utils.isEmpty(linkImageUrl)) { |
|
||||
glideRequestManager.load(linkImageUrl).into(holder.ivLinkPreview); |
|
||||
holder.tvLinkTitle.setText(linkContext.getLinkTitle()); |
|
||||
holder.tvLinkSummary.setText(linkContext.getLinkSummary()); |
|
||||
holder.ivLinkPreview.setVisibility(View.VISIBLE); |
|
||||
holder.linkMessageContainer.setVisibility(View.VISIBLE); |
|
||||
} |
|
||||
|
|
||||
holder.tvMessage.setText(Utils.getSpannableUrl(link.getText())); |
|
||||
holder.tvMessage.setVisibility(View.VISIBLE); |
|
||||
} |
|
||||
break; |
|
||||
|
|
||||
case MEDIA_SHARE: |
|
||||
{ |
|
||||
final ProfileModel modelUser = mediaModel.getUser(); |
|
||||
if (modelUser != null) { |
|
||||
holder.tvMessage.setText(HtmlCompat.fromHtml("<small>"+context.getString(R.string.dms_inbox_media_shared_from, modelUser.getUsername())+"</small>", 63)); |
|
||||
holder.tvMessage.setVisibility(View.VISIBLE); |
|
||||
} |
|
||||
} |
|
||||
case MEDIA: { |
|
||||
glideRequestManager.load(mediaModel.getThumbUrl()).into(holder.ivMediaPreview); |
|
||||
|
|
||||
final MediaItemType modelMediaType = mediaModel.getMediaType(); |
|
||||
holder.mediaTypeIcon.setVisibility(modelMediaType == MediaItemType.MEDIA_TYPE_VIDEO || |
|
||||
modelMediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE); |
|
||||
|
|
||||
holder.mediaMessageContainer.setVisibility(View.VISIBLE); |
|
||||
} |
|
||||
break; |
|
||||
|
|
||||
case RAVEN_MEDIA: { |
|
||||
final DirectItemRavenMediaModel ravenMediaModel = directItemModel.getRavenMediaModel(); |
|
||||
|
|
||||
final boolean isExpired = ravenMediaModel == null || (mediaModel = ravenMediaModel.getMedia()) == null || |
|
||||
Utils.isEmpty(mediaModel.getThumbUrl()) && mediaModel.getPk() < 1; |
|
||||
|
|
||||
final RavenExpiringMediaActionSummaryModel mediaActionSummary = ravenMediaModel.getExpiringMediaActionSummary(); |
|
||||
holder.mediaExpiredIcon.setVisibility(isExpired ? View.VISIBLE : View.GONE); |
|
||||
|
|
||||
int textRes = R.string.dms_inbox_raven_media_unknown; |
|
||||
if (isExpired) textRes = R.string.dms_inbox_raven_media_expired; |
|
||||
|
|
||||
if (!isExpired) { |
|
||||
if (mediaActionSummary != null) { |
|
||||
final RavenExpiringMediaType expiringMediaType = mediaActionSummary.getType(); |
|
||||
|
|
||||
if (expiringMediaType == RavenExpiringMediaType.RAVEN_DELIVERED) |
|
||||
textRes = R.string.dms_inbox_raven_media_delivered; |
|
||||
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_SENT) |
|
||||
textRes = R.string.dms_inbox_raven_media_sent; |
|
||||
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_OPENED) |
|
||||
textRes = R.string.dms_inbox_raven_media_opened; |
|
||||
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_REPLAYED) |
|
||||
textRes = R.string.dms_inbox_raven_media_replayed; |
|
||||
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_SENDING) |
|
||||
textRes = R.string.dms_inbox_raven_media_sending; |
|
||||
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_BLOCKED) |
|
||||
textRes = R.string.dms_inbox_raven_media_blocked; |
|
||||
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_SUGGESTED) |
|
||||
textRes = R.string.dms_inbox_raven_media_suggested; |
|
||||
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_SCREENSHOT) |
|
||||
textRes = R.string.dms_inbox_raven_media_screenshot; |
|
||||
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_CANNOT_DELIVER) |
|
||||
textRes = R.string.dms_inbox_raven_media_cant_deliver; |
|
||||
} |
|
||||
|
|
||||
final RavenMediaViewType ravenMediaViewType = ravenMediaModel.getViewType(); |
|
||||
if (ravenMediaViewType == RavenMediaViewType.PERMANENT || ravenMediaViewType == RavenMediaViewType.REPLAYABLE) { |
|
||||
final MediaItemType mediaType = mediaModel.getMediaType(); |
|
||||
textRes = -1; |
|
||||
holder.mediaTypeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO || |
|
||||
mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE); |
|
||||
|
|
||||
glideRequestManager.load(mediaModel.getThumbUrl()).into(holder.ivMediaPreview); |
|
||||
holder.mediaMessageContainer.setVisibility(View.VISIBLE); |
|
||||
} |
|
||||
} |
|
||||
if (textRes != -1) { |
|
||||
holder.tvMessage.setText(context.getText(textRes)); |
|
||||
holder.tvMessage.setVisibility(View.VISIBLE); |
|
||||
} |
|
||||
} |
|
||||
break; |
|
||||
|
|
||||
case REEL_SHARE: { |
|
||||
final DirectItemReelShareModel reelShare = directItemModel.getReelShare(); |
|
||||
if (!Utils.isEmpty(text = reelShare.getText())) { |
|
||||
holder.tvMessage.setText(text); |
|
||||
holder.tvMessage.setVisibility(View.VISIBLE); |
|
||||
} |
|
||||
|
|
||||
final DirectItemMediaModel reelShareMedia = reelShare.getMedia(); |
|
||||
final MediaItemType mediaType = reelShareMedia.getMediaType(); |
|
||||
|
|
||||
if (mediaType == null) |
|
||||
holder.mediaExpiredIcon.setVisibility(View.VISIBLE); |
|
||||
else { |
|
||||
holder.mediaTypeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO || |
|
||||
mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE); |
|
||||
|
|
||||
glideRequestManager.load(reelShareMedia.getThumbUrl()).into(holder.ivMediaPreview); |
|
||||
holder.mediaMessageContainer.setVisibility(View.VISIBLE); |
|
||||
} |
|
||||
} |
|
||||
break; |
|
||||
|
|
||||
case STORY_SHARE: { |
|
||||
final DirectItemReelShareModel reelShare = directItemModel.getReelShare(); |
|
||||
if (reelShare == null) { |
|
||||
holder.tvMessage.setText(HtmlCompat.fromHtml(directItemModel.getText().toString(), 63)); |
|
||||
holder.tvMessage.setVisibility(View.VISIBLE); |
|
||||
} |
|
||||
else { |
|
||||
if (!Utils.isEmpty(text = reelShare.getText())) { |
|
||||
holder.tvMessage.setText(text); |
|
||||
holder.tvMessage.setVisibility(View.VISIBLE); |
|
||||
} |
|
||||
|
|
||||
final DirectItemMediaModel reelShareMedia = reelShare.getMedia(); |
|
||||
final MediaItemType mediaType = reelShareMedia.getMediaType(); |
|
||||
|
|
||||
holder.mediaTypeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO || |
|
||||
mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE); |
|
||||
|
|
||||
glideRequestManager.load(reelShareMedia.getThumbUrl()).into(holder.ivMediaPreview); |
|
||||
holder.mediaMessageContainer.setVisibility(View.VISIBLE); |
|
||||
} |
|
||||
} |
|
||||
break; |
|
||||
|
|
||||
case VOICE_MEDIA: { |
|
||||
final DirectItemVoiceMediaModel voiceMediaModel = directItemModel.getVoiceMediaModel(); |
|
||||
|
|
||||
if (voiceMediaModel != null) { |
|
||||
final int[] waveformData = voiceMediaModel.getWaveformData(); |
|
||||
if (waveformData != null) holder.waveformSeekBar.setSample(waveformData); |
|
||||
|
|
||||
final long durationMs = voiceMediaModel.getDurationMs(); |
|
||||
holder.tvVoiceDuration.setText(Utils.millisToString(durationMs)); |
|
||||
holder.waveformSeekBar.setProgress(voiceMediaModel.getProgress()); |
|
||||
holder.waveformSeekBar.setProgressChangeListener((waveformSeekBar, progress, fromUser) -> { |
|
||||
// todo progress audio player |
|
||||
voiceMediaModel.setProgress(progress); |
|
||||
if (fromUser) |
|
||||
holder.tvVoiceDuration.setText(Utils.millisToString(durationMs * progress / 100)); |
|
||||
}); |
|
||||
holder.btnPlayVoice.setTag(voiceMediaModel); |
|
||||
holder.btnPlayVoice.setOnClickListener(voicePlayClickListener); |
|
||||
} else { |
|
||||
holder.waveformSeekBar.setProgress(0); |
|
||||
} |
|
||||
|
|
||||
holder.voiceMessageContainer.setVisibility(View.VISIBLE); |
|
||||
} |
|
||||
break; |
|
||||
|
|
||||
case ANIMATED_MEDIA: { |
|
||||
glideRequestManager.asGif().load(directItemModel.getAnimatedMediaModel().getGifUrl()) |
|
||||
.into(holder.ivAnimatedMessage); |
|
||||
holder.ivAnimatedMessage.setVisibility(View.VISIBLE); |
|
||||
} |
|
||||
break; |
|
||||
|
|
||||
case PROFILE: { |
|
||||
final ProfileModel profileModel = directItemModel.getProfileModel(); |
|
||||
Glide.with(holder.ivMessageProfilePic).load(profileModel.getSdProfilePic()) |
|
||||
.into(holder.ivMessageProfilePic); |
|
||||
holder.btnOpenProfile.setTag(profileModel); |
|
||||
holder.btnOpenProfile.setOnClickListener(openProfileClickListener); |
|
||||
|
|
||||
holder.tvProfileName.setText(profileModel.getName()); |
|
||||
holder.tvProfileUsername.setText(profileModel.getUsername()); |
|
||||
holder.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE); |
|
||||
|
|
||||
holder.btnOpenProfile.setVisibility(View.VISIBLE); |
|
||||
holder.profileMessageContainer.setVisibility(View.VISIBLE); |
|
||||
} |
|
||||
break; |
|
||||
|
|
||||
case VIDEO_CALL_EVENT: { |
|
||||
// todo add call event info |
|
||||
holder.tvMessage.setVisibility(View.VISIBLE); |
|
||||
holder.itemView.setBackgroundColor(0xFF_1F90E6); |
|
||||
} |
|
||||
break; |
|
||||
|
|
||||
case ACTION_LOG: { |
|
||||
text = directItemModel.getActionLogModel().getDescription(); |
|
||||
holder.tvMessage.setText(HtmlCompat.fromHtml("<small>"+text+"</small>", 63)); |
|
||||
holder.tvMessage.setVisibility(View.VISIBLE); |
|
||||
} |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
|
public void onBindViewHolder(@NonNull final DirectMessageViewHolder holder, final int position) { |
||||
|
final DirectItemModel directItemModel = getItem(position); |
||||
|
holder.bind(directItemModel); |
||||
} |
} |
||||
|
|
||||
@Override |
@Override |
||||
public int getItemViewType(final int position) { |
public int getItemViewType(final int position) { |
||||
return directItemModels.get(position).getItemType().ordinal(); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public int getItemCount() { |
|
||||
return directItemModels == null ? 0 : directItemModels.size(); |
|
||||
} |
|
||||
|
|
||||
@Nullable |
|
||||
private ProfileModel getUser(final long userId) { |
|
||||
if (users != null) { |
|
||||
ProfileModel result = myProfileHolder; |
|
||||
for (final ProfileModel user : users) { |
|
||||
if (Long.toString(userId).equals(user.getId())) result = user; |
|
||||
} |
|
||||
if (leftusers != null) |
|
||||
for (final ProfileModel leftuser : leftusers) { |
|
||||
if (Long.toString(userId).equals(leftuser.getId())) result = leftuser; |
|
||||
} |
|
||||
return result; |
|
||||
} |
|
||||
return null; |
|
||||
|
return getItem(position).getItemType().ordinal(); |
||||
} |
} |
||||
} |
} |
@ -0,0 +1,107 @@ |
|||||
|
package awais.instagrabber.adapters.viewholder; |
||||
|
|
||||
|
import android.content.Context; |
||||
|
import android.view.View; |
||||
|
import android.widget.ImageView; |
||||
|
import android.widget.LinearLayout; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
import androidx.core.text.HtmlCompat; |
||||
|
import androidx.recyclerview.widget.RecyclerView; |
||||
|
|
||||
|
import com.bumptech.glide.Glide; |
||||
|
import com.bumptech.glide.RequestManager; |
||||
|
|
||||
|
import awais.instagrabber.R; |
||||
|
import awais.instagrabber.databinding.LayoutIncludeSimpleItemBinding; |
||||
|
import awais.instagrabber.models.ProfileModel; |
||||
|
import awais.instagrabber.models.direct_messages.DirectItemModel; |
||||
|
import awais.instagrabber.models.direct_messages.InboxThreadModel; |
||||
|
import awais.instagrabber.models.enums.DirectItemType; |
||||
|
|
||||
|
public final class DirectMessageInboxItemViewHolder extends RecyclerView.ViewHolder { |
||||
|
private final LinearLayout multipleProfilePicsContainer; |
||||
|
private final ImageView[] multipleProfilePics; |
||||
|
private final LayoutIncludeSimpleItemBinding binding; |
||||
|
|
||||
|
public DirectMessageInboxItemViewHolder(@NonNull final LayoutIncludeSimpleItemBinding binding) { |
||||
|
super(binding.getRoot()); |
||||
|
this.binding = binding; |
||||
|
binding.tvLikes.setVisibility(View.GONE); |
||||
|
multipleProfilePicsContainer = binding.container; |
||||
|
final LinearLayout containerChild = (LinearLayout) multipleProfilePicsContainer.getChildAt(1); |
||||
|
multipleProfilePics = new ImageView[]{ |
||||
|
(ImageView) multipleProfilePicsContainer.getChildAt(0), |
||||
|
(ImageView) containerChild.getChildAt(0), |
||||
|
(ImageView) containerChild.getChildAt(1) |
||||
|
}; |
||||
|
binding.tvDate.setSelected(true); |
||||
|
binding.tvUsername.setSelected(true); |
||||
|
} |
||||
|
|
||||
|
public void bind(final InboxThreadModel model) { |
||||
|
final DirectItemModel[] itemModels; |
||||
|
if (model == null || (itemModels = model.getItems()) == null) { |
||||
|
return; |
||||
|
} |
||||
|
itemView.setTag(model); |
||||
|
final RequestManager glideRequestManager = Glide.with(itemView); |
||||
|
final ProfileModel[] users = model.getUsers(); |
||||
|
if (users.length > 1) { |
||||
|
binding.ivProfilePic.setVisibility(View.GONE); |
||||
|
multipleProfilePicsContainer.setVisibility(View.VISIBLE); |
||||
|
for (int i = 0; i < Math.min(3, users.length); ++i) |
||||
|
glideRequestManager.load(users[i].getSdProfilePic()).into(multipleProfilePics[i]); |
||||
|
} else { |
||||
|
binding.ivProfilePic.setVisibility(View.VISIBLE); |
||||
|
multipleProfilePicsContainer.setVisibility(View.GONE); |
||||
|
glideRequestManager.load(users.length == 1 ? users[0].getSdProfilePic() : null).into(binding.ivProfilePic); |
||||
|
} |
||||
|
binding.tvUsername.setText(model.getThreadTitle()); |
||||
|
final DirectItemModel lastItemModel = itemModels[itemModels.length - 1]; |
||||
|
final DirectItemType itemType = lastItemModel.getItemType(); |
||||
|
binding.notTextType.setVisibility(itemType != DirectItemType.TEXT ? View.VISIBLE : View.GONE); |
||||
|
final Context context = itemView.getContext(); |
||||
|
final CharSequence messageText; |
||||
|
switch (itemType) { |
||||
|
case TEXT: |
||||
|
case LIKE: |
||||
|
messageText = lastItemModel.getText(); |
||||
|
break; |
||||
|
case LINK: |
||||
|
messageText = context.getString(R.string.direct_messages_sent_link); |
||||
|
break; |
||||
|
case MEDIA: |
||||
|
case MEDIA_SHARE: |
||||
|
case RAVEN_MEDIA: |
||||
|
messageText = context.getString(R.string.direct_messages_sent_media); |
||||
|
break; |
||||
|
case ACTION_LOG: |
||||
|
final DirectItemModel.DirectItemActionLogModel logModel = lastItemModel.getActionLogModel(); |
||||
|
messageText = logModel != null ? logModel.getDescription() : "..."; |
||||
|
break; |
||||
|
case REEL_SHARE: |
||||
|
final DirectItemModel.DirectItemReelShareModel reelShare = lastItemModel.getReelShare(); |
||||
|
if (reelShare == null) |
||||
|
messageText = context.getString(R.string.direct_messages_sent_media); |
||||
|
else { |
||||
|
final String reelType = reelShare.getType(); |
||||
|
final int textRes; |
||||
|
if ("reply".equals(reelType)) |
||||
|
textRes = R.string.direct_messages_replied_story; |
||||
|
else if ("mention".equals(reelType)) |
||||
|
textRes = R.string.direct_messages_mention_story; |
||||
|
else if ("reaction".equals(reelType)) |
||||
|
textRes = R.string.direct_messages_reacted_story; |
||||
|
else textRes = R.string.direct_messages_sent_media; |
||||
|
|
||||
|
messageText = context.getString(textRes) + " : " + reelShare.getText(); |
||||
|
} |
||||
|
break; |
||||
|
default: |
||||
|
messageText = "<i>Unsupported message</i>"; |
||||
|
} |
||||
|
binding.tvComment.setText(HtmlCompat.fromHtml(messageText.toString(), HtmlCompat.FROM_HTML_MODE_COMPACT)); |
||||
|
binding.tvDate.setText(lastItemModel.getDateTime()); |
||||
|
} |
||||
|
} |
@ -1,43 +0,0 @@ |
|||||
package awais.instagrabber.adapters.viewholder; |
|
||||
|
|
||||
import android.view.View; |
|
||||
import android.widget.ImageView; |
|
||||
import android.widget.LinearLayout; |
|
||||
import android.widget.TextView; |
|
||||
|
|
||||
import androidx.annotation.NonNull; |
|
||||
import androidx.recyclerview.widget.RecyclerView; |
|
||||
|
|
||||
import awais.instagrabber.R; |
|
||||
|
|
||||
public final class DirectMessageViewHolder extends RecyclerView.ViewHolder { |
|
||||
public final LinearLayout multipleProfilePicsContainer; |
|
||||
public final ImageView[] multipleProfilePics; |
|
||||
public final ImageView ivProfilePic, notTextType; |
|
||||
public final TextView tvUsername, tvDate, tvMessage; |
|
||||
|
|
||||
public DirectMessageViewHolder(@NonNull final View itemView, final View.OnClickListener clickListener) { |
|
||||
super(itemView); |
|
||||
|
|
||||
if (clickListener != null) itemView.setOnClickListener(clickListener); |
|
||||
|
|
||||
itemView.findViewById(R.id.tvLikes).setVisibility(View.GONE); |
|
||||
|
|
||||
tvDate = itemView.findViewById(R.id.tvDate); |
|
||||
tvMessage = itemView.findViewById(R.id.tvComment); |
|
||||
tvUsername = itemView.findViewById(R.id.tvUsername); |
|
||||
notTextType = itemView.findViewById(R.id.notTextType); |
|
||||
ivProfilePic = itemView.findViewById(R.id.ivProfilePic); |
|
||||
|
|
||||
multipleProfilePicsContainer = itemView.findViewById(R.id.container); |
|
||||
final LinearLayout containerChild = (LinearLayout) multipleProfilePicsContainer.getChildAt(1); |
|
||||
multipleProfilePics = new ImageView[]{ |
|
||||
(ImageView) multipleProfilePicsContainer.getChildAt(0), |
|
||||
(ImageView) containerChild.getChildAt(0), |
|
||||
(ImageView) containerChild.getChildAt(1) |
|
||||
}; |
|
||||
|
|
||||
tvDate.setSelected(true); |
|
||||
tvUsername.setSelected(true); |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,383 @@ |
|||||
|
package awais.instagrabber.adapters.viewholder.directmessages; |
||||
|
|
||||
|
import android.content.Context; |
||||
|
import android.text.Spanned; |
||||
|
import android.util.Log; |
||||
|
import android.view.View; |
||||
|
import android.view.ViewGroup; |
||||
|
import android.widget.FrameLayout; |
||||
|
import android.widget.ImageView; |
||||
|
import android.widget.TextView; |
||||
|
|
||||
|
import androidx.annotation.Nullable; |
||||
|
import androidx.core.text.HtmlCompat; |
||||
|
import androidx.recyclerview.widget.RecyclerView; |
||||
|
|
||||
|
import com.bumptech.glide.Glide; |
||||
|
import com.bumptech.glide.RequestManager; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
import awais.instagrabber.R; |
||||
|
import awais.instagrabber.databinding.ItemMessageItemBinding; |
||||
|
import awais.instagrabber.models.ProfileModel; |
||||
|
import awais.instagrabber.models.direct_messages.DirectItemModel; |
||||
|
import awais.instagrabber.models.enums.DirectItemType; |
||||
|
import awais.instagrabber.models.enums.MediaItemType; |
||||
|
import awais.instagrabber.models.enums.RavenExpiringMediaType; |
||||
|
import awais.instagrabber.models.enums.RavenMediaViewType; |
||||
|
import awais.instagrabber.utils.Constants; |
||||
|
import awais.instagrabber.utils.Utils; |
||||
|
|
||||
|
import static androidx.core.text.HtmlCompat.FROM_HTML_MODE_COMPACT; |
||||
|
|
||||
|
public final class DirectMessageViewHolder extends RecyclerView.ViewHolder { |
||||
|
private static final String TAG = "DirectMessageViewHolder"; |
||||
|
private static final int MESSAGE_INCOMING = 69; |
||||
|
private static final int MESSAGE_OUTGOING = 420; |
||||
|
|
||||
|
private final ProfileModel myProfileHolder = ProfileModel.getDefaultProfileModel(Utils.getUserIdFromCookie(Utils.settingsHelper.getString(Constants.COOKIE))); |
||||
|
private final ItemMessageItemBinding binding; |
||||
|
private final List<ProfileModel> users; |
||||
|
private final List<ProfileModel> leftUsers; |
||||
|
private final int itemMargin; |
||||
|
private final String strDmYou; |
||||
|
private DirectItemModel.DirectItemVoiceMediaModel prevVoiceModel; |
||||
|
private ImageView prevPlayIcon; |
||||
|
|
||||
|
private final View.OnClickListener voicePlayClickListener = v -> { |
||||
|
final Object tag = v.getTag(); |
||||
|
if (v instanceof ViewGroup && tag instanceof DirectItemModel.DirectItemVoiceMediaModel) { |
||||
|
final ImageView playIcon = (ImageView) ((ViewGroup) v).getChildAt(0); |
||||
|
final DirectItemModel.DirectItemVoiceMediaModel voiceMediaModel = (DirectItemModel.DirectItemVoiceMediaModel) tag; |
||||
|
final boolean voicePlaying = voiceMediaModel.isPlaying(); |
||||
|
voiceMediaModel.setPlaying(!voicePlaying); |
||||
|
|
||||
|
if (voiceMediaModel == prevVoiceModel) { |
||||
|
// todo pause / resume |
||||
|
} else { |
||||
|
// todo release prev audio, start new voice |
||||
|
if (prevVoiceModel != null) prevVoiceModel.setPlaying(false); |
||||
|
if (prevPlayIcon != null) |
||||
|
prevPlayIcon.setImageResource(android.R.drawable.ic_media_play); |
||||
|
} |
||||
|
|
||||
|
if (voicePlaying) { |
||||
|
playIcon.setImageResource(android.R.drawable.ic_media_play); |
||||
|
} else { |
||||
|
playIcon.setImageResource(android.R.drawable.ic_media_pause); |
||||
|
} |
||||
|
|
||||
|
prevVoiceModel = voiceMediaModel; |
||||
|
prevPlayIcon = playIcon; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
private final View.OnClickListener openProfileClickListener = v -> { |
||||
|
final Object tag = v.getTag(); |
||||
|
if (tag instanceof ProfileModel) { |
||||
|
// todo do profile stuff |
||||
|
final ProfileModel profileModel = (ProfileModel) tag; |
||||
|
Log.d(TAG, "--> " + profileModel); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
public DirectMessageViewHolder(final ItemMessageItemBinding binding, |
||||
|
final List<ProfileModel> users, |
||||
|
final List<ProfileModel> leftUsers) { |
||||
|
super(binding.getRoot()); |
||||
|
this.binding = binding; |
||||
|
this.users = users; |
||||
|
this.leftUsers = leftUsers; |
||||
|
this.itemMargin = Utils.displayMetrics.widthPixels / 5; |
||||
|
strDmYou = binding.getRoot().getContext().getString(R.string.direct_messages_you); |
||||
|
} |
||||
|
|
||||
|
public void bind(final DirectItemModel directItemModel) { |
||||
|
if (directItemModel == null) { |
||||
|
return; |
||||
|
} |
||||
|
final Context context = itemView.getContext(); |
||||
|
itemView.setTag(directItemModel); |
||||
|
final DirectItemType itemType = directItemModel.getItemType(); |
||||
|
final ProfileModel user = getUser(directItemModel.getUserId()); |
||||
|
final int type = user == myProfileHolder ? MESSAGE_OUTGOING : MESSAGE_INCOMING; |
||||
|
|
||||
|
final RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) itemView.getLayoutParams(); |
||||
|
layoutParams.setMargins(type == MESSAGE_OUTGOING ? itemMargin : 0, 0, |
||||
|
type == MESSAGE_INCOMING ? itemMargin : 0, 0); |
||||
|
|
||||
|
binding.tvMessage.setVisibility(View.GONE); |
||||
|
final View voiceMessageContainer = (View) binding.waveformSeekBar.getParent(); |
||||
|
final View linkMessageContainer = (View) binding.ivLinkPreview.getParent(); |
||||
|
final View mediaMessageContainer = (View) binding.ivMediaPreview.getParent(); |
||||
|
final View mediaTypeIcon = binding.typeIcon; |
||||
|
final View profileMessageContainer = (View) binding.profileInfo.getParent(); |
||||
|
|
||||
|
voiceMessageContainer.setVisibility(View.GONE); |
||||
|
binding.ivAnimatedMessage.setVisibility(View.GONE); |
||||
|
linkMessageContainer.setVisibility(View.GONE); |
||||
|
mediaMessageContainer.setVisibility(View.GONE); |
||||
|
mediaTypeIcon.setVisibility(View.GONE); |
||||
|
binding.mediaExpiredIcon.setVisibility(View.GONE); |
||||
|
profileMessageContainer.setVisibility(View.GONE); |
||||
|
binding.isVerified.setVisibility(View.GONE); |
||||
|
|
||||
|
final FrameLayout btnOpenProfile = binding.btnInfo; |
||||
|
btnOpenProfile.setVisibility(View.GONE); |
||||
|
btnOpenProfile.setOnClickListener(null); |
||||
|
btnOpenProfile.setTag(null); |
||||
|
|
||||
|
CharSequence text = "?"; |
||||
|
if (user != null && user != myProfileHolder) text = user.getUsername(); |
||||
|
else if (user == myProfileHolder) text = strDmYou; |
||||
|
text = text + " - " + directItemModel.getDateTime(); |
||||
|
|
||||
|
binding.tvUsername.setText(text); |
||||
|
|
||||
|
binding.ivProfilePic.setVisibility(type == MESSAGE_INCOMING ? View.VISIBLE : View.GONE); |
||||
|
|
||||
|
final RequestManager glideRequestManager = Glide.with(itemView); |
||||
|
|
||||
|
if (type == MESSAGE_INCOMING && user != null) |
||||
|
glideRequestManager.load(user.getSdProfilePic()).into(binding.ivProfilePic); |
||||
|
|
||||
|
DirectItemModel.DirectItemMediaModel mediaModel = directItemModel.getMediaModel(); |
||||
|
switch (itemType) { |
||||
|
case PLACEHOLDER: |
||||
|
binding.tvMessage.setText(HtmlCompat.fromHtml(directItemModel.getText().toString(), FROM_HTML_MODE_COMPACT)); |
||||
|
binding.tvMessage.setVisibility(View.VISIBLE); |
||||
|
break; |
||||
|
case TEXT: |
||||
|
case LIKE: |
||||
|
text = directItemModel.getText(); |
||||
|
text = Utils.getSpannableUrl(text.toString()); // for urls |
||||
|
if (Utils.hasMentions(text)) text = Utils.getMentionText(text); // for mentions |
||||
|
|
||||
|
if (text instanceof Spanned) |
||||
|
binding.tvMessage.setText(text, TextView.BufferType.SPANNABLE); |
||||
|
else if (text == "") { |
||||
|
binding.tvMessage.setText(context.getText(R.string.dms_inbox_raven_message_unknown)); |
||||
|
} else binding.tvMessage.setText(text); |
||||
|
|
||||
|
binding.tvMessage.setVisibility(View.VISIBLE); |
||||
|
break; |
||||
|
|
||||
|
case LINK: { |
||||
|
final DirectItemModel.DirectItemLinkModel link = directItemModel.getLinkModel(); |
||||
|
final DirectItemModel.DirectItemLinkContext linkContext = link.getLinkContext(); |
||||
|
|
||||
|
final String linkImageUrl = linkContext.getLinkImageUrl(); |
||||
|
if (!Utils.isEmpty(linkImageUrl)) { |
||||
|
glideRequestManager.load(linkImageUrl).into(binding.ivLinkPreview); |
||||
|
binding.tvLinkTitle.setText(linkContext.getLinkTitle()); |
||||
|
binding.tvLinkSummary.setText(linkContext.getLinkSummary()); |
||||
|
binding.ivLinkPreview.setVisibility(View.VISIBLE); |
||||
|
linkMessageContainer.setVisibility(View.VISIBLE); |
||||
|
} |
||||
|
|
||||
|
binding.tvMessage.setText(Utils.getSpannableUrl(link.getText())); |
||||
|
binding.tvMessage.setVisibility(View.VISIBLE); |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case MEDIA_SHARE: { |
||||
|
final ProfileModel modelUser = mediaModel.getUser(); |
||||
|
if (modelUser != null) { |
||||
|
binding.tvMessage.setText(HtmlCompat.fromHtml("<small>" + context.getString(R.string.dms_inbox_media_shared_from, modelUser.getUsername()) + "</small>", FROM_HTML_MODE_COMPACT)); |
||||
|
binding.tvMessage.setVisibility(View.VISIBLE); |
||||
|
} |
||||
|
} |
||||
|
case MEDIA: { |
||||
|
glideRequestManager.load(mediaModel.getThumbUrl()).into(binding.ivMediaPreview); |
||||
|
|
||||
|
final MediaItemType modelMediaType = mediaModel.getMediaType(); |
||||
|
mediaTypeIcon.setVisibility(modelMediaType == MediaItemType.MEDIA_TYPE_VIDEO || |
||||
|
modelMediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE); |
||||
|
mediaMessageContainer.setVisibility(View.VISIBLE); |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case RAVEN_MEDIA: { |
||||
|
final DirectItemModel.DirectItemRavenMediaModel ravenMediaModel = directItemModel.getRavenMediaModel(); |
||||
|
|
||||
|
final boolean isExpired = ravenMediaModel == null || (mediaModel = ravenMediaModel.getMedia()) == null || |
||||
|
Utils.isEmpty(mediaModel.getThumbUrl()) && mediaModel.getPk() < 1; |
||||
|
|
||||
|
DirectItemModel.RavenExpiringMediaActionSummaryModel mediaActionSummary = null; |
||||
|
if (ravenMediaModel != null) { |
||||
|
mediaActionSummary = ravenMediaModel.getExpiringMediaActionSummary(); |
||||
|
} |
||||
|
binding.mediaExpiredIcon.setVisibility(isExpired ? View.VISIBLE : View.GONE); |
||||
|
|
||||
|
int textRes = R.string.dms_inbox_raven_media_unknown; |
||||
|
if (isExpired) textRes = R.string.dms_inbox_raven_media_expired; |
||||
|
|
||||
|
if (!isExpired) { |
||||
|
if (mediaActionSummary != null) { |
||||
|
final RavenExpiringMediaType expiringMediaType = mediaActionSummary.getType(); |
||||
|
|
||||
|
if (expiringMediaType == RavenExpiringMediaType.RAVEN_DELIVERED) |
||||
|
textRes = R.string.dms_inbox_raven_media_delivered; |
||||
|
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_SENT) |
||||
|
textRes = R.string.dms_inbox_raven_media_sent; |
||||
|
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_OPENED) |
||||
|
textRes = R.string.dms_inbox_raven_media_opened; |
||||
|
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_REPLAYED) |
||||
|
textRes = R.string.dms_inbox_raven_media_replayed; |
||||
|
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_SENDING) |
||||
|
textRes = R.string.dms_inbox_raven_media_sending; |
||||
|
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_BLOCKED) |
||||
|
textRes = R.string.dms_inbox_raven_media_blocked; |
||||
|
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_SUGGESTED) |
||||
|
textRes = R.string.dms_inbox_raven_media_suggested; |
||||
|
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_SCREENSHOT) |
||||
|
textRes = R.string.dms_inbox_raven_media_screenshot; |
||||
|
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_CANNOT_DELIVER) |
||||
|
textRes = R.string.dms_inbox_raven_media_cant_deliver; |
||||
|
} |
||||
|
|
||||
|
final RavenMediaViewType ravenMediaViewType = ravenMediaModel.getViewType(); |
||||
|
if (ravenMediaViewType == RavenMediaViewType.PERMANENT || ravenMediaViewType == RavenMediaViewType.REPLAYABLE) { |
||||
|
final MediaItemType mediaType = mediaModel.getMediaType(); |
||||
|
textRes = -1; |
||||
|
mediaTypeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO || |
||||
|
mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE); |
||||
|
|
||||
|
glideRequestManager.load(mediaModel.getThumbUrl()).into(binding.ivMediaPreview); |
||||
|
mediaMessageContainer.setVisibility(View.VISIBLE); |
||||
|
} |
||||
|
} |
||||
|
if (textRes != -1) { |
||||
|
binding.tvMessage.setText(context.getText(textRes)); |
||||
|
binding.tvMessage.setVisibility(View.VISIBLE); |
||||
|
} |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case REEL_SHARE: { |
||||
|
final DirectItemModel.DirectItemReelShareModel reelShare = directItemModel.getReelShare(); |
||||
|
if (!Utils.isEmpty(text = reelShare.getText())) { |
||||
|
binding.tvMessage.setText(text); |
||||
|
binding.tvMessage.setVisibility(View.VISIBLE); |
||||
|
} |
||||
|
|
||||
|
final DirectItemModel.DirectItemMediaModel reelShareMedia = reelShare.getMedia(); |
||||
|
final MediaItemType mediaType = reelShareMedia.getMediaType(); |
||||
|
|
||||
|
if (mediaType == null) |
||||
|
binding.mediaExpiredIcon.setVisibility(View.VISIBLE); |
||||
|
else { |
||||
|
mediaTypeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO || |
||||
|
mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE); |
||||
|
|
||||
|
glideRequestManager.load(reelShareMedia.getThumbUrl()).into(binding.ivMediaPreview); |
||||
|
mediaMessageContainer.setVisibility(View.VISIBLE); |
||||
|
} |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case STORY_SHARE: { |
||||
|
final DirectItemModel.DirectItemReelShareModel reelShare = directItemModel.getReelShare(); |
||||
|
if (reelShare == null) { |
||||
|
binding.tvMessage.setText(HtmlCompat.fromHtml(directItemModel.getText().toString(), FROM_HTML_MODE_COMPACT)); |
||||
|
binding.tvMessage.setVisibility(View.VISIBLE); |
||||
|
} else { |
||||
|
if (!Utils.isEmpty(text = reelShare.getText())) { |
||||
|
binding.tvMessage.setText(text); |
||||
|
binding.tvMessage.setVisibility(View.VISIBLE); |
||||
|
} |
||||
|
|
||||
|
final DirectItemModel.DirectItemMediaModel reelShareMedia = reelShare.getMedia(); |
||||
|
final MediaItemType mediaType = reelShareMedia.getMediaType(); |
||||
|
|
||||
|
mediaTypeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO || |
||||
|
mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE); |
||||
|
|
||||
|
glideRequestManager.load(reelShareMedia.getThumbUrl()).into(binding.ivMediaPreview); |
||||
|
mediaMessageContainer.setVisibility(View.VISIBLE); |
||||
|
} |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case VOICE_MEDIA: { |
||||
|
final DirectItemModel.DirectItemVoiceMediaModel voiceMediaModel = directItemModel.getVoiceMediaModel(); |
||||
|
|
||||
|
if (voiceMediaModel != null) { |
||||
|
final int[] waveformData = voiceMediaModel.getWaveformData(); |
||||
|
if (waveformData != null) binding.waveformSeekBar.setSample(waveformData); |
||||
|
|
||||
|
final long durationMs = voiceMediaModel.getDurationMs(); |
||||
|
binding.tvVoiceDuration.setText(Utils.millisToString(durationMs)); |
||||
|
binding.waveformSeekBar.setProgress(voiceMediaModel.getProgress()); |
||||
|
binding.waveformSeekBar.setProgressChangeListener((waveformSeekBar, progress, fromUser) -> { |
||||
|
// todo progress audio player |
||||
|
voiceMediaModel.setProgress(progress); |
||||
|
if (fromUser) |
||||
|
binding.tvVoiceDuration.setText(Utils.millisToString(durationMs * progress / 100)); |
||||
|
}); |
||||
|
binding.btnPlayVoice.setTag(voiceMediaModel); |
||||
|
binding.btnPlayVoice.setOnClickListener(voicePlayClickListener); |
||||
|
} else { |
||||
|
binding.waveformSeekBar.setProgress(0); |
||||
|
} |
||||
|
voiceMessageContainer.setVisibility(View.VISIBLE); |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case ANIMATED_MEDIA: { |
||||
|
glideRequestManager.asGif().load(directItemModel.getAnimatedMediaModel().getGifUrl()) |
||||
|
.into(binding.ivAnimatedMessage); |
||||
|
binding.ivAnimatedMessage.setVisibility(View.VISIBLE); |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case PROFILE: { |
||||
|
final ProfileModel profileModel = directItemModel.getProfileModel(); |
||||
|
Glide.with(binding.profileInfo).load(profileModel.getSdProfilePic()) |
||||
|
.into(binding.profileInfo); |
||||
|
btnOpenProfile.setTag(profileModel); |
||||
|
btnOpenProfile.setOnClickListener(openProfileClickListener); |
||||
|
|
||||
|
binding.tvFullName.setText(profileModel.getName()); |
||||
|
binding.profileInfoText.setText(profileModel.getUsername()); |
||||
|
binding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE); |
||||
|
|
||||
|
btnOpenProfile.setVisibility(View.VISIBLE); |
||||
|
profileMessageContainer.setVisibility(View.VISIBLE); |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case VIDEO_CALL_EVENT: { |
||||
|
// todo add call event info |
||||
|
binding.tvMessage.setVisibility(View.VISIBLE); |
||||
|
binding.profileInfoText.setBackgroundColor(0xFF_1F90E6); |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case ACTION_LOG: { |
||||
|
text = directItemModel.getActionLogModel().getDescription(); |
||||
|
binding.tvMessage.setText(HtmlCompat.fromHtml("<small>" + text + "</small>", FROM_HTML_MODE_COMPACT)); |
||||
|
binding.tvMessage.setVisibility(View.VISIBLE); |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Nullable |
||||
|
private ProfileModel getUser(final long userId) { |
||||
|
if (users != null) { |
||||
|
ProfileModel result = myProfileHolder; |
||||
|
for (final ProfileModel user : users) { |
||||
|
if (Long.toString(userId).equals(user.getId())) result = user; |
||||
|
} |
||||
|
if (leftUsers != null) |
||||
|
for (final ProfileModel leftUser : leftUsers) { |
||||
|
if (Long.toString(userId).equals(leftUser.getId())) result = leftUser; |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
} |
@ -1,91 +0,0 @@ |
|||||
package awais.instagrabber.adapters.viewholder.directmessages; |
|
||||
|
|
||||
import android.view.View; |
|
||||
import android.widget.ImageView; |
|
||||
import android.widget.TextView; |
|
||||
|
|
||||
import androidx.annotation.NonNull; |
|
||||
import androidx.cardview.widget.CardView; |
|
||||
import androidx.recyclerview.widget.RecyclerView; |
|
||||
|
|
||||
import awais.instagrabber.R; |
|
||||
import awais.instagrabber.customviews.RamboTextView; |
|
||||
import awais.instagrabber.customviews.masoudss_waveform.WaveformSeekBar; |
|
||||
import awais.instagrabber.interfaces.MentionClickListener; |
|
||||
|
|
||||
public final class TextMessageViewHolder extends RecyclerView.ViewHolder { |
|
||||
public final CardView rootCardView; |
|
||||
public final TextView tvUsername; |
|
||||
public final ImageView ivProfilePic; |
|
||||
// text message |
|
||||
public final RamboTextView tvMessage; |
|
||||
// expired message icon |
|
||||
public final View mediaExpiredIcon; |
|
||||
// media message |
|
||||
public final View mediaMessageContainer; |
|
||||
public final ImageView ivMediaPreview, mediaTypeIcon; |
|
||||
// profile messag |
|
||||
public final View profileMessageContainer, isVerified, btnOpenProfile; |
|
||||
public final TextView tvProfileUsername, tvProfileName; |
|
||||
public final ImageView ivMessageProfilePic; |
|
||||
// animated message |
|
||||
public final ImageView ivAnimatedMessage; |
|
||||
// link message |
|
||||
public final View linkMessageContainer; |
|
||||
public final ImageView ivLinkPreview; |
|
||||
public final TextView tvLinkTitle, tvLinkSummary; |
|
||||
// voice message |
|
||||
public final View voiceMessageContainer, btnPlayVoice; |
|
||||
public final WaveformSeekBar waveformSeekBar; |
|
||||
public final TextView tvVoiceDuration; |
|
||||
|
|
||||
public TextMessageViewHolder(@NonNull final View itemView, final View.OnClickListener clickListener, |
|
||||
final MentionClickListener mentionClickListener) { |
|
||||
super(itemView); |
|
||||
|
|
||||
if (clickListener != null) itemView.setOnClickListener(clickListener); |
|
||||
|
|
||||
tvUsername = itemView.findViewById(R.id.tvUsername); |
|
||||
ivProfilePic = itemView.findViewById(R.id.ivProfilePic); |
|
||||
|
|
||||
// text message |
|
||||
tvMessage = itemView.findViewById(R.id.tvMessage); |
|
||||
tvMessage.setCaptionIsExpandable(true); |
|
||||
tvMessage.setCaptionIsExpanded(true); |
|
||||
if (mentionClickListener != null) tvMessage.setMentionClickListener(mentionClickListener); |
|
||||
|
|
||||
// root view |
|
||||
rootCardView = (CardView) tvMessage.getParent().getParent(); |
|
||||
|
|
||||
// expired message icon |
|
||||
mediaExpiredIcon = itemView.findViewById(R.id.mediaExpiredIcon); |
|
||||
|
|
||||
// media message |
|
||||
ivMediaPreview = itemView.findViewById(R.id.ivMediaPreview); |
|
||||
mediaMessageContainer = (View) ivMediaPreview.getParent(); |
|
||||
mediaTypeIcon = mediaMessageContainer.findViewById(R.id.typeIcon); |
|
||||
|
|
||||
// profile message |
|
||||
btnOpenProfile = itemView.findViewById(R.id.btnInfo); |
|
||||
ivMessageProfilePic = itemView.findViewById(R.id.profileInfo); |
|
||||
profileMessageContainer = (View) ivMessageProfilePic.getParent(); |
|
||||
isVerified = profileMessageContainer.findViewById(R.id.isVerified); |
|
||||
tvProfileName = profileMessageContainer.findViewById(R.id.tvFullName); |
|
||||
tvProfileUsername = profileMessageContainer.findViewById(R.id.profileInfoText); |
|
||||
|
|
||||
// animated message |
|
||||
ivAnimatedMessage = itemView.findViewById(R.id.ivAnimatedMessage); |
|
||||
|
|
||||
// link message |
|
||||
ivLinkPreview = itemView.findViewById(R.id.ivLinkPreview); |
|
||||
linkMessageContainer = (View) ivLinkPreview.getParent(); |
|
||||
tvLinkTitle = linkMessageContainer.findViewById(R.id.tvLinkTitle); |
|
||||
tvLinkSummary = linkMessageContainer.findViewById(R.id.tvLinkSummary); |
|
||||
|
|
||||
// voice message |
|
||||
waveformSeekBar = itemView.findViewById(R.id.waveformSeekBar); |
|
||||
voiceMessageContainer = (View) waveformSeekBar.getParent(); |
|
||||
btnPlayVoice = voiceMessageContainer.findViewById(R.id.btnPlayVoice); |
|
||||
tvVoiceDuration = voiceMessageContainer.findViewById(R.id.tvVoiceDuration); |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,176 @@ |
|||||
|
package awais.instagrabber.asyncs; |
||||
|
|
||||
|
import android.graphics.Bitmap; |
||||
|
import android.os.AsyncTask; |
||||
|
import android.util.Log; |
||||
|
|
||||
|
import org.json.JSONObject; |
||||
|
|
||||
|
import java.io.BufferedOutputStream; |
||||
|
import java.io.BufferedReader; |
||||
|
import java.io.ByteArrayOutputStream; |
||||
|
import java.io.IOException; |
||||
|
import java.io.InputStream; |
||||
|
import java.io.InputStreamReader; |
||||
|
import java.io.OutputStream; |
||||
|
import java.net.HttpURLConnection; |
||||
|
import java.net.URL; |
||||
|
import java.util.Date; |
||||
|
import java.util.HashMap; |
||||
|
import java.util.Map; |
||||
|
import java.util.Random; |
||||
|
import java.util.UUID; |
||||
|
|
||||
|
import awais.instagrabber.models.ImageUploadOptions; |
||||
|
import awais.instagrabber.utils.Utils; |
||||
|
|
||||
|
public class ImageUploader extends AsyncTask<ImageUploadOptions, Void, ImageUploader.ImageUploadResponse> { |
||||
|
private static final String TAG = "ImageUploader"; |
||||
|
private static final long LOWER = 1000000000L; |
||||
|
private static final long UPPER = 9999999999L; |
||||
|
private OnImageUploadCompleteListener listener; |
||||
|
|
||||
|
protected ImageUploadResponse doInBackground(final ImageUploadOptions... imageUploadOptions) { |
||||
|
if (imageUploadOptions == null || imageUploadOptions.length == 0 || imageUploadOptions[0] == null) { |
||||
|
return null; |
||||
|
} |
||||
|
HttpURLConnection connection = null; |
||||
|
OutputStream out = null; |
||||
|
InputStream inputStream = null; |
||||
|
BufferedReader r = null; |
||||
|
ByteArrayOutputStream baos = null; |
||||
|
try { |
||||
|
final ImageUploadOptions options = imageUploadOptions[0]; |
||||
|
final Bitmap bitmap = options.getBitmap(); |
||||
|
baos = new ByteArrayOutputStream(); |
||||
|
final boolean compressResult = bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos); |
||||
|
if (!compressResult) { |
||||
|
Log.e(TAG, "Compress result was false!"); |
||||
|
return null; |
||||
|
} |
||||
|
final byte[] bytes = baos.toByteArray(); |
||||
|
final String contentLength = String.valueOf(bytes.length); |
||||
|
final Map<String, String> headers = new HashMap<>(); |
||||
|
final String uploadId = String.valueOf(new Date().getTime()); |
||||
|
final long random = LOWER + new Random().nextLong() * (UPPER - LOWER + 1); |
||||
|
final String name = String.format("%s_0_%s", uploadId, random); |
||||
|
final String waterfallId = options.getWaterfallId() != null ? options.getWaterfallId() : UUID.randomUUID().toString(); |
||||
|
headers.put("X-Entity-Type", "image/jpeg"); |
||||
|
headers.put("Offset", "0"); |
||||
|
headers.put("X_FB_PHOTO_WATERFALL_ID", waterfallId); |
||||
|
headers.put("X-Instagram-Rupload-Params", new JSONObject(createPhotoRuploadParams(options, uploadId)).toString()); |
||||
|
headers.put("X-Entity-Name", name); |
||||
|
headers.put("X-Entity-Length", contentLength); |
||||
|
headers.put("Content-Type", "application/octet-stream"); |
||||
|
headers.put("Content-Length", contentLength); |
||||
|
headers.put("Accept-Encoding", "gzip"); |
||||
|
final String url = "https://www.instagram.com/rupload_igphoto/" + name + "/"; |
||||
|
connection = (HttpURLConnection) new URL(url).openConnection(); |
||||
|
connection.setRequestMethod("POST"); |
||||
|
connection.setUseCaches(false); |
||||
|
connection.setDoOutput(true); |
||||
|
Utils.setConnectionHeaders(connection, headers); |
||||
|
out = new BufferedOutputStream(connection.getOutputStream()); |
||||
|
out.write(bytes); |
||||
|
out.flush(); |
||||
|
final int responseCode = connection.getResponseCode(); |
||||
|
Log.d(TAG, "response: " + responseCode); |
||||
|
final String responseCodeString = String.valueOf(responseCode); |
||||
|
final InputStream responseInputStream = responseCodeString.startsWith("4") || responseCodeString.startsWith("5") |
||||
|
? connection.getErrorStream() : connection.getInputStream(); |
||||
|
r = new BufferedReader(new InputStreamReader(responseInputStream)); |
||||
|
final StringBuilder builder = new StringBuilder(); |
||||
|
for (String line = r.readLine(); line != null; line = r.readLine()) { |
||||
|
if (builder.length() != 0) { |
||||
|
builder.append("\n"); |
||||
|
} |
||||
|
builder.append(line); |
||||
|
} |
||||
|
return new ImageUploadResponse(responseCode, new JSONObject(builder.toString())); |
||||
|
} catch (Exception ex) { |
||||
|
Log.e(TAG, "Image upload error:", ex); |
||||
|
} finally { |
||||
|
if (r != null) { |
||||
|
try { |
||||
|
r.close(); |
||||
|
} catch (IOException ignored) {} |
||||
|
} |
||||
|
if (inputStream != null) { |
||||
|
try { |
||||
|
inputStream.close(); |
||||
|
} catch (IOException ignored) {} |
||||
|
} |
||||
|
if (out != null) { |
||||
|
try { |
||||
|
out.close(); |
||||
|
} catch (IOException ignored) {} |
||||
|
} |
||||
|
if (connection != null) { |
||||
|
connection.disconnect(); |
||||
|
} |
||||
|
if (baos != null) { |
||||
|
try { |
||||
|
baos.close(); |
||||
|
} catch (IOException ignored) {} |
||||
|
} |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onPostExecute(final ImageUploadResponse response) { |
||||
|
if (listener != null) { |
||||
|
listener.onImageUploadComplete(response); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private Map<String, String> createPhotoRuploadParams(final ImageUploadOptions options, final String uploadId) { |
||||
|
final Map<String, Integer> retryContext = new HashMap<>(); |
||||
|
retryContext.put("num_step_auto_retry", 0); |
||||
|
retryContext.put("num_reupload", 0); |
||||
|
retryContext.put("num_step_manual_retry", 0); |
||||
|
final String retryContextString = new JSONObject(retryContext).toString(); |
||||
|
final Map<String, String> params = new HashMap<>(); |
||||
|
params.put("retry_context", retryContextString); |
||||
|
params.put("media_type", "1"); |
||||
|
params.put("upload_id", uploadId); |
||||
|
params.put("xsharing_user_ids", "[]"); |
||||
|
final Map<String, String> imageCompression = new HashMap<>(); |
||||
|
imageCompression.put("lib_name", "moz"); |
||||
|
imageCompression.put("lib_version", "3.1.m"); |
||||
|
imageCompression.put("quality", "80"); |
||||
|
params.put("image_compression", new JSONObject(imageCompression).toString()); |
||||
|
if (options.isSidecar()) { |
||||
|
params.put("is_sidecar", "1"); |
||||
|
} |
||||
|
return params; |
||||
|
} |
||||
|
|
||||
|
public void setOnTaskCompleteListener(final OnImageUploadCompleteListener listener) { |
||||
|
if (listener != null) { |
||||
|
this.listener = listener; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public interface OnImageUploadCompleteListener { |
||||
|
void onImageUploadComplete(ImageUploadResponse response); |
||||
|
} |
||||
|
|
||||
|
public static class ImageUploadResponse { |
||||
|
private int responseCode; |
||||
|
private JSONObject response; |
||||
|
|
||||
|
public ImageUploadResponse(int responseCode, JSONObject response) { |
||||
|
this.responseCode = responseCode; |
||||
|
this.response = response; |
||||
|
} |
||||
|
|
||||
|
public int getResponseCode() { |
||||
|
return responseCode; |
||||
|
} |
||||
|
|
||||
|
public JSONObject getResponse() { |
||||
|
return response; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,207 @@ |
|||||
|
package awais.instagrabber.asyncs.direct_messages; |
||||
|
|
||||
|
import android.os.AsyncTask; |
||||
|
import android.util.Log; |
||||
|
|
||||
|
import org.json.JSONObject; |
||||
|
|
||||
|
import java.io.BufferedReader; |
||||
|
import java.io.DataOutputStream; |
||||
|
import java.io.IOException; |
||||
|
import java.io.InputStreamReader; |
||||
|
import java.io.UnsupportedEncodingException; |
||||
|
import java.net.HttpURLConnection; |
||||
|
import java.net.URL; |
||||
|
import java.net.URLEncoder; |
||||
|
import java.util.Collections; |
||||
|
import java.util.HashMap; |
||||
|
import java.util.Map; |
||||
|
import java.util.UUID; |
||||
|
|
||||
|
import awais.instagrabber.utils.Constants; |
||||
|
import awais.instagrabber.utils.Utils; |
||||
|
|
||||
|
import static awais.instagrabber.utils.Utils.settingsHelper; |
||||
|
|
||||
|
public class DirectThreadBroadcaster extends AsyncTask<DirectThreadBroadcaster.BroadcastOptions, Void, DirectThreadBroadcaster.DirectThreadBroadcastResponse> { |
||||
|
private static final String TAG = "DirectThreadBroadcaster"; |
||||
|
|
||||
|
private final String threadId; |
||||
|
|
||||
|
private OnBroadcastCompleteListener listener; |
||||
|
|
||||
|
public DirectThreadBroadcaster(String threadId) { |
||||
|
this.threadId = threadId; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected DirectThreadBroadcastResponse doInBackground(final BroadcastOptions... broadcastOptionsArray) { |
||||
|
if (broadcastOptionsArray == null || broadcastOptionsArray.length == 0 || broadcastOptionsArray[0] == null) { |
||||
|
return null; |
||||
|
} |
||||
|
final BroadcastOptions broadcastOptions = broadcastOptionsArray[0]; |
||||
|
final String cookie = settingsHelper.getString(Constants.COOKIE); |
||||
|
final String cc = UUID.randomUUID().toString(); |
||||
|
final Map<String, String> form = new HashMap<>(); |
||||
|
form.put("_csrftoken", cookie.split("csrftoken=")[1].split(";")[0]); |
||||
|
form.put("_uid", Utils.getUserIdFromCookie(cookie)); |
||||
|
form.put("__uuid", settingsHelper.getString(Constants.DEVICE_UUID)); |
||||
|
form.put("client_context", cc); |
||||
|
form.put("mutation_token", cc); |
||||
|
form.putAll(broadcastOptions.getFormMap()); |
||||
|
form.put("thread_ids", String.format("[%s]", threadId)); |
||||
|
form.put("action", "send_item"); |
||||
|
final String message = new JSONObject(form).toString(); |
||||
|
final String content = Utils.sign(message); |
||||
|
final String url = "https://i.instagram.com/api/v1/direct_v2/threads/broadcast/" + broadcastOptions.getItemType().getValue() + "/"; |
||||
|
HttpURLConnection connection = null; |
||||
|
DataOutputStream outputStream = null; |
||||
|
BufferedReader r = null; |
||||
|
try { |
||||
|
connection = (HttpURLConnection) new URL(url).openConnection(); |
||||
|
connection.setRequestMethod("POST"); |
||||
|
connection.setRequestProperty("User-Agent", Constants.I_USER_AGENT); |
||||
|
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); |
||||
|
if (content != null) { |
||||
|
connection.setRequestProperty("Content-Length", "" + content.getBytes().length); |
||||
|
} |
||||
|
connection.setUseCaches(false); |
||||
|
connection.setDoOutput(true); |
||||
|
outputStream = new DataOutputStream(connection.getOutputStream()); |
||||
|
outputStream.writeBytes(content); |
||||
|
outputStream.flush(); |
||||
|
final int responseCode = connection.getResponseCode(); |
||||
|
if (responseCode != HttpURLConnection.HTTP_OK) { |
||||
|
Log.d(TAG, responseCode + ": " + content + ": " + cookie); |
||||
|
return new DirectThreadBroadcastResponse(responseCode, null); |
||||
|
} |
||||
|
r = new BufferedReader(new InputStreamReader(connection.getInputStream())); |
||||
|
final StringBuilder builder = new StringBuilder(); |
||||
|
for (String line = r.readLine(); line != null; line = r.readLine()) { |
||||
|
if (builder.length() != 0) { |
||||
|
builder.append("\n"); |
||||
|
} |
||||
|
builder.append(line); |
||||
|
} |
||||
|
return new DirectThreadBroadcastResponse(responseCode, new JSONObject(builder.toString())); |
||||
|
} catch (Exception e) { |
||||
|
Log.e(TAG, "Error", e); |
||||
|
} finally { |
||||
|
if (r != null) { |
||||
|
try { |
||||
|
r.close(); |
||||
|
} catch (IOException ignored) { |
||||
|
} |
||||
|
} |
||||
|
if (outputStream != null) { |
||||
|
try { |
||||
|
outputStream.close(); |
||||
|
} catch (IOException ignored) { |
||||
|
} |
||||
|
} |
||||
|
if (connection != null) { |
||||
|
connection.disconnect(); |
||||
|
} |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onPostExecute(final DirectThreadBroadcastResponse result) { |
||||
|
if (listener != null) { |
||||
|
listener.onTaskComplete(result); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void setOnTaskCompleteListener(final OnBroadcastCompleteListener listener) { |
||||
|
if (listener != null) { |
||||
|
this.listener = listener; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public interface OnBroadcastCompleteListener { |
||||
|
void onTaskComplete(DirectThreadBroadcastResponse response); |
||||
|
} |
||||
|
|
||||
|
public enum ItemType { |
||||
|
TEXT("text"), |
||||
|
IMAGE("configure_photo"); |
||||
|
|
||||
|
private final String value; |
||||
|
|
||||
|
ItemType(final String value) { |
||||
|
this.value = value; |
||||
|
} |
||||
|
|
||||
|
public String getValue() { |
||||
|
return value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static abstract class BroadcastOptions { |
||||
|
private final ItemType itemType; |
||||
|
|
||||
|
public BroadcastOptions(final ItemType itemType) { |
||||
|
this.itemType = itemType; |
||||
|
} |
||||
|
|
||||
|
public ItemType getItemType() { |
||||
|
return itemType; |
||||
|
} |
||||
|
|
||||
|
abstract Map<String, String> getFormMap(); |
||||
|
} |
||||
|
|
||||
|
public static class TextBroadcastOptions extends BroadcastOptions { |
||||
|
private final String text; |
||||
|
|
||||
|
public TextBroadcastOptions(String text) throws UnsupportedEncodingException { |
||||
|
super(ItemType.TEXT); |
||||
|
this.text = URLEncoder.encode(text, "UTF-8") |
||||
|
.replaceAll("\\+", "%20").replaceAll("%21", "!").replaceAll("%27", "'") |
||||
|
.replaceAll("%28", "(").replaceAll("%29", ")").replaceAll("%7E", "~"); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
Map<String, String> getFormMap() { |
||||
|
return Collections.singletonMap("text", text); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static class ImageBroadcastOptions extends BroadcastOptions { |
||||
|
final boolean allowFullAspectRatio; |
||||
|
final String uploadId; |
||||
|
|
||||
|
public ImageBroadcastOptions(final boolean allowFullAspectRatio, final String uploadId) { |
||||
|
super(ItemType.IMAGE); |
||||
|
this.allowFullAspectRatio = allowFullAspectRatio; |
||||
|
this.uploadId = uploadId; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
Map<String, String> getFormMap() { |
||||
|
final Map<String, String> form = new HashMap<>(); |
||||
|
form.put("allow_full_aspect_ratio", String.valueOf(allowFullAspectRatio)); |
||||
|
form.put("upload_id", uploadId); |
||||
|
return form; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static class DirectThreadBroadcastResponse { |
||||
|
private int responseCode; |
||||
|
private JSONObject response; |
||||
|
|
||||
|
public DirectThreadBroadcastResponse(int responseCode, JSONObject response) { |
||||
|
this.responseCode = responseCode; |
||||
|
this.response = response; |
||||
|
} |
||||
|
|
||||
|
public int getResponseCode() { |
||||
|
return responseCode; |
||||
|
} |
||||
|
|
||||
|
public JSONObject getResponse() { |
||||
|
return response; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,149 @@ |
|||||
|
package awais.instagrabber.fragments.directmessages; |
||||
|
|
||||
|
import android.os.AsyncTask; |
||||
|
import android.os.Bundle; |
||||
|
import android.util.Log; |
||||
|
import android.view.LayoutInflater; |
||||
|
import android.view.View; |
||||
|
import android.view.ViewGroup; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
import androidx.annotation.Nullable; |
||||
|
import androidx.fragment.app.Fragment; |
||||
|
import androidx.fragment.app.FragmentActivity; |
||||
|
import androidx.lifecycle.MutableLiveData; |
||||
|
import androidx.lifecycle.ViewModel; |
||||
|
import androidx.lifecycle.ViewModelProvider; |
||||
|
import androidx.navigation.NavDirections; |
||||
|
import androidx.navigation.fragment.NavHostFragment; |
||||
|
import androidx.recyclerview.widget.LinearLayoutManager; |
||||
|
import androidx.recyclerview.widget.RecyclerView; |
||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; |
||||
|
|
||||
|
import java.util.Arrays; |
||||
|
import java.util.Collections; |
||||
|
import java.util.LinkedList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
import awais.instagrabber.BuildConfig; |
||||
|
import awais.instagrabber.adapters.DirectMessageInboxAdapter; |
||||
|
import awais.instagrabber.asyncs.direct_messages.InboxFetcher; |
||||
|
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader; |
||||
|
import awais.instagrabber.databinding.FragmentDirectMessagesInboxBinding; |
||||
|
import awais.instagrabber.interfaces.FetchListener; |
||||
|
import awais.instagrabber.models.direct_messages.InboxModel; |
||||
|
import awais.instagrabber.models.direct_messages.InboxThreadModel; |
||||
|
import awais.instagrabber.utils.Utils; |
||||
|
|
||||
|
public class DirectMessageInboxFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener { |
||||
|
private static final String TAG = "DirectMessagesInboxFrag"; |
||||
|
|
||||
|
private FragmentActivity fragmentActivity; |
||||
|
private SwipeRefreshLayout root; |
||||
|
private RecyclerView inboxList; |
||||
|
private RecyclerLazyLoader lazyLoader; |
||||
|
private LinearLayoutManager layoutManager; |
||||
|
private String endCursor; |
||||
|
private AsyncTask<Void, Void, InboxModel> currentlyRunning; |
||||
|
private InboxThreadModelListViewModel listViewModel; |
||||
|
|
||||
|
private final FetchListener<InboxModel> fetchListener = new FetchListener<InboxModel>() { |
||||
|
@Override |
||||
|
public void doBefore() { |
||||
|
root.setRefreshing(true); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onResult(final InboxModel inboxModel) { |
||||
|
if (inboxModel != null) { |
||||
|
endCursor = inboxModel.getOldestCursor(); |
||||
|
if ("MINCURSOR".equals(endCursor) || "MAXCURSOR".equals(endCursor)) |
||||
|
endCursor = null; |
||||
|
// todo get request / unseen count from inboxModel |
||||
|
final InboxThreadModel[] threads = inboxModel.getThreads(); |
||||
|
if (threads != null && threads.length > 0) { |
||||
|
List<InboxThreadModel> list = listViewModel.getList().getValue(); |
||||
|
list = list != null ? new LinkedList<>(list) : new LinkedList<>(); |
||||
|
// final int oldSize = list != null ? list.size() : 0; |
||||
|
final List<InboxThreadModel> newList = Arrays.asList(threads); |
||||
|
list.addAll(newList); |
||||
|
listViewModel.getList().postValue(list); |
||||
|
} |
||||
|
} |
||||
|
root.setRefreshing(false); |
||||
|
stopCurrentExecutor(); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
@Override |
||||
|
public void onCreate(@Nullable final Bundle savedInstanceState) { |
||||
|
super.onCreate(savedInstanceState); |
||||
|
fragmentActivity = requireActivity(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public View onCreateView(@NonNull final LayoutInflater inflater, |
||||
|
final ViewGroup container, |
||||
|
final Bundle savedInstanceState) { |
||||
|
if (root != null) { |
||||
|
return root; |
||||
|
} |
||||
|
final FragmentDirectMessagesInboxBinding binding = FragmentDirectMessagesInboxBinding.inflate(inflater, container, false); |
||||
|
root = binding.getRoot(); |
||||
|
root.setOnRefreshListener(this); |
||||
|
inboxList = binding.inboxList; |
||||
|
inboxList.setHasFixedSize(true); |
||||
|
layoutManager = new LinearLayoutManager(requireContext()); |
||||
|
inboxList.setLayoutManager(layoutManager); |
||||
|
final DirectMessageInboxAdapter inboxAdapter = new DirectMessageInboxAdapter(inboxThreadModel -> { |
||||
|
final NavDirections action = DirectMessageInboxFragmentDirections.actionDMInboxFragmentToDMThreadFragment(inboxThreadModel.getThreadId(), inboxThreadModel.getThreadTitle()); |
||||
|
NavHostFragment.findNavController(this).navigate(action); |
||||
|
}); |
||||
|
inboxList.setAdapter(inboxAdapter); |
||||
|
listViewModel = new ViewModelProvider(fragmentActivity).get(InboxThreadModelListViewModel.class); |
||||
|
listViewModel.getList().observe(fragmentActivity, inboxAdapter::submitList); |
||||
|
initData(); |
||||
|
return root; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onRefresh() { |
||||
|
endCursor = null; |
||||
|
lazyLoader.resetState(); |
||||
|
listViewModel.getList().postValue(Collections.emptyList()); |
||||
|
stopCurrentExecutor(); |
||||
|
currentlyRunning = new InboxFetcher(null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
||||
|
} |
||||
|
|
||||
|
private void initData() { |
||||
|
lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> { |
||||
|
if (!Utils.isEmpty(endCursor)) |
||||
|
currentlyRunning = new InboxFetcher(endCursor, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
||||
|
endCursor = null; |
||||
|
}); |
||||
|
inboxList.addOnScrollListener(lazyLoader); |
||||
|
stopCurrentExecutor(); |
||||
|
currentlyRunning = new InboxFetcher(null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
||||
|
} |
||||
|
|
||||
|
private void stopCurrentExecutor() { |
||||
|
if (currentlyRunning != null) { |
||||
|
try { |
||||
|
currentlyRunning.cancel(true); |
||||
|
} catch (final Exception e) { |
||||
|
if (BuildConfig.DEBUG) Log.e(TAG, "", e); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static class InboxThreadModelListViewModel extends ViewModel { |
||||
|
private MutableLiveData<List<InboxThreadModel>> list; |
||||
|
|
||||
|
public MutableLiveData<List<InboxThreadModel>> getList() { |
||||
|
if (list == null) { |
||||
|
list = new MutableLiveData<>(); |
||||
|
} |
||||
|
return list; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,351 @@ |
|||||
|
package awais.instagrabber.fragments.directmessages; |
||||
|
|
||||
|
import android.app.Activity; |
||||
|
import android.content.Intent; |
||||
|
import android.graphics.Bitmap; |
||||
|
import android.graphics.BitmapFactory; |
||||
|
import android.net.Uri; |
||||
|
import android.os.AsyncTask; |
||||
|
import android.os.Bundle; |
||||
|
import android.os.Handler; |
||||
|
import android.util.Log; |
||||
|
import android.view.LayoutInflater; |
||||
|
import android.view.View; |
||||
|
import android.view.ViewGroup; |
||||
|
import android.widget.LinearLayout; |
||||
|
import android.widget.Toast; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
import androidx.annotation.Nullable; |
||||
|
import androidx.fragment.app.Fragment; |
||||
|
import androidx.fragment.app.FragmentActivity; |
||||
|
import androidx.lifecycle.MutableLiveData; |
||||
|
import androidx.lifecycle.ViewModel; |
||||
|
import androidx.lifecycle.ViewModelProvider; |
||||
|
import androidx.recyclerview.widget.LinearLayoutManager; |
||||
|
import androidx.recyclerview.widget.RecyclerView; |
||||
|
|
||||
|
import org.json.JSONException; |
||||
|
import org.json.JSONObject; |
||||
|
|
||||
|
import java.io.File; |
||||
|
import java.io.FileNotFoundException; |
||||
|
import java.io.IOException; |
||||
|
import java.io.InputStream; |
||||
|
import java.io.UnsupportedEncodingException; |
||||
|
import java.net.HttpURLConnection; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.Arrays; |
||||
|
import java.util.Collections; |
||||
|
import java.util.LinkedList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
import awais.instagrabber.R; |
||||
|
import awais.instagrabber.activities.PostViewer; |
||||
|
import awais.instagrabber.activities.ProfileViewer; |
||||
|
import awais.instagrabber.activities.StoryViewer; |
||||
|
import awais.instagrabber.adapters.MessageItemsAdapter; |
||||
|
import awais.instagrabber.asyncs.ImageUploader; |
||||
|
import awais.instagrabber.asyncs.direct_messages.DirectMessageInboxThreadFetcher; |
||||
|
import awais.instagrabber.asyncs.direct_messages.DirectThreadBroadcaster; |
||||
|
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader; |
||||
|
import awais.instagrabber.databinding.FragmentDirectMessagesThreadBinding; |
||||
|
import awais.instagrabber.interfaces.FetchListener; |
||||
|
import awais.instagrabber.interfaces.MentionClickListener; |
||||
|
import awais.instagrabber.models.ImageUploadOptions; |
||||
|
import awais.instagrabber.models.PostModel; |
||||
|
import awais.instagrabber.models.ProfileModel; |
||||
|
import awais.instagrabber.models.StoryModel; |
||||
|
import awais.instagrabber.models.direct_messages.DirectItemModel; |
||||
|
import awais.instagrabber.models.direct_messages.InboxThreadModel; |
||||
|
import awais.instagrabber.models.enums.DirectItemType; |
||||
|
import awais.instagrabber.models.enums.DownloadMethod; |
||||
|
import awais.instagrabber.models.enums.UserInboxDirection; |
||||
|
import awais.instagrabber.utils.Constants; |
||||
|
import awais.instagrabber.utils.Utils; |
||||
|
|
||||
|
public class DirectMessageThreadFragment extends Fragment { |
||||
|
private static final String TAG = "DirectMessagesThreadFmt"; |
||||
|
private static final int PICK_IMAGE = 100; |
||||
|
|
||||
|
private FragmentActivity fragmentActivity; |
||||
|
private String threadId; |
||||
|
private String cursor; |
||||
|
private FragmentDirectMessagesThreadBinding binding; |
||||
|
private DirectItemModelListViewModel listViewModel; |
||||
|
private RecyclerView messageList; |
||||
|
private boolean hasSentSomething; |
||||
|
private boolean hasOlder = true; |
||||
|
|
||||
|
private final ProfileModel myProfileHolder = ProfileModel.getDefaultProfileModel(); |
||||
|
private final List<ProfileModel> users = new ArrayList<>(); |
||||
|
private final List<ProfileModel> leftUsers = new ArrayList<>(); |
||||
|
|
||||
|
private final View.OnClickListener clickListener = v -> { |
||||
|
if (v == binding.commentSend) { |
||||
|
final String text = binding.commentText.getText().toString(); |
||||
|
if (Utils.isEmpty(text)) { |
||||
|
Toast.makeText(requireContext(), R.string.comment_send_empty_comment, Toast.LENGTH_SHORT).show(); |
||||
|
return; |
||||
|
} |
||||
|
sendText(text); |
||||
|
return; |
||||
|
} |
||||
|
if (v == binding.image) { |
||||
|
final Intent intent = new Intent(); |
||||
|
intent.setType("image/*"); |
||||
|
intent.setAction(Intent.ACTION_GET_CONTENT); |
||||
|
startActivityForResult(Intent.createChooser(intent, getString(R.string.select_picture)), PICK_IMAGE); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
private final FetchListener<InboxThreadModel> fetchListener = new FetchListener<InboxThreadModel>() { |
||||
|
@Override |
||||
|
public void doBefore() { |
||||
|
binding.swipeRefreshLayout.setRefreshing(true); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onResult(final InboxThreadModel result) { |
||||
|
if (result == null && ("MINCURSOR".equals(cursor) || "MAXCURSOR".equals(cursor) || Utils.isEmpty(cursor))) |
||||
|
Toast.makeText(requireContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); |
||||
|
|
||||
|
if (result != null) { |
||||
|
cursor = result.getOldestCursor(); |
||||
|
hasOlder = result.hasOlder(); |
||||
|
if ("MINCURSOR".equals(cursor) || "MAXCURSOR".equals(cursor)) { |
||||
|
cursor = null; |
||||
|
} |
||||
|
users.clear(); |
||||
|
users.addAll(Arrays.asList(result.getUsers())); |
||||
|
leftUsers.clear(); |
||||
|
leftUsers.addAll(Arrays.asList(result.getLeftUsers())); |
||||
|
|
||||
|
// thread title is already comma separated username, so no need to set by ourselves |
||||
|
// String[] users = new String[result.getUsers().length]; |
||||
|
// for (int i = 0; i < users.length; ++i) { |
||||
|
// users[i] = result.getUsers()[i].getUsername(); |
||||
|
// } |
||||
|
|
||||
|
List<DirectItemModel> list = listViewModel.getList().getValue(); |
||||
|
final List<DirectItemModel> newList = Arrays.asList(result.getItems()); |
||||
|
list = list != null ? new LinkedList<>(list) : new LinkedList<>(); |
||||
|
if (hasSentSomething) { |
||||
|
list = newList; |
||||
|
hasSentSomething = false; |
||||
|
final Handler handler = new Handler(); |
||||
|
handler.postDelayed(() -> { |
||||
|
if (messageList != null) { |
||||
|
messageList.smoothScrollToPosition(0); |
||||
|
} |
||||
|
}, 200); |
||||
|
} else { |
||||
|
list.addAll(newList); |
||||
|
} |
||||
|
listViewModel.getList().postValue(list); |
||||
|
} |
||||
|
binding.swipeRefreshLayout.setRefreshing(false); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
@Override |
||||
|
public void onCreate(@Nullable final Bundle savedInstanceState) { |
||||
|
super.onCreate(savedInstanceState); |
||||
|
fragmentActivity = requireActivity(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public View onCreateView(@NonNull final LayoutInflater inflater, |
||||
|
final ViewGroup container, |
||||
|
final Bundle savedInstanceState) { |
||||
|
binding = FragmentDirectMessagesThreadBinding.inflate(inflater, container, false); |
||||
|
final LinearLayout root = binding.getRoot(); |
||||
|
if (getArguments() == null) { |
||||
|
return root; |
||||
|
} |
||||
|
threadId = DirectMessageThreadFragmentArgs.fromBundle(getArguments()).getThreadId(); |
||||
|
binding.swipeRefreshLayout.setEnabled(false); |
||||
|
messageList = binding.messageList; |
||||
|
messageList.setHasFixedSize(true); |
||||
|
binding.commentSend.setOnClickListener(clickListener); |
||||
|
binding.image.setOnClickListener(clickListener); |
||||
|
final LinearLayoutManager layoutManager = new LinearLayoutManager(requireContext()); |
||||
|
layoutManager.setReverseLayout(true); |
||||
|
// layoutManager.setStackFromEnd(true); |
||||
|
messageList.setLayoutManager(layoutManager); |
||||
|
messageList.addOnScrollListener(new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> { |
||||
|
if (Utils.isEmpty(cursor) || !hasOlder) { |
||||
|
return; |
||||
|
} |
||||
|
new DirectMessageInboxThreadFetcher(threadId, UserInboxDirection.OLDER, cursor, fetchListener).execute(); // serial because we don't want messages to be randomly ordered |
||||
|
})); |
||||
|
|
||||
|
final View.OnClickListener onClickListener = v -> { |
||||
|
Object tag = v.getTag(); |
||||
|
if (tag instanceof DirectItemModel) { |
||||
|
final DirectItemModel directItemModel = (DirectItemModel) tag; |
||||
|
final DirectItemType itemType = directItemModel.getItemType(); |
||||
|
switch (itemType) { |
||||
|
case MEDIA_SHARE: |
||||
|
startActivity(new Intent(requireContext(), PostViewer.class) |
||||
|
.putExtra(Constants.EXTRAS_POST, new PostModel(directItemModel.getMediaModel().getCode(), false))); |
||||
|
break; |
||||
|
case LINK: |
||||
|
Intent linkIntent = new Intent(Intent.ACTION_VIEW); |
||||
|
linkIntent.setData(Uri.parse(directItemModel.getLinkModel().getLinkContext().getLinkUrl())); |
||||
|
startActivity(linkIntent); |
||||
|
break; |
||||
|
case TEXT: |
||||
|
case REEL_SHARE: |
||||
|
Utils.copyText(v.getContext(), directItemModel.getText()); |
||||
|
Toast.makeText(v.getContext(), R.string.clipboard_copied, Toast.LENGTH_SHORT).show(); |
||||
|
break; |
||||
|
case RAVEN_MEDIA: |
||||
|
case MEDIA: |
||||
|
final ProfileModel user = getUser(directItemModel.getUserId()); |
||||
|
Utils.dmDownload(requireContext(), user.getUsername(), DownloadMethod.DOWNLOAD_DIRECT, Collections.singletonList(itemType == DirectItemType.MEDIA ? directItemModel.getMediaModel() : directItemModel.getRavenMediaModel().getMedia())); |
||||
|
Toast.makeText(v.getContext(), R.string.downloader_downloading_media, Toast.LENGTH_SHORT).show(); |
||||
|
break; |
||||
|
case STORY_SHARE: |
||||
|
if (directItemModel.getReelShare() != null) { |
||||
|
StoryModel sm = new StoryModel( |
||||
|
directItemModel.getReelShare().getReelId(), |
||||
|
directItemModel.getReelShare().getMedia().getVideoUrl(), |
||||
|
directItemModel.getReelShare().getMedia().getMediaType(), |
||||
|
directItemModel.getTimestamp(), |
||||
|
directItemModel.getReelShare().getReelOwnerName(), |
||||
|
String.valueOf(directItemModel.getReelShare().getReelOwnerId()), |
||||
|
false |
||||
|
); |
||||
|
sm.setVideoUrl(directItemModel.getReelShare().getMedia().getVideoUrl()); |
||||
|
StoryModel[] sms = {sm}; |
||||
|
startActivity(new Intent(requireContext(), StoryViewer.class) |
||||
|
.putExtra(Constants.EXTRAS_USERNAME, directItemModel.getReelShare().getReelOwnerName()) |
||||
|
.putExtra(Constants.EXTRAS_STORIES, sms) |
||||
|
); |
||||
|
} else if (directItemModel.getText() != null && directItemModel.getText().toString().contains("@")) { |
||||
|
searchUsername(directItemModel.getText().toString().split("@")[1].split(" ")[0]); |
||||
|
} |
||||
|
break; |
||||
|
case PLACEHOLDER: |
||||
|
if (directItemModel.getText().toString().contains("@")) |
||||
|
searchUsername(directItemModel.getText().toString().split("@")[1].split(" ")[0]); |
||||
|
break; |
||||
|
default: |
||||
|
Log.d("austin_debug", "unsupported type " + itemType); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
final MentionClickListener mentionClickListener = (view, text, isHashtag) -> searchUsername(text); |
||||
|
final MessageItemsAdapter adapter = new MessageItemsAdapter(users, leftUsers, onClickListener, mentionClickListener); |
||||
|
messageList.setAdapter(adapter); |
||||
|
listViewModel = new ViewModelProvider(fragmentActivity).get(DirectItemModelListViewModel.class); |
||||
|
listViewModel.getList().observe(fragmentActivity, adapter::submitList); |
||||
|
new DirectMessageInboxThreadFetcher(threadId, UserInboxDirection.OLDER, null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
||||
|
return root; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { |
||||
|
super.onActivityResult(requestCode, resultCode, data); |
||||
|
if (requestCode == PICK_IMAGE && resultCode == Activity.RESULT_OK) { |
||||
|
if (data == null || data.getData() == null) { |
||||
|
Log.w(TAG, "data is null!"); |
||||
|
return; |
||||
|
} |
||||
|
final Uri uri = data.getData(); |
||||
|
sendImage(uri); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onDestroy() { |
||||
|
super.onDestroy(); |
||||
|
listViewModel.getList().postValue(Collections.emptyList()); |
||||
|
} |
||||
|
|
||||
|
private void sendText(final String text) { |
||||
|
final DirectThreadBroadcaster.TextBroadcastOptions options; |
||||
|
try { |
||||
|
options = new DirectThreadBroadcaster.TextBroadcastOptions(text); |
||||
|
} catch (UnsupportedEncodingException e) { |
||||
|
Log.e(TAG, "Error", e); |
||||
|
return; |
||||
|
} |
||||
|
broadcast(options, result -> { |
||||
|
if (result == null || result.getResponseCode() != HttpURLConnection.HTTP_OK) { |
||||
|
Toast.makeText(requireContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); |
||||
|
return; |
||||
|
} |
||||
|
binding.commentText.setText(""); |
||||
|
// binding.commentText.clearFocus(); |
||||
|
hasSentSomething = true; |
||||
|
new DirectMessageInboxThreadFetcher(threadId, UserInboxDirection.OLDER, null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private void sendImage(final Uri imageUri) { |
||||
|
try(InputStream inputStream = requireContext().getContentResolver().openInputStream(imageUri)) { |
||||
|
final Bitmap bitmap = BitmapFactory.decodeStream(inputStream); |
||||
|
// Upload Image |
||||
|
final ImageUploader imageUploader = new ImageUploader(); |
||||
|
imageUploader.setOnTaskCompleteListener(response -> { |
||||
|
if (response == null || response.getResponseCode() != HttpURLConnection.HTTP_OK) { |
||||
|
Toast.makeText(requireContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); |
||||
|
if (response != null && response.getResponse() != null) { |
||||
|
Log.e(TAG, response.getResponse().toString()); |
||||
|
} |
||||
|
return; |
||||
|
} |
||||
|
final JSONObject responseJson = response.getResponse(); |
||||
|
try { |
||||
|
final String uploadId = responseJson.getString("upload_id"); |
||||
|
// Broadcast |
||||
|
final DirectThreadBroadcaster.ImageBroadcastOptions options = new DirectThreadBroadcaster.ImageBroadcastOptions(true, uploadId); |
||||
|
hasSentSomething = true; |
||||
|
broadcast(options, onBroadcastCompleteListener -> new DirectMessageInboxThreadFetcher(threadId, UserInboxDirection.OLDER, null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)); |
||||
|
} catch (JSONException e) { |
||||
|
Log.e(TAG, "Error parsing json response", e); |
||||
|
} |
||||
|
}); |
||||
|
final ImageUploadOptions options = ImageUploadOptions.builder(bitmap).build(); |
||||
|
imageUploader.execute(options); |
||||
|
} |
||||
|
catch (IOException e) { |
||||
|
Log.e(TAG, "Error opening file", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void broadcast(final DirectThreadBroadcaster.BroadcastOptions broadcastOptions, final DirectThreadBroadcaster.OnBroadcastCompleteListener listener) { |
||||
|
final DirectThreadBroadcaster broadcaster = new DirectThreadBroadcaster(threadId); |
||||
|
broadcaster.setOnTaskCompleteListener(listener); |
||||
|
broadcaster.execute(broadcastOptions); |
||||
|
} |
||||
|
|
||||
|
@NonNull |
||||
|
private ProfileModel getUser(final long userId) { |
||||
|
ProfileModel result = myProfileHolder; |
||||
|
for (final ProfileModel user : users) { |
||||
|
if (Long.toString(userId).equals(user.getId())) result = user; |
||||
|
} |
||||
|
for (final ProfileModel leftUser : leftUsers) { |
||||
|
if (Long.toString(userId).equals(leftUser.getId())) result = leftUser; |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
private void searchUsername(final String text) { |
||||
|
startActivity(new Intent(requireContext(), ProfileViewer.class).putExtra(Constants.EXTRAS_USERNAME, text)); |
||||
|
} |
||||
|
|
||||
|
public static class DirectItemModelListViewModel extends ViewModel { |
||||
|
private MutableLiveData<List<DirectItemModel>> list; |
||||
|
|
||||
|
public MutableLiveData<List<DirectItemModel>> getList() { |
||||
|
if (list == null) { |
||||
|
list = new MutableLiveData<>(); |
||||
|
} |
||||
|
return list; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,62 @@ |
|||||
|
package awais.instagrabber.models; |
||||
|
|
||||
|
import android.graphics.Bitmap; |
||||
|
|
||||
|
public class ImageUploadOptions { |
||||
|
private final Bitmap bitmap; |
||||
|
private boolean isSidecar; |
||||
|
private String waterfallId; |
||||
|
|
||||
|
public static class Builder { |
||||
|
private Bitmap bitmap; |
||||
|
private boolean isSidecar; |
||||
|
private String waterfallId; |
||||
|
|
||||
|
public Builder(final Bitmap bitmap) { |
||||
|
this.bitmap = bitmap; |
||||
|
} |
||||
|
|
||||
|
public Builder setBitmap(final Bitmap bitmap) { |
||||
|
this.bitmap = bitmap; |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
public Builder setIsSidecar(final boolean isSidecar) { |
||||
|
this.isSidecar = isSidecar; |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
public Builder setWaterfallId(final String waterfallId) { |
||||
|
this.waterfallId = waterfallId; |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
public ImageUploadOptions build() { |
||||
|
return new ImageUploadOptions(bitmap, isSidecar, waterfallId); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static Builder builder(final Bitmap file) { |
||||
|
return new Builder(file); |
||||
|
} |
||||
|
|
||||
|
private ImageUploadOptions(final Bitmap bitmap, |
||||
|
final boolean isSidecar, |
||||
|
final String waterfallId) { |
||||
|
this.bitmap = bitmap; |
||||
|
this.isSidecar = isSidecar; |
||||
|
this.waterfallId = waterfallId; |
||||
|
} |
||||
|
|
||||
|
public Bitmap getBitmap() { |
||||
|
return bitmap; |
||||
|
} |
||||
|
|
||||
|
public boolean isSidecar() { |
||||
|
return isSidecar; |
||||
|
} |
||||
|
|
||||
|
public String getWaterfallId() { |
||||
|
return waterfallId; |
||||
|
} |
||||
|
} |
@ -1,6 +1,16 @@ |
|||||
package awais.instagrabber.models.enums; |
package awais.instagrabber.models.enums; |
||||
|
|
||||
public enum UserInboxDirection { |
public enum UserInboxDirection { |
||||
OLDER, |
|
||||
NEWER, |
|
||||
|
OLDER("older"), |
||||
|
NEWER("newer"); |
||||
|
|
||||
|
private final String value; |
||||
|
|
||||
|
UserInboxDirection(final String value) { |
||||
|
this.value = value; |
||||
|
} |
||||
|
|
||||
|
public String getValue() { |
||||
|
return value; |
||||
|
} |
||||
} |
} |
@ -0,0 +1,10 @@ |
|||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
android:width="24dp" |
||||
|
android:height="24dp" |
||||
|
android:viewportWidth="24" |
||||
|
android:viewportHeight="24" |
||||
|
android:tint="?attr/colorControlNormal"> |
||||
|
<path |
||||
|
android:fillColor="@android:color/white" |
||||
|
android:pathData="M19,5v14L5,19L5,5h14m0,-2L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM14.14,11.86l-3,3.87L9,13.14 6,17h12l-3.86,-5.14z"/> |
||||
|
</vector> |
@ -0,0 +1,10 @@ |
|||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
android:width="24dp" |
||||
|
android:height="24dp" |
||||
|
android:viewportWidth="24" |
||||
|
android:viewportHeight="24" |
||||
|
android:tint="?attr/colorControlNormal"> |
||||
|
<path |
||||
|
android:fillColor="@android:color/white" |
||||
|
android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/> |
||||
|
</vector> |
@ -0,0 +1,54 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:id="@+id/homeCoordinator" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent" |
||||
|
android:animateLayoutChanges="true" |
||||
|
android:fitsSystemWindows="true" |
||||
|
tools:context=".activities.DirectMessagesActivity"> |
||||
|
|
||||
|
<fragment |
||||
|
android:id="@+id/direct_messages_nav_host_fragment" |
||||
|
android:name="androidx.navigation.fragment.NavHostFragment" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent" |
||||
|
app:defaultNavHost="true" |
||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" |
||||
|
app:navGraph="@navigation/direct_messages_nav_graph" |
||||
|
tools:ignore="FragmentTagUsage" /> |
||||
|
|
||||
|
<com.google.android.material.appbar.AppBarLayout |
||||
|
android:id="@+id/appBarLayout" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
app:liftOnScroll="true"> |
||||
|
|
||||
|
<com.google.android.material.appbar.CollapsingToolbarLayout |
||||
|
android:id="@+id/collapsingToolbarLayout" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:animateLayoutChanges="true" |
||||
|
app:contentScrim="?attr/colorPrimary" |
||||
|
app:layout_scrollFlags="scroll|enterAlways|exitUntilCollapsed|snap" |
||||
|
app:scrimAnimationDuration="10"> |
||||
|
|
||||
|
<androidx.appcompat.widget.Toolbar |
||||
|
android:id="@+id/toolbar" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="?attr/actionBarSize" |
||||
|
app:layout_collapseMode="pin" |
||||
|
app:layout_scrollFlags="scroll|exitUntilCollapsed"> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/toolbar_title" |
||||
|
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent" |
||||
|
android:gravity="center_vertical" |
||||
|
android:text="@string/app_name" /> |
||||
|
</androidx.appcompat.widget.Toolbar> |
||||
|
</com.google.android.material.appbar.CollapsingToolbarLayout> |
||||
|
</com.google.android.material.appbar.AppBarLayout> |
||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout> |
@ -0,0 +1,10 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
android:id="@+id/swipeRefreshLayout" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent"> |
||||
|
<androidx.recyclerview.widget.RecyclerView |
||||
|
android:id="@+id/inbox_list" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent" /> |
||||
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout> |
@ -0,0 +1,26 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<navigation xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
|
android:id="@+id/direct_messages_nav_graph" |
||||
|
app:startDestination="@id/directMessagesInboxFragment"> |
||||
|
|
||||
|
<fragment |
||||
|
android:id="@+id/directMessagesInboxFragment" |
||||
|
android:name="awais.instagrabber.fragments.directmessages.DirectMessageInboxFragment" |
||||
|
android:label="DirectMessagesInboxFragment" > |
||||
|
<action |
||||
|
android:id="@+id/action_dMInboxFragment_to_dMThreadFragment" |
||||
|
app:destination="@id/directMessagesThreadFragment" /> |
||||
|
</fragment> |
||||
|
<fragment |
||||
|
android:id="@+id/directMessagesThreadFragment" |
||||
|
android:name="awais.instagrabber.fragments.directmessages.DirectMessageThreadFragment" |
||||
|
android:label="DirectMessagesThreadFragment"> |
||||
|
<argument |
||||
|
android:name="threadId" |
||||
|
app:argType="string" /> |
||||
|
<argument |
||||
|
android:name="title" |
||||
|
app:argType="string" /> |
||||
|
</fragment> |
||||
|
</navigation> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue