mirror of
https://github.com/KokaKiwi/BarInsta
synced 2024-11-17 20:27:29 +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.preference:preference:1.1.1"
|
||||||
implementation "androidx.work:work-runtime:2.5.0"
|
implementation "androidx.work:work-runtime:2.5.0"
|
||||||
implementation 'androidx.palette:palette:1.0.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'
|
implementation 'com.google.guava:guava:27.0.1-android'
|
||||||
|
|
||||||
@ -89,7 +90,7 @@ dependencies {
|
|||||||
annotationProcessor "androidx.room:room-compiler:$room_version"
|
annotationProcessor "androidx.room:room-compiler:$room_version"
|
||||||
|
|
||||||
// CameraX
|
// 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-camera2:$camerax_version"
|
||||||
implementation "androidx.camera:camera-lifecycle:$camerax_version"
|
implementation "androidx.camera:camera-lifecycle:$camerax_version"
|
||||||
implementation "androidx.camera:camera-view:1.0.0-alpha22"
|
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:name="android.hardware.camera.any"
|
||||||
android:required="false" />
|
android:required="false" />
|
||||||
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".InstaGrabberApplication"
|
android:name=".InstaGrabberApplication"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
@ -37,8 +36,8 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND" />
|
<action android:name="android.intent.action.SEND" />
|
||||||
<!--<action android:name="android.intent.action.SEARCH" />-->
|
<!-- <action android:name="android.intent.action.SEARCH" /> -->
|
||||||
<!--<action android:name="android.intent.action.WEB_SEARCH" />-->
|
<!-- <action android:name="android.intent.action.WEB_SEARCH" /> -->
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
@ -147,6 +146,14 @@
|
|||||||
<service
|
<service
|
||||||
android:name=".services.DeleteImageIntentService"
|
android:name=".services.DeleteImageIntentService"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
<service
|
||||||
|
android:name=".services.DMSyncService"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
|
<receiver
|
||||||
|
android:name=".services.DMSyncAlarmReceiver"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
<uses-library
|
<uses-library
|
||||||
android:name="org.apache.http.legacy"
|
android:name="org.apache.http.legacy"
|
||||||
@ -156,4 +163,5 @@
|
|||||||
android:name="fontProviderRequests"
|
android:name="fontProviderRequests"
|
||||||
android:value="Noto Color Emoji Compat" />
|
android:value="Noto Color Emoji Compat" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
@ -38,6 +38,7 @@ import androidx.emoji.text.FontRequestEmojiCompatConfig;
|
|||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
|
import androidx.lifecycle.Observer;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
import androidx.navigation.NavBackStackEntry;
|
import androidx.navigation.NavBackStackEntry;
|
||||||
import androidx.navigation.NavController;
|
import androidx.navigation.NavController;
|
||||||
@ -62,12 +63,15 @@ import awais.instagrabber.asyncs.SuggestionsFetcher;
|
|||||||
import awais.instagrabber.customviews.emoji.EmojiVariantManager;
|
import awais.instagrabber.customviews.emoji.EmojiVariantManager;
|
||||||
import awais.instagrabber.databinding.ActivityMainBinding;
|
import awais.instagrabber.databinding.ActivityMainBinding;
|
||||||
import awais.instagrabber.fragments.PostViewV2Fragment;
|
import awais.instagrabber.fragments.PostViewV2Fragment;
|
||||||
|
import awais.instagrabber.fragments.directmessages.DirectMessageInboxFragmentDirections;
|
||||||
import awais.instagrabber.fragments.main.FeedFragment;
|
import awais.instagrabber.fragments.main.FeedFragment;
|
||||||
|
import awais.instagrabber.fragments.settings.PreferenceKeys;
|
||||||
import awais.instagrabber.interfaces.FetchListener;
|
import awais.instagrabber.interfaces.FetchListener;
|
||||||
import awais.instagrabber.models.IntentModel;
|
import awais.instagrabber.models.IntentModel;
|
||||||
import awais.instagrabber.models.SuggestionModel;
|
import awais.instagrabber.models.SuggestionModel;
|
||||||
import awais.instagrabber.models.enums.SuggestionType;
|
import awais.instagrabber.models.enums.SuggestionType;
|
||||||
import awais.instagrabber.services.ActivityCheckerService;
|
import awais.instagrabber.services.ActivityCheckerService;
|
||||||
|
import awais.instagrabber.services.DMSyncAlarmReceiver;
|
||||||
import awais.instagrabber.utils.AppExecutors;
|
import awais.instagrabber.utils.AppExecutors;
|
||||||
import awais.instagrabber.utils.Constants;
|
import awais.instagrabber.utils.Constants;
|
||||||
import awais.instagrabber.utils.CookieUtils;
|
import awais.instagrabber.utils.CookieUtils;
|
||||||
@ -159,6 +163,14 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
|
|||||||
EmojiVariantManager.getInstance();
|
EmojiVariantManager.getInstance();
|
||||||
});
|
});
|
||||||
initEmojiCompat();
|
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
|
@Override
|
||||||
@ -247,15 +259,22 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void createNotificationChannels() {
|
private void createNotificationChannels() {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
|
||||||
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(getApplicationContext());
|
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(getApplicationContext());
|
||||||
notificationManager.createNotificationChannel(new NotificationChannel(Constants.DOWNLOAD_CHANNEL_ID,
|
notificationManager.createNotificationChannel(new NotificationChannel(Constants.DOWNLOAD_CHANNEL_ID,
|
||||||
Constants.DOWNLOAD_CHANNEL_NAME,
|
Constants.DOWNLOAD_CHANNEL_NAME,
|
||||||
NotificationManager.IMPORTANCE_DEFAULT));
|
NotificationManager.IMPORTANCE_DEFAULT));
|
||||||
notificationManager.createNotificationChannel(new NotificationChannel(Constants.ACTIVITY_CHANNEL_ID,
|
notificationManager.createNotificationChannel(new NotificationChannel(Constants.ACTIVITY_CHANNEL_ID,
|
||||||
Constants.ACTIVITY_CHANNEL_NAME,
|
Constants.ACTIVITY_CHANNEL_NAME,
|
||||||
NotificationManager.IMPORTANCE_DEFAULT));
|
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() {
|
private void setupSuggestions() {
|
||||||
@ -521,6 +540,10 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
|
|||||||
showActivityView();
|
showActivityView();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (Constants.ACTION_SHOW_DM_THREAD.equals(action)) {
|
||||||
|
showThread(intent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (Intent.ACTION_SEND.equals(action) && type != null) {
|
if (Intent.ACTION_SEND.equals(action) && type != null) {
|
||||||
if (type.equals("text/plain")) {
|
if (type.equals("text/plain")) {
|
||||||
handleUrl(intent.getStringExtra(Intent.EXTRA_TEXT));
|
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) {
|
private void handleUrl(final String url) {
|
||||||
if (url == null) return;
|
if (url == null) return;
|
||||||
// Log.d(TAG, url);
|
// Log.d(TAG, url);
|
||||||
|
@ -12,26 +12,17 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||||||
import com.facebook.drawee.view.SimpleDraweeView;
|
import com.facebook.drawee.view.SimpleDraweeView;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import awais.instagrabber.R;
|
import awais.instagrabber.R;
|
||||||
import awais.instagrabber.adapters.DirectMessageInboxAdapter.OnItemClickListener;
|
import awais.instagrabber.adapters.DirectMessageInboxAdapter.OnItemClickListener;
|
||||||
import awais.instagrabber.databinding.LayoutDmInboxItemBinding;
|
import awais.instagrabber.databinding.LayoutDmInboxItemBinding;
|
||||||
import awais.instagrabber.models.enums.DirectItemType;
|
|
||||||
import awais.instagrabber.models.enums.MediaItemType;
|
import awais.instagrabber.models.enums.MediaItemType;
|
||||||
import awais.instagrabber.repositories.responses.User;
|
import awais.instagrabber.repositories.responses.User;
|
||||||
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
|
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.DirectThread;
|
||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadDirectStory;
|
import awais.instagrabber.repositories.responses.directmessages.DirectThreadDirectStory;
|
||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadLastSeenAt;
|
import awais.instagrabber.utils.DMUtils;
|
||||||
import awais.instagrabber.repositories.responses.directmessages.RavenExpiringMediaActionSummary;
|
|
||||||
import awais.instagrabber.utils.ResponseBodyUtils;
|
|
||||||
import awais.instagrabber.utils.TextUtils;
|
import awais.instagrabber.utils.TextUtils;
|
||||||
|
|
||||||
public final class DirectInboxItemViewHolder extends RecyclerView.ViewHolder {
|
public final class DirectInboxItemViewHolder extends RecyclerView.ViewHolder {
|
||||||
@ -133,218 +124,17 @@ public final class DirectInboxItemViewHolder extends RecyclerView.ViewHolder {
|
|||||||
if (directStory != null && !directStory.getItems().isEmpty()) {
|
if (directStory != null && !directStory.getItems().isEmpty()) {
|
||||||
final DirectItem item = directStory.getItems().get(0);
|
final DirectItem item = directStory.getItems().get(0);
|
||||||
final MediaItemType mediaType = item.getVisualMedia().getMedia().getMediaType();
|
final MediaItemType mediaType = item.getVisualMedia().getMedia().getMediaType();
|
||||||
final String username = getUsername(thread.getUsers(), item.getUserId(), viewerId, resources);
|
final String username = DMUtils.getUsername(thread.getUsers(), item.getUserId(), viewerId, resources);
|
||||||
final String subtitle = getMediaSpecificSubtitle(username, resources, mediaType);
|
final String subtitle = DMUtils.getMediaSpecificSubtitle(username, resources, mediaType);
|
||||||
binding.subtitle.setText(subtitle);
|
binding.subtitle.setText(subtitle);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final DirectItem item = thread.getFirstDirectItem();
|
final DirectItem item = thread.getFirstDirectItem();
|
||||||
if (item == null) return;
|
if (item == null) return;
|
||||||
final long senderId = item.getUserId();
|
final String subtitle = DMUtils.getMessageString(thread, resources, viewerId, item);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
binding.subtitle.setText(subtitle != null ? subtitle : "");
|
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) {
|
private void setDateTime(@NonNull final DirectItem item) {
|
||||||
final long timestamp = item.getTimestamp() / 1000;
|
final long timestamp = item.getTimestamp() / 1000;
|
||||||
final String dateTimeString = TextUtils.getRelativeDateTimeString(itemView.getContext(), timestamp);
|
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) {
|
private void setReadState(@NonNull final DirectThread thread) {
|
||||||
final boolean read;
|
final boolean read = DMUtils.isRead(thread);
|
||||||
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()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
binding.unread.setVisibility(read ? View.GONE : View.VISIBLE);
|
binding.unread.setVisibility(read ? View.GONE : View.VISIBLE);
|
||||||
binding.threadTitle.setTypeface(binding.threadTitle.getTypeface(), read ? Typeface.NORMAL : Typeface.BOLD);
|
binding.threadTitle.setTypeface(binding.threadTitle.getTypeface(), read ? Typeface.NORMAL : Typeface.BOLD);
|
||||||
binding.subtitle.setTypeface(binding.subtitle.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.DirectItemReactions;
|
||||||
import awais.instagrabber.repositories.responses.directmessages.DirectItemStoryShare;
|
import awais.instagrabber.repositories.responses.directmessages.DirectItemStoryShare;
|
||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
||||||
|
import awais.instagrabber.utils.DMUtils;
|
||||||
import awais.instagrabber.utils.DeepLinkParser;
|
import awais.instagrabber.utils.DeepLinkParser;
|
||||||
import awais.instagrabber.utils.ResponseBodyUtils;
|
import awais.instagrabber.utils.ResponseBodyUtils;
|
||||||
|
|
||||||
@ -196,9 +197,9 @@ public abstract class DirectItemViewHolder extends RecyclerView.ViewHolder imple
|
|||||||
if (item.isPending()) {
|
if (item.isPending()) {
|
||||||
binding.deliveryStatus.setImageResource(R.drawable.ic_check_24);
|
binding.deliveryStatus.setImageResource(R.drawable.ic_check_24);
|
||||||
} else {
|
} else {
|
||||||
final boolean read = ResponseBodyUtils.isRead(item,
|
final boolean read = DMUtils.isRead(item,
|
||||||
thread.getLastSeenAt(),
|
thread.getLastSeenAt(),
|
||||||
userIds
|
userIds
|
||||||
);
|
);
|
||||||
binding.deliveryStatus.setImageResource(R.drawable.ic_check_all_24);
|
binding.deliveryStatus.setImageResource(R.drawable.ic_check_all_24);
|
||||||
ImageViewCompat.setImageTintList(
|
ImageViewCompat.setImageTintList(
|
||||||
@ -324,8 +325,8 @@ public abstract class DirectItemViewHolder extends RecyclerView.ViewHolder imple
|
|||||||
final String repliedToUsername = user != null ? user.getUsername() : "";
|
final String repliedToUsername = user != null ? user.getUsername() : "";
|
||||||
if (item.getUserId() == currentUser.getPk()) {
|
if (item.getUserId() == currentUser.getPk()) {
|
||||||
return thread.isGroup()
|
return thread.isGroup()
|
||||||
? resources.getString(R.string.replied_you_group, repliedToUsername)
|
? resources.getString(R.string.replied_you_group, repliedToUsername)
|
||||||
: resources.getString(R.string.replied_you);
|
: resources.getString(R.string.replied_you);
|
||||||
}
|
}
|
||||||
if (repliedToUserId == currentUser.getPk()) {
|
if (repliedToUserId == currentUser.getPk()) {
|
||||||
return resources.getString(R.string.replied_to_you);
|
return resources.getString(R.string.replied_to_you);
|
||||||
|
@ -20,14 +20,16 @@ import java.util.Date;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import awais.instagrabber.db.dao.AccountDao;
|
import awais.instagrabber.db.dao.AccountDao;
|
||||||
|
import awais.instagrabber.db.dao.DMLastNotifiedDao;
|
||||||
import awais.instagrabber.db.dao.FavoriteDao;
|
import awais.instagrabber.db.dao.FavoriteDao;
|
||||||
import awais.instagrabber.db.entities.Account;
|
import awais.instagrabber.db.entities.Account;
|
||||||
|
import awais.instagrabber.db.entities.DMLastNotified;
|
||||||
import awais.instagrabber.db.entities.Favorite;
|
import awais.instagrabber.db.entities.Favorite;
|
||||||
import awais.instagrabber.models.enums.FavoriteType;
|
import awais.instagrabber.models.enums.FavoriteType;
|
||||||
import awais.instagrabber.utils.Utils;
|
import awais.instagrabber.utils.Utils;
|
||||||
|
|
||||||
@Database(entities = {Account.class, Favorite.class},
|
@Database(entities = {Account.class, Favorite.class, DMLastNotified.class},
|
||||||
version = 4)
|
version = 5)
|
||||||
@TypeConverters({Converters.class})
|
@TypeConverters({Converters.class})
|
||||||
public abstract class AppDatabase extends RoomDatabase {
|
public abstract class AppDatabase extends RoomDatabase {
|
||||||
private static final String TAG = AppDatabase.class.getSimpleName();
|
private static final String TAG = AppDatabase.class.getSimpleName();
|
||||||
@ -38,12 +40,14 @@ public abstract class AppDatabase extends RoomDatabase {
|
|||||||
|
|
||||||
public abstract FavoriteDao favoriteDao();
|
public abstract FavoriteDao favoriteDao();
|
||||||
|
|
||||||
|
public abstract DMLastNotifiedDao dmLastNotifiedDao();
|
||||||
|
|
||||||
public static AppDatabase getDatabase(final Context context) {
|
public static AppDatabase getDatabase(final Context context) {
|
||||||
if (INSTANCE == null) {
|
if (INSTANCE == null) {
|
||||||
synchronized (AppDatabase.class) {
|
synchronized (AppDatabase.class) {
|
||||||
if (INSTANCE == null) {
|
if (INSTANCE == null) {
|
||||||
INSTANCE = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "cookiebox.db")
|
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();
|
.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
|
@NonNull
|
||||||
private static List<Favorite> backupOldFavorites(@NonNull final SupportSQLiteDatabase db) {
|
private static List<Favorite> backupOldFavorites(@NonNull final SupportSQLiteDatabase db) {
|
||||||
// check if old favorites table had the column query_display
|
// check if old favorites table had the column query_display
|
||||||
|
@ -2,6 +2,10 @@ package awais.instagrabber.db;
|
|||||||
|
|
||||||
import androidx.room.TypeConverter;
|
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 java.util.Date;
|
||||||
|
|
||||||
import awais.instagrabber.models.enums.FavoriteType;
|
import awais.instagrabber.models.enums.FavoriteType;
|
||||||
@ -30,4 +34,16 @@ public class Converters {
|
|||||||
public static String favoriteTypeToString(FavoriteType favoriteType) {
|
public static String favoriteTypeToString(FavoriteType favoriteType) {
|
||||||
return favoriteType == null ? null : favoriteType.toString();
|
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;
|
package awais.instagrabber.fragments.settings;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.TypedArray;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.widget.AppCompatButton;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.appcompat.widget.AppCompatTextView;
|
|
||||||
import androidx.navigation.NavDirections;
|
import androidx.navigation.NavDirections;
|
||||||
import androidx.navigation.fragment.NavHostFragment;
|
import androidx.navigation.fragment.NavHostFragment;
|
||||||
import androidx.preference.ListPreference;
|
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
import androidx.preference.PreferenceCategory;
|
|
||||||
import androidx.preference.PreferenceScreen;
|
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.List;
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
import awais.instagrabber.R;
|
import awais.instagrabber.R;
|
||||||
import awais.instagrabber.dialogs.TimeSettingsDialog;
|
|
||||||
import awais.instagrabber.utils.Constants;
|
import awais.instagrabber.utils.Constants;
|
||||||
import awais.instagrabber.utils.CookieUtils;
|
import awais.instagrabber.utils.CookieUtils;
|
||||||
import awais.instagrabber.utils.DirectoryChooser;
|
|
||||||
import awais.instagrabber.utils.LocaleUtils;
|
|
||||||
import awais.instagrabber.utils.TextUtils;
|
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.fragments.settings.SettingsPreferencesFragmentDirections.actionSettingsToDm;
|
||||||
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
|
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;
|
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||||
|
|
||||||
public class SettingsPreferencesFragment extends BasePreferencesFragment {
|
public class SettingsPreferencesFragment extends BasePreferencesFragment {
|
||||||
private static final String TAG = "SettingsPrefsFrag";
|
private static final String TAG = SettingsPreferencesFragment.class.getSimpleName();
|
||||||
private boolean isLoggedIn;
|
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
|
@Override
|
||||||
void setupPreferenceScreen(final PreferenceScreen screen) {
|
void setupPreferenceScreen(final PreferenceScreen screen) {
|
||||||
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
|
||||||
isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0;
|
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
if (context == null) return;
|
if (context == null) return;
|
||||||
final PreferenceCategory generalCategory = new PreferenceCategory(context);
|
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||||
screen.addPreference(generalCategory);
|
final boolean isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0;
|
||||||
generalCategory.setTitle(R.string.pref_category_general);
|
for (final SettingScreen settingScreen : screens) {
|
||||||
generalCategory.setIconSpaceReserved(false);
|
if (settingScreen.isLoginRequired() && !isLoggedIn) continue;
|
||||||
generalCategory.addPreference(getThemePreference(context));
|
screen.addPreference(getNavPreference(context, settingScreen));
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
// else {
|
// else {
|
||||||
// final PreferenceCategory anonUsersPreferenceCategory = new PreferenceCategory(context);
|
// final PreferenceCategory anonUsersPreferenceCategory = new PreferenceCategory(context);
|
||||||
@ -95,275 +59,43 @@ public class SettingsPreferencesFragment extends BasePreferencesFragment {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
private Preference getLanguagePreference() {
|
private Preference getNavPreference(@NonNull final Context context,
|
||||||
final Context context = getContext();
|
@NonNull final SettingScreen settingScreen) {
|
||||||
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) {
|
|
||||||
final Preference preference = new Preference(context);
|
final Preference preference = new Preference(context);
|
||||||
preference.setTitle(R.string.pref_category_theme);
|
preference.setTitle(settingScreen.getTitleResId());
|
||||||
// preference.setIcon(R.drawable.ic_format_paint_24);
|
|
||||||
preference.setIconSpaceReserved(false);
|
preference.setIconSpaceReserved(false);
|
||||||
preference.setOnPreferenceClickListener(preference1 -> {
|
preference.setOnPreferenceClickListener(preference1 -> {
|
||||||
final NavDirections navDirections = SettingsPreferencesFragmentDirections.actionSettingsPreferencesFragmentToThemePreferencesFragment();
|
NavHostFragment.findNavController(this).navigate(settingScreen.getDirections());
|
||||||
NavHostFragment.findNavController(this).navigate(navDirections);
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
return preference;
|
return preference;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Preference getDownloadUserFolderPreference() {
|
private static class SettingScreen {
|
||||||
final Context context = getContext();
|
private final int titleResId;
|
||||||
if (context == null) return null;
|
private final NavDirections directions;
|
||||||
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);
|
private final boolean loginRequired;
|
||||||
preference.setKey(Constants.DOWNLOAD_USER_FOLDER);
|
|
||||||
preference.setTitle(R.string.download_user_folder);
|
|
||||||
preference.setIconSpaceReserved(false);
|
|
||||||
return preference;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Preference getSaveToCustomFolderPreference() {
|
public SettingScreen(@StringRes final int titleResId, final NavDirections directions) {
|
||||||
final Context context = getContext();
|
this(titleResId, directions, false);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public SettingScreen(@StringRes final int titleResId, final NavDirections directions, final boolean loginRequired) {
|
||||||
public void onBindViewHolder(final PreferenceViewHolder holder) {
|
this.titleResId = titleResId;
|
||||||
super.onBindViewHolder(holder);
|
this.directions = directions;
|
||||||
final SwitchMaterial cbSaveTo = (SwitchMaterial) holder.findViewById(R.id.cbSaveTo);
|
this.loginRequired = loginRequired;
|
||||||
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 {
|
public int getTitleResId() {
|
||||||
void onResult(String result);
|
return titleResId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface OnSelectFolderButtonClickListener {
|
public NavDirections getDirections() {
|
||||||
void onClick(ResultCallback resultCallback);
|
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;
|
if (insertIndex < 0) return;
|
||||||
synchronized (this.inbox) {
|
synchronized (this.inbox) {
|
||||||
final DirectInbox currentDirectInbox = getCurrentDirectInbox();
|
final DirectInbox currentDirectInbox = getCurrentDirectInbox();
|
||||||
|
if (currentDirectInbox == null) return;
|
||||||
final List<DirectThread> threadsCopy = new LinkedList<>(currentDirectInbox.getThreads());
|
final List<DirectThread> threadsCopy = new LinkedList<>(currentDirectInbox.getThreads());
|
||||||
threadsCopy.add(insertIndex, thread);
|
threadsCopy.add(insertIndex, thread);
|
||||||
try {
|
try {
|
||||||
@ -338,6 +339,7 @@ public final class InboxManager {
|
|||||||
public void removeThread(@NonNull final String threadId) {
|
public void removeThread(@NonNull final String threadId) {
|
||||||
synchronized (this.inbox) {
|
synchronized (this.inbox) {
|
||||||
final DirectInbox currentDirectInbox = getCurrentDirectInbox();
|
final DirectInbox currentDirectInbox = getCurrentDirectInbox();
|
||||||
|
if (currentDirectInbox == null) return;
|
||||||
final List<DirectThread> threadsCopy = currentDirectInbox.getThreads()
|
final List<DirectThread> threadsCopy = currentDirectInbox.getThreads()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(t -> !t.getThreadId().equals(threadId))
|
.filter(t -> !t.getThreadId().equals(threadId))
|
||||||
|
@ -382,6 +382,8 @@ public final class ThreadManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void fetchPendingRequests() {
|
public void fetchPendingRequests() {
|
||||||
|
final Boolean isGroup = this.isGroup.getValue();
|
||||||
|
if (isGroup == null || !isGroup) return;
|
||||||
final Call<DirectThreadParticipantRequestsResponse> request = service.participantRequests(threadId, 1, null);
|
final Call<DirectThreadParticipantRequestsResponse> request = service.participantRequests(threadId, 1, null);
|
||||||
request.enqueue(new Callback<DirectThreadParticipantRequestsResponse>() {
|
request.enqueue(new Callback<DirectThreadParticipantRequestsResponse>() {
|
||||||
|
|
||||||
|
@ -2,6 +2,9 @@ package awais.instagrabber.repositories.responses.directmessages;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -41,6 +44,7 @@ public class DirectItem implements Cloneable {
|
|||||||
private Date date;
|
private Date date;
|
||||||
private boolean isPending;
|
private boolean isPending;
|
||||||
private boolean showForwardAttribution;
|
private boolean showForwardAttribution;
|
||||||
|
private LocalDateTime localDateTime;
|
||||||
|
|
||||||
public DirectItem(final String itemId,
|
public DirectItem(final String itemId,
|
||||||
final long userId,
|
final long userId,
|
||||||
@ -214,6 +218,13 @@ public class DirectItem implements Cloneable {
|
|||||||
return date;
|
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) {
|
public void setItemId(final String itemId) {
|
||||||
this.itemId = itemId;
|
this.itemId = itemId;
|
||||||
}
|
}
|
||||||
|
@ -133,6 +133,6 @@ public class ActivityCheckerService extends Service {
|
|||||||
final Intent intent = new Intent(getApplicationContext(), MainActivity.class)
|
final Intent intent = new Intent(getApplicationContext(), MainActivity.class)
|
||||||
.setAction(Constants.ACTION_SHOW_ACTIVITY)
|
.setAction(Constants.ACTION_SHOW_ACTIVITY)
|
||||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
.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
|
// Notification ids
|
||||||
public static final int ACTIVITY_NOTIFICATION_ID = 10;
|
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
|
// 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\":" +
|
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 FDROID_SHA1_FINGERPRINT = "C1661EB8FD09F618307E687786D5E5056F65084D";
|
||||||
public static final String SKIPPED_VERSION = "skipped_version";
|
public static final String SKIPPED_VERSION = "skipped_version";
|
||||||
public static final String DEFAULT_TAB = "default_tab";
|
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_DARK_THEME = "dark_theme";
|
||||||
public static final String PREF_LIGHT_THEME = "light_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";
|
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_SAVED_POSTS_LAYOUT = "saved_posts_layout";
|
||||||
public static final String PREF_EMOJI_VARIANTS = "emoji_variants";
|
public static final String PREF_EMOJI_VARIANTS = "emoji_variants";
|
||||||
public static final String PREF_REACTIONS = "reactions";
|
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 androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@ -34,4 +35,8 @@ public final class DateUtils {
|
|||||||
final Calendar calendar = Calendar.getInstance(Locale.getDefault());
|
final Calendar calendar = Calendar.getInstance(Locale.getDefault());
|
||||||
return -(calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET)) / (60 * 1000);
|
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) {
|
if (settingsHelper.getInteger(Constants.PREV_INSTALL_VERSION) < BuildConfig.VERSION_CODE) {
|
||||||
int appUaCode = settingsHelper.getInteger(Constants.APP_UA_CODE);
|
int appUaCode = settingsHelper.getInteger(Constants.APP_UA_CODE);
|
||||||
int browserUaCode = settingsHelper.getInteger(Constants.BROWSER_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);
|
browserUaCode = ThreadLocalRandom.current().nextInt(0, UserAgentUtils.browsers.length);
|
||||||
settingsHelper.putInteger(Constants.BROWSER_UA_CODE, browserUaCode);
|
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);
|
appUaCode = ThreadLocalRandom.current().nextInt(0, UserAgentUtils.devices.length);
|
||||||
settingsHelper.putInteger(Constants.APP_UA_CODE, appUaCode);
|
settingsHelper.putInteger(Constants.APP_UA_CODE, appUaCode);
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@ import org.json.JSONObject;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import awais.instagrabber.BuildConfig;
|
import awais.instagrabber.BuildConfig;
|
||||||
import awais.instagrabber.models.StoryModel;
|
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.MediaCandidate;
|
||||||
import awais.instagrabber.repositories.responses.User;
|
import awais.instagrabber.repositories.responses.User;
|
||||||
import awais.instagrabber.repositories.responses.VideoVersion;
|
import awais.instagrabber.repositories.responses.VideoVersion;
|
||||||
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
|
|
||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThreadLastSeenAt;
|
|
||||||
import awaisomereport.LogCollector;
|
import awaisomereport.LogCollector;
|
||||||
|
|
||||||
public final class ResponseBodyUtils {
|
public final class ResponseBodyUtils {
|
||||||
@ -1123,25 +1120,6 @@ public final class ResponseBodyUtils {
|
|||||||
return candidate.getUrl();
|
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 {
|
public static StoryModel parseBroadcastItem(final JSONObject data) throws JSONException {
|
||||||
final StoryModel model = new StoryModel(data.getString("id"),
|
final StoryModel model = new StoryModel(data.getString("id"),
|
||||||
data.getString("cover_frame_url"),
|
data.getString("cover_frame_url"),
|
||||||
|
@ -8,6 +8,10 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.StringDef;
|
import androidx.annotation.StringDef;
|
||||||
import androidx.appcompat.app.AppCompatDelegate;
|
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_LANGUAGE;
|
||||||
import static awais.instagrabber.utils.Constants.APP_THEME;
|
import static awais.instagrabber.utils.Constants.APP_THEME;
|
||||||
import static awais.instagrabber.utils.Constants.APP_UA;
|
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,
|
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_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,
|
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 {}
|
public @interface StringSettings {}
|
||||||
|
|
||||||
@StringDef({DOWNLOAD_USER_FOLDER, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS,
|
@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,
|
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 {}
|
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 {}
|
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:name="awais.instagrabber.fragments.settings.SettingsPreferencesFragment"
|
||||||
android:label="@string/action_settings">
|
android:label="@string/action_settings">
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_settingsPreferencesFragment_to_themePreferencesFragment"
|
android:id="@+id/action_settings_to_theme"
|
||||||
app:destination="@id/themePreferencesFragment" />
|
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>
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/aboutFragment"
|
android:id="@+id/aboutFragment"
|
||||||
@ -97,4 +118,32 @@
|
|||||||
android:id="@+id/backupPreferencesFragment"
|
android:id="@+id/backupPreferencesFragment"
|
||||||
android:name="awais.instagrabber.fragments.settings.BackupPreferencesFragment"
|
android:name="awais.instagrabber.fragments.settings.BackupPreferencesFragment"
|
||||||
android:label="@string/backup_and_restore" />
|
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>
|
</navigation>
|
@ -124,4 +124,12 @@
|
|||||||
<item>@style/AppTheme.Dark.Black</item>
|
<item>@style/AppTheme.Dark.Black</item>
|
||||||
<item>@style/AppTheme.Dark.MaterialDark</item>
|
<item>@style/AppTheme.Dark.MaterialDark</item>
|
||||||
</string-array>
|
</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>
|
</resources>
|
||||||
|
@ -474,4 +474,14 @@
|
|||||||
<string name="accept">Accept</string>
|
<string name="accept">Accept</string>
|
||||||
<string name="you">You</string>
|
<string name="you">You</string>
|
||||||
<string name="no_pending_requests">No pending requests</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>
|
</resources>
|
||||||
|
Loading…
Reference in New Issue
Block a user