mirror of
https://github.com/KokaKiwi/BarInsta
synced 2024-12-26 22:56:58 +00:00
Merge pull request #753 from ammargitham/dm-notifications-enhancements
DM Sync
This commit is contained in:
commit
8a007cbeda
@ -56,13 +56,13 @@ configurations.all {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.1'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||
|
||||
def appcompat_version = "1.2.0"
|
||||
def nav_version = '2.3.2'
|
||||
def nav_version = '2.3.3'
|
||||
def exoplayer_version = '2.12.0'
|
||||
|
||||
implementation 'com.google.android.material:material:1.3.0'
|
||||
implementation 'com.google.android.material:material:1.4.0-alpha01'
|
||||
|
||||
implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version"
|
||||
implementation "com.google.android.exoplayer:exoplayer-dash:$exoplayer_version"
|
||||
@ -70,15 +70,16 @@ dependencies {
|
||||
|
||||
implementation "androidx.appcompat:appcompat:$appcompat_version"
|
||||
implementation "androidx.appcompat:appcompat-resources:$appcompat_version"
|
||||
implementation "androidx.recyclerview:recyclerview:1.2.0-beta01"
|
||||
implementation "androidx.recyclerview:recyclerview:1.2.0-beta02"
|
||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||
implementation "androidx.viewpager2:viewpager2:1.0.0"
|
||||
implementation "androidx.navigation:navigation-fragment:$nav_version"
|
||||
implementation "androidx.navigation:navigation-ui:$nav_version"
|
||||
implementation "androidx.constraintlayout:constraintlayout:2.0.4"
|
||||
implementation "androidx.preference:preference:1.1.1"
|
||||
implementation "androidx.work:work-runtime:2.4.0"
|
||||
implementation "androidx.work:work-runtime:2.5.0"
|
||||
implementation 'androidx.palette:palette:1.0.0'
|
||||
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
|
||||
|
||||
implementation 'com.google.guava:guava:27.0.1-android'
|
||||
|
||||
@ -89,10 +90,10 @@ dependencies {
|
||||
annotationProcessor "androidx.room:room-compiler:$room_version"
|
||||
|
||||
// CameraX
|
||||
def camerax_version = "1.0.0-rc01"
|
||||
def camerax_version = "1.1.0-alpha02"
|
||||
implementation "androidx.camera:camera-camera2:$camerax_version"
|
||||
implementation "androidx.camera:camera-lifecycle:$camerax_version"
|
||||
implementation "androidx.camera:camera-view:1.0.0-alpha20"
|
||||
implementation "androidx.camera:camera-view:1.0.0-alpha22"
|
||||
|
||||
// EmojiCompat
|
||||
def emoji_compat_version = "1.1.0"
|
||||
|
161
app/schemas/awais.instagrabber.db.AppDatabase/5.json
Normal file
161
app/schemas/awais.instagrabber.db.AppDatabase/5.json
Normal file
@ -0,0 +1,161 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 5,
|
||||
"identityHash": "0b38e12b76bb081ec837191c5ef5b54e",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "accounts",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `uid` TEXT, `username` TEXT, `cookie` TEXT, `full_name` TEXT, `profile_pic` TEXT)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "uid",
|
||||
"columnName": "uid",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "username",
|
||||
"columnName": "username",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "cookie",
|
||||
"columnName": "cookie",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "fullName",
|
||||
"columnName": "full_name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "profilePic",
|
||||
"columnName": "profile_pic",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "favorites",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `query_text` TEXT, `type` TEXT, `display_name` TEXT, `pic_url` TEXT, `date_added` INTEGER)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "query",
|
||||
"columnName": "query_text",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "display_name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "picUrl",
|
||||
"columnName": "pic_url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "dateAdded",
|
||||
"columnName": "date_added",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "dm_last_notified",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `thread_id` TEXT, `last_notified_msg_ts` INTEGER, `last_notified_at` INTEGER)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "threadId",
|
||||
"columnName": "thread_id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastNotifiedMsgTs",
|
||||
"columnName": "last_notified_msg_ts",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastNotifiedAt",
|
||||
"columnName": "last_notified_at",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_dm_last_notified_thread_id",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"thread_id"
|
||||
],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_dm_last_notified_thread_id` ON `${TABLE_NAME}` (`thread_id`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0b38e12b76bb081ec837191c5ef5b54e')"
|
||||
]
|
||||
}
|
||||
}
|
@ -8,12 +8,12 @@
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<!--<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />-->
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera.any"
|
||||
android:required="false" />
|
||||
|
||||
|
||||
<application
|
||||
android:name=".InstaGrabberApplication"
|
||||
android:allowBackup="true"
|
||||
@ -37,8 +37,8 @@
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<!--<action android:name="android.intent.action.SEARCH" />-->
|
||||
<!--<action android:name="android.intent.action.WEB_SEARCH" />-->
|
||||
<!-- <action android:name="android.intent.action.SEARCH" /> -->
|
||||
<!-- <action android:name="android.intent.action.WEB_SEARCH" /> -->
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
@ -147,6 +147,19 @@
|
||||
<service
|
||||
android:name=".services.DeleteImageIntentService"
|
||||
android:exported="false" />
|
||||
<service
|
||||
android:name=".services.DMSyncService"
|
||||
android:exported="false" />
|
||||
|
||||
<receiver
|
||||
android:name=".services.DMSyncAlarmReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
<!--<receiver android:name=".services.BootCompletedReceiver" >-->
|
||||
<!-- <intent-filter>-->
|
||||
<!-- <action android:name="android.intent.action.BOOT_COMPLETED" />-->
|
||||
<!-- </intent-filter>-->
|
||||
<!--</receiver>-->
|
||||
|
||||
<uses-library
|
||||
android:name="org.apache.http.legacy"
|
||||
@ -156,4 +169,5 @@
|
||||
android:name="fontProviderRequests"
|
||||
android:value="Noto Color Emoji Compat" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -38,6 +38,8 @@ import androidx.emoji.text.FontRequestEmojiCompatConfig;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.NavBackStackEntry;
|
||||
import androidx.navigation.NavController;
|
||||
import androidx.navigation.NavDestination;
|
||||
@ -45,6 +47,7 @@ import androidx.navigation.ui.NavigationUI;
|
||||
|
||||
import com.google.android.material.appbar.AppBarLayout;
|
||||
import com.google.android.material.appbar.CollapsingToolbarLayout;
|
||||
import com.google.android.material.behavior.HideBottomViewOnScrollBehavior;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -61,12 +64,15 @@ import awais.instagrabber.asyncs.SuggestionsFetcher;
|
||||
import awais.instagrabber.customviews.emoji.EmojiVariantManager;
|
||||
import awais.instagrabber.databinding.ActivityMainBinding;
|
||||
import awais.instagrabber.fragments.PostViewV2Fragment;
|
||||
import awais.instagrabber.fragments.directmessages.DirectMessageInboxFragmentDirections;
|
||||
import awais.instagrabber.fragments.main.FeedFragment;
|
||||
import awais.instagrabber.fragments.settings.PreferenceKeys;
|
||||
import awais.instagrabber.interfaces.FetchListener;
|
||||
import awais.instagrabber.models.IntentModel;
|
||||
import awais.instagrabber.models.SuggestionModel;
|
||||
import awais.instagrabber.models.enums.SuggestionType;
|
||||
import awais.instagrabber.services.ActivityCheckerService;
|
||||
import awais.instagrabber.services.DMSyncAlarmReceiver;
|
||||
import awais.instagrabber.utils.AppExecutors;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.CookieUtils;
|
||||
@ -75,6 +81,7 @@ import awais.instagrabber.utils.IntentUtils;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
import awais.instagrabber.utils.emoji.EmojiParser;
|
||||
import awais.instagrabber.viewmodels.AppStateViewModel;
|
||||
|
||||
import static awais.instagrabber.utils.NavigationExtensions.setupWithNavController;
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
@ -102,6 +109,8 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
|
||||
private int firstFragmentGraphIndex;
|
||||
private boolean isActivityCheckerServiceBound = false;
|
||||
private boolean isBackStackEmpty = false;
|
||||
private boolean isLoggedIn;
|
||||
private HideBottomViewOnScrollBehavior<BottomNavigationView> behavior;
|
||||
|
||||
private final ServiceConnection serviceConnection = new ServiceConnection() {
|
||||
@Override
|
||||
@ -131,10 +140,18 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
|
||||
binding = ActivityMainBinding.inflate(getLayoutInflater());
|
||||
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||
CookieUtils.setupCookies(cookie);
|
||||
isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != 0;
|
||||
setContentView(binding.getRoot());
|
||||
final Toolbar toolbar = binding.toolbar;
|
||||
setSupportActionBar(toolbar);
|
||||
createNotificationChannels();
|
||||
try {
|
||||
final CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) binding.bottomNavView.getLayoutParams();
|
||||
//noinspection unchecked
|
||||
behavior = (HideBottomViewOnScrollBehavior<BottomNavigationView>) layoutParams.getBehavior();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "onCreate: ", e);
|
||||
}
|
||||
if (savedInstanceState == null) {
|
||||
setupBottomNavigationBar(true);
|
||||
}
|
||||
@ -142,6 +159,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
|
||||
final boolean checkUpdates = settingsHelper.getBoolean(Constants.CHECK_UPDATES);
|
||||
if (checkUpdates) FlavorTown.updateCheck(this);
|
||||
FlavorTown.changelogCheck(this);
|
||||
new ViewModelProvider(this).get(AppStateViewModel.class); // Just initiate the App state here
|
||||
final Intent intent = getIntent();
|
||||
handleIntent(intent);
|
||||
if (!TextUtils.isEmpty(cookie) && settingsHelper.getBoolean(Constants.CHECK_ACTIVITY)) {
|
||||
@ -154,6 +172,14 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
|
||||
EmojiVariantManager.getInstance();
|
||||
});
|
||||
initEmojiCompat();
|
||||
// initDmService();
|
||||
}
|
||||
|
||||
private void initDmService() {
|
||||
if (!isLoggedIn) return;
|
||||
final boolean enabled = settingsHelper.getBoolean(PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH);
|
||||
if (!enabled) return;
|
||||
DMSyncAlarmReceiver.setAlarm(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -242,15 +268,22 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
|
||||
}
|
||||
|
||||
private void createNotificationChannels() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(getApplicationContext());
|
||||
notificationManager.createNotificationChannel(new NotificationChannel(Constants.DOWNLOAD_CHANNEL_ID,
|
||||
Constants.DOWNLOAD_CHANNEL_NAME,
|
||||
NotificationManager.IMPORTANCE_DEFAULT));
|
||||
notificationManager.createNotificationChannel(new NotificationChannel(Constants.ACTIVITY_CHANNEL_ID,
|
||||
Constants.ACTIVITY_CHANNEL_NAME,
|
||||
NotificationManager.IMPORTANCE_DEFAULT));
|
||||
}
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
|
||||
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(getApplicationContext());
|
||||
notificationManager.createNotificationChannel(new NotificationChannel(Constants.DOWNLOAD_CHANNEL_ID,
|
||||
Constants.DOWNLOAD_CHANNEL_NAME,
|
||||
NotificationManager.IMPORTANCE_DEFAULT));
|
||||
notificationManager.createNotificationChannel(new NotificationChannel(Constants.ACTIVITY_CHANNEL_ID,
|
||||
Constants.ACTIVITY_CHANNEL_NAME,
|
||||
NotificationManager.IMPORTANCE_DEFAULT));
|
||||
notificationManager.createNotificationChannel(new NotificationChannel(Constants.DM_UNREAD_CHANNEL_ID,
|
||||
Constants.DM_UNREAD_CHANNEL_NAME,
|
||||
NotificationManager.IMPORTANCE_DEFAULT));
|
||||
final NotificationChannel silentNotificationChannel = new NotificationChannel(Constants.SILENT_NOTIFICATIONS_CHANNEL_ID,
|
||||
Constants.SILENT_NOTIFICATIONS_CHANNEL_NAME,
|
||||
NotificationManager.IMPORTANCE_LOW);
|
||||
silentNotificationChannel.setSound(null, null);
|
||||
notificationManager.createNotificationChannel(silentNotificationChannel);
|
||||
}
|
||||
|
||||
private void setupSuggestions() {
|
||||
@ -387,8 +420,6 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
|
||||
|
||||
private void setupBottomNavigationBar(final boolean setDefaultFromSettings) {
|
||||
int main_nav_ids = R.array.main_nav_ids;
|
||||
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||
final boolean isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != 0;
|
||||
if (!isLoggedIn) {
|
||||
main_nav_ids = R.array.logged_out_main_nav_ids;
|
||||
final int selectedItemId = binding.bottomNavView.getSelectedItemId();
|
||||
@ -477,7 +508,11 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
|
||||
final int destinationId = destination.getId();
|
||||
@SuppressLint("RestrictedApi") final Deque<NavBackStackEntry> backStack = navController.getBackStack();
|
||||
setupMenu(backStack.size(), destinationId);
|
||||
binding.bottomNavView.setVisibility(SHOW_BOTTOM_VIEW_DESTINATIONS.contains(destinationId) ? View.VISIBLE : View.GONE);
|
||||
final boolean contains = SHOW_BOTTOM_VIEW_DESTINATIONS.contains(destinationId);
|
||||
binding.bottomNavView.setVisibility(contains ? View.VISIBLE : View.GONE);
|
||||
if (contains && behavior != null) {
|
||||
behavior.slideUp(binding.bottomNavView);
|
||||
}
|
||||
|
||||
// explicitly hide keyboard when we navigate
|
||||
final View view = getCurrentFocus();
|
||||
@ -518,6 +553,10 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
|
||||
showActivityView();
|
||||
return;
|
||||
}
|
||||
if (Constants.ACTION_SHOW_DM_THREAD.equals(action)) {
|
||||
showThread(intent);
|
||||
return;
|
||||
}
|
||||
if (Intent.ACTION_SEND.equals(action) && type != null) {
|
||||
if (type.equals("text/plain")) {
|
||||
handleUrl(intent.getStringExtra(Intent.EXTRA_TEXT));
|
||||
@ -531,6 +570,58 @@ 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);
|
||||
}
|
||||
|
||||
public void navigateToThread(final String threadId, final String threadTitle) {
|
||||
if (threadId == null || threadTitle == null) return;
|
||||
currentNavControllerLiveData.observe(this, new Observer<NavController>() {
|
||||
@Override
|
||||
public void onChanged(final NavController navController) {
|
||||
if (navController == null) return;
|
||||
if (navController.getGraph().getId() != R.id.direct_messages_nav_graph) return;
|
||||
try {
|
||||
final NavDestination currentDestination = navController.getCurrentDestination();
|
||||
if (currentDestination != null && currentDestination.getId() == R.id.directMessagesInboxFragment) {
|
||||
// if we are already on the inbox page, navigate to the thread
|
||||
// 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);
|
||||
navController.navigate(action);
|
||||
});
|
||||
return;
|
||||
}
|
||||
// add a destination change listener to navigate to thread once we are on the inbox page
|
||||
navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
|
||||
@Override
|
||||
public void onDestinationChanged(@NonNull final NavController controller,
|
||||
@NonNull final NavDestination destination,
|
||||
@Nullable final Bundle arguments) {
|
||||
if (destination.getId() == R.id.directMessagesInboxFragment) {
|
||||
final DirectMessageInboxFragmentDirections.ActionInboxToThread action = DirectMessageInboxFragmentDirections
|
||||
.actionInboxToThread(threadId, threadTitle);
|
||||
controller.navigate(action);
|
||||
controller.removeOnDestinationChangedListener(this);
|
||||
}
|
||||
}
|
||||
});
|
||||
// pop back stack until we reach the inbox page
|
||||
navController.popBackStack(R.id.directMessagesInboxFragment, false);
|
||||
} finally {
|
||||
currentNavControllerLiveData.removeObserver(this);
|
||||
}
|
||||
}
|
||||
});
|
||||
final int selectedItemId = binding.bottomNavView.getSelectedItemId();
|
||||
if (selectedItemId != R.navigation.direct_messages_nav_graph) {
|
||||
setBottomNavSelectedItem(R.navigation.direct_messages_nav_graph);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleUrl(final String url) {
|
||||
if (url == null) return;
|
||||
// Log.d(TAG, url);
|
||||
|
@ -258,7 +258,7 @@ public final class DirectItemsAdapter extends RecyclerView.Adapter<RecyclerView.
|
||||
public void setThread(final DirectThread thread) {
|
||||
if (thread == null) return;
|
||||
this.thread = thread;
|
||||
notifyDataSetChanged();
|
||||
// notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void submitList(@Nullable final List<DirectItem> list) {
|
||||
|
@ -36,7 +36,9 @@ public final class DirectMessageInboxAdapter extends ListAdapter<DirectThread, D
|
||||
final DirectItem oldItemFirst = oldThread.getFirstDirectItem();
|
||||
final DirectItem newItemFirst = newThread.getFirstDirectItem();
|
||||
if (oldItemFirst == null || newItemFirst == null) return false;
|
||||
return oldItemFirst.getItemId().equals(newItemFirst.getItemId());
|
||||
final boolean idsEqual = oldItemFirst.getItemId().equals(newItemFirst.getItemId());
|
||||
if (!idsEqual) return false;
|
||||
return oldItemFirst.getTimestamp() == newItemFirst.getTimestamp();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -43,14 +43,15 @@ public final class FeedAdapterV2 extends ListAdapter<Media, RecyclerView.ViewHol
|
||||
private static final DiffUtil.ItemCallback<Media> DIFF_CALLBACK = new DiffUtil.ItemCallback<Media>() {
|
||||
@Override
|
||||
public boolean areItemsTheSame(@NonNull final Media oldItem, @NonNull final Media newItem) {
|
||||
return oldItem.getPk().equals(newItem.getPk());
|
||||
return Objects.equals(oldItem.getPk(), newItem.getPk());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(@NonNull final Media oldItem, @NonNull final Media newItem) {
|
||||
final Caption oldItemCaption = oldItem.getCaption();
|
||||
final Caption newItemCaption = newItem.getCaption();
|
||||
return oldItem.getPk().equals(newItem.getPk()) && Objects.equals(getCaptionText(oldItemCaption), getCaptionText(newItemCaption));
|
||||
return Objects.equals(oldItem.getPk(), newItem.getPk())
|
||||
&& Objects.equals(getCaptionText(oldItemCaption), getCaptionText(newItemCaption));
|
||||
}
|
||||
|
||||
private String getCaptionText(final Caption caption) {
|
||||
|
@ -0,0 +1,107 @@
|
||||
package awais.instagrabber.adapters;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
import androidx.recyclerview.widget.ListAdapter;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||
import com.facebook.drawee.backends.pipeline.PipelineDraweeControllerBuilder;
|
||||
import com.facebook.drawee.controller.BaseControllerListener;
|
||||
import com.facebook.drawee.drawable.ScalingUtils;
|
||||
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
|
||||
import com.facebook.imagepipeline.common.ResizeOptions;
|
||||
import com.facebook.imagepipeline.image.ImageInfo;
|
||||
import com.facebook.imagepipeline.request.ImageRequest;
|
||||
import com.facebook.imagepipeline.request.ImageRequestBuilder;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import awais.instagrabber.databinding.ItemMediaBinding;
|
||||
import awais.instagrabber.repositories.responses.giphy.GiphyGif;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
|
||||
public class GifItemsAdapter extends ListAdapter<GiphyGif, GifItemsAdapter.GifViewHolder> {
|
||||
|
||||
private static final DiffUtil.ItemCallback<GiphyGif> diffCallback = new DiffUtil.ItemCallback<GiphyGif>() {
|
||||
@Override
|
||||
public boolean areItemsTheSame(@NonNull final GiphyGif oldItem, @NonNull final GiphyGif newItem) {
|
||||
return Objects.equals(oldItem.getId(), newItem.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(@NonNull final GiphyGif oldItem, @NonNull final GiphyGif newItem) {
|
||||
return Objects.equals(oldItem.getId(), newItem.getId());
|
||||
}
|
||||
};
|
||||
|
||||
private final OnItemClickListener onItemClickListener;
|
||||
|
||||
public GifItemsAdapter(final OnItemClickListener onItemClickListener) {
|
||||
super(diffCallback);
|
||||
this.onItemClickListener = onItemClickListener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public GifViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
|
||||
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
|
||||
final ItemMediaBinding binding = ItemMediaBinding.inflate(layoutInflater, parent, false);
|
||||
return new GifViewHolder(binding, onItemClickListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull final GifViewHolder holder, final int position) {
|
||||
holder.bind(getItem(position));
|
||||
}
|
||||
|
||||
public static class GifViewHolder extends RecyclerView.ViewHolder {
|
||||
private static final String TAG = GifViewHolder.class.getSimpleName();
|
||||
private static final int size = Utils.displayMetrics.widthPixels / 3;
|
||||
|
||||
private final ItemMediaBinding binding;
|
||||
private final OnItemClickListener onItemClickListener;
|
||||
|
||||
public GifViewHolder(@NonNull final ItemMediaBinding binding,
|
||||
final OnItemClickListener onItemClickListener) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
this.onItemClickListener = onItemClickListener;
|
||||
binding.duration.setVisibility(View.GONE);
|
||||
final GenericDraweeHierarchyBuilder builder = new GenericDraweeHierarchyBuilder(itemView.getResources());
|
||||
builder.setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER);
|
||||
binding.item.setHierarchy(builder.build());
|
||||
}
|
||||
|
||||
public void bind(final GiphyGif item) {
|
||||
if (onItemClickListener != null) {
|
||||
itemView.setOnClickListener(v -> onItemClickListener.onItemClick(item));
|
||||
}
|
||||
final BaseControllerListener<ImageInfo> controllerListener = new BaseControllerListener<ImageInfo>() {
|
||||
@Override
|
||||
public void onFailure(final String id, final Throwable throwable) {
|
||||
Log.e(TAG, "onFailure: ", throwable);
|
||||
}
|
||||
};
|
||||
final ImageRequest request = ImageRequestBuilder
|
||||
.newBuilderWithSource(Uri.parse(item.getImages().getFixedHeight().getWebp()))
|
||||
.setResizeOptions(ResizeOptions.forDimensions(size, size))
|
||||
.build();
|
||||
final PipelineDraweeControllerBuilder builder = Fresco.newDraweeControllerBuilder()
|
||||
.setImageRequest(request)
|
||||
.setAutoPlayAnimations(true)
|
||||
.setControllerListener(controllerListener);
|
||||
binding.item.setController(builder.build());
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnItemClickListener {
|
||||
void onItemClick(GiphyGif giphyGif);
|
||||
}
|
||||
}
|
@ -81,16 +81,18 @@ public class TopicClusterViewHolder extends RecyclerView.ViewHolder {
|
||||
}
|
||||
if (bitmap != null) {
|
||||
Palette.from(bitmap).generate(p -> {
|
||||
final Palette.Swatch swatch = p.getDominantSwatch();
|
||||
final Resources resources = itemView.getResources();
|
||||
int titleTextColor = resources.getColor(R.color.white);
|
||||
if (swatch != null) {
|
||||
backgroundColor.set(swatch.getRgb());
|
||||
GradientDrawable gd = new GradientDrawable(
|
||||
GradientDrawable.Orientation.TOP_BOTTOM,
|
||||
new int[]{Color.TRANSPARENT, backgroundColor.get()});
|
||||
titleTextColor = swatch.getTitleTextColor();
|
||||
binding.background.setBackground(gd);
|
||||
if (p != null) {
|
||||
final Palette.Swatch swatch = p.getDominantSwatch();
|
||||
if (swatch != null) {
|
||||
backgroundColor.set(swatch.getRgb());
|
||||
GradientDrawable gd = new GradientDrawable(
|
||||
GradientDrawable.Orientation.TOP_BOTTOM,
|
||||
new int[]{Color.TRANSPARENT, backgroundColor.get()});
|
||||
titleTextColor = swatch.getTitleTextColor();
|
||||
binding.background.setBackground(gd);
|
||||
}
|
||||
}
|
||||
titleColor.set(titleTextColor);
|
||||
binding.title.setTextColor(titleTextColor);
|
||||
@ -127,8 +129,8 @@ public class TopicClusterViewHolder extends RecyclerView.ViewHolder {
|
||||
// binding.title.setTransitionName("title-" + topicCluster.getId());
|
||||
binding.cover.setTransitionName("cover-" + topicCluster.getId());
|
||||
final String thumbUrl = ResponseBodyUtils.getThumbUrl(topicCluster.getCoverMedias() == null
|
||||
? topicCluster.getCoverMedia()
|
||||
: topicCluster.getCoverMedias().get(0));
|
||||
? topicCluster.getCoverMedia()
|
||||
: topicCluster.getCoverMedias().get(0));
|
||||
if (thumbUrl == null) {
|
||||
binding.cover.setImageURI((String) null);
|
||||
} else {
|
||||
|
@ -12,26 +12,17 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.adapters.DirectMessageInboxAdapter.OnItemClickListener;
|
||||
import awais.instagrabber.databinding.LayoutDmInboxItemBinding;
|
||||
import awais.instagrabber.models.enums.DirectItemType;
|
||||
import awais.instagrabber.models.enums.MediaItemType;
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItemReelShare;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItemVisualMedia;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadDirectStory;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadLastSeenAt;
|
||||
import awais.instagrabber.repositories.responses.directmessages.RavenExpiringMediaActionSummary;
|
||||
import awais.instagrabber.utils.ResponseBodyUtils;
|
||||
import awais.instagrabber.utils.DMUtils;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
|
||||
public final class DirectInboxItemViewHolder extends RecyclerView.ViewHolder {
|
||||
@ -133,218 +124,17 @@ public final class DirectInboxItemViewHolder extends RecyclerView.ViewHolder {
|
||||
if (directStory != null && !directStory.getItems().isEmpty()) {
|
||||
final DirectItem item = directStory.getItems().get(0);
|
||||
final MediaItemType mediaType = item.getVisualMedia().getMedia().getMediaType();
|
||||
final String username = getUsername(thread.getUsers(), item.getUserId(), viewerId, resources);
|
||||
final String subtitle = getMediaSpecificSubtitle(username, resources, mediaType);
|
||||
final String username = DMUtils.getUsername(thread.getUsers(), item.getUserId(), viewerId, resources);
|
||||
final String subtitle = DMUtils.getMediaSpecificSubtitle(username, resources, mediaType);
|
||||
binding.subtitle.setText(subtitle);
|
||||
return;
|
||||
}
|
||||
final DirectItem item = thread.getFirstDirectItem();
|
||||
if (item == null) return;
|
||||
final long senderId = item.getUserId();
|
||||
final DirectItemType itemType = item.getItemType();
|
||||
String subtitle = null;
|
||||
final String username = getUsername(thread.getUsers(), senderId, viewerId, resources);
|
||||
String message = "";
|
||||
if (itemType == null) {
|
||||
message = resources.getString(R.string.dms_inbox_raven_message_unknown);
|
||||
} else {
|
||||
switch (itemType) {
|
||||
case TEXT:
|
||||
message = item.getText();
|
||||
break;
|
||||
case LIKE:
|
||||
message = item.getLike();
|
||||
break;
|
||||
case LINK:
|
||||
message = item.getLink().getText();
|
||||
break;
|
||||
case PLACEHOLDER:
|
||||
message = item.getPlaceholder().getMessage();
|
||||
break;
|
||||
case MEDIA_SHARE:
|
||||
subtitle = resources.getString(R.string.dms_inbox_shared_post, username != null ? username : "", item.getMediaShare().getUser().getUsername());
|
||||
break;
|
||||
case ANIMATED_MEDIA:
|
||||
subtitle = resources.getString(R.string.dms_inbox_shared_gif, username != null ? username : "");
|
||||
break;
|
||||
case PROFILE:
|
||||
subtitle = resources.getString(R.string.dms_inbox_shared_profile, username != null ? username : "", item.getProfile().getUsername());
|
||||
break;
|
||||
case LOCATION:
|
||||
subtitle = resources.getString(R.string.dms_inbox_shared_location, username != null ? username : "", item.getLocation().getName());
|
||||
break;
|
||||
case MEDIA: {
|
||||
final MediaItemType mediaType = item.getMedia().getMediaType();
|
||||
subtitle = getMediaSpecificSubtitle(username, resources, mediaType);
|
||||
break;
|
||||
}
|
||||
case STORY_SHARE: {
|
||||
final String reelType = item.getStoryShare().getReelType();
|
||||
if (reelType == null) {
|
||||
subtitle = item.getStoryShare().getTitle();
|
||||
} else {
|
||||
final int format = reelType.equals("highlight_reel")
|
||||
? R.string.dms_inbox_shared_highlight
|
||||
: R.string.dms_inbox_shared_story;
|
||||
subtitle = resources.getString(format, username != null ? username : "",
|
||||
item.getStoryShare().getMedia().getUser().getUsername());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case VOICE_MEDIA:
|
||||
subtitle = resources.getString(R.string.dms_inbox_shared_voice, username != null ? username : "");
|
||||
break;
|
||||
case ACTION_LOG:
|
||||
subtitle = item.getActionLog().getDescription();
|
||||
break;
|
||||
case VIDEO_CALL_EVENT:
|
||||
subtitle = item.getVideoCallEvent().getDescription();
|
||||
break;
|
||||
case CLIP:
|
||||
subtitle = resources.getString(R.string.dms_inbox_shared_clip, username != null ? username : "",
|
||||
item.getClip().getClip().getUser().getUsername());
|
||||
break;
|
||||
case FELIX_SHARE:
|
||||
subtitle = resources.getString(R.string.dms_inbox_shared_igtv, username != null ? username : "",
|
||||
item.getFelixShare().getVideo().getUser().getUsername());
|
||||
break;
|
||||
case RAVEN_MEDIA:
|
||||
subtitle = getRavenMediaSubtitle(item, resources, username);
|
||||
break;
|
||||
case REEL_SHARE:
|
||||
final DirectItemReelShare reelShare = item.getReelShare();
|
||||
if (reelShare == null) {
|
||||
subtitle = "";
|
||||
break;
|
||||
}
|
||||
final String reelType = reelShare.getType();
|
||||
switch (reelType) {
|
||||
case "reply":
|
||||
if (viewerId == item.getUserId()) {
|
||||
subtitle = resources.getString(R.string.dms_inbox_replied_story_outgoing, reelShare.getText());
|
||||
} else {
|
||||
subtitle = resources.getString(R.string.dms_inbox_replied_story_incoming, username != null ? username : "", reelShare.getText());
|
||||
}
|
||||
break;
|
||||
case "mention":
|
||||
if (viewerId == item.getUserId()) {
|
||||
// You mentioned the other person
|
||||
final long mentionedUserId = item.getReelShare().getMentionedUserId();
|
||||
final String otherUsername = getUsername(thread.getUsers(), mentionedUserId, viewerId, resources);
|
||||
subtitle = resources.getString(R.string.dms_inbox_mentioned_story_outgoing, otherUsername);
|
||||
} else {
|
||||
// They mentioned you
|
||||
subtitle = resources.getString(R.string.dms_inbox_mentioned_story_incoming, username != null ? username : "");
|
||||
}
|
||||
break;
|
||||
case "reaction":
|
||||
if (viewerId == item.getUserId()) {
|
||||
subtitle = resources.getString(R.string.dms_inbox_reacted_story_outgoing, reelShare.getText());
|
||||
} else {
|
||||
subtitle = resources.getString(R.string.dms_inbox_reacted_story_incoming, username != null ? username : "", reelShare.getText());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
subtitle = "";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
message = resources.getString(R.string.dms_inbox_raven_message_unknown);
|
||||
}
|
||||
}
|
||||
if (subtitle == null) {
|
||||
if (thread.getUsers().size() > 1
|
||||
|| (thread.getUsers().size() == 1 && senderId == viewerId)) {
|
||||
subtitle = String.format("%s: %s", username != null ? username : "", message);
|
||||
} else {
|
||||
subtitle = message;
|
||||
}
|
||||
}
|
||||
final String subtitle = DMUtils.getMessageString(thread, resources, viewerId, item);
|
||||
binding.subtitle.setText(subtitle != null ? subtitle : "");
|
||||
}
|
||||
|
||||
private String getMediaSpecificSubtitle(final String username, final Resources resources, final MediaItemType mediaType) {
|
||||
final String userSharedAnImage = resources.getString(R.string.dms_inbox_shared_image, username != null ? username : "");
|
||||
final String userSharedAVideo = resources.getString(R.string.dms_inbox_shared_video, username != null ? username : "");
|
||||
final String userSentAMessage = resources.getString(R.string.dms_inbox_shared_message, username != null ? username : "");
|
||||
String subtitle;
|
||||
switch (mediaType) {
|
||||
case MEDIA_TYPE_IMAGE:
|
||||
subtitle = userSharedAnImage;
|
||||
break;
|
||||
case MEDIA_TYPE_VIDEO:
|
||||
subtitle = userSharedAVideo;
|
||||
break;
|
||||
default:
|
||||
subtitle = userSentAMessage;
|
||||
break;
|
||||
}
|
||||
return subtitle;
|
||||
}
|
||||
|
||||
private String getRavenMediaSubtitle(final DirectItem item,
|
||||
final Resources resources,
|
||||
final String username) {
|
||||
String subtitle = "↗ ";
|
||||
final DirectItemVisualMedia visualMedia = item.getVisualMedia();
|
||||
final RavenExpiringMediaActionSummary summary = visualMedia.getExpiringMediaActionSummary();
|
||||
if (summary != null) {
|
||||
final RavenExpiringMediaActionSummary.ActionType expiringMediaType = summary.getType();
|
||||
int textRes = 0;
|
||||
switch (expiringMediaType) {
|
||||
case DELIVERED:
|
||||
textRes = R.string.dms_inbox_raven_media_delivered;
|
||||
break;
|
||||
case SENT:
|
||||
textRes = R.string.dms_inbox_raven_media_sent;
|
||||
break;
|
||||
case OPENED:
|
||||
textRes = R.string.dms_inbox_raven_media_opened;
|
||||
break;
|
||||
case REPLAYED:
|
||||
textRes = R.string.dms_inbox_raven_media_replayed;
|
||||
break;
|
||||
case SENDING:
|
||||
textRes = R.string.dms_inbox_raven_media_sending;
|
||||
break;
|
||||
case BLOCKED:
|
||||
textRes = R.string.dms_inbox_raven_media_blocked;
|
||||
break;
|
||||
case SUGGESTED:
|
||||
textRes = R.string.dms_inbox_raven_media_suggested;
|
||||
break;
|
||||
case SCREENSHOT:
|
||||
textRes = R.string.dms_inbox_raven_media_screenshot;
|
||||
break;
|
||||
case CANNOT_DELIVER:
|
||||
textRes = R.string.dms_inbox_raven_media_cant_deliver;
|
||||
break;
|
||||
}
|
||||
if (textRes > 0) {
|
||||
subtitle += itemView.getContext().getString(textRes);
|
||||
}
|
||||
return subtitle;
|
||||
}
|
||||
final MediaItemType mediaType = visualMedia.getMedia().getMediaType();
|
||||
subtitle = getMediaSpecificSubtitle(username, resources, mediaType);
|
||||
return subtitle;
|
||||
}
|
||||
|
||||
private String getUsername(final List<User> users,
|
||||
final long userId,
|
||||
final long viewerId,
|
||||
final Resources resources) {
|
||||
if (userId == viewerId) {
|
||||
return resources.getString(R.string.you);
|
||||
}
|
||||
final Optional<User> senderOptional = users.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(user -> user.getPk() == userId)
|
||||
.findFirst();
|
||||
return senderOptional.map(User::getUsername).orElse(null);
|
||||
}
|
||||
|
||||
private void setDateTime(@NonNull final DirectItem item) {
|
||||
final long timestamp = item.getTimestamp() / 1000;
|
||||
final String dateTimeString = TextUtils.getRelativeDateTimeString(itemView.getContext(), timestamp);
|
||||
@ -352,9 +142,7 @@ public final class DirectInboxItemViewHolder extends RecyclerView.ViewHolder {
|
||||
}
|
||||
|
||||
private void setReadState(@NonNull final DirectThread thread) {
|
||||
final DirectItem item = thread.getItems().get(0);
|
||||
final Map<Long, DirectThreadLastSeenAt> lastSeenAtMap = thread.getLastSeenAt();
|
||||
final boolean read = ResponseBodyUtils.isRead(item, lastSeenAtMap, Collections.singletonList(thread.getViewerId()), thread.getDirectStory());
|
||||
final boolean read = DMUtils.isRead(thread);
|
||||
binding.unread.setVisibility(read ? View.GONE : View.VISIBLE);
|
||||
binding.threadTitle.setTypeface(null, read ? Typeface.NORMAL : Typeface.BOLD);
|
||||
binding.subtitle.setTypeface(null, read ? Typeface.NORMAL : Typeface.BOLD);
|
||||
|
@ -45,6 +45,7 @@ import awais.instagrabber.repositories.responses.directmessages.DirectItemEmojiR
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItemReactions;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItemStoryShare;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
||||
import awais.instagrabber.utils.DMUtils;
|
||||
import awais.instagrabber.utils.DeepLinkParser;
|
||||
import awais.instagrabber.utils.ResponseBodyUtils;
|
||||
|
||||
@ -196,7 +197,10 @@ public abstract class DirectItemViewHolder extends RecyclerView.ViewHolder imple
|
||||
if (item.isPending()) {
|
||||
binding.deliveryStatus.setImageResource(R.drawable.ic_check_24);
|
||||
} else {
|
||||
final boolean read = ResponseBodyUtils.isRead(item, thread.getLastSeenAt(), userIds, null);
|
||||
final boolean read = DMUtils.isRead(item,
|
||||
thread.getLastSeenAt(),
|
||||
userIds
|
||||
);
|
||||
binding.deliveryStatus.setImageResource(R.drawable.ic_check_all_24);
|
||||
ImageViewCompat.setImageTintList(
|
||||
binding.deliveryStatus,
|
||||
@ -321,8 +325,8 @@ public abstract class DirectItemViewHolder extends RecyclerView.ViewHolder imple
|
||||
final String repliedToUsername = user != null ? user.getUsername() : "";
|
||||
if (item.getUserId() == currentUser.getPk()) {
|
||||
return thread.isGroup()
|
||||
? resources.getString(R.string.replied_you_group, repliedToUsername)
|
||||
: resources.getString(R.string.replied_you);
|
||||
? resources.getString(R.string.replied_you_group, repliedToUsername)
|
||||
: resources.getString(R.string.replied_you);
|
||||
}
|
||||
if (repliedToUserId == currentUser.getPk()) {
|
||||
return resources.getString(R.string.replied_to_you);
|
||||
@ -361,7 +365,7 @@ public abstract class DirectItemViewHolder extends RecyclerView.ViewHolder imple
|
||||
final DirectItemReactions reactions = item.getReactions();
|
||||
final List<DirectItemEmojiReaction> emojis = reactions != null ? reactions.getEmojis() : null;
|
||||
if (emojis == null || emojis.isEmpty()) {
|
||||
binding.container.setPadding(messageInfoPaddingSmall, messageInfoPaddingSmall, messageInfoPaddingSmall, messageInfoPaddingSmall);
|
||||
binding.container.setPadding(messageInfoPaddingSmall, messageInfoPaddingSmall, messageInfoPaddingSmall, 0);
|
||||
binding.reactionsWrapper.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
@ -13,15 +12,12 @@ import java.util.Locale;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.CookieUtils;
|
||||
import awais.instagrabber.utils.NetworkUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
import awais.instagrabber.webservices.DirectMessagesService;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public class CreateThreadAction extends AsyncTask<Void, Void, Void> {
|
||||
private static final String TAG = "CommentAction";
|
||||
|
||||
|
@ -5,9 +5,6 @@ import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.interfaces.FetchListener;
|
||||
import awais.instagrabber.repositories.responses.FriendshipStatus;
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
|
@ -20,14 +20,16 @@ import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.db.dao.AccountDao;
|
||||
import awais.instagrabber.db.dao.DMLastNotifiedDao;
|
||||
import awais.instagrabber.db.dao.FavoriteDao;
|
||||
import awais.instagrabber.db.entities.Account;
|
||||
import awais.instagrabber.db.entities.DMLastNotified;
|
||||
import awais.instagrabber.db.entities.Favorite;
|
||||
import awais.instagrabber.models.enums.FavoriteType;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
|
||||
@Database(entities = {Account.class, Favorite.class},
|
||||
version = 4)
|
||||
@Database(entities = {Account.class, Favorite.class, DMLastNotified.class},
|
||||
version = 5)
|
||||
@TypeConverters({Converters.class})
|
||||
public abstract class AppDatabase extends RoomDatabase {
|
||||
private static final String TAG = AppDatabase.class.getSimpleName();
|
||||
@ -38,12 +40,14 @@ public abstract class AppDatabase extends RoomDatabase {
|
||||
|
||||
public abstract FavoriteDao favoriteDao();
|
||||
|
||||
public abstract DMLastNotifiedDao dmLastNotifiedDao();
|
||||
|
||||
public static AppDatabase getDatabase(final Context context) {
|
||||
if (INSTANCE == null) {
|
||||
synchronized (AppDatabase.class) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "cookiebox.db")
|
||||
.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4)
|
||||
.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@ -140,6 +144,18 @@ public abstract class AppDatabase extends RoomDatabase {
|
||||
}
|
||||
};
|
||||
|
||||
static final Migration MIGRATION_4_5 = new Migration(4, 5) {
|
||||
@Override
|
||||
public void migrate(@NonNull final SupportSQLiteDatabase database) {
|
||||
database.execSQL("CREATE TABLE IF NOT EXISTS `dm_last_notified` (" +
|
||||
"`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " +
|
||||
"`thread_id` TEXT, " +
|
||||
"`last_notified_msg_ts` INTEGER, " +
|
||||
"`last_notified_at` INTEGER)");
|
||||
database.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_dm_last_notified_thread_id` ON `dm_last_notified` (`thread_id`)");
|
||||
}
|
||||
};
|
||||
|
||||
@NonNull
|
||||
private static List<Favorite> backupOldFavorites(@NonNull final SupportSQLiteDatabase db) {
|
||||
// check if old favorites table had the column query_display
|
||||
|
@ -2,6 +2,10 @@ package awais.instagrabber.db;
|
||||
|
||||
import androidx.room.TypeConverter;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Date;
|
||||
|
||||
import awais.instagrabber.models.enums.FavoriteType;
|
||||
@ -30,4 +34,16 @@ public class Converters {
|
||||
public static String favoriteTypeToString(FavoriteType favoriteType) {
|
||||
return favoriteType == null ? null : favoriteType.toString();
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static LocalDateTime fromTimestampToLocalDateTime(Long value) {
|
||||
if (value == null) return null;
|
||||
return LocalDateTime.ofInstant(Instant.ofEpochMilli(value), ZoneOffset.systemDefault());
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static Long localDateTimeToTimestamp(LocalDateTime localDateTime) {
|
||||
if (localDateTime == null) return null;
|
||||
return localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
package awais.instagrabber.db.dao;
|
||||
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Delete;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
import androidx.room.Update;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.db.entities.DMLastNotified;
|
||||
|
||||
@Dao
|
||||
public interface DMLastNotifiedDao {
|
||||
|
||||
@Query("SELECT * FROM dm_last_notified")
|
||||
List<DMLastNotified> getAllDMDmLastNotified();
|
||||
|
||||
@Query("SELECT * FROM dm_last_notified WHERE thread_id = :threadId")
|
||||
DMLastNotified findDMLastNotifiedByThreadId(String threadId);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
List<Long> insertDMLastNotified(DMLastNotified... dmLastNotified);
|
||||
|
||||
@Update
|
||||
void updateDMLastNotified(DMLastNotified... dmLastNotified);
|
||||
|
||||
@Delete
|
||||
void deleteDMLastNotified(DMLastNotified... dmLastNotified);
|
||||
|
||||
@Query("DELETE from dm_last_notified")
|
||||
void deleteAllDMLastNotified();
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
package awais.instagrabber.db.datasources;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.db.AppDatabase;
|
||||
import awais.instagrabber.db.dao.DMLastNotifiedDao;
|
||||
import awais.instagrabber.db.entities.DMLastNotified;
|
||||
|
||||
public class DMLastNotifiedDataSource {
|
||||
private static final String TAG = DMLastNotifiedDataSource.class.getSimpleName();
|
||||
|
||||
private static DMLastNotifiedDataSource INSTANCE;
|
||||
|
||||
private final DMLastNotifiedDao dmLastNotifiedDao;
|
||||
|
||||
private DMLastNotifiedDataSource(final DMLastNotifiedDao dmLastNotifiedDao) {
|
||||
this.dmLastNotifiedDao = dmLastNotifiedDao;
|
||||
}
|
||||
|
||||
public static DMLastNotifiedDataSource getInstance(@NonNull Context context) {
|
||||
if (INSTANCE == null) {
|
||||
synchronized (DMLastNotifiedDataSource.class) {
|
||||
if (INSTANCE == null) {
|
||||
final AppDatabase database = AppDatabase.getDatabase(context);
|
||||
INSTANCE = new DMLastNotifiedDataSource(database.dmLastNotifiedDao());
|
||||
}
|
||||
}
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public final DMLastNotified getDMLastNotified(final String threadId) {
|
||||
return dmLastNotifiedDao.findDMLastNotifiedByThreadId(threadId);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public final List<DMLastNotified> getAllDMDmLastNotified() {
|
||||
return dmLastNotifiedDao.getAllDMDmLastNotified();
|
||||
}
|
||||
|
||||
public final void insertOrUpdateDMLastNotified(final String threadId,
|
||||
final LocalDateTime lastNotifiedMsgTs,
|
||||
final LocalDateTime lastNotifiedAt) {
|
||||
final DMLastNotified dmLastNotified = getDMLastNotified(threadId);
|
||||
final DMLastNotified toUpdate = new DMLastNotified(dmLastNotified == null ? 0 : dmLastNotified.getId(),
|
||||
threadId,
|
||||
lastNotifiedMsgTs,
|
||||
lastNotifiedAt);
|
||||
if (dmLastNotified != null) {
|
||||
dmLastNotifiedDao.updateDMLastNotified(toUpdate);
|
||||
return;
|
||||
}
|
||||
dmLastNotifiedDao.insertDMLastNotified(toUpdate);
|
||||
}
|
||||
|
||||
public final void deleteDMLastNotified(@NonNull final DMLastNotified dmLastNotified) {
|
||||
dmLastNotifiedDao.deleteDMLastNotified(dmLastNotified);
|
||||
}
|
||||
|
||||
public final void deleteAllDMLastNotified() {
|
||||
dmLastNotifiedDao.deleteAllDMLastNotified();
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
package awais.instagrabber.db.entities;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Index;
|
||||
import androidx.room.PrimaryKey;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
@Entity(tableName = DMLastNotified.TABLE_NAME, indices = {@Index(value = DMLastNotified.COL_THREAD_ID, unique = true)})
|
||||
public class DMLastNotified {
|
||||
public final static String TABLE_NAME = "dm_last_notified";
|
||||
public final static String COL_ID = "id";
|
||||
public final static String COL_THREAD_ID = "thread_id";
|
||||
public final static String COL_LAST_NOTIFIED_MSG_TS = "last_notified_msg_ts";
|
||||
public final static String COL_LAST_NOTIFIED_AT = "last_notified_at";
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
@ColumnInfo(name = COL_ID)
|
||||
private final int id;
|
||||
|
||||
@ColumnInfo(name = COL_THREAD_ID)
|
||||
private final String threadId;
|
||||
|
||||
@ColumnInfo(name = COL_LAST_NOTIFIED_MSG_TS)
|
||||
private final LocalDateTime lastNotifiedMsgTs;
|
||||
|
||||
@ColumnInfo(name = COL_LAST_NOTIFIED_AT)
|
||||
private final LocalDateTime lastNotifiedAt;
|
||||
|
||||
public DMLastNotified(final int id,
|
||||
final String threadId,
|
||||
final LocalDateTime lastNotifiedMsgTs,
|
||||
final LocalDateTime lastNotifiedAt) {
|
||||
this.id = id;
|
||||
this.threadId = threadId;
|
||||
this.lastNotifiedMsgTs = lastNotifiedMsgTs;
|
||||
this.lastNotifiedAt = lastNotifiedAt;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getThreadId() {
|
||||
return threadId;
|
||||
}
|
||||
|
||||
public LocalDateTime getLastNotifiedMsgTs() {
|
||||
return lastNotifiedMsgTs;
|
||||
}
|
||||
|
||||
public LocalDateTime getLastNotifiedAt() {
|
||||
return lastNotifiedAt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final DMLastNotified that = (DMLastNotified) o;
|
||||
return id == that.id &&
|
||||
Objects.equals(threadId, that.threadId) &&
|
||||
Objects.equals(lastNotifiedMsgTs, that.lastNotifiedMsgTs) &&
|
||||
Objects.equals(lastNotifiedAt, that.lastNotifiedAt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, threadId, lastNotifiedMsgTs, lastNotifiedAt);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DMLastNotified{" +
|
||||
"id=" + id +
|
||||
", threadId='" + threadId + '\'' +
|
||||
", lastNotifiedMsgTs='" + lastNotifiedMsgTs + '\'' +
|
||||
", lastNotifiedAt='" + lastNotifiedAt + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
package awais.instagrabber.db.repositories;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.db.datasources.DMLastNotifiedDataSource;
|
||||
import awais.instagrabber.db.entities.DMLastNotified;
|
||||
import awais.instagrabber.utils.AppExecutors;
|
||||
|
||||
public class DMLastNotifiedRepository {
|
||||
private static final String TAG = DMLastNotifiedRepository.class.getSimpleName();
|
||||
|
||||
private static DMLastNotifiedRepository instance;
|
||||
|
||||
private final AppExecutors appExecutors;
|
||||
private final DMLastNotifiedDataSource dmLastNotifiedDataSource;
|
||||
|
||||
private DMLastNotifiedRepository(final AppExecutors appExecutors, final DMLastNotifiedDataSource dmLastNotifiedDataSource) {
|
||||
this.appExecutors = appExecutors;
|
||||
this.dmLastNotifiedDataSource = dmLastNotifiedDataSource;
|
||||
}
|
||||
|
||||
public static DMLastNotifiedRepository getInstance(final DMLastNotifiedDataSource dmLastNotifiedDataSource) {
|
||||
if (instance == null) {
|
||||
instance = new DMLastNotifiedRepository(AppExecutors.getInstance(), dmLastNotifiedDataSource);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void getDMLastNotified(final String threadId,
|
||||
final RepositoryCallback<DMLastNotified> callback) {
|
||||
// request on the I/O thread
|
||||
appExecutors.diskIO().execute(() -> {
|
||||
final DMLastNotified dmLastNotified = dmLastNotifiedDataSource.getDMLastNotified(threadId);
|
||||
// notify on the main thread
|
||||
appExecutors.mainThread().execute(() -> {
|
||||
if (callback == null) return;
|
||||
if (dmLastNotified == null) {
|
||||
callback.onDataNotAvailable();
|
||||
return;
|
||||
}
|
||||
callback.onSuccess(dmLastNotified);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public void getAllDMDmLastNotified(final RepositoryCallback<List<DMLastNotified>> callback) {
|
||||
// request on the I/O thread
|
||||
appExecutors.diskIO().execute(() -> {
|
||||
final List<DMLastNotified> allDMDmLastNotified = dmLastNotifiedDataSource.getAllDMDmLastNotified();
|
||||
// notify on the main thread
|
||||
appExecutors.mainThread().execute(() -> {
|
||||
if (callback == null) return;
|
||||
if (allDMDmLastNotified == null) {
|
||||
callback.onDataNotAvailable();
|
||||
return;
|
||||
}
|
||||
// cachedAccounts = accounts;
|
||||
callback.onSuccess(allDMDmLastNotified);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public void insertOrUpdateDMLastNotified(final List<DMLastNotified> dmLastNotifiedList,
|
||||
final RepositoryCallback<Void> callback) {
|
||||
// request on the I/O thread
|
||||
appExecutors.diskIO().execute(() -> {
|
||||
for (final DMLastNotified dmLastNotified : dmLastNotifiedList) {
|
||||
dmLastNotifiedDataSource.insertOrUpdateDMLastNotified(dmLastNotified.getThreadId(),
|
||||
dmLastNotified.getLastNotifiedMsgTs(),
|
||||
dmLastNotified.getLastNotifiedAt());
|
||||
}
|
||||
// notify on the main thread
|
||||
appExecutors.mainThread().execute(() -> {
|
||||
if (callback == null) return;
|
||||
callback.onSuccess(null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public void insertOrUpdateDMLastNotified(final String threadId,
|
||||
final LocalDateTime lastNotifiedMsgTs,
|
||||
final LocalDateTime lastNotifiedAt,
|
||||
final RepositoryCallback<DMLastNotified> callback) {
|
||||
// request on the I/O thread
|
||||
appExecutors.diskIO().execute(() -> {
|
||||
dmLastNotifiedDataSource.insertOrUpdateDMLastNotified(threadId, lastNotifiedMsgTs, lastNotifiedAt);
|
||||
final DMLastNotified updated = dmLastNotifiedDataSource.getDMLastNotified(threadId);
|
||||
// notify on the main thread
|
||||
appExecutors.mainThread().execute(() -> {
|
||||
if (callback == null) return;
|
||||
if (updated == null) {
|
||||
callback.onDataNotAvailable();
|
||||
return;
|
||||
}
|
||||
callback.onSuccess(updated);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public void deleteDMLastNotified(final DMLastNotified dmLastNotified,
|
||||
final RepositoryCallback<Void> callback) {
|
||||
// request on the I/O thread
|
||||
appExecutors.diskIO().execute(() -> {
|
||||
dmLastNotifiedDataSource.deleteDMLastNotified(dmLastNotified);
|
||||
// notify on the main thread
|
||||
appExecutors.mainThread().execute(() -> {
|
||||
if (callback == null) return;
|
||||
callback.onSuccess(null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public void deleteAllDMLastNotified(final RepositoryCallback<Void> callback) {
|
||||
// request on the I/O thread
|
||||
appExecutors.diskIO().execute(() -> {
|
||||
dmLastNotifiedDataSource.deleteAllDMLastNotified();
|
||||
// notify on the main thread
|
||||
appExecutors.mainThread().execute(() -> {
|
||||
if (callback == null) return;
|
||||
callback.onSuccess(null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
package awais.instagrabber.dialogs;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
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.DialogFragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.adapters.GifItemsAdapter;
|
||||
import awais.instagrabber.customviews.helpers.TextWatcherAdapter;
|
||||
import awais.instagrabber.databinding.LayoutGifPickerBinding;
|
||||
import awais.instagrabber.repositories.responses.giphy.GiphyGif;
|
||||
import awais.instagrabber.utils.Debouncer;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.viewmodels.GifPickerViewModel;
|
||||
|
||||
public class GifPickerBottomDialogFragment extends BottomSheetDialogFragment {
|
||||
private static final String TAG = GifPickerBottomDialogFragment.class.getSimpleName();
|
||||
private static final int INPUT_DEBOUNCE_INTERVAL = 500;
|
||||
private static final String INPUT_KEY = "gif_search_input";
|
||||
|
||||
private LayoutGifPickerBinding binding;
|
||||
private GifPickerViewModel viewModel;
|
||||
private GifItemsAdapter gifItemsAdapter;
|
||||
private OnSelectListener onSelectListener;
|
||||
private Debouncer<String> inputDebouncer;
|
||||
|
||||
public static GifPickerBottomDialogFragment newInstance() {
|
||||
final Bundle args = new Bundle();
|
||||
final GifPickerBottomDialogFragment fragment = new GifPickerBottomDialogFragment();
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setStyle(DialogFragment.STYLE_NORMAL, R.style.ThemeOverlay_Rounded_BottomSheetDialog);
|
||||
final Debouncer.Callback<String> callback = new Debouncer.Callback<String>() {
|
||||
@Override
|
||||
public void call(final String key) {
|
||||
final Editable text = binding.input.getText();
|
||||
if (TextUtils.isEmpty(text)) {
|
||||
viewModel.search(null);
|
||||
return;
|
||||
}
|
||||
viewModel.search(text.toString().trim());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final Throwable t) {
|
||||
Log.e(TAG, "onError: ", t);
|
||||
}
|
||||
};
|
||||
inputDebouncer = new Debouncer<>(callback, INPUT_DEBOUNCE_INTERVAL);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {
|
||||
binding = LayoutGifPickerBinding.inflate(inflater, container, false);
|
||||
viewModel = new ViewModelProvider(this).get(GifPickerViewModel.class);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
final Dialog dialog = getDialog();
|
||||
if (dialog == null) return;
|
||||
final BottomSheetDialog bottomSheetDialog = (BottomSheetDialog) dialog;
|
||||
final View bottomSheetInternal = bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet);
|
||||
if (bottomSheetInternal == null) return;
|
||||
bottomSheetInternal.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
bottomSheetInternal.requestLayout();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
setupList();
|
||||
setupInput();
|
||||
setupObservers();
|
||||
}
|
||||
|
||||
private void setupList() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
binding.gifList.setLayoutManager(new GridLayoutManager(context, 3));
|
||||
binding.gifList.setHasFixedSize(true);
|
||||
gifItemsAdapter = new GifItemsAdapter(entry -> {
|
||||
if (onSelectListener == null) return;
|
||||
onSelectListener.onSelect(entry);
|
||||
});
|
||||
binding.gifList.setAdapter(gifItemsAdapter);
|
||||
}
|
||||
|
||||
private void setupInput() {
|
||||
binding.input.addTextChangedListener(new TextWatcherAdapter() {
|
||||
@Override
|
||||
public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
|
||||
inputDebouncer.call(INPUT_KEY);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupObservers() {
|
||||
viewModel.getImages().observe(getViewLifecycleOwner(), imagesResource -> {
|
||||
if (imagesResource == null) return;
|
||||
switch (imagesResource.status) {
|
||||
case SUCCESS:
|
||||
gifItemsAdapter.submitList(imagesResource.data);
|
||||
break;
|
||||
case ERROR:
|
||||
final Context context = getContext();
|
||||
if (context != null && imagesResource.message != null) {
|
||||
Snackbar.make(context, binding.getRoot(), imagesResource.message, Snackbar.LENGTH_LONG);
|
||||
}
|
||||
break;
|
||||
case LOADING:
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setOnSelectListener(final OnSelectListener onSelectListener) {
|
||||
this.onSelectListener = onSelectListener;
|
||||
}
|
||||
|
||||
public interface OnSelectListener {
|
||||
void onSelect(GiphyGif giphyGif);
|
||||
}
|
||||
}
|
@ -41,20 +41,26 @@ import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
public class ProfilePicDialogFragment extends DialogFragment {
|
||||
private static final String TAG = "ProfilePicDlgFragment";
|
||||
|
||||
private final long id;
|
||||
private final String name;
|
||||
private final String fallbackUrl;
|
||||
private long id;
|
||||
private String name;
|
||||
private String fallbackUrl;
|
||||
|
||||
private boolean isLoggedIn;
|
||||
private DialogProfilepicBinding binding;
|
||||
private String url;
|
||||
|
||||
public ProfilePicDialogFragment(final long id, final String name, final String fallbackUrl) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.fallbackUrl = fallbackUrl;
|
||||
public static ProfilePicDialogFragment getInstance(final long id, final String name, final String fallbackUrl) {
|
||||
final Bundle args = new Bundle();
|
||||
args.putLong("id", id);
|
||||
args.putString("name", name);
|
||||
args.putString("fallbackUrl", fallbackUrl);
|
||||
final ProfilePicDialogFragment fragment = new ProfilePicDialogFragment();
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
public ProfilePicDialogFragment() {}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater,
|
||||
final ViewGroup container,
|
||||
@ -94,6 +100,14 @@ public class ProfilePicDialogFragment extends DialogFragment {
|
||||
}
|
||||
|
||||
private void init() {
|
||||
final Bundle arguments = getArguments();
|
||||
if (arguments == null) {
|
||||
dismiss();
|
||||
return;
|
||||
}
|
||||
id = arguments.getLong("id");
|
||||
name = arguments.getString("name");
|
||||
fallbackUrl = arguments.getString("fallbackUrl");
|
||||
binding.download.setOnClickListener(v -> {
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
@ -127,11 +141,12 @@ public class ProfilePicDialogFragment extends DialogFragment {
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
final Context context = getContext();
|
||||
try {
|
||||
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
if (context == null) {
|
||||
dismiss();
|
||||
return;
|
||||
}
|
||||
catch(final Throwable e) {}
|
||||
getDialog().dismiss();
|
||||
Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
} else setupPhoto(fallbackUrl);
|
||||
|
@ -416,6 +416,8 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
||||
// binding.locationBiography.setCaptionIsExpandable(true);
|
||||
// binding.locationBiography.setCaptionIsExpanded(true);
|
||||
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
if (TextUtils.isEmpty(biography)) {
|
||||
locationDetailsBinding.locationBiography.setVisibility(View.GONE);
|
||||
} else {
|
||||
@ -432,13 +434,13 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
||||
final String originalText = autoLinkItem.getOriginalText().trim();
|
||||
navigateToProfile(originalText);
|
||||
});
|
||||
locationDetailsBinding.locationBiography.addOnEmailClickListener(autoLinkItem -> Utils.openEmailAddress(getContext(),
|
||||
locationDetailsBinding.locationBiography.addOnEmailClickListener(autoLinkItem -> Utils.openEmailAddress(context,
|
||||
autoLinkItem.getOriginalText()
|
||||
.trim()));
|
||||
locationDetailsBinding.locationBiography
|
||||
.addOnURLClickListener(autoLinkItem -> Utils.openURL(getContext(), autoLinkItem.getOriginalText().trim()));
|
||||
.addOnURLClickListener(autoLinkItem -> Utils.openURL(context, autoLinkItem.getOriginalText().trim()));
|
||||
locationDetailsBinding.locationBiography.setOnLongClickListener(v -> {
|
||||
Utils.copyText(getContext(), biography);
|
||||
Utils.copyText(context, biography);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
@ -465,7 +467,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
||||
locationDetailsBinding.locationUrl.setVisibility(View.VISIBLE);
|
||||
locationDetailsBinding.locationUrl.setText(TextUtils.getSpannableUrl(url));
|
||||
}
|
||||
final FavoriteDataSource dataSource = FavoriteDataSource.getInstance(getContext());
|
||||
final FavoriteDataSource dataSource = FavoriteDataSource.getInstance(context);
|
||||
final FavoriteRepository favoriteRepository = FavoriteRepository.getInstance(dataSource);
|
||||
locationDetailsBinding.favChip.setVisibility(View.VISIBLE);
|
||||
favoriteRepository.getFavorite(String.valueOf(locationId), FavoriteType.LOCATION, new RepositoryCallback<Favorite>() {
|
||||
|
@ -29,6 +29,7 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
import com.google.android.material.badge.BadgeDrawable;
|
||||
import com.google.android.material.badge.BadgeUtils;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -172,6 +173,7 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
|
||||
}
|
||||
|
||||
private void setupObservers() {
|
||||
removeViewModelObservers();
|
||||
threadsObserver = list -> {
|
||||
if (inboxAdapter == null) return;
|
||||
inboxAdapter.submitList(list, () -> {
|
||||
@ -181,8 +183,28 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
|
||||
});
|
||||
};
|
||||
viewModel.getThreads().observe(fragmentActivity, threadsObserver);
|
||||
viewModel.getFetchingInbox().observe(getViewLifecycleOwner(), fetching -> binding.swipeRefreshLayout.setRefreshing(fetching));
|
||||
viewModel.getUnseenCount().observe(getViewLifecycleOwner(), this::setBottomNavBarBadge);
|
||||
viewModel.getInbox().observe(getViewLifecycleOwner(), inboxResource -> {
|
||||
if (inboxResource == null) return;
|
||||
switch (inboxResource.status) {
|
||||
case SUCCESS:
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
break;
|
||||
case ERROR:
|
||||
if (inboxResource.message != null) {
|
||||
Snackbar.make(binding.getRoot(), inboxResource.message, Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
break;
|
||||
case LOADING:
|
||||
binding.swipeRefreshLayout.setRefreshing(true);
|
||||
break;
|
||||
}
|
||||
});
|
||||
viewModel.getUnseenCount().observe(getViewLifecycleOwner(), unseenCountResource -> {
|
||||
if (unseenCountResource == null) return;
|
||||
final Integer unseenCount = unseenCountResource.data;
|
||||
setBottomNavBarBadge(unseenCount == null ? 0 : unseenCount);
|
||||
});
|
||||
viewModel.getPendingRequestsTotal().observe(getViewLifecycleOwner(), this::attachPendingRequestsBadge);
|
||||
}
|
||||
|
||||
@ -230,11 +252,10 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
|
||||
inboxAdapter = new DirectMessageInboxAdapter(thread -> {
|
||||
if (navigating) return;
|
||||
navigating = true;
|
||||
final Bundle bundle = new Bundle();
|
||||
bundle.putString("threadId", thread.getThreadId());
|
||||
bundle.putString("title", thread.getThreadTitle());
|
||||
if (isAdded()) {
|
||||
NavHostFragment.findNavController(this).navigate(R.id.action_inbox_to_thread, bundle);
|
||||
final DirectMessageInboxFragmentDirections.ActionInboxToThread directions = DirectMessageInboxFragmentDirections
|
||||
.actionInboxToThread(thread.getThreadId(), thread.getThreadTitle());
|
||||
NavHostFragment.findNavController(this).navigate(directions);
|
||||
}
|
||||
navigating = false;
|
||||
});
|
||||
|
@ -11,13 +11,11 @@ import android.widget.CompoundButton;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.util.Pair;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.lifecycle.ViewModelStoreOwner;
|
||||
import androidx.navigation.NavBackStackEntry;
|
||||
import androidx.navigation.NavController;
|
||||
import androidx.navigation.NavDestination;
|
||||
@ -31,13 +29,13 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import awais.instagrabber.ProfileNavGraphDirections;
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.UserSearchNavGraphDirections;
|
||||
import awais.instagrabber.activities.MainActivity;
|
||||
import awais.instagrabber.adapters.DirectPendingUsersAdapter;
|
||||
import awais.instagrabber.adapters.DirectPendingUsersAdapter.PendingUser;
|
||||
import awais.instagrabber.adapters.DirectPendingUsersAdapter.PendingUserCallback;
|
||||
@ -52,12 +50,11 @@ import awais.instagrabber.fragments.UserSearchFragment;
|
||||
import awais.instagrabber.fragments.UserSearchFragmentDirections;
|
||||
import awais.instagrabber.models.Resource;
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse;
|
||||
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
|
||||
import awais.instagrabber.viewmodels.DirectInboxViewModel;
|
||||
import awais.instagrabber.viewmodels.DirectPendingInboxViewModel;
|
||||
import awais.instagrabber.viewmodels.AppStateViewModel;
|
||||
import awais.instagrabber.viewmodels.DirectSettingsViewModel;
|
||||
import awais.instagrabber.viewmodels.factories.DirectSettingsViewModelFactory;
|
||||
|
||||
public class DirectMessageSettingsFragment extends Fragment implements ConfirmDialogFragmentCallback {
|
||||
private static final String TAG = DirectMessageSettingsFragment.class.getSimpleName();
|
||||
@ -77,33 +74,14 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi
|
||||
super.onCreate(savedInstanceState);
|
||||
final Bundle arguments = getArguments();
|
||||
if (arguments == null) return;
|
||||
final NavController navController = NavHostFragment.findNavController(this);
|
||||
final ViewModelStoreOwner viewModelStoreOwner = navController.getViewModelStoreOwner(R.id.direct_messages_nav_graph);
|
||||
final DirectMessageSettingsFragmentArgs args = DirectMessageSettingsFragmentArgs.fromBundle(arguments);
|
||||
final boolean pending = args.getPending();
|
||||
final List<DirectThread> threads;
|
||||
final User viewer;
|
||||
if (pending) {
|
||||
final DirectPendingInboxViewModel inboxViewModel = new ViewModelProvider(viewModelStoreOwner).get(DirectPendingInboxViewModel.class);
|
||||
threads = inboxViewModel.getThreads().getValue();
|
||||
viewer = inboxViewModel.getViewer();
|
||||
} else {
|
||||
final DirectInboxViewModel inboxViewModel = new ViewModelProvider(viewModelStoreOwner).get(DirectInboxViewModel.class);
|
||||
threads = inboxViewModel.getThreads().getValue();
|
||||
viewer = inboxViewModel.getViewer();
|
||||
}
|
||||
final String threadId = args.getThreadId();
|
||||
final Optional<DirectThread> first = threads != null ? threads.stream()
|
||||
.filter(thread -> thread.getThreadId().equals(threadId))
|
||||
.findFirst()
|
||||
: Optional.empty();
|
||||
if (!first.isPresent()) {
|
||||
navController.navigateUp();
|
||||
return;
|
||||
}
|
||||
viewModel = new ViewModelProvider(this).get(DirectSettingsViewModel.class);
|
||||
viewModel.setViewer(viewer);
|
||||
viewModel.setThread(first.get());
|
||||
final MainActivity fragmentActivity = (MainActivity) requireActivity();
|
||||
final AppStateViewModel appStateViewModel = new ViewModelProvider(fragmentActivity).get(AppStateViewModel.class);
|
||||
viewModel = new ViewModelProvider(this, new DirectSettingsViewModelFactory(fragmentActivity.getApplication(),
|
||||
args.getThreadId(),
|
||||
args.getPending(),
|
||||
appStateViewModel.getCurrentUser()))
|
||||
.get(DirectSettingsViewModel.class);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@ -143,21 +121,23 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi
|
||||
binding.muteMessages.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
viewModel.getUsers().observe(getViewLifecycleOwner(), users -> {
|
||||
// Need to observe, so that getValue is correct
|
||||
viewModel.getUsers().observe(getViewLifecycleOwner(), users -> {});
|
||||
viewModel.getLeftUsers().observe(getViewLifecycleOwner(), users -> {});
|
||||
viewModel.getUsersAndLeftUsers().observe(getViewLifecycleOwner(), usersPair -> {
|
||||
if (usersAdapter == null) return;
|
||||
usersAdapter.submitUsers(users.first, users.second);
|
||||
usersAdapter.submitUsers(usersPair.first, usersPair.second);
|
||||
});
|
||||
viewModel.getTitle().observe(getViewLifecycleOwner(), title -> binding.titleEdit.setText(title));
|
||||
viewModel.getAdminUserIds().observe(getViewLifecycleOwner(), adminUserIds -> {
|
||||
if (usersAdapter == null) return;
|
||||
usersAdapter.setAdminUserIds(adminUserIds);
|
||||
});
|
||||
viewModel.getMuted().observe(getViewLifecycleOwner(), muted -> binding.muteMessages.setChecked(muted));
|
||||
viewModel.isMuted().observe(getViewLifecycleOwner(), muted -> binding.muteMessages.setChecked(muted));
|
||||
viewModel.isPending().observe(getViewLifecycleOwner(), pending -> binding.muteMessages.setVisibility(pending ? View.GONE : View.VISIBLE));
|
||||
if (viewModel.isViewerAdmin()) {
|
||||
viewModel.getApprovalRequiredToJoin().observe(getViewLifecycleOwner(), required -> binding.approvalRequired.setChecked(required));
|
||||
viewModel.getPendingRequests().observe(getViewLifecycleOwner(), this::setPendingRequests);
|
||||
}
|
||||
viewModel.isViewerAdmin().observe(getViewLifecycleOwner(), this::setApprovalRelatedUI);
|
||||
viewModel.getApprovalRequiredToJoin().observe(getViewLifecycleOwner(), required -> binding.approvalRequired.setChecked(required));
|
||||
viewModel.getPendingRequests().observe(getViewLifecycleOwner(), this::setPendingRequests);
|
||||
final NavController navController = NavHostFragment.findNavController(this);
|
||||
final NavBackStackEntry backStackEntry = navController.getCurrentBackStackEntry();
|
||||
if (backStackEntry != null) {
|
||||
@ -192,7 +172,11 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi
|
||||
|
||||
private void addMembers(final Set<User> users) {
|
||||
final Boolean approvalRequired = viewModel.getApprovalRequiredToJoin().getValue();
|
||||
if (!viewModel.isViewerAdmin() && approvalRequired != null && approvalRequired) {
|
||||
Boolean isViewerAdmin = viewModel.isViewerAdmin().getValue();
|
||||
if (isViewerAdmin == null) {
|
||||
isViewerAdmin = false;
|
||||
}
|
||||
if (!isViewerAdmin && approvalRequired != null && approvalRequired) {
|
||||
approvalRequiredUsers = users;
|
||||
final ConfirmDialogFragment confirmDialogFragment = ConfirmDialogFragment.newInstance(
|
||||
APPROVAL_REQUIRED_REQUEST_CODE,
|
||||
@ -226,13 +210,15 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi
|
||||
}
|
||||
|
||||
private void setupSettings() {
|
||||
binding.groupSettings.setVisibility(viewModel.isGroup() ? View.VISIBLE : View.GONE);
|
||||
Boolean isGroup = viewModel.isGroup().getValue();
|
||||
if (isGroup == null) isGroup = false;
|
||||
binding.groupSettings.setVisibility(isGroup ? View.VISIBLE : View.GONE);
|
||||
binding.muteMessagesLabel.setOnClickListener(v -> binding.muteMessages.toggle());
|
||||
binding.muteMessages.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||
final LiveData<Resource<Object>> resourceLiveData = isChecked ? viewModel.mute() : viewModel.unmute();
|
||||
handleSwitchChangeResource(resourceLiveData, buttonView);
|
||||
});
|
||||
if (!viewModel.isGroup()) return;
|
||||
if (!isGroup) return;
|
||||
binding.titleEdit.addTextChangedListener(new TextWatcherAdapter() {
|
||||
@Override
|
||||
public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
|
||||
@ -256,14 +242,13 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi
|
||||
final NavDestination currentDestination = navController.getCurrentDestination();
|
||||
if (currentDestination == null) return;
|
||||
if (currentDestination.getId() != R.id.directMessagesSettingsFragment) return;
|
||||
final Pair<List<User>, List<User>> users = viewModel.getUsers().getValue();
|
||||
final List<User> users = viewModel.getUsers().getValue();
|
||||
final long[] currentUserIds;
|
||||
if (users != null && users.first != null) {
|
||||
final List<User> currentMembers = users.first;
|
||||
currentUserIds = currentMembers.stream()
|
||||
.mapToLong(User::getPk)
|
||||
.sorted()
|
||||
.toArray();
|
||||
if (users != null) {
|
||||
currentUserIds = users.stream()
|
||||
.mapToLong(User::getPk)
|
||||
.sorted()
|
||||
.toArray();
|
||||
} else {
|
||||
currentUserIds = new long[0];
|
||||
}
|
||||
@ -281,7 +266,6 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi
|
||||
final LiveData<Resource<Object>> resourceLiveData = isChecked ? viewModel.muteMentions() : viewModel.unmuteMentions();
|
||||
handleSwitchChangeResource(resourceLiveData, buttonView);
|
||||
});
|
||||
setApprovalRelatedUI();
|
||||
binding.leave.setOnClickListener(v -> {
|
||||
final ConfirmDialogFragment confirmDialogFragment = ConfirmDialogFragment.newInstance(
|
||||
LEAVE_THREAD_REQUEST_CODE,
|
||||
@ -293,7 +277,9 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi
|
||||
);
|
||||
confirmDialogFragment.show(getChildFragmentManager(), "leave_thread_confirmation_dialog");
|
||||
});
|
||||
if (viewModel.isViewerAdmin()) {
|
||||
Boolean isViewerAdmin = viewModel.isViewerAdmin().getValue();
|
||||
if (isViewerAdmin == null) isViewerAdmin = false;
|
||||
if (isViewerAdmin) {
|
||||
binding.end.setVisibility(View.VISIBLE);
|
||||
binding.end.setOnClickListener(v -> {
|
||||
final ConfirmDialogFragment confirmDialogFragment = ConfirmDialogFragment.newInstance(
|
||||
@ -311,8 +297,8 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi
|
||||
}
|
||||
}
|
||||
|
||||
private void setApprovalRelatedUI() {
|
||||
if (!viewModel.isViewerAdmin()) {
|
||||
private void setApprovalRelatedUI(final boolean isViewerAdmin) {
|
||||
if (!isViewerAdmin) {
|
||||
binding.pendingMembersGroup.setVisibility(View.GONE);
|
||||
binding.approvalRequired.setVisibility(View.GONE);
|
||||
binding.approvalRequiredLabel.setVisibility(View.GONE);
|
||||
@ -352,7 +338,7 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
binding.users.setLayoutManager(new LinearLayoutManager(context));
|
||||
final User inviter = viewModel.getThread().getInviter();
|
||||
final User inviter = viewModel.getInviter().getValue();
|
||||
usersAdapter = new DirectUsersAdapter(
|
||||
inviter != null ? inviter.getPk() : -1,
|
||||
(position, user, selected) -> {
|
||||
|
@ -30,14 +30,12 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MediatorLiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.lifecycle.ViewModelStoreOwner;
|
||||
import androidx.navigation.NavBackStackEntry;
|
||||
import androidx.navigation.NavController;
|
||||
import androidx.navigation.NavDirections;
|
||||
@ -58,7 +56,6 @@ import com.google.common.collect.ImmutableList;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import awais.instagrabber.ProfileNavGraphDirections;
|
||||
@ -83,6 +80,7 @@ import awais.instagrabber.customviews.helpers.SwipeAndRestoreItemTouchHelperCall
|
||||
import awais.instagrabber.customviews.helpers.TextWatcherAdapter;
|
||||
import awais.instagrabber.databinding.FragmentDirectMessagesThreadBinding;
|
||||
import awais.instagrabber.dialogs.DirectItemReactionDialogFragment;
|
||||
import awais.instagrabber.dialogs.GifPickerBottomDialogFragment;
|
||||
import awais.instagrabber.dialogs.MediaPickerBottomDialogFragment;
|
||||
import awais.instagrabber.fragments.PostViewV2Fragment;
|
||||
import awais.instagrabber.fragments.UserSearchFragment;
|
||||
@ -104,9 +102,8 @@ import awais.instagrabber.utils.ResponseBodyUtils;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
import awais.instagrabber.viewmodels.AppStateViewModel;
|
||||
import awais.instagrabber.viewmodels.DirectInboxViewModel;
|
||||
import awais.instagrabber.viewmodels.DirectPendingInboxViewModel;
|
||||
import awais.instagrabber.viewmodels.DirectThreadViewModel;
|
||||
import awais.instagrabber.viewmodels.factories.DirectThreadViewModelFactory;
|
||||
|
||||
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
|
||||
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN;
|
||||
@ -232,7 +229,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
@Override
|
||||
public void onReaction(final DirectItem item, final Emoji emoji) {
|
||||
if (item == null) return;
|
||||
final LiveData<Resource<DirectItem>> resourceLiveData = viewModel.sendReaction(item, emoji);
|
||||
final LiveData<Resource<Object>> resourceLiveData = viewModel.sendReaction(item, emoji);
|
||||
if (resourceLiveData != null) {
|
||||
resourceLiveData.observe(getViewLifecycleOwner(), directItemResource -> handleSentMessage(resourceLiveData));
|
||||
}
|
||||
@ -292,13 +289,29 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
};
|
||||
private final MutableLiveData<Integer> inputLength = new MutableLiveData<>(0);
|
||||
private ItemTouchHelper itemTouchHelper;
|
||||
private LiveData<Boolean> pendingLiveData;
|
||||
private LiveData<DirectThread> threadLiveData;
|
||||
private LiveData<Integer> inputModeLiveData;
|
||||
private LiveData<String> threadTitleLiveData;
|
||||
private LiveData<Resource<Object>> fetchingLiveData;
|
||||
private LiveData<List<DirectItem>> itemsLiveData;
|
||||
private LiveData<DirectItem> replyToItemLiveData;
|
||||
private LiveData<Integer> pendingRequestsCountLiveData;
|
||||
private LiveData<List<User>> usersLiveData;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
fragmentActivity = (MainActivity) requireActivity();
|
||||
appStateViewModel = new ViewModelProvider(fragmentActivity).get(AppStateViewModel.class);
|
||||
viewModel = new ViewModelProvider(this).get(DirectThreadViewModel.class);
|
||||
final Bundle arguments = getArguments();
|
||||
if (arguments == null) return;
|
||||
final DirectMessageThreadFragmentArgs fragmentArgs = DirectMessageThreadFragmentArgs.fromBundle(arguments);
|
||||
viewModel = new ViewModelProvider(this, new DirectThreadViewModelFactory(fragmentActivity.getApplication(),
|
||||
fragmentArgs.getThreadId(),
|
||||
fragmentArgs.getPending(),
|
||||
appStateViewModel.getCurrentUser()))
|
||||
.get(DirectThreadViewModel.class);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@ -329,7 +342,6 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
init();
|
||||
binding.send.post(() -> initialSendX = binding.send.getX());
|
||||
shouldRefresh = false;
|
||||
setObservers();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -403,6 +415,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
wasKbShowing = true;
|
||||
binding.emojiPicker.setAlpha(0);
|
||||
}
|
||||
removeObservers();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@ -420,7 +433,8 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
}
|
||||
binding.send.stopScale();
|
||||
setupBackStackResultObserver();
|
||||
attachPendingRequestsBadge(viewModel.getPendingRequestsCount().getValue());
|
||||
setObservers();
|
||||
// attachPendingRequestsBadge(viewModel.getPendingRequestsCount().getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -460,42 +474,38 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
if (context == null) return;
|
||||
if (getArguments() == null) return;
|
||||
actionBar = fragmentActivity.getSupportActionBar();
|
||||
final DirectMessageThreadFragmentArgs fragmentArgs = DirectMessageThreadFragmentArgs.fromBundle(getArguments());
|
||||
viewModel.getThreadTitle().postValue(fragmentArgs.getTitle());
|
||||
final String threadId = fragmentArgs.getThreadId();
|
||||
viewModel.setThreadId(threadId);
|
||||
setupList();
|
||||
root.post(this::setupInput);
|
||||
root.post(this::getInitialData);
|
||||
// root.post(this::getInitialData);
|
||||
}
|
||||
|
||||
private void getInitialData() {
|
||||
final Bundle arguments = getArguments();
|
||||
if (arguments == null) return;
|
||||
final DirectMessageThreadFragmentArgs args = DirectMessageThreadFragmentArgs.fromBundle(arguments);
|
||||
final boolean pending = args.getPending();
|
||||
final NavController navController = NavHostFragment.findNavController(this);
|
||||
final ViewModelStoreOwner viewModelStoreOwner = navController.getViewModelStoreOwner(R.id.direct_messages_nav_graph);
|
||||
final List<DirectThread> threads;
|
||||
if (!pending) {
|
||||
final DirectInboxViewModel threadListViewModel = new ViewModelProvider(viewModelStoreOwner).get(DirectInboxViewModel.class);
|
||||
threads = threadListViewModel.getThreads().getValue();
|
||||
} else {
|
||||
final DirectPendingInboxViewModel threadListViewModel = new ViewModelProvider(viewModelStoreOwner).get(DirectPendingInboxViewModel.class);
|
||||
threads = threadListViewModel.getThreads().getValue();
|
||||
}
|
||||
final Optional<DirectThread> first = threads != null
|
||||
? threads.stream()
|
||||
.filter(thread -> thread.getThreadId().equals(viewModel.getThreadId()))
|
||||
.findFirst()
|
||||
: Optional.empty();
|
||||
if (first.isPresent()) {
|
||||
final DirectThread thread = first.get();
|
||||
viewModel.setThread(thread);
|
||||
return;
|
||||
}
|
||||
viewModel.fetchChats();
|
||||
}
|
||||
// private void getInitialData() {
|
||||
// final Bundle arguments = getArguments();
|
||||
// if (arguments == null) return;
|
||||
// final DirectMessageThreadFragmentArgs args = DirectMessageThreadFragmentArgs.fromBundle(arguments);
|
||||
// final boolean pending = args.getPending();
|
||||
// final NavController navController = NavHostFragment.findNavController(this);
|
||||
// final ViewModelStoreOwner viewModelStoreOwner = navController.getViewModelStoreOwner(R.id.direct_messages_nav_graph);
|
||||
// final List<DirectThread> threads;
|
||||
// if (!pending) {
|
||||
// final DirectInboxViewModel threadListViewModel = new ViewModelProvider(viewModelStoreOwner).get(DirectInboxViewModel.class);
|
||||
// threads = threadListViewModel.getThreads().getValue();
|
||||
// } else {
|
||||
// final DirectPendingInboxViewModel threadListViewModel = new ViewModelProvider(viewModelStoreOwner).get(DirectPendingInboxViewModel.class);
|
||||
// threads = threadListViewModel.getThreads().getValue();
|
||||
// }
|
||||
// final Optional<DirectThread> first = threads != null
|
||||
// ? threads.stream()
|
||||
// .filter(thread -> thread.getThreadId().equals(viewModel.getThreadId()))
|
||||
// .findFirst()
|
||||
// : Optional.empty();
|
||||
// if (first.isPresent()) {
|
||||
// final DirectThread thread = first.get();
|
||||
// viewModel.setThread(thread);
|
||||
// return;
|
||||
// }
|
||||
// viewModel.fetchChats();
|
||||
// }
|
||||
|
||||
private void setupList() {
|
||||
final Context context = getContext();
|
||||
@ -539,7 +549,14 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
}
|
||||
|
||||
private void setObservers() {
|
||||
viewModel.isPending().observe(getViewLifecycleOwner(), isPending -> {
|
||||
threadLiveData = viewModel.getThread();
|
||||
if (threadLiveData == null) {
|
||||
final NavController navController = NavHostFragment.findNavController(this);
|
||||
navController.navigateUp();
|
||||
return;
|
||||
}
|
||||
pendingLiveData = viewModel.isPending();
|
||||
pendingLiveData.observe(getViewLifecycleOwner(), isPending -> {
|
||||
if (isPending == null) {
|
||||
hideInput();
|
||||
return;
|
||||
@ -553,7 +570,8 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
if (inputMode != null && inputMode == 1) return;
|
||||
showInput();
|
||||
});
|
||||
viewModel.getInputMode().observe(getViewLifecycleOwner(), inputMode -> {
|
||||
inputModeLiveData = viewModel.getInputMode();
|
||||
inputModeLiveData.observe(getViewLifecycleOwner(), inputMode -> {
|
||||
final Boolean isPending = viewModel.isPending().getValue();
|
||||
if (isPending != null && isPending) return;
|
||||
if (inputMode == null || inputMode == 0) return;
|
||||
@ -561,21 +579,34 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
hideInput();
|
||||
}
|
||||
});
|
||||
viewModel.getThreadTitle().observe(getViewLifecycleOwner(), this::setTitle);
|
||||
viewModel.getFetching().observe(getViewLifecycleOwner(), fetching -> {
|
||||
if (fetching) {
|
||||
setTitle(getString(R.string.dms_thread_updating));
|
||||
return;
|
||||
threadTitleLiveData = viewModel.getThreadTitle();
|
||||
threadTitleLiveData.observe(getViewLifecycleOwner(), this::setTitle);
|
||||
fetchingLiveData = viewModel.isFetching();
|
||||
fetchingLiveData.observe(getViewLifecycleOwner(), fetchingResource -> {
|
||||
if (fetchingResource == null) return;
|
||||
switch (fetchingResource.status) {
|
||||
case SUCCESS:
|
||||
case ERROR:
|
||||
setTitle(viewModel.getThreadTitle().getValue());
|
||||
if (fetchingResource.message != null) {
|
||||
Snackbar.make(binding.getRoot(), fetchingResource.message, Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
break;
|
||||
case LOADING:
|
||||
setTitle(getString(R.string.dms_thread_updating));
|
||||
break;
|
||||
}
|
||||
setTitle(viewModel.getThreadTitle().getValue());
|
||||
});
|
||||
final ItemsAdapterDataMerger itemsAdapterDataMerger = new ItemsAdapterDataMerger(appStateViewModel.getCurrentUser(), viewModel.getThread());
|
||||
itemsAdapterDataMerger.observe(getViewLifecycleOwner(), userThreadPair -> {
|
||||
viewModel.setCurrentUser(userThreadPair.first);
|
||||
setupItemsAdapter(userThreadPair.first, userThreadPair.second);
|
||||
});
|
||||
viewModel.getItems().observe(getViewLifecycleOwner(), this::submitItemsToAdapter);
|
||||
viewModel.getReplyToItem().observe(getViewLifecycleOwner(), item -> {
|
||||
// final ItemsAdapterDataMerger itemsAdapterDataMerger = new ItemsAdapterDataMerger(appStateViewModel.getCurrentUser(), viewModel.getThread());
|
||||
// itemsAdapterDataMerger.observe(getViewLifecycleOwner(), userThreadPair -> {
|
||||
// viewModel.setCurrentUser(userThreadPair.first);
|
||||
// setupItemsAdapter(userThreadPair.first, userThreadPair.second);
|
||||
// });
|
||||
threadLiveData.observe(getViewLifecycleOwner(), this::setupItemsAdapter);
|
||||
itemsLiveData = viewModel.getItems();
|
||||
itemsLiveData.observe(getViewLifecycleOwner(), this::submitItemsToAdapter);
|
||||
replyToItemLiveData = viewModel.getReplyToItem();
|
||||
replyToItemLiveData.observe(getViewLifecycleOwner(), item -> {
|
||||
if (item == null) {
|
||||
if (binding.input.length() == 0) {
|
||||
showExtraInputOption(true);
|
||||
@ -630,14 +661,30 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
}
|
||||
prevLength = length;
|
||||
});
|
||||
viewModel.getPendingRequestsCount().observe(getViewLifecycleOwner(), this::attachPendingRequestsBadge);
|
||||
viewModel.getUsers().observe(getViewLifecycleOwner(), users -> {
|
||||
pendingRequestsCountLiveData = viewModel.getPendingRequestsCount();
|
||||
pendingRequestsCountLiveData.observe(getViewLifecycleOwner(), this::attachPendingRequestsBadge);
|
||||
usersLiveData = viewModel.getUsers();
|
||||
usersLiveData.observe(getViewLifecycleOwner(), users -> {
|
||||
if (users == null || users.isEmpty()) return;
|
||||
final User user = users.get(0);
|
||||
binding.acceptPendingRequestQuestion.setText(getString(R.string.accept_request_from_user, user.getUsername(), user.getFullName()));
|
||||
});
|
||||
}
|
||||
|
||||
private void removeObservers() {
|
||||
pendingLiveData.removeObservers(getViewLifecycleOwner());
|
||||
inputModeLiveData.removeObservers(getViewLifecycleOwner());
|
||||
threadTitleLiveData.removeObservers(getViewLifecycleOwner());
|
||||
fetchingLiveData.removeObservers(getViewLifecycleOwner());
|
||||
threadLiveData.removeObservers(getViewLifecycleOwner());
|
||||
itemsLiveData.removeObservers(getViewLifecycleOwner());
|
||||
replyToItemLiveData.removeObservers(getViewLifecycleOwner());
|
||||
inputLength.removeObservers(getViewLifecycleOwner());
|
||||
pendingRequestsCountLiveData.removeObservers(getViewLifecycleOwner());
|
||||
usersLiveData.removeObservers(getViewLifecycleOwner());
|
||||
|
||||
}
|
||||
|
||||
private void hidePendingOptions() {
|
||||
binding.acceptPendingRequestQuestion.setVisibility(View.GONE);
|
||||
binding.decline.setVisibility(View.GONE);
|
||||
@ -666,9 +713,15 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
case SUCCESS:
|
||||
resourceLiveData.removeObservers(getViewLifecycleOwner());
|
||||
if (isDecline) {
|
||||
removeObservers();
|
||||
viewModel.removeThread();
|
||||
final NavController navController = NavHostFragment.findNavController(this);
|
||||
navController.navigateUp();
|
||||
return;
|
||||
}
|
||||
removeObservers();
|
||||
viewModel.moveFromPending();
|
||||
setObservers();
|
||||
break;
|
||||
case LOADING:
|
||||
break;
|
||||
@ -684,6 +737,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
|
||||
private void hideInput() {
|
||||
binding.emojiToggle.setVisibility(View.GONE);
|
||||
binding.gif.setVisibility(View.GONE);
|
||||
binding.camera.setVisibility(View.GONE);
|
||||
binding.gallery.setVisibility(View.GONE);
|
||||
binding.input.setVisibility(View.GONE);
|
||||
@ -697,6 +751,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
|
||||
private void showInput() {
|
||||
binding.emojiToggle.setVisibility(View.VISIBLE);
|
||||
binding.gif.setVisibility(View.VISIBLE);
|
||||
binding.camera.setVisibility(View.VISIBLE);
|
||||
binding.gallery.setVisibility(View.VISIBLE);
|
||||
binding.input.setVisibility(View.VISIBLE);
|
||||
@ -735,16 +790,18 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
binding.send.setListenForRecord(true);
|
||||
startIconAnimation();
|
||||
}
|
||||
binding.gallery.setVisibility(View.VISIBLE);
|
||||
binding.gif.setVisibility(View.VISIBLE);
|
||||
binding.camera.setVisibility(View.VISIBLE);
|
||||
binding.gallery.setVisibility(View.VISIBLE);
|
||||
return;
|
||||
}
|
||||
if (binding.send.isListenForRecord()) {
|
||||
binding.send.setListenForRecord(false);
|
||||
startIconAnimation();
|
||||
}
|
||||
binding.gallery.setVisibility(View.GONE);
|
||||
binding.gif.setVisibility(View.GONE);
|
||||
binding.camera.setVisibility(View.GONE);
|
||||
binding.gallery.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private String getDirectItemPreviewText(final DirectItem item) {
|
||||
@ -835,12 +892,15 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
});
|
||||
}
|
||||
|
||||
private void setupItemsAdapter(final User currentUser, final DirectThread thread) {
|
||||
private void setupItemsAdapter(final DirectThread thread) {
|
||||
if (thread == null) return;
|
||||
if (itemsAdapter != null) {
|
||||
if (itemsAdapter.getThread() == thread) return;
|
||||
itemsAdapter.setThread(thread);
|
||||
return;
|
||||
}
|
||||
final User currentUser = appStateViewModel.getCurrentUser();
|
||||
if (currentUser == null) return;
|
||||
itemsAdapter = new DirectItemsAdapter(currentUser, thread, directItemCallback, directItemLongClickListener);
|
||||
itemsAdapter.setHasStableIds(true);
|
||||
itemsAdapter.setStateRestorationPolicy(RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY);
|
||||
@ -881,8 +941,9 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
public void onStart() {
|
||||
isRecording = true;
|
||||
binding.input.setHint(null);
|
||||
binding.gallery.setVisibility(View.GONE);
|
||||
binding.gif.setVisibility(View.GONE);
|
||||
binding.camera.setVisibility(View.GONE);
|
||||
binding.gallery.setVisibility(View.GONE);
|
||||
if (PermissionUtils.hasAudioRecordPerms(context)) {
|
||||
viewModel.startRecording();
|
||||
return;
|
||||
@ -902,8 +963,9 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
public void onFinish(final long recordTime) {
|
||||
Log.d(TAG, "onFinish");
|
||||
binding.input.setHint("Message");
|
||||
binding.gallery.setVisibility(View.VISIBLE);
|
||||
binding.gif.setVisibility(View.VISIBLE);
|
||||
binding.camera.setVisibility(View.VISIBLE);
|
||||
binding.gallery.setVisibility(View.VISIBLE);
|
||||
viewModel.stopRecording(false);
|
||||
isRecording = false;
|
||||
}
|
||||
@ -915,16 +977,18 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
if (PermissionUtils.hasAudioRecordPerms(context)) {
|
||||
tooltip.show(binding.send);
|
||||
}
|
||||
binding.gallery.setVisibility(View.VISIBLE);
|
||||
binding.gif.setVisibility(View.VISIBLE);
|
||||
binding.camera.setVisibility(View.VISIBLE);
|
||||
binding.gallery.setVisibility(View.VISIBLE);
|
||||
viewModel.stopRecording(true);
|
||||
isRecording = false;
|
||||
}
|
||||
});
|
||||
binding.recordView.setOnBasketAnimationEndListener(() -> {
|
||||
binding.input.setHint(R.string.dms_thread_message_hint);
|
||||
binding.gallery.setVisibility(View.VISIBLE);
|
||||
binding.gif.setVisibility(View.VISIBLE);
|
||||
binding.camera.setVisibility(View.VISIBLE);
|
||||
binding.gallery.setVisibility(View.VISIBLE);
|
||||
});
|
||||
binding.input.addTextChangedListener(new TextWatcherAdapter() {
|
||||
// int prevLength = 0;
|
||||
@ -955,7 +1019,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
binding.send.setOnRecordClickListener(v -> {
|
||||
final Editable text = binding.input.getText();
|
||||
if (TextUtils.isEmpty(text)) return;
|
||||
final LiveData<Resource<DirectItem>> resourceLiveData = viewModel.sendText(text.toString());
|
||||
final LiveData<Resource<Object>> resourceLiveData = viewModel.sendText(text.toString());
|
||||
resourceLiveData.observe(getViewLifecycleOwner(), resource -> handleSentMessage(resourceLiveData));
|
||||
binding.input.setText("");
|
||||
viewModel.setReplyToItem(null);
|
||||
@ -1001,6 +1065,16 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
mediaPicker.show(getChildFragmentManager(), "MediaPicker");
|
||||
hideKeyboard(true);
|
||||
});
|
||||
binding.gif.setOnClickListener(v -> {
|
||||
final GifPickerBottomDialogFragment gifPicker = GifPickerBottomDialogFragment.newInstance();
|
||||
gifPicker.setOnSelectListener(giphyGif -> {
|
||||
gifPicker.dismiss();
|
||||
if (giphyGif == null) return;
|
||||
handleSentMessage(viewModel.sendAnimatedMedia(giphyGif));
|
||||
});
|
||||
gifPicker.show(getChildFragmentManager(), "GifPicker");
|
||||
hideKeyboard(true);
|
||||
});
|
||||
binding.camera.setOnClickListener(v -> {
|
||||
final Intent intent = new Intent(context, CameraActivity.class);
|
||||
startActivityForResult(intent, CAMERA_REQUEST_CODE);
|
||||
@ -1028,8 +1102,8 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
navController.navigate(navDirections);
|
||||
}
|
||||
|
||||
private void handleSentMessage(final LiveData<Resource<DirectItem>> resourceLiveData) {
|
||||
final Resource<DirectItem> resource = resourceLiveData.getValue();
|
||||
private void handleSentMessage(final LiveData<Resource<Object>> resourceLiveData) {
|
||||
final Resource<Object> resource = resourceLiveData.getValue();
|
||||
if (resource == null) return;
|
||||
final Resource.Status status = resource.status;
|
||||
switch (status) {
|
||||
@ -1129,7 +1203,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
private void setSendToMicIcon() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
final Drawable sendToMicDrawable = ContextCompat.getDrawable(context, R.drawable.avd_send_to_mic_anim);
|
||||
final Drawable sendToMicDrawable = Utils.getAnimatableDrawable(context, R.drawable.avd_send_to_mic_anim);
|
||||
if (sendToMicDrawable instanceof Animatable) {
|
||||
AnimatedVectorDrawableCompat.registerAnimationCallback(sendToMicDrawable, sendToMicAnimationCallback);
|
||||
}
|
||||
@ -1139,7 +1213,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
private void setMicToSendIcon() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
final Drawable micToSendDrawable = ContextCompat.getDrawable(context, R.drawable.avd_mic_to_send_anim);
|
||||
final Drawable micToSendDrawable = Utils.getAnimatableDrawable(context, R.drawable.avd_mic_to_send_anim);
|
||||
if (micToSendDrawable instanceof Animatable) {
|
||||
AnimatedVectorDrawableCompat.registerAnimationCallback(micToSendDrawable, micToSendAnimationCallback);
|
||||
}
|
||||
@ -1350,6 +1424,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
ObjectAnimator.ofFloat(binding.inputBg, TRANSLATION_Y, -height),
|
||||
ObjectAnimator.ofFloat(binding.recordView, TRANSLATION_Y, -height),
|
||||
ObjectAnimator.ofFloat(binding.emojiToggle, TRANSLATION_Y, -height),
|
||||
ObjectAnimator.ofFloat(binding.gif, TRANSLATION_Y, -height),
|
||||
ObjectAnimator.ofFloat(binding.gallery, TRANSLATION_Y, -height),
|
||||
ObjectAnimator.ofFloat(binding.camera, TRANSLATION_Y, -height),
|
||||
ObjectAnimator.ofFloat(binding.send, TRANSLATION_Y, -height),
|
||||
@ -1377,10 +1452,6 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
animatorSet.start();
|
||||
}
|
||||
|
||||
private void showLongClickOptions(final View itemView) {
|
||||
|
||||
}
|
||||
|
||||
private void showReactionsDialog(final DirectItem item) {
|
||||
final LiveData<List<User>> users = viewModel.getUsers();
|
||||
final LiveData<List<User>> leftUsers = viewModel.getLeftUsers();
|
||||
@ -1407,7 +1478,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
||||
}
|
||||
if (reaction == null) return;
|
||||
if (reaction.getSenderId() == viewModel.getViewerId()) {
|
||||
final LiveData<Resource<DirectItem>> resourceLiveData = viewModel.sendDeleteReaction(itemId);
|
||||
final LiveData<Resource<Object>> resourceLiveData = viewModel.sendDeleteReaction(itemId);
|
||||
if (resourceLiveData != null) {
|
||||
resourceLiveData.observe(getViewLifecycleOwner(), directItemResource -> handleSentMessage(resourceLiveData));
|
||||
}
|
||||
|
@ -19,6 +19,9 @@ import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
@ -102,16 +105,41 @@ public class DirectPendingInboxFragment extends Fragment implements SwipeRefresh
|
||||
}
|
||||
|
||||
private void setupObservers() {
|
||||
removeViewModelObservers();
|
||||
threadsObserver = list -> {
|
||||
if (inboxAdapter == null) return;
|
||||
inboxAdapter.submitList(list, () -> {
|
||||
if (binding.swipeRefreshLayout.getVisibility() == View.GONE) {
|
||||
binding.swipeRefreshLayout.setVisibility(View.VISIBLE);
|
||||
binding.empty.setVisibility(View.GONE);
|
||||
}
|
||||
inboxAdapter.submitList(list == null ? Collections.emptyList() : list, () -> {
|
||||
if (!scrollToTop) return;
|
||||
binding.pendingInboxList.smoothScrollToPosition(0);
|
||||
scrollToTop = false;
|
||||
});
|
||||
if (list == null || list.isEmpty()) {
|
||||
binding.swipeRefreshLayout.setVisibility(View.GONE);
|
||||
binding.empty.setVisibility(View.VISIBLE);
|
||||
}
|
||||
};
|
||||
viewModel.getThreads().observe(fragmentActivity, threadsObserver);
|
||||
viewModel.getFetchingInbox().observe(getViewLifecycleOwner(), fetching -> binding.swipeRefreshLayout.setRefreshing(fetching));
|
||||
viewModel.getInbox().observe(getViewLifecycleOwner(), inboxResource -> {
|
||||
if (inboxResource == null) return;
|
||||
switch (inboxResource.status) {
|
||||
case SUCCESS:
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
break;
|
||||
case ERROR:
|
||||
if (inboxResource.message != null) {
|
||||
Snackbar.make(binding.getRoot(), inboxResource.message, Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
break;
|
||||
case LOADING:
|
||||
binding.swipeRefreshLayout.setRefreshing(true);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void removeViewModelObservers() {
|
||||
|
@ -375,8 +375,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
if (profileModel != null) {
|
||||
restrictMenuItem.setVisible(!Objects.equals(profileModel.getPk(), CookieUtils.getUserIdFromCookie(cookie)));
|
||||
restrictMenuItem.setTitle(profileModel.getFriendshipStatus().isRestricted() ? R.string.unrestrict : R.string.restrict);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
restrictMenuItem.setVisible(false);
|
||||
}
|
||||
}
|
||||
@ -394,8 +393,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
if (profileModel != null) {
|
||||
mutePostsMenuItem.setVisible(!Objects.equals(profileModel.getPk(), CookieUtils.getUserIdFromCookie(cookie)));
|
||||
mutePostsMenuItem.setTitle(profileModel.getFriendshipStatus().isMuting() ? R.string.mute_posts : R.string.unmute_posts);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
mutePostsMenuItem.setVisible(false);
|
||||
}
|
||||
}
|
||||
@ -403,8 +401,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
if (chainingMenuItem != null) {
|
||||
if (profileModel != null) {
|
||||
chainingMenuItem.setVisible(!Objects.equals(profileModel.getPk(), CookieUtils.getUserIdFromCookie(cookie)));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
chainingMenuItem.setVisible(false);
|
||||
}
|
||||
}
|
||||
@ -524,7 +521,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
profileDetailsBinding.countsBarrier.setVisibility(View.GONE);
|
||||
profileDetailsBinding.countsBarrier.getRoot().setVisibility(View.GONE);
|
||||
profileDetailsBinding.mainProfileImage.setVisibility(View.INVISIBLE);
|
||||
fetchProfileDetails();
|
||||
}
|
||||
@ -641,11 +638,10 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
final Context context = getContext();
|
||||
try {
|
||||
if (t == null) Toast.makeText(context,
|
||||
isLoggedIn ? R.string.error_loading_profile_loggedin : R.string.error_loading_profile,
|
||||
Toast.LENGTH_LONG).show();
|
||||
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 e) {}
|
||||
}
|
||||
|
||||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
@ -742,7 +738,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
profileDetailsBinding.mainProfileImage.setImageURI(profileModel.getProfilePicUrl());
|
||||
profileDetailsBinding.mainProfileImage.setVisibility(View.VISIBLE);
|
||||
|
||||
profileDetailsBinding.countsBarrier.setVisibility(View.VISIBLE);
|
||||
profileDetailsBinding.countsBarrier.getRoot().setVisibility(View.VISIBLE);
|
||||
|
||||
final long followersCount = profileModel.getFollowerCount();
|
||||
final long followingCount = profileModel.getFollowingCount();
|
||||
@ -784,8 +780,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
final String biography = profileModel.getBiography();
|
||||
if (TextUtils.isEmpty(biography)) {
|
||||
profileDetailsBinding.mainBiography.setVisibility(View.GONE);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
profileDetailsBinding.mainBiography.setVisibility(View.VISIBLE);
|
||||
profileDetailsBinding.mainBiography.setText(biography);
|
||||
profileDetailsBinding.mainBiography.addOnHashtagListener(autoLinkItem -> {
|
||||
@ -855,15 +850,14 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
String profileContext = profileModel.getProfileContext();
|
||||
if (TextUtils.isEmpty(profileContext)) {
|
||||
profileDetailsBinding.profileContext.setVisibility(View.GONE);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
profileDetailsBinding.profileContext.setVisibility(View.VISIBLE);
|
||||
final List<UserProfileContextLink> userProfileContextLinks = profileModel.getProfileContextLinks();
|
||||
for (int i = 0; i < userProfileContextLinks.size(); i++) {
|
||||
final UserProfileContextLink link = userProfileContextLinks.get(i);
|
||||
if (link.getUsername() != null)
|
||||
profileContext = profileContext.substring(0, link.getStart() + i)
|
||||
+ "@" + profileContext.substring(link.getStart() + i);
|
||||
+ "@" + profileContext.substring(link.getStart() + i);
|
||||
}
|
||||
profileDetailsBinding.profileContext.setText(profileContext);
|
||||
profileDetailsBinding.profileContext.addOnMentionClickListener(autoLinkItem -> {
|
||||
@ -921,13 +915,13 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
profileDetailsBinding.btnTagged.setVisibility(View.VISIBLE);
|
||||
profileDetailsBinding.btnSaved.setVisibility(View.VISIBLE);
|
||||
profileDetailsBinding.btnLiked.setVisibility(View.VISIBLE);
|
||||
// profileDetailsBinding.btnDM.setVisibility(View.GONE);
|
||||
// profileDetailsBinding.btnDM.setVisibility(View.GONE);
|
||||
profileDetailsBinding.btnSaved.setText(R.string.saved);
|
||||
return;
|
||||
}
|
||||
profileDetailsBinding.btnSaved.setVisibility(View.GONE);
|
||||
profileDetailsBinding.btnLiked.setVisibility(View.GONE);
|
||||
// profileDetailsBinding.btnDM.setVisibility(View.VISIBLE);
|
||||
// profileDetailsBinding.btnDM.setVisibility(View.VISIBLE);
|
||||
profileDetailsBinding.btnFollow.setVisibility(View.VISIBLE);
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
@ -1037,8 +1031,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
}))
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
else if (profileModel.getFriendshipStatus().isFollowing() || profileModel.getFriendshipStatus().isOutgoingRequest()) {
|
||||
} else if (profileModel.getFriendshipStatus().isFollowing() || profileModel.getFriendshipStatus().isOutgoingRequest()) {
|
||||
friendshipService.unfollow(
|
||||
profileModel.getPk(),
|
||||
new ServiceCallback<FriendshipChangeResponse>() {
|
||||
@ -1086,27 +1079,27 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
PostItemType.TAGGED);
|
||||
NavHostFragment.findNavController(this).navigate(action);
|
||||
});
|
||||
// profileDetailsBinding.btnDM.setOnClickListener(v -> {
|
||||
// profileDetailsBinding.btnDM.setEnabled(false);
|
||||
// new CreateThreadAction(cookie, profileModel.getPk(), thread -> {
|
||||
// if (thread == null) {
|
||||
// Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
// profileDetailsBinding.btnDM.setEnabled(true);
|
||||
// return;
|
||||
// }
|
||||
// if (isAdded()) {
|
||||
// final Bundle bundle = new Bundle();
|
||||
// bundle.putString("threadId", thread.getThreadId());
|
||||
// bundle.putString("title", thread.getThreadTitle());
|
||||
// if (isAdded()) {
|
||||
// final NavDirections action = ProfileFragmentDirections
|
||||
// .actionProfileFragmentToDMThreadFragment(thread.getThreadId(), profileModel.getUsername());
|
||||
// NavHostFragment.findNavController(this).navigate(action);
|
||||
// }
|
||||
// }
|
||||
// profileDetailsBinding.btnDM.setEnabled(true);
|
||||
// }).execute();
|
||||
// });
|
||||
// profileDetailsBinding.btnDM.setOnClickListener(v -> {
|
||||
// profileDetailsBinding.btnDM.setEnabled(false);
|
||||
// new CreateThreadAction(cookie, profileModel.getPk(), thread -> {
|
||||
// if (thread == null) {
|
||||
// Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
|
||||
// profileDetailsBinding.btnDM.setEnabled(true);
|
||||
// return;
|
||||
// }
|
||||
// if (isAdded()) {
|
||||
// final Bundle bundle = new Bundle();
|
||||
// bundle.putString("threadId", thread.getThreadId());
|
||||
// bundle.putString("title", thread.getThreadTitle());
|
||||
// if (isAdded()) {
|
||||
// final NavDirections action = ProfileFragmentDirections
|
||||
// .actionProfileFragmentToDMThreadFragment(thread.getThreadId(), profileModel.getUsername());
|
||||
// NavHostFragment.findNavController(this).navigate(action);
|
||||
// }
|
||||
// }
|
||||
// profileDetailsBinding.btnDM.setEnabled(true);
|
||||
// }).execute();
|
||||
// });
|
||||
profileDetailsBinding.mainProfileImage.setOnClickListener(v -> {
|
||||
if (!hasStories) {
|
||||
// show profile pic
|
||||
@ -1149,7 +1142,9 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
private void showProfilePicDialog() {
|
||||
if (profileModel != null) {
|
||||
final FragmentManager fragmentManager = getParentFragmentManager();
|
||||
final ProfilePicDialogFragment fragment = new ProfilePicDialogFragment(profileModel.getPk(), username, profileModel.getProfilePicUrl());
|
||||
final ProfilePicDialogFragment fragment = ProfilePicDialogFragment.getInstance(profileModel.getPk(),
|
||||
username,
|
||||
profileModel.getProfilePicUrl());
|
||||
final FragmentTransaction ft = fragmentManager.beginTransaction();
|
||||
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
|
||||
.add(fragment, "profilePicDialog")
|
||||
|
@ -0,0 +1,201 @@
|
||||
package awais.instagrabber.fragments.settings;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.text.Editable;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.customviews.helpers.TextWatcherAdapter;
|
||||
import awais.instagrabber.databinding.PrefAutoRefreshDmFreqBinding;
|
||||
import awais.instagrabber.services.DMSyncAlarmReceiver;
|
||||
import awais.instagrabber.services.DMSyncService;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.Debouncer;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
|
||||
import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER;
|
||||
import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT;
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public class DMPreferencesFragment extends BasePreferencesFragment {
|
||||
private static final String TAG = DMPreferencesFragment.class.getSimpleName();
|
||||
|
||||
@Override
|
||||
void setupPreferenceScreen(final PreferenceScreen screen) {
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
screen.addPreference(getMarkDMSeenPreference(context));
|
||||
// screen.addPreference(getAutoRefreshDMPreference(context));
|
||||
// screen.addPreference(getAutoRefreshDMFreqPreference(context));
|
||||
}
|
||||
|
||||
private Preference getMarkDMSeenPreference(@NonNull final Context context) {
|
||||
return PreferenceHelper.getSwitchPreference(
|
||||
context,
|
||||
Constants.DM_MARK_AS_SEEN,
|
||||
R.string.dm_mark_as_seen_setting,
|
||||
R.string.dm_mark_as_seen_setting_summary,
|
||||
false,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
private Preference getAutoRefreshDMPreference(@NonNull final Context context) {
|
||||
return PreferenceHelper.getSwitchPreference(
|
||||
context,
|
||||
PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH,
|
||||
R.string.enable_dm_auto_refesh,
|
||||
-1,
|
||||
false,
|
||||
(preference, newValue) -> {
|
||||
if (!(newValue instanceof Boolean)) return false;
|
||||
final boolean enabled = (Boolean) newValue;
|
||||
if (enabled) {
|
||||
DMSyncAlarmReceiver.setAlarm(context);
|
||||
return true;
|
||||
}
|
||||
DMSyncAlarmReceiver.cancelAlarm(context);
|
||||
try {
|
||||
final Context applicationContext = context.getApplicationContext();
|
||||
applicationContext.stopService(new Intent(applicationContext, DMSyncService.class));
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "getAutoRefreshDMPreference: ", e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private Preference getAutoRefreshDMFreqPreference(@NonNull final Context context) {
|
||||
return new AutoRefreshDMFrePreference(context);
|
||||
}
|
||||
|
||||
public static class AutoRefreshDMFrePreference extends Preference {
|
||||
private static final String TAG = AutoRefreshDMFrePreference.class.getSimpleName();
|
||||
private static final String DEBOUNCE_KEY = "dm_sync_service_update";
|
||||
public static final int INTERVAL = 2000;
|
||||
|
||||
private final Debouncer.Callback<String> changeCallback;
|
||||
|
||||
private Debouncer<String> serviceUpdateDebouncer;
|
||||
private PrefAutoRefreshDmFreqBinding binding;
|
||||
|
||||
public AutoRefreshDMFrePreference(final Context context) {
|
||||
super(context);
|
||||
setLayoutResource(R.layout.pref_auto_refresh_dm_freq);
|
||||
// setKey(key);
|
||||
setIconSpaceReserved(false);
|
||||
changeCallback = new Debouncer.Callback<String>() {
|
||||
@Override
|
||||
public void call(final String key) {
|
||||
DMSyncAlarmReceiver.setAlarm(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(final Throwable t) {
|
||||
Log.e(TAG, "onError: ", t);
|
||||
}
|
||||
};
|
||||
serviceUpdateDebouncer = new Debouncer<>(changeCallback, INTERVAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDependencyChanged(final Preference dependency, final boolean disableDependent) {
|
||||
// super.onDependencyChanged(dependency, disableDependent);
|
||||
if (binding == null) return;
|
||||
binding.startText.setEnabled(!disableDependent);
|
||||
binding.freqNum.setEnabled(!disableDependent);
|
||||
binding.freqUnit.setEnabled(!disableDependent);
|
||||
if (disableDependent) {
|
||||
serviceUpdateDebouncer.terminate();
|
||||
return;
|
||||
}
|
||||
serviceUpdateDebouncer = new Debouncer<>(changeCallback, INTERVAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(final PreferenceViewHolder holder) {
|
||||
super.onBindViewHolder(holder);
|
||||
setDependency(PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH);
|
||||
binding = PrefAutoRefreshDmFreqBinding.bind(holder.itemView);
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
setupUnitSpinner(context);
|
||||
setupNumberEditText(context);
|
||||
}
|
||||
|
||||
private void setupUnitSpinner(final Context context) {
|
||||
final ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(context,
|
||||
R.array.dm_auto_refresh_freq_unit_labels,
|
||||
android.R.layout.simple_spinner_item);
|
||||
final String[] values = context.getResources().getStringArray(R.array.dm_auto_refresh_freq_units);
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
binding.freqUnit.setAdapter(adapter);
|
||||
|
||||
String unit = settingsHelper.getString(PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT);
|
||||
if (TextUtils.isEmpty(unit)) {
|
||||
unit = "secs";
|
||||
}
|
||||
int position = 0;
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (Objects.equals(unit, values[i])) {
|
||||
position = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
binding.freqUnit.setSelection(position);
|
||||
binding.freqUnit.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(final AdapterView<?> parent, final View view, final int position, final long id) {
|
||||
settingsHelper.putString(PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT, values[position]);
|
||||
if (!isEnabled()) {
|
||||
serviceUpdateDebouncer.terminate();
|
||||
return;
|
||||
}
|
||||
serviceUpdateDebouncer.call(DEBOUNCE_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(final AdapterView<?> parent) {}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupNumberEditText(final Context context) {
|
||||
int currentValue = settingsHelper.getInteger(PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER);
|
||||
if (currentValue <= 0) {
|
||||
currentValue = 30;
|
||||
}
|
||||
binding.freqNum.setText(String.valueOf(currentValue));
|
||||
binding.freqNum.addTextChangedListener(new TextWatcherAdapter() {
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(final Editable s) {
|
||||
if (TextUtils.isEmpty(s)) return;
|
||||
try {
|
||||
final int value = Integer.parseInt(s.toString());
|
||||
if (value <= 0) return;
|
||||
settingsHelper.putInteger(PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER, value);
|
||||
if (!isEnabled()) {
|
||||
serviceUpdateDebouncer.terminate();
|
||||
return;
|
||||
}
|
||||
serviceUpdateDebouncer.call(DEBOUNCE_KEY);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "afterTextChanged: ", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
package awais.instagrabber.fragments.settings;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.widget.AppCompatButton;
|
||||
import androidx.appcompat.widget.AppCompatTextView;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
import androidx.preference.SwitchPreferenceCompat;
|
||||
|
||||
import com.google.android.material.switchmaterial.SwitchMaterial;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.DirectoryChooser;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
|
||||
import static awais.instagrabber.utils.Constants.FOLDER_PATH;
|
||||
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public class DownloadsPreferencesFragment extends BasePreferencesFragment {
|
||||
@Override
|
||||
void setupPreferenceScreen(final PreferenceScreen screen) {
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
screen.addPreference(getDownloadUserFolderPreference(context));
|
||||
screen.addPreference(getSaveToCustomFolderPreference(context));
|
||||
}
|
||||
|
||||
private Preference getDownloadUserFolderPreference(@NonNull final Context context) {
|
||||
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);
|
||||
preference.setKey(Constants.DOWNLOAD_USER_FOLDER);
|
||||
preference.setTitle(R.string.download_user_folder);
|
||||
preference.setIconSpaceReserved(false);
|
||||
return preference;
|
||||
}
|
||||
|
||||
private Preference getSaveToCustomFolderPreference(@NonNull final Context context) {
|
||||
return new SaveToCustomFolderPreference(context, (resultCallback) -> new DirectoryChooser()
|
||||
.setInitialDirectory(settingsHelper.getString(FOLDER_PATH))
|
||||
.setInteractionListener(file -> {
|
||||
settingsHelper.putString(FOLDER_PATH, file.getAbsolutePath());
|
||||
resultCallback.onResult(file.getAbsolutePath());
|
||||
})
|
||||
.show(getParentFragmentManager(), null));
|
||||
}
|
||||
|
||||
public static class SaveToCustomFolderPreference extends Preference {
|
||||
private AppCompatTextView customPathTextView;
|
||||
private final OnSelectFolderButtonClickListener onSelectFolderButtonClickListener;
|
||||
private final String key;
|
||||
|
||||
public SaveToCustomFolderPreference(final Context context, final OnSelectFolderButtonClickListener onSelectFolderButtonClickListener) {
|
||||
super(context);
|
||||
this.onSelectFolderButtonClickListener = onSelectFolderButtonClickListener;
|
||||
key = Constants.FOLDER_SAVE_TO;
|
||||
setLayoutResource(R.layout.pref_custom_folder);
|
||||
setKey(key);
|
||||
setTitle(R.string.save_to_folder);
|
||||
setIconSpaceReserved(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(final PreferenceViewHolder holder) {
|
||||
super.onBindViewHolder(holder);
|
||||
final SwitchMaterial cbSaveTo = (SwitchMaterial) holder.findViewById(R.id.cbSaveTo);
|
||||
final View buttonContainer = holder.findViewById(R.id.button_container);
|
||||
customPathTextView = (AppCompatTextView) holder.findViewById(R.id.custom_path);
|
||||
cbSaveTo.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||
settingsHelper.putBoolean(FOLDER_SAVE_TO, isChecked);
|
||||
buttonContainer.setVisibility(isChecked ? View.VISIBLE : View.GONE);
|
||||
final String customPath = settingsHelper.getString(FOLDER_PATH);
|
||||
customPathTextView.setText(customPath);
|
||||
});
|
||||
final boolean savedToEnabled = settingsHelper.getBoolean(key);
|
||||
holder.itemView.setOnClickListener(v -> cbSaveTo.toggle());
|
||||
cbSaveTo.setChecked(savedToEnabled);
|
||||
buttonContainer.setVisibility(savedToEnabled ? View.VISIBLE : View.GONE);
|
||||
final AppCompatButton btnSaveTo = (AppCompatButton) holder.findViewById(R.id.btnSaveTo);
|
||||
btnSaveTo.setOnClickListener(v -> {
|
||||
if (onSelectFolderButtonClickListener == null) return;
|
||||
onSelectFolderButtonClickListener.onClick(result -> {
|
||||
if (TextUtils.isEmpty(result)) return;
|
||||
customPathTextView.setText(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public interface ResultCallback {
|
||||
void onResult(String result);
|
||||
}
|
||||
|
||||
public interface OnSelectFolderButtonClickListener {
|
||||
void onClick(ResultCallback resultCallback);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package awais.instagrabber.fragments.settings;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.SwitchPreferenceCompat;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.CookieUtils;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public class GeneralPreferencesFragment extends BasePreferencesFragment {
|
||||
|
||||
@Override
|
||||
void setupPreferenceScreen(final PreferenceScreen screen) {
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||
final boolean isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0;
|
||||
if (isLoggedIn) {
|
||||
screen.addPreference(getDefaultTabPreference(context));
|
||||
}
|
||||
screen.addPreference(getUpdateCheckPreference(context));
|
||||
}
|
||||
|
||||
private Preference getDefaultTabPreference(@NonNull final Context context) {
|
||||
final ListPreference preference = new ListPreference(context);
|
||||
preference.setSummaryProvider(ListPreference.SimpleSummaryProvider.getInstance());
|
||||
final TypedArray mainNavIds = getResources().obtainTypedArray(R.array.main_nav_ids);
|
||||
final int length = mainNavIds.length();
|
||||
final String[] values = new String[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
final int resourceId = mainNavIds.getResourceId(i, -1);
|
||||
if (resourceId < 0) continue;
|
||||
values[i] = getResources().getResourceEntryName(resourceId);
|
||||
}
|
||||
mainNavIds.recycle();
|
||||
preference.setKey(Constants.DEFAULT_TAB);
|
||||
preference.setTitle(R.string.pref_start_screen);
|
||||
preference.setDialogTitle(R.string.pref_start_screen);
|
||||
preference.setEntries(R.array.main_nav_ids_values);
|
||||
preference.setEntryValues(values);
|
||||
preference.setIconSpaceReserved(false);
|
||||
return preference;
|
||||
}
|
||||
|
||||
private Preference getUpdateCheckPreference(@NonNull final Context context) {
|
||||
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);
|
||||
preference.setKey(Constants.CHECK_UPDATES);
|
||||
preference.setTitle(R.string.update_check);
|
||||
preference.setIconSpaceReserved(false);
|
||||
return preference;
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package awais.instagrabber.fragments.settings;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.dialogs.TimeSettingsDialog;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.LocaleUtils;
|
||||
import awais.instagrabber.utils.UserAgentUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public class LocalePreferencesFragment extends BasePreferencesFragment {
|
||||
@Override
|
||||
void setupPreferenceScreen(final PreferenceScreen screen) {
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
screen.addPreference(getLanguagePreference(context));
|
||||
screen.addPreference(getPostTimeFormatPreference(context));
|
||||
}
|
||||
|
||||
private Preference getLanguagePreference(@NonNull final Context context) {
|
||||
final ListPreference preference = new ListPreference(context);
|
||||
preference.setSummaryProvider(ListPreference.SimpleSummaryProvider.getInstance());
|
||||
final int length = getResources().getStringArray(R.array.languages).length;
|
||||
final String[] values = new String[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
values[i] = String.valueOf(i);
|
||||
}
|
||||
preference.setKey(Constants.APP_LANGUAGE);
|
||||
preference.setTitle(R.string.select_language);
|
||||
preference.setDialogTitle(R.string.select_language);
|
||||
preference.setEntries(R.array.languages);
|
||||
preference.setIconSpaceReserved(false);
|
||||
preference.setEntryValues(values);
|
||||
preference.setOnPreferenceChangeListener((preference1, newValue) -> {
|
||||
shouldRecreate();
|
||||
final int appUaCode = settingsHelper.getInteger(Constants.APP_UA_CODE);
|
||||
final String appUa = UserAgentUtils.generateAppUA(appUaCode, LocaleUtils.getCurrentLocale().getLanguage());
|
||||
settingsHelper.putString(Constants.APP_UA, appUa);
|
||||
return true;
|
||||
});
|
||||
return preference;
|
||||
}
|
||||
|
||||
private Preference getPostTimeFormatPreference(@NonNull final Context context) {
|
||||
final Preference preference = new Preference(context);
|
||||
preference.setTitle(R.string.time_settings);
|
||||
preference.setSummary(Utils.datetimeParser.format(new Date()));
|
||||
preference.setIconSpaceReserved(false);
|
||||
preference.setOnPreferenceClickListener(preference1 -> {
|
||||
new TimeSettingsDialog(
|
||||
settingsHelper.getBoolean(Constants.CUSTOM_DATE_TIME_FORMAT_ENABLED),
|
||||
settingsHelper.getString(Constants.CUSTOM_DATE_TIME_FORMAT),
|
||||
settingsHelper.getString(Constants.DATE_TIME_SELECTION),
|
||||
settingsHelper.getBoolean(Constants.SWAP_DATE_TIME_FORMAT_ENABLED),
|
||||
(isCustomFormat,
|
||||
formatSelection,
|
||||
spTimeFormatSelectedItemPosition,
|
||||
spSeparatorSelectedItemPosition,
|
||||
spDateFormatSelectedItemPosition,
|
||||
selectedFormat,
|
||||
currentFormat,
|
||||
swapDateTime) -> {
|
||||
if (isCustomFormat) {
|
||||
settingsHelper.putString(Constants.CUSTOM_DATE_TIME_FORMAT, formatSelection);
|
||||
} else {
|
||||
final String formatSelectionUpdated = spTimeFormatSelectedItemPosition + ";"
|
||||
+ spSeparatorSelectedItemPosition + ';'
|
||||
+ spDateFormatSelectedItemPosition; // time;separator;date
|
||||
settingsHelper.putString(Constants.DATE_TIME_FORMAT, selectedFormat);
|
||||
settingsHelper.putString(Constants.DATE_TIME_SELECTION, formatSelectionUpdated);
|
||||
}
|
||||
settingsHelper.putBoolean(Constants.CUSTOM_DATE_TIME_FORMAT_ENABLED, isCustomFormat);
|
||||
settingsHelper.putBoolean(Constants.SWAP_DATE_TIME_FORMAT_ENABLED, swapDateTime);
|
||||
Utils.datetimeParser = (SimpleDateFormat) currentFormat.clone();
|
||||
preference.setSummary(Utils.datetimeParser.format(new Date()));
|
||||
}
|
||||
).show(getParentFragmentManager(), null);
|
||||
return true;
|
||||
});
|
||||
return preference;
|
||||
}
|
||||
}
|
@ -14,6 +14,7 @@ import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.navigation.NavController;
|
||||
import androidx.navigation.NavDirections;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.preference.Preference;
|
||||
@ -132,43 +133,58 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
|
||||
// generalCategory.setIconSpaceReserved(false);
|
||||
// screen.addPreference(generalCategory);
|
||||
screen.addPreference(getDivider(context));
|
||||
final NavController navController = NavHostFragment.findNavController(this);
|
||||
if (isLoggedIn) {
|
||||
screen.addPreference(getPreference(R.string.action_notif, R.drawable.ic_not_liked, preference -> {
|
||||
final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalNotificationsViewerFragment("notif", 0l);
|
||||
NavHostFragment.findNavController(this).navigate(navDirections);
|
||||
if (isSafeToNavigate(navController)) {
|
||||
final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalNotificationsViewerFragment("notif", 0L);
|
||||
navController.navigate(navDirections);
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
screen.addPreference(getPreference(R.string.action_ayml, R.drawable.ic_suggested_users, preference -> {
|
||||
final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalNotificationsViewerFragment("ayml", 0l);
|
||||
NavHostFragment.findNavController(this).navigate(navDirections);
|
||||
if (isSafeToNavigate(navController)) {
|
||||
final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalNotificationsViewerFragment("ayml", 0L);
|
||||
navController.navigate(navDirections);
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
screen.addPreference(getPreference(R.string.action_archive, R.drawable.ic_archive, preference -> {
|
||||
final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalStoryListViewerFragment("archive");
|
||||
NavHostFragment.findNavController(this).navigate(navDirections);
|
||||
if (isSafeToNavigate(navController)) {
|
||||
final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalStoryListViewerFragment("archive");
|
||||
navController.navigate(navDirections);
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
screen.addPreference(getPreference(R.string.title_favorites, R.drawable.ic_star_24, preference -> {
|
||||
final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToFavoritesFragment();
|
||||
NavHostFragment.findNavController(this).navigate(navDirections);
|
||||
if (isSafeToNavigate(navController)) {
|
||||
final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToFavoritesFragment();
|
||||
navController.navigate(navDirections);
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
|
||||
screen.addPreference(getDivider(context));
|
||||
screen.addPreference(getPreference(R.string.action_settings, R.drawable.ic_outline_settings_24, preference -> {
|
||||
final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToSettingsPreferencesFragment();
|
||||
NavHostFragment.findNavController(this).navigate(navDirections);
|
||||
if (isSafeToNavigate(navController)) {
|
||||
final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToSettingsPreferencesFragment();
|
||||
navController.navigate(navDirections);
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
screen.addPreference(getPreference(R.string.backup_and_restore, R.drawable.ic_settings_backup_restore_24, preference -> {
|
||||
final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToBackupPreferencesFragment();
|
||||
NavHostFragment.findNavController(this).navigate(navDirections);
|
||||
if (isSafeToNavigate(navController)) {
|
||||
final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToBackupPreferencesFragment();
|
||||
navController.navigate(navDirections);
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
screen.addPreference(getPreference(R.string.action_about, R.drawable.ic_outline_info_24, preference1 -> {
|
||||
final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToAboutFragment();
|
||||
NavHostFragment.findNavController(this).navigate(navDirections);
|
||||
if (isSafeToNavigate(navController)) {
|
||||
final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToAboutFragment();
|
||||
navController.navigate(navDirections);
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
|
||||
@ -187,6 +203,11 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
|
||||
screen.addPreference(reminderPreference);
|
||||
}
|
||||
|
||||
private boolean isSafeToNavigate(final NavController navController) {
|
||||
return navController.getCurrentDestination() != null
|
||||
&& navController.getCurrentDestination().getId() == R.id.morePreferencesFragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {
|
||||
if (resultCode == Constants.LOGIN_RESULT_CODE) {
|
||||
|
@ -0,0 +1,43 @@
|
||||
package awais.instagrabber.fragments.settings;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
|
||||
public class NotificationsPreferencesFragment extends BasePreferencesFragment {
|
||||
@Override
|
||||
void setupPreferenceScreen(final PreferenceScreen screen) {
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
screen.addPreference(getActivityNotificationsPreference(context));
|
||||
screen.addPreference(getDMNotificationsPreference(context));
|
||||
}
|
||||
|
||||
private Preference getActivityNotificationsPreference(@NonNull final Context context) {
|
||||
return PreferenceHelper.getSwitchPreference(
|
||||
context,
|
||||
Constants.CHECK_ACTIVITY,
|
||||
R.string.activity_setting,
|
||||
-1,
|
||||
false,
|
||||
(preference, newValue) -> {
|
||||
shouldRecreate();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private Preference getDMNotificationsPreference(@NonNull final Context context) {
|
||||
return PreferenceHelper.getSwitchPreference(
|
||||
context,
|
||||
PreferenceKeys.PREF_ENABLE_DM_NOTIFICATIONS,
|
||||
R.string.enable_dm_notifications,
|
||||
-1,
|
||||
false,
|
||||
null);
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package awais.instagrabber.fragments.settings;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.SwitchPreferenceCompat;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public class PostPreferencesFragment extends BasePreferencesFragment {
|
||||
@Override
|
||||
void setupPreferenceScreen(final PreferenceScreen screen) {
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
// generalCategory.addPreference(getAutoPlayVideosPreference(context));
|
||||
screen.addPreference(getAlwaysMuteVideosPreference(context));
|
||||
screen.addPreference(getShowCaptionPreference(context));
|
||||
}
|
||||
|
||||
private Preference getAutoPlayVideosPreference(@NonNull final Context context) {
|
||||
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);
|
||||
preference.setKey(Constants.AUTOPLAY_VIDEOS);
|
||||
preference.setTitle(R.string.post_viewer_autoplay_video);
|
||||
preference.setIconSpaceReserved(false);
|
||||
return preference;
|
||||
}
|
||||
|
||||
private Preference getAlwaysMuteVideosPreference(@NonNull final Context context) {
|
||||
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);
|
||||
preference.setKey(Constants.MUTED_VIDEOS);
|
||||
preference.setTitle(R.string.post_viewer_muted_autoplay);
|
||||
preference.setIconSpaceReserved(false);
|
||||
return preference;
|
||||
}
|
||||
|
||||
private Preference getShowCaptionPreference(@NonNull final Context context) {
|
||||
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);
|
||||
preference.setKey(Constants.SHOW_CAPTIONS);
|
||||
preference.setDefaultValue(true);
|
||||
preference.setTitle(R.string.post_viewer_show_captions);
|
||||
preference.setIconSpaceReserved(false);
|
||||
return preference;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package awais.instagrabber.fragments.settings;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.preference.Preference.OnPreferenceChangeListener;
|
||||
import androidx.preference.SwitchPreferenceCompat;
|
||||
|
||||
public final class PreferenceHelper {
|
||||
|
||||
public static SwitchPreferenceCompat getSwitchPreference(@NonNull final Context context,
|
||||
@NonNull final String key,
|
||||
@StringRes final int titleResId,
|
||||
@StringRes final int summaryResId,
|
||||
final boolean iconSpaceReserved,
|
||||
final OnPreferenceChangeListener onPreferenceChangeListener) {
|
||||
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);
|
||||
preference.setKey(key);
|
||||
preference.setTitle(titleResId);
|
||||
preference.setIconSpaceReserved(iconSpaceReserved);
|
||||
if (summaryResId != -1) {
|
||||
preference.setSummary(summaryResId);
|
||||
}
|
||||
if (onPreferenceChangeListener != null) {
|
||||
preference.setOnPreferenceChangeListener(onPreferenceChangeListener);
|
||||
}
|
||||
return preference;
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package awais.instagrabber.fragments.settings;
|
||||
|
||||
public final class PreferenceKeys {
|
||||
public static final String PREF_ENABLE_DM_NOTIFICATIONS = "enable_dm_notifications";
|
||||
public static final String PREF_ENABLE_DM_AUTO_REFRESH = "enable_dm_auto_refresh";
|
||||
public static final String PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT = "enable_dm_auto_refresh_freq_unit";
|
||||
public static final String PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER = "enable_dm_auto_refresh_freq_number";
|
||||
}
|
@ -1,91 +1,55 @@
|
||||
package awais.instagrabber.fragments.settings;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.widget.AppCompatButton;
|
||||
import androidx.appcompat.widget.AppCompatTextView;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.navigation.NavDirections;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
import androidx.preference.SwitchPreferenceCompat;
|
||||
|
||||
import com.google.android.material.switchmaterial.SwitchMaterial;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.dialogs.TimeSettingsDialog;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.CookieUtils;
|
||||
import awais.instagrabber.utils.DirectoryChooser;
|
||||
import awais.instagrabber.utils.LocaleUtils;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.utils.UserAgentUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
|
||||
import static awais.instagrabber.utils.Constants.FOLDER_PATH;
|
||||
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
|
||||
import static awais.instagrabber.fragments.settings.SettingsPreferencesFragmentDirections.actionSettingsToDm;
|
||||
import static awais.instagrabber.fragments.settings.SettingsPreferencesFragmentDirections.actionSettingsToDownloads;
|
||||
import static awais.instagrabber.fragments.settings.SettingsPreferencesFragmentDirections.actionSettingsToGeneral;
|
||||
import static awais.instagrabber.fragments.settings.SettingsPreferencesFragmentDirections.actionSettingsToLocale;
|
||||
import static awais.instagrabber.fragments.settings.SettingsPreferencesFragmentDirections.actionSettingsToNotifications;
|
||||
import static awais.instagrabber.fragments.settings.SettingsPreferencesFragmentDirections.actionSettingsToPost;
|
||||
import static awais.instagrabber.fragments.settings.SettingsPreferencesFragmentDirections.actionSettingsToStories;
|
||||
import static awais.instagrabber.fragments.settings.SettingsPreferencesFragmentDirections.actionSettingsToTheme;
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public class SettingsPreferencesFragment extends BasePreferencesFragment {
|
||||
private static final String TAG = "SettingsPrefsFrag";
|
||||
private boolean isLoggedIn;
|
||||
private static final String TAG = SettingsPreferencesFragment.class.getSimpleName();
|
||||
private static final List<SettingScreen> screens = ImmutableList.of(
|
||||
new SettingScreen(R.string.pref_category_general, actionSettingsToGeneral()),
|
||||
new SettingScreen(R.string.pref_category_theme, actionSettingsToTheme()),
|
||||
new SettingScreen(R.string.pref_category_locale, actionSettingsToLocale()),
|
||||
new SettingScreen(R.string.pref_category_post, actionSettingsToPost()),
|
||||
new SettingScreen(R.string.pref_category_stories, actionSettingsToStories(), true),
|
||||
new SettingScreen(R.string.pref_category_dm, actionSettingsToDm(), true),
|
||||
new SettingScreen(R.string.pref_category_notifications, actionSettingsToNotifications(), true),
|
||||
new SettingScreen(R.string.pref_category_downloads, actionSettingsToDownloads())
|
||||
);
|
||||
|
||||
@Override
|
||||
void setupPreferenceScreen(final PreferenceScreen screen) {
|
||||
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||
isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0;
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
final PreferenceCategory generalCategory = new PreferenceCategory(context);
|
||||
screen.addPreference(generalCategory);
|
||||
generalCategory.setTitle(R.string.pref_category_general);
|
||||
generalCategory.setIconSpaceReserved(false);
|
||||
generalCategory.addPreference(getThemePreference(context));
|
||||
generalCategory.addPreference(getDefaultTabPreference());
|
||||
generalCategory.addPreference(getUpdateCheckPreference());
|
||||
// generalCategory.addPreference(getAutoPlayVideosPreference());
|
||||
generalCategory.addPreference(getAlwaysMuteVideosPreference());
|
||||
generalCategory.addPreference(getShowCaptionPreference());
|
||||
|
||||
// screen.addPreference(getDivider(context));
|
||||
// final PreferenceCategory themeCategory = new PreferenceCategory(context);
|
||||
// screen.addPreference(themeCategory);
|
||||
// themeCategory.setTitle(R.string.pref_category_theme);
|
||||
// themeCategory.setIconSpaceReserved(false);
|
||||
// themeCategory.addPreference(getAmoledThemePreference());
|
||||
|
||||
final PreferenceCategory downloadsCategory = new PreferenceCategory(context);
|
||||
screen.addPreference(downloadsCategory);
|
||||
downloadsCategory.setTitle(R.string.pref_category_downloads);
|
||||
downloadsCategory.setIconSpaceReserved(false);
|
||||
downloadsCategory.addPreference(getDownloadUserFolderPreference());
|
||||
downloadsCategory.addPreference(getSaveToCustomFolderPreference());
|
||||
|
||||
final PreferenceCategory localeCategory = new PreferenceCategory(context);
|
||||
screen.addPreference(localeCategory);
|
||||
localeCategory.setTitle(R.string.pref_category_locale);
|
||||
localeCategory.setIconSpaceReserved(false);
|
||||
localeCategory.addPreference(getLanguagePreference());
|
||||
localeCategory.addPreference(getPostTimePreference());
|
||||
|
||||
if (isLoggedIn) {
|
||||
final PreferenceCategory loggedInUsersPreferenceCategory = new PreferenceCategory(context);
|
||||
screen.addPreference(loggedInUsersPreferenceCategory);
|
||||
loggedInUsersPreferenceCategory.setIconSpaceReserved(false);
|
||||
loggedInUsersPreferenceCategory.setTitle(R.string.login_settings);
|
||||
loggedInUsersPreferenceCategory.addPreference(getStorySortPreference());
|
||||
loggedInUsersPreferenceCategory.addPreference(getMarkStoriesSeenPreference());
|
||||
loggedInUsersPreferenceCategory.addPreference(getMarkDMSeenPreference());
|
||||
loggedInUsersPreferenceCategory.addPreference(getEnableActivityNotificationsPreference());
|
||||
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||
final boolean isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0;
|
||||
for (final SettingScreen settingScreen : screens) {
|
||||
if (settingScreen.isLoginRequired() && !isLoggedIn) continue;
|
||||
screen.addPreference(getNavPreference(context, settingScreen));
|
||||
}
|
||||
// else {
|
||||
// final PreferenceCategory anonUsersPreferenceCategory = new PreferenceCategory(context);
|
||||
@ -95,275 +59,43 @@ public class SettingsPreferencesFragment extends BasePreferencesFragment {
|
||||
// }
|
||||
}
|
||||
|
||||
private Preference getLanguagePreference() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return null;
|
||||
final ListPreference preference = new ListPreference(context);
|
||||
preference.setSummaryProvider(ListPreference.SimpleSummaryProvider.getInstance());
|
||||
final int length = getResources().getStringArray(R.array.languages).length;
|
||||
final String[] values = new String[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
values[i] = String.valueOf(i);
|
||||
}
|
||||
preference.setKey(Constants.APP_LANGUAGE);
|
||||
preference.setTitle(R.string.select_language);
|
||||
preference.setDialogTitle(R.string.select_language);
|
||||
preference.setEntries(R.array.languages);
|
||||
preference.setIconSpaceReserved(false);
|
||||
preference.setEntryValues(values);
|
||||
preference.setOnPreferenceChangeListener((preference1, newValue) -> {
|
||||
shouldRecreate();
|
||||
final int appUaCode = settingsHelper.getInteger(Constants.APP_UA_CODE);
|
||||
final String appUa = UserAgentUtils.generateAppUA(appUaCode, LocaleUtils.getCurrentLocale().getLanguage());
|
||||
settingsHelper.putString(Constants.APP_UA, appUa);
|
||||
return true;
|
||||
});
|
||||
return preference;
|
||||
}
|
||||
|
||||
private Preference getDefaultTabPreference() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return null;
|
||||
final ListPreference preference = new ListPreference(context);
|
||||
preference.setEnabled(isLoggedIn);
|
||||
preference.setSummaryProvider(ListPreference.SimpleSummaryProvider.getInstance());
|
||||
final TypedArray mainNavIds = getResources().obtainTypedArray(R.array.main_nav_ids);
|
||||
final int length = mainNavIds.length();
|
||||
final String[] values = new String[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
final int resourceId = mainNavIds.getResourceId(i, -1);
|
||||
if (resourceId < 0) continue;
|
||||
values[i] = getResources().getResourceEntryName(resourceId);
|
||||
}
|
||||
mainNavIds.recycle();
|
||||
preference.setKey(Constants.DEFAULT_TAB);
|
||||
preference.setTitle(R.string.pref_start_screen);
|
||||
preference.setDialogTitle(R.string.pref_start_screen);
|
||||
preference.setEntries(R.array.main_nav_ids_values);
|
||||
preference.setEntryValues(values);
|
||||
preference.setIconSpaceReserved(false);
|
||||
return preference;
|
||||
}
|
||||
|
||||
private Preference getUpdateCheckPreference() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return null;
|
||||
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);
|
||||
preference.setKey(Constants.CHECK_UPDATES);
|
||||
preference.setTitle(R.string.update_check);
|
||||
preference.setIconSpaceReserved(false);
|
||||
return preference;
|
||||
}
|
||||
|
||||
private Preference getThemePreference(@NonNull final Context context) {
|
||||
private Preference getNavPreference(@NonNull final Context context,
|
||||
@NonNull final SettingScreen settingScreen) {
|
||||
final Preference preference = new Preference(context);
|
||||
preference.setTitle(R.string.pref_category_theme);
|
||||
// preference.setIcon(R.drawable.ic_format_paint_24);
|
||||
preference.setTitle(settingScreen.getTitleResId());
|
||||
preference.setIconSpaceReserved(false);
|
||||
preference.setOnPreferenceClickListener(preference1 -> {
|
||||
final NavDirections navDirections = SettingsPreferencesFragmentDirections.actionSettingsPreferencesFragmentToThemePreferencesFragment();
|
||||
NavHostFragment.findNavController(this).navigate(navDirections);
|
||||
NavHostFragment.findNavController(this).navigate(settingScreen.getDirections());
|
||||
return true;
|
||||
});
|
||||
return preference;
|
||||
}
|
||||
|
||||
private Preference getDownloadUserFolderPreference() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return null;
|
||||
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);
|
||||
preference.setKey(Constants.DOWNLOAD_USER_FOLDER);
|
||||
preference.setTitle(R.string.download_user_folder);
|
||||
preference.setIconSpaceReserved(false);
|
||||
return preference;
|
||||
}
|
||||
private static class SettingScreen {
|
||||
private final int titleResId;
|
||||
private final NavDirections directions;
|
||||
private final boolean loginRequired;
|
||||
|
||||
private Preference getSaveToCustomFolderPreference() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return null;
|
||||
return new SaveToCustomFolderPreference(context, (resultCallback) -> new DirectoryChooser()
|
||||
.setInitialDirectory(settingsHelper.getString(FOLDER_PATH))
|
||||
.setInteractionListener(file -> {
|
||||
settingsHelper.putString(FOLDER_PATH, file.getAbsolutePath());
|
||||
resultCallback.onResult(file.getAbsolutePath());
|
||||
})
|
||||
.show(getParentFragmentManager(), null));
|
||||
}
|
||||
|
||||
private Preference getAutoPlayVideosPreference() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return null;
|
||||
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);
|
||||
preference.setKey(Constants.AUTOPLAY_VIDEOS);
|
||||
preference.setTitle(R.string.post_viewer_autoplay_video);
|
||||
preference.setIconSpaceReserved(false);
|
||||
return preference;
|
||||
}
|
||||
|
||||
private Preference getAlwaysMuteVideosPreference() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return null;
|
||||
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);
|
||||
preference.setKey(Constants.MUTED_VIDEOS);
|
||||
preference.setTitle(R.string.post_viewer_muted_autoplay);
|
||||
preference.setIconSpaceReserved(false);
|
||||
return preference;
|
||||
}
|
||||
|
||||
private Preference getShowCaptionPreference() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return null;
|
||||
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);
|
||||
preference.setKey(Constants.SHOW_CAPTIONS);
|
||||
preference.setDefaultValue(true);
|
||||
preference.setTitle(R.string.post_viewer_show_captions);
|
||||
preference.setIconSpaceReserved(false);
|
||||
return preference;
|
||||
}
|
||||
|
||||
private Preference getStorySortPreference() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return null;
|
||||
final ListPreference preference = new ListPreference(context);
|
||||
preference.setSummaryProvider(ListPreference.SimpleSummaryProvider.getInstance());
|
||||
final int length = getResources().getStringArray(R.array.story_sorts).length;
|
||||
final String[] values = new String[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
values[i] = String.valueOf(i);
|
||||
}
|
||||
preference.setKey(Constants.STORY_SORT);
|
||||
preference.setTitle(R.string.story_sort_setting);
|
||||
preference.setDialogTitle(R.string.story_sort_setting);
|
||||
preference.setEntries(R.array.story_sorts);
|
||||
preference.setIconSpaceReserved(false);
|
||||
preference.setEntryValues(values);
|
||||
return preference;
|
||||
}
|
||||
|
||||
private Preference getMarkStoriesSeenPreference() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return null;
|
||||
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);
|
||||
preference.setKey(Constants.MARK_AS_SEEN);
|
||||
preference.setTitle(R.string.mark_as_seen_setting);
|
||||
preference.setSummary(R.string.mark_as_seen_setting_summary);
|
||||
preference.setIconSpaceReserved(false);
|
||||
return preference;
|
||||
}
|
||||
|
||||
private Preference getMarkDMSeenPreference() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return null;
|
||||
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);
|
||||
preference.setKey(Constants.DM_MARK_AS_SEEN);
|
||||
preference.setTitle(R.string.dm_mark_as_seen_setting);
|
||||
preference.setSummary(R.string.dm_mark_as_seen_setting_summary);
|
||||
preference.setIconSpaceReserved(false);
|
||||
return preference;
|
||||
}
|
||||
|
||||
private Preference getEnableActivityNotificationsPreference() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return null;
|
||||
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);
|
||||
preference.setKey(Constants.CHECK_ACTIVITY);
|
||||
preference.setTitle(R.string.activity_setting);
|
||||
preference.setIconSpaceReserved(false);
|
||||
preference.setOnPreferenceChangeListener((preference1, newValue) -> {
|
||||
shouldRecreate();
|
||||
return true;
|
||||
});
|
||||
return preference;
|
||||
}
|
||||
|
||||
private Preference getPostTimePreference() {
|
||||
final Context context = getContext();
|
||||
if (context == null) return null;
|
||||
final Preference preference = new Preference(context);
|
||||
preference.setTitle(R.string.time_settings);
|
||||
preference.setSummary(Utils.datetimeParser.format(new Date()));
|
||||
preference.setIconSpaceReserved(false);
|
||||
preference.setOnPreferenceClickListener(preference1 -> {
|
||||
new TimeSettingsDialog(
|
||||
settingsHelper.getBoolean(Constants.CUSTOM_DATE_TIME_FORMAT_ENABLED),
|
||||
settingsHelper.getString(Constants.CUSTOM_DATE_TIME_FORMAT),
|
||||
settingsHelper.getString(Constants.DATE_TIME_SELECTION),
|
||||
settingsHelper.getBoolean(Constants.SWAP_DATE_TIME_FORMAT_ENABLED),
|
||||
(isCustomFormat,
|
||||
formatSelection,
|
||||
spTimeFormatSelectedItemPosition,
|
||||
spSeparatorSelectedItemPosition,
|
||||
spDateFormatSelectedItemPosition,
|
||||
selectedFormat,
|
||||
currentFormat,
|
||||
swapDateTime) -> {
|
||||
if (isCustomFormat) {
|
||||
settingsHelper.putString(Constants.CUSTOM_DATE_TIME_FORMAT, formatSelection);
|
||||
} else {
|
||||
final String formatSelectionUpdated = spTimeFormatSelectedItemPosition + ";"
|
||||
+ spSeparatorSelectedItemPosition + ';'
|
||||
+ spDateFormatSelectedItemPosition; // time;separator;date
|
||||
settingsHelper.putString(Constants.DATE_TIME_FORMAT, selectedFormat);
|
||||
settingsHelper.putString(Constants.DATE_TIME_SELECTION, formatSelectionUpdated);
|
||||
}
|
||||
settingsHelper.putBoolean(Constants.CUSTOM_DATE_TIME_FORMAT_ENABLED, isCustomFormat);
|
||||
settingsHelper.putBoolean(Constants.SWAP_DATE_TIME_FORMAT_ENABLED, swapDateTime);
|
||||
Utils.datetimeParser = (SimpleDateFormat) currentFormat.clone();
|
||||
preference.setSummary(Utils.datetimeParser.format(new Date()));
|
||||
}
|
||||
).show(getParentFragmentManager(), null);
|
||||
return true;
|
||||
});
|
||||
return preference;
|
||||
}
|
||||
|
||||
public static class SaveToCustomFolderPreference extends Preference {
|
||||
private AppCompatTextView customPathTextView;
|
||||
private final OnSelectFolderButtonClickListener onSelectFolderButtonClickListener;
|
||||
private final String key;
|
||||
|
||||
public SaveToCustomFolderPreference(final Context context, final OnSelectFolderButtonClickListener onSelectFolderButtonClickListener) {
|
||||
super(context);
|
||||
this.onSelectFolderButtonClickListener = onSelectFolderButtonClickListener;
|
||||
key = Constants.FOLDER_SAVE_TO;
|
||||
setLayoutResource(R.layout.pref_custom_folder);
|
||||
setKey(key);
|
||||
setTitle(R.string.save_to_folder);
|
||||
setIconSpaceReserved(false);
|
||||
public SettingScreen(@StringRes final int titleResId, final NavDirections directions) {
|
||||
this(titleResId, directions, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(final PreferenceViewHolder holder) {
|
||||
super.onBindViewHolder(holder);
|
||||
final SwitchMaterial cbSaveTo = (SwitchMaterial) holder.findViewById(R.id.cbSaveTo);
|
||||
final View buttonContainer = holder.findViewById(R.id.button_container);
|
||||
customPathTextView = (AppCompatTextView) holder.findViewById(R.id.custom_path);
|
||||
cbSaveTo.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||
settingsHelper.putBoolean(FOLDER_SAVE_TO, isChecked);
|
||||
buttonContainer.setVisibility(isChecked ? View.VISIBLE : View.GONE);
|
||||
final String customPath = settingsHelper.getString(FOLDER_PATH);
|
||||
customPathTextView.setText(customPath);
|
||||
});
|
||||
final boolean savedToEnabled = settingsHelper.getBoolean(key);
|
||||
holder.itemView.setOnClickListener(v -> cbSaveTo.toggle());
|
||||
cbSaveTo.setChecked(savedToEnabled);
|
||||
buttonContainer.setVisibility(savedToEnabled ? View.VISIBLE : View.GONE);
|
||||
final AppCompatButton btnSaveTo = (AppCompatButton) holder.findViewById(R.id.btnSaveTo);
|
||||
btnSaveTo.setOnClickListener(v -> {
|
||||
if (onSelectFolderButtonClickListener == null) return;
|
||||
onSelectFolderButtonClickListener.onClick(result -> {
|
||||
if (TextUtils.isEmpty(result)) return;
|
||||
customPathTextView.setText(result);
|
||||
});
|
||||
});
|
||||
public SettingScreen(@StringRes final int titleResId, final NavDirections directions, final boolean loginRequired) {
|
||||
this.titleResId = titleResId;
|
||||
this.directions = directions;
|
||||
this.loginRequired = loginRequired;
|
||||
}
|
||||
|
||||
public interface ResultCallback {
|
||||
void onResult(String result);
|
||||
public int getTitleResId() {
|
||||
return titleResId;
|
||||
}
|
||||
|
||||
public interface OnSelectFolderButtonClickListener {
|
||||
void onClick(ResultCallback resultCallback);
|
||||
public NavDirections getDirections() {
|
||||
return directions;
|
||||
}
|
||||
|
||||
public boolean isLoginRequired() {
|
||||
return loginRequired;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,48 @@
|
||||
package awais.instagrabber.fragments.settings;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.SwitchPreferenceCompat;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
|
||||
public class StoriesPreferencesFragment extends BasePreferencesFragment {
|
||||
@Override
|
||||
void setupPreferenceScreen(final PreferenceScreen screen) {
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
screen.addPreference(getStorySortPreference(context));
|
||||
screen.addPreference(getMarkStoriesSeenPreference(context));
|
||||
}
|
||||
|
||||
private Preference getStorySortPreference(@NonNull final Context context) {
|
||||
final ListPreference preference = new ListPreference(context);
|
||||
preference.setSummaryProvider(ListPreference.SimpleSummaryProvider.getInstance());
|
||||
final int length = getResources().getStringArray(R.array.story_sorts).length;
|
||||
final String[] values = new String[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
values[i] = String.valueOf(i);
|
||||
}
|
||||
preference.setKey(Constants.STORY_SORT);
|
||||
preference.setTitle(R.string.story_sort_setting);
|
||||
preference.setDialogTitle(R.string.story_sort_setting);
|
||||
preference.setEntries(R.array.story_sorts);
|
||||
preference.setIconSpaceReserved(false);
|
||||
preference.setEntryValues(values);
|
||||
return preference;
|
||||
}
|
||||
|
||||
private Preference getMarkStoriesSeenPreference(@NonNull final Context context) {
|
||||
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);
|
||||
preference.setKey(Constants.MARK_AS_SEEN);
|
||||
preference.setTitle(R.string.mark_as_seen_setting);
|
||||
preference.setSummary(R.string.mark_as_seen_setting_summary);
|
||||
preference.setIconSpaceReserved(false);
|
||||
return preference;
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
package awais.instagrabber.managers;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
||||
|
||||
public final class DirectMessagesManager {
|
||||
private static final String TAG = DirectMessagesManager.class.getSimpleName();
|
||||
private static final Object LOCK = new Object();
|
||||
|
||||
private static DirectMessagesManager instance;
|
||||
|
||||
private final InboxManager inboxManager;
|
||||
private final InboxManager pendingInboxManager;
|
||||
|
||||
public static DirectMessagesManager getInstance() {
|
||||
if (instance == null) {
|
||||
synchronized (LOCK) {
|
||||
if (instance == null) {
|
||||
instance = new DirectMessagesManager();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private DirectMessagesManager() {
|
||||
inboxManager = InboxManager.getInstance(false);
|
||||
pendingInboxManager = InboxManager.getInstance(true);
|
||||
}
|
||||
|
||||
public void moveThreadFromPending(@NonNull final String threadId) {
|
||||
final List<DirectThread> pendingThreads = pendingInboxManager.getThreads().getValue();
|
||||
if (pendingThreads == null) return;
|
||||
final int index = Iterables.indexOf(pendingThreads, t -> t.getThreadId().equals(threadId));
|
||||
if (index < 0) return;
|
||||
final DirectThread thread = pendingThreads.get(index);
|
||||
final DirectItem threadFirstDirectItem = thread.getFirstDirectItem();
|
||||
if (threadFirstDirectItem == null) return;
|
||||
final List<DirectThread> threads = inboxManager.getThreads().getValue();
|
||||
int insertIndex = 0;
|
||||
for (final DirectThread tempThread : threads) {
|
||||
final DirectItem firstDirectItem = tempThread.getFirstDirectItem();
|
||||
if (firstDirectItem == null) continue;
|
||||
final long timestamp = firstDirectItem.getTimestamp();
|
||||
if (timestamp < threadFirstDirectItem.getTimestamp()) {
|
||||
break;
|
||||
}
|
||||
insertIndex++;
|
||||
}
|
||||
thread.setPending(false);
|
||||
inboxManager.addThread(thread, insertIndex);
|
||||
pendingInboxManager.removeThread(threadId);
|
||||
final Integer currentTotal = inboxManager.getPendingRequestsTotal().getValue();
|
||||
if (currentTotal == null) return;
|
||||
inboxManager.setPendingRequestsTotal(currentTotal - 1);
|
||||
}
|
||||
|
||||
public InboxManager getInboxManager() {
|
||||
return inboxManager;
|
||||
}
|
||||
|
||||
public InboxManager getPendingInboxManager() {
|
||||
return pendingInboxManager;
|
||||
}
|
||||
|
||||
public ThreadManager getThreadManager(@NonNull final String threadId,
|
||||
final boolean pending,
|
||||
@NonNull final User currentUser,
|
||||
@NonNull final ContentResolver contentResolver) {
|
||||
return ThreadManager.getInstance(threadId, pending, currentUser, contentResolver);
|
||||
}
|
||||
}
|
360
app/src/main/java/awais/instagrabber/managers/InboxManager.java
Normal file
360
app/src/main/java/awais/instagrabber/managers/InboxManager.java
Normal file
@ -0,0 +1,360 @@
|
||||
package awais.instagrabber.managers;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.Transformations;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import awais.instagrabber.models.Resource;
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectBadgeCount;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectInbox;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectInboxResponse;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.CookieUtils;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
import awais.instagrabber.webservices.DirectMessagesService;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
import static androidx.lifecycle.Transformations.distinctUntilChanged;
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public final class InboxManager {
|
||||
private static final String TAG = InboxManager.class.getSimpleName();
|
||||
private static final LoadingCache<String, Object> THREAD_LOCKS = CacheBuilder
|
||||
.newBuilder()
|
||||
.expireAfterAccess(1, TimeUnit.MINUTES) // max lock time ever expected
|
||||
.build(CacheLoader.from(Object::new));
|
||||
private static final Comparator<DirectThread> THREAD_COMPARATOR = (t1, t2) -> {
|
||||
final DirectItem t1FirstDirectItem = t1.getFirstDirectItem();
|
||||
final DirectItem t2FirstDirectItem = t2.getFirstDirectItem();
|
||||
if (t1FirstDirectItem == null && t2FirstDirectItem == null) return 0;
|
||||
if (t1FirstDirectItem == null) return 1;
|
||||
if (t2FirstDirectItem == null) return -1;
|
||||
return Long.compare(t2FirstDirectItem.getTimestamp(), t1FirstDirectItem.getTimestamp());
|
||||
};
|
||||
|
||||
private final MutableLiveData<Resource<DirectInbox>> inbox = new MutableLiveData<>();
|
||||
private final MutableLiveData<Resource<Integer>> unseenCount = new MutableLiveData<>();
|
||||
private final MutableLiveData<Integer> pendingRequestsTotal = new MutableLiveData<>(0);
|
||||
|
||||
private final LiveData<List<DirectThread>> threads;
|
||||
private final DirectMessagesService service;
|
||||
private final boolean pending;
|
||||
|
||||
private Call<DirectInboxResponse> inboxRequest;
|
||||
private Call<DirectBadgeCount> unseenCountRequest;
|
||||
private long seqId;
|
||||
private String cursor;
|
||||
private boolean hasOlder = true;
|
||||
private User viewer;
|
||||
|
||||
@NonNull
|
||||
public static InboxManager getInstance(final boolean pending) {
|
||||
return new InboxManager(pending);
|
||||
}
|
||||
|
||||
private InboxManager(final boolean pending) {
|
||||
this.pending = pending;
|
||||
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||
final long userId = CookieUtils.getUserIdFromCookie(cookie);
|
||||
final String deviceUuid = settingsHelper.getString(Constants.DEVICE_UUID);
|
||||
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
|
||||
if (TextUtils.isEmpty(csrfToken) || userId <= 0 || TextUtils.isEmpty(deviceUuid)) {
|
||||
throw new IllegalArgumentException("User is not logged in!");
|
||||
}
|
||||
service = DirectMessagesService.getInstance(csrfToken, userId, deviceUuid);
|
||||
|
||||
// Transformations
|
||||
threads = distinctUntilChanged(Transformations.map(inbox, inboxResource -> {
|
||||
if (inboxResource == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final DirectInbox inbox = inboxResource.data;
|
||||
if (inbox == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return ImmutableList.sortedCopyOf(THREAD_COMPARATOR, inbox.getThreads());
|
||||
}));
|
||||
|
||||
fetchInbox();
|
||||
if (!pending) {
|
||||
fetchUnseenCount();
|
||||
}
|
||||
}
|
||||
|
||||
public LiveData<Resource<DirectInbox>> getInbox() {
|
||||
return distinctUntilChanged(inbox);
|
||||
}
|
||||
|
||||
public LiveData<List<DirectThread>> getThreads() {
|
||||
return threads;
|
||||
}
|
||||
|
||||
public LiveData<Resource<Integer>> getUnseenCount() {
|
||||
return distinctUntilChanged(unseenCount);
|
||||
}
|
||||
|
||||
public LiveData<Integer> getPendingRequestsTotal() {
|
||||
return distinctUntilChanged(pendingRequestsTotal);
|
||||
}
|
||||
|
||||
public User getViewer() {
|
||||
return viewer;
|
||||
}
|
||||
|
||||
public void fetchInbox() {
|
||||
final Resource<DirectInbox> inboxResource = inbox.getValue();
|
||||
if ((inboxResource != null && inboxResource.status == Resource.Status.LOADING) || !hasOlder) return;
|
||||
stopCurrentInboxRequest();
|
||||
inbox.postValue(Resource.loading(getCurrentDirectInbox()));
|
||||
inboxRequest = pending ? service.fetchPendingInbox(cursor, seqId) : service.fetchInbox(cursor, seqId);
|
||||
inboxRequest.enqueue(new Callback<DirectInboxResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<DirectInboxResponse> call, @NonNull final Response<DirectInboxResponse> response) {
|
||||
parseInboxResponse(response.body());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Call<DirectInboxResponse> call, @NonNull final Throwable t) {
|
||||
Log.e(TAG, "Failed fetching dm inbox", t);
|
||||
inbox.postValue(Resource.error(t.getMessage(), getCurrentDirectInbox()));
|
||||
hasOlder = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void fetchUnseenCount() {
|
||||
final Resource<Integer> unseenCountResource = unseenCount.getValue();
|
||||
if ((unseenCountResource != null && unseenCountResource.status == Resource.Status.LOADING)) return;
|
||||
stopCurrentUnseenCountRequest();
|
||||
unseenCount.postValue(Resource.loading(getCurrentUnseenCount()));
|
||||
unseenCountRequest = service.fetchUnseenCount();
|
||||
unseenCountRequest.enqueue(new Callback<DirectBadgeCount>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<DirectBadgeCount> call, @NonNull final Response<DirectBadgeCount> response) {
|
||||
final DirectBadgeCount directBadgeCount = response.body();
|
||||
if (directBadgeCount == null) {
|
||||
Log.e(TAG, "onResponse: directBadgeCount Response is null");
|
||||
unseenCount.postValue(Resource.error("Unseen count response is null", getCurrentUnseenCount()));
|
||||
return;
|
||||
}
|
||||
unseenCount.postValue(Resource.success(directBadgeCount.getBadgeCount()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Call<DirectBadgeCount> call, @NonNull final Throwable t) {
|
||||
Log.e(TAG, "Failed fetching unseen count", t);
|
||||
unseenCount.postValue(Resource.error(t.getMessage(), getCurrentUnseenCount()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
cursor = null;
|
||||
seqId = 0;
|
||||
hasOlder = true;
|
||||
fetchInbox();
|
||||
if (!pending) {
|
||||
fetchUnseenCount();
|
||||
}
|
||||
}
|
||||
|
||||
private DirectInbox getCurrentDirectInbox() {
|
||||
final Resource<DirectInbox> inboxResource = inbox.getValue();
|
||||
return inboxResource != null ? inboxResource.data : null;
|
||||
}
|
||||
|
||||
private void parseInboxResponse(final DirectInboxResponse response) {
|
||||
if (response == null) {
|
||||
Log.e(TAG, "parseInboxResponse: Response is null");
|
||||
inbox.postValue(Resource.error("Response is null", getCurrentDirectInbox()));
|
||||
hasOlder = false;
|
||||
return;
|
||||
}
|
||||
if (!response.getStatus().equals("ok")) {
|
||||
final String msg = "DM inbox fetch response: status not ok";
|
||||
Log.e(TAG, msg);
|
||||
inbox.postValue(Resource.error(msg, getCurrentDirectInbox()));
|
||||
hasOlder = false;
|
||||
return;
|
||||
}
|
||||
seqId = response.getSeqId();
|
||||
if (viewer == null) {
|
||||
viewer = response.getViewer();
|
||||
}
|
||||
final DirectInbox inbox = response.getInbox();
|
||||
if (!TextUtils.isEmpty(cursor)) {
|
||||
final DirectInbox currentDirectInbox = getCurrentDirectInbox();
|
||||
if (currentDirectInbox != null) {
|
||||
List<DirectThread> threads = currentDirectInbox.getThreads();
|
||||
threads = threads == null ? new LinkedList<>() : new LinkedList<>(threads);
|
||||
threads.addAll(inbox.getThreads());
|
||||
inbox.setThreads(threads);
|
||||
}
|
||||
}
|
||||
this.inbox.postValue(Resource.success(inbox));
|
||||
cursor = inbox.getOldestCursor();
|
||||
hasOlder = inbox.hasOlder();
|
||||
pendingRequestsTotal.postValue(response.getPendingRequestsTotal());
|
||||
}
|
||||
|
||||
public void setThread(@NonNull final String threadId,
|
||||
@NonNull final DirectThread thread) {
|
||||
final DirectInbox inbox = getCurrentDirectInbox();
|
||||
if (inbox == null) return;
|
||||
final int index = getThreadIndex(threadId, inbox);
|
||||
setThread(inbox, index, thread);
|
||||
}
|
||||
|
||||
private void setThread(@NonNull final DirectInbox inbox,
|
||||
final int index,
|
||||
@NonNull final DirectThread thread) {
|
||||
if (index < 0) return;
|
||||
synchronized (this.inbox) {
|
||||
final List<DirectThread> threadsCopy = new LinkedList<>(inbox.getThreads());
|
||||
threadsCopy.set(index, thread);
|
||||
try {
|
||||
final DirectInbox clone = (DirectInbox) inbox.clone();
|
||||
clone.setThreads(threadsCopy);
|
||||
this.inbox.postValue(Resource.success(clone));
|
||||
} catch (CloneNotSupportedException e) {
|
||||
Log.e(TAG, "setThread: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addItemsToThread(@NonNull final String threadId,
|
||||
final int insertIndex,
|
||||
@NonNull final Collection<DirectItem> items) {
|
||||
final DirectInbox inbox = getCurrentDirectInbox();
|
||||
if (inbox == null) return;
|
||||
synchronized (THREAD_LOCKS.getUnchecked(threadId)) {
|
||||
final int index = getThreadIndex(threadId, inbox);
|
||||
if (index < 0) return;
|
||||
final List<DirectThread> threads = inbox.getThreads();
|
||||
final DirectThread thread = threads.get(index);
|
||||
List<DirectItem> list = thread.getItems();
|
||||
list = list == null ? new LinkedList<>() : new LinkedList<>(list);
|
||||
if (insertIndex >= 0) {
|
||||
list.addAll(insertIndex, items);
|
||||
} else {
|
||||
list.addAll(items);
|
||||
}
|
||||
try {
|
||||
final DirectThread threadClone = (DirectThread) thread.clone();
|
||||
threadClone.setItems(list);
|
||||
setThread(inbox, index, threadClone);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "addItemsToThread: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setItemsToThread(@NonNull final String threadId,
|
||||
@NonNull final List<DirectItem> updatedItems) {
|
||||
final DirectInbox inbox = getCurrentDirectInbox();
|
||||
if (inbox == null) return;
|
||||
synchronized (THREAD_LOCKS.getUnchecked(threadId)) {
|
||||
final int index = getThreadIndex(threadId, inbox);
|
||||
if (index < 0) return;
|
||||
final List<DirectThread> threads = inbox.getThreads();
|
||||
final DirectThread thread = threads.get(index);
|
||||
thread.setItems(updatedItems);
|
||||
setThread(inbox, index, thread);
|
||||
}
|
||||
}
|
||||
|
||||
private int getThreadIndex(@NonNull final String threadId,
|
||||
@NonNull final DirectInbox inbox) {
|
||||
final List<DirectThread> threads = inbox.getThreads();
|
||||
if (threads == null || threads.isEmpty()) {
|
||||
return -1;
|
||||
}
|
||||
return Iterables.indexOf(threads, t -> {
|
||||
if (t == null) return false;
|
||||
return t.getThreadId().equals(threadId);
|
||||
});
|
||||
}
|
||||
|
||||
private Integer getCurrentUnseenCount() {
|
||||
final Resource<Integer> unseenCountResource = unseenCount.getValue();
|
||||
return unseenCountResource != null ? unseenCountResource.data : null;
|
||||
}
|
||||
|
||||
private void stopCurrentInboxRequest() {
|
||||
if (inboxRequest == null || inboxRequest.isCanceled() || inboxRequest.isExecuted()) return;
|
||||
inboxRequest.cancel();
|
||||
inboxRequest = null;
|
||||
}
|
||||
|
||||
private void stopCurrentUnseenCountRequest() {
|
||||
if (unseenCountRequest == null || unseenCountRequest.isCanceled() || unseenCountRequest.isExecuted()) return;
|
||||
unseenCountRequest.cancel();
|
||||
unseenCountRequest = null;
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
stopCurrentInboxRequest();
|
||||
stopCurrentUnseenCountRequest();
|
||||
}
|
||||
|
||||
public void addThread(@NonNull final DirectThread thread, final int insertIndex) {
|
||||
if (insertIndex < 0) return;
|
||||
synchronized (this.inbox) {
|
||||
final DirectInbox currentDirectInbox = getCurrentDirectInbox();
|
||||
if (currentDirectInbox == null) return;
|
||||
final List<DirectThread> threadsCopy = new LinkedList<>(currentDirectInbox.getThreads());
|
||||
threadsCopy.add(insertIndex, thread);
|
||||
try {
|
||||
final DirectInbox clone = (DirectInbox) currentDirectInbox.clone();
|
||||
clone.setThreads(threadsCopy);
|
||||
this.inbox.setValue(Resource.success(clone));
|
||||
} catch (CloneNotSupportedException e) {
|
||||
Log.e(TAG, "setThread: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeThread(@NonNull final String threadId) {
|
||||
synchronized (this.inbox) {
|
||||
final DirectInbox currentDirectInbox = getCurrentDirectInbox();
|
||||
if (currentDirectInbox == null) return;
|
||||
final List<DirectThread> threadsCopy = currentDirectInbox.getThreads()
|
||||
.stream()
|
||||
.filter(t -> !t.getThreadId().equals(threadId))
|
||||
.collect(Collectors.toList());
|
||||
try {
|
||||
final DirectInbox clone = (DirectInbox) currentDirectInbox.clone();
|
||||
clone.setThreads(threadsCopy);
|
||||
this.inbox.postValue(Resource.success(clone));
|
||||
} catch (CloneNotSupportedException e) {
|
||||
Log.e(TAG, "setThread: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setPendingRequestsTotal(final int total) {
|
||||
pendingRequestsTotal.postValue(total);
|
||||
}
|
||||
}
|
1798
app/src/main/java/awais/instagrabber/managers/ThreadManager.java
Normal file
1798
app/src/main/java/awais/instagrabber/managers/ThreadManager.java
Normal file
File diff suppressed because it is too large
Load Diff
@ -3,6 +3,8 @@ package awais.instagrabber.models;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class Resource<T> {
|
||||
public final Status status;
|
||||
public final T data;
|
||||
@ -31,6 +33,21 @@ public class Resource<T> {
|
||||
return new Resource<>(Status.LOADING, data, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final Resource<?> resource = (Resource<?>) o;
|
||||
return status == resource.status &&
|
||||
Objects.equals(data, resource.data) &&
|
||||
Objects.equals(message, resource.message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(status, data, message);
|
||||
}
|
||||
|
||||
public enum Status {
|
||||
SUCCESS,
|
||||
ERROR,
|
||||
|
@ -7,7 +7,8 @@ public enum BroadcastItemType {
|
||||
IMAGE("configure_photo"),
|
||||
LINK("link"),
|
||||
VIDEO("configure_video"),
|
||||
VOICE("share_voice");
|
||||
VOICE("share_voice"),
|
||||
ANIMATED_MEDIA("animated_media");
|
||||
|
||||
private final String value;
|
||||
|
||||
|
@ -0,0 +1,14 @@
|
||||
package awais.instagrabber.repositories;
|
||||
|
||||
import awais.instagrabber.repositories.responses.giphy.GiphyGifResponse;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
public interface GifRepository {
|
||||
|
||||
@GET("/api/v1/creatives/story_media_search_keyed_format/")
|
||||
Call<GiphyGifResponse> searchGiphyGifs(@Query("request_surface") final String requestSurface,
|
||||
@Query("q") final String query,
|
||||
@Query("media_types") final String mediaTypes);
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package awais.instagrabber.repositories.requests.directmessages;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import awais.instagrabber.models.enums.BroadcastItemType;
|
||||
import awais.instagrabber.repositories.responses.giphy.GiphyGif;
|
||||
|
||||
public class AnimatedMediaBroadcastOptions extends BroadcastOptions {
|
||||
|
||||
private final GiphyGif giphyGif;
|
||||
|
||||
public AnimatedMediaBroadcastOptions(final String clientContext,
|
||||
final ThreadIdOrUserIds threadIdOrUserIds,
|
||||
final GiphyGif giphyGif) {
|
||||
super(clientContext, threadIdOrUserIds, BroadcastItemType.ANIMATED_MEDIA);
|
||||
this.giphyGif = giphyGif;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getFormMap() {
|
||||
final Map<String, String> form = new HashMap<>();
|
||||
form.put("is_sticker", String.valueOf(giphyGif.isSticker()));
|
||||
form.put("id", giphyGif.getId());
|
||||
return form;
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package awais.instagrabber.repositories.responses;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class AnimatedMediaFixedHeight {
|
||||
private final int height;
|
||||
private final int width;
|
||||
@ -34,4 +36,21 @@ public class AnimatedMediaFixedHeight {
|
||||
public String getWebp() {
|
||||
return webp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final AnimatedMediaFixedHeight that = (AnimatedMediaFixedHeight) o;
|
||||
return height == that.height &&
|
||||
width == that.width &&
|
||||
Objects.equals(mp4, that.mp4) &&
|
||||
Objects.equals(url, that.url) &&
|
||||
Objects.equals(webp, that.webp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(height, width, mp4, url, webp);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package awais.instagrabber.repositories.responses;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class AnimatedMediaImages {
|
||||
private final AnimatedMediaFixedHeight fixedHeight;
|
||||
|
||||
@ -10,4 +12,17 @@ public class AnimatedMediaImages {
|
||||
public AnimatedMediaFixedHeight getFixedHeight() {
|
||||
return fixedHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final AnimatedMediaImages that = (AnimatedMediaImages) o;
|
||||
return Objects.equals(fixedHeight, that.fixedHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(fixedHeight);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package awais.instagrabber.repositories.responses;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class Audio implements Serializable {
|
||||
private final String audioSrc;
|
||||
@ -41,4 +42,21 @@ public class Audio implements Serializable {
|
||||
public long getAudioSrcExpirationTimestampUs() {
|
||||
return audioSrcExpirationTimestampUs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final Audio audio = (Audio) o;
|
||||
return duration == audio.duration &&
|
||||
waveformSamplingFrequencyHz == audio.waveformSamplingFrequencyHz &&
|
||||
audioSrcExpirationTimestampUs == audio.audioSrcExpirationTimestampUs &&
|
||||
Objects.equals(audioSrc, audio.audioSrc) &&
|
||||
Objects.equals(waveformData, audio.waveformData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(audioSrc, duration, waveformData, waveformSamplingFrequencyHz, audioSrcExpirationTimestampUs);
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import com.google.gson.JsonParseException;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Objects;
|
||||
|
||||
public class Caption implements Serializable {
|
||||
private long mPk;
|
||||
@ -42,6 +43,21 @@ public class Caption implements Serializable {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final Caption caption = (Caption) o;
|
||||
return mPk == caption.mPk &&
|
||||
userId == caption.userId &&
|
||||
Objects.equals(text, caption.text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mPk, userId, text);
|
||||
}
|
||||
|
||||
public static class CaptionDeserializer implements JsonDeserializer<Caption> {
|
||||
|
||||
private static final String TAG = CaptionDeserializer.class.getSimpleName();
|
||||
|
@ -1,6 +1,7 @@
|
||||
package awais.instagrabber.repositories.responses;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
public class EndOfFeedDemarcator implements Serializable {
|
||||
private final long id;
|
||||
@ -18,4 +19,18 @@ public class EndOfFeedDemarcator implements Serializable {
|
||||
public EndOfFeedGroupSet getGroupSet() {
|
||||
return groupSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final EndOfFeedDemarcator that = (EndOfFeedDemarcator) o;
|
||||
return id == that.id &&
|
||||
Objects.equals(groupSet, that.groupSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, groupSet);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package awais.instagrabber.repositories.responses;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class EndOfFeedGroup implements Serializable {
|
||||
private final String id;
|
||||
@ -31,4 +32,20 @@ public class EndOfFeedGroup implements Serializable {
|
||||
public List<Media> getFeedItems() {
|
||||
return feedItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final EndOfFeedGroup that = (EndOfFeedGroup) o;
|
||||
return Objects.equals(id, that.id) &&
|
||||
Objects.equals(title, that.title) &&
|
||||
Objects.equals(nextMaxId, that.nextMaxId) &&
|
||||
Objects.equals(feedItems, that.feedItems);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, title, nextMaxId, feedItems);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package awais.instagrabber.repositories.responses;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class EndOfFeedGroupSet implements Serializable {
|
||||
private final long id;
|
||||
@ -48,4 +49,22 @@ public class EndOfFeedGroupSet implements Serializable {
|
||||
public List<EndOfFeedGroup> getGroups() {
|
||||
return groups;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final EndOfFeedGroupSet that = (EndOfFeedGroupSet) o;
|
||||
return id == that.id &&
|
||||
Objects.equals(activeGroupId, that.activeGroupId) &&
|
||||
Objects.equals(connectedGroupId, that.connectedGroupId) &&
|
||||
Objects.equals(nextMaxId, that.nextMaxId) &&
|
||||
Objects.equals(paginationSource, that.paginationSource) &&
|
||||
Objects.equals(groups, that.groups);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, activeGroupId, connectedGroupId, nextMaxId, paginationSource, groups);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package awais.instagrabber.repositories.responses;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
public class FriendshipStatus implements Serializable {
|
||||
private final boolean following;
|
||||
@ -78,6 +79,29 @@ public class FriendshipStatus implements Serializable {
|
||||
return isMutingReel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final FriendshipStatus that = (FriendshipStatus) o;
|
||||
return following == that.following &&
|
||||
followedBy == that.followedBy &&
|
||||
blocking == that.blocking &&
|
||||
muting == that.muting &&
|
||||
isPrivate == that.isPrivate &&
|
||||
incomingRequest == that.incomingRequest &&
|
||||
outgoingRequest == that.outgoingRequest &&
|
||||
isBestie == that.isBestie &&
|
||||
isRestricted == that.isRestricted &&
|
||||
isMutingReel == that.isMutingReel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(following, followedBy, blocking, muting, isPrivate, incomingRequest, outgoingRequest, isBestie, isRestricted,
|
||||
isMutingReel);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
|
@ -2,6 +2,7 @@ package awais.instagrabber.repositories.responses;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ImageVersions2 implements Serializable {
|
||||
private final List<MediaCandidate> candidates;
|
||||
@ -13,4 +14,17 @@ public class ImageVersions2 implements Serializable {
|
||||
public List<MediaCandidate> getCandidates() {
|
||||
return candidates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final ImageVersions2 that = (ImageVersions2) o;
|
||||
return Objects.equals(candidates, that.candidates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(candidates);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package awais.instagrabber.repositories.responses;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
public class Location implements Serializable {
|
||||
private final long pk;
|
||||
@ -54,4 +55,23 @@ public class Location implements Serializable {
|
||||
public float getLat() {
|
||||
return lat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final Location location = (Location) o;
|
||||
return pk == location.pk &&
|
||||
Float.compare(location.lng, lng) == 0 &&
|
||||
Float.compare(location.lat, lat) == 0 &&
|
||||
Objects.equals(shortName, location.shortName) &&
|
||||
Objects.equals(name, location.name) &&
|
||||
Objects.equals(address, location.address) &&
|
||||
Objects.equals(city, location.city);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(pk, shortName, name, address, city, lng, lat);
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import awais.instagrabber.models.enums.MediaItemType;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
@ -272,4 +273,52 @@ public class Media implements Serializable {
|
||||
final Caption caption1 = getCaption();
|
||||
caption1.setText(caption);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final Media media = (Media) o;
|
||||
return takenAt == media.takenAt &&
|
||||
canViewerReshare == media.canViewerReshare &&
|
||||
commentLikesEnabled == media.commentLikesEnabled &&
|
||||
commentsDisabled == media.commentsDisabled &&
|
||||
nextMaxId == media.nextMaxId &&
|
||||
commentCount == media.commentCount &&
|
||||
originalWidth == media.originalWidth &&
|
||||
originalHeight == media.originalHeight &&
|
||||
likeCount == media.likeCount &&
|
||||
hasLiked == media.hasLiked &&
|
||||
isReelMedia == media.isReelMedia &&
|
||||
hasAudio == media.hasAudio &&
|
||||
Double.compare(media.videoDuration, videoDuration) == 0 &&
|
||||
viewCount == media.viewCount &&
|
||||
canViewerSave == media.canViewerSave &&
|
||||
isSidecarChild == media.isSidecarChild &&
|
||||
hasViewerSaved == media.hasViewerSaved &&
|
||||
Objects.equals(pk, media.pk) &&
|
||||
Objects.equals(id, media.id) &&
|
||||
Objects.equals(code, media.code) &&
|
||||
Objects.equals(user, media.user) &&
|
||||
mediaType == media.mediaType &&
|
||||
Objects.equals(imageVersions2, media.imageVersions2) &&
|
||||
Objects.equals(videoVersions, media.videoVersions) &&
|
||||
Objects.equals(caption, media.caption) &&
|
||||
Objects.equals(audio, media.audio) &&
|
||||
Objects.equals(title, media.title) &&
|
||||
Objects.equals(location, media.location) &&
|
||||
Objects.equals(usertags, media.usertags) &&
|
||||
Objects.equals(carouselMedia, media.carouselMedia) &&
|
||||
Objects.equals(injected, media.injected) &&
|
||||
Objects.equals(endOfFeedDemarcator, media.endOfFeedDemarcator) &&
|
||||
Objects.equals(dateString, media.dateString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(pk, id, code, takenAt, user, mediaType, canViewerReshare, commentLikesEnabled, commentsDisabled, nextMaxId, commentCount,
|
||||
imageVersions2, originalWidth, originalHeight, likeCount, hasLiked, isReelMedia, videoVersions, hasAudio, videoDuration,
|
||||
viewCount, caption, canViewerSave, audio, title, location, usertags, carouselMedia, isSidecarChild, hasViewerSaved,
|
||||
injected, endOfFeedDemarcator, dateString);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package awais.instagrabber.repositories.responses;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
public class MediaCandidate implements Serializable {
|
||||
private final int width;
|
||||
@ -24,4 +25,19 @@ public class MediaCandidate implements Serializable {
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final MediaCandidate that = (MediaCandidate) o;
|
||||
return width == that.width &&
|
||||
height == that.height &&
|
||||
Objects.equals(url, that.url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(width, height, url);
|
||||
}
|
||||
}
|
||||
|
@ -194,17 +194,40 @@ public class User implements Serializable {
|
||||
return profileContextLinksWithUserIds;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final User that = (User) o;
|
||||
return pk == that.pk &&
|
||||
Objects.equals(username, that.username);
|
||||
final User user = (User) o;
|
||||
return pk == user.pk &&
|
||||
isPrivate == user.isPrivate &&
|
||||
isVerified == user.isVerified &&
|
||||
hasAnonymousProfilePicture == user.hasAnonymousProfilePicture &&
|
||||
isUnpublished == user.isUnpublished &&
|
||||
isFavorite == user.isFavorite &&
|
||||
isDirectappInstalled == user.isDirectappInstalled &&
|
||||
mediaCount == user.mediaCount &&
|
||||
followerCount == user.followerCount &&
|
||||
followingCount == user.followingCount &&
|
||||
followingTagCount == user.followingTagCount &&
|
||||
usertagsCount == user.usertagsCount &&
|
||||
Objects.equals(username, user.username) &&
|
||||
Objects.equals(fullName, user.fullName) &&
|
||||
Objects.equals(profilePicUrl, user.profilePicUrl) &&
|
||||
Objects.equals(profilePicId, user.profilePicId) &&
|
||||
Objects.equals(friendshipStatus, user.friendshipStatus) &&
|
||||
Objects.equals(reelAutoArchive, user.reelAutoArchive) &&
|
||||
Objects.equals(allowedCommenterType, user.allowedCommenterType) &&
|
||||
Objects.equals(biography, user.biography) &&
|
||||
Objects.equals(externalUrl, user.externalUrl) &&
|
||||
Objects.equals(publicEmail, user.publicEmail);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(pk, username);
|
||||
return Objects.hash(pk, username, fullName, isPrivate, profilePicUrl, profilePicId, friendshipStatus, isVerified, hasAnonymousProfilePicture,
|
||||
isUnpublished, isFavorite, isDirectappInstalled, reelAutoArchive, allowedCommenterType, mediaCount, followerCount,
|
||||
followingCount, followingTagCount, biography, externalUrl, usertagsCount, publicEmail);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package awais.instagrabber.repositories.responses;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class UsertagIn implements Serializable {
|
||||
private final User user;
|
||||
@ -19,4 +20,18 @@ public class UsertagIn implements Serializable {
|
||||
public List<String> getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final UsertagIn usertagIn = (UsertagIn) o;
|
||||
return Objects.equals(user, usertagIn.user) &&
|
||||
Objects.equals(position, usertagIn.position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(user, position);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package awais.instagrabber.repositories.responses;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class Usertags implements Serializable {
|
||||
private final List<UsertagIn> in;
|
||||
@ -13,4 +14,17 @@ public class Usertags implements Serializable {
|
||||
public List<UsertagIn> getIn() {
|
||||
return in;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final Usertags usertags = (Usertags) o;
|
||||
return Objects.equals(in, usertags.in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(in);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package awais.instagrabber.repositories.responses;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
public class VideoVersion implements Serializable {
|
||||
private final String id;
|
||||
@ -36,4 +37,21 @@ public class VideoVersion implements Serializable {
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final VideoVersion that = (VideoVersion) o;
|
||||
return width == that.width &&
|
||||
height == that.height &&
|
||||
Objects.equals(id, that.id) &&
|
||||
Objects.equals(type, that.type) &&
|
||||
Objects.equals(url, that.url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, type, width, height, url);
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
package awais.instagrabber.repositories.responses.directmessages;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DirectInbox {
|
||||
private final List<DirectThread> threads;
|
||||
public class DirectInbox implements Cloneable {
|
||||
private List<DirectThread> threads;
|
||||
private final boolean hasOlder;
|
||||
private final int unseenCount;
|
||||
private final String unseenCountTs;
|
||||
@ -28,6 +30,10 @@ public class DirectInbox {
|
||||
return threads;
|
||||
}
|
||||
|
||||
public void setThreads(final List<DirectThread> threads) {
|
||||
this.threads = threads;
|
||||
}
|
||||
|
||||
public boolean hasOlder() {
|
||||
return hasOlder;
|
||||
}
|
||||
@ -47,4 +53,10 @@ public class DirectInbox {
|
||||
public boolean isBlendedInboxEnabled() {
|
||||
return blendedInboxEnabled;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,12 @@ package awais.instagrabber.repositories.responses.directmessages;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import awais.instagrabber.models.enums.DirectItemType;
|
||||
import awais.instagrabber.repositories.responses.Location;
|
||||
@ -40,6 +44,7 @@ public class DirectItem implements Cloneable {
|
||||
private Date date;
|
||||
private boolean isPending;
|
||||
private boolean showForwardAttribution;
|
||||
private LocalDateTime localDateTime;
|
||||
|
||||
public DirectItem(final String itemId,
|
||||
final long userId,
|
||||
@ -213,6 +218,13 @@ public class DirectItem implements Cloneable {
|
||||
return date;
|
||||
}
|
||||
|
||||
public LocalDateTime getLocalDateTime() {
|
||||
if (localDateTime == null) {
|
||||
localDateTime = Instant.ofEpochMilli(timestamp / 1000).atZone(ZoneId.systemDefault()).toLocalDateTime();;
|
||||
}
|
||||
return localDateTime;
|
||||
}
|
||||
|
||||
public void setItemId(final String itemId) {
|
||||
this.itemId = itemId;
|
||||
}
|
||||
@ -239,21 +251,47 @@ public class DirectItem implements Cloneable {
|
||||
return super.clone();
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public boolean equals(final Object o) {
|
||||
// if (this == o) return true;
|
||||
// if (o == null || getClass() != o.getClass()) return false;
|
||||
// final DirectItem that = (DirectItem) o;
|
||||
// return userId == that.userId &&
|
||||
// timestamp == that.timestamp &&
|
||||
// isPending == that.isPending &&
|
||||
// Objects.equals(itemId, that.itemId) &&
|
||||
// itemType == that.itemType &&
|
||||
// Objects.equals(clientContext, that.clientContext);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public int hashCode() {
|
||||
// return Objects.hash(itemId, userId, timestamp, itemType, clientContext, isPending);
|
||||
// }
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final DirectItem that = (DirectItem) o;
|
||||
return userId == that.userId &&
|
||||
timestamp == that.timestamp &&
|
||||
hideInThread == that.hideInThread &&
|
||||
isPending == that.isPending &&
|
||||
showForwardAttribution == that.showForwardAttribution &&
|
||||
Objects.equals(itemId, that.itemId) &&
|
||||
itemType == that.itemType &&
|
||||
Objects.equals(text, that.text) &&
|
||||
Objects.equals(like, that.like) &&
|
||||
Objects.equals(link, that.link) &&
|
||||
Objects.equals(clientContext, that.clientContext) &&
|
||||
Objects.equals(reelShare, that.reelShare) &&
|
||||
Objects.equals(storyShare, that.storyShare) &&
|
||||
Objects.equals(mediaShare, that.mediaShare) &&
|
||||
Objects.equals(profile, that.profile) &&
|
||||
Objects.equals(placeholder, that.placeholder) &&
|
||||
Objects.equals(media, that.media) &&
|
||||
Objects.equals(previewMedias, that.previewMedias) &&
|
||||
Objects.equals(actionLog, that.actionLog) &&
|
||||
Objects.equals(videoCallEvent, that.videoCallEvent) &&
|
||||
Objects.equals(clip, that.clip) &&
|
||||
Objects.equals(felixShare, that.felixShare) &&
|
||||
Objects.equals(visualMedia, that.visualMedia) &&
|
||||
Objects.equals(animatedMedia, that.animatedMedia) &&
|
||||
Objects.equals(reactions, that.reactions) &&
|
||||
Objects.equals(repliedToMessage, that.repliedToMessage) &&
|
||||
Objects.equals(voiceMedia, that.voiceMedia) &&
|
||||
Objects.equals(location, that.location) &&
|
||||
Objects.equals(date, that.date);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects
|
||||
.hash(itemId, userId, timestamp, itemType, text, like, link, clientContext, reelShare, storyShare, mediaShare, profile, placeholder,
|
||||
media, previewMedias, actionLog, videoCallEvent, clip, felixShare, visualMedia, animatedMedia, reactions, repliedToMessage,
|
||||
voiceMedia, location, hideInThread, date, isPending, showForwardAttribution);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package awais.instagrabber.repositories.responses.directmessages;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class DirectItemActionLog {
|
||||
private final String description;
|
||||
@ -27,6 +28,21 @@ public class DirectItemActionLog {
|
||||
return textAttributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final DirectItemActionLog that = (DirectItemActionLog) o;
|
||||
return Objects.equals(description, that.description) &&
|
||||
Objects.equals(bold, that.bold) &&
|
||||
Objects.equals(textAttributes, that.textAttributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(description, bold, textAttributes);
|
||||
}
|
||||
|
||||
public static class TextRange {
|
||||
private final int start;
|
||||
private final int end;
|
||||
@ -55,5 +71,21 @@ public class DirectItemActionLog {
|
||||
public String getIntent() {
|
||||
return intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final TextRange textRange = (TextRange) o;
|
||||
return start == textRange.start &&
|
||||
end == textRange.end &&
|
||||
Objects.equals(color, textRange.color) &&
|
||||
Objects.equals(intent, textRange.intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(start, end, color, intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package awais.instagrabber.repositories.responses.directmessages;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import awais.instagrabber.repositories.responses.AnimatedMediaImages;
|
||||
|
||||
public final class DirectItemAnimatedMedia {
|
||||
@ -31,4 +33,20 @@ public final class DirectItemAnimatedMedia {
|
||||
public boolean isSticker() {
|
||||
return isSticker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final DirectItemAnimatedMedia that = (DirectItemAnimatedMedia) o;
|
||||
return isRandom == that.isRandom &&
|
||||
isSticker == that.isSticker &&
|
||||
Objects.equals(id, that.id) &&
|
||||
Objects.equals(images, that.images);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, images, isRandom, isSticker);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package awais.instagrabber.repositories.responses.directmessages;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import awais.instagrabber.repositories.responses.Media;
|
||||
|
||||
public class DirectItemClip {
|
||||
@ -12,4 +14,17 @@ public class DirectItemClip {
|
||||
public Media getClip() {
|
||||
return clip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final DirectItemClip that = (DirectItemClip) o;
|
||||
return Objects.equals(clip, that.clip);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(clip);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package awais.instagrabber.repositories.responses.directmessages;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import awais.instagrabber.repositories.responses.Media;
|
||||
|
||||
public class DirectItemFelixShare {
|
||||
@ -12,4 +14,17 @@ public class DirectItemFelixShare {
|
||||
public Media getVideo() {
|
||||
return video;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final DirectItemFelixShare that = (DirectItemFelixShare) o;
|
||||
return Objects.equals(video, that.video);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(video);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package awais.instagrabber.repositories.responses.directmessages;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class DirectItemLink {
|
||||
private final String text;
|
||||
private final DirectItemLinkContext linkContext;
|
||||
@ -31,4 +33,20 @@ public class DirectItemLink {
|
||||
public String getMutationToken() {
|
||||
return mutationToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final DirectItemLink that = (DirectItemLink) o;
|
||||
return Objects.equals(text, that.text) &&
|
||||
Objects.equals(linkContext, that.linkContext) &&
|
||||
Objects.equals(clientContext, that.clientContext) &&
|
||||
Objects.equals(mutationToken, that.mutationToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(text, linkContext, clientContext, mutationToken);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package awais.instagrabber.repositories.responses.directmessages;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class DirectItemLinkContext {
|
||||
private final String linkUrl;
|
||||
private final String linkTitle;
|
||||
@ -31,4 +33,20 @@ public class DirectItemLinkContext {
|
||||
public String getLinkImageUrl() {
|
||||
return linkImageUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final DirectItemLinkContext that = (DirectItemLinkContext) o;
|
||||
return Objects.equals(linkUrl, that.linkUrl) &&
|
||||
Objects.equals(linkTitle, that.linkTitle) &&
|
||||
Objects.equals(linkSummary, that.linkSummary) &&
|
||||
Objects.equals(linkImageUrl, that.linkImageUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(linkUrl, linkTitle, linkSummary, linkImageUrl);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package awais.instagrabber.repositories.responses.directmessages;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class DirectItemPlaceholder {
|
||||
private final boolean isLinked;
|
||||
private final String title;
|
||||
@ -24,4 +26,19 @@ public class DirectItemPlaceholder {
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final DirectItemPlaceholder that = (DirectItemPlaceholder) o;
|
||||
return isLinked == that.isLinked &&
|
||||
Objects.equals(title, that.title) &&
|
||||
Objects.equals(message, that.message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(isLinked, title, message);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package awais.instagrabber.repositories.responses.directmessages;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import awais.instagrabber.repositories.responses.Media;
|
||||
|
||||
public class DirectItemReelShare {
|
||||
@ -61,4 +63,24 @@ public class DirectItemReelShare {
|
||||
public long getMentionedUserId() {
|
||||
return mentionedUserId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final DirectItemReelShare that = (DirectItemReelShare) o;
|
||||
return reelOwnerId == that.reelOwnerId &&
|
||||
mentionedUserId == that.mentionedUserId &&
|
||||
isReelPersisted == that.isReelPersisted &&
|
||||
Objects.equals(text, that.text) &&
|
||||
Objects.equals(type, that.type) &&
|
||||
Objects.equals(reelType, that.reelType) &&
|
||||
Objects.equals(media, that.media) &&
|
||||
Objects.equals(reactionInfo, that.reactionInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(text, type, reelOwnerId, mentionedUserId, isReelPersisted, reelType, media, reactionInfo);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package awais.instagrabber.repositories.responses.directmessages;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class DirectItemReelShareReactionInfo {
|
||||
private final String emoji;
|
||||
private final String intensity;
|
||||
@ -16,4 +18,18 @@ public class DirectItemReelShareReactionInfo {
|
||||
public String getIntensity() {
|
||||
return intensity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final DirectItemReelShareReactionInfo that = (DirectItemReelShareReactionInfo) o;
|
||||
return Objects.equals(emoji, that.emoji) &&
|
||||
Objects.equals(intensity, that.intensity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(emoji, intensity);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package awais.instagrabber.repositories.responses.directmessages;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import awais.instagrabber.repositories.responses.Media;
|
||||
|
||||
public class DirectItemStoryShare {
|
||||
@ -54,4 +56,23 @@ public class DirectItemStoryShare {
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final DirectItemStoryShare that = (DirectItemStoryShare) o;
|
||||
return isReelPersisted == that.isReelPersisted &&
|
||||
Objects.equals(reelId, that.reelId) &&
|
||||
Objects.equals(reelType, that.reelType) &&
|
||||
Objects.equals(text, that.text) &&
|
||||
Objects.equals(media, that.media) &&
|
||||
Objects.equals(title, that.title) &&
|
||||
Objects.equals(message, that.message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(reelId, reelType, text, isReelPersisted, media, title, message);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package awais.instagrabber.repositories.responses.directmessages;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class DirectItemVideoCallEvent {
|
||||
private final String action;
|
||||
@ -40,4 +41,21 @@ public final class DirectItemVideoCallEvent {
|
||||
public List<DirectItemActionLog.TextRange> getTextAttributes() {
|
||||
return textAttributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final DirectItemVideoCallEvent that = (DirectItemVideoCallEvent) o;
|
||||
return threadHasAudioOnlyCall == that.threadHasAudioOnlyCall &&
|
||||
Objects.equals(action, that.action) &&
|
||||
Objects.equals(encodedServerDataInfo, that.encodedServerDataInfo) &&
|
||||
Objects.equals(description, that.description) &&
|
||||
Objects.equals(textAttributes, that.textAttributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(action, encodedServerDataInfo, description, threadHasAudioOnlyCall, textAttributes);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package awais.instagrabber.repositories.responses.directmessages;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import awais.instagrabber.models.enums.RavenMediaViewMode;
|
||||
import awais.instagrabber.repositories.responses.Media;
|
||||
@ -64,4 +65,25 @@ public class DirectItemVisualMedia {
|
||||
public Media getMedia() {
|
||||
return media;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final DirectItemVisualMedia media1 = (DirectItemVisualMedia) o;
|
||||
return urlExpireAtSecs == media1.urlExpireAtSecs &&
|
||||
playbackDurationSecs == media1.playbackDurationSecs &&
|
||||
seenCount == media1.seenCount &&
|
||||
replayExpiringAtUs == media1.replayExpiringAtUs &&
|
||||
Objects.equals(seenUserIds, media1.seenUserIds) &&
|
||||
viewMode == media1.viewMode &&
|
||||
Objects.equals(expiringMediaActionSummary, media1.expiringMediaActionSummary) &&
|
||||
Objects.equals(media, media1.media);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects
|
||||
.hash(urlExpireAtSecs, playbackDurationSecs, seenUserIds, viewMode, seenCount, replayExpiringAtUs, expiringMediaActionSummary, media);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package awais.instagrabber.repositories.responses.directmessages;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import awais.instagrabber.repositories.responses.Media;
|
||||
|
||||
public class DirectItemVoiceMedia {
|
||||
@ -24,4 +26,19 @@ public class DirectItemVoiceMedia {
|
||||
public String getViewMode() {
|
||||
return viewMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final DirectItemVoiceMedia that = (DirectItemVoiceMedia) o;
|
||||
return seenCount == that.seenCount &&
|
||||
Objects.equals(media, that.media) &&
|
||||
Objects.equals(viewMode, that.viewMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(media, seenCount, viewMode);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package awais.instagrabber.repositories.responses.directmessages;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.io.Serializable;
|
||||
@ -9,13 +10,13 @@ import java.util.Objects;
|
||||
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
|
||||
public class DirectThread implements Serializable {
|
||||
public class DirectThread implements Serializable, Cloneable {
|
||||
private final String threadId;
|
||||
private final String threadV2Id;
|
||||
private final List<User> users;
|
||||
private final List<User> leftUsers;
|
||||
private final List<Long> adminUserIds;
|
||||
private final List<DirectItem> items;
|
||||
private List<User> users;
|
||||
private List<User> leftUsers;
|
||||
private List<Long> adminUserIds;
|
||||
private List<DirectItem> items;
|
||||
private final long lastActivityAt;
|
||||
private boolean muted;
|
||||
private final boolean isPin;
|
||||
@ -127,18 +128,34 @@ public class DirectThread implements Serializable {
|
||||
return users;
|
||||
}
|
||||
|
||||
public void setUsers(final List<User> users) {
|
||||
this.users = users;
|
||||
}
|
||||
|
||||
public List<User> getLeftUsers() {
|
||||
return leftUsers;
|
||||
}
|
||||
|
||||
public void setLeftUsers(final List<User> leftUsers) {
|
||||
this.leftUsers = leftUsers;
|
||||
}
|
||||
|
||||
public List<Long> getAdminUserIds() {
|
||||
return adminUserIds;
|
||||
}
|
||||
|
||||
public void setAdminUserIds(final List<Long> adminUserIds) {
|
||||
this.adminUserIds = adminUserIds;
|
||||
}
|
||||
|
||||
public List<DirectItem> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
public void setItems(final List<DirectItem> items) {
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public long getLastActivityAt() {
|
||||
return lastActivityAt;
|
||||
}
|
||||
@ -284,17 +301,59 @@ public class DirectThread implements Serializable {
|
||||
return firstItem;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final DirectThread that = (DirectThread) o;
|
||||
return Objects.equals(threadId, that.threadId) &&
|
||||
Objects.equals(threadV2Id, that.threadV2Id);
|
||||
return lastActivityAt == that.lastActivityAt &&
|
||||
muted == that.muted &&
|
||||
isPin == that.isPin &&
|
||||
named == that.named &&
|
||||
canonical == that.canonical &&
|
||||
pending == that.pending &&
|
||||
archived == that.archived &&
|
||||
valuedRequest == that.valuedRequest &&
|
||||
viewerId == that.viewerId &&
|
||||
folder == that.folder &&
|
||||
vcMuted == that.vcMuted &&
|
||||
isGroup == that.isGroup &&
|
||||
mentionsMuted == that.mentionsMuted &&
|
||||
hasOlder == that.hasOlder &&
|
||||
hasNewer == that.hasNewer &&
|
||||
isSpam == that.isSpam &&
|
||||
approvalRequiredForNewMembers == that.approvalRequiredForNewMembers &&
|
||||
inputMode == that.inputMode &&
|
||||
Objects.equals(threadId, that.threadId) &&
|
||||
Objects.equals(threadV2Id, that.threadV2Id) &&
|
||||
Objects.equals(users, that.users) &&
|
||||
Objects.equals(leftUsers, that.leftUsers) &&
|
||||
Objects.equals(adminUserIds, that.adminUserIds) &&
|
||||
Objects.equals(items, that.items) &&
|
||||
Objects.equals(threadType, that.threadType) &&
|
||||
Objects.equals(threadTitle, that.threadTitle) &&
|
||||
Objects.equals(pendingScore, that.pendingScore) &&
|
||||
Objects.equals(inviter, that.inviter) &&
|
||||
Objects.equals(lastSeenAt, that.lastSeenAt) &&
|
||||
Objects.equals(newestCursor, that.newestCursor) &&
|
||||
Objects.equals(oldestCursor, that.oldestCursor) &&
|
||||
Objects.equals(lastPermanentItem, that.lastPermanentItem) &&
|
||||
Objects.equals(directStory, that.directStory) &&
|
||||
Objects.equals(threadContextItems, that.threadContextItems);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(threadId, threadV2Id);
|
||||
return Objects
|
||||
.hash(threadId, threadV2Id, users, leftUsers, adminUserIds, items, lastActivityAt, muted, isPin, named, canonical, pending, archived,
|
||||
valuedRequest, threadType, viewerId, threadTitle, pendingScore, folder, vcMuted, isGroup, mentionsMuted, inviter, hasOlder,
|
||||
hasNewer, lastSeenAt, newestCursor, oldestCursor, isSpam, lastPermanentItem, directStory, approvalRequiredForNewMembers,
|
||||
inputMode, threadContextItems);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package awais.instagrabber.repositories.responses.directmessages;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class DirectThreadDirectStory {
|
||||
private final List<DirectItem> items;
|
||||
@ -18,4 +19,18 @@ public class DirectThreadDirectStory {
|
||||
public int getUnseenCount() {
|
||||
return unseenCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final DirectThreadDirectStory that = (DirectThreadDirectStory) o;
|
||||
return unseenCount == that.unseenCount &&
|
||||
Objects.equals(items, that.items);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(items, unseenCount);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package awais.instagrabber.repositories.responses.directmessages;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class DirectThreadLastSeenAt {
|
||||
private final String timestamp;
|
||||
private final String itemId;
|
||||
@ -16,4 +18,18 @@ public class DirectThreadLastSeenAt {
|
||||
public String getItemId() {
|
||||
return itemId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final DirectThreadLastSeenAt that = (DirectThreadLastSeenAt) o;
|
||||
return Objects.equals(timestamp, that.timestamp) &&
|
||||
Objects.equals(itemId, that.itemId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(timestamp, itemId);
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ public class DirectThreadParticipantRequestsResponse implements Serializable, Cl
|
||||
private final Map<Long, String> requesterUsernames;
|
||||
private final String cursor;
|
||||
private final int totalThreadParticipants;
|
||||
private final int totalParticipantRequests;
|
||||
private int totalParticipantRequests;
|
||||
private final String status;
|
||||
|
||||
public DirectThreadParticipantRequestsResponse(final List<User> users,
|
||||
@ -54,6 +54,10 @@ public class DirectThreadParticipantRequestsResponse implements Serializable, Cl
|
||||
return totalParticipantRequests;
|
||||
}
|
||||
|
||||
public void setTotalParticipantRequests(final int totalParticipantRequests) {
|
||||
this.totalParticipantRequests = totalParticipantRequests;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ package awais.instagrabber.repositories.responses.directmessages;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public final class RavenExpiringMediaActionSummary {
|
||||
private final ActionType type;
|
||||
private final long timestamp;
|
||||
@ -25,6 +27,21 @@ public final class RavenExpiringMediaActionSummary {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final RavenExpiringMediaActionSummary that = (RavenExpiringMediaActionSummary) o;
|
||||
return timestamp == that.timestamp &&
|
||||
count == that.count &&
|
||||
type == that.type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(type, timestamp, count);
|
||||
}
|
||||
|
||||
// thanks to http://github.com/warifp/InstagramAutoPostImageUrl/blob/master/vendor/mgp25/instagram-php/src/Response/Model/ActionBadge.php
|
||||
public enum ActionType {
|
||||
@SerializedName("raven_delivered")
|
||||
|
@ -1,6 +1,7 @@
|
||||
package awais.instagrabber.repositories.responses.directmessages;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ThreadContext implements Serializable {
|
||||
private final int type;
|
||||
@ -18,4 +19,18 @@ public class ThreadContext implements Serializable {
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final ThreadContext that = (ThreadContext) o;
|
||||
return type == that.type &&
|
||||
Objects.equals(text, that.text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(type, text);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,70 @@
|
||||
package awais.instagrabber.repositories.responses.giphy;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class GiphyGif {
|
||||
private final String type;
|
||||
private final String id;
|
||||
private final String title;
|
||||
private final int isSticker;
|
||||
private final GiphyGifImages images;
|
||||
|
||||
public GiphyGif(final String type, final String id, final String title, final int isSticker, final GiphyGifImages images) {
|
||||
this.type = type;
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.isSticker = isSticker;
|
||||
this.images = images;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public boolean isSticker() {
|
||||
return isSticker == 1;
|
||||
}
|
||||
|
||||
public GiphyGifImages getImages() {
|
||||
return images;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final GiphyGif giphyGif = (GiphyGif) o;
|
||||
return isSticker == giphyGif.isSticker &&
|
||||
Objects.equals(type, giphyGif.type) &&
|
||||
Objects.equals(id, giphyGif.id) &&
|
||||
Objects.equals(title, giphyGif.title) &&
|
||||
Objects.equals(images, giphyGif.images);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(type, id, title, isSticker, images);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GiphyGif{" +
|
||||
"type='" + type + '\'' +
|
||||
", id='" + id + '\'' +
|
||||
", title='" + title + '\'' +
|
||||
", isSticker=" + isSticker() +
|
||||
", images=" + images +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package awais.instagrabber.repositories.responses.giphy;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class GiphyGifImage {
|
||||
private final int height;
|
||||
private final int width;
|
||||
private final long webpSize;
|
||||
private final String webp;
|
||||
|
||||
public GiphyGifImage(final int height, final int width, final long webpSize, final String webp) {
|
||||
this.height = height;
|
||||
this.width = width;
|
||||
this.webpSize = webpSize;
|
||||
this.webp = webp;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public long getWebpSize() {
|
||||
return webpSize;
|
||||
}
|
||||
|
||||
public String getWebp() {
|
||||
return webp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final GiphyGifImage that = (GiphyGifImage) o;
|
||||
return height == that.height &&
|
||||
width == that.width &&
|
||||
webpSize == that.webpSize &&
|
||||
Objects.equals(webp, that.webp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(height, width, webpSize, webp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GiphyGifImage{" +
|
||||
"height=" + height +
|
||||
", width=" + width +
|
||||
", webpSize=" + webpSize +
|
||||
", webp='" + webp + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package awais.instagrabber.repositories.responses.giphy;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import awais.instagrabber.repositories.responses.AnimatedMediaFixedHeight;
|
||||
|
||||
public class GiphyGifImages {
|
||||
private final AnimatedMediaFixedHeight fixedHeight;
|
||||
|
||||
public GiphyGifImages(final AnimatedMediaFixedHeight fixedHeight) {
|
||||
this.fixedHeight = fixedHeight;
|
||||
}
|
||||
|
||||
public AnimatedMediaFixedHeight getFixedHeight() {
|
||||
return fixedHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final GiphyGifImages that = (GiphyGifImages) o;
|
||||
return Objects.equals(fixedHeight, that.fixedHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(fixedHeight);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GiphyGifImages{" +
|
||||
"fixedHeight=" + fixedHeight +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package awais.instagrabber.repositories.responses.giphy;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class GiphyGifResponse {
|
||||
private final GiphyGifResults results;
|
||||
private final String status;
|
||||
|
||||
public GiphyGifResponse(final GiphyGifResults results, final String status) {
|
||||
this.results = results;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public GiphyGifResults getResults() {
|
||||
return results;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final GiphyGifResponse that = (GiphyGifResponse) o;
|
||||
return Objects.equals(results, that.results) &&
|
||||
Objects.equals(status, that.status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(results, status);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GiphyGifResponse{" +
|
||||
"results=" + results +
|
||||
", status='" + status + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package awais.instagrabber.repositories.responses.giphy;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class GiphyGifResults {
|
||||
private final List<GiphyGif> giphyGifs;
|
||||
private final List<GiphyGif> giphy;
|
||||
|
||||
public GiphyGifResults(final List<GiphyGif> giphyGifs, final List<GiphyGif> giphy) {
|
||||
this.giphyGifs = giphyGifs;
|
||||
this.giphy = giphy;
|
||||
}
|
||||
|
||||
public List<GiphyGif> getGiphyGifs() {
|
||||
return giphyGifs;
|
||||
}
|
||||
|
||||
public List<GiphyGif> getGiphy() {
|
||||
return giphy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final GiphyGifResults that = (GiphyGifResults) o;
|
||||
return Objects.equals(giphyGifs, that.giphyGifs) &&
|
||||
Objects.equals(giphy, that.giphy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(giphyGifs, giphy);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GiphyGifResults{" +
|
||||
"giphyGifs=" + giphyGifs +
|
||||
", giphy=" + giphy +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -126,6 +126,6 @@ public class ActivityCheckerService extends Service {
|
||||
final Intent intent = new Intent(getApplicationContext(), MainActivity.class)
|
||||
.setAction(Constants.ACTION_SHOW_ACTIVITY)
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
return PendingIntent.getActivity(getApplicationContext(), 1738, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
return PendingIntent.getActivity(getApplicationContext(), Constants.SHOW_ACTIVITY_REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,27 @@
|
||||
package awais.instagrabber.services;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import awais.instagrabber.fragments.settings.PreferenceKeys;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.CookieUtils;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public class BootCompletedReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(final Context context, final Intent intent) {
|
||||
if (!Objects.equals(intent.getAction(), "android.intent.action.BOOT_COMPLETED")) return;
|
||||
final boolean enabled = settingsHelper.getBoolean(PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH);
|
||||
if (!enabled) return;
|
||||
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||
final boolean isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != 0;
|
||||
if (!isLoggedIn) return;
|
||||
DMSyncAlarmReceiver.setAlarm(context);
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
package awais.instagrabber.services;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.time.temporal.TemporalUnit;
|
||||
|
||||
import awais.instagrabber.fragments.settings.PreferenceKeys;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public class DMSyncAlarmReceiver extends BroadcastReceiver {
|
||||
private static final String TAG = DMSyncAlarmReceiver.class.getSimpleName();
|
||||
|
||||
@Override
|
||||
public void onReceive(final Context context, final Intent intent) {
|
||||
final boolean enabled = settingsHelper.getBoolean(PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH);
|
||||
if (!enabled) {
|
||||
// If somehow the alarm was triggered even when auto refresh is disabled
|
||||
cancelAlarm(context);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final Context applicationContext = context.getApplicationContext();
|
||||
ContextCompat.startForegroundService(applicationContext, new Intent(applicationContext, DMSyncService.class));
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "onReceive: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setAlarm(@NonNull final Context context) {
|
||||
Log.d(TAG, "setting DMSyncService Alarm");
|
||||
final AlarmManager alarmManager = getAlarmManager(context);
|
||||
if (alarmManager == null) return;
|
||||
final PendingIntent pendingIntent = getPendingIntent(context);
|
||||
alarmManager.setInexactRepeating(AlarmManager.RTC, System.currentTimeMillis(), getIntervalMillis(), pendingIntent);
|
||||
}
|
||||
|
||||
public static void cancelAlarm(@NonNull final Context context) {
|
||||
Log.d(TAG, "cancelling DMSyncService Alarm");
|
||||
final AlarmManager alarmManager = getAlarmManager(context);
|
||||
if (alarmManager == null) return;
|
||||
final PendingIntent pendingIntent = getPendingIntent(context);
|
||||
alarmManager.cancel(pendingIntent);
|
||||
}
|
||||
|
||||
private static AlarmManager getAlarmManager(@NonNull final Context context) {
|
||||
return (AlarmManager) context.getApplicationContext().getSystemService(Context.ALARM_SERVICE);
|
||||
}
|
||||
|
||||
private static PendingIntent getPendingIntent(@NonNull final Context context) {
|
||||
final Context applicationContext = context.getApplicationContext();
|
||||
final Intent intent = new Intent(applicationContext, DMSyncAlarmReceiver.class);
|
||||
return PendingIntent.getBroadcast(applicationContext,
|
||||
Constants.DM_SYNC_SERVICE_REQUEST_CODE,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
|
||||
private static long getIntervalMillis() {
|
||||
int amount = settingsHelper.getInteger(PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER);
|
||||
if (amount <= 0) {
|
||||
amount = 30;
|
||||
}
|
||||
final String unit = settingsHelper.getString(PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT);
|
||||
final TemporalUnit temporalUnit;
|
||||
switch (unit) {
|
||||
case "mins":
|
||||
temporalUnit = ChronoUnit.MINUTES;
|
||||
break;
|
||||
default:
|
||||
case "secs":
|
||||
temporalUnit = ChronoUnit.SECONDS;
|
||||
}
|
||||
return Duration.of(amount, temporalUnit).toMillis();
|
||||
}
|
||||
}
|
259
app/src/main/java/awais/instagrabber/services/DMSyncService.java
Normal file
259
app/src/main/java/awais/instagrabber/services/DMSyncService.java
Normal file
@ -0,0 +1,259 @@
|
||||
package awais.instagrabber.services;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.lifecycle.LifecycleService;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.activities.MainActivity;
|
||||
import awais.instagrabber.db.datasources.DMLastNotifiedDataSource;
|
||||
import awais.instagrabber.db.entities.DMLastNotified;
|
||||
import awais.instagrabber.db.repositories.DMLastNotifiedRepository;
|
||||
import awais.instagrabber.db.repositories.RepositoryCallback;
|
||||
import awais.instagrabber.fragments.settings.PreferenceKeys;
|
||||
import awais.instagrabber.managers.DirectMessagesManager;
|
||||
import awais.instagrabber.managers.InboxManager;
|
||||
import awais.instagrabber.models.Resource;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectInbox;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadLastSeenAt;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.CookieUtils;
|
||||
import awais.instagrabber.utils.DMUtils;
|
||||
import awais.instagrabber.utils.DateUtils;
|
||||
import awais.instagrabber.utils.TextUtils;
|
||||
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public class DMSyncService extends LifecycleService {
|
||||
private static final String TAG = DMSyncService.class.getSimpleName();
|
||||
|
||||
private InboxManager inboxManager;
|
||||
private DMLastNotifiedRepository dmLastNotifiedRepository;
|
||||
private Map<String, DMLastNotified> dmLastNotifiedMap;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
startForeground(Constants.DM_CHECK_NOTIFICATION_ID, buildForegroundNotification());
|
||||
Log.d(TAG, "onCreate: Service created");
|
||||
final DirectMessagesManager directMessagesManager = DirectMessagesManager.getInstance();
|
||||
inboxManager = directMessagesManager.getInboxManager();
|
||||
dmLastNotifiedRepository = DMLastNotifiedRepository.getInstance(DMLastNotifiedDataSource.getInstance(getApplicationContext()));
|
||||
}
|
||||
|
||||
private void parseUnread(@NonNull final DirectInbox directInbox) {
|
||||
dmLastNotifiedRepository.getAllDMDmLastNotified(new RepositoryCallback<List<DMLastNotified>>() {
|
||||
@Override
|
||||
public void onSuccess(final List<DMLastNotified> result) {
|
||||
dmLastNotifiedMap = result != null
|
||||
? result.stream().collect(Collectors.toMap(DMLastNotified::getThreadId, Function.identity()))
|
||||
: Collections.emptyMap();
|
||||
parseUnreadActual(directInbox);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataNotAvailable() {
|
||||
dmLastNotifiedMap = Collections.emptyMap();
|
||||
parseUnreadActual(directInbox);
|
||||
}
|
||||
});
|
||||
// Log.d(TAG, "inbox observer: " + directInbox);
|
||||
}
|
||||
|
||||
private void parseUnreadActual(@NonNull final DirectInbox directInbox) {
|
||||
final List<DirectThread> threads = directInbox.getThreads();
|
||||
final ImmutableMap.Builder<String, List<DirectItem>> unreadMessagesMapBuilder = ImmutableMap.builder();
|
||||
if (threads == null) {
|
||||
stopSelf();
|
||||
return;
|
||||
}
|
||||
for (final DirectThread thread : threads) {
|
||||
if (thread.isMuted()) continue;
|
||||
final boolean read = DMUtils.isRead(thread);
|
||||
if (read) continue;
|
||||
final List<DirectItem> unreadMessages = getUnreadMessages(thread);
|
||||
if (unreadMessages.isEmpty()) continue;
|
||||
unreadMessagesMapBuilder.put(thread.getThreadId(), unreadMessages);
|
||||
}
|
||||
final Map<String, List<DirectItem>> unreadMessagesMap = unreadMessagesMapBuilder.build();
|
||||
if (unreadMessagesMap.isEmpty()) {
|
||||
stopSelf();
|
||||
return;
|
||||
}
|
||||
showNotification(directInbox, unreadMessagesMap);
|
||||
final LocalDateTime now = LocalDateTime.now();
|
||||
// Update db
|
||||
final ImmutableList.Builder<DMLastNotified> lastNotifiedListBuilder = ImmutableList.builder();
|
||||
for (final Map.Entry<String, List<DirectItem>> unreadMessagesEntry : unreadMessagesMap.entrySet()) {
|
||||
final List<DirectItem> unreadItems = unreadMessagesEntry.getValue();
|
||||
final DirectItem latestItem = unreadItems.get(unreadItems.size() - 1);
|
||||
lastNotifiedListBuilder.add(new DMLastNotified(0,
|
||||
unreadMessagesEntry.getKey(),
|
||||
latestItem.getLocalDateTime(),
|
||||
now));
|
||||
}
|
||||
dmLastNotifiedRepository.insertOrUpdateDMLastNotified(
|
||||
lastNotifiedListBuilder.build(),
|
||||
new RepositoryCallback<Void>() {
|
||||
@Override
|
||||
public void onSuccess(final Void result) {
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataNotAvailable() {
|
||||
stopSelf();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<DirectItem> getUnreadMessages(@NonNull final DirectThread thread) {
|
||||
final List<DirectItem> items = thread.getItems();
|
||||
if (items == null) return Collections.emptyList();
|
||||
final DMLastNotified dmLastNotified = dmLastNotifiedMap.get(thread.getThreadId());
|
||||
final long viewerId = thread.getViewerId();
|
||||
final Map<Long, DirectThreadLastSeenAt> lastSeenAt = thread.getLastSeenAt();
|
||||
final ImmutableList.Builder<DirectItem> unreadListBuilder = ImmutableList.builder();
|
||||
int count = 0;
|
||||
for (final DirectItem item : items) {
|
||||
if (item == null) continue;
|
||||
if (item.getUserId() == viewerId) break; // Reached a message from the viewer, it is assumed the viewer has read the next messages
|
||||
final boolean read = DMUtils.isRead(item, lastSeenAt, Collections.singletonList(viewerId));
|
||||
if (read) break;
|
||||
if (dmLastNotified != null && dmLastNotified.getLastNotifiedMsgTs() != null) {
|
||||
if (count == 0 && DateUtils.isBeforeOrEqual(item.getLocalDateTime(), dmLastNotified.getLastNotifiedMsgTs())) {
|
||||
// The first unread item has been notified and hence all subsequent items can be ignored
|
||||
// since the items are in desc timestamp order
|
||||
break;
|
||||
}
|
||||
}
|
||||
unreadListBuilder.add(item);
|
||||
count++;
|
||||
// Inbox style notification only allows 6 lines
|
||||
if (count >= 6) break;
|
||||
}
|
||||
// Reversing, so that oldest messages are on top
|
||||
return unreadListBuilder.build().reverse();
|
||||
}
|
||||
|
||||
private void showNotification(final DirectInbox directInbox,
|
||||
final Map<String, List<DirectItem>> unreadMessagesMap) {
|
||||
final NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (notificationManager == null) return;
|
||||
for (final Map.Entry<String, List<DirectItem>> unreadMessagesEntry : unreadMessagesMap.entrySet()) {
|
||||
final Optional<DirectThread> directThreadOptional = getThread(directInbox, unreadMessagesEntry.getKey());
|
||||
if (!directThreadOptional.isPresent()) continue;
|
||||
final DirectThread thread = directThreadOptional.get();
|
||||
final DirectItem firstDirectItem = thread.getFirstDirectItem();
|
||||
if (firstDirectItem == null) continue;
|
||||
final List<DirectItem> unreadMessages = unreadMessagesEntry.getValue();
|
||||
final NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
|
||||
inboxStyle.setBigContentTitle(thread.getThreadTitle());
|
||||
for (final DirectItem item : unreadMessages) {
|
||||
inboxStyle.addLine(DMUtils.getMessageString(thread, getResources(), thread.getViewerId(), item));
|
||||
}
|
||||
final Notification notification = new NotificationCompat.Builder(this, Constants.DM_UNREAD_CHANNEL_ID)
|
||||
.setStyle(inboxStyle)
|
||||
.setSmallIcon(R.drawable.ic_round_mode_comment_24)
|
||||
.setContentTitle(thread.getThreadTitle())
|
||||
.setContentText(DMUtils.getMessageString(thread, getResources(), thread.getViewerId(), firstDirectItem))
|
||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||
.setDefaults(NotificationCompat.DEFAULT_ALL)
|
||||
.setGroup(Constants.GROUP_KEY_DM)
|
||||
.setAutoCancel(true)
|
||||
.setContentIntent(getThreadPendingIntent(thread.getThreadId(), thread.getThreadTitle()))
|
||||
.build();
|
||||
notificationManager.notify(Constants.DM_UNREAD_PARENT_NOTIFICATION_ID, notification);
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<DirectThread> getThread(@NonNull final DirectInbox directInbox, final String threadId) {
|
||||
return directInbox.getThreads()
|
||||
.stream()
|
||||
.filter(thread -> Objects.equals(thread.getThreadId(), threadId))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private PendingIntent getThreadPendingIntent(final String threadId, final String threadTitle) {
|
||||
final Intent intent = new Intent(getApplicationContext(), MainActivity.class)
|
||||
.setAction(Constants.ACTION_SHOW_DM_THREAD)
|
||||
.putExtra(Constants.DM_THREAD_ACTION_EXTRA_THREAD_ID, threadId)
|
||||
.putExtra(Constants.DM_THREAD_ACTION_EXTRA_THREAD_TITLE, threadTitle)
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
return PendingIntent.getActivity(getApplicationContext(), Constants.SHOW_DM_THREAD, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(final Intent intent, final int flags, final int startId) {
|
||||
super.onStartCommand(intent, flags, startId);
|
||||
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||
final boolean isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != 0;
|
||||
if (!isLoggedIn) {
|
||||
stopSelf();
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
// Need to setup here if service was started by the boot completed receiver
|
||||
CookieUtils.setupCookies(cookie);
|
||||
final boolean notificationsEnabled = settingsHelper.getBoolean(PreferenceKeys.PREF_ENABLE_DM_NOTIFICATIONS);
|
||||
inboxManager.getInbox().observe(this, inboxResource -> {
|
||||
if (!notificationsEnabled || inboxResource == null || inboxResource.status != Resource.Status.SUCCESS) {
|
||||
stopSelf();
|
||||
return;
|
||||
}
|
||||
final DirectInbox directInbox = inboxResource.data;
|
||||
if (directInbox == null) {
|
||||
stopSelf();
|
||||
return;
|
||||
}
|
||||
parseUnread(directInbox);
|
||||
});
|
||||
Log.d(TAG, "onStartCommand: refreshing inbox");
|
||||
inboxManager.refresh();
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(@NonNull final Intent intent) {
|
||||
super.onBind(intent);
|
||||
return null;
|
||||
}
|
||||
|
||||
private Notification buildForegroundNotification() {
|
||||
final Resources resources = getResources();
|
||||
return new NotificationCompat.Builder(this, Constants.SILENT_NOTIFICATIONS_CHANNEL_ID)
|
||||
.setOngoing(true)
|
||||
.setSound(null)
|
||||
.setContentTitle(resources.getString(R.string.app_name))
|
||||
.setContentText(resources.getString(R.string.checking_for_new_messages))
|
||||
.setSmallIcon(R.mipmap.ic_launcher)
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.setGroup(Constants.GROUP_KEY_SILENT_NOTIFICATIONS)
|
||||
.build();
|
||||
}
|
||||
}
|
@ -7,10 +7,10 @@ import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.util.LruCache;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.util.Pair;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
|
@ -58,6 +58,8 @@ public final class Constants {
|
||||
|
||||
// Notification ids
|
||||
public static final int ACTIVITY_NOTIFICATION_ID = 10;
|
||||
public static final int DM_UNREAD_PARENT_NOTIFICATION_ID = 20;
|
||||
public static final int DM_CHECK_NOTIFICATION_ID = 11;
|
||||
|
||||
// see https://github.com/dilame/instagram-private-api/blob/master/src/core/constants.ts
|
||||
public static final String SUPPORTED_CAPABILITIES = "[ { \"name\": \"SUPPORTED_SDK_VERSIONS\", \"value\":" +
|
||||
@ -74,12 +76,6 @@ public final class Constants {
|
||||
public static final String FDROID_SHA1_FINGERPRINT = "C1661EB8FD09F618307E687786D5E5056F65084D";
|
||||
public static final String SKIPPED_VERSION = "skipped_version";
|
||||
public static final String DEFAULT_TAB = "default_tab";
|
||||
public static final String ACTIVITY_CHANNEL_ID = "activity";
|
||||
public static final String DOWNLOAD_CHANNEL_ID = "download";
|
||||
public static final String ACTIVITY_CHANNEL_NAME = "Activity";
|
||||
public static final String DOWNLOAD_CHANNEL_NAME = "Downloads";
|
||||
public static final String NOTIF_GROUP_NAME = "awais.instagrabber.InstaNotif";
|
||||
public static final String ACTION_SHOW_ACTIVITY = "show_activity";
|
||||
public static final String PREF_DARK_THEME = "dark_theme";
|
||||
public static final String PREF_LIGHT_THEME = "light_theme";
|
||||
public static final String DEFAULT_HASH_TAG_PIC = "https://www.instagram.com/static/images/hashtag/search-hashtag-default-avatar.png/1d8417c9a4f5.png";
|
||||
@ -94,4 +90,27 @@ public final class Constants {
|
||||
public static final String PREF_SAVED_POSTS_LAYOUT = "saved_posts_layout";
|
||||
public static final String PREF_EMOJI_VARIANTS = "emoji_variants";
|
||||
public static final String PREF_REACTIONS = "reactions";
|
||||
|
||||
public static final String ACTIVITY_CHANNEL_ID = "activity";
|
||||
public static final String ACTIVITY_CHANNEL_NAME = "Activity";
|
||||
public static final String DOWNLOAD_CHANNEL_ID = "download";
|
||||
public static final String DOWNLOAD_CHANNEL_NAME = "Downloads";
|
||||
public static final String DM_UNREAD_CHANNEL_ID = "dmUnread";
|
||||
public static final String DM_UNREAD_CHANNEL_NAME = "Messages";
|
||||
public static final String SILENT_NOTIFICATIONS_CHANNEL_ID = "silentNotifications";
|
||||
public static final String SILENT_NOTIFICATIONS_CHANNEL_NAME = "Silent notifications";
|
||||
|
||||
public static final String NOTIF_GROUP_NAME = "awais.instagrabber.InstaNotif";
|
||||
public static final String GROUP_KEY_DM = "awais.instagrabber.MESSAGES";
|
||||
public static final String GROUP_KEY_SILENT_NOTIFICATIONS = "awais.instagrabber.SILENT_NOTIFICATIONS";
|
||||
|
||||
public static final int SHOW_ACTIVITY_REQUEST_CODE = 1738;
|
||||
public static final int SHOW_DM_THREAD = 2000;
|
||||
public static final int DM_SYNC_SERVICE_REQUEST_CODE = 3000;
|
||||
|
||||
public static final String ACTION_SHOW_ACTIVITY = "show_activity";
|
||||
public static final String ACTION_SHOW_DM_THREAD = "show_dm_thread";
|
||||
|
||||
public static final String DM_THREAD_ACTION_EXTRA_THREAD_ID = "thread_id";
|
||||
public static final String DM_THREAD_ACTION_EXTRA_THREAD_TITLE = "thread_title";
|
||||
}
|
285
app/src/main/java/awais/instagrabber/utils/DMUtils.java
Normal file
285
app/src/main/java/awais/instagrabber/utils/DMUtils.java
Normal file
@ -0,0 +1,285 @@
|
||||
package awais.instagrabber.utils;
|
||||
|
||||
import android.content.res.Resources;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import awais.instagrabber.R;
|
||||
import awais.instagrabber.models.enums.DirectItemType;
|
||||
import awais.instagrabber.models.enums.MediaItemType;
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItemAnimatedMedia;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItemReelShare;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItemVisualMedia;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadLastSeenAt;
|
||||
import awais.instagrabber.repositories.responses.directmessages.RavenExpiringMediaActionSummary;
|
||||
|
||||
public final class DMUtils {
|
||||
public static boolean isRead(final DirectItem item,
|
||||
@NonNull final Map<Long, DirectThreadLastSeenAt> lastSeenAt,
|
||||
final List<Long> userIdsToCheck) {
|
||||
// Further check if directStory exists
|
||||
// if (read && directStory != null) {
|
||||
// read = false;
|
||||
// }
|
||||
return lastSeenAt.entrySet()
|
||||
.stream()
|
||||
.filter(entry -> userIdsToCheck.contains(entry.getKey()))
|
||||
.anyMatch(entry -> {
|
||||
final String userLastSeenTsString = entry.getValue().getTimestamp();
|
||||
if (userLastSeenTsString == null) return false;
|
||||
final long userTs = Long.parseLong(userLastSeenTsString);
|
||||
final long itemTs = item.getTimestamp();
|
||||
return userTs >= itemTs;
|
||||
});
|
||||
}
|
||||
|
||||
public static boolean isRead(@NonNull final DirectThread thread) {
|
||||
final boolean read;
|
||||
if (thread.getDirectStory() != null) {
|
||||
return false;
|
||||
}
|
||||
final DirectItem item = thread.getFirstDirectItem();
|
||||
final long viewerId = thread.getViewerId();
|
||||
if (item != null && item.getUserId() == viewerId) {
|
||||
// if last item was sent by user, then it is read (even though we have auto read unchecked?)
|
||||
read = true;
|
||||
} else {
|
||||
final Map<Long, DirectThreadLastSeenAt> lastSeenAtMap = thread.getLastSeenAt();
|
||||
read = isRead(item, lastSeenAtMap, Collections.singletonList(viewerId));
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
public static String getMessageString(@NonNull final DirectThread thread,
|
||||
final Resources resources,
|
||||
final long viewerId,
|
||||
final DirectItem item) {
|
||||
final long senderId = item.getUserId();
|
||||
final DirectItemType itemType = item.getItemType();
|
||||
String subtitle = null;
|
||||
final String username = getUsername(thread.getUsers(), senderId, viewerId, resources);
|
||||
String message = "";
|
||||
if (itemType == null) {
|
||||
message = resources.getString(R.string.dms_inbox_raven_message_unknown);
|
||||
} else {
|
||||
switch (itemType) {
|
||||
case TEXT:
|
||||
message = item.getText();
|
||||
break;
|
||||
case LIKE:
|
||||
message = item.getLike();
|
||||
break;
|
||||
case LINK:
|
||||
message = item.getLink().getText();
|
||||
break;
|
||||
case PLACEHOLDER:
|
||||
message = item.getPlaceholder().getMessage();
|
||||
break;
|
||||
case MEDIA_SHARE:
|
||||
final User mediaShareUser = item.getMediaShare().getUser();
|
||||
subtitle = resources.getString(R.string.dms_inbox_shared_post,
|
||||
username != null ? username : "",
|
||||
mediaShareUser == null ? "" : mediaShareUser.getUsername());
|
||||
break;
|
||||
case ANIMATED_MEDIA:
|
||||
final DirectItemAnimatedMedia animatedMedia = item.getAnimatedMedia();
|
||||
subtitle = resources.getString(animatedMedia.isSticker() ? R.string.dms_inbox_shared_sticker
|
||||
: R.string.dms_inbox_shared_gif,
|
||||
username != null ? username : "");
|
||||
break;
|
||||
case PROFILE:
|
||||
subtitle = resources
|
||||
.getString(R.string.dms_inbox_shared_profile, username != null ? username : "", item.getProfile().getUsername());
|
||||
break;
|
||||
case LOCATION:
|
||||
subtitle = resources
|
||||
.getString(R.string.dms_inbox_shared_location, username != null ? username : "", item.getLocation().getName());
|
||||
break;
|
||||
case MEDIA: {
|
||||
final MediaItemType mediaType = item.getMedia().getMediaType();
|
||||
subtitle = getMediaSpecificSubtitle(username, resources, mediaType);
|
||||
break;
|
||||
}
|
||||
case STORY_SHARE: {
|
||||
final String reelType = item.getStoryShare().getReelType();
|
||||
if (reelType == null) {
|
||||
subtitle = item.getStoryShare().getTitle();
|
||||
} else {
|
||||
final int format = reelType.equals("highlight_reel")
|
||||
? R.string.dms_inbox_shared_highlight
|
||||
: R.string.dms_inbox_shared_story;
|
||||
final User storyShareMediaUser = item.getStoryShare().getMedia().getUser();
|
||||
subtitle = resources.getString(format,
|
||||
username != null ? username : "",
|
||||
storyShareMediaUser == null ? "" : storyShareMediaUser.getUsername());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case VOICE_MEDIA:
|
||||
subtitle = resources.getString(R.string.dms_inbox_shared_voice, username != null ? username : "");
|
||||
break;
|
||||
case ACTION_LOG:
|
||||
subtitle = item.getActionLog().getDescription();
|
||||
break;
|
||||
case VIDEO_CALL_EVENT:
|
||||
subtitle = item.getVideoCallEvent().getDescription();
|
||||
break;
|
||||
case CLIP:
|
||||
final User clipUser = item.getClip().getClip().getUser();
|
||||
subtitle = resources.getString(R.string.dms_inbox_shared_clip,
|
||||
username != null ? username : "",
|
||||
clipUser == null ? "" : clipUser.getUsername());
|
||||
break;
|
||||
case FELIX_SHARE:
|
||||
final User felixShareVideoUser = item.getFelixShare().getVideo().getUser();
|
||||
subtitle = resources.getString(R.string.dms_inbox_shared_igtv,
|
||||
username != null ? username : "",
|
||||
felixShareVideoUser == null ? "" : felixShareVideoUser.getUsername());
|
||||
break;
|
||||
case RAVEN_MEDIA:
|
||||
subtitle = getRavenMediaSubtitle(item, resources, username);
|
||||
break;
|
||||
case REEL_SHARE:
|
||||
final DirectItemReelShare reelShare = item.getReelShare();
|
||||
if (reelShare == null) {
|
||||
subtitle = "";
|
||||
break;
|
||||
}
|
||||
final String reelType = reelShare.getType();
|
||||
switch (reelType) {
|
||||
case "reply":
|
||||
if (viewerId == item.getUserId()) {
|
||||
subtitle = resources.getString(R.string.dms_inbox_replied_story_outgoing, reelShare.getText());
|
||||
} else {
|
||||
subtitle = resources
|
||||
.getString(R.string.dms_inbox_replied_story_incoming, username != null ? username : "", reelShare.getText());
|
||||
}
|
||||
break;
|
||||
case "mention":
|
||||
if (viewerId == item.getUserId()) {
|
||||
// You mentioned the other person
|
||||
final long mentionedUserId = item.getReelShare().getMentionedUserId();
|
||||
final String otherUsername = getUsername(thread.getUsers(), mentionedUserId, viewerId, resources);
|
||||
subtitle = resources.getString(R.string.dms_inbox_mentioned_story_outgoing, otherUsername);
|
||||
} else {
|
||||
// They mentioned you
|
||||
subtitle = resources.getString(R.string.dms_inbox_mentioned_story_incoming, username != null ? username : "");
|
||||
}
|
||||
break;
|
||||
case "reaction":
|
||||
if (viewerId == item.getUserId()) {
|
||||
subtitle = resources.getString(R.string.dms_inbox_reacted_story_outgoing, reelShare.getText());
|
||||
} else {
|
||||
subtitle = resources
|
||||
.getString(R.string.dms_inbox_reacted_story_incoming, username != null ? username : "", reelShare.getText());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
subtitle = "";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
message = resources.getString(R.string.dms_inbox_raven_message_unknown);
|
||||
}
|
||||
}
|
||||
if (subtitle == null) {
|
||||
if (thread.isGroup() || (!thread.isGroup() && senderId == viewerId)) {
|
||||
subtitle = String.format("%s: %s", username != null ? username : "", message);
|
||||
} else {
|
||||
subtitle = message;
|
||||
}
|
||||
}
|
||||
return subtitle;
|
||||
}
|
||||
|
||||
public static String getUsername(final List<User> users,
|
||||
final long userId,
|
||||
final long viewerId,
|
||||
final Resources resources) {
|
||||
if (userId == viewerId) {
|
||||
return resources.getString(R.string.you);
|
||||
}
|
||||
final Optional<User> senderOptional = users.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(user -> user.getPk() == userId)
|
||||
.findFirst();
|
||||
return senderOptional.map(User::getUsername).orElse(null);
|
||||
}
|
||||
|
||||
public static String getMediaSpecificSubtitle(final String username, final Resources resources, final MediaItemType mediaType) {
|
||||
final String userSharedAnImage = resources.getString(R.string.dms_inbox_shared_image, username != null ? username : "");
|
||||
final String userSharedAVideo = resources.getString(R.string.dms_inbox_shared_video, username != null ? username : "");
|
||||
final String userSentAMessage = resources.getString(R.string.dms_inbox_shared_message, username != null ? username : "");
|
||||
String subtitle;
|
||||
switch (mediaType) {
|
||||
case MEDIA_TYPE_IMAGE:
|
||||
subtitle = userSharedAnImage;
|
||||
break;
|
||||
case MEDIA_TYPE_VIDEO:
|
||||
subtitle = userSharedAVideo;
|
||||
break;
|
||||
default:
|
||||
subtitle = userSentAMessage;
|
||||
break;
|
||||
}
|
||||
return subtitle;
|
||||
}
|
||||
|
||||
private static String getRavenMediaSubtitle(final DirectItem item,
|
||||
final Resources resources,
|
||||
final String username) {
|
||||
String subtitle = "↗ ";
|
||||
final DirectItemVisualMedia visualMedia = item.getVisualMedia();
|
||||
final RavenExpiringMediaActionSummary summary = visualMedia.getExpiringMediaActionSummary();
|
||||
if (summary != null) {
|
||||
final RavenExpiringMediaActionSummary.ActionType expiringMediaType = summary.getType();
|
||||
int textRes = 0;
|
||||
switch (expiringMediaType) {
|
||||
case DELIVERED:
|
||||
textRes = R.string.dms_inbox_raven_media_delivered;
|
||||
break;
|
||||
case SENT:
|
||||
textRes = R.string.dms_inbox_raven_media_sent;
|
||||
break;
|
||||
case OPENED:
|
||||
textRes = R.string.dms_inbox_raven_media_opened;
|
||||
break;
|
||||
case REPLAYED:
|
||||
textRes = R.string.dms_inbox_raven_media_replayed;
|
||||
break;
|
||||
case SENDING:
|
||||
textRes = R.string.dms_inbox_raven_media_sending;
|
||||
break;
|
||||
case BLOCKED:
|
||||
textRes = R.string.dms_inbox_raven_media_blocked;
|
||||
break;
|
||||
case SUGGESTED:
|
||||
textRes = R.string.dms_inbox_raven_media_suggested;
|
||||
break;
|
||||
case SCREENSHOT:
|
||||
textRes = R.string.dms_inbox_raven_media_screenshot;
|
||||
break;
|
||||
case CANNOT_DELIVER:
|
||||
textRes = R.string.dms_inbox_raven_media_cant_deliver;
|
||||
break;
|
||||
}
|
||||
if (textRes > 0) {
|
||||
subtitle += resources.getString(textRes);
|
||||
}
|
||||
return subtitle;
|
||||
}
|
||||
final MediaItemType mediaType = visualMedia.getMedia().getMediaType();
|
||||
subtitle = getMediaSpecificSubtitle(username, resources, mediaType);
|
||||
return subtitle;
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package awais.instagrabber.utils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
@ -34,4 +35,8 @@ public final class DateUtils {
|
||||
final Calendar calendar = Calendar.getInstance(Locale.getDefault());
|
||||
return -(calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET)) / (60 * 1000);
|
||||
}
|
||||
|
||||
public static boolean isBeforeOrEqual(final LocalDateTime localDateTime, final LocalDateTime comparedTo) {
|
||||
return localDateTime.isBefore(comparedTo) || localDateTime.isEqual(comparedTo);
|
||||
}
|
||||
}
|
||||
|
@ -8,13 +8,16 @@ import java.util.UUID;
|
||||
|
||||
import awais.instagrabber.models.enums.DirectItemType;
|
||||
import awais.instagrabber.models.enums.MediaItemType;
|
||||
import awais.instagrabber.repositories.responses.AnimatedMediaImages;
|
||||
import awais.instagrabber.repositories.responses.Audio;
|
||||
import awais.instagrabber.repositories.responses.ImageVersions2;
|
||||
import awais.instagrabber.repositories.responses.Media;
|
||||
import awais.instagrabber.repositories.responses.MediaCandidate;
|
||||
import awais.instagrabber.repositories.responses.VideoVersion;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItemAnimatedMedia;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItemVoiceMedia;
|
||||
import awais.instagrabber.repositories.responses.giphy.GiphyGif;
|
||||
|
||||
public final class DirectItemFactory {
|
||||
|
||||
@ -213,4 +216,45 @@ public final class DirectItemFactory {
|
||||
0,
|
||||
false);
|
||||
}
|
||||
|
||||
public static DirectItem createAnimatedMedia(final long userId,
|
||||
final String clientContext,
|
||||
final GiphyGif giphyGif) {
|
||||
final AnimatedMediaImages animatedImages = new AnimatedMediaImages(giphyGif.getImages().getFixedHeight());
|
||||
final DirectItemAnimatedMedia animateMedia = new DirectItemAnimatedMedia(
|
||||
giphyGif.getId(),
|
||||
animatedImages,
|
||||
false,
|
||||
giphyGif.isSticker()
|
||||
);
|
||||
return new DirectItem(
|
||||
UUID.randomUUID().toString(),
|
||||
userId,
|
||||
System.currentTimeMillis() * 1000,
|
||||
DirectItemType.ANIMATED_MEDIA,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
clientContext,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
animateMedia,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
0,
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
import javax.security.cert.CertificateException;
|
||||
@ -106,11 +105,11 @@ public final class FlavorTown {
|
||||
if (settingsHelper.getInteger(Constants.PREV_INSTALL_VERSION) < BuildConfig.VERSION_CODE) {
|
||||
int appUaCode = settingsHelper.getInteger(Constants.APP_UA_CODE);
|
||||
int browserUaCode = settingsHelper.getInteger(Constants.BROWSER_UA_CODE);
|
||||
if (browserUaCode == -1) {
|
||||
if (browserUaCode == -1 || browserUaCode >= UserAgentUtils.browsers.length) {
|
||||
browserUaCode = ThreadLocalRandom.current().nextInt(0, UserAgentUtils.browsers.length);
|
||||
settingsHelper.putInteger(Constants.BROWSER_UA_CODE, browserUaCode);
|
||||
}
|
||||
if (appUaCode == -1) {
|
||||
if (appUaCode == -1 || appUaCode >= UserAgentUtils.devices.length) {
|
||||
appUaCode = ThreadLocalRandom.current().nextInt(0, UserAgentUtils.devices.length);
|
||||
settingsHelper.putInteger(Constants.APP_UA_CODE, appUaCode);
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ import org.json.JSONObject;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import awais.instagrabber.BuildConfig;
|
||||
import awais.instagrabber.models.StoryModel;
|
||||
@ -31,9 +30,6 @@ import awais.instagrabber.repositories.responses.Media;
|
||||
import awais.instagrabber.repositories.responses.MediaCandidate;
|
||||
import awais.instagrabber.repositories.responses.User;
|
||||
import awais.instagrabber.repositories.responses.VideoVersion;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadDirectStory;
|
||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadLastSeenAt;
|
||||
import awaisomereport.LogCollector;
|
||||
|
||||
public final class ResponseBodyUtils {
|
||||
@ -1125,36 +1121,15 @@ public final class ResponseBodyUtils {
|
||||
return candidate.getUrl();
|
||||
}
|
||||
|
||||
public static boolean isRead(final DirectItem item,
|
||||
final Map<Long, DirectThreadLastSeenAt> lastSeenAt,
|
||||
final List<Long> userIdsToCheck,
|
||||
final DirectThreadDirectStory directStory) {
|
||||
boolean read = lastSeenAt.entrySet()
|
||||
.stream()
|
||||
.filter(entry -> userIdsToCheck.contains(entry.getKey()))
|
||||
.anyMatch(entry -> {
|
||||
final String userLastSeenTsString = entry.getValue().getTimestamp();
|
||||
if (userLastSeenTsString == null) return false;
|
||||
final long userTs = Long.parseLong(userLastSeenTsString);
|
||||
final long itemTs = item.getTimestamp();
|
||||
return userTs >= itemTs;
|
||||
});
|
||||
// Further check if directStory exists
|
||||
if (read && directStory != null) {
|
||||
read = false;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
public static StoryModel parseBroadcastItem(final JSONObject data) throws JSONException {
|
||||
final StoryModel model = new StoryModel(data.getString("id"),
|
||||
data.getString("cover_frame_url"),
|
||||
data.getString("cover_frame_url"),
|
||||
MediaItemType.MEDIA_TYPE_LIVE,
|
||||
data.optLong("published_time", 0),
|
||||
data.getJSONObject("broadcast_owner").getString("username"),
|
||||
data.getJSONObject("broadcast_owner").getLong("pk"),
|
||||
false);
|
||||
data.getString("cover_frame_url"),
|
||||
data.getString("cover_frame_url"),
|
||||
MediaItemType.MEDIA_TYPE_LIVE,
|
||||
data.optLong("published_time", 0),
|
||||
data.getJSONObject("broadcast_owner").getString("username"),
|
||||
data.getJSONObject("broadcast_owner").getLong("pk"),
|
||||
false);
|
||||
model.setVideoUrl(data.getString("dash_playback_url"));
|
||||
return model;
|
||||
}
|
||||
|
@ -8,6 +8,10 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringDef;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
|
||||
import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH;
|
||||
import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER;
|
||||
import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT;
|
||||
import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_NOTIFICATIONS;
|
||||
import static awais.instagrabber.utils.Constants.APP_LANGUAGE;
|
||||
import static awais.instagrabber.utils.Constants.APP_THEME;
|
||||
import static awais.instagrabber.utils.Constants.APP_UA;
|
||||
@ -130,14 +134,14 @@ public final class SettingsHelper {
|
||||
CUSTOM_DATE_TIME_FORMAT, DEVICE_UUID, SKIPPED_VERSION, DEFAULT_TAB, PREF_DARK_THEME, PREF_LIGHT_THEME,
|
||||
PREF_POSTS_LAYOUT, PREF_PROFILE_POSTS_LAYOUT, PREF_TOPIC_POSTS_LAYOUT, PREF_HASHTAG_POSTS_LAYOUT,
|
||||
PREF_LOCATION_POSTS_LAYOUT, PREF_LIKED_POSTS_LAYOUT, PREF_TAGGED_POSTS_LAYOUT, PREF_SAVED_POSTS_LAYOUT,
|
||||
STORY_SORT, PREF_EMOJI_VARIANTS, PREF_REACTIONS})
|
||||
STORY_SORT, PREF_EMOJI_VARIANTS, PREF_REACTIONS, PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT})
|
||||
public @interface StringSettings {}
|
||||
|
||||
@StringDef({DOWNLOAD_USER_FOLDER, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS,
|
||||
SHOW_CAPTIONS, CUSTOM_DATE_TIME_FORMAT_ENABLED, MARK_AS_SEEN, DM_MARK_AS_SEEN, CHECK_ACTIVITY,
|
||||
CHECK_UPDATES, SWAP_DATE_TIME_FORMAT_ENABLED})
|
||||
CHECK_UPDATES, SWAP_DATE_TIME_FORMAT_ENABLED, PREF_ENABLE_DM_NOTIFICATIONS, PREF_ENABLE_DM_AUTO_REFRESH})
|
||||
public @interface BooleanSettings {}
|
||||
|
||||
@StringDef({PREV_INSTALL_VERSION, BROWSER_UA_CODE, APP_UA_CODE})
|
||||
@StringDef({PREV_INSTALL_VERSION, BROWSER_UA_CODE, APP_UA_CODE, PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER})
|
||||
public @interface IntegerSettings {}
|
||||
}
|
@ -10,6 +10,7 @@ import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.media.MediaScannerConnection;
|
||||
import android.media.MediaScannerConnection.OnScanCompletedListener;
|
||||
import android.net.Uri;
|
||||
@ -28,10 +29,13 @@ import android.view.inputmethod.InputMethodManager;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat;
|
||||
|
||||
import com.google.android.exoplayer2.database.ExoDatabaseProvider;
|
||||
import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor;
|
||||
@ -347,4 +351,15 @@ public final class Utils {
|
||||
Log.e(TAG, "hideKeyboard: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Drawable getAnimatableDrawable(@NonNull final Context context,
|
||||
@DrawableRes final int drawableResId) {
|
||||
final Drawable drawable;
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
drawable = ContextCompat.getDrawable(context, drawableResId);
|
||||
} else {
|
||||
drawable = AnimatedVectorDrawableCompat.create(context, drawableResId);
|
||||
}
|
||||
return drawable;
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user