Ammar Githam
4 years ago
39 changed files with 2074 additions and 598 deletions
-
3app/build.gradle
-
161app/schemas/awais.instagrabber.db.AppDatabase/5.json
-
14app/src/main/AndroidManifest.xml
-
93app/src/main/java/awais/instagrabber/activities/MainActivity.java
-
232app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectInboxItemViewHolder.java
-
11app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemViewHolder.java
-
22app/src/main/java/awais/instagrabber/db/AppDatabase.java
-
16app/src/main/java/awais/instagrabber/db/Converters.java
-
34app/src/main/java/awais/instagrabber/db/dao/DMLastNotifiedDao.java
-
70app/src/main/java/awais/instagrabber/db/datasources/DMLastNotifiedDataSource.java
-
85app/src/main/java/awais/instagrabber/db/entities/DMLastNotified.java
-
126app/src/main/java/awais/instagrabber/db/repositories/DMLastNotifiedRepository.java
-
201app/src/main/java/awais/instagrabber/fragments/settings/DMPreferencesFragment.java
-
101app/src/main/java/awais/instagrabber/fragments/settings/DownloadsPreferencesFragment.java
-
61app/src/main/java/awais/instagrabber/fragments/settings/GeneralPreferencesFragment.java
-
48app/src/main/java/awais/instagrabber/fragments/settings/LocalePreferencesFragment.java
-
43app/src/main/java/awais/instagrabber/fragments/settings/NotificationsPreferencesFragment.java
-
94app/src/main/java/awais/instagrabber/fragments/settings/PostPreferencesFragment.java
-
30app/src/main/java/awais/instagrabber/fragments/settings/PreferenceHelper.java
-
8app/src/main/java/awais/instagrabber/fragments/settings/PreferenceKeys.java
-
362app/src/main/java/awais/instagrabber/fragments/settings/SettingsPreferencesFragment.java
-
48app/src/main/java/awais/instagrabber/fragments/settings/StoriesPreferencesFragment.java
-
2app/src/main/java/awais/instagrabber/managers/InboxManager.java
-
2app/src/main/java/awais/instagrabber/managers/ThreadManager.java
-
11app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItem.java
-
2app/src/main/java/awais/instagrabber/services/ActivityCheckerService.java
-
87app/src/main/java/awais/instagrabber/services/DMSyncAlarmReceiver.java
-
248app/src/main/java/awais/instagrabber/services/DMSyncService.java
-
31app/src/main/java/awais/instagrabber/utils/Constants.java
-
273app/src/main/java/awais/instagrabber/utils/DMUtils.java
-
5app/src/main/java/awais/instagrabber/utils/DateUtils.java
-
4app/src/main/java/awais/instagrabber/utils/FlavorTown.java
-
22app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java
-
10app/src/main/java/awais/instagrabber/utils/SettingsHelper.java
-
10app/src/main/res/drawable/ic_round_mode_comment_24.xml
-
33app/src/main/res/layout/pref_auto_refresh_dm_freq.xml
-
51app/src/main/res/navigation/more_nav_graph.xml
-
8app/src/main/res/values/arrays.xml
-
10app/src/main/res/values/strings.xml
@ -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')" |
|||
] |
|||
} |
|||
} |
@ -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"; |
|||
} |
@ -0,0 +1,48 @@ |
|||
package awais.instagrabber.fragments.settings; |
|||
|
|||
import android.content.Context; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.preference.ListPreference; |
|||
import androidx.preference.Preference; |
|||
import androidx.preference.PreferenceScreen; |
|||
import androidx.preference.SwitchPreferenceCompat; |
|||
|
|||
import awais.instagrabber.R; |
|||
import awais.instagrabber.utils.Constants; |
|||
|
|||
public class StoriesPreferencesFragment extends BasePreferencesFragment { |
|||
@Override |
|||
void setupPreferenceScreen(final PreferenceScreen screen) { |
|||
final Context context = getContext(); |
|||
if (context == null) return; |
|||
screen.addPreference(getStorySortPreference(context)); |
|||
screen.addPreference(getMarkStoriesSeenPreference(context)); |
|||
} |
|||
|
|||
private Preference getStorySortPreference(@NonNull final Context context) { |
|||
final ListPreference preference = new ListPreference(context); |
|||
preference.setSummaryProvider(ListPreference.SimpleSummaryProvider.getInstance()); |
|||
final int length = getResources().getStringArray(R.array.story_sorts).length; |
|||
final String[] values = new String[length]; |
|||
for (int i = 0; i < length; i++) { |
|||
values[i] = String.valueOf(i); |
|||
} |
|||
preference.setKey(Constants.STORY_SORT); |
|||
preference.setTitle(R.string.story_sort_setting); |
|||
preference.setDialogTitle(R.string.story_sort_setting); |
|||
preference.setEntries(R.array.story_sorts); |
|||
preference.setIconSpaceReserved(false); |
|||
preference.setEntryValues(values); |
|||
return preference; |
|||
} |
|||
|
|||
private Preference getMarkStoriesSeenPreference(@NonNull final Context context) { |
|||
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context); |
|||
preference.setKey(Constants.MARK_AS_SEEN); |
|||
preference.setTitle(R.string.mark_as_seen_setting); |
|||
preference.setSummary(R.string.mark_as_seen_setting_summary); |
|||
preference.setIconSpaceReserved(false); |
|||
return preference; |
|||
} |
|||
} |
@ -0,0 +1,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(); |
|||
} |
|||
} |
@ -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(); |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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> |
@ -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> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue