mirror of
				https://github.com/KokaKiwi/BarInsta
				synced 2025-10-30 19:15:35 +00:00 
			
		
		
		
	Merge branch 'master' into feature/option_prepend_username
This commit is contained in:
		
						commit
						5623f6d266
					
				| @ -66,11 +66,13 @@ android { | ||||
|             dimension "repo" | ||||
|             // versionNameSuffix "-github" // appended in assemble task | ||||
|             buildConfigField("String", "dsn", SENTRY_DSN) | ||||
|             buildConfigField("boolean", "isPre", "false") | ||||
|         } | ||||
| 
 | ||||
|         fdroid { | ||||
|             dimension "repo" | ||||
|             versionNameSuffix "-fdroid" | ||||
|             buildConfigField("boolean", "isPre", "false") | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -84,6 +86,7 @@ android { | ||||
| 
 | ||||
|             def suffix = "${versionName}-${flavor}_${builtType}" // eg. 19.1.0-github_debug or release | ||||
|             if (builtType.toString() == 'release' && project.hasProperty("pre")) { | ||||
|                 buildConfigField("boolean", "isPre", "true") | ||||
|                 // append latest commit short hash for pre-release | ||||
|                 suffix = "${versionName}.${getGitHash()}-${flavor}" // eg. 19.1.0.b123456-github | ||||
|             } | ||||
| @ -105,7 +108,7 @@ dependencies { | ||||
|     def nav_version = '2.3.4' | ||||
|     def exoplayer_version = '2.13.2' | ||||
| 
 | ||||
|     implementation 'com.google.android.material:material:1.4.0-alpha01' | ||||
|     implementation 'com.google.android.material:material:1.4.0-alpha02' | ||||
| 
 | ||||
|     implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version" | ||||
|     implementation "com.google.android.exoplayer:exoplayer-dash:$exoplayer_version" | ||||
|  | ||||
| @ -0,0 +1,64 @@ | ||||
| package awais.instagrabber.utils; | ||||
| 
 | ||||
| import android.util.Log; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.appcompat.app.AppCompatActivity; | ||||
| 
 | ||||
| import org.json.JSONObject; | ||||
| 
 | ||||
| import java.net.HttpURLConnection; | ||||
| import java.net.URL; | ||||
| 
 | ||||
| public class UpdateChecker { | ||||
|     private static final Object LOCK = new Object(); | ||||
|     private static final String TAG = UpdateChecker.class.getSimpleName(); | ||||
| 
 | ||||
|     private static UpdateChecker instance; | ||||
| 
 | ||||
|     public static UpdateChecker getInstance() { | ||||
|         if (instance == null) { | ||||
|             synchronized (LOCK) { | ||||
|                 if (instance == null) { | ||||
|                     instance = new UpdateChecker(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return instance; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Needs to be called asynchronously | ||||
|      * | ||||
|      * @return the latest version from f-droid | ||||
|      */ | ||||
|     @Nullable | ||||
|     public String getLatestVersion() { | ||||
|         HttpURLConnection conn = null; | ||||
|         try { | ||||
|             conn = (HttpURLConnection) new URL("https://f-droid.org/api/v1/packages/me.austinhuang.instagrabber").openConnection(); | ||||
|             conn.setUseCaches(false); | ||||
|             conn.setRequestProperty("User-Agent", "https://Barinsta.AustinHuang.me / mailto:Barinsta@AustinHuang.me"); | ||||
|             conn.connect(); | ||||
|             final int responseCode = conn.getResponseCode(); | ||||
|             if (responseCode == HttpURLConnection.HTTP_OK) { | ||||
|                 final JSONObject data = new JSONObject(NetworkUtils.readFromConnection(conn)); | ||||
|                 return "v" + data.getJSONArray("packages").getJSONObject(0).getString("versionName"); | ||||
|                 // if (BuildConfig.VERSION_CODE < data.getInt("suggestedVersionCode")) { | ||||
|                 // } | ||||
|             } | ||||
|         } catch (final Exception e) { | ||||
|             Log.e(TAG, "", e); | ||||
|         } finally { | ||||
|             if (conn != null) { | ||||
|                 conn.disconnect(); | ||||
|             } | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     public void onDownload(@NonNull final AppCompatActivity context) { | ||||
|         Utils.openURL(context, "https://f-droid.org/packages/me.austinhuang.instagrabber/"); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,61 @@ | ||||
| package awais.instagrabber.utils; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.util.Log; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| 
 | ||||
| import java.net.HttpURLConnection; | ||||
| import java.net.URL; | ||||
| 
 | ||||
| public class UpdateChecker { | ||||
|     private static final Object LOCK = new Object(); | ||||
|     private static final String TAG = UpdateChecker.class.getSimpleName(); | ||||
| 
 | ||||
|     private static UpdateChecker instance; | ||||
| 
 | ||||
|     public static UpdateChecker getInstance() { | ||||
|         if (instance == null) { | ||||
|             synchronized (LOCK) { | ||||
|                 if (instance == null) { | ||||
|                     instance = new UpdateChecker(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return instance; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Needs to be called asynchronously | ||||
|      * | ||||
|      * @return the latest version from Github | ||||
|      */ | ||||
|     @Nullable | ||||
|     public String getLatestVersion() { | ||||
|         HttpURLConnection conn = null; | ||||
|         try { | ||||
|             conn = (HttpURLConnection) new URL("https://github.com/austinhuang0131/barinsta/releases/latest").openConnection(); | ||||
|             conn.setInstanceFollowRedirects(false); | ||||
|             conn.setUseCaches(false); | ||||
|             conn.setRequestProperty("User-Agent", "https://Barinsta.AustinHuang.me / mailto:Barinsta@AustinHuang.me"); | ||||
|             conn.connect(); | ||||
|             final int responseCode = conn.getResponseCode(); | ||||
|             if (responseCode == HttpURLConnection.HTTP_MOVED_TEMP) { | ||||
|                 return "v" + conn.getHeaderField("Location").split("/v")[1]; | ||||
|                 // return !version.equals(BuildConfig.VERSION_NAME); | ||||
|             } | ||||
|         } catch (final Exception e) { | ||||
|             Log.e(TAG, "", e); | ||||
|         } finally { | ||||
|             if (conn != null) { | ||||
|                 conn.disconnect(); | ||||
|             } | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     public void onDownload(@NonNull final Context context) { | ||||
|         Utils.openURL(context, "https://github.com/austinhuang0131/instagrabber/releases/latest"); | ||||
|     } | ||||
| } | ||||
| @ -8,7 +8,6 @@ import android.content.ComponentName; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.content.ServiceConnection; | ||||
| import android.content.res.TypedArray; | ||||
| import android.database.MatrixCursor; | ||||
| import android.net.Uri; | ||||
| import android.os.Build; | ||||
| @ -24,6 +23,7 @@ import android.view.WindowManager; | ||||
| import android.widget.AutoCompleteTextView; | ||||
| import android.widget.Toast; | ||||
| 
 | ||||
| import androidx.annotation.IdRes; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.appcompat.app.ActionBar; | ||||
| @ -50,15 +50,16 @@ import com.google.android.material.appbar.CollapsingToolbarLayout; | ||||
| import com.google.android.material.badge.BadgeDrawable; | ||||
| import com.google.android.material.behavior.HideBottomViewOnScrollBehavior; | ||||
| import com.google.android.material.bottomnavigation.BottomNavigationView; | ||||
| import com.google.common.collect.ImmutableList; | ||||
| import com.google.common.collect.Iterators; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.Arrays; | ||||
| import java.util.Collections; | ||||
| import java.util.Deque; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| import awais.instagrabber.BuildConfig; | ||||
| import awais.instagrabber.R; | ||||
| import awais.instagrabber.adapters.SuggestionsAdapter; | ||||
| import awais.instagrabber.asyncs.PostFetcher; | ||||
| @ -69,6 +70,7 @@ import awais.instagrabber.fragments.directmessages.DirectMessageInboxFragmentDir | ||||
| import awais.instagrabber.fragments.main.FeedFragment; | ||||
| import awais.instagrabber.fragments.settings.PreferenceKeys; | ||||
| import awais.instagrabber.models.IntentModel; | ||||
| import awais.instagrabber.models.Tab; | ||||
| import awais.instagrabber.models.enums.SuggestionType; | ||||
| import awais.instagrabber.repositories.responses.search.SearchItem; | ||||
| import awais.instagrabber.repositories.responses.search.SearchResponse; | ||||
| @ -94,15 +96,8 @@ import static awais.instagrabber.utils.Utils.settingsHelper; | ||||
| 
 | ||||
| public class MainActivity extends BaseLanguageActivity implements FragmentManager.OnBackStackChangedListener { | ||||
|     private static final String TAG = "MainActivity"; | ||||
| 
 | ||||
|     private static final List<Integer> SHOW_BOTTOM_VIEW_DESTINATIONS = Arrays.asList( | ||||
|             R.id.directMessagesInboxFragment, | ||||
|             R.id.feedFragment, | ||||
|             R.id.profileFragment, | ||||
|             R.id.discoverFragment, | ||||
|             R.id.morePreferencesFragment); | ||||
|     private static final Map<Integer, Integer> NAV_TO_MENU_ID_MAP = new HashMap<>(); | ||||
|     private static final String FIRST_FRAGMENT_GRAPH_INDEX_KEY = "firstFragmentGraphIndex"; | ||||
|     private static final String LAST_SELECT_NAV_MENU_ID = "lastSelectedNavMenuId"; | ||||
| 
 | ||||
|     private ActivityMainBinding binding; | ||||
|     private LiveData<NavController> currentNavControllerLiveData; | ||||
| @ -114,10 +109,13 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage | ||||
|     private boolean showSearch = true; | ||||
|     private Handler suggestionsFetchHandler; | ||||
|     private int firstFragmentGraphIndex; | ||||
|     private int lastSelectedNavMenuId; | ||||
|     private boolean isActivityCheckerServiceBound = false; | ||||
|     private boolean isBackStackEmpty = false; | ||||
|     private boolean isLoggedIn; | ||||
|     private HideBottomViewOnScrollBehavior<BottomNavigationView> behavior; | ||||
|     private List<Tab> currentTabs; | ||||
|     private List<Integer> showBottomViewDestinations = Collections.emptyList(); | ||||
| 
 | ||||
|     private final ServiceConnection serviceConnection = new ServiceConnection() { | ||||
|         @Override | ||||
| @ -133,14 +131,6 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     static { | ||||
|         NAV_TO_MENU_ID_MAP.put(R.navigation.direct_messages_nav_graph, R.id.direct_messages_nav_graph); | ||||
|         NAV_TO_MENU_ID_MAP.put(R.navigation.feed_nav_graph, R.id.feed_nav_graph); | ||||
|         NAV_TO_MENU_ID_MAP.put(R.navigation.profile_nav_graph, R.id.profile_nav_graph); | ||||
|         NAV_TO_MENU_ID_MAP.put(R.navigation.discover_nav_graph, R.id.discover_nav_graph); | ||||
|         NAV_TO_MENU_ID_MAP.put(R.navigation.more_nav_graph, R.id.more_nav_graph); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void onCreate(@Nullable final Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
| @ -165,8 +155,10 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage | ||||
|             setupBottomNavigationBar(true); | ||||
|         } | ||||
|         setupSuggestions(); | ||||
|         final boolean checkUpdates = settingsHelper.getBoolean(Constants.CHECK_UPDATES); | ||||
|         if (checkUpdates) FlavorTown.updateCheck(this); | ||||
|         if (!BuildConfig.isPre) { | ||||
|             final boolean checkUpdates = settingsHelper.getBoolean(Constants.CHECK_UPDATES); | ||||
|             if (checkUpdates) FlavorTown.updateCheck(this); | ||||
|         } | ||||
|         FlavorTown.changelogCheck(this); | ||||
|         new ViewModelProvider(this).get(AppStateViewModel.class); // Just initiate the App state here | ||||
|         final Intent intent = getIntent(); | ||||
| @ -227,6 +219,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage | ||||
|     @Override | ||||
|     protected void onSaveInstanceState(@NonNull final Bundle outState) { | ||||
|         outState.putString(FIRST_FRAGMENT_GRAPH_INDEX_KEY, String.valueOf(firstFragmentGraphIndex)); | ||||
|         outState.putString(LAST_SELECT_NAV_MENU_ID, String.valueOf(binding.bottomNavView.getSelectedItemId())); | ||||
|         super.onSaveInstanceState(outState); | ||||
|     } | ||||
| 
 | ||||
| @ -239,6 +232,12 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage | ||||
|                 firstFragmentGraphIndex = Integer.parseInt(key); | ||||
|             } catch (NumberFormatException ignored) { } | ||||
|         } | ||||
|         final String lastSelected = (String) savedInstanceState.get(LAST_SELECT_NAV_MENU_ID); | ||||
|         if (lastSelected != null) { | ||||
|             try { | ||||
|                 lastSelectedNavMenuId = Integer.parseInt(lastSelected); | ||||
|             } catch (NumberFormatException ignored) { } | ||||
|         } | ||||
|         setupBottomNavigationBar(false); | ||||
|     } | ||||
| 
 | ||||
| @ -273,9 +272,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage | ||||
|             final NavController navController = currentNavControllerLiveData.getValue(); | ||||
|             if (navController != null) { | ||||
|                 @SuppressLint("RestrictedApi") final Deque<NavBackStackEntry> backStack = navController.getBackStack(); | ||||
|                 if (backStack != null) { | ||||
|                     currentNavControllerBackStack = backStack.size(); | ||||
|                 } | ||||
|                 currentNavControllerBackStack = backStack.size(); | ||||
|             } | ||||
|         } | ||||
|         if (isTaskRoot() && isBackStackEmpty && currentNavControllerBackStack == 2) { | ||||
| @ -328,7 +325,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage | ||||
|             final Bundle bundle = new Bundle(); | ||||
|             switch (type) { | ||||
|                 case TYPE_LOCATION: | ||||
|                     bundle.putLong("locationId", Long.valueOf(query)); | ||||
|                     bundle.putLong("locationId", Long.parseLong(query)); | ||||
|                     navController.navigate(R.id.action_global_locationFragment, bundle); | ||||
|                     break; | ||||
|                 case TYPE_HASHTAG: | ||||
| @ -378,14 +375,16 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage | ||||
|                         cursor = null; | ||||
|                         return; | ||||
|                     } | ||||
|                     final List<SearchItem> result = new ArrayList<SearchItem>(); | ||||
|                     final List<SearchItem> result = new ArrayList<>(); | ||||
|                     if (isLoggedIn) { | ||||
|                         if (body.getList() != null) result.addAll(searchHash ? body.getList() | ||||
|                                 .stream() | ||||
|                                 .filter(i -> i.getUser() == null) | ||||
|                                 .collect(Collectors.toList()) : body.getList()); | ||||
|                     } | ||||
|                     else { | ||||
|                         if (body.getList() != null) { | ||||
|                             result.addAll(searchHash ? body.getList() | ||||
|                                                            .stream() | ||||
|                                                            .filter(i -> i.getUser() == null) | ||||
|                                                            .collect(Collectors.toList()) | ||||
|                                                      : body.getList()); | ||||
|                         } | ||||
|                     } else { | ||||
|                         if (body.getUsers() != null && !searchHash) result.addAll(body.getUsers()); | ||||
|                         if (body.getHashtags() != null) result.addAll(body.getHashtags()); | ||||
|                         if (body.getPlaces() != null) result.addAll(body.getPlaces()); | ||||
| @ -430,9 +429,10 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage | ||||
| 
 | ||||
|                 @Override | ||||
|                 public void onFailure(@NonNull final Call<SearchResponse> call, | ||||
|                                       Throwable t) { | ||||
|                     if (!call.isCanceled() && t != null) | ||||
|                                       @NonNull Throwable t) { | ||||
|                     if (!call.isCanceled()) { | ||||
|                         Log.e(TAG, "Exception on search:", t); | ||||
|                     } | ||||
|                 } | ||||
|             }; | ||||
| 
 | ||||
| @ -454,7 +454,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage | ||||
|                     } | ||||
|                     prevSuggestionAsync = searchService.search(isLoggedIn, | ||||
|                                                                searchUser || searchHash ? currentSearchQuery.substring(1) | ||||
|                                                                     : currentSearchQuery, | ||||
|                                                                                         : currentSearchQuery, | ||||
|                                                                searchUser ? "user" : (searchHash ? "hashtag" : "blended")); | ||||
|                     suggestionAdapter.changeCursor(null); | ||||
|                     prevSuggestionAsync.enqueue(cb); | ||||
| @ -484,36 +484,18 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     private void setupBottomNavigationBar(final boolean setDefaultFromSettings) { | ||||
|         int main_nav_ids = R.array.main_nav_ids; | ||||
|         if (!isLoggedIn) { | ||||
|             main_nav_ids = R.array.logged_out_main_nav_ids; | ||||
|             final int selectedItemId = binding.bottomNavView.getSelectedItemId(); | ||||
|             binding.bottomNavView.getMenu().clear(); | ||||
|             binding.bottomNavView.inflateMenu(R.menu.logged_out_bottom_navigation_menu); | ||||
|             if (selectedItemId == R.id.profile_nav_graph | ||||
|                     || selectedItemId == R.id.more_nav_graph) { | ||||
|                 binding.bottomNavView.setSelectedItemId(selectedItemId); | ||||
|             } else { | ||||
|                 setBottomNavSelectedItem(R.navigation.profile_nav_graph); | ||||
|             } | ||||
|         } | ||||
|         final List<Integer> mainNavList = getMainNavList(main_nav_ids); | ||||
|         if (setDefaultFromSettings) { | ||||
|             final String defaultTabResNameString = settingsHelper.getString(Constants.DEFAULT_TAB); | ||||
|             try { | ||||
|                 int navId = 0; | ||||
|                 if (!TextUtils.isEmpty(defaultTabResNameString)) { | ||||
|                     navId = getResources().getIdentifier(defaultTabResNameString, "navigation", getPackageName()); | ||||
|                 } | ||||
|                 final int defaultNavId = navId <= 0 ? R.navigation.profile_nav_graph | ||||
|                                                     : navId; | ||||
|                 final int index = mainNavList.indexOf(defaultNavId); | ||||
|                 if (index >= 0) firstFragmentGraphIndex = index; | ||||
|                 setBottomNavSelectedItem(defaultNavId); | ||||
|             } catch (NumberFormatException e) { | ||||
|                 Log.e(TAG, "Error parsing id", e); | ||||
|             } | ||||
|     private void setupBottomNavigationBar(final boolean setDefaultTabFromSettings) { | ||||
|         currentTabs = !isLoggedIn ? setupAnonBottomNav() : setupMainBottomNav(); | ||||
|         final List<Integer> mainNavList = currentTabs.stream() | ||||
|                                                      .map(Tab::getNavigationResId) | ||||
|                                                      .collect(Collectors.toList()); | ||||
|         showBottomViewDestinations = currentTabs.stream() | ||||
|                                                 .map(Tab::getStartDestinationFragmentId) | ||||
|                                                 .collect(Collectors.toList()); | ||||
|         if (setDefaultTabFromSettings) { | ||||
|             setSelectedTab(currentTabs); | ||||
|         } else { | ||||
|             binding.bottomNavView.setSelectedItemId(lastSelectedNavMenuId); | ||||
|         } | ||||
|         final LiveData<NavController> navControllerLiveData = setupWithNavController( | ||||
|                 binding.bottomNavView, | ||||
| @ -536,27 +518,86 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     private void setBottomNavSelectedItem(final int navId) { | ||||
|         final Integer menuId = NAV_TO_MENU_ID_MAP.get(navId); | ||||
|         if (menuId != null) { | ||||
|             binding.bottomNavView.setSelectedItemId(menuId); | ||||
|     private void setSelectedTab(final List<Tab> tabs) { | ||||
|         final String defaultTabResNameString = settingsHelper.getString(Constants.DEFAULT_TAB); | ||||
|         try { | ||||
|             int navId = 0; | ||||
|             if (!TextUtils.isEmpty(defaultTabResNameString)) { | ||||
|                 navId = getResources().getIdentifier(defaultTabResNameString, "navigation", getPackageName()); | ||||
|             } | ||||
|             final int navGraph = isLoggedIn ? R.navigation.feed_nav_graph | ||||
|                                             : R.navigation.profile_nav_graph; | ||||
|             final int defaultNavId = navId <= 0 ? navGraph : navId; | ||||
|             int index = Iterators.indexOf(tabs.iterator(), tab -> { | ||||
|                 if (tab == null) return false; | ||||
|                 return tab.getNavigationResId() == defaultNavId; | ||||
|             }); | ||||
|             if (index < 0 || index >= tabs.size()) index = 0; | ||||
|             firstFragmentGraphIndex = index; | ||||
|             setBottomNavSelectedTab(tabs.get(index)); | ||||
|         } catch (Exception e) { | ||||
|             Log.e(TAG, "Error parsing id", e); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     private List<Integer> getMainNavList(final int main_nav_ids) { | ||||
|         final TypedArray navIds = getResources().obtainTypedArray(main_nav_ids); | ||||
|         final List<Integer> mainNavList = new ArrayList<>(navIds.length()); | ||||
|         final int length = navIds.length(); | ||||
|         for (int i = 0; i < length; i++) { | ||||
|             final int resourceId = navIds.getResourceId(i, -1); | ||||
|             if (resourceId < 0) continue; | ||||
|             mainNavList.add(resourceId); | ||||
|     private List<Tab> setupAnonBottomNav() { | ||||
|         final int selectedItemId = binding.bottomNavView.getSelectedItemId(); | ||||
|         final Tab profileTab = new Tab(R.drawable.ic_person_24, | ||||
|                                        getString(R.string.profile), | ||||
|                                        false, | ||||
|                                        "profile_nav_graph", | ||||
|                                        R.navigation.profile_nav_graph, | ||||
|                                        R.id.profile_nav_graph, | ||||
|                                        R.id.profileFragment); | ||||
|         final Tab moreTab = new Tab(R.drawable.ic_more_horiz_24, | ||||
|                                     getString(R.string.more), | ||||
|                                     false, | ||||
|                                     "more_nav_graph", | ||||
|                                     R.navigation.more_nav_graph, | ||||
|                                     R.id.more_nav_graph, | ||||
|                                     R.id.morePreferencesFragment); | ||||
|         final Menu menu = binding.bottomNavView.getMenu(); | ||||
|         menu.clear(); | ||||
|         menu.add(0, profileTab.getNavigationRootId(), 0, profileTab.getTitle()).setIcon(profileTab.getIconResId()); | ||||
|         menu.add(0, moreTab.getNavigationRootId(), 0, moreTab.getTitle()).setIcon(moreTab.getIconResId()); | ||||
|         if (selectedItemId != R.id.profile_nav_graph && selectedItemId != R.id.more_nav_graph) { | ||||
|             setBottomNavSelectedTab(profileTab); | ||||
|         } | ||||
|         navIds.recycle(); | ||||
|         return mainNavList; | ||||
|         return ImmutableList.of(profileTab, moreTab); | ||||
|     } | ||||
| 
 | ||||
|     private List<Tab> setupMainBottomNav() { | ||||
|         final Menu menu = binding.bottomNavView.getMenu(); | ||||
|         menu.clear(); | ||||
|         final List<Tab> navTabList = Utils.getNavTabList(this).first; | ||||
|         for (final Tab tab : navTabList) { | ||||
|             menu.add(0, tab.getNavigationRootId(), 0, tab.getTitle()).setIcon(tab.getIconResId()); | ||||
|         } | ||||
|         return navTabList; | ||||
|     } | ||||
| 
 | ||||
|     private void setBottomNavSelectedTab(@NonNull final Tab tab) { | ||||
|         binding.bottomNavView.setSelectedItemId(tab.getNavigationRootId()); | ||||
|     } | ||||
| 
 | ||||
|     private void setBottomNavSelectedTab(@SuppressWarnings("SameParameterValue") @IdRes final int navGraphRootId) { | ||||
|         binding.bottomNavView.setSelectedItemId(navGraphRootId); | ||||
|     } | ||||
| 
 | ||||
|     // @NonNull | ||||
|     // private List<Integer> getMainNavList(final int main_nav_ids) { | ||||
|     //     final TypedArray navIds = getResources().obtainTypedArray(main_nav_ids); | ||||
|     //     final List<Integer> mainNavList = new ArrayList<>(navIds.length()); | ||||
|     //     final int length = navIds.length(); | ||||
|     //     for (int i = 0; i < length; i++) { | ||||
|     //         final int resourceId = navIds.getResourceId(i, -1); | ||||
|     //         if (resourceId < 0) continue; | ||||
|     //         mainNavList.add(resourceId); | ||||
|     //     } | ||||
|     //     navIds.recycle(); | ||||
|     //     return mainNavList; | ||||
|     // } | ||||
| 
 | ||||
|     private void setupNavigation(final Toolbar toolbar, final NavController navController) { | ||||
|         if (navController == null) return; | ||||
|         NavigationUI.setupWithNavController(toolbar, navController); | ||||
| @ -574,7 +615,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage | ||||
|             final int destinationId = destination.getId(); | ||||
|             @SuppressLint("RestrictedApi") final Deque<NavBackStackEntry> backStack = navController.getBackStack(); | ||||
|             setupMenu(backStack.size(), destinationId); | ||||
|             final boolean contains = SHOW_BOTTOM_VIEW_DESTINATIONS.contains(destinationId); | ||||
|             final boolean contains = showBottomViewDestinations.contains(destinationId); | ||||
|             binding.bottomNavView.setVisibility(contains ? View.VISIBLE : View.GONE); | ||||
|             if (contains && behavior != null) { | ||||
|                 behavior.slideUp(binding.bottomNavView); | ||||
| @ -684,7 +725,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage | ||||
|         }); | ||||
|         final int selectedItemId = binding.bottomNavView.getSelectedItemId(); | ||||
|         if (selectedItemId != R.navigation.direct_messages_nav_graph) { | ||||
|             setBottomNavSelectedItem(R.navigation.direct_messages_nav_graph); | ||||
|             setBottomNavSelectedTab(R.id.direct_messages_nav_graph); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -756,7 +797,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage | ||||
|         final NavController navController = currentNavControllerLiveData.getValue(); | ||||
|         if (navController == null) return; | ||||
|         final Bundle bundle = new Bundle(); | ||||
|         bundle.putLong("locationId", Long.valueOf(locationId)); | ||||
|         bundle.putLong("locationId", Long.parseLong(locationId)); | ||||
|         navController.navigate(R.id.action_global_locationFragment, bundle); | ||||
|     } | ||||
| 
 | ||||
| @ -864,6 +905,14 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage | ||||
|         return binding.toolbar; | ||||
|     } | ||||
| 
 | ||||
|     public List<Tab> getCurrentTabs() { | ||||
|         return currentTabs; | ||||
|     } | ||||
| 
 | ||||
| //    public boolean isNavRootInCurrentTabs(@IdRes final int navRootId) { | ||||
| //        return showBottomViewDestinations.stream().anyMatch(id -> id == navRootId); | ||||
| //    } | ||||
| 
 | ||||
|     private void setNavBarDMUnreadCountBadge(final int unseenCount) { | ||||
|         final BadgeDrawable badge = binding.bottomNavView.getOrCreateBadge(R.id.direct_messages_nav_graph); | ||||
|         if (badge == null) return; | ||||
|  | ||||
| @ -10,7 +10,6 @@ import android.view.ViewGroup; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.cursoradapter.widget.CursorAdapter; | ||||
| 
 | ||||
| import awais.instagrabber.R; | ||||
| import awais.instagrabber.databinding.ItemSuggestionBinding; | ||||
| import awais.instagrabber.models.enums.SuggestionType; | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										156
									
								
								app/src/main/java/awais/instagrabber/adapters/TabsAdapter.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								app/src/main/java/awais/instagrabber/adapters/TabsAdapter.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,156 @@ | ||||
| package awais.instagrabber.adapters; | ||||
| 
 | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.ViewGroup; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.StringRes; | ||||
| import androidx.recyclerview.widget.DiffUtil; | ||||
| import androidx.recyclerview.widget.ListAdapter; | ||||
| import androidx.recyclerview.widget.RecyclerView; | ||||
| 
 | ||||
| import com.google.common.collect.ImmutableList; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.Objects; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| import awais.instagrabber.R; | ||||
| import awais.instagrabber.adapters.viewholder.TabViewHolder; | ||||
| import awais.instagrabber.databinding.ItemFavSectionHeaderBinding; | ||||
| import awais.instagrabber.databinding.ItemTabOrderPrefBinding; | ||||
| import awais.instagrabber.models.Tab; | ||||
| import awais.instagrabber.utils.Utils; | ||||
| 
 | ||||
| public class TabsAdapter extends ListAdapter<TabsAdapter.TabOrHeader, RecyclerView.ViewHolder> { | ||||
|     private static final DiffUtil.ItemCallback<TabOrHeader> DIFF_CALLBACK = new DiffUtil.ItemCallback<TabOrHeader>() { | ||||
|         @Override | ||||
|         public boolean areItemsTheSame(@NonNull final TabOrHeader oldItem, @NonNull final TabOrHeader newItem) { | ||||
|             if (oldItem.isHeader() && newItem.isHeader()) { | ||||
|                 return oldItem.header == newItem.header; | ||||
|             } | ||||
|             if (!oldItem.isHeader() && !newItem.isHeader()) { | ||||
|                 final Tab oldTab = oldItem.tab; | ||||
|                 final Tab newTab = newItem.tab; | ||||
|                 return oldTab.getIconResId() == newTab.getIconResId() | ||||
|                         && Objects.equals(oldTab.getTitle(), newTab.getTitle()); | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public boolean areContentsTheSame(@NonNull final TabOrHeader oldItem, @NonNull final TabOrHeader newItem) { | ||||
|             if (oldItem.isHeader() && newItem.isHeader()) { | ||||
|                 return oldItem.header == newItem.header; | ||||
|             } | ||||
|             if (!oldItem.isHeader() && !newItem.isHeader()) { | ||||
|                 final Tab oldTab = oldItem.tab; | ||||
|                 final Tab newTab = newItem.tab; | ||||
|                 return oldTab.getIconResId() == newTab.getIconResId() | ||||
|                         && Objects.equals(oldTab.getTitle(), newTab.getTitle()); | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     private final TabAdapterCallback tabAdapterCallback; | ||||
| 
 | ||||
|     private List<Tab> current = new ArrayList<>(); | ||||
|     private List<Tab> others = new ArrayList<>(); | ||||
| 
 | ||||
|     public TabsAdapter(@NonNull final TabAdapterCallback tabAdapterCallback) { | ||||
|         super(DIFF_CALLBACK); | ||||
|         this.tabAdapterCallback = tabAdapterCallback; | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) { | ||||
|         final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); | ||||
|         if (viewType == 1) { | ||||
|             final ItemTabOrderPrefBinding binding = ItemTabOrderPrefBinding.inflate(layoutInflater, parent, false); | ||||
|             return new TabViewHolder(binding, tabAdapterCallback); | ||||
|         } | ||||
|         final ItemFavSectionHeaderBinding headerBinding = ItemFavSectionHeaderBinding.inflate(layoutInflater, parent, false); | ||||
|         return new DirectUsersAdapter.HeaderViewHolder(headerBinding); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) { | ||||
|         if (holder instanceof DirectUsersAdapter.HeaderViewHolder) { | ||||
|             ((DirectUsersAdapter.HeaderViewHolder) holder).bind(R.string.other_tabs); | ||||
|             return; | ||||
|         } | ||||
|         if (holder instanceof TabViewHolder) { | ||||
|             final Tab tab = getItem(position).tab; | ||||
|             ((TabViewHolder) holder).bind(tab, others.contains(tab), current.size() == 5); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int getItemViewType(final int position) { | ||||
|         return getItem(position).isHeader() ? 0 : 1; | ||||
|     } | ||||
| 
 | ||||
|     public void submitList(final List<Tab> current, final List<Tab> others, final Runnable commitCallback) { | ||||
|         final ImmutableList.Builder<TabOrHeader> builder = ImmutableList.builder(); | ||||
|         if (current != null) { | ||||
|             builder.addAll(current.stream() | ||||
|                                   .map(TabOrHeader::new) | ||||
|                                   .collect(Collectors.toList())); | ||||
|         } | ||||
|         builder.add(new TabOrHeader(R.string.other_tabs)); | ||||
|         if (others != null) { | ||||
|             builder.addAll(others.stream() | ||||
|                                  .map(TabOrHeader::new) | ||||
|                                  .collect(Collectors.toList())); | ||||
|         } | ||||
|         // Mutable non-null copies | ||||
|         this.current = current != null ? new ArrayList<>(current) : new ArrayList<>(); | ||||
|         this.others = others != null ? new ArrayList<>(others) : new ArrayList<>(); | ||||
|         submitList(builder.build(), commitCallback); | ||||
|     } | ||||
| 
 | ||||
|     public void submitList(final List<Tab> current, final List<Tab> others) { | ||||
|         submitList(current, others, null); | ||||
|     } | ||||
| 
 | ||||
|     public void moveItem(final int from, final int to) { | ||||
|         final List<Tab> currentCopy = new ArrayList<>(current); | ||||
|         Utils.moveItem(from, to, currentCopy); | ||||
|         submitList(currentCopy, others); | ||||
|         tabAdapterCallback.onOrderChange(currentCopy); | ||||
|     } | ||||
| 
 | ||||
|     public int getCurrentCount() { | ||||
|         return current.size(); | ||||
|     } | ||||
| 
 | ||||
|     public static class TabOrHeader { | ||||
|         Tab tab; | ||||
|         int header; | ||||
| 
 | ||||
|         public TabOrHeader(final Tab tab) { | ||||
|             this.tab = tab; | ||||
|         } | ||||
| 
 | ||||
|         public TabOrHeader(@StringRes final int header) { | ||||
|             this.header = header; | ||||
|         } | ||||
| 
 | ||||
|         boolean isHeader() { | ||||
|             return header != 0; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public interface TabAdapterCallback { | ||||
|         void onStartDrag(TabViewHolder viewHolder); | ||||
| 
 | ||||
|         void onOrderChange(List<Tab> newOrderTabs); | ||||
| 
 | ||||
|         void onAdd(Tab tab); | ||||
| 
 | ||||
|         void onRemove(Tab tab); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,88 @@ | ||||
| package awais.instagrabber.adapters.viewholder; | ||||
| 
 | ||||
| import android.annotation.SuppressLint; | ||||
| import android.content.res.ColorStateList; | ||||
| import android.graphics.drawable.Drawable; | ||||
| import android.view.MotionEvent; | ||||
| import android.view.View; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.core.content.ContextCompat; | ||||
| import androidx.core.widget.ImageViewCompat; | ||||
| import androidx.recyclerview.widget.RecyclerView; | ||||
| 
 | ||||
| import com.google.android.material.color.MaterialColors; | ||||
| 
 | ||||
| import awais.instagrabber.R; | ||||
| import awais.instagrabber.adapters.TabsAdapter; | ||||
| import awais.instagrabber.databinding.ItemTabOrderPrefBinding; | ||||
| import awais.instagrabber.models.Tab; | ||||
| 
 | ||||
| public class TabViewHolder extends RecyclerView.ViewHolder { | ||||
|     private final ItemTabOrderPrefBinding binding; | ||||
|     private final TabsAdapter.TabAdapterCallback tabAdapterCallback; | ||||
|     private final int highlightColor; | ||||
|     private final Drawable originalBgColor; | ||||
| 
 | ||||
|     private boolean draggable = true; | ||||
| 
 | ||||
|     @SuppressLint("ClickableViewAccessibility") | ||||
|     public TabViewHolder(@NonNull final ItemTabOrderPrefBinding binding, | ||||
|                          @NonNull final TabsAdapter.TabAdapterCallback tabAdapterCallback) { | ||||
|         super(binding.getRoot()); | ||||
|         this.binding = binding; | ||||
|         this.tabAdapterCallback = tabAdapterCallback; | ||||
|         highlightColor = MaterialColors.getColor(itemView.getContext(), R.attr.colorControlHighlight, 0); | ||||
|         originalBgColor = itemView.getBackground(); | ||||
|         binding.handle.setOnTouchListener((v, event) -> { | ||||
|             if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { | ||||
|                 tabAdapterCallback.onStartDrag(this); | ||||
|             } | ||||
|             return true; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     public void bind(@NonNull final Tab tab, | ||||
|                      final boolean isInOthers, | ||||
|                      final boolean isCurrentFull) { | ||||
|         draggable = !isInOthers; | ||||
|         binding.icon.setImageResource(tab.getIconResId()); | ||||
|         binding.title.setText(tab.getTitle()); | ||||
|         binding.handle.setVisibility(isInOthers ? View.GONE : View.VISIBLE); | ||||
|         binding.addRemove.setImageResource(isInOthers ? R.drawable.ic_round_add_circle_24 | ||||
|                                                       : R.drawable.ic_round_remove_circle_24); | ||||
|         final ColorStateList tintList = ColorStateList.valueOf(ContextCompat.getColor( | ||||
|                 itemView.getContext(), | ||||
|                 isInOthers ? R.color.green_500 | ||||
|                            : R.color.red_500)); | ||||
|         ImageViewCompat.setImageTintList(binding.addRemove, tintList); | ||||
|         binding.addRemove.setOnClickListener(v -> { | ||||
|             if (isInOthers) { | ||||
|                 tabAdapterCallback.onAdd(tab); | ||||
|                 return; | ||||
|             } | ||||
|             tabAdapterCallback.onRemove(tab); | ||||
|         }); | ||||
|         final boolean enabled = tab.isRemovable() | ||||
|                 && !(isInOthers && isCurrentFull); // All slots are full in current | ||||
|         binding.addRemove.setEnabled(enabled); | ||||
|         binding.addRemove.setAlpha(enabled ? 1 : 0.5F); | ||||
|     } | ||||
| 
 | ||||
|     public boolean isDraggable() { | ||||
|         return draggable; | ||||
|     } | ||||
| 
 | ||||
|     public void setDragging(final boolean isDragging) { | ||||
|         if (isDragging) { | ||||
|             if (highlightColor != 0) { | ||||
|                 itemView.setBackgroundColor(highlightColor); | ||||
|             } else { | ||||
|                 itemView.setAlpha(0.5F); | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|         itemView.setAlpha(1); | ||||
|         itemView.setBackground(originalBgColor); | ||||
|     } | ||||
| } | ||||
| @ -16,7 +16,6 @@ import awais.instagrabber.databinding.LayoutDmBaseBinding; | ||||
| import awais.instagrabber.databinding.LayoutDmRavenMediaBinding; | ||||
| import awais.instagrabber.models.enums.MediaItemType; | ||||
| import awais.instagrabber.models.enums.RavenMediaViewMode; | ||||
| import awais.instagrabber.repositories.responses.ImageVersions2; | ||||
| import awais.instagrabber.repositories.responses.Media; | ||||
| import awais.instagrabber.repositories.responses.User; | ||||
| import awais.instagrabber.repositories.responses.directmessages.DirectItem; | ||||
|  | ||||
| @ -47,7 +47,7 @@ public class FavoriteDataSource { | ||||
|     } | ||||
| 
 | ||||
|     public final void insertOrUpdateFavorite(@NonNull final Favorite favorite) { | ||||
|         if (favorite.getId() > 0) { | ||||
|         if (favorite.getId() != 0) { | ||||
|             favoriteDao.updateFavorites(favorite); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| @ -0,0 +1,275 @@ | ||||
| package awais.instagrabber.dialogs; | ||||
| 
 | ||||
| import android.app.Dialog; | ||||
| import android.content.Context; | ||||
| import android.graphics.Canvas; | ||||
| import android.os.Bundle; | ||||
| import android.util.Pair; | ||||
| import android.view.View; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.appcompat.app.AlertDialog; | ||||
| import androidx.fragment.app.DialogFragment; | ||||
| import androidx.recyclerview.widget.ItemTouchHelper; | ||||
| import androidx.recyclerview.widget.ItemTouchHelper.SimpleCallback; | ||||
| import androidx.recyclerview.widget.LinearLayoutManager; | ||||
| import androidx.recyclerview.widget.RecyclerView; | ||||
| 
 | ||||
| import com.google.android.material.dialog.MaterialAlertDialogBuilder; | ||||
| import com.google.common.collect.ImmutableList; | ||||
| 
 | ||||
| import java.util.List; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| import awais.instagrabber.R; | ||||
| import awais.instagrabber.adapters.DirectUsersAdapter; | ||||
| import awais.instagrabber.adapters.TabsAdapter; | ||||
| import awais.instagrabber.adapters.viewholder.TabViewHolder; | ||||
| import awais.instagrabber.fragments.settings.PreferenceKeys; | ||||
| import awais.instagrabber.models.Tab; | ||||
| import awais.instagrabber.utils.Utils; | ||||
| 
 | ||||
| import static androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_DRAG; | ||||
| import static androidx.recyclerview.widget.ItemTouchHelper.DOWN; | ||||
| import static androidx.recyclerview.widget.ItemTouchHelper.UP; | ||||
| 
 | ||||
| public class TabOrderPreferenceDialogFragment extends DialogFragment { | ||||
|     private Callback callback; | ||||
|     private Context context; | ||||
|     private List<Tab> tabsInPref; | ||||
|     private ItemTouchHelper itemTouchHelper; | ||||
|     private AlertDialog dialog; | ||||
|     private List<Tab> newOrderTabs; | ||||
|     private List<Tab> newOtherTabs; | ||||
| 
 | ||||
|     private final TabsAdapter.TabAdapterCallback tabAdapterCallback = new TabsAdapter.TabAdapterCallback() { | ||||
|         @Override | ||||
|         public void onStartDrag(final TabViewHolder viewHolder) { | ||||
|             if (itemTouchHelper == null || viewHolder == null) return; | ||||
|             itemTouchHelper.startDrag(viewHolder); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public void onOrderChange(final List<Tab> newOrderTabs) { | ||||
|             if (newOrderTabs == null || tabsInPref == null || dialog == null) return; | ||||
|             TabOrderPreferenceDialogFragment.this.newOrderTabs = newOrderTabs; | ||||
|             setSaveButtonState(newOrderTabs); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public void onAdd(final Tab tab) { | ||||
|             // Add this tab to newOrderTabs | ||||
|             newOrderTabs = ImmutableList.<Tab>builder() | ||||
|                     .addAll(newOrderTabs) | ||||
|                     .add(tab) | ||||
|                     .build(); | ||||
|             // Remove this tab from newOtherTabs | ||||
|             if (newOtherTabs != null) { | ||||
|                 newOtherTabs = newOtherTabs.stream() | ||||
|                                            .filter(t -> !t.equals(tab)) | ||||
|                                            .collect(Collectors.toList()); | ||||
|             } | ||||
|             setSaveButtonState(newOrderTabs); | ||||
|             // submit these tab lists to adapter | ||||
|             if (adapter == null) return; | ||||
|             adapter.submitList(newOrderTabs, newOtherTabs, () -> list.postDelayed(() -> adapter.notifyDataSetChanged(), 300)); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public void onRemove(final Tab tab) { | ||||
|             // Remove this tab from newOrderTabs | ||||
|             newOrderTabs = newOrderTabs.stream() | ||||
|                                        .filter(t -> !t.equals(tab)) | ||||
|                                        .collect(Collectors.toList()); | ||||
|             // Add this tab to newOtherTabs | ||||
|             if (newOtherTabs != null) { | ||||
|                 newOtherTabs = ImmutableList.<Tab>builder() | ||||
|                         .addAll(newOtherTabs) | ||||
|                         .add(tab) | ||||
|                         .build(); | ||||
|             } | ||||
|             setSaveButtonState(newOrderTabs); | ||||
|             // submit these tab lists to adapter | ||||
|             if (adapter == null) return; | ||||
|             adapter.submitList(newOrderTabs, newOtherTabs, () -> list.postDelayed(() -> { | ||||
|                 adapter.notifyDataSetChanged(); | ||||
|                 if (tab.getNavigationRootId() == R.id.direct_messages_nav_graph) { | ||||
|                     final ConfirmDialogFragment dialogFragment = ConfirmDialogFragment.newInstance( | ||||
|                             111, 0, R.string.dm_remove_warning, R.string.ok, 0, 0 | ||||
|                     ); | ||||
|                     dialogFragment.show(getChildFragmentManager(), "dm_warning_dialog"); | ||||
|                 } | ||||
|             }, 500)); | ||||
|         } | ||||
| 
 | ||||
|         private void setSaveButtonState(final List<Tab> newOrderTabs) { | ||||
|             dialog.getButton(AlertDialog.BUTTON_POSITIVE) | ||||
|                   .setEnabled(!newOrderTabs.equals(tabsInPref)); | ||||
|         } | ||||
|     }; | ||||
|     private final SimpleCallback simpleCallback = new SimpleCallback(UP | DOWN, 0) { | ||||
|         private int movePosition = RecyclerView.NO_POSITION; | ||||
| 
 | ||||
|         @Override | ||||
|         public int getMovementFlags(@NonNull final RecyclerView recyclerView, @NonNull final RecyclerView.ViewHolder viewHolder) { | ||||
|             if (viewHolder instanceof DirectUsersAdapter.HeaderViewHolder) return 0; | ||||
|             if (viewHolder instanceof TabViewHolder && !((TabViewHolder) viewHolder).isDraggable()) return 0; | ||||
|             return super.getMovementFlags(recyclerView, viewHolder); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public void onChildDraw(@NonNull final Canvas c, | ||||
|                                 @NonNull final RecyclerView recyclerView, | ||||
|                                 @NonNull final RecyclerView.ViewHolder viewHolder, | ||||
|                                 final float dX, | ||||
|                                 final float dY, | ||||
|                                 final int actionState, | ||||
|                                 final boolean isCurrentlyActive) { | ||||
|             if (actionState != ACTION_STATE_DRAG) { | ||||
|                 super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); | ||||
|                 return; | ||||
|             } | ||||
|             final TabsAdapter adapter = (TabsAdapter) recyclerView.getAdapter(); | ||||
|             if (adapter == null) { | ||||
|                 super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); | ||||
|                 return; | ||||
|             } | ||||
|             // Do not allow dragging into 'Other tabs' category | ||||
|             float edgeY = dY; | ||||
|             final int lastPosition = adapter.getCurrentCount() - 1; | ||||
|             final View view = viewHolder.itemView; | ||||
|             // final int topEdge = recyclerView.getTop(); | ||||
|             final int bottomEdge = view.getHeight() * adapter.getCurrentCount() - view.getBottom(); | ||||
|             // if (movePosition == 0 && dY < topEdge) { | ||||
|             //     edgeY = topEdge; | ||||
|             // } else | ||||
|             if (movePosition >= lastPosition && dY >= bottomEdge) { | ||||
|                 edgeY = bottomEdge; | ||||
|             } | ||||
|             super.onChildDraw(c, recyclerView, viewHolder, dX, edgeY, actionState, isCurrentlyActive); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public boolean onMove(@NonNull final RecyclerView recyclerView, | ||||
|                               @NonNull final RecyclerView.ViewHolder viewHolder, | ||||
|                               @NonNull final RecyclerView.ViewHolder target) { | ||||
|             final TabsAdapter adapter = (TabsAdapter) recyclerView.getAdapter(); | ||||
|             if (adapter == null) return false; | ||||
|             movePosition = target.getBindingAdapterPosition(); | ||||
|             if (movePosition >= adapter.getCurrentCount()) { | ||||
|                 return false; | ||||
|             } | ||||
|             final int from = viewHolder.getBindingAdapterPosition(); | ||||
|             final int to = target.getBindingAdapterPosition(); | ||||
|             adapter.moveItem(from, to); | ||||
|             // adapter.notifyItemMoved(from, to); | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public void onSwiped(@NonNull final RecyclerView.ViewHolder viewHolder, final int direction) {} | ||||
| 
 | ||||
|         @Override | ||||
|         public void onSelectedChanged(@Nullable final RecyclerView.ViewHolder viewHolder, final int actionState) { | ||||
|             super.onSelectedChanged(viewHolder, actionState); | ||||
|             if (!(viewHolder instanceof TabViewHolder)) { | ||||
|                 movePosition = RecyclerView.NO_POSITION; | ||||
|                 return; | ||||
|             } | ||||
|             if (actionState == ACTION_STATE_DRAG) { | ||||
|                 ((TabViewHolder) viewHolder).setDragging(true); | ||||
|                 movePosition = viewHolder.getBindingAdapterPosition(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public void clearView(@NonNull final RecyclerView recyclerView, | ||||
|                               @NonNull final RecyclerView.ViewHolder viewHolder) { | ||||
|             super.clearView(recyclerView, viewHolder); | ||||
|             ((TabViewHolder) viewHolder).setDragging(false); | ||||
|             movePosition = RecyclerView.NO_POSITION; | ||||
|         } | ||||
|     }; | ||||
|     private TabsAdapter adapter; | ||||
|     private RecyclerView list; | ||||
| 
 | ||||
|     public static TabOrderPreferenceDialogFragment newInstance() { | ||||
|         final Bundle args = new Bundle(); | ||||
|         final TabOrderPreferenceDialogFragment fragment = new TabOrderPreferenceDialogFragment(); | ||||
|         fragment.setArguments(args); | ||||
|         return fragment; | ||||
|     } | ||||
| 
 | ||||
|     public TabOrderPreferenceDialogFragment() {} | ||||
| 
 | ||||
|     @Override | ||||
|     public void onAttach(@NonNull final Context context) { | ||||
|         super.onAttach(context); | ||||
|         try { | ||||
|             callback = (Callback) getParentFragment(); | ||||
|         } catch (ClassCastException e) { | ||||
|             // throw new ClassCastException("Calling fragment must implement TabOrderPreferenceDialogFragment.Callback interface"); | ||||
|         } | ||||
|         this.context = context; | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) { | ||||
|         return new MaterialAlertDialogBuilder(context) | ||||
|                 .setView(createView()) | ||||
|                 .setPositiveButton(R.string.save, (d, w) -> { | ||||
|                     final boolean hasChanged = newOrderTabs != null && !newOrderTabs.equals(tabsInPref); | ||||
|                     if (hasChanged) { | ||||
|                         saveNewOrder(); | ||||
|                     } | ||||
|                     if (callback == null) return; | ||||
|                     callback.onSave(hasChanged); | ||||
|                 }) | ||||
|                 .setNegativeButton(R.string.cancel, (dialog, which) -> { | ||||
|                     if (callback == null) return; | ||||
|                     callback.onCancel(); | ||||
|                 }) | ||||
|                 .create(); | ||||
|     } | ||||
| 
 | ||||
|     private void saveNewOrder() { | ||||
|         final String newOrderString = newOrderTabs.stream() | ||||
|                                                   .map(Tab::getGraphName) | ||||
|                                                   .collect(Collectors.joining(",")); | ||||
|         Utils.settingsHelper.putString(PreferenceKeys.PREF_TAB_ORDER, newOrderString); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onStart() { | ||||
|         super.onStart(); | ||||
|         final Dialog dialog = getDialog(); | ||||
|         if (!(dialog instanceof AlertDialog)) return; | ||||
|         this.dialog = (AlertDialog) dialog; | ||||
|         this.dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     private View createView() { | ||||
|         list = new RecyclerView(context); | ||||
|         list.setLayoutManager(new LinearLayoutManager(context)); | ||||
|         itemTouchHelper = new ItemTouchHelper(simpleCallback); | ||||
|         itemTouchHelper.attachToRecyclerView(list); | ||||
|         adapter = new TabsAdapter(tabAdapterCallback); | ||||
|         list.setAdapter(adapter); | ||||
|         final Pair<List<Tab>, List<Tab>> navTabListPair = Utils.getNavTabList(context); | ||||
|         tabsInPref = navTabListPair.first; | ||||
|         // initially set newOrderTabs and newOtherTabs same as current tabs | ||||
|         newOrderTabs = navTabListPair.first; | ||||
|         newOtherTabs = navTabListPair.second; | ||||
|         adapter.submitList(navTabListPair.first, navTabListPair.second); | ||||
|         return list; | ||||
|     } | ||||
| 
 | ||||
|     public interface Callback { | ||||
|         void onSave(final boolean orderHasChanged); | ||||
| 
 | ||||
|         void onCancel(); | ||||
|     } | ||||
| } | ||||
| @ -65,13 +65,14 @@ import awais.instagrabber.webservices.GraphQLService; | ||||
| import awais.instagrabber.webservices.LocationService; | ||||
| import awais.instagrabber.webservices.ServiceCallback; | ||||
| import awais.instagrabber.webservices.StoriesService; | ||||
| //import awaisomereport.LogCollector; | ||||
| 
 | ||||
| import static androidx.core.content.PermissionChecker.checkSelfPermission; | ||||
| import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION; | ||||
| //import static awais.instagrabber.utils.Utils.logCollector; | ||||
| import static awais.instagrabber.utils.Utils.settingsHelper; | ||||
| 
 | ||||
| //import awaisomereport.LogCollector; | ||||
| //import static awais.instagrabber.utils.Utils.logCollector; | ||||
| 
 | ||||
| public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener { | ||||
|     private static final String TAG = "LocationFragment"; | ||||
|     private static final int STORAGE_PERM_REQUEST_CODE = 8020; | ||||
|  | ||||
| @ -31,7 +31,6 @@ import awais.instagrabber.R; | ||||
| import awais.instagrabber.adapters.NotificationsAdapter; | ||||
| import awais.instagrabber.adapters.NotificationsAdapter.OnNotificationClickListener; | ||||
| import awais.instagrabber.databinding.FragmentNotificationsViewerBinding; | ||||
| import awais.instagrabber.fragments.settings.MorePreferencesFragmentDirections; | ||||
| import awais.instagrabber.models.enums.NotificationType; | ||||
| import awais.instagrabber.repositories.requests.StoryViewerOptions; | ||||
| import awais.instagrabber.repositories.responses.FriendshipChangeResponse; | ||||
| @ -278,7 +277,7 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe | ||||
|     } | ||||
| 
 | ||||
|     private void openProfile(final String username) { | ||||
|         final NavDirections action = MorePreferencesFragmentDirections | ||||
|         final NavDirections action = NotificationsViewerFragmentDirections | ||||
|                 .actionGlobalProfileFragment("@" + username); | ||||
|         NavHostFragment.findNavController(this).navigate(action); | ||||
|     } | ||||
|  | ||||
| @ -306,6 +306,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe | ||||
|     private AccountRepository accountRepository; | ||||
|     private FavoriteRepository favoriteRepository; | ||||
|     private AppStateViewModel appStateViewModel; | ||||
|     private boolean disableDm = false; | ||||
| 
 | ||||
|     @Override | ||||
|     public void onCreate(@Nullable final Bundle savedInstanceState) { | ||||
| @ -321,8 +322,10 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe | ||||
|         mediaService = isLoggedIn ? MediaService.getInstance(null, null, 0) : null; | ||||
|         userService = isLoggedIn ? UserService.getInstance() : null; | ||||
|         graphQLService = isLoggedIn ? null : GraphQLService.getInstance(); | ||||
|         accountRepository = AccountRepository.getInstance(AccountDataSource.getInstance(getContext())); | ||||
|         favoriteRepository = FavoriteRepository.getInstance(FavoriteDataSource.getInstance(getContext())); | ||||
|         final Context context = getContext(); | ||||
|         if (context == null) return; | ||||
|         accountRepository = AccountRepository.getInstance(AccountDataSource.getInstance(context)); | ||||
|         favoriteRepository = FavoriteRepository.getInstance(FavoriteDataSource.getInstance(context)); | ||||
|         appStateViewModel = new ViewModelProvider(fragmentActivity).get(AppStateViewModel.class); | ||||
|         setHasOptionsMenu(true); | ||||
|     } | ||||
| @ -577,6 +580,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe | ||||
|     } | ||||
| 
 | ||||
|     private void init() { | ||||
|         disableDm = !Utils.isNavRootInCurrentTabs("direct_messages_nav_graph"); | ||||
|         if (getArguments() != null) { | ||||
|             final ProfileFragmentArgs fragmentArgs = ProfileFragmentArgs.fromBundle(getArguments()); | ||||
|             username = fragmentArgs.getUsername(); | ||||
| @ -938,7 +942,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe | ||||
|             } | ||||
|             profileDetailsBinding.btnSaved.setVisibility(View.GONE); | ||||
|             profileDetailsBinding.btnLiked.setVisibility(View.GONE); | ||||
|             profileDetailsBinding.btnDM.setVisibility(View.VISIBLE); | ||||
|             profileDetailsBinding.btnDM.setVisibility(disableDm ? View.GONE : View.VISIBLE); | ||||
|             profileDetailsBinding.btnFollow.setVisibility(View.VISIBLE); | ||||
|             final Context context = getContext(); | ||||
|             if (context == null) return; | ||||
| @ -1116,23 +1120,25 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe | ||||
|                                                                                                               PostItemType.TAGGED); | ||||
|             NavHostFragment.findNavController(this).navigate(action); | ||||
|         }); | ||||
|         profileDetailsBinding.btnDM.setOnClickListener(v -> { | ||||
|             profileDetailsBinding.btnDM.setEnabled(false); | ||||
|             new CreateThreadAction(cookie, profileModel.getPk(), thread -> { | ||||
|                 if (thread == null) { | ||||
|                     Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); | ||||
|         if (!disableDm) { | ||||
|             profileDetailsBinding.btnDM.setOnClickListener(v -> { | ||||
|                 profileDetailsBinding.btnDM.setEnabled(false); | ||||
|                 new CreateThreadAction(cookie, profileModel.getPk(), thread -> { | ||||
|                     if (thread == null) { | ||||
|                         Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); | ||||
|                         profileDetailsBinding.btnDM.setEnabled(true); | ||||
|                         return; | ||||
|                     } | ||||
|                     final InboxManager inboxManager = DirectMessagesManager.getInstance().getInboxManager(); | ||||
|                     if (!inboxManager.containsThread(thread.getThreadId())) { | ||||
|                         thread.setTemp(true); | ||||
|                         inboxManager.addThread(thread, 0); | ||||
|                     } | ||||
|                     fragmentActivity.navigateToThread(thread.getThreadId(), profileModel.getUsername()); | ||||
|                     profileDetailsBinding.btnDM.setEnabled(true); | ||||
|                     return; | ||||
|                 } | ||||
|                 final InboxManager inboxManager = DirectMessagesManager.getInstance().getInboxManager(); | ||||
|                 if (!inboxManager.containsThread(thread.getThreadId())) { | ||||
|                     thread.setTemp(true); | ||||
|                     inboxManager.addThread(thread, 0); | ||||
|                 } | ||||
|                 fragmentActivity.navigateToThread(thread.getThreadId(), profileModel.getUsername()); | ||||
|                 profileDetailsBinding.btnDM.setEnabled(true); | ||||
|             }).execute(); | ||||
|         }); | ||||
|                 }).execute(); | ||||
|             }); | ||||
|         } | ||||
|         profileDetailsBinding.mainProfileImage.setOnClickListener(v -> { | ||||
|             if (!hasStories) { | ||||
|                 // show profile pic | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| package awais.instagrabber.fragments.settings; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.content.res.TypedArray; | ||||
| import android.util.Pair; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.preference.ListPreference; | ||||
| @ -12,13 +12,17 @@ import androidx.preference.SwitchPreferenceCompat; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import awais.instagrabber.R; | ||||
| import awais.instagrabber.dialogs.ConfirmDialogFragment; | ||||
| import awais.instagrabber.dialogs.TabOrderPreferenceDialogFragment; | ||||
| import awais.instagrabber.models.Tab; | ||||
| import awais.instagrabber.utils.Constants; | ||||
| import awais.instagrabber.utils.CookieUtils; | ||||
| import awais.instagrabber.utils.TextUtils; | ||||
| import awais.instagrabber.utils.Utils; | ||||
| 
 | ||||
| import static awais.instagrabber.utils.Utils.settingsHelper; | ||||
| 
 | ||||
| public class GeneralPreferencesFragment extends BasePreferencesFragment { | ||||
| public class GeneralPreferencesFragment extends BasePreferencesFragment implements TabOrderPreferenceDialogFragment.Callback { | ||||
| 
 | ||||
|     @Override | ||||
|     void setupPreferenceScreen(final PreferenceScreen screen) { | ||||
| @ -28,12 +32,14 @@ public class GeneralPreferencesFragment extends BasePreferencesFragment { | ||||
|         final boolean isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0; | ||||
|         if (isLoggedIn) { | ||||
|             screen.addPreference(getDefaultTabPreference(context)); | ||||
|             screen.addPreference(getTabOrderPreference(context)); | ||||
|         } | ||||
|         screen.addPreference(getUpdateCheckPreference(context)); | ||||
|         screen.addPreference(getFlagSecurePreference(context)); | ||||
|         final List<Preference> preferences = FlavorSettings.getInstance().getPreferences(context, | ||||
|                                                                                          getChildFragmentManager(), | ||||
|                                                                                          SettingCategory.GENERAL); | ||||
|         final List<Preference> preferences = FlavorSettings.getInstance() | ||||
|                                                            .getPreferences(context, | ||||
|                                                                            getChildFragmentManager(), | ||||
|                                                                            SettingCategory.GENERAL); | ||||
|         if (preferences != null) { | ||||
|             for (final Preference preference : preferences) { | ||||
|                 screen.addPreference(preference); | ||||
| @ -44,24 +50,36 @@ public class GeneralPreferencesFragment extends BasePreferencesFragment { | ||||
|     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(); | ||||
|         final Pair<List<Tab>, List<Tab>> listPair = Utils.getNavTabList(context); | ||||
|         final List<Tab> tabs = listPair.first; | ||||
|         final String[] titles = tabs.stream() | ||||
|                                     .map(Tab::getTitle) | ||||
|                                     .toArray(String[]::new); | ||||
|         final String[] navGraphFileNames = tabs.stream() | ||||
|                                                .map(Tab::getGraphName) | ||||
|                                                .toArray(String[]::new); | ||||
|         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.setEntries(titles); | ||||
|         preference.setEntryValues(navGraphFileNames); | ||||
|         preference.setIconSpaceReserved(false); | ||||
|         return preference; | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     private Preference getTabOrderPreference(@NonNull final Context context) { | ||||
|         final Preference preference = new Preference(context); | ||||
|         preference.setTitle(R.string.tab_order); | ||||
|         preference.setIconSpaceReserved(false); | ||||
|         preference.setOnPreferenceClickListener(preference1 -> { | ||||
|             final TabOrderPreferenceDialogFragment dialogFragment = TabOrderPreferenceDialogFragment.newInstance(); | ||||
|             dialogFragment.show(getChildFragmentManager(), "tab_order_dialog"); | ||||
|             return true; | ||||
|         }); | ||||
|         return preference; | ||||
|     } | ||||
| 
 | ||||
|     private Preference getUpdateCheckPreference(@NonNull final Context context) { | ||||
|         final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context); | ||||
|         preference.setKey(Constants.CHECK_UPDATES); | ||||
| @ -82,4 +100,22 @@ public class GeneralPreferencesFragment extends BasePreferencesFragment { | ||||
|                     return true; | ||||
|                 }); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onSave(final boolean orderHasChanged) { | ||||
|         if (!orderHasChanged) return; | ||||
|         final ConfirmDialogFragment dialogFragment = ConfirmDialogFragment.newInstance( | ||||
|                 111, | ||||
|                 0, | ||||
|                 R.string.tab_order_start_next_launch, | ||||
|                 R.string.ok, | ||||
|                 0, | ||||
|                 0); | ||||
|         dialogFragment.show(getChildFragmentManager(), "tab_order_set_dialog"); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onCancel() { | ||||
| 
 | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -27,6 +27,7 @@ import java.util.List; | ||||
| import awais.instagrabber.BuildConfig; | ||||
| import awais.instagrabber.R; | ||||
| import awais.instagrabber.activities.Login; | ||||
| import awais.instagrabber.activities.MainActivity; | ||||
| import awais.instagrabber.databinding.PrefAccountSwitcherBinding; | ||||
| import awais.instagrabber.db.datasources.AccountDataSource; | ||||
| import awais.instagrabber.db.entities.Account; | ||||
| @ -38,6 +39,7 @@ import awais.instagrabber.utils.Constants; | ||||
| import awais.instagrabber.utils.CookieUtils; | ||||
| import awais.instagrabber.utils.FlavorTown; | ||||
| import awais.instagrabber.utils.TextUtils; | ||||
| import awais.instagrabber.utils.Utils; | ||||
| import awais.instagrabber.webservices.ServiceCallback; | ||||
| import awais.instagrabber.webservices.UserService; | ||||
| 
 | ||||
| @ -55,8 +57,10 @@ public class MorePreferencesFragment extends BasePreferencesFragment { | ||||
|     void setupPreferenceScreen(final PreferenceScreen screen) { | ||||
|         final String cookie = settingsHelper.getString(Constants.COOKIE); | ||||
|         final boolean isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0; | ||||
|         final MainActivity activity = (MainActivity) getActivity(); | ||||
|         // screen.addPreference(new MoreHeaderPreference(getContext())); | ||||
|         final Context context = getContext(); | ||||
|         final Resources resources = context.getResources(); | ||||
|         if (context == null) return; | ||||
|         accountRepository = AccountRepository.getInstance(AccountDataSource.getInstance(context)); | ||||
|         final PreferenceCategory accountCategory = new PreferenceCategory(context); | ||||
| @ -135,13 +139,30 @@ public class MorePreferencesFragment extends BasePreferencesFragment { | ||||
|         screen.addPreference(getDivider(context)); | ||||
|         final NavController navController = NavHostFragment.findNavController(this); | ||||
|         if (isLoggedIn) { | ||||
|             screen.addPreference(getPreference(R.string.action_notif, R.drawable.ic_not_liked, preference -> { | ||||
|                 if (isSafeToNavigate(navController)) { | ||||
|                     final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalNotificationsViewerFragment("notif"); | ||||
|                     navController.navigate(navDirections); | ||||
|                 } | ||||
|                 return true; | ||||
|             })); | ||||
|             boolean showActivity = true; | ||||
|             boolean showExplore = false; | ||||
|             if (activity != null) { | ||||
|                 showActivity = !Utils.isNavRootInCurrentTabs("notification_viewer_nav_graph"); | ||||
|                 showExplore = !Utils.isNavRootInCurrentTabs("discover_nav_graph"); | ||||
|             } | ||||
|             if (showActivity) { | ||||
|                 screen.addPreference(getPreference(R.string.action_notif, R.drawable.ic_not_liked, preference -> { | ||||
|                     if (isSafeToNavigate(navController)) { | ||||
|                         final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalNotificationsViewerFragment("notif"); | ||||
|                         navController.navigate(navDirections); | ||||
|                     } | ||||
|                     return true; | ||||
|                 })); | ||||
|             } | ||||
|             if (showExplore) { | ||||
|                 screen.addPreference(getPreference(R.string.title_discover, R.drawable.ic_explore_24, preference -> { | ||||
|                     if (isSafeToNavigate(navController)) { | ||||
|                         navController.navigate(R.id.discover_nav_graph); | ||||
|                     } | ||||
|                     return true; | ||||
|                 })); | ||||
|             } | ||||
| 
 | ||||
|             screen.addPreference(getPreference(R.string.action_ayml, R.drawable.ic_suggested_users, preference -> { | ||||
|                 if (isSafeToNavigate(navController)) { | ||||
|                     final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalNotificationsViewerFragment("ayml"); | ||||
| @ -157,13 +178,21 @@ public class MorePreferencesFragment extends BasePreferencesFragment { | ||||
|                 return true; | ||||
|             })); | ||||
|         } | ||||
|         screen.addPreference(getPreference(R.string.title_favorites, R.drawable.ic_star_24, preference -> { | ||||
|             if (isSafeToNavigate(navController)) { | ||||
|                 final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToFavoritesFragment(); | ||||
|                 navController.navigate(navDirections); | ||||
|             } | ||||
|             return true; | ||||
|         })); | ||||
| 
 | ||||
|         // Check if favorites has been added as a tab. And if so, do not add in this list | ||||
|         boolean showFavorites = true; | ||||
|         if (activity != null) { | ||||
|             showFavorites = !Utils.isNavRootInCurrentTabs("favorites_nav_graph"); | ||||
|         } | ||||
|         if (showFavorites) { | ||||
|             screen.addPreference(getPreference(R.string.title_favorites, R.drawable.ic_star_24, preference -> { | ||||
|                 if (isSafeToNavigate(navController)) { | ||||
|                     final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToFavoritesFragment(); | ||||
|                     navController.navigate(navDirections); | ||||
|                 } | ||||
|                 return true; | ||||
|             })); | ||||
|         } | ||||
| 
 | ||||
|         screen.addPreference(getDivider(context)); | ||||
|         screen.addPreference(getPreference(R.string.action_settings, R.drawable.ic_outline_settings_24, preference -> { | ||||
| @ -193,7 +222,8 @@ public class MorePreferencesFragment extends BasePreferencesFragment { | ||||
|                                            BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ")", | ||||
|                                            -1, | ||||
|                                            preference -> { | ||||
|                                                FlavorTown.updateCheck((AppCompatActivity) requireActivity(), true); | ||||
|                                                if (BuildConfig.isPre) return true; | ||||
|                                                FlavorTown.updateCheck(activity, true); | ||||
|                                                return true; | ||||
|                                            })); | ||||
|         screen.addPreference(getDivider(context)); | ||||
|  | ||||
| @ -6,4 +6,5 @@ public final class PreferenceKeys { | ||||
|     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"; | ||||
|     public static final String PREF_ENABLE_SENTRY = "enable_sentry"; | ||||
|     public static final String PREF_TAB_ORDER = "tab_order"; | ||||
| } | ||||
|  | ||||
| @ -37,6 +37,14 @@ public class StoriesPreferencesFragment extends BasePreferencesFragment { | ||||
|         return preference; | ||||
|     } | ||||
| 
 | ||||
|     private Preference getHideMutedReelsPreference(@NonNull final Context context) { | ||||
|         final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context); | ||||
|         preference.setKey(Constants.HIDE_MUTED_REELS); | ||||
|         preference.setTitle(R.string.hide_muted_reels_setting); | ||||
|         preference.setIconSpaceReserved(false); | ||||
|         return preference; | ||||
|     } | ||||
| 
 | ||||
|     private Preference getMarkStoriesSeenPreference(@NonNull final Context context) { | ||||
|         final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context); | ||||
|         preference.setKey(Constants.MARK_AS_SEEN); | ||||
|  | ||||
							
								
								
									
										110
									
								
								app/src/main/java/awais/instagrabber/models/Tab.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								app/src/main/java/awais/instagrabber/models/Tab.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,110 @@ | ||||
| package awais.instagrabber.models; | ||||
| 
 | ||||
| import androidx.annotation.DrawableRes; | ||||
| import androidx.annotation.IdRes; | ||||
| import androidx.annotation.NavigationRes; | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| import java.util.Objects; | ||||
| 
 | ||||
| public class Tab { | ||||
|     private final int iconResId; | ||||
|     private final String title; | ||||
|     private final boolean removable; | ||||
| 
 | ||||
|     /** | ||||
|      * This is name part of the navigation resource | ||||
|      * eg: @navigation/<b>graphName</b> | ||||
|      */ | ||||
|     private final String graphName; | ||||
| 
 | ||||
|     /** | ||||
|      * This is the actual resource id of the navigation resource (R.navigation.graphName = navigationResId) | ||||
|      */ | ||||
|     private final int navigationResId; | ||||
| 
 | ||||
|     /** | ||||
|      * This is the resource id of the root navigation tag of the navigation resource. | ||||
|      * <p>eg: inside R.navigation.direct_messages_nav_graph, the id of the root tag is R.id.direct_messages_nav_graph. | ||||
|      * <p>So this field would equal to the value of R.id.direct_messages_nav_graph | ||||
|      */ | ||||
|     private final int navigationRootId; | ||||
| 
 | ||||
|     /** | ||||
|      * This is the start destination of the nav graph | ||||
|      */ | ||||
|     private final int startDestinationFragmentId; | ||||
| 
 | ||||
|     public Tab(@DrawableRes final int iconResId, | ||||
|                @NonNull final String title, | ||||
|                final boolean removable, | ||||
|                @NonNull final String graphName, | ||||
|                @NavigationRes final int navigationResId, | ||||
|                @IdRes final int navigationRootId, | ||||
|                @IdRes final int startDestinationFragmentId) { | ||||
|         this.iconResId = iconResId; | ||||
|         this.title = title; | ||||
|         this.removable = removable; | ||||
|         this.graphName = graphName; | ||||
|         this.navigationResId = navigationResId; | ||||
|         this.navigationRootId = navigationRootId; | ||||
|         this.startDestinationFragmentId = startDestinationFragmentId; | ||||
|     } | ||||
| 
 | ||||
|     public int getIconResId() { | ||||
|         return iconResId; | ||||
|     } | ||||
| 
 | ||||
|     public String getTitle() { | ||||
|         return title; | ||||
|     } | ||||
| 
 | ||||
|     public boolean isRemovable() { | ||||
|         return removable; | ||||
|     } | ||||
| 
 | ||||
|     public String getGraphName() { | ||||
|         return graphName; | ||||
|     } | ||||
| 
 | ||||
|     public int getNavigationResId() { | ||||
|         return navigationResId; | ||||
|     } | ||||
| 
 | ||||
|     public int getNavigationRootId() { | ||||
|         return navigationRootId; | ||||
|     } | ||||
| 
 | ||||
|     public int getStartDestinationFragmentId() { | ||||
|         return startDestinationFragmentId; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean equals(final Object o) { | ||||
|         if (this == o) return true; | ||||
|         if (o == null || getClass() != o.getClass()) return false; | ||||
|         final Tab tab = (Tab) o; | ||||
|         return iconResId == tab.iconResId && | ||||
|                 removable == tab.removable && | ||||
|                 navigationResId == tab.navigationResId && | ||||
|                 navigationRootId == tab.navigationRootId && | ||||
|                 startDestinationFragmentId == tab.startDestinationFragmentId && | ||||
|                 Objects.equals(title, tab.title) && | ||||
|                 Objects.equals(graphName, tab.graphName); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int hashCode() { | ||||
|         return Objects.hash(iconResId, title, removable, graphName, navigationResId, navigationRootId, startDestinationFragmentId); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public String toString() { | ||||
|         return "Tab{" + | ||||
|                 "title='" + title + '\'' + | ||||
|                 ", removable=" + removable + | ||||
|                 ", graphName='" + graphName + '\'' + | ||||
|                 '}'; | ||||
|     } | ||||
| } | ||||
| @ -3,7 +3,6 @@ package awais.instagrabber.repositories; | ||||
| import java.util.Map; | ||||
| 
 | ||||
| import awais.instagrabber.repositories.responses.search.SearchResponse; | ||||
| 
 | ||||
| import retrofit2.Call; | ||||
| import retrofit2.http.GET; | ||||
| import retrofit2.http.QueryMap; | ||||
|  | ||||
| @ -1,7 +1,5 @@ | ||||
| package awais.instagrabber.repositories.responses.notification; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| public class NotificationCounts { | ||||
|     private final int commentLikes; | ||||
|     private final int usertags; | ||||
|  | ||||
| @ -2,8 +2,6 @@ package awais.instagrabber.repositories.responses.search; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| import awais.instagrabber.repositories.responses.User; | ||||
| 
 | ||||
| public class SearchResponse { | ||||
|     // app | ||||
|     private final List<SearchItem> list; | ||||
|  | ||||
| @ -29,6 +29,7 @@ public final class Constants { | ||||
|     public static final String CUSTOM_DATE_TIME_FORMAT_ENABLED = "data_time_custom_enabled"; | ||||
|     public static final String SWAP_DATE_TIME_FORMAT_ENABLED = "swap_date_time_enabled"; | ||||
|     public static final String MARK_AS_SEEN = "mark_as_seen"; | ||||
|     public static final String HIDE_MUTED_REELS = "hide_muted_reels"; | ||||
|     public static final String DM_MARK_AS_SEEN = "dm_mark_as_seen"; | ||||
|     // deprecated: public static final String INSTADP = "instadp"; | ||||
|     // deprecated: public static final String STORIESIG = "storiesig"; | ||||
| @ -80,7 +81,6 @@ public final class Constants { | ||||
| //    public static final String SIGNATURE_KEY = "9193488027538fd3450b83b7d05286d4ca9599a0f7eeed90d8c85925698a05dc"; | ||||
|     public static final String BREADCRUMB_KEY = "iN4$aGr0m"; | ||||
|     public static final int LOGIN_RESULT_CODE = 5000; | ||||
|     public static final String FDROID_SHA1_FINGERPRINT = "C1661EB8FD09F618307E687786D5E5056F65084D"; | ||||
|     public static final String SKIPPED_VERSION = "skipped_version"; | ||||
|     public static final String DEFAULT_TAB = "default_tab"; | ||||
|     public static final String PREF_DARK_THEME = "dark_theme"; | ||||
|  | ||||
| @ -98,19 +98,19 @@ public final class DownloadUtils { | ||||
|     //        } | ||||
|     //    } | ||||
| 
 | ||||
|     private static void dmDownloadImpl(@NonNull final Context context, | ||||
|                                        @Nullable final String username, | ||||
|                                        final String modelId, | ||||
|                                        final String url) { | ||||
|         final File dir = getDownloadDir(context, username); | ||||
|         if (dir.exists() || dir.mkdirs()) { | ||||
|             download(context, | ||||
|                      url, | ||||
|                      getDownloadSaveFile(dir, modelId, url).getAbsolutePath()); | ||||
|             return; | ||||
|         } | ||||
|         Toast.makeText(context, R.string.error_creating_folders, Toast.LENGTH_SHORT).show(); | ||||
|     } | ||||
| //    private static void dmDownloadImpl(@NonNull final Context context, | ||||
| //                                       @Nullable final String username, | ||||
| //                                       final String modelId, | ||||
| //                                       final String url) { | ||||
| //        final File dir = getDownloadDir(context, username); | ||||
| //        if (dir.exists() || dir.mkdirs()) { | ||||
| //            download(context, | ||||
| //                     url, | ||||
| //                     getDownloadSaveFile(dir, modelId, url).getAbsolutePath()); | ||||
| //            return; | ||||
| //        } | ||||
| //        Toast.makeText(context, R.string.error_creating_folders, Toast.LENGTH_SHORT).show(); | ||||
| //    } | ||||
| 
 | ||||
|     @NonNull | ||||
|     private static File getDownloadSaveFile(final File finalDir, | ||||
| @ -312,12 +312,17 @@ public final class DownloadUtils { | ||||
|                 case MEDIA_TYPE_IMAGE: | ||||
|                 case MEDIA_TYPE_VIDEO: { | ||||
|                     final String url = getUrlOfType(media); | ||||
|                     File file; | ||||
|                     if (Utils.settingsHelper.getBoolean(Constants.DOWNLOAD_PREPEND_USER_NAME) && mediaUser != null) { | ||||
|                         file = getDownloadSaveFile(downloadDir, media.getCode(), url, mediaUser.getUsername()); | ||||
|                     } else { | ||||
|                         file = getDownloadSaveFile(downloadDir, media.getCode(), url); | ||||
|                     String fileName = media.getId(); | ||||
|                     if (mediaUser != null && TextUtils.isEmpty(media.getCode())) { | ||||
|                         fileName = mediaUser.getUsername() + "_" + fileName; | ||||
|                     } | ||||
|                     if (!TextUtils.isEmpty(media.getCode())) { | ||||
|                         fileName = media.getCode(); | ||||
|                         if (Utils.settingsHelper.getBoolean(Constants.DOWNLOAD_PREPEND_USER_NAME) && mediaUser != null) { | ||||
|                             fileName = mediaUser.getUsername() + "_" + fileName; | ||||
|                         } | ||||
|                     } | ||||
|                     final File file = getDownloadSaveFile(downloadDir, fileName, url); | ||||
|                     map.put(url, file.getAbsolutePath()); | ||||
|                     break; | ||||
|                 } | ||||
|  | ||||
| @ -156,12 +156,21 @@ public final class ExportImportUtils { | ||||
|                     query, | ||||
|                     favoriteType, | ||||
|                     favsObject.optString("s"), | ||||
|                     favoriteType == FavoriteType.HASHTAG ? null | ||||
|                                                          : favsObject.optString("pic_url"), | ||||
|                     favoriteType == FavoriteType.USER ? favsObject.optString("pic_url") : null, | ||||
|                     new Date(favsObject.getLong("d"))); | ||||
|             // Log.d(TAG, "importJson: favoriteModel: " + favoriteModel); | ||||
|             FavoriteRepository.getInstance(FavoriteDataSource.getInstance(context)) | ||||
|                               .insertOrUpdateFavorite(favorite, null); | ||||
|             final FavoriteRepository favRepo = FavoriteRepository.getInstance(FavoriteDataSource.getInstance(context)); | ||||
|             favRepo.getFavorite(query, favoriteType, new RepositoryCallback<Favorite>() { | ||||
|                 @Override | ||||
|                 public void onSuccess(final Favorite result) { | ||||
|                     // local has priority since it's more frequently updated | ||||
|                 } | ||||
| 
 | ||||
|                 @Override | ||||
|                 public void onDataNotAvailable() { | ||||
|                     favRepo.insertOrUpdateFavorite(favorite, null); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -1,104 +1,72 @@ | ||||
| package awais.instagrabber.utils; | ||||
| 
 | ||||
| import android.annotation.SuppressLint; | ||||
| import android.content.ActivityNotFoundException; | ||||
| import android.content.Context; | ||||
| import android.content.DialogInterface; | ||||
| import android.content.Intent; | ||||
| import android.content.pm.PackageInfo; | ||||
| import android.content.pm.PackageManager; | ||||
| import android.content.pm.Signature; | ||||
| import android.content.res.Resources; | ||||
| import android.net.Uri; | ||||
| import android.os.AsyncTask; | ||||
| import android.util.Log; | ||||
| import android.widget.Toast; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.appcompat.app.AlertDialog; | ||||
| import androidx.appcompat.app.AppCompatActivity; | ||||
| 
 | ||||
| import java.security.MessageDigest; | ||||
| import java.security.NoSuchAlgorithmException; | ||||
| import java.util.Objects; | ||||
| import java.util.concurrent.ThreadLocalRandom; | ||||
| 
 | ||||
| import javax.security.cert.CertificateException; | ||||
| import javax.security.cert.X509Certificate; | ||||
| import java.util.regex.Matcher; | ||||
| import java.util.regex.Pattern; | ||||
| 
 | ||||
| import awais.instagrabber.BuildConfig; | ||||
| import awais.instagrabber.R; | ||||
| import awais.instagrabber.databinding.DialogUpdateBinding; | ||||
| 
 | ||||
| import static awais.instagrabber.utils.Utils.settingsHelper; | ||||
| 
 | ||||
| public final class FlavorTown { | ||||
|     private static final String TAG = "FlavorTown"; | ||||
|     private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); | ||||
|     private static AlertDialog dialog; | ||||
|     private static final UpdateChecker UPDATE_CHECKER = UpdateChecker.getInstance(); | ||||
|     private static final Pattern VERSION_NAME_PATTERN = Pattern.compile("v?(\\d+\\.\\d+\\.\\d+)(?:_?)(\\w*)(?:-?)(\\w*)"); | ||||
| 
 | ||||
|     private static boolean checking = false; | ||||
| 
 | ||||
|     public static void updateCheck(@NonNull final AppCompatActivity context) { | ||||
|         updateCheck(context, false); | ||||
|     } | ||||
| 
 | ||||
|     @SuppressLint("PackageManagerGetSignatures") | ||||
|     public static void updateCheck(@NonNull final AppCompatActivity context, final boolean force) { | ||||
|         boolean isInstalledFromFdroid = false; | ||||
|         final PackageInfo packageInfo; | ||||
|         try { | ||||
|             packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES); | ||||
|             for (Signature signature : packageInfo.signatures) { | ||||
|                 final X509Certificate cert = X509Certificate.getInstance(signature.toByteArray()); | ||||
|                 final String fingerprint = bytesToHex(MessageDigest.getInstance("SHA-1").digest(cert.getEncoded())); | ||||
|                 isInstalledFromFdroid = fingerprint.equals(Constants.FDROID_SHA1_FINGERPRINT); | ||||
|                 // Log.d(TAG, "fingerprint:" + fingerprint); | ||||
|             } | ||||
|         } catch (PackageManager.NameNotFoundException | NoSuchAlgorithmException | CertificateException e) { | ||||
|             Log.e(TAG, "Error", e); | ||||
|         } | ||||
|         if (isInstalledFromFdroid) return; | ||||
|         final DialogUpdateBinding binding = DialogUpdateBinding.inflate(context.getLayoutInflater(), null, false); | ||||
|         binding.skipUpdate.setOnCheckedChangeListener((buttonView, isChecked) -> { | ||||
|             if (dialog == null) return; | ||||
|             dialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(!isChecked); | ||||
|         }); | ||||
|         Resources res = context.getResources(); | ||||
|         new UpdateChecker(version -> { | ||||
|             if (force && version.equals(BuildConfig.VERSION_NAME)) { | ||||
|                 Toast.makeText(context, "You're already on the latest version", Toast.LENGTH_SHORT).show(); | ||||
|     public static void updateCheck(@NonNull final AppCompatActivity context, | ||||
|                                    final boolean force) { | ||||
|         if (checking) return; | ||||
|         checking = true; | ||||
|         AppExecutors.getInstance().networkIO().execute(() -> { | ||||
|             final String onlineVersionName = UPDATE_CHECKER.getLatestVersion(); | ||||
|             if (onlineVersionName == null) return; | ||||
|             final String onlineVersion = getVersion(onlineVersionName); | ||||
|             final String localVersion = getVersion(BuildConfig.VERSION_NAME); | ||||
|             if (Objects.equals(onlineVersion, localVersion)) { | ||||
|                 if (force) { | ||||
|                     AppExecutors.getInstance().mainThread().execute(() -> { | ||||
|                         final Context applicationContext = context.getApplicationContext(); | ||||
|                         // Check if app was closed or crashed before reaching here | ||||
|                         if (applicationContext == null) return; | ||||
|                         // Show toast if version number preference was tapped | ||||
|                         Toast.makeText(applicationContext, R.string.on_latest_version, Toast.LENGTH_SHORT).show(); | ||||
|                     }); | ||||
|                 } | ||||
|                 return; | ||||
|             } | ||||
|             final String skippedVersion = settingsHelper.getString(Constants.SKIPPED_VERSION); | ||||
|             final boolean shouldShowDialog = force || (!version.equals(BuildConfig.VERSION_NAME) && !BuildConfig.DEBUG && !skippedVersion | ||||
|                     .equals(version)); | ||||
|             final boolean shouldShowDialog = UpdateCheckCommon.shouldShowUpdateDialog(force, onlineVersionName); | ||||
|             if (!shouldShowDialog) return; | ||||
|             dialog = new AlertDialog.Builder(context) | ||||
|                     .setTitle(res.getString(R.string.update_available, version)) | ||||
|                     .setView(binding.getRoot()) | ||||
|                     .setNeutralButton(R.string.cancel, (dialog, which) -> { | ||||
|                         if (binding.skipUpdate.isChecked()) { | ||||
|                             settingsHelper.putString(Constants.SKIPPED_VERSION, version); | ||||
|                         } | ||||
|                         dialog.dismiss(); | ||||
|                     }) | ||||
|                     .setPositiveButton(R.string.action_github, (dialog1, which) -> { | ||||
|                         try { | ||||
|                             context.startActivity(new Intent(Intent.ACTION_VIEW).setData( | ||||
|                                     Uri.parse("https://github.com/austinhuang0131/instagrabber/releases/latest"))); | ||||
|                         } catch (final ActivityNotFoundException e) { | ||||
|                             // do nothing | ||||
|                         } | ||||
|                     }) | ||||
|                     // if we don't show dialog for fdroid users, is the below required? | ||||
|                     .setNegativeButton(R.string.action_fdroid, (dialog, which) -> { | ||||
|                         try { | ||||
|                             context.startActivity(new Intent(Intent.ACTION_VIEW).setData( | ||||
|                                     Uri.parse("https://f-droid.org/packages/me.austinhuang.instagrabber/"))); | ||||
|                         } catch (final ActivityNotFoundException e) { | ||||
|                             // do nothing | ||||
|                         } | ||||
|                     }) | ||||
|                     .show(); | ||||
|         }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); | ||||
|             UpdateCheckCommon.showUpdateDialog(context, onlineVersionName, (dialog, which) -> { | ||||
|                 UPDATE_CHECKER.onDownload(context); | ||||
|                 dialog.dismiss(); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     private static String getVersion(@NonNull final String versionName) { | ||||
|         final Matcher matcher = VERSION_NAME_PATTERN.matcher(versionName); | ||||
|         if (!matcher.matches()) return versionName; | ||||
|         try { | ||||
|             return matcher.group(1); | ||||
|         } catch (Exception e) { | ||||
|             Log.e(TAG, "getVersion: ", e); | ||||
|         } | ||||
|         return versionName; | ||||
|     } | ||||
| 
 | ||||
|     public static void changelogCheck(@NonNull final Context context) { | ||||
| @ -121,14 +89,4 @@ public final class FlavorTown { | ||||
|             settingsHelper.putInteger(Constants.PREV_INSTALL_VERSION, BuildConfig.VERSION_CODE); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static String bytesToHex(byte[] bytes) { | ||||
|         char[] hexChars = new char[bytes.length * 2]; | ||||
|         for (int j = 0; j < bytes.length; j++) { | ||||
|             int v = bytes[j] & 0xFF; | ||||
|             hexChars[j * 2] = HEX_ARRAY[v >>> 4]; | ||||
|             hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; | ||||
|         } | ||||
|         return new String(hexChars); | ||||
|     } | ||||
| } | ||||
| @ -1090,11 +1090,19 @@ public final class ResponseBodyUtils { | ||||
|         if (imageVersions2 == null) return null; | ||||
|         final List<MediaCandidate> candidates = imageVersions2.getCandidates(); | ||||
|         if (candidates == null || candidates.isEmpty()) return null; | ||||
|         final boolean isSquare = Integer.compare(media.getOriginalWidth(), media.getOriginalHeight()) == 0; | ||||
|         final List<MediaCandidate> sortedCandidates = candidates.stream() | ||||
|                 .sorted((c1, c2) -> Integer.compare(c2.getWidth(), c1.getWidth())) | ||||
|                 .filter(c -> c.getWidth() < type.getValue()) | ||||
|                 .collect(Collectors.toList()); | ||||
|         final MediaCandidate candidate = sortedCandidates.get(0); | ||||
|         if (sortedCandidates.size() == 1) return sortedCandidates.get(0).getUrl(); | ||||
|         final List<MediaCandidate> filteredCandidates = sortedCandidates.stream() | ||||
|                 .filter(c -> | ||||
|                         c.getWidth() <= media.getOriginalWidth() | ||||
|                                 && c.getWidth() <= type.getValue() | ||||
|                                 && (isSquare || Integer.compare(c.getWidth(), c.getHeight()) != 0) | ||||
|                 ) | ||||
|                 .collect(Collectors.toList()); | ||||
|         final MediaCandidate candidate = filteredCandidates.get(0); | ||||
|         if (candidate == null) return null; | ||||
|         return candidate.getUrl(); | ||||
|     } | ||||
|  | ||||
| @ -16,6 +16,7 @@ import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_D | ||||
| 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.fragments.settings.PreferenceKeys.PREF_ENABLE_SENTRY; | ||||
| import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_TAB_ORDER; | ||||
| import static awais.instagrabber.utils.Constants.APP_LANGUAGE; | ||||
| import static awais.instagrabber.utils.Constants.APP_THEME; | ||||
| import static awais.instagrabber.utils.Constants.APP_UA; | ||||
| @ -38,12 +39,13 @@ import static awais.instagrabber.utils.Constants.DOWNLOAD_USER_FOLDER; | ||||
| import static awais.instagrabber.utils.Constants.FLAG_SECURE; | ||||
| import static awais.instagrabber.utils.Constants.FOLDER_PATH; | ||||
| import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO; | ||||
| import static awais.instagrabber.utils.Constants.HIDE_MUTED_REELS; | ||||
| import static awais.instagrabber.utils.Constants.KEYWORD_FILTERS; | ||||
| import static awais.instagrabber.utils.Constants.MARK_AS_SEEN; | ||||
| import static awais.instagrabber.utils.Constants.MUTED_VIDEOS; | ||||
| import static awais.instagrabber.utils.Constants.PREF_DARK_THEME; | ||||
| import static awais.instagrabber.utils.Constants.PREF_EMOJI_VARIANTS; | ||||
| import static awais.instagrabber.utils.Constants.PREF_HASHTAG_POSTS_LAYOUT; | ||||
| import static awais.instagrabber.utils.Constants.KEYWORD_FILTERS; | ||||
| import static awais.instagrabber.utils.Constants.PREF_LIGHT_THEME; | ||||
| import static awais.instagrabber.utils.Constants.PREF_LIKED_POSTS_LAYOUT; | ||||
| import static awais.instagrabber.utils.Constants.PREF_LOCATION_POSTS_LAYOUT; | ||||
| @ -156,13 +158,13 @@ public final class SettingsHelper { | ||||
|                     CUSTOM_DATE_TIME_FORMAT, DEVICE_UUID, SKIPPED_VERSION, DEFAULT_TAB, PREF_DARK_THEME, PREF_LIGHT_THEME, | ||||
|                     PREF_POSTS_LAYOUT, PREF_PROFILE_POSTS_LAYOUT, PREF_TOPIC_POSTS_LAYOUT, PREF_HASHTAG_POSTS_LAYOUT, | ||||
|                     PREF_LOCATION_POSTS_LAYOUT, PREF_LIKED_POSTS_LAYOUT, PREF_TAGGED_POSTS_LAYOUT, PREF_SAVED_POSTS_LAYOUT, | ||||
|                     STORY_SORT, PREF_EMOJI_VARIANTS, PREF_REACTIONS, PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT}) | ||||
|                     STORY_SORT, PREF_EMOJI_VARIANTS, PREF_REACTIONS, PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT, PREF_TAB_ORDER}) | ||||
|     public @interface StringSettings {} | ||||
| 
 | ||||
|     @StringDef({DOWNLOAD_USER_FOLDER, DOWNLOAD_PREPEND_USER_NAME, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS, | ||||
|                        SHOW_CAPTIONS, CUSTOM_DATE_TIME_FORMAT_ENABLED, MARK_AS_SEEN, DM_MARK_AS_SEEN, CHECK_ACTIVITY, | ||||
|                        CHECK_UPDATES, SWAP_DATE_TIME_FORMAT_ENABLED, PREF_ENABLE_DM_NOTIFICATIONS, PREF_ENABLE_DM_AUTO_REFRESH, | ||||
|                        FLAG_SECURE, TOGGLE_KEYWORD_FILTER, PREF_ENABLE_SENTRY}) | ||||
|                        FLAG_SECURE, TOGGLE_KEYWORD_FILTER, PREF_ENABLE_SENTRY, HIDE_MUTED_REELS}) | ||||
|     public @interface BooleanSettings {} | ||||
| 
 | ||||
|     @StringDef({PREV_INSTALL_VERSION, BROWSER_UA_CODE, APP_UA_CODE, PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER}) | ||||
|  | ||||
| @ -2,8 +2,6 @@ package awais.instagrabber.utils; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.text.SpannableString; | ||||
| import android.text.SpannableStringBuilder; | ||||
| import android.text.Spanned; | ||||
| import android.text.format.DateFormat; | ||||
| import android.text.format.DateUtils; | ||||
| import android.text.style.URLSpan; | ||||
| @ -11,17 +9,12 @@ import android.util.Patterns; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| import java.io.UnsupportedEncodingException; | ||||
| import java.net.URLEncoder; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.Date; | ||||
| import java.util.List; | ||||
| import java.util.Locale; | ||||
| import java.util.regex.Matcher; | ||||
| import java.util.regex.Pattern; | ||||
| 
 | ||||
| import awais.instagrabber.customviews.CommentMentionClickSpan; | ||||
| 
 | ||||
| public final class TextUtils { | ||||
|     // extracted from String class | ||||
|  | ||||
| @ -0,0 +1,38 @@ | ||||
| package awais.instagrabber.utils; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.content.DialogInterface; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| import com.google.android.material.dialog.MaterialAlertDialogBuilder; | ||||
| 
 | ||||
| import awais.instagrabber.BuildConfig; | ||||
| import awais.instagrabber.R; | ||||
| 
 | ||||
| import static awais.instagrabber.utils.Utils.settingsHelper; | ||||
| 
 | ||||
| public final class UpdateCheckCommon { | ||||
| 
 | ||||
|     public static boolean shouldShowUpdateDialog(final boolean force, | ||||
|                                                  @NonNull final String version) { | ||||
|         final String skippedVersion = settingsHelper.getString(Constants.SKIPPED_VERSION); | ||||
|         return force || (!BuildConfig.DEBUG && !skippedVersion.equals(version)); | ||||
|     } | ||||
| 
 | ||||
|     public static void showUpdateDialog(@NonNull final Context context, | ||||
|                                         @NonNull final String version, | ||||
|                                         @NonNull final DialogInterface.OnClickListener onDownloadClickListener) { | ||||
|         AppExecutors.getInstance().mainThread().execute(() -> { | ||||
|             new MaterialAlertDialogBuilder(context) | ||||
|                     .setTitle(context.getString(R.string.update_available, version)) | ||||
|                     .setNeutralButton(R.string.skip_update, (dialog, which) -> { | ||||
|                         settingsHelper.putString(Constants.SKIPPED_VERSION, version); | ||||
|                         dialog.dismiss(); | ||||
|                     }) | ||||
|                     .setPositiveButton(R.string.action_download, onDownloadClickListener) | ||||
|                     .setNegativeButton(R.string.cancel, null) | ||||
|                     .show(); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
| @ -1,58 +0,0 @@ | ||||
| package awais.instagrabber.utils; | ||||
| 
 | ||||
| import android.os.AsyncTask; | ||||
| import android.util.Log; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| import org.json.JSONObject; | ||||
| 
 | ||||
| import java.net.HttpURLConnection; | ||||
| import java.net.URL; | ||||
| 
 | ||||
| import awais.instagrabber.BuildConfig; | ||||
| import awais.instagrabber.interfaces.FetchListener; | ||||
| 
 | ||||
| public final class UpdateChecker extends AsyncTask<Void, Void, Boolean> { | ||||
|     private final FetchListener<String> fetchListener; | ||||
|     private String version; | ||||
| 
 | ||||
|     public UpdateChecker(final FetchListener<String> fetchListener) { | ||||
|         this.fetchListener = fetchListener; | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     protected Boolean doInBackground(final Void... voids) { | ||||
|         try { | ||||
|             version = ""; | ||||
| 
 | ||||
|             HttpURLConnection conn = | ||||
|                     (HttpURLConnection) new URL("https://f-droid.org/api/v1/packages/me.austinhuang.instagrabber").openConnection(); | ||||
|             conn.setUseCaches(false); | ||||
|             conn.setRequestProperty("User-Agent", "https://Barinsta.AustinHuang.me / mailto:Barinsta@AustinHuang.me"); | ||||
|             conn.connect(); | ||||
| 
 | ||||
|             final int responseCode = conn.getResponseCode(); | ||||
|             if (responseCode == HttpURLConnection.HTTP_OK) { | ||||
|                 final JSONObject data = new JSONObject(NetworkUtils.readFromConnection(conn)); | ||||
|                 if (BuildConfig.VERSION_CODE < data.getInt("suggestedVersionCode")) { | ||||
|                     version = data.getJSONArray("packages").getJSONObject(0).getString("versionName"); | ||||
|                     return true; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             conn.disconnect(); | ||||
|         } catch (final Exception e) { | ||||
|             if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); | ||||
|         } | ||||
| 
 | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void onPostExecute(final Boolean result) { | ||||
|         if (result != null && result && fetchListener != null) | ||||
|             fetchListener.onResult("v"+version); | ||||
|     } | ||||
| } | ||||
| @ -8,6 +8,7 @@ import android.content.ContentResolver; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.content.res.Resources; | ||||
| import android.content.res.TypedArray; | ||||
| import android.graphics.Color; | ||||
| import android.graphics.Rect; | ||||
| import android.graphics.drawable.Drawable; | ||||
| @ -30,6 +31,7 @@ import android.webkit.MimeTypeMap; | ||||
| import android.widget.Toast; | ||||
| 
 | ||||
| import androidx.annotation.DrawableRes; | ||||
| import androidx.annotation.IdRes; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.appcompat.app.ActionBar; | ||||
| @ -40,6 +42,8 @@ import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat; | ||||
| import com.google.android.exoplayer2.database.ExoDatabaseProvider; | ||||
| import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor; | ||||
| import com.google.android.exoplayer2.upstream.cache.SimpleCache; | ||||
| import com.google.common.collect.ImmutableList; | ||||
| import com.google.common.collect.Ordering; | ||||
| import com.google.common.io.Files; | ||||
| 
 | ||||
| import org.json.JSONObject; | ||||
| @ -47,22 +51,25 @@ import org.json.JSONObject; | ||||
| import java.io.File; | ||||
| import java.lang.reflect.Field; | ||||
| import java.text.SimpleDateFormat; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Arrays; | ||||
| import java.util.Collections; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| 
 | ||||
| //import javax.crypto.Mac; | ||||
| //import javax.crypto.spec.SecretKeySpec; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| import awais.instagrabber.R; | ||||
| import awais.instagrabber.fragments.settings.PreferenceKeys; | ||||
| import awais.instagrabber.models.PostsLayoutPreferences; | ||||
| import awais.instagrabber.models.Tab; | ||||
| import awais.instagrabber.models.enums.FavoriteType; | ||||
| //import awaisomereport.LogCollector; | ||||
| 
 | ||||
| public final class Utils { | ||||
|     private static final String TAG = "Utils"; | ||||
|     private static final int VIDEO_CACHE_MAX_BYTES = 10 * 1024 * 1024; | ||||
| 
 | ||||
| //    public static LogCollector logCollector; | ||||
|     // public static LogCollector logCollector; | ||||
|     public static SettingsHelper settingsHelper; | ||||
|     public static boolean sessionVolumeFull = false; | ||||
|     public static final MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton(); | ||||
| @ -74,6 +81,7 @@ public final class Utils { | ||||
|     private static int actionBarHeight; | ||||
|     public static Handler applicationHandler; | ||||
|     public static String cacheDir; | ||||
|     public static String tabOrderString; | ||||
|     private static int defaultStatusBarColor; | ||||
| 
 | ||||
|     public static int convertDpToPx(final float dp) { | ||||
| @ -93,34 +101,34 @@ public final class Utils { | ||||
|     } | ||||
| 
 | ||||
|     public static Map<String, String> sign(final Map<String, Object> form) { | ||||
| //        final String signed = sign(Constants.SIGNATURE_KEY, new JSONObject(form).toString()); | ||||
| //        if (signed == null) { | ||||
| //            return null; | ||||
| //        } | ||||
|         // final String signed = sign(Constants.SIGNATURE_KEY, new JSONObject(form).toString()); | ||||
|         // if (signed == null) { | ||||
|         //     return null; | ||||
|         // } | ||||
|         final Map<String, String> map = new HashMap<>(); | ||||
| //        map.put("ig_sig_key_version", Constants.SIGNATURE_VERSION); | ||||
| //        map.put("signed_body", signed); | ||||
|         // map.put("ig_sig_key_version", Constants.SIGNATURE_VERSION); | ||||
|         // map.put("signed_body", signed); | ||||
|         map.put("signed_body", "SIGNATURE." + new JSONObject(form).toString()); | ||||
|         return map; | ||||
|     } | ||||
| 
 | ||||
| //    public static String sign(final String key, final String message) { | ||||
| //        try { | ||||
| //            final Mac hasher = Mac.getInstance("HmacSHA256"); | ||||
| //            hasher.init(new SecretKeySpec(key.getBytes(), "HmacSHA256")); | ||||
| //            byte[] hash = hasher.doFinal(message.getBytes()); | ||||
| //            final StringBuilder hexString = new StringBuilder(); | ||||
| //            for (byte b : hash) { | ||||
| //                final String hex = Integer.toHexString(0xff & b); | ||||
| //                if (hex.length() == 1) hexString.append('0'); | ||||
| //                hexString.append(hex); | ||||
| //            } | ||||
| //            return hexString.toString() + "." + message; | ||||
| //        } catch (Exception e) { | ||||
| //            Log.e(TAG, "Error signing", e); | ||||
| //            return null; | ||||
| //        } | ||||
| //    } | ||||
|     // public static String sign(final String key, final String message) { | ||||
|     //     try { | ||||
|     //         final Mac hasher = Mac.getInstance("HmacSHA256"); | ||||
|     //         hasher.init(new SecretKeySpec(key.getBytes(), "HmacSHA256")); | ||||
|     //         byte[] hash = hasher.doFinal(message.getBytes()); | ||||
|     //         final StringBuilder hexString = new StringBuilder(); | ||||
|     //         for (byte b : hash) { | ||||
|     //             final String hex = Integer.toHexString(0xff & b); | ||||
|     //             if (hex.length() == 1) hexString.append('0'); | ||||
|     //             hexString.append(hex); | ||||
|     //         } | ||||
|     //         return hexString.toString() + "." + message; | ||||
|     //     } catch (Exception e) { | ||||
|     //         Log.e(TAG, "Error signing", e); | ||||
|     //         return null; | ||||
|     //     } | ||||
|     // } | ||||
| 
 | ||||
|     public static String getMimeType(@NonNull final Uri uri, final ContentResolver contentResolver) { | ||||
|         String mimeType; | ||||
| @ -371,4 +379,116 @@ public final class Utils { | ||||
|         if (window == null) return; | ||||
|         window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); | ||||
|     } | ||||
| 
 | ||||
|     public static <T> void moveItem(int sourceIndex, int targetIndex, List<T> list) { | ||||
|         if (sourceIndex <= targetIndex) { | ||||
|             Collections.rotate(list.subList(sourceIndex, targetIndex + 1), -1); | ||||
|         } else { | ||||
|             Collections.rotate(list.subList(targetIndex, sourceIndex + 1), 1); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static final List<Integer> NON_REMOVABLE_NAV_ROOT_IDS = ImmutableList.of(R.id.profile_nav_graph, R.id.more_nav_graph); | ||||
| 
 | ||||
|     @NonNull | ||||
|     public static Pair<List<Tab>, List<Tab>> getNavTabList(@NonNull final Context context) { | ||||
|         final Resources resources = context.getResources(); | ||||
|         final String[] titleArray = resources.getStringArray(R.array.main_nav_titles); | ||||
| 
 | ||||
|         TypedArray typedArray = resources.obtainTypedArray(R.array.main_nav_graphs); | ||||
|         int length = typedArray.length(); | ||||
|         final String[] navGraphNames = new String[length]; | ||||
|         final int[] navigationResIds = new int[length]; | ||||
|         for (int i = 0; i < length; i++) { | ||||
|             final int resourceId = typedArray.getResourceId(i, 0); | ||||
|             if (resourceId == 0) continue; | ||||
|             navigationResIds[i] = resourceId; | ||||
|             navGraphNames[i] = resources.getResourceEntryName(resourceId); | ||||
|         } | ||||
|         typedArray.recycle(); | ||||
| 
 | ||||
|         typedArray = resources.obtainTypedArray(R.array.main_nav_graph_root_ids); | ||||
|         length = typedArray.length(); | ||||
|         final int[] navRootIds = new int[length]; | ||||
|         for (int i = 0; i < length; i++) { | ||||
|             final int resourceId = typedArray.getResourceId(i, 0); | ||||
|             if (resourceId == 0) continue; | ||||
|             navRootIds[i] = resourceId; | ||||
|         } | ||||
|         typedArray.recycle(); | ||||
| 
 | ||||
|         typedArray = resources.obtainTypedArray(R.array.main_nav_drawables); | ||||
|         length = typedArray.length(); | ||||
|         final int[] iconIds = new int[length]; | ||||
|         for (int i = 0; i < length; i++) { | ||||
|             final int resourceId = typedArray.getResourceId(i, 0); | ||||
|             if (resourceId == 0) continue; | ||||
|             iconIds[i] = resourceId; | ||||
|         } | ||||
|         typedArray.recycle(); | ||||
| 
 | ||||
|         typedArray = resources.obtainTypedArray(R.array.main_nav_start_dest_frag_ids); | ||||
|         length = typedArray.length(); | ||||
|         final int[] startDestFragIds = new int[length]; | ||||
|         for (int i = 0; i < length; i++) { | ||||
|             final int resourceId = typedArray.getResourceId(i, 0); | ||||
|             if (resourceId == 0) continue; | ||||
|             startDestFragIds[i] = resourceId; | ||||
|         } | ||||
|         typedArray.recycle(); | ||||
| 
 | ||||
|         final List<String> currentOrderGraphNames = getCurrentOrderOfGraphNamesFromPref(navGraphNames); | ||||
| 
 | ||||
|         if (titleArray.length != iconIds.length || titleArray.length != navGraphNames.length) { | ||||
|             throw new RuntimeException(String.format("Array lengths don't match!: titleArray%s, navGraphNames: %s, iconIds: %s", | ||||
|                                                      Arrays.toString(titleArray), Arrays.toString(navGraphNames), Arrays.toString(iconIds))); | ||||
|         } | ||||
|         final List<Tab> tabs = new ArrayList<>(); | ||||
|         final List<Tab> otherTabs = new ArrayList<>(); // Will contain tabs not in current list | ||||
|         for (int i = 0; i < length; i++) { | ||||
|             final String navGraphName = navGraphNames[i]; | ||||
|             final int navRootId = navRootIds[i]; | ||||
|             final Tab tab = new Tab(iconIds[i], | ||||
|                                     titleArray[i], | ||||
|                                     !NON_REMOVABLE_NAV_ROOT_IDS.contains(navRootId), | ||||
|                                     navGraphName, | ||||
|                                     navigationResIds[i], | ||||
|                                     navRootId, | ||||
|                                     startDestFragIds[i]); | ||||
|             if (!currentOrderGraphNames.contains(navGraphName)) { | ||||
|                 otherTabs.add(tab); | ||||
|                 continue; | ||||
|             } | ||||
|             tabs.add(tab); | ||||
|         } | ||||
|         Collections.sort(tabs, Ordering.explicit(currentOrderGraphNames).onResultOf(tab -> { | ||||
|             if (tab == null) return null; | ||||
|             return tab.getGraphName(); | ||||
|         })); | ||||
|         return new Pair<>(tabs, otherTabs); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     private static List<String> getCurrentOrderOfGraphNamesFromPref(@NonNull final String[] navGraphNames) { | ||||
|         tabOrderString = settingsHelper.getString(PreferenceKeys.PREF_TAB_ORDER); | ||||
|         final List<String> navGraphNameList = Arrays.asList(navGraphNames); | ||||
|         if (TextUtils.isEmpty(tabOrderString)) { | ||||
|             // Use top 5 entries for default list | ||||
|             return navGraphNameList.subList(0, 5); | ||||
|         } | ||||
|         // Make sure that the list from preference does not contain any invalid values | ||||
|         final List<String> orderGraphNames = Arrays.stream(tabOrderString.split(",")) | ||||
|                                                    .filter(s -> !TextUtils.isEmpty(s)) | ||||
|                                                    .filter(navGraphNameList::contains) | ||||
|                                                    .collect(Collectors.toList()); | ||||
|         if (orderGraphNames.isEmpty()) { | ||||
|             // Use top 5 entries for default list | ||||
|             return navGraphNameList.subList(0, 5); | ||||
|         } | ||||
|         return orderGraphNames; | ||||
|     } | ||||
| 
 | ||||
|     public static boolean isNavRootInCurrentTabs(final String navRootString) { | ||||
|         return tabOrderString.contains(navRootString); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -12,12 +12,12 @@ import java.util.Map; | ||||
| import java.util.UUID; | ||||
| 
 | ||||
| import awais.instagrabber.repositories.FeedRepository; | ||||
| import awais.instagrabber.repositories.responses.Media; | ||||
| import awais.instagrabber.repositories.responses.PostsFetchResponse; | ||||
| import awais.instagrabber.repositories.responses.feed.EndOfFeedDemarcator; | ||||
| import awais.instagrabber.repositories.responses.feed.EndOfFeedGroup; | ||||
| import awais.instagrabber.repositories.responses.feed.EndOfFeedGroupSet; | ||||
| import awais.instagrabber.repositories.responses.feed.FeedFetchResponse; | ||||
| import awais.instagrabber.repositories.responses.Media; | ||||
| import awais.instagrabber.repositories.responses.PostsFetchResponse; | ||||
| import awais.instagrabber.utils.TextUtils; | ||||
| import retrofit2.Call; | ||||
| import retrofit2.Callback; | ||||
|  | ||||
| @ -12,12 +12,12 @@ import java.util.stream.Collectors; | ||||
| import awais.instagrabber.repositories.NewsRepository; | ||||
| import awais.instagrabber.repositories.responses.AymlResponse; | ||||
| import awais.instagrabber.repositories.responses.AymlUser; | ||||
| import awais.instagrabber.repositories.responses.notification.NotificationCounts; | ||||
| import awais.instagrabber.repositories.responses.UserSearchResponse; | ||||
| import awais.instagrabber.repositories.responses.NewsInboxResponse; | ||||
| import awais.instagrabber.repositories.responses.User; | ||||
| import awais.instagrabber.repositories.responses.UserSearchResponse; | ||||
| import awais.instagrabber.repositories.responses.notification.Notification; | ||||
| import awais.instagrabber.repositories.responses.notification.NotificationArgs; | ||||
| import awais.instagrabber.repositories.responses.User; | ||||
| import awais.instagrabber.repositories.responses.notification.NotificationCounts; | ||||
| import awais.instagrabber.utils.Constants; | ||||
| import retrofit2.Call; | ||||
| import retrofit2.Callback; | ||||
|  | ||||
| @ -1,15 +1,10 @@ | ||||
| package awais.instagrabber.webservices; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| import com.google.common.collect.ImmutableMap; | ||||
| 
 | ||||
| import awais.instagrabber.repositories.SearchRepository; | ||||
| import awais.instagrabber.repositories.responses.search.SearchResponse; | ||||
| import awais.instagrabber.utils.TextUtils; | ||||
| import retrofit2.Call; | ||||
| import retrofit2.Callback; | ||||
| import retrofit2.Response; | ||||
| import retrofit2.Retrofit; | ||||
| 
 | ||||
| public class SearchService extends BaseService { | ||||
|  | ||||
| @ -137,7 +137,7 @@ public class StoriesService extends BaseService { | ||||
|             final JSONArray feedStoriesReel = new JSONObject(body).getJSONArray("tray"); | ||||
|             for (int i = 0; i < feedStoriesReel.length(); ++i) { | ||||
|                 final JSONObject node = feedStoriesReel.getJSONObject(i); | ||||
|                 if (node.optBoolean("hide_from_feed_unit")) continue; | ||||
|                 if (node.optBoolean("hide_from_feed_unit") && Utils.settingsHelper.getBoolean(Constants.HIDE_MUTED_REELS)) continue; | ||||
|                 final JSONObject userJson = node.getJSONObject(node.has("user") ? "user" : "owner"); | ||||
|                 try { | ||||
|                     final User user = new User(userJson.getLong("pk"), | ||||
| @ -179,17 +179,22 @@ public class StoriesService extends BaseService { | ||||
|                                                null, | ||||
|                                                null | ||||
|                     ); | ||||
|                     final String id = node.getString("id"); | ||||
|                     final long timestamp = node.getLong("latest_reel_media"); | ||||
|                     final int mediaCount = node.getInt("media_count"); | ||||
|                     final boolean fullyRead = !node.isNull("seen") && node.getLong("seen") == timestamp; | ||||
|                     final JSONObject itemJson = node.has("items") ? node.getJSONArray("items").optJSONObject(0) : null; | ||||
|                     final boolean isBestie = node.optBoolean("has_besties_media", false); | ||||
|                     StoryModel firstStoryModel = null; | ||||
|                     if (itemJson != null) { | ||||
|                         firstStoryModel = ResponseBodyUtils.parseStoryItem(itemJson, false, null); | ||||
|                     } | ||||
|                     feedStoryModels.add(new FeedStoryModel(id, user, fullyRead, timestamp, firstStoryModel, mediaCount, false, isBestie)); | ||||
|                     feedStoryModels.add(new FeedStoryModel( | ||||
|                             node.getString("id"), | ||||
|                             user, | ||||
|                             fullyRead, | ||||
|                             timestamp, | ||||
|                             firstStoryModel, | ||||
|                             node.getInt("media_count"), | ||||
|                             false, | ||||
|                             node.optBoolean("has_besties_media"))); | ||||
|                 } | ||||
|                 catch (Exception e) {} // to cover promotional reels with non-long user pk's | ||||
|             } | ||||
| @ -242,13 +247,16 @@ public class StoriesService extends BaseService { | ||||
|                                            null, | ||||
|                                            null | ||||
|                 ); | ||||
|                 final String id = node.getString("id"); | ||||
|                 final long timestamp = node.getLong("published_time"); | ||||
|                 // final JSONObject itemJson = node.has("items") ? node.getJSONArray("items").getJSONObject(0) : null; | ||||
|                 final StoryModel firstStoryModel = ResponseBodyUtils.parseBroadcastItem(node); | ||||
|                 // if (itemJson != null) { | ||||
|                 // } | ||||
|                 feedStoryModels.add(new FeedStoryModel(id, user, false, timestamp, firstStoryModel, 1, true, false)); | ||||
|                 feedStoryModels.add(new FeedStoryModel( | ||||
|                         node.getString("id"), | ||||
|                         user, | ||||
|                         false, | ||||
|                         node.getLong("published_time"), | ||||
|                         ResponseBodyUtils.parseBroadcastItem(node), | ||||
|                         1, | ||||
|                         true, | ||||
|                         false | ||||
|                 )); | ||||
|             } | ||||
|             callback.onSuccess(sort(feedStoryModels)); | ||||
|         } catch (JSONException e) { | ||||
|  | ||||
| @ -1,9 +0,0 @@ | ||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:width="24dp" | ||||
|     android:height="24dp" | ||||
|     android:viewportWidth="24" | ||||
|     android:viewportHeight="24"> | ||||
|     <path | ||||
|         android:fillColor="@android:color/white" | ||||
|         android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM11,19.93c-3.95,-0.49 -7,-3.85 -7,-7.93 0,-0.62 0.08,-1.21 0.21,-1.79L9,15v1c0,1.1 0.9,2 2,2v1.93zM17.9,17.39c-0.26,-0.81 -1,-1.39 -1.9,-1.39h-1v-3c0,-0.55 -0.45,-1 -1,-1L8,12v-2h2c0.55,0 1,-0.45 1,-1L11,7h2c1.1,0 2,-0.9 2,-2v-0.41c2.93,1.19 5,4.06 5,7.41 0,2.08 -0.8,3.97 -2.1,5.39z" /> | ||||
| </vector> | ||||
							
								
								
									
										10
									
								
								app/src/main/res/drawable/ic_explore_24.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/src/main/res/drawable/ic_explore_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="M12,10.9c-0.61,0 -1.1,0.49 -1.1,1.1s0.49,1.1 1.1,1.1c0.61,0 1.1,-0.49 1.1,-1.1s-0.49,-1.1 -1.1,-1.1zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM14.19,14.19L6,18l3.81,-8.19L18,6l-3.81,8.19z"/> | ||||
| </vector> | ||||
| @ -1,9 +0,0 @@ | ||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:width="24dp" | ||||
|     android:height="24dp" | ||||
|     android:viewportWidth="24" | ||||
|     android:viewportHeight="24"> | ||||
|     <path | ||||
|         android:fillColor="@android:color/white" | ||||
|         android:pathData="M13.5,0.67s0.74,2.65 0.74,4.8c0,2.06 -1.35,3.73 -3.41,3.73 -2.07,0 -3.63,-1.67 -3.63,-3.73l0.03,-0.36C5.21,7.51 4,10.62 4,14c0,4.42 3.58,8 8,8s8,-3.58 8,-8C20,8.61 17.41,3.8 13.5,0.67zM11.71,19c-1.78,0 -3.22,-1.4 -3.22,-3.14 0,-1.62 1.05,-2.76 2.81,-3.12 1.77,-0.36 3.6,-1.21 4.62,-2.58 0.39,1.29 0.59,2.65 0.59,4.04 0,2.65 -2.15,4.8 -4.8,4.8z" /> | ||||
| </vector> | ||||
							
								
								
									
										10
									
								
								app/src/main/res/drawable/ic_home_24.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/src/main/res/drawable/ic_home_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="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"/> | ||||
| </vector> | ||||
							
								
								
									
										10
									
								
								app/src/main/res/drawable/ic_message_24.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/src/main/res/drawable/ic_message_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="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM18,14L6,14v-2h12v2zM18,11L6,11L6,9h12v2zM18,8L6,8L6,6h12v2z"/> | ||||
| </vector> | ||||
							
								
								
									
										10
									
								
								app/src/main/res/drawable/ic_person_24.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/src/main/res/drawable/ic_person_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="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/> | ||||
| </vector> | ||||
							
								
								
									
										10
									
								
								app/src/main/res/drawable/ic_round_add_circle_24.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/src/main/res/drawable/ic_round_add_circle_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="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM16,13h-3v3c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1v-3L8,13c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1h3L11,8c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v3h3c0.55,0 1,0.45 1,1s-0.45,1 -1,1z"/> | ||||
| </vector> | ||||
							
								
								
									
										10
									
								
								app/src/main/res/drawable/ic_round_drag_handle_24.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/src/main/res/drawable/ic_round_drag_handle_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="M19,9H5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1h14c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1zM5,15h14c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1H5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1z"/> | ||||
| </vector> | ||||
							
								
								
									
										10
									
								
								app/src/main/res/drawable/ic_round_remove_circle_24.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/src/main/res/drawable/ic_round_remove_circle_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="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM16,13L8,13c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1h8c0.55,0 1,0.45 1,1s-0.45,1 -1,1z"/> | ||||
| </vector> | ||||
| @ -47,7 +47,6 @@ | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:layout_gravity="bottom" | ||||
|         app:labelVisibilityMode="labeled" | ||||
|         app:layout_behavior="@string/hide_bottom_view_on_scroll_behavior" | ||||
|         app:menu="@menu/main_bottom_navigation_menu" /> | ||||
|         app:labelVisibilityMode="auto" | ||||
|         app:layout_behavior="@string/hide_bottom_view_on_scroll_behavior" /> | ||||
| </androidx.coordinatorlayout.widget.CoordinatorLayout> | ||||
							
								
								
									
										45
									
								
								app/src/main/res/layout/item_tab_order_pref.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								app/src/main/res/layout/item_tab_order_pref.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,45 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="56dp" | ||||
|     android:orientation="horizontal"> | ||||
| 
 | ||||
|     <androidx.appcompat.widget.AppCompatImageView | ||||
|         android:id="@+id/add_remove" | ||||
|         android:layout_width="24dp" | ||||
|         android:layout_height="match_parent" | ||||
|         android:layout_marginStart="16dp" | ||||
|         android:layout_marginEnd="8dp" | ||||
|         android:scaleType="centerInside" | ||||
|         tools:srcCompat="@drawable/ic_round_add_circle_24" | ||||
|         tools:tint="@color/green_500" /> | ||||
| 
 | ||||
|     <androidx.appcompat.widget.AppCompatImageView | ||||
|         android:id="@+id/icon" | ||||
|         android:layout_width="24dp" | ||||
|         android:layout_height="match_parent" | ||||
|         android:layout_marginStart="8dp" | ||||
|         android:layout_marginEnd="16dp" | ||||
|         android:scaleType="centerInside" | ||||
|         tools:srcCompat="@drawable/ic_home_24" /> | ||||
| 
 | ||||
|     <androidx.appcompat.widget.AppCompatTextView | ||||
|         android:id="@+id/title" | ||||
|         android:layout_width="0dp" | ||||
|         android:layout_height="match_parent" | ||||
|         android:layout_weight="1" | ||||
|         android:gravity="center_vertical" | ||||
|         android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" | ||||
|         tools:text="@string/feed" /> | ||||
| 
 | ||||
|     <androidx.appcompat.widget.AppCompatImageView | ||||
|         android:id="@+id/handle" | ||||
|         android:layout_width="24dp" | ||||
|         android:layout_height="match_parent" | ||||
|         android:layout_marginStart="16dp" | ||||
|         android:layout_marginEnd="16dp" | ||||
|         android:scaleType="centerInside" | ||||
|         app:srcCompat="@drawable/ic_round_drag_handle_24" /> | ||||
| </LinearLayout> | ||||
| @ -1,12 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <menu xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
|     <item | ||||
|         android:id="@+id/profile_nav_graph" | ||||
|         android:icon="@drawable/ic_profile_24" | ||||
|         android:title="@string/profile" /> | ||||
| 
 | ||||
|     <item | ||||
|         android:id="@+id/more_nav_graph" | ||||
|         android:icon="@drawable/ic_more_horiz_24" | ||||
|         android:title="@string/more" /> | ||||
| </menu> | ||||
| @ -1,30 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <menu xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
|     <item | ||||
|         android:id="@+id/direct_messages_nav_graph" | ||||
|         android:icon="@drawable/ic_round_send_24" | ||||
|         android:title="@string/title_dm" /> | ||||
|     <item | ||||
|         android:id="@+id/feed_nav_graph" | ||||
|         android:icon="@drawable/ic_feed" | ||||
|         android:title="@string/feed" /> | ||||
|     <item | ||||
|         android:id="@+id/profile_nav_graph" | ||||
|         android:icon="@drawable/ic_profile_24" | ||||
|         android:title="@string/profile" /> | ||||
| 
 | ||||
|     <item | ||||
|         android:id="@+id/discover_nav_graph" | ||||
|         android:icon="@drawable/ic_discover" | ||||
|         android:title="@string/title_discover" /> | ||||
| 
 | ||||
|     <!--<item--> | ||||
|     <!--    android:id="@+id/favouritesFragment"--> | ||||
|     <!--    android:icon="@drawable/ic_star_24"--> | ||||
|     <!--    android:title="@string/title_favorites"/>--> | ||||
| 
 | ||||
|     <item | ||||
|         android:id="@+id/more_nav_graph" | ||||
|         android:icon="@drawable/ic_more_horiz_24" | ||||
|         android:title="@string/more" /> | ||||
| </menu> | ||||
							
								
								
									
										43
									
								
								app/src/main/res/navigation/favorites_nav_graph.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								app/src/main/res/navigation/favorites_nav_graph.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <navigation xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     android:id="@+id/favorites_nav_graph" | ||||
|     app:startDestination="@id/favoritesFragment"> | ||||
| 
 | ||||
|     <include app:graph="@navigation/profile_nav_graph" /> | ||||
|     <include app:graph="@navigation/hashtag_nav_graph" /> | ||||
|     <include app:graph="@navigation/location_nav_graph" /> | ||||
|     <include app:graph="@navigation/comments_nav_graph" /> | ||||
|     <include app:graph="@navigation/likes_nav_graph" /> | ||||
| 
 | ||||
|     <action | ||||
|         android:id="@+id/action_global_profileFragment" | ||||
|         app:destination="@id/profile_nav_graph"> | ||||
|         <argument | ||||
|             android:name="username" | ||||
|             app:argType="string" | ||||
|             app:nullable="true" /> | ||||
|     </action> | ||||
| 
 | ||||
|     <action | ||||
|         android:id="@+id/action_global_hashTagFragment" | ||||
|         app:destination="@id/hashtag_nav_graph"> | ||||
|         <argument | ||||
|             android:name="hashtag" | ||||
|             app:argType="string" | ||||
|             app:nullable="false" /> | ||||
|     </action> | ||||
| 
 | ||||
|     <action | ||||
|         android:id="@+id/action_global_locationFragment" | ||||
|         app:destination="@id/location_nav_graph"> | ||||
|         <argument | ||||
|             android:name="locationId" | ||||
|             app:argType="long" /> | ||||
|     </action> | ||||
| 
 | ||||
|     <fragment | ||||
|         android:id="@+id/favoritesFragment" | ||||
|         android:name="awais.instagrabber.fragments.FavoritesFragment" | ||||
|         android:label="@string/title_favorites" /> | ||||
| </navigation> | ||||
| @ -11,6 +11,7 @@ | ||||
|     <include app:graph="@navigation/likes_nav_graph" /> | ||||
|     <include app:graph="@navigation/notification_viewer_nav_graph" /> | ||||
|     <include app:graph="@navigation/story_list_nav_graph" /> | ||||
|     <include app:graph="@navigation/discover_nav_graph" /> | ||||
| 
 | ||||
|     <action | ||||
|         android:id="@+id/action_global_profileFragment" | ||||
|  | ||||
| @ -13,7 +13,8 @@ | ||||
|         <argument | ||||
|             android:name="type" | ||||
|             app:argType="string" | ||||
|             app:nullable="false" /> | ||||
|             app:nullable="false" | ||||
|             android:defaultValue="notif"/> | ||||
|         <argument | ||||
|             android:name="targetId" | ||||
|             android:defaultValue="0L" | ||||
| @ -23,6 +24,17 @@ | ||||
|             app:destination="@id/storyViewerFragment" /> | ||||
|     </fragment> | ||||
| 
 | ||||
|     <include app:graph="@navigation/profile_nav_graph" /> | ||||
| 
 | ||||
|     <action | ||||
|         android:id="@+id/action_global_profileFragment" | ||||
|         app:destination="@id/profile_nav_graph"> | ||||
|         <argument | ||||
|             android:name="username" | ||||
|             app:argType="string" | ||||
|             app:nullable="true" /> | ||||
|     </action> | ||||
| 
 | ||||
|     <action | ||||
|         android:id="@+id/action_global_notificationsViewerFragment" | ||||
|         app:destination="@id/notificationsViewer"> | ||||
|  | ||||
| @ -71,11 +71,9 @@ | ||||
|             app:argType="long" /> | ||||
|     </action> | ||||
| 
 | ||||
|     <include app:graph="@navigation/notification_viewer_nav_graph" /> | ||||
| 
 | ||||
|     <action | ||||
|         android:id="@+id/action_global_notificationsViewerFragment" | ||||
|         app:destination="@id/notification_viewer_nav_graph"> | ||||
|         app:destination="@id/notificationsViewer"> | ||||
|         <argument | ||||
|             android:name="type" | ||||
|             app:argType="string" | ||||
| @ -86,6 +84,22 @@ | ||||
|             app:argType="long" /> | ||||
|     </action> | ||||
| 
 | ||||
|     <fragment | ||||
|         android:id="@+id/notificationsViewer" | ||||
|         android:name="awais.instagrabber.fragments.NotificationsViewerFragment" | ||||
|         android:label="@string/title_notifications" | ||||
|         tools:layout="@layout/fragment_notifications_viewer"> | ||||
|         <argument | ||||
|             android:name="type" | ||||
|             app:argType="string" | ||||
|             app:nullable="false" | ||||
|             android:defaultValue="notif"/> | ||||
|         <argument | ||||
|             android:name="targetId" | ||||
|             android:defaultValue="0L" | ||||
|             app:argType="long" /> | ||||
|     </fragment> | ||||
| 
 | ||||
|     <include app:graph="@navigation/saved_nav_graph" /> | ||||
| 
 | ||||
|     <action | ||||
|  | ||||
| @ -88,24 +88,61 @@ | ||||
|         <item>HH:mm:ss</item> | ||||
|         <item>H:mm:ss</item> | ||||
|     </string-array> | ||||
|     <array name="main_nav_ids"> | ||||
|     <array name="main_nav_graph_root_ids"> | ||||
|         <item>@id/direct_messages_nav_graph</item> | ||||
|         <item>@id/feed_nav_graph</item> | ||||
|         <item>@id/profile_nav_graph</item> | ||||
|         <item>@id/discover_nav_graph</item> | ||||
|         <item>@id/more_nav_graph</item> | ||||
|         <!-- New graphs should go below --> | ||||
|         <item>@id/favorites_nav_graph</item> | ||||
|         <item>@id/notification_viewer_nav_graph</item> | ||||
|     </array> | ||||
|     <!-- Nav graphs should correspond 1-to-1 with the above nav graph ids --> | ||||
|     <array name="main_nav_graphs"> | ||||
|         <item>@navigation/direct_messages_nav_graph</item> | ||||
|         <item>@navigation/feed_nav_graph</item> | ||||
|         <item>@navigation/profile_nav_graph</item> | ||||
|         <item>@navigation/discover_nav_graph</item> | ||||
|         <item>@navigation/more_nav_graph</item> | ||||
|         <item>@navigation/favorites_nav_graph</item> | ||||
|         <item>@navigation/notification_viewer_nav_graph</item> | ||||
|     </array> | ||||
|     <string-array name="main_nav_ids_values" translatable="false"> | ||||
|     <!-- Titles should correspond 1-to-1 with the above nav graphs --> | ||||
|     <string-array name="main_nav_titles" translatable="false"> | ||||
|         <item>@string/title_dm</item> | ||||
|         <item>@string/feed</item> | ||||
|         <item>@string/profile</item> | ||||
|         <item>@string/title_discover</item> | ||||
|         <item>@string/more</item> | ||||
|         <item>@string/title_favorites</item> | ||||
|         <item>@string/title_notifications</item> | ||||
|     </string-array> | ||||
|     <array name="logged_out_main_nav_ids"> | ||||
|         <item>@navigation/profile_nav_graph</item> | ||||
|         <item>@navigation/more_nav_graph</item> | ||||
|     <!-- Drawable should correspond 1-to-1 with the above titles --> | ||||
|     <array name="main_nav_drawables" translatable="false"> | ||||
|         <item>@drawable/ic_message_24</item> | ||||
|         <item>@drawable/ic_home_24</item> | ||||
|         <item>@drawable/ic_person_24</item> | ||||
|         <item>@drawable/ic_explore_24</item> | ||||
|         <item>@drawable/ic_more_horiz_24</item> | ||||
|         <item>@drawable/ic_star_24</item> | ||||
|         <item>@drawable/ic_not_liked</item> | ||||
|     </array> | ||||
|     <!-- fragmentIds should correspond 1-to-1 with the above drawabled --> | ||||
|     <!-- these are the start destination of the corresponding nav graphs --> | ||||
|     <array name="main_nav_start_dest_frag_ids" translatable="false"> | ||||
|         <item>@id/directMessagesInboxFragment</item> | ||||
|         <item>@id/feedFragment</item> | ||||
|         <item>@id/profileFragment</item> | ||||
|         <item>@id/discoverFragment</item> | ||||
|         <item>@id/morePreferencesFragment</item> | ||||
|         <item>@id/favoritesFragment</item> | ||||
|         <item>@id/notificationsViewer</item> | ||||
|     </array> | ||||
|     <!--<array name="logged_out_main_nav_graphs">--> | ||||
|     <!--    <item>@navigation/profile_nav_graph</item>--> | ||||
|     <!--    <item>@navigation/more_nav_graph</item>--> | ||||
|     <!--</array>--> | ||||
|     <string-array name="light_themes" translatable="false"> | ||||
|         <item>@string/light_white_theme</item> | ||||
|         <item>@string/light_barinsta_theme</item> | ||||
|  | ||||
| @ -29,6 +29,7 @@ | ||||
|     <string name="download_prepend_username">Prepend Username to Filename</string> | ||||
|     <string name="mark_as_seen_setting">Mark stories as seen after viewing</string> | ||||
|     <string name="mark_as_seen_setting_summary">Story author will know you viewed it</string> | ||||
|     <string name="hide_muted_reels_setting">Hide muted stories from feed</string> | ||||
|     <string name="dm_mark_as_seen_setting">Mark DM as seen after viewing</string> | ||||
|     <string name="dm_mark_as_seen_setting_summary">Other members will know you viewed it</string> | ||||
|     <string name="activity_setting">Enable activity notifications</string> | ||||
| @ -476,6 +477,12 @@ | ||||
|     <string name="delete_unsuccessful">Delete unsuccessful</string> | ||||
|     <string name="crash_report_subject">Barinsta Crash Report</string> | ||||
|     <string name="crash_report_title">Select an email app to send crash logs</string> | ||||
|     <string name="skip_update">Skip this update</string> | ||||
|     <string name="on_latest_version">You\'re already on the latest version</string> | ||||
|     <string name="tab_order">Screen order</string> | ||||
|     <string name="other_tabs">Other tabs</string> | ||||
|     <string name="tab_order_start_next_launch">The tab order will be reflected on next launch</string> | ||||
|     <string name="dm_remove_warning">If saved, all DM related features will be disabled on next launch</string> | ||||
|     <string name="copy_caption">Copy caption</string> | ||||
|     <string name="copy_reply">Copy reply</string> | ||||
| </resources> | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user