share profile by dm: preparation

This commit is contained in:
Austin Huang 2021-06-15 13:27:58 -04:00
parent 8edac65015
commit d76018596d
No known key found for this signature in database
GPG Key ID: 84C23AA04587A91F
6 changed files with 250 additions and 125 deletions

View File

@ -2,6 +2,7 @@ package awais.instagrabber.fragments.main;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Typeface;
import android.os.Bundle;
@ -33,6 +34,8 @@ import androidx.core.content.PermissionChecker;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavController;
import androidx.navigation.NavDirections;
@ -52,6 +55,7 @@ import java.util.Objects;
import java.util.Set;
import awais.instagrabber.R;
import awais.instagrabber.UserSearchNavGraphDirections;
import awais.instagrabber.activities.MainActivity;
import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.adapters.HighlightsAdapter;
@ -66,6 +70,8 @@ import awais.instagrabber.db.repositories.FavoriteRepository;
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
import awais.instagrabber.dialogs.ProfilePicDialogFragment;
import awais.instagrabber.fragments.PostViewV2Fragment;
import awais.instagrabber.fragments.UserSearchFragment;
import awais.instagrabber.fragments.UserSearchFragmentDirections;
import awais.instagrabber.managers.DirectMessagesManager;
import awais.instagrabber.managers.InboxManager;
import awais.instagrabber.models.HighlightModel;
@ -78,6 +84,7 @@ import awais.instagrabber.repositories.responses.FriendshipStatus;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.UserProfileContextLink;
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
import awais.instagrabber.utils.AppExecutors;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils;
@ -128,6 +135,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
private HighlightsViewModel highlightsViewModel;
private MenuItem blockMenuItem, restrictMenuItem, chainingMenuItem;
private MenuItem muteStoriesMenuItem, mutePostsMenuItem, removeFollowerMenuItem;
private MenuItem shareLinkMenuItem, shareDmMenuItem;
private boolean accountIsUpdated = false;
private boolean postsSetupDone = false;
private Set<Media> selectedFeedModels;
@ -143,6 +151,8 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
private ProfileFragmentViewModel viewModel;
private String csrfToken;
private String deviceUuid;
private MutableLiveData<Object> backStackSavedStateCollectionLiveData;
private MutableLiveData<Object> backStackSavedStateResultLiveData;
private final ServiceCallback<FriendshipChangeResponse> changeCb = new ServiceCallback<FriendshipChangeResponse>() {
@Override
@ -321,6 +331,32 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
}
}
};
private final Observer<Object> backStackSavedStateObserver = result -> {
if (result == null) return;
if ((result instanceof RankedRecipient)) {
// Log.d(TAG, "result: " + result);
final Context context = getContext();
if (context != null) {
Toast.makeText(context, R.string.sending, Toast.LENGTH_SHORT).show();
}
viewModel.shareDm((RankedRecipient) result);
} else if ((result instanceof Set)) {
try {
// Log.d(TAG, "result: " + result);
final Context context = getContext();
if (context != null) {
Toast.makeText(context, R.string.sending, Toast.LENGTH_SHORT).show();
}
//noinspection unchecked
viewModel.shareDm((Set<RankedRecipient>) result);
} catch (Exception e) {
Log.e(TAG, "share: ", e);
}
}
// clear result
backStackSavedStateCollectionLiveData.postValue(null);
backStackSavedStateResultLiveData.postValue(null);
};
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
@ -446,118 +482,152 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
if (removeFollowerMenuItem != null) {
removeFollowerMenuItem.setVisible(isNotMe && profileModel.getFriendshipStatus().getFollowedBy());
}
shareLinkMenuItem = menu.findItem(R.id.share_link);
if (shareLinkMenuItem != null) {
shareLinkMenuItem.setVisible(profileModel != null && !TextUtils.isEmpty(profileModel.getUsername()));
}
shareDmMenuItem = menu.findItem(R.id.share_dm);
if (shareDmMenuItem != null) {
shareDmMenuItem.setVisible(profileModel != null && profileModel.getPk() != 0L);
}
}
@Override
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
if (item.getItemId() == R.id.layout) {
showPostsLayoutPreferences();
return true;
switch (item.getItemId()) {
case R.id.layout: {
showPostsLayoutPreferences();
return true;
}
case R.id.restrict: {
if (!isLoggedIn) return false;
final String action = profileModel.getFriendshipStatus().isRestricted() ? "Unrestrict" : "Restrict";
friendshipRepository.toggleRestrict(
csrfToken,
deviceUuid,
profileModel.getPk(),
!profileModel.getFriendshipStatus().isRestricted(),
CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
if (throwable != null) {
Log.e(TAG, "Error while performing " + action, throwable);
return;
}
// Log.d(TAG, action + " success: " + response);
fetchProfileDetails();
}), Dispatchers.getIO())
);
return true;
}
case R.id.block: {
if (!isLoggedIn) return false;
// changeCb
friendshipRepository.changeBlock(
csrfToken,
myId,
deviceUuid,
profileModel.getFriendshipStatus().getBlocking(),
profileModel.getPk(),
CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
if (throwable != null) {
changeCb.onFailure(throwable);
return;
}
changeCb.onSuccess(response);
}), Dispatchers.getIO())
);
return true;
}
case R.id.chaining: {
if (!isLoggedIn) return false;
final Bundle bundle = new Bundle();
bundle.putString("type", "chaining");
bundle.putLong("targetId", profileModel.getPk());
NavHostFragment.findNavController(this).navigate(R.id.action_global_notificationsViewerFragment, bundle);
return true;
}
case R.id.mute_stories: {
if (!isLoggedIn) return false;
final String action = profileModel.getFriendshipStatus().isMutingReel() ? "Unmute stories" : "Mute stories";
friendshipRepository.changeMute(
csrfToken,
myId,
deviceUuid,
profileModel.getFriendshipStatus().isMutingReel(),
profileModel.getPk(),
true,
CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
if (throwable != null) {
changeCb.onFailure(throwable);
return;
}
changeCb.onSuccess(response);
}), Dispatchers.getIO())
);
return true;
}
case R.id.mute_posts: {
if (!isLoggedIn) return false;
final String action = profileModel.getFriendshipStatus().getMuting() ? "Unmute stories" : "Mute stories";
friendshipRepository.changeMute(
csrfToken,
myId,
deviceUuid,
profileModel.getFriendshipStatus().getMuting(),
profileModel.getPk(),
false,
CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
if (throwable != null) {
changeCb.onFailure(throwable);
return;
}
changeCb.onSuccess(response);
}), Dispatchers.getIO())
);
return true;
}
case R.id.remove_follower: {
if (!isLoggedIn) return false;
friendshipRepository.removeFollower(
csrfToken,
myId,
deviceUuid,
profileModel.getPk(),
CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
if (throwable != null) {
changeCb.onFailure(throwable);
return;
}
changeCb.onSuccess(response);
}), Dispatchers.getIO())
);
return true;
}
case R.id.share_link: {
final Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
sharingIntent.setType("text/plain");
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, "https://instagram.com/" + profileModel.getUsername());
startActivity(sharingIntent);
return true;
}
case R.id.share_dm: {
final UserSearchNavGraphDirections.ActionGlobalUserSearch actionGlobalUserSearch = UserSearchFragmentDirections
.actionGlobalUserSearch()
.setTitle(getString(R.string.share))
.setActionLabel(getString(R.string.send))
.setShowGroups(true)
.setMultiple(true)
.setSearchMode(UserSearchFragment.SearchMode.RAVEN);
final NavController navController = NavHostFragment.findNavController(ProfileFragment.this);
try {
navController.navigate(actionGlobalUserSearch);
} catch (Exception e) {
Log.e(TAG, "setupShare: ", e);
}
return true;
}
default:
return super.onOptionsItemSelected(item);
}
if (item.getItemId() == R.id.restrict) {
if (!isLoggedIn) return false;
final String action = profileModel.getFriendshipStatus().isRestricted() ? "Unrestrict" : "Restrict";
friendshipRepository.toggleRestrict(
csrfToken,
deviceUuid,
profileModel.getPk(),
!profileModel.getFriendshipStatus().isRestricted(),
CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
if (throwable != null) {
Log.e(TAG, "Error while performing " + action, throwable);
return;
}
// Log.d(TAG, action + " success: " + response);
fetchProfileDetails();
}), Dispatchers.getIO())
);
return true;
}
if (item.getItemId() == R.id.block) {
if (!isLoggedIn) return false;
// changeCb
friendshipRepository.changeBlock(
csrfToken,
myId,
deviceUuid,
profileModel.getFriendshipStatus().getBlocking(),
profileModel.getPk(),
CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
if (throwable != null) {
changeCb.onFailure(throwable);
return;
}
changeCb.onSuccess(response);
}), Dispatchers.getIO())
);
return true;
}
if (item.getItemId() == R.id.chaining) {
if (!isLoggedIn) return false;
final Bundle bundle = new Bundle();
bundle.putString("type", "chaining");
bundle.putLong("targetId", profileModel.getPk());
NavHostFragment.findNavController(this).navigate(R.id.action_global_notificationsViewerFragment, bundle);
return true;
}
if (item.getItemId() == R.id.mute_stories) {
if (!isLoggedIn) return false;
final String action = profileModel.getFriendshipStatus().isMutingReel() ? "Unmute stories" : "Mute stories";
friendshipRepository.changeMute(
csrfToken,
myId,
deviceUuid,
profileModel.getFriendshipStatus().isMutingReel(),
profileModel.getPk(),
true,
CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
if (throwable != null) {
changeCb.onFailure(throwable);
return;
}
changeCb.onSuccess(response);
}), Dispatchers.getIO())
);
return true;
}
if (item.getItemId() == R.id.mute_posts) {
if (!isLoggedIn) return false;
final String action = profileModel.getFriendshipStatus().getMuting() ? "Unmute stories" : "Mute stories";
friendshipRepository.changeMute(
csrfToken,
myId,
deviceUuid,
profileModel.getFriendshipStatus().getMuting(),
profileModel.getPk(),
false,
CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
if (throwable != null) {
changeCb.onFailure(throwable);
return;
}
changeCb.onSuccess(response);
}), Dispatchers.getIO())
);
return true;
}
if (item.getItemId() == R.id.remove_follower) {
if (!isLoggedIn) return false;
friendshipRepository.removeFollower(
csrfToken,
myId,
deviceUuid,
profileModel.getPk(),
CoroutineUtilsKt.getContinuation((response, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
if (throwable != null) {
changeCb.onFailure(throwable);
return;
}
changeCb.onSuccess(response);
}), Dispatchers.getIO())
);
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
@ -1029,6 +1099,12 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
if (removeFollowerMenuItem != null) {
removeFollowerMenuItem.setVisible(profileModel.getFriendshipStatus().getFollowedBy());
}
if (shareLinkMenuItem != null) {
shareLinkMenuItem.setVisible(!TextUtils.isEmpty(profileModel.getUsername()));
}
if (shareDmMenuItem != null) {
shareDmMenuItem.setVisible(profileModel.getPk() != 0L);
}
}
}

View File

@ -4,6 +4,7 @@ import android.content.ContentResolver
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import awais.instagrabber.models.enums.BroadcastItemType
import awais.instagrabber.models.Resource
import awais.instagrabber.models.Resource.Companion.error
import awais.instagrabber.models.Resource.Companion.loading
@ -67,19 +68,20 @@ object DirectMessagesManager {
suspend fun createThread(userPk: Long): DirectThread = DirectMessagesService.createThread(csrfToken, viewerId, deviceUuid, listOf(userPk), null)
fun sendMedia(recipient: RankedRecipient, mediaId: String, scope: CoroutineScope) {
sendMedia(setOf(recipient), mediaId, scope)
fun sendMedia(recipient: RankedRecipient, mediaId: String, itemType: BroadcastItemType, scope: CoroutineScope) {
sendMedia(setOf(recipient), mediaId, itemType, scope)
}
fun sendMedia(
recipients: Set<RankedRecipient>,
mediaId: String,
itemType: BroadcastItemType,
scope: CoroutineScope,
) {
val threadIds = recipients.mapNotNull{ it.thread?.threadId }
val userIdsTemp = recipients.mapNotNull{ it.user?.pk }
val userIds = userIdsTemp.map{ listOf(it.toString(10)) }
sendMedia(threadIds, userIds, mediaId, scope) {
sendMedia(threadIds, userIds, mediaId, itemType, scope) {
inboxManager.refresh(scope)
}
}
@ -88,6 +90,7 @@ object DirectMessagesManager {
threadIds: List<String>,
userIds: List<List<String>>,
mediaId: String,
itemType: BroadcastItemType,
scope: CoroutineScope,
callback: (() -> Unit)?,
): LiveData<Resource<Any?>> {
@ -95,14 +98,24 @@ object DirectMessagesManager {
data.postValue(loading(null))
scope.launch(Dispatchers.IO) {
try {
DirectMessagesService.broadcastMediaShare(
csrfToken,
viewerId,
deviceUuid,
UUID.randomUUID().toString(),
ThreadIdsOrUserIds(threadIds, userIds),
mediaId
)
if (itemType == BroadcastItemType.MEDIA_SHARE)
DirectMessagesService.broadcastMediaShare(
csrfToken,
viewerId,
deviceUuid,
UUID.randomUUID().toString(),
ThreadIdsOrUserIds(threadIds, userIds),
mediaId
)
if (itemType == BroadcastItemType.PROFILE)
DirectMessagesService.broadcastProfile(
csrfToken,
viewerId,
deviceUuid,
UUID.randomUUID().toString(),
ThreadIdsOrUserIds(threadIds, userIds),
mediaId
)
data.postValue(success(Any()))
callback?.invoke()
} catch (e: Exception) {

View File

@ -7,6 +7,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import awais.instagrabber.R
import awais.instagrabber.managers.DirectMessagesManager
import awais.instagrabber.models.enums.BroadcastItemType
import awais.instagrabber.models.Resource
import awais.instagrabber.models.Resource.Companion.error
import awais.instagrabber.models.Resource.Companion.loading
@ -335,7 +336,7 @@ class PostViewV2ViewModel : ViewModel() {
messageManager = DirectMessagesManager
}
val mediaId = media.id ?: return
messageManager?.sendMedia(result, mediaId, viewModelScope)
messageManager?.sendMedia(result, mediaId, BroadcastItemType.MEDIA_SHARE, viewModelScope)
}
fun shareDm(recipients: Set<RankedRecipient>) {
@ -343,6 +344,6 @@ class PostViewV2ViewModel : ViewModel() {
messageManager = DirectMessagesManager
}
val mediaId = media.id ?: return
messageManager?.sendMedia(recipients, mediaId, viewModelScope)
messageManager?.sendMedia(recipients, mediaId, BroadcastItemType.MEDIA_SHARE, viewModelScope)
}
}

View File

@ -5,8 +5,11 @@ import androidx.lifecycle.*
import androidx.savedstate.SavedStateRegistryOwner
import awais.instagrabber.db.repositories.AccountRepository
import awais.instagrabber.db.repositories.FavoriteRepository
import awais.instagrabber.managers.DirectMessagesManager
import awais.instagrabber.models.enums.BroadcastItemType
import awais.instagrabber.models.Resource
import awais.instagrabber.repositories.responses.User
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient
import awais.instagrabber.webservices.*
class ProfileFragmentViewModel(
@ -21,6 +24,7 @@ class ProfileFragmentViewModel(
) : ViewModel() {
private val _profile = MutableLiveData<Resource<User?>>(Resource.loading(null))
private val _isLoggedIn = MutableLiveData(false)
private var messageManager: DirectMessagesManager? = null
val profile: LiveData<Resource<User?>> = _profile
@ -58,6 +62,20 @@ class ProfileFragmentViewModel(
_profile.postValue(Resource.success(null))
}
}
fun shareDm(result: RankedRecipient) {
if (messageManager == null) {
messageManager = DirectMessagesManager
}
// messageManager?.sendMedia(result, mediaId, BroadcastItemType.PROFILE, viewModelScope)
}
fun shareDm(recipients: Set<RankedRecipient>) {
if (messageManager == null) {
messageManager = DirectMessagesManager
}
// messageManager?.sendMedia(recipients, mediaId, BroadcastItemType.PROFILE, viewModelScope)
}
}
@Suppress("UNCHECKED_CAST")

View File

@ -167,6 +167,16 @@ object DirectMessagesService {
): DirectThreadBroadcastResponse =
broadcast(csrfToken, userId, deviceUuid, MediaShareBroadcastOptions(clientContext, threadIdsOrUserIds, mediaId))
suspend fun broadcastProfile(
csrfToken: String,
userId: Long,
deviceUuid: String,
clientContext: String,
threadIdsOrUserIds: ThreadIdsOrUserIds,
profileId: String,
): DirectThreadBroadcastResponse =
broadcast(csrfToken, userId, deviceUuid, ProfileBroadcastOptions(clientContext, threadIdsOrUserIds, profileId))
private suspend fun broadcast(
csrfToken: String,
userId: Long,

View File

@ -15,36 +15,43 @@
<item
android:id="@+id/block"
android:icon="@drawable/ic_block_24"
android:title="@string/block"
android:visible="false"
app:showAsAction="never" />
<item
android:id="@+id/restrict"
android:icon="@drawable/ic_highlight_off_24"
android:title="@string/restrict"
android:visible="false"
app:showAsAction="never" />
<item
android:id="@+id/remove_follower"
android:icon="@drawable/ic_block_24"
android:title="@string/remove_follower"
android:visible="false"
app:showAsAction="never" />
<item
android:id="@+id/mute_stories"
android:icon="@drawable/ic_highlight_off_24"
android:title="@string/mute_stories"
android:visible="false"
app:showAsAction="never" />
<item
android:id="@+id/mute_posts"
android:icon="@drawable/ic_highlight_off_24"
android:title="@string/mute_posts"
android:visible="false"
app:showAsAction="never" />
<item
android:id="@+id/share_link"
android:title="@string/share_link"
android:visible="false"
app:showAsAction="never" />
<item
android:id="@+id/share_dm"
android:title="@string/share_via_dm"
android:visible="false"
app:showAsAction="never" />
</menu>