Browse Source

Merge branch 'master' into master

renovate/org.robolectric-robolectric-4.x
Austin Huang 4 years ago
committed by GitHub
parent
commit
700711f5f3
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      app/build.gradle
  2. 2
      app/src/main/AndroidManifest.xml
  3. 21
      app/src/main/java/awais/instagrabber/activities/MainActivity.java
  4. 3
      app/src/main/java/awais/instagrabber/adapters/SliderCallbackAdapter.java
  5. 2
      app/src/main/java/awais/instagrabber/adapters/SliderItemsAdapter.java
  6. 7
      app/src/main/java/awais/instagrabber/adapters/viewholder/SliderVideoViewHolder.java
  7. 3
      app/src/main/java/awais/instagrabber/customviews/VideoPlayerCallbackAdapter.java
  8. 5
      app/src/main/java/awais/instagrabber/customviews/VideoPlayerViewHelper.java
  9. 60
      app/src/main/java/awais/instagrabber/customviews/emoji/EmojiCategory.java
  10. 33
      app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java
  11. 2
      app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java
  12. 15
      app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java
  13. 68
      app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java
  14. 2
      app/src/main/java/awais/instagrabber/fragments/directmessages/DirectPendingInboxFragment.java
  15. 18
      app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java
  16. 3
      app/src/main/java/awais/instagrabber/managers/DirectMessagesManager.java
  17. 12
      app/src/main/java/awais/instagrabber/managers/InboxManager.java
  18. 26
      app/src/main/java/awais/instagrabber/managers/ThreadManager.java
  19. 9
      app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectThread.java
  20. 12
      app/src/main/java/awais/instagrabber/utils/Utils.java
  21. 324
      app/src/main/java/awais/instagrabber/utils/emoji/EmojiParser.java
  22. 50
      app/src/main/java/awais/instagrabber/utils/emoji/ImmutableEntry.java
  23. 1305
      app/src/main/java/awais/instagrabber/utils/emoji/UnicodeMap.java
  24. 3
      app/src/main/java/awais/instagrabber/viewmodels/DirectSettingsViewModel.java
  25. 47
      app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.java
  26. 6
      app/src/main/java/awais/instagrabber/viewmodels/factories/DirectSettingsViewModelFactory.java
  27. 6
      app/src/main/java/awais/instagrabber/viewmodels/factories/DirectThreadViewModelFactory.java
  28. BIN
      app/src/main/res/drawable/barinsta_logo.png
  29. 10
      app/src/main/res/drawable/launch_screen.xml
  30. 2
      app/src/main/res/layout/fragment_direct_messages_thread.xml
  31. 2
      app/src/main/res/menu/dm_thread_menu.xml
  32. 9
      app/src/main/res/navigation/direct_messages_nav_graph.xml
  33. 4457
      app/src/main/res/raw/emoji_test.txt
  34. 1
      app/src/main/res/raw/emojis.json
  35. 1
      app/src/main/res/values/strings.xml
  36. 4
      app/src/main/res/values/themes.xml
  37. 2
      build.gradle
  38. 6
      renovate.json

1
app/build.gradle

@ -111,7 +111,6 @@ dependencies {
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'org.apache.commons:commons-imaging:1.0-alpha2'
implementation 'com.ibm.icu:icu4j:68.1'
implementation 'com.github.ammargitham:uCrop:2.3-native-beta-2'
implementation 'com.github.ammargitham:android-gpuimage:2.1.1-beta4'

2
app/src/main/AndroidManifest.xml

@ -22,7 +22,7 @@
android:label="@string/app_name"
android:requestLegacyExternalStorage="true"
android:supportsRtl="true"
android:theme="@style/AppTheme.Light.White"
android:theme="@style/AppTheme.Launcher"
tools:ignore="UnusedAttribute">
<activity
android:name=".activities.MainActivity"

21
app/src/main/java/awais/instagrabber/activities/MainActivity.java

@ -72,7 +72,6 @@ import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.IntentModel;
import awais.instagrabber.models.SuggestionModel;
import awais.instagrabber.models.enums.SuggestionType;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.services.ActivityCheckerService;
import awais.instagrabber.services.DMSyncAlarmReceiver;
import awais.instagrabber.utils.AppExecutors;
@ -251,7 +250,17 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
@Override
public void onBackPressed() {
if (isTaskRoot() && isBackStackEmpty) {
int currentNavControllerBackStack = 2;
if (currentNavControllerLiveData != null) {
final NavController navController = currentNavControllerLiveData.getValue();
if (navController != null) {
@SuppressLint("RestrictedApi") final Deque<NavBackStackEntry> backStack = navController.getBackStack();
if (backStack != null) {
currentNavControllerBackStack = backStack.size();
}
}
}
if (isTaskRoot() && isBackStackEmpty && currentNavControllerBackStack == 2) {
finishAfterTransition();
return;
}
@ -577,10 +586,10 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
private void showThread(@NonNull final Intent intent) {
final String threadId = intent.getStringExtra(Constants.DM_THREAD_ACTION_EXTRA_THREAD_ID);
final String threadTitle = intent.getStringExtra(Constants.DM_THREAD_ACTION_EXTRA_THREAD_TITLE);
navigateToThread(threadId, threadTitle, null);
navigateToThread(threadId, threadTitle);
}
public void navigateToThread(final String threadId, final String threadTitle, final DirectThread backup) {
public void navigateToThread(final String threadId, final String threadTitle) {
if (threadId == null || threadTitle == null) return;
currentNavControllerLiveData.observe(this, new Observer<NavController>() {
@Override
@ -594,7 +603,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
// need handler.post() to wait for the fragment manager to be ready to navigate
new Handler().post(() -> {
final DirectMessageInboxFragmentDirections.ActionInboxToThread action = DirectMessageInboxFragmentDirections
.actionInboxToThread(threadId, threadTitle, backup);
.actionInboxToThread(threadId, threadTitle);
navController.navigate(action);
});
return;
@ -607,7 +616,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
@Nullable final Bundle arguments) {
if (destination.getId() == R.id.directMessagesInboxFragment) {
final DirectMessageInboxFragmentDirections.ActionInboxToThread action = DirectMessageInboxFragmentDirections
.actionInboxToThread(threadId, threadTitle, backup);
.actionInboxToThread(threadId, threadTitle);
controller.navigate(action);
controller.removeOnDestinationChangedListener(this);
}

3
app/src/main/java/awais/instagrabber/adapters/SliderCallbackAdapter.java

@ -12,4 +12,7 @@ public class SliderCallbackAdapter implements SliderItemsAdapter.SliderCallback
@Override
public void onPlayerPause(final int position) {}
@Override
public void onPlayerRelease(final int position) {}
}

2
app/src/main/java/awais/instagrabber/adapters/SliderItemsAdapter.java

@ -148,5 +148,7 @@ public final class SliderItemsAdapter extends ListAdapter<Media, SliderItemViewH
void onPlayerPlay(int position);
void onPlayerPause(int position);
void onPlayerRelease(int position);
}
}

7
app/src/main/java/awais/instagrabber/adapters/viewholder/SliderVideoViewHolder.java

@ -114,6 +114,13 @@ public class SliderVideoViewHolder extends SliderItemViewHolder {
sliderCallback.onPlayerPause(position);
}
}
@Override
public void onRelease() {
if (sliderCallback != null) {
sliderCallback.onPlayerRelease(position);
}
}
};
final float aspectRatio = (float) media.getOriginalWidth() / media.getOriginalHeight();
String videoUrl = null;

3
app/src/main/java/awais/instagrabber/customviews/VideoPlayerCallbackAdapter.java

@ -15,4 +15,7 @@ public class VideoPlayerCallbackAdapter implements VideoPlayerViewHelper.VideoPl
@Override
public void onPause() {}
@Override
public void onRelease() {}
}

5
app/src/main/java/awais/instagrabber/customviews/VideoPlayerViewHelper.java

@ -359,6 +359,9 @@ public class VideoPlayerViewHelper implements Player.EventListener {
// }
public void releasePlayer() {
if (videoPlayerCallback != null) {
videoPlayerCallback.onRelease();
}
if (player != null) {
player.release();
player = null;
@ -453,5 +456,7 @@ public class VideoPlayerViewHelper implements Player.EventListener {
void onPlay();
void onPause();
void onRelease();
}
}

60
app/src/main/java/awais/instagrabber/customviews/emoji/EmojiCategory.java

@ -16,35 +16,6 @@ public class EmojiCategory {
public EmojiCategory(final EmojiCategoryType type) {
this.type = type;
switch (type) {
case SMILEYS_AND_EMOTION:
drawableRes = R.drawable.ic_round_emoji_emotions_24;
break;
case ANIMALS_AND_NATURE:
drawableRes = R.drawable.ic_round_emoji_nature_24;
break;
case FOOD_AND_DRINK:
drawableRes = R.drawable.ic_round_emoji_food_beverage_24;
break;
case TRAVEL_AND_PLACES:
drawableRes = R.drawable.ic_round_emoji_transportation_24;
break;
case ACTIVITIES:
drawableRes = R.drawable.ic_round_emoji_events_24;
break;
case OBJECTS:
drawableRes = R.drawable.ic_round_emoji_objects_24;
break;
case SYMBOLS:
drawableRes = R.drawable.ic_round_emoji_symbols_24;
break;
case FLAGS:
drawableRes = R.drawable.ic_round_emoji_flags_24;
break;
case OTHERS:
drawableRes = R.drawable.ic_round_unknown_24;
break;
}
}
public EmojiCategoryType getType() {
@ -56,6 +27,37 @@ public class EmojiCategory {
}
public int getDrawableRes() {
if (drawableRes == 0) {
switch (type) {
case SMILEYS_AND_EMOTION:
drawableRes = R.drawable.ic_round_emoji_emotions_24;
break;
case ANIMALS_AND_NATURE:
drawableRes = R.drawable.ic_round_emoji_nature_24;
break;
case FOOD_AND_DRINK:
drawableRes = R.drawable.ic_round_emoji_food_beverage_24;
break;
case TRAVEL_AND_PLACES:
drawableRes = R.drawable.ic_round_emoji_transportation_24;
break;
case ACTIVITIES:
drawableRes = R.drawable.ic_round_emoji_events_24;
break;
case OBJECTS:
drawableRes = R.drawable.ic_round_emoji_objects_24;
break;
case SYMBOLS:
drawableRes = R.drawable.ic_round_emoji_symbols_24;
break;
case FLAGS:
drawableRes = R.drawable.ic_round_emoji_flags_24;
break;
case OTHERS:
drawableRes = R.drawable.ic_round_unknown_24;
break;
}
}
return drawableRes;
}

33
app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java

@ -48,6 +48,7 @@ import androidx.core.content.PermissionChecker;
import androidx.core.view.ViewCompat;
import androidx.core.widget.NestedScrollView;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer;
@ -1062,15 +1063,28 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment im
@Override
public void onPlayerPlay(final int position) {
final FragmentActivity activity = getActivity();
if (activity == null) return;
Utils.enabledKeepScreenOn(activity);
if (!detailsVisible || hasBeenToggled) return;
showPlayerControls();
}
@Override
public void onPlayerPause(final int position) {
final FragmentActivity activity = getActivity();
if (activity == null) return;
Utils.disableKeepScreenOn(activity);
if (detailsVisible || hasBeenToggled) return;
toggleDetails();
}
@Override
public void onPlayerRelease(final int position) {
final FragmentActivity activity = getActivity();
if (activity == null) return;
Utils.disableKeepScreenOn(activity);
}
});
binding.sliderParent.setAdapter(sliderItemsAdapter);
if (sliderPosition >= 0 && sliderPosition < media.getCarouselMedia().size()) {
@ -1206,7 +1220,7 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment im
@Override
public void onPlayerViewLoaded() {
binding.playerControls.getRoot().setVisibility(View.VISIBLE);
// binding.playerControls.getRoot().setVisibility(View.VISIBLE);
final ViewGroup.LayoutParams layoutParams = binding.videoPost.playerView.getLayoutParams();
final int requiredWidth = Utils.displayMetrics.widthPixels;
final int resultingHeight = NumberUtils
@ -1218,10 +1232,27 @@ public class PostViewV2Fragment extends SharedElementTransitionDialogFragment im
@Override
public void onPlay() {
final FragmentActivity activity = getActivity();
if (activity == null) return;
Utils.enabledKeepScreenOn(activity);
if (detailsVisible) {
new Handler().postDelayed(() -> toggleDetails(), DETAILS_HIDE_DELAY_MILLIS);
}
}
@Override
public void onPause() {
final FragmentActivity activity = getActivity();
if (activity == null) return;
Utils.disableKeepScreenOn(activity);
}
@Override
public void onRelease() {
final FragmentActivity activity = getActivity();
if (activity == null) return;
Utils.disableKeepScreenOn(activity);
}
};
final float aspectRatio = (float) media.getOriginalWidth() / media.getOriginalHeight();
String videoUrl = null;

2
app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java

@ -257,7 +257,7 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
navigating = true;
if (isAdded()) {
final DirectMessageInboxFragmentDirections.ActionInboxToThread directions = DirectMessageInboxFragmentDirections
.actionInboxToThread(thread.getThreadId(), thread.getThreadTitle(), thread);
.actionInboxToThread(thread.getThreadId(), thread.getThreadTitle());
NavHostFragment.findNavController(this).navigate(directions);
}
navigating = false;

15
app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.java

@ -81,7 +81,6 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi
final AppStateViewModel appStateViewModel = new ViewModelProvider(fragmentActivity).get(AppStateViewModel.class);
viewModel = new ViewModelProvider(this, new DirectSettingsViewModelFactory(fragmentActivity.getApplication(),
args.getThreadId(),
args.getBackup(),
args.getPending(),
appStateViewModel.getCurrentUser()))
.get(DirectSettingsViewModel.class);
@ -348,15 +347,15 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi
usersAdapter = new DirectUsersAdapter(
inviter != null ? inviter.getPk() : -1,
(position, user, selected) -> {
if (!TextUtils.isEmpty(user.getFbId())) {
if (TextUtils.isEmpty(user.getUsername()) && !TextUtils.isEmpty(user.getFbId())) {
Utils.openURL(context, "https://facebook.com/" + user.getFbId());
return;
}
else {
final ProfileNavGraphDirections.ActionGlobalProfileFragment directions = ProfileNavGraphDirections
.actionGlobalProfileFragment()
.setUsername("@" + user.getUsername());
NavHostFragment.findNavController(this).navigate(directions);
}
if (TextUtils.isEmpty(user.getUsername())) return;
final ProfileNavGraphDirections.ActionGlobalProfileFragment directions = ProfileNavGraphDirections
.actionGlobalProfileFragment()
.setUsername("@" + user.getUsername());
NavHostFragment.findNavController(this).navigate(directions);
},
(position, user) -> {
final ArrayList<Option<String>> options = viewModel.createUserOptions(user);

68
app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java

@ -100,6 +100,7 @@ import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
import awais.instagrabber.utils.AppExecutors;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.DMUtils;
import awais.instagrabber.utils.DownloadUtils;
import awais.instagrabber.utils.PermissionUtils;
import awais.instagrabber.utils.ResponseBodyUtils;
@ -317,6 +318,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
backStackSavedStateResultLiveData.postValue(null);
};
private final MutableLiveData<Integer> inputLength = new MutableLiveData<>(0);
private MenuItem markAsSeenMenuItem;
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
@ -329,7 +331,6 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
final DirectMessageThreadFragmentArgs fragmentArgs = DirectMessageThreadFragmentArgs.fromBundle(arguments);
viewModel = new ViewModelProvider(this, new DirectThreadViewModelFactory(fragmentActivity.getApplication(),
fragmentArgs.getThreadId(),
fragmentArgs.getBackup(),
fragmentArgs.getPending(),
appStateViewModel.getCurrentUser()))
.get(DirectThreadViewModel.class);
@ -368,9 +369,13 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
@Override
public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
inflater.inflate(R.menu.dm_thread_menu, menu);
final MenuItem markAsSeenMenuItem = menu.findItem(R.id.mark_as_seen);
markAsSeenMenuItem = menu.findItem(R.id.mark_as_seen);
if (markAsSeenMenuItem != null) {
markAsSeenMenuItem.setVisible(false);
if (autoMarkAsSeen) {
markAsSeenMenuItem.setVisible(false);
} else {
markAsSeenMenuItem.setEnabled(false);
}
}
}
@ -379,15 +384,14 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
final int itemId = item.getItemId();
if (itemId == R.id.info) {
final DirectMessageThreadFragmentDirections.ActionThreadToSettings directions = DirectMessageThreadFragmentDirections
.actionThreadToSettings(viewModel.getThreadId(), null, null);
.actionThreadToSettings(viewModel.getThreadId(), null);
final Boolean pending = viewModel.isPending().getValue();
directions.setPending(pending == null ? false : pending);
NavHostFragment.findNavController(this).navigate(directions);
return true;
}
if (itemId == R.id.mark_as_seen) {
// new ThreadAction().execute("seen", lastMessage);
item.setVisible(false);
handleMarkAsSeen(item);
return true;
}
if (itemId == R.id.refresh && viewModel != null) {
@ -397,6 +401,40 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
return super.onOptionsItemSelected(item);
}
private void handleMarkAsSeen(@NonNull final MenuItem item) {
final LiveData<Resource<Object>> resourceLiveData = viewModel.markAsSeen();
resourceLiveData.observe(getViewLifecycleOwner(), new Observer<Resource<Object>>() {
@Override
public void onChanged(final Resource<Object> resource) {
try {
if (resource == null) return;
final Context context = getContext();
if (context == null) return;
switch (resource.status) {
case SUCCESS:
Toast.makeText(context, R.string.marked_as_seen, Toast.LENGTH_SHORT).show();
case LOADING:
item.setEnabled(false);
break;
case ERROR:
item.setEnabled(true);
if (resource.message != null) {
Snackbar.make(context, binding.getRoot(), resource.message, Snackbar.LENGTH_LONG).show();
return;
}
if (resource.resId != 0) {
Snackbar.make(binding.getRoot(), resource.resId, Snackbar.LENGTH_LONG).show();
return;
}
break;
}
} finally {
resourceLiveData.removeObserver(this);
}
}
});
}
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
@ -464,6 +502,12 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
cleanup();
}
@Override
public void onDestroy() {
viewModel.deleteThreadIfRequired();
super.onDestroy();
}
@SuppressLint("UnsafeExperimentalUsageError")
private void cleanup() {
if (prevTitleRunnable != null) {
@ -903,9 +947,15 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
}
private void submitItemsToAdapter(final List<DirectItem> items) {
if (autoMarkAsSeen) {
binding.chats.post(() -> viewModel.markAsSeen());
}
binding.chats.post(() -> {
if (autoMarkAsSeen) {
viewModel.markAsSeen();
return;
}
final DirectThread thread = threadLiveData.getValue();
if (thread == null) return;
markAsSeenMenuItem.setEnabled(!DMUtils.isRead(thread));
});
if (itemsAdapter == null) return;
itemsAdapter.submitList(items, () -> {
itemOrHeaders = itemsAdapter.getList();

2
app/src/main/java/awais/instagrabber/fragments/directmessages/DirectPendingInboxFragment.java

@ -163,7 +163,7 @@ public class DirectPendingInboxFragment extends Fragment implements SwipeRefresh
navigating = true;
if (isAdded()) {
final DirectPendingInboxFragmentDirections.ActionPendingInboxToThread directions = DirectPendingInboxFragmentDirections
.actionPendingInboxToThread(thread.getThreadId(), thread.getThreadTitle(), thread);
.actionPendingInboxToThread(thread.getThreadId(), thread.getThreadTitle());
directions.setPending(true);
NavHostFragment.findNavController(this).navigate(directions);
}

18
app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java

@ -74,6 +74,8 @@ import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
import awais.instagrabber.dialogs.ProfilePicDialogFragment;
import awais.instagrabber.fragments.PostViewV2Fragment;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.managers.DirectMessagesManager;
import awais.instagrabber.managers.InboxManager;
import awais.instagrabber.models.HighlightModel;
import awais.instagrabber.models.PostsLayoutPreferences;
import awais.instagrabber.models.StoryModel;
@ -590,8 +592,11 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
private void fetchProfileDetails() {
accountIsUpdated = false;
new ProfileFetcher(TextUtils.isEmpty(username) ? null : username.trim().substring(1),
myId, isLoggedIn, new FetchListener<User>() {
String usernameTemp = username.trim();
if (usernameTemp.startsWith("@")) {
usernameTemp = usernameTemp.substring(1);
}
new ProfileFetcher(TextUtils.isEmpty(username) ? null : usernameTemp, myId, isLoggedIn, new FetchListener<User>() {
@Override
public void onResult(final User user) {
if (getContext() == null) return;
@ -612,7 +617,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
isLoggedIn ? R.string.error_loading_profile_loggedin : R.string.error_loading_profile,
Toast.LENGTH_LONG).show();
else Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
} catch (final Throwable e) {}
} catch (final Throwable ignored) {}
}
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
@ -1073,7 +1078,12 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
profileDetailsBinding.btnDM.setEnabled(true);
return;
}
fragmentActivity.navigateToThread(thread.getThreadId(), profileModel.getUsername(), thread);
final InboxManager inboxManager = DirectMessagesManager.getInstance().getInboxManager();
if (!inboxManager.containsThread(thread.getThreadId())) {
thread.setTemp(true);
inboxManager.addThread(thread, 0);
}
fragmentActivity.navigateToThread(thread.getThreadId(), profileModel.getUsername());
profileDetailsBinding.btnDM.setEnabled(true);
}).execute();
});

3
app/src/main/java/awais/instagrabber/managers/DirectMessagesManager.java

@ -74,9 +74,8 @@ public final class DirectMessagesManager {
public ThreadManager getThreadManager(@NonNull final String threadId,
final boolean pending,
final DirectThread backup,
@NonNull final User currentUser,
@NonNull final ContentResolver contentResolver) {
return ThreadManager.getInstance(threadId, pending, backup, currentUser, contentResolver);
return ThreadManager.getInstance(threadId, pending, currentUser, contentResolver);
}
}

12
app/src/main/java/awais/instagrabber/managers/InboxManager.java

@ -18,6 +18,7 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@ -357,4 +358,15 @@ public final class InboxManager {
public void setPendingRequestsTotal(final int total) {
pendingRequestsTotal.postValue(total);
}
public boolean containsThread(final String threadId) {
if (threadId == null) return false;
synchronized (this.inbox) {
final DirectInbox currentDirectInbox = getCurrentDirectInbox();
if (currentDirectInbox == null) return false;
final List<DirectThread> threads = currentDirectInbox.getThreads();
if (threads == null) return false;
return threads.stream().anyMatch(thread -> Objects.equals(thread.getThreadId(), threadId));
}
}
}

26
app/src/main/java/awais/instagrabber/managers/ThreadManager.java

@ -123,7 +123,6 @@ public final class ThreadManager {
public static ThreadManager getInstance(@NonNull final String threadId,
final boolean pending,
final DirectThread backup,
@NonNull final User currentUser,
@NonNull final ContentResolver contentResolver) {
ThreadManager instance = INSTANCE_MAP.get(threadId);
@ -131,7 +130,7 @@ public final class ThreadManager {
synchronized (LOCK) {
instance = INSTANCE_MAP.get(threadId);
if (instance == null) {
instance = new ThreadManager(threadId, pending, backup, currentUser, contentResolver);
instance = new ThreadManager(threadId, pending, currentUser, contentResolver);
INSTANCE_MAP.put(threadId, instance);
}
}
@ -145,7 +144,6 @@ public final class ThreadManager {
private ThreadManager(@NonNull final String threadId,
final boolean pending,
final DirectThread backup,
@NonNull final User currentUser,
@NonNull final ContentResolver contentResolver) {
final DirectMessagesManager messagesManager = DirectMessagesManager.getInstance();
@ -164,17 +162,17 @@ public final class ThreadManager {
service = DirectMessagesService.getInstance(csrfToken, viewerId, deviceUuid);
mediaService = MediaService.getInstance(deviceUuid, csrfToken, viewerId);
friendshipService = FriendshipService.getInstance(deviceUuid, csrfToken, viewerId);
setupTransformations(backup);
setupTransformations();
// fetchChats();
}
public void moveFromPending() {
final DirectMessagesManager messagesManager = DirectMessagesManager.getInstance();
this.inboxManager = messagesManager.getInboxManager();
setupTransformations(null);
setupTransformations();
}
private void setupTransformations(final DirectThread backup) {
private void setupTransformations() {
// Transformations
thread = distinctUntilChanged(map(inboxManager.getInbox(), inboxResource -> {
if (inboxResource == null) {
@ -188,7 +186,7 @@ public final class ThreadManager {
final DirectThread thread = threads.stream()
.filter(t -> t.getThreadId().equals(threadId))
.findFirst()
.orElse(backup);
.orElse(null);
if (thread != null) {
cursor = thread.getOldestCursor();
hasOlder = thread.hasOlder();
@ -1799,18 +1797,23 @@ public final class ThreadManager {
return inviter;
}
public void markAsSeen(@NonNull final DirectItem directItem) {
public LiveData<Resource<Object>> markAsSeen(@NonNull final DirectItem directItem) {
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
data.postValue(Resource.loading(null));
final Call<DirectItemSeenResponse> request = service.markAsSeen(threadId, directItem);
request.enqueue(new Callback<DirectItemSeenResponse>() {
@Override
public void onResponse(@NonNull final Call<DirectItemSeenResponse> call,
@NonNull final Response<DirectItemSeenResponse> response) {
if (!response.isSuccessful()) {
handleErrorBody(call, response, null);
handleErrorBody(call, response, data);
return;
}
final DirectItemSeenResponse seenResponse = response.body();
if (seenResponse == null) return;
if (seenResponse == null) {
data.postValue(Resource.error(R.string.generic_null_response, null));
return;
}
inboxManager.fetchUnseenCount();
final DirectItemSeenResponsePayload payload = seenResponse.getPayload();
if (payload == null) return;
@ -1822,14 +1825,17 @@ public final class ThreadManager {
lastSeenAt.put(currentUser.getPk(), new DirectThreadLastSeenAt(timestamp, directItem.getItemId()));
thread.setLastSeenAt(lastSeenAt);
setThread(thread, true);
data.postValue(Resource.success(new Object()));
}
@Override
public void onFailure(@NonNull final Call<DirectItemSeenResponse> call,
@NonNull final Throwable t) {
Log.e(TAG, "onFailure: ", t);
data.postValue(Resource.error(t.getMessage(), null));
}
});
return data;
}
private interface OnSuccessAction {

9
app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectThread.java

@ -45,6 +45,7 @@ public class DirectThread implements Serializable, Cloneable {
private boolean approvalRequiredForNewMembers;
private int inputMode;
private final List<ThreadContext> threadContextItems;
private boolean isTemp;
public DirectThread(final String threadId,
final String threadV2Id,
@ -292,6 +293,14 @@ public class DirectThread implements Serializable, Cloneable {
return threadContextItems;
}
public boolean isTemp() {
return isTemp;
}
public void setTemp(final boolean isTemp) {
this.isTemp = isTemp;
}
@Nullable
public DirectItem getFirstDirectItem() {
DirectItem firstItem = null;

12
app/src/main/java/awais/instagrabber/utils/Utils.java

@ -359,4 +359,16 @@ public final class Utils {
}
return drawable;
}
public static void enabledKeepScreenOn(@NonNull final Activity activity) {
final Window window = activity.getWindow();
if (window == null) return;
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
public static void disableKeepScreenOn(@NonNull final Activity activity) {
final Window window = activity.getWindow();
if (window == null) return;
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
}

324
app/src/main/java/awais/instagrabber/utils/emoji/EmojiParser.java

@ -2,27 +2,25 @@ package awais.instagrabber.utils.emoji;
import android.util.Log;
import com.google.common.base.CharMatcher;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.ibm.icu.impl.Utility;
import com.ibm.icu.lang.CharSequences;
import com.ibm.icu.text.UnicodeSet;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import awais.instagrabber.customviews.emoji.Emoji;
import awais.instagrabber.customviews.emoji.EmojiCategory;
import awais.instagrabber.customviews.emoji.EmojiCategoryType;
import awais.instagrabber.utils.NetworkUtils;
public final class EmojiParser {
private static final String TAG = EmojiParser.class.getSimpleName();
@ -30,51 +28,8 @@ public final class EmojiParser {
private static EmojiParser instance;
// private static final String COMBINING_ENCLOSING_KEYCAP = "\u20E3";
// private static final String ZWJ = "\u200D";
// private static final UnicodeSet REGIONAL_INDICATORS = new UnicodeSet(0x1F1E6, 0x1F1FF).freeze();
// private static final UnicodeSet TAGS = new UnicodeSet(0xE0000, 0xE007F).freeze();
// private static final UnicodeSet FAMILY = new UnicodeSet("[\u200D 👦-👩 💋 ❤]").freeze();
// private static final UnicodeSet GENDER = new UnicodeSet().add(0x2640).add(0x2642).freeze();
// private static final UnicodeSet SPECIALS = new UnicodeSet("["
// + "{🐈‍⬛}{🐻‍❄}{👨‍🍼}{👩‍🍼}{🧑‍🍼}{🧑‍🎄}{🧑‍🤝‍🧑}{🏳‍🌈} {👁‍🗨} {🏴‍☠} {🐕‍🦺} {👨‍🦯} {👨‍🦼} {👨‍🦽} {👩‍🦯} {👩‍🦼} {👩‍🦽}"
// + "{🏳‍⚧}{🧑‍⚕}{🧑‍⚖}{🧑‍✈}{🧑‍🌾}{🧑‍🍳}{🧑‍🎓}{🧑‍🎤}{🧑‍🎨}{🧑‍🏫}{🧑‍🏭}{🧑‍💻}{🧑‍💼}{🧑‍🔧}{🧑‍🔬}{🧑‍🚀}{🧑‍🚒}{🧑‍🦯}{🧑‍🦼}{🧑‍🦽}"
// + "{❤‍🔥}, {❤‍🩹}, {😮‍💨}, {😵‍💫}" // #E13.1
// + "]").freeze();
// May have to add from above, if there is a failure in testAnnotationPaths. Failure will be like:
// got java.util.TreeSet<[//ldml/annotations/annotation[@cp="🏳‍⚧"][@type="tts"], //ldml/annotations/annotation[@cp="🧑‍⚕"][@type="tts"], ...
// just extract the items in "...", and change into {...} for adding above.
// Example: //ldml/annotations/annotation[@cp="🧑‍⚕"] ==> {🧑}
// private static final UnicodeSet MAN_WOMAN = new UnicodeSet("[👨 👩]").freeze();
// private static final UnicodeSet OBJECT = new UnicodeSet("[👩 🎓 🌾 🍳 🏫 🏭 🎨 🚒 ✈ 🚀 🎤 💻 🔬 💼 🔧 ⚖ ⚕]").freeze();
// private static final String TYPE_TTS = "[@type=\"tts\"]";
private static final String EMOJI_VARIANT = "\uFE0F";
private static final UnicodeSet SKIN_TONE_MODIFIERS = new UnicodeSet("[🏻-🏿]").freeze();
private static final String SKIN_TONE_PATTERN = SKIN_TONE_MODIFIERS.toPattern(true);
private static final Map<EmojiCategoryType, EmojiCategory> CATEGORY_MAP = new LinkedHashMap<>();
private static final Map<String, Emoji> ALL_EMOJIS = new HashMap<>();
// private final UnicodeMap<String> emojiToMajorCategory = new UnicodeMap<>();
// private final UnicodeMap<String> emojiToMinorCategory = new UnicodeMap<>();
// private final UnicodeMap<String> toName = new UnicodeMap<>();
// /**
// * A mapping from a majorCategory to a unique ordering number, based on the first time it is encountered.
// */
// private final Map<String, Long> majorToOrder = new HashMap<>();
// private final List<String> majorToOrder = new LinkedList<String>();
// /**
// * A mapping from a minorCategory to a unique ordering number, based on the first time it is encountered.
// */
// private final Map<String, Long> minorToOrder = new HashMap<>();
// private final Map<String, Long> emojiToOrder = new LinkedHashMap<>();
// private final UnicodeSet nonConstructed = new UnicodeSet();
// private final UnicodeSet allRgi = new UnicodeSet();
// private final UnicodeSet allRgiNoES = new UnicodeSet();
// private final UnicodeMap<String> EXTRA_SYMBOL_MINOR_CATEGORIES = new UnicodeMap<>();
// private final Map<String, Long> EXTRA_SYMBOL_ORDER;
// private final boolean DEBUG = false;
// private Set<String> NAME_PATHS = null;
// private Set<String> KEYWORD_PATHS = null;
private Map<String, Emoji> allEmojis = Collections.emptyMap();
private Map<EmojiCategoryType, EmojiCategory> categoryMap = Collections.emptyMap();
private ImmutableList<EmojiCategory> categories;
public static EmojiParser getInstance() {
@ -89,267 +44,56 @@ public final class EmojiParser {
}
private EmojiParser() {
// Log.d(TAG, "Emoji: " + new Date());
// String[][] data = {
// {"arrow", "→ ↓ ↑ ← ↔ ↕ ⇆ ⇅"},
// {"alphanum", "© ® ℗ ™ µ"},
// {"geometric", "▼ ▶ ▲ ◀ ● ○ ◯ ◊"},
// {"math", "× ÷ √ ∞ ∆ ∇ ⁻ ¹ ² ³ ≡ ∈ ⊂ ∩ ∪ ° + ± − = ≈ ≠ > < ≤ ≥ ¬ | ~"},
// {"punctuation", "§ † ‡ \\u0020 , 、 ، ; : ؛ ! ¡ ? ¿ ؟ ¶ ※ / \\ & # % ‰ ′ ″ ‴ @ * ♪ ♭ ♯ ` ´ ^ ¨ ‐ ― _ - – — • · . … 。 ‧ ・ ‘ ’ ‚ ' “ ” „ » « ( ) [ ] { } 〔 〕 〈 〉 《 》 「 」 『 』 〖 〗 【 】"},
// {"currency", "€ £ ¥ ₹ ₽ $ ¢ ฿ ₪ ₺ ₫ ₱ ₩ ₡ ₦ ₮ ৳ ₴ ₸ ₲ ₵ ៛ ₭ ֏ ₥ ₾ ₼ ₿ ؋"},
// {"other-symbol", "‾‽‸⁂↚↛↮↙↜↝↞↟↠↡↢↣↤↥↦↧↨↫↬↭↯↰↱↲↳↴↵↶↷↸↹↺↻↼↽↾↿⇀⇁⇂⇃⇄⇇⇈⇉⇊⇋⇌⇐⇍⇑⇒⇏⇓⇔⇎⇖⇗⇘⇙⇚⇛⇜⇝⇞⇟⇠⇡⇢⇣⇤⇥⇦⇧⇨⇩⇪⇵∀∂∃∅∉∋∎∏∑≮≯∓∕⁄∗∘∙∝∟∠∣∥∧∫∬∮∴∵∶∷∼∽∾≃≅≌≒≖≣≦≧≪≫≬≳≺≻⊁⊃⊆⊇⊕⊖⊗⊘⊙⊚⊛⊞⊟⊥⊮⊰⊱⋭⊶⊹⊿⋁⋂⋃⋅⋆⋈⋒⋘⋙⋮⋯⋰⋱■□▢▣▤▥▦▧▨▩▬▭▮▰△▴▵▷▸▹►▻▽▾▿◁◂◃◄◅◆◇◈◉◌◍◎◐◑◒◓◔◕◖◗◘◙◜◝◞◟◠◡◢◣◤◥◦◳◷◻◽◿⨧⨯⨼⩣⩽⪍⪚⪺₢₣₤₰₳₶₷₨﷼"},
// };
// get the maximum suborder for each subcategory
// Map<String, Long> subcategoryToMaxSuborder = new HashMap<>();
// for (String[] row : data) {
// final String subcategory = row[0];
// for (Entry<String, String> entry : emojiToMinorCategory.entrySet()) {
// if (entry.getValue().equals(subcategory)) {
// String emoji = entry.getKey();
// Long order = emojiToOrder.get(emoji);
// Long currentMax = subcategoryToMaxSuborder.get(subcategory);
// if (order == null) continue;
// if (currentMax == null || currentMax < order) {
// subcategoryToMaxSuborder.put(subcategory, order);
// }
// }
// }
// }
// if (DEBUG) System.out.println(subcategoryToMaxSuborder);
// Map<String, Long> _EXTRA_SYMBOL_ORDER = new LinkedHashMap<>();
// for (String[] row : data) {
// final String subcategory = row[0];
// final String characters = row[1];
//
// List<String> items = new ArrayList<>();
// for (int cp : With.codePointArray(characters)) {
// if (cp != ' ') {
// items.add(With.fromCodePoint(cp));
// }
// }
// final UnicodeSet uset = new UnicodeSet().addAll(items);
// if (uset.containsSome(EXTRA_SYMBOL_MINOR_CATEGORIES.keySet())) {
// throw new IllegalArgumentException("Duplicate values in " + EXTRA_SYMBOL_MINOR_CATEGORIES);
// }
// EXTRA_SYMBOL_MINOR_CATEGORIES.putAll(uset, subcategory);
// final Long countObject = subcategoryToMaxSuborder.get(subcategory);
// if (countObject == null) continue;
// long count = countObject;
// for (String s : items) {
// ++count;
// _EXTRA_SYMBOL_ORDER.put(s, count);
// }
// subcategoryToMaxSuborder.put(subcategory, count);
// }
// if (DEBUG) System.out.println(_EXTRA_SYMBOL_ORDER);
// EXTRA_SYMBOL_MINOR_CATEGORIES.freeze();
// EXTRA_SYMBOL_ORDER = ImmutableMap.copyOf(_EXTRA_SYMBOL_ORDER);
/*
# group: Smileys & People
# subgroup: face-positive
1F600 ; fully-qualified # 😀 grinning face
*/
final Splitter semi = Splitter.on(CharMatcher.anyOf(";#")).trimResults();
String majorCategory;
final String file = "res/raw/emoji_test.txt";
final String file = "res/raw/emojis.json";
final ClassLoader classLoader = getClass().getClassLoader();
if (classLoader == null) {
Log.e(TAG, "Emoji: classLoader is null");
return;
}
try (final InputStream in = classLoader.getResourceAsStream(file);
final BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
String line;
EmojiCategoryType categoryType = null;
while ((line = reader.readLine()) != null) {
if (line.startsWith("#")) {
line = line.substring(1).trim();
if (line.startsWith("group:")) {
majorCategory = line.substring("group:".length()).trim();
if (!majorCategory.equals("Component")) { // Skip Component
if (majorCategory.equals("Smileys & Emotion") || majorCategory.equals("People & Body")) {
// Put 'People & Body' in 'Smileys & Emotion' category
categoryType = EmojiCategoryType.SMILEYS_AND_EMOTION;
} else {
categoryType = EmojiCategoryType.valueOfName(majorCategory);
}
final boolean contains = CATEGORY_MAP.containsKey(categoryType);
if (!contains) {
CATEGORY_MAP.put(categoryType, new EmojiCategory(categoryType));
}
}
}
continue;
}
if (categoryType == null) continue;
line = line.trim();
if (line.isEmpty()) {
continue;
}
final Iterator<String> it = semi.split(line).iterator();
String emojiHex = it.next();
String original = Utility.fromHex(emojiHex, 4, " ");
String status = it.next();
if (!status.startsWith("fully-qualified")) { // only use fully qualified
continue;
}
final EmojiCategory emojiCategory = CATEGORY_MAP.get(categoryType);
final Map<String, Emoji> emojis = emojiCategory == null ? new LinkedHashMap<>() : emojiCategory.getEmojis();
String comment = it.next();
// The comment is now of the form: # 😁 E0.6 beaming face with smiling eyes
int spacePos = comment.indexOf(' ');
spacePos = comment.indexOf(' ', spacePos + 1); // get second space
final String name = comment.substring(spacePos + 1).trim();
final Emoji emoji = new Emoji(original, name);
ALL_EMOJIS.put(original, emoji);
String minimal = original.replace(EMOJI_VARIANT, "");
//noinspection deprecation
boolean singleton = CharSequences.getSingleCodePoint(minimal) != Integer.MAX_VALUE;
if (!singleton && SKIN_TONE_MODIFIERS.containsSome(minimal)) {
// skin tone variant
final String parent = minimal.replaceAll(SKIN_TONE_PATTERN, "");
final Emoji parentEmoji = emojis.get(parent);
if (parentEmoji != null) {
parentEmoji.addVariant(emoji);
}
continue;
}
emojis.put(original, emoji);
// skip constructed values
// if (minimal.contains(COMBINING_ENCLOSING_KEYCAP)
// || REGIONAL_INDICATORS.containsSome(minimal)
// || TAGS.containsSome(minimal)
// || !singleton && MODIFIERS.containsSome(minimal)
// || !singleton && FAMILY.containsAll(minimal)) {
// // do nothing
// } else if (minimal.contains(ZWJ)) { // only do certain ZWJ sequences
// if (SPECIALS.contains(minimal)
// || GENDER.containsSome(minimal)
// || MAN_WOMAN.contains(minimal.codePointAt(0)) && OBJECT.contains(minimal.codePointBefore(minimal.length()))) {
// // nonConstructed.add(minimal);
// }
// } else if (!minimal.contains("🔟")) {
// // nonConstructed.add(minimal);
// }
}
// for (Entry<Pair<Integer,Integer>, String> entry : majorPlusMinorToEmoji.entries()) {
// String minimal = entry.getValue();
// emojiToOrder.put(minimal, emojiToOrder.size());
// }
try (final InputStream in = classLoader.getResourceAsStream(file)) {
final String json = NetworkUtils.readFromInputStream(in);
final Gson gson = new Gson();
final Type type = new TypeToken<Map<EmojiCategoryType, EmojiCategory>>() {}.getType();
categoryMap = gson.fromJson(json, type);
// Log.d(TAG, "EmojiParser: " + categoryMap);
allEmojis = categoryMap.values()
.stream()
.flatMap((Function<EmojiCategory, Stream<Emoji>>) emojiCategory -> {
final Map<String, Emoji> emojis = emojiCategory.getEmojis();
return emojis.values().stream();
})
.flatMap(emoji -> ImmutableList.<Emoji>builder()
.add(emoji)
.addAll(emoji.getVariants())
.build()
.stream())
.collect(Collectors.toMap(Emoji::getUnicode, Function.identity()));
} catch (IOException e) {
Log.e(TAG, "Emoji: ", e);
Log.e(TAG, "EmojiParser: ", e);
}
}
// private static <K, V> void putUnique(Map<K, V> map, K key, V value) {
// V oldValue = map.put(key, value);
// if (oldValue != null) {
// throw new ICUException("Attempt to change value of " + map
// + " for " + key
// + " from " + oldValue
// + " to " + value
// );
// }
// }
public Map<EmojiCategoryType, EmojiCategory> getCategoryMap() {
return CATEGORY_MAP;
return categoryMap;
}
public List<EmojiCategory> getEmojiCategories() {
if (categories == null) {
final Collection<EmojiCategory> categoryCollection = CATEGORY_MAP.values();
final Collection<EmojiCategory> categoryCollection = categoryMap.values();
categories = ImmutableList.copyOf(categoryCollection);
}
return categories;
}
public Map<String, Emoji> getAllEmojis() {
return ALL_EMOJIS;
return allEmojis;
}
public Emoji getEmoji(final String emoji) {
if (emoji == null) {
return null;
}
return ALL_EMOJIS.get(emoji);
return allEmojis.get(emoji);
}
// public String getMinorCategory(String emoji) {
// String minorCat = emojiToMinorCategory.get(emoji);
// if (minorCat == null) {
// minorCat = EXTRA_SYMBOL_MINOR_CATEGORIES.get(emoji);
// if (minorCat == null) {
// throw new InternalCldrException("No minor category (aka subgroup) found for " + emoji
// + ". Update emoji-test.txt to latest, and setValue PathHeader.. functionMap.put(\"minor\", ...");
// }
// }
// return minorCat;
// }
// public long getEmojiToOrder(String emoji) {
// Long result = emojiToOrder.get(emoji);
// if (result == null) {
// result = EXTRA_SYMBOL_ORDER.get(emoji);
// if (result == null) {
// throw new InternalCldrException("No Order found for " + emoji
// + ". Update emoji-test.txt to latest, and setValue PathHeader.. functionMap.put(\"minor\", ...");
// }
// }
// return result;
// }
// public long getEmojiMinorOrder(String minor) {
// Long result = minorToOrder.get(minor);
// if (result == null) {
// throw new InternalCldrException("No minor category (aka subgroup) found for " + minor
// + ". Update emoji-test.txt to latest, and setValue PathHeader.. functionMap.put(\"minor\", ...");
// }
// return result;
// }
// public String getMajorCategory(String emoji) {
// String majorCat = emojiToMajorCategory.get(emoji);
// if (majorCat == null) {
// if (EXTRA_SYMBOL_MINOR_CATEGORIES.containsKey(emoji)) {
// majorCat = "Symbols";
// } else {
// throw new InternalCldrException("No minor category (aka subgroup) found for " + emoji
// + ". Update emoji-test.txt to latest, and setValue PathHeader.. functionMap.put(\"major\", ...");
// }
// }
// return majorCat;
// }
// public Set<String> getMinorCategoriesWithExtras() {
// Set<String> result = new LinkedHashSet<>(emojiToMinorCategory.values());
// result.addAll(EXTRA_SYMBOL_MINOR_CATEGORIES.getAvailableValues());
// return ImmutableSet.copyOf(result);
// }
// public UnicodeSet getEmojiInMinorCategoriesWithExtras(String minorCategory) {
// return new UnicodeSet(emojiToMinorCategory.getSet(minorCategory))
// .addAll(EXTRA_SYMBOL_MINOR_CATEGORIES.getSet(minorCategory))
// .freeze();
// }
// public synchronized Set<String> getNamePaths() {
// return NAME_PATHS != null ? NAME_PATHS : (NAME_PATHS = buildPaths(TYPE_TTS));
// }
// public synchronized Set<String> getKeywordPaths() {
// return KEYWORD_PATHS != null ? KEYWORD_PATHS : (KEYWORD_PATHS = buildPaths(""));
// }
// private ImmutableSet<String> buildPaths(String suffix) {
// ImmutableSet.Builder<String> builder = ImmutableSet.builder();
// for (String s : getNonConstructed()) {
// String base = "//ldml/annotations/annotation[@cp=\"" + s + "\"]" + suffix;
// builder.add(base);
// }
// return builder.build();
// }
}

50
app/src/main/java/awais/instagrabber/utils/emoji/ImmutableEntry.java

@ -1,50 +0,0 @@
package awais.instagrabber.utils.emoji;
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
/*
*******************************************************************************
* Copyright (C) 2009-2012, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
import java.util.Map;
/**
* @author markdavis
*/
public class ImmutableEntry<K, V> implements Map.Entry<K, V> {
final K k;
final V v;
ImmutableEntry(K key, V value) {
k = key;
v = value;
}
public K getKey() {return k;}
public V getValue() {return v;}
public V setValue(V value) {
throw new UnsupportedOperationException();
}
public boolean equals(Object o) {
try {
Map.Entry e = (Map.Entry) o;
return UnicodeMap.areEqual(e.getKey(), k) && UnicodeMap.areEqual(e.getValue(), v);
} catch (ClassCastException e) {
return false;
}
}
public int hashCode() {
return ((k == null ? 0 : k.hashCode()) ^ (v == null ? 0 : v.hashCode()));
}
public String toString() {
return k + "=" + v;
}
}

1305
app/src/main/java/awais/instagrabber/utils/emoji/UnicodeMap.java
File diff suppressed because it is too large
View File

3
app/src/main/java/awais/instagrabber/viewmodels/DirectSettingsViewModel.java

@ -45,7 +45,6 @@ public class DirectSettingsViewModel extends AndroidViewModel {
public DirectSettingsViewModel(final Application application,
@NonNull final String threadId,
final DirectThread backup,
final boolean pending,
@NonNull final User currentUser) {
super(application);
@ -59,7 +58,7 @@ public class DirectSettingsViewModel extends AndroidViewModel {
final ContentResolver contentResolver = application.getContentResolver();
resources = getApplication().getResources();
final DirectMessagesManager messagesManager = DirectMessagesManager.getInstance();
threadManager = messagesManager.getThreadManager(threadId, pending, backup, currentUser, contentResolver);
threadManager = messagesManager.getThreadManager(threadId, pending, currentUser, contentResolver);
}
@NonNull

47
app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.java

@ -23,6 +23,7 @@ import java.util.stream.Collectors;
import awais.instagrabber.customviews.emoji.Emoji;
import awais.instagrabber.managers.DirectMessagesManager;
import awais.instagrabber.managers.InboxManager;
import awais.instagrabber.managers.ThreadManager;
import awais.instagrabber.models.Resource;
import awais.instagrabber.repositories.responses.User;
@ -57,7 +58,6 @@ public class DirectThreadViewModel extends AndroidViewModel {
public DirectThreadViewModel(@NonNull final Application application,
@NonNull final String threadId,
final DirectThread backup,
final boolean pending,
@NonNull final User currentUser) {
super(application);
@ -74,7 +74,7 @@ public class DirectThreadViewModel extends AndroidViewModel {
contentResolver = application.getContentResolver();
recordingsDir = DirectoryUtils.getOutputMediaDirectory(application, "Recordings");
final DirectMessagesManager messagesManager = DirectMessagesManager.getInstance();
threadManager = messagesManager.getThreadManager(threadId, pending, backup, currentUser, contentResolver);
threadManager = messagesManager.getThreadManager(threadId, pending, currentUser, contentResolver);
threadManager.fetchPendingRequests();
}
@ -278,16 +278,24 @@ public class DirectThreadViewModel extends AndroidViewModel {
return threadManager.declineRequest();
}
public void markAsSeen() {
if (currentUser == null) return;
public LiveData<Resource<Object>> markAsSeen() {
if (currentUser == null) {
return getSuccessEventResObjectLiveData();
}
final DirectThread thread = getThread().getValue();
if (thread == null) return;
if (thread == null) {
return getSuccessEventResObjectLiveData();
}
final List<DirectItem> items = thread.getItems();
if (items == null || items.isEmpty()) return;
if (items == null || items.isEmpty()) {
return getSuccessEventResObjectLiveData();
}
final Optional<DirectItem> itemOptional = items.stream()
.filter(item -> item.getUserId() != currentUser.getPk())
.findFirst();
if (!itemOptional.isPresent()) return;
if (!itemOptional.isPresent()) {
return getSuccessEventResObjectLiveData();
}
final DirectItem directItem = itemOptional.get();
final Map<Long, DirectThreadLastSeenAt> lastSeenAt = thread.getLastSeenAt();
if (lastSeenAt != null) {
@ -296,10 +304,29 @@ public class DirectThreadViewModel extends AndroidViewModel {
if (seenAt != null
&& (Objects.equals(seenAt.getItemId(), directItem.getItemId())
|| Long.parseLong(seenAt.getTimestamp()) >= directItem.getTimestamp())) {
return;
return getSuccessEventResObjectLiveData();
}
} catch (Exception ignored) {}
} catch (Exception ignored) {
return getSuccessEventResObjectLiveData();
}
}
return threadManager.markAsSeen(directItem);
}
@NonNull
private MutableLiveData<Resource<Object>> getSuccessEventResObjectLiveData() {
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
data.postValue(Resource.success(new Object()));
return data;
}
public void deleteThreadIfRequired() {
final DirectThread thread = getThread().getValue();
if (thread == null) return;
if (thread.isTemp() && (thread.getItems() == null || thread.getItems().isEmpty())) {
final InboxManager inboxManager = DirectMessagesManager.getInstance().getInboxManager();
inboxManager.removeThread(threadId);
}
threadManager.markAsSeen(directItem);
}
}

6
app/src/main/java/awais/instagrabber/viewmodels/factories/DirectSettingsViewModelFactory.java

@ -7,25 +7,21 @@ import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.viewmodels.DirectSettingsViewModel;
public class DirectSettingsViewModelFactory implements ViewModelProvider.Factory {
private final Application application;
private final String threadId;
private final DirectThread backup;
private final boolean pending;
private final User currentUser;
public DirectSettingsViewModelFactory(@NonNull final Application application,
@NonNull final String threadId,
@NonNull final DirectThread backup,
final boolean pending,
@NonNull final User currentUser) {
this.application = application;
this.threadId = threadId;
this.backup = backup;
this.pending = pending;
this.currentUser = currentUser;
}
@ -34,6 +30,6 @@ public class DirectSettingsViewModelFactory implements ViewModelProvider.Factory
@Override
public <T extends ViewModel> T create(@NonNull final Class<T> modelClass) {
//noinspection unchecked
return (T) new DirectSettingsViewModel(application, threadId, backup, pending, currentUser);
return (T) new DirectSettingsViewModel(application, threadId, pending, currentUser);
}
}

6
app/src/main/java/awais/instagrabber/viewmodels/factories/DirectThreadViewModelFactory.java

@ -7,25 +7,21 @@ import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.viewmodels.DirectThreadViewModel;
public class DirectThreadViewModelFactory implements ViewModelProvider.Factory {
private final Application application;
private final String threadId;
private final DirectThread backup;
private final boolean pending;
private final User currentUser;
public DirectThreadViewModelFactory(@NonNull final Application application,
@NonNull final String threadId,
final DirectThread backup,
final boolean pending,
@NonNull final User currentUser) {
this.application = application;
this.threadId = threadId;
this.backup = backup;
this.pending = pending;
this.currentUser = currentUser;
}
@ -34,6 +30,6 @@ public class DirectThreadViewModelFactory implements ViewModelProvider.Factory {
@Override
public <T extends ViewModel> T create(@NonNull final Class<T> modelClass) {
//noinspection unchecked
return (T) new DirectThreadViewModel(application, threadId, backup, pending, currentUser);
return (T) new DirectThreadViewModel(application, threadId, pending, currentUser);
}
}

BIN
app/src/main/res/drawable/barinsta_logo.png

After

Width: 300  |  Height: 284  |  Size: 48 KiB

10
app/src/main/res/drawable/launch_screen.xml

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
android:opacity="opaque">
<item android:drawable="@color/grey_900" />
<item>
<bitmap
android:gravity="center"
android:src="@drawable/barinsta_logo" />
</item>
</layer-list>

2
app/src/main/res/layout/fragment_direct_messages_thread.xml

@ -257,7 +257,7 @@
android:layout_width="0dp"
android:layout_height="250dp"
android:translationY="250dp"
android:visibility="gone"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"

2
app/src/main/res/menu/dm_thread_menu.xml

@ -8,7 +8,7 @@
app:showAsAction="ifRoom" />
<item
android:id="@+id/mark_as_seen"
android:icon="@drawable/ic_outline_views_24"
android:icon="@drawable/ic_check_all_24"
android:title="@string/mark_as_seen"
app:showAsAction="ifRoom" />
<item

9
app/src/main/res/navigation/direct_messages_nav_graph.xml

@ -122,10 +122,6 @@
android:name="pending"
android:defaultValue="false"
app:argType="boolean" />
<argument
android:name="backup"
app:nullable="true"
app:argType="awais.instagrabber.repositories.responses.directmessages.DirectThread" />
<action
android:id="@+id/action_thread_to_settings"
app:destination="@id/directMessagesSettingsFragment" />
@ -156,11 +152,6 @@
android:defaultValue="false"
app:argType="boolean" />
<argument
android:name="backup"
app:nullable="true"
app:argType="awais.instagrabber.repositories.responses.directmessages.DirectThread" />
<action
android:id="@+id/action_settings_to_inbox"
app:destination="@id/directMessagesInboxFragment"

4457
app/src/main/res/raw/emoji_test.txt
File diff suppressed because it is too large
View File

1
app/src/main/res/raw/emojis.json
File diff suppressed because it is too large
View File

1
app/src/main/res/values/strings.xml

@ -471,4 +471,5 @@
<string name="edit_keyword_filter">Edit keyword filters</string>
<string name="added_keywords">Added keyword: {0} to filter list</string>
<string name="removed_keywords">Removed keyword: {0} from filter list</string>
<string name="marked_as_seen">Marked as seen</string>
</resources>

4
app/src/main/res/values/themes.xml

@ -140,4 +140,8 @@
<item name="dmOutgoingBgColor">@color/deep_purple_400</item>
<item name="dmDateHeaderBgColor">@color/deep_purple_600</item>
</style>
<style name="AppTheme.Launcher" parent="AppTheme.Dark">
<item name="android:windowBackground">@drawable/launch_screen</item>
</style>
</resources>

2
build.gradle

@ -5,7 +5,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.2'
classpath 'com.android.tools.build:gradle:4.1.3'
def nav_version = "2.3.4"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
}

6
renovate.json

@ -0,0 +1,6 @@
{
"extends": [
"config:base"
],
"prConcurrentLimit": 5
}
Loading…
Cancel
Save