mirror of
https://github.com/KokaKiwi/BarInsta
synced 2024-12-22 21:16:57 +00:00
DM sync service
This commit is contained in:
parent
605f15ee3a
commit
df5a96e035
@ -79,6 +79,7 @@ dependencies {
|
||||
implementation "androidx.preference:preference:1.1.1"
|
||||
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,7 +90,7 @@ dependencies {
|
||||
annotationProcessor "androidx.room:room-compiler:$room_version"
|
||||
|
||||
// CameraX
|
||||
def camerax_version = "1.0.0-alpha02"
|
||||
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-alpha22"
|
||||
|
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')"
|
||||
]
|
||||
}
|
||||
}
|
@ -13,7 +13,6 @@
|
||||
android:name="android.hardware.camera.any"
|
||||
android:required="false" />
|
||||
|
||||
|
||||
<application
|
||||
android:name=".InstaGrabberApplication"
|
||||
android:allowBackup="true"
|
||||
@ -37,8 +36,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 +146,14 @@
|
||||
<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" />
|
||||
|
||||
<uses-library
|
||||
android:name="org.apache.http.legacy"
|
||||
@ -156,4 +163,5 @@
|
||||
android:name="fontProviderRequests"
|
||||
android:value="Noto Color Emoji Compat" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -38,6 +38,7 @@ 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;
|
||||
@ -62,12 +63,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;
|
||||
@ -159,6 +163,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
|
||||
@ -247,15 +259,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() {
|
||||
@ -521,6 +540,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));
|
||||
@ -534,6 +557,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);
|
||||
|
@ -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,19 +142,7 @@ public final class DirectInboxItemViewHolder extends RecyclerView.ViewHolder {
|
||||
}
|
||||
|
||||
private void setReadState(@NonNull final DirectThread thread) {
|
||||
final boolean read;
|
||||
if (thread.getDirectStory() != null) {
|
||||
read = false;
|
||||
} else {
|
||||
final DirectItem item = thread.getFirstDirectItem();
|
||||
if (item.getUserId() == thread.getViewerId()) {
|
||||
// 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 = ResponseBodyUtils.isRead(item, lastSeenAtMap, Collections.singletonList(thread.getViewerId()));
|
||||
}
|
||||
}
|
||||
final boolean read = DMUtils.isRead(thread);
|
||||
binding.unread.setVisibility(read ? View.GONE : View.VISIBLE);
|
||||
binding.threadTitle.setTypeface(binding.threadTitle.getTypeface(), read ? Typeface.NORMAL : Typeface.BOLD);
|
||||
binding.subtitle.setTypeface(binding.subtitle.getTypeface(), 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,9 +197,9 @@ 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
|
||||
final boolean read = DMUtils.isRead(item,
|
||||
thread.getLastSeenAt(),
|
||||
userIds
|
||||
);
|
||||
binding.deliveryStatus.setImageResource(R.drawable.ic_check_all_24);
|
||||
ImageViewCompat.setImageTintList(
|
||||
@ -324,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);
|
||||
|
@ -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,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 = 5;
|
||||
}
|
||||
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,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 awais.instagrabber.R;
|
||||
import awais.instagrabber.utils.Constants;
|
||||
import awais.instagrabber.utils.LocaleUtils;
|
||||
import awais.instagrabber.utils.UserAgentUtils;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -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,94 @@
|
||||
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 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.Utils;
|
||||
|
||||
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));
|
||||
screen.addPreference(getPostTimeFormatPreference(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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -323,6 +323,7 @@ public final class InboxManager {
|
||||
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 {
|
||||
@ -338,6 +339,7 @@ public final class InboxManager {
|
||||
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))
|
||||
|
@ -382,6 +382,8 @@ public final class ThreadManager {
|
||||
}
|
||||
|
||||
public void fetchPendingRequests() {
|
||||
final Boolean isGroup = this.isGroup.getValue();
|
||||
if (isGroup == null || !isGroup) return;
|
||||
final Call<DirectThreadParticipantRequestsResponse> request = service.participantRequests(threadId, 1, null);
|
||||
request.enqueue(new Callback<DirectThreadParticipantRequestsResponse>() {
|
||||
|
||||
|
@ -2,6 +2,9 @@ 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;
|
||||
@ -41,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,
|
||||
@ -214,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;
|
||||
}
|
||||
|
@ -133,6 +133,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,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 = 5;
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
248
app/src/main/java/awais/instagrabber/services/DMSyncService.java
Normal file
248
app/src/main/java/awais/instagrabber/services/DMSyncService.java
Normal file
@ -0,0 +1,248 @@
|
||||
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.DMUtils;
|
||||
import awais.instagrabber.utils.DateUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
|
||||
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 boolean notificationsEnabled = Utils.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();
|
||||
}
|
||||
}
|
@ -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";
|
||||
}
|
273
app/src/main/java/awais/instagrabber/utils/DMUtils.java
Normal file
273
app/src/main/java/awais/instagrabber/utils/DMUtils.java
Normal file
@ -0,0 +1,273 @@
|
||||
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.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:
|
||||
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.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);
|
||||
}
|
||||
}
|
||||
|
@ -105,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,8 +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.DirectThreadLastSeenAt;
|
||||
import awaisomereport.LogCollector;
|
||||
|
||||
public final class ResponseBodyUtils {
|
||||
@ -1123,25 +1120,6 @@ public final class ResponseBodyUtils {
|
||||
return candidate.getUrl();
|
||||
}
|
||||
|
||||
public static boolean isRead(final DirectItem item,
|
||||
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 StoryModel parseBroadcastItem(final JSONObject data) throws JSONException {
|
||||
final StoryModel model = new StoryModel(data.getString("id"),
|
||||
data.getString("cover_frame_url"),
|
||||
|
@ -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
app/src/main/res/drawable/ic_round_mode_comment_24.xml
Normal file
10
app/src/main/res/drawable/ic_round_mode_comment_24.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M22,4c0,-1.1 -0.9,-2 -2,-2H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h14l4,4V4z"/>
|
||||
</vector>
|
33
app/src/main/res/layout/pref_auto_refresh_dm_freq.xml
Normal file
33
app/src/main/res/layout/pref_auto_refresh_dm_freq.xml
Normal file
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:animateLayoutChanges="true"
|
||||
android:clickable="false"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="15dp"
|
||||
android:paddingEnd="15dp">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/start_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/auto_refresh_every"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatEditText
|
||||
android:id="@+id/freq_num"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="2dp"
|
||||
android:layout_marginEnd="2dp"
|
||||
android:inputType="number"
|
||||
android:textAlignment="center" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatSpinner
|
||||
android:id="@+id/freq_unit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
@ -78,8 +78,29 @@
|
||||
android:name="awais.instagrabber.fragments.settings.SettingsPreferencesFragment"
|
||||
android:label="@string/action_settings">
|
||||
<action
|
||||
android:id="@+id/action_settingsPreferencesFragment_to_themePreferencesFragment"
|
||||
android:id="@+id/action_settings_to_theme"
|
||||
app:destination="@id/themePreferencesFragment" />
|
||||
<action
|
||||
android:id="@+id/action_settings_to_locale"
|
||||
app:destination="@id/localePreferencesFragment" />
|
||||
<action
|
||||
android:id="@+id/action_settings_to_general"
|
||||
app:destination="@id/generalPreferencesFragment" />
|
||||
<action
|
||||
android:id="@+id/action_settings_to_downloads"
|
||||
app:destination="@id/downloadsPreferencesFragment" />
|
||||
<action
|
||||
android:id="@+id/action_settings_to_dm"
|
||||
app:destination="@id/DMPreferencesFragment" />
|
||||
<action
|
||||
android:id="@+id/action_settings_to_stories"
|
||||
app:destination="@id/storiesPreferencesFragment" />
|
||||
<action
|
||||
android:id="@+id/action_settings_to_notifications"
|
||||
app:destination="@id/notificationsPreferencesFragment" />
|
||||
<action
|
||||
android:id="@+id/action_settings_to_post"
|
||||
app:destination="@id/postPreferencesFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/aboutFragment"
|
||||
@ -97,4 +118,32 @@
|
||||
android:id="@+id/backupPreferencesFragment"
|
||||
android:name="awais.instagrabber.fragments.settings.BackupPreferencesFragment"
|
||||
android:label="@string/backup_and_restore" />
|
||||
<fragment
|
||||
android:id="@+id/localePreferencesFragment"
|
||||
android:name="awais.instagrabber.fragments.settings.LocalePreferencesFragment"
|
||||
android:label="@string/pref_category_locale" />
|
||||
<fragment
|
||||
android:id="@+id/generalPreferencesFragment"
|
||||
android:name="awais.instagrabber.fragments.settings.GeneralPreferencesFragment"
|
||||
android:label="@string/pref_category_general" />
|
||||
<fragment
|
||||
android:id="@+id/downloadsPreferencesFragment"
|
||||
android:name="awais.instagrabber.fragments.settings.DownloadsPreferencesFragment"
|
||||
android:label="@string/pref_category_downloads" />
|
||||
<fragment
|
||||
android:id="@+id/DMPreferencesFragment"
|
||||
android:name="awais.instagrabber.fragments.settings.DMPreferencesFragment"
|
||||
android:label="@string/pref_category_dm" />
|
||||
<fragment
|
||||
android:id="@+id/storiesPreferencesFragment"
|
||||
android:name="awais.instagrabber.fragments.settings.StoriesPreferencesFragment"
|
||||
android:label="@string/pref_category_stories" />
|
||||
<fragment
|
||||
android:id="@+id/notificationsPreferencesFragment"
|
||||
android:name="awais.instagrabber.fragments.settings.NotificationsPreferencesFragment"
|
||||
android:label="@string/pref_category_notifications" />
|
||||
<fragment
|
||||
android:id="@+id/postPreferencesFragment"
|
||||
android:name="awais.instagrabber.fragments.settings.PostPreferencesFragment"
|
||||
android:label="PostPreferencesFragment" />
|
||||
</navigation>
|
@ -124,4 +124,12 @@
|
||||
<item>@style/AppTheme.Dark.Black</item>
|
||||
<item>@style/AppTheme.Dark.MaterialDark</item>
|
||||
</string-array>
|
||||
<string-array name="dm_auto_refresh_freq_units">
|
||||
<item>secs</item>
|
||||
<item>mins</item>
|
||||
</string-array>
|
||||
<string-array name="dm_auto_refresh_freq_unit_labels">
|
||||
<item>@string/secs</item>
|
||||
<item>@string/mins</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
@ -474,4 +474,14 @@
|
||||
<string name="accept">Accept</string>
|
||||
<string name="you">You</string>
|
||||
<string name="no_pending_requests">No pending requests</string>
|
||||
<string name="checking_for_new_messages">Checking for new messages</string>
|
||||
<string name="pref_category_stories">Stories</string>
|
||||
<string name="pref_category_dm">DM</string>
|
||||
<string name="pref_category_notifications">Notifications</string>
|
||||
<string name="pref_category_post">Post</string>
|
||||
<string name="enable_dm_notifications">Enable DM notifications</string>
|
||||
<string name="enable_dm_auto_refesh">Auto refresh messages</string>
|
||||
<string name="auto_refresh_every">Auto refresh every</string>
|
||||
<string name="secs">secs</string>
|
||||
<string name="mins">mins</string>
|
||||
</resources>
|
||||
|
Loading…
Reference in New Issue
Block a user