mirror of
				https://github.com/KokaKiwi/BarInsta
				synced 2025-10-31 11:35:34 +00:00 
			
		
		
		
	Customise screen/tab order
This commit is contained in:
		
							parent
							
								
									d1e985505c
								
							
						
					
					
						commit
						9fe896bc65
					
				| @ -8,7 +8,6 @@ import android.content.ComponentName; | |||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.content.Intent; | import android.content.Intent; | ||||||
| import android.content.ServiceConnection; | import android.content.ServiceConnection; | ||||||
| import android.content.res.TypedArray; |  | ||||||
| import android.database.MatrixCursor; | import android.database.MatrixCursor; | ||||||
| import android.net.Uri; | import android.net.Uri; | ||||||
| import android.os.AsyncTask; | import android.os.AsyncTask; | ||||||
| @ -25,6 +24,7 @@ import android.view.WindowManager; | |||||||
| import android.widget.AutoCompleteTextView; | import android.widget.AutoCompleteTextView; | ||||||
| import android.widget.Toast; | import android.widget.Toast; | ||||||
| 
 | 
 | ||||||
|  | import androidx.annotation.IdRes; | ||||||
| import androidx.annotation.NonNull; | import androidx.annotation.NonNull; | ||||||
| import androidx.annotation.Nullable; | import androidx.annotation.Nullable; | ||||||
| import androidx.appcompat.app.ActionBar; | import androidx.appcompat.app.ActionBar; | ||||||
| @ -50,13 +50,12 @@ import com.google.android.material.appbar.AppBarLayout; | |||||||
| import com.google.android.material.appbar.CollapsingToolbarLayout; | import com.google.android.material.appbar.CollapsingToolbarLayout; | ||||||
| import com.google.android.material.behavior.HideBottomViewOnScrollBehavior; | import com.google.android.material.behavior.HideBottomViewOnScrollBehavior; | ||||||
| import com.google.android.material.bottomnavigation.BottomNavigationView; | 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.Deque; | import java.util.Deque; | ||||||
| import java.util.HashMap; |  | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Map; | import java.util.stream.Collectors; | ||||||
| 
 | 
 | ||||||
| import awais.instagrabber.R; | import awais.instagrabber.R; | ||||||
| import awais.instagrabber.adapters.SuggestionsAdapter; | import awais.instagrabber.adapters.SuggestionsAdapter; | ||||||
| @ -71,6 +70,7 @@ import awais.instagrabber.fragments.settings.PreferenceKeys; | |||||||
| import awais.instagrabber.interfaces.FetchListener; | import awais.instagrabber.interfaces.FetchListener; | ||||||
| import awais.instagrabber.models.IntentModel; | import awais.instagrabber.models.IntentModel; | ||||||
| import awais.instagrabber.models.SuggestionModel; | import awais.instagrabber.models.SuggestionModel; | ||||||
|  | import awais.instagrabber.models.Tab; | ||||||
| import awais.instagrabber.models.enums.SuggestionType; | import awais.instagrabber.models.enums.SuggestionType; | ||||||
| import awais.instagrabber.services.ActivityCheckerService; | import awais.instagrabber.services.ActivityCheckerService; | ||||||
| import awais.instagrabber.services.DMSyncAlarmReceiver; | import awais.instagrabber.services.DMSyncAlarmReceiver; | ||||||
| @ -90,13 +90,13 @@ import static awais.instagrabber.utils.Utils.settingsHelper; | |||||||
| public class MainActivity extends BaseLanguageActivity implements FragmentManager.OnBackStackChangedListener { | public class MainActivity extends BaseLanguageActivity implements FragmentManager.OnBackStackChangedListener { | ||||||
|     private static final String TAG = "MainActivity"; |     private static final String TAG = "MainActivity"; | ||||||
| 
 | 
 | ||||||
|     private static final List<Integer> SHOW_BOTTOM_VIEW_DESTINATIONS = Arrays.asList( |     private static final List<Integer> SHOW_BOTTOM_VIEW_DESTINATIONS = ImmutableList.of( | ||||||
|             R.id.directMessagesInboxFragment, |             R.id.directMessagesInboxFragment, | ||||||
|             R.id.feedFragment, |             R.id.feedFragment, | ||||||
|             R.id.profileFragment, |             R.id.profileFragment, | ||||||
|             R.id.discoverFragment, |             R.id.discoverFragment, | ||||||
|             R.id.morePreferencesFragment); |             R.id.morePreferencesFragment, | ||||||
|     private static final Map<Integer, Integer> NAV_TO_MENU_ID_MAP = new HashMap<>(); |             R.id.favoritesFragment); | ||||||
|     private static final String FIRST_FRAGMENT_GRAPH_INDEX_KEY = "firstFragmentGraphIndex"; |     private static final String FIRST_FRAGMENT_GRAPH_INDEX_KEY = "firstFragmentGraphIndex"; | ||||||
| 
 | 
 | ||||||
|     private ActivityMainBinding binding; |     private ActivityMainBinding binding; | ||||||
| @ -127,14 +127,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 |     @Override | ||||||
|     protected void onCreate(@Nullable final Bundle savedInstanceState) { |     protected void onCreate(@Nullable final Bundle savedInstanceState) { | ||||||
|         super.onCreate(savedInstanceState); |         super.onCreate(savedInstanceState); | ||||||
| @ -255,11 +247,9 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage | |||||||
|             final NavController navController = currentNavControllerLiveData.getValue(); |             final NavController navController = currentNavControllerLiveData.getValue(); | ||||||
|             if (navController != null) { |             if (navController != null) { | ||||||
|                 @SuppressLint("RestrictedApi") final Deque<NavBackStackEntry> backStack = navController.getBackStack(); |                 @SuppressLint("RestrictedApi") final Deque<NavBackStackEntry> backStack = navController.getBackStack(); | ||||||
|                 if (backStack != null) { |  | ||||||
|                 currentNavControllerBackStack = backStack.size(); |                 currentNavControllerBackStack = backStack.size(); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         } |  | ||||||
|         if (isTaskRoot() && isBackStackEmpty && currentNavControllerBackStack == 2) { |         if (isTaskRoot() && isBackStackEmpty && currentNavControllerBackStack == 2) { | ||||||
|             finishAfterTransition(); |             finishAfterTransition(); | ||||||
|             return; |             return; | ||||||
| @ -431,36 +421,14 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage | |||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void setupBottomNavigationBar(final boolean setDefaultFromSettings) { |     private void setupBottomNavigationBar(final boolean setDefaultTabFromSettings) { | ||||||
|         int main_nav_ids = R.array.main_nav_ids; |         final List<Tab> tabs = !isLoggedIn ? setupAnonBottomNav() : setupMainBottomNav(); | ||||||
|         if (!isLoggedIn) { | 
 | ||||||
|             main_nav_ids = R.array.logged_out_main_nav_ids; |         final List<Integer> mainNavList = tabs.stream() | ||||||
|             final int selectedItemId = binding.bottomNavView.getSelectedItemId(); |                                               .map(Tab::getNavigationResId) | ||||||
|             binding.bottomNavView.getMenu().clear(); |                                               .collect(Collectors.toList()); | ||||||
|             binding.bottomNavView.inflateMenu(R.menu.logged_out_bottom_navigation_menu); |         if (setDefaultTabFromSettings) { | ||||||
|             if (selectedItemId == R.id.profile_nav_graph |             setSelectedTab(tabs); | ||||||
|                     || 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.feed_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); |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|         final LiveData<NavController> navControllerLiveData = setupWithNavController( |         final LiveData<NavController> navControllerLiveData = setupWithNavController( | ||||||
|                 binding.bottomNavView, |                 binding.bottomNavView, | ||||||
| @ -483,27 +451,84 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage | |||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void setBottomNavSelectedItem(final int navId) { |     private void setSelectedTab(final List<Tab> tabs) { | ||||||
|         final Integer menuId = NAV_TO_MENU_ID_MAP.get(navId); |         final String defaultTabResNameString = settingsHelper.getString(Constants.DEFAULT_TAB); | ||||||
|         if (menuId != null) { |         try { | ||||||
|             binding.bottomNavView.setSelectedItemId(menuId); |             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) firstFragmentGraphIndex = index; | ||||||
|  |             if (index < 0 || index >= tabs.size()) index = 0; | ||||||
|  |             setBottomNavSelectedTab(tabs.get(index)); | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             Log.e(TAG, "Error parsing id", e); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @NonNull |     private List<Tab> setupAnonBottomNav() { | ||||||
|     private List<Integer> getMainNavList(final int main_nav_ids) { |         final int selectedItemId = binding.bottomNavView.getSelectedItemId(); | ||||||
|         final TypedArray navIds = getResources().obtainTypedArray(main_nav_ids); |         final Tab profileTab = new Tab(R.drawable.ic_person_24, | ||||||
|         final List<Integer> mainNavList = new ArrayList<>(navIds.length()); |                                        getString(R.string.profile), | ||||||
|         final int length = navIds.length(); |                                        false, | ||||||
|         for (int i = 0; i < length; i++) { |                                        "profile_nav_graph", | ||||||
|             final int resourceId = navIds.getResourceId(i, -1); |                                        R.navigation.profile_nav_graph, | ||||||
|             if (resourceId < 0) continue; |                                        R.id.profile_nav_graph); | ||||||
|             mainNavList.add(resourceId); |         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); | ||||||
|  |         final Menu menu = binding.bottomNavView.getMenu(); | ||||||
|  |         menu.clear(); | ||||||
|  |         // binding.bottomNavView.inflateMenu(R.menu.logged_out_bottom_navigation_menu); | ||||||
|  |         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 ImmutableList.of(profileTab, moreTab); | ||||||
|         return mainNavList; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     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) { |     private void setupNavigation(final Toolbar toolbar, final NavController navController) { | ||||||
|         if (navController == null) return; |         if (navController == null) return; | ||||||
|         NavigationUI.setupWithNavController(toolbar, navController); |         NavigationUI.setupWithNavController(toolbar, navController); | ||||||
| @ -631,7 +656,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage | |||||||
|         }); |         }); | ||||||
|         final int selectedItemId = binding.bottomNavView.getSelectedItemId(); |         final int selectedItemId = binding.bottomNavView.getSelectedItemId(); | ||||||
|         if (selectedItemId != R.navigation.direct_messages_nav_graph) { |         if (selectedItemId != R.navigation.direct_messages_nav_graph) { | ||||||
|             setBottomNavSelectedItem(R.navigation.direct_messages_nav_graph); |             setBottomNavSelectedTab(R.id.direct_messages_nav_graph); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										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); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,267 @@ | |||||||
|  | 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(), 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(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -2,6 +2,7 @@ package awais.instagrabber.fragments.settings; | |||||||
| 
 | 
 | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.content.res.TypedArray; | import android.content.res.TypedArray; | ||||||
|  | import android.util.Log; | ||||||
| 
 | 
 | ||||||
| import androidx.annotation.NonNull; | import androidx.annotation.NonNull; | ||||||
| import androidx.preference.ListPreference; | import androidx.preference.ListPreference; | ||||||
| @ -10,13 +11,14 @@ import androidx.preference.PreferenceScreen; | |||||||
| import androidx.preference.SwitchPreferenceCompat; | import androidx.preference.SwitchPreferenceCompat; | ||||||
| 
 | 
 | ||||||
| import awais.instagrabber.R; | import awais.instagrabber.R; | ||||||
|  | import awais.instagrabber.dialogs.TabOrderPreferenceDialogFragment; | ||||||
| import awais.instagrabber.utils.Constants; | import awais.instagrabber.utils.Constants; | ||||||
| import awais.instagrabber.utils.CookieUtils; | import awais.instagrabber.utils.CookieUtils; | ||||||
| import awais.instagrabber.utils.TextUtils; | import awais.instagrabber.utils.TextUtils; | ||||||
| 
 | 
 | ||||||
| import static awais.instagrabber.utils.Utils.settingsHelper; | import static awais.instagrabber.utils.Utils.settingsHelper; | ||||||
| 
 | 
 | ||||||
| public class GeneralPreferencesFragment extends BasePreferencesFragment { | public class GeneralPreferencesFragment extends BasePreferencesFragment implements TabOrderPreferenceDialogFragment.Callback { | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     void setupPreferenceScreen(final PreferenceScreen screen) { |     void setupPreferenceScreen(final PreferenceScreen screen) { | ||||||
| @ -26,6 +28,7 @@ public class GeneralPreferencesFragment extends BasePreferencesFragment { | |||||||
|         final boolean isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0; |         final boolean isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0; | ||||||
|         if (isLoggedIn) { |         if (isLoggedIn) { | ||||||
|             screen.addPreference(getDefaultTabPreference(context)); |             screen.addPreference(getDefaultTabPreference(context)); | ||||||
|  |             screen.addPreference(getTabOrderPreference(context)); | ||||||
|         } |         } | ||||||
|         screen.addPreference(getUpdateCheckPreference(context)); |         screen.addPreference(getUpdateCheckPreference(context)); | ||||||
|         screen.addPreference(getFlagSecurePreference(context)); |         screen.addPreference(getFlagSecurePreference(context)); | ||||||
| @ -34,24 +37,37 @@ public class GeneralPreferencesFragment extends BasePreferencesFragment { | |||||||
|     private Preference getDefaultTabPreference(@NonNull final Context context) { |     private Preference getDefaultTabPreference(@NonNull final Context context) { | ||||||
|         final ListPreference preference = new ListPreference(context); |         final ListPreference preference = new ListPreference(context); | ||||||
|         preference.setSummaryProvider(ListPreference.SimpleSummaryProvider.getInstance()); |         preference.setSummaryProvider(ListPreference.SimpleSummaryProvider.getInstance()); | ||||||
|         final TypedArray mainNavIds = getResources().obtainTypedArray(R.array.main_nav_ids); |         final TypedArray mainNavGraphs = getResources().obtainTypedArray(R.array.main_nav_graphs); | ||||||
|         final int length = mainNavIds.length(); |         final int length = mainNavGraphs.length(); | ||||||
|         final String[] values = new String[length]; |         final String[] navGraphFileNames = new String[length]; | ||||||
|         for (int i = 0; i < length; i++) { |         for (int i = 0; i < length; i++) { | ||||||
|             final int resourceId = mainNavIds.getResourceId(i, -1); |             final int resourceId = mainNavGraphs.getResourceId(i, -1); | ||||||
|             if (resourceId < 0) continue; |             if (resourceId < 0) continue; | ||||||
|             values[i] = getResources().getResourceEntryName(resourceId); |             navGraphFileNames[i] = getResources().getResourceEntryName(resourceId); | ||||||
|         } |         } | ||||||
|         mainNavIds.recycle(); |         mainNavGraphs.recycle(); | ||||||
|         preference.setKey(Constants.DEFAULT_TAB); |         preference.setKey(Constants.DEFAULT_TAB); | ||||||
|         preference.setTitle(R.string.pref_start_screen); |         preference.setTitle(R.string.pref_start_screen); | ||||||
|         preference.setDialogTitle(R.string.pref_start_screen); |         preference.setDialogTitle(R.string.pref_start_screen); | ||||||
|         preference.setEntries(R.array.main_nav_ids_values); |         preference.setEntries(R.array.main_nav_titles); | ||||||
|         preference.setEntryValues(values); |         preference.setEntryValues(navGraphFileNames); | ||||||
|         preference.setIconSpaceReserved(false); |         preference.setIconSpaceReserved(false); | ||||||
|         return preference; |         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) { |     private Preference getUpdateCheckPreference(@NonNull final Context context) { | ||||||
|         final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context); |         final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context); | ||||||
|         preference.setKey(Constants.CHECK_UPDATES); |         preference.setKey(Constants.CHECK_UPDATES); | ||||||
| @ -72,4 +88,14 @@ public class GeneralPreferencesFragment extends BasePreferencesFragment { | |||||||
|                     return true; |                     return true; | ||||||
|                 }); |                 }); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public void onSave(final boolean orderHasChanged) { | ||||||
|  |         Log.d("", "onSave: " + orderHasChanged); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public void onCancel() { | ||||||
|  | 
 | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -5,4 +5,5 @@ public final class PreferenceKeys { | |||||||
|     public static final String PREF_ENABLE_DM_AUTO_REFRESH = "enable_dm_auto_refresh"; |     public static final String PREF_ENABLE_DM_AUTO_REFRESH = "enable_dm_auto_refresh"; | ||||||
|     public static final String PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT = "enable_dm_auto_refresh_freq_unit"; |     public static final String PREF_ENABLE_DM_AUTO_REFRESH_FREQ_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_DM_AUTO_REFRESH_FREQ_NUMBER = "enable_dm_auto_refresh_freq_number"; | ||||||
|  |     public static final String PREF_TAB_ORDER = "tab_order"; | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										98
									
								
								app/src/main/java/awais/instagrabber/models/Tab.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								app/src/main/java/awais/instagrabber/models/Tab.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,98 @@ | |||||||
|  | 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; | ||||||
|  | 
 | ||||||
|  |     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) { | ||||||
|  |         this.iconResId = iconResId; | ||||||
|  |         this.title = title; | ||||||
|  |         this.removable = removable; | ||||||
|  |         this.graphName = graphName; | ||||||
|  |         this.navigationResId = navigationResId; | ||||||
|  |         this.navigationRootId = navigationRootId; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     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; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @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 && | ||||||
|  |                 Objects.equals(title, tab.title) && | ||||||
|  |                 Objects.equals(graphName, tab.graphName); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public int hashCode() { | ||||||
|  |         return Objects.hash(iconResId, title, removable, graphName, navigationResId, navigationRootId); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @NonNull | ||||||
|  |     @Override | ||||||
|  |     public String toString() { | ||||||
|  |         return "Tab{" + | ||||||
|  |                 "title='" + title + '\'' + | ||||||
|  |                 ", removable=" + removable + | ||||||
|  |                 ", graphName='" + graphName + '\'' + | ||||||
|  |                 '}'; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -15,6 +15,7 @@ import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_D | |||||||
| import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER; | import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER; | ||||||
| import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT; | import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT; | ||||||
| import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_NOTIFICATIONS; | import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_NOTIFICATIONS; | ||||||
|  | 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_LANGUAGE; | ||||||
| import static awais.instagrabber.utils.Constants.APP_THEME; | import static awais.instagrabber.utils.Constants.APP_THEME; | ||||||
| import static awais.instagrabber.utils.Constants.APP_UA; | import static awais.instagrabber.utils.Constants.APP_UA; | ||||||
| @ -36,12 +37,12 @@ import static awais.instagrabber.utils.Constants.DOWNLOAD_USER_FOLDER; | |||||||
| import static awais.instagrabber.utils.Constants.FLAG_SECURE; | import static awais.instagrabber.utils.Constants.FLAG_SECURE; | ||||||
| import static awais.instagrabber.utils.Constants.FOLDER_PATH; | import static awais.instagrabber.utils.Constants.FOLDER_PATH; | ||||||
| import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO; | import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO; | ||||||
|  | import static awais.instagrabber.utils.Constants.KEYWORD_FILTERS; | ||||||
| import static awais.instagrabber.utils.Constants.MARK_AS_SEEN; | import static awais.instagrabber.utils.Constants.MARK_AS_SEEN; | ||||||
| import static awais.instagrabber.utils.Constants.MUTED_VIDEOS; | import static awais.instagrabber.utils.Constants.MUTED_VIDEOS; | ||||||
| import static awais.instagrabber.utils.Constants.PREF_DARK_THEME; | 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_EMOJI_VARIANTS; | ||||||
| import static awais.instagrabber.utils.Constants.PREF_HASHTAG_POSTS_LAYOUT; | 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_LIGHT_THEME; | ||||||
| import static awais.instagrabber.utils.Constants.PREF_LIKED_POSTS_LAYOUT; | import static awais.instagrabber.utils.Constants.PREF_LIKED_POSTS_LAYOUT; | ||||||
| import static awais.instagrabber.utils.Constants.PREF_LOCATION_POSTS_LAYOUT; | import static awais.instagrabber.utils.Constants.PREF_LOCATION_POSTS_LAYOUT; | ||||||
| @ -150,7 +151,7 @@ public final class SettingsHelper { | |||||||
|                     CUSTOM_DATE_TIME_FORMAT, DEVICE_UUID, SKIPPED_VERSION, DEFAULT_TAB, PREF_DARK_THEME, PREF_LIGHT_THEME, |                     CUSTOM_DATE_TIME_FORMAT, DEVICE_UUID, SKIPPED_VERSION, DEFAULT_TAB, PREF_DARK_THEME, PREF_LIGHT_THEME, | ||||||
|                     PREF_POSTS_LAYOUT, PREF_PROFILE_POSTS_LAYOUT, PREF_TOPIC_POSTS_LAYOUT, PREF_HASHTAG_POSTS_LAYOUT, |                     PREF_POSTS_LAYOUT, PREF_PROFILE_POSTS_LAYOUT, PREF_TOPIC_POSTS_LAYOUT, PREF_HASHTAG_POSTS_LAYOUT, | ||||||
|                     PREF_LOCATION_POSTS_LAYOUT, PREF_LIKED_POSTS_LAYOUT, PREF_TAGGED_POSTS_LAYOUT, PREF_SAVED_POSTS_LAYOUT, |                     PREF_LOCATION_POSTS_LAYOUT, PREF_LIKED_POSTS_LAYOUT, PREF_TAGGED_POSTS_LAYOUT, PREF_SAVED_POSTS_LAYOUT, | ||||||
|                     STORY_SORT, PREF_EMOJI_VARIANTS, PREF_REACTIONS, 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 {} |     public @interface StringSettings {} | ||||||
| 
 | 
 | ||||||
|     @StringDef({DOWNLOAD_USER_FOLDER, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS, |     @StringDef({DOWNLOAD_USER_FOLDER, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS, | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ import android.content.ContentResolver; | |||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.content.Intent; | import android.content.Intent; | ||||||
| import android.content.res.Resources; | import android.content.res.Resources; | ||||||
|  | import android.content.res.TypedArray; | ||||||
| import android.graphics.Color; | import android.graphics.Color; | ||||||
| import android.graphics.Rect; | import android.graphics.Rect; | ||||||
| import android.graphics.drawable.Drawable; | import android.graphics.drawable.Drawable; | ||||||
| @ -40,6 +41,8 @@ import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat; | |||||||
| import com.google.android.exoplayer2.database.ExoDatabaseProvider; | import com.google.android.exoplayer2.database.ExoDatabaseProvider; | ||||||
| import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor; | import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor; | ||||||
| import com.google.android.exoplayer2.upstream.cache.SimpleCache; | 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 com.google.common.io.Files; | ||||||
| 
 | 
 | ||||||
| import org.json.JSONObject; | import org.json.JSONObject; | ||||||
| @ -47,22 +50,25 @@ import org.json.JSONObject; | |||||||
| import java.io.File; | import java.io.File; | ||||||
| import java.lang.reflect.Field; | import java.lang.reflect.Field; | ||||||
| import java.text.SimpleDateFormat; | import java.text.SimpleDateFormat; | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.Arrays; | ||||||
|  | import java.util.Collections; | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
|  | import java.util.List; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| 
 | import java.util.stream.Collectors; | ||||||
| //import javax.crypto.Mac; |  | ||||||
| //import javax.crypto.spec.SecretKeySpec; |  | ||||||
| 
 | 
 | ||||||
| import awais.instagrabber.R; | import awais.instagrabber.R; | ||||||
|  | import awais.instagrabber.fragments.settings.PreferenceKeys; | ||||||
| import awais.instagrabber.models.PostsLayoutPreferences; | import awais.instagrabber.models.PostsLayoutPreferences; | ||||||
|  | import awais.instagrabber.models.Tab; | ||||||
| import awais.instagrabber.models.enums.FavoriteType; | import awais.instagrabber.models.enums.FavoriteType; | ||||||
| //import awaisomereport.LogCollector; |  | ||||||
| 
 | 
 | ||||||
| public final class Utils { | public final class Utils { | ||||||
|     private static final String TAG = "Utils"; |     private static final String TAG = "Utils"; | ||||||
|     private static final int VIDEO_CACHE_MAX_BYTES = 10 * 1024 * 1024; |     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 SettingsHelper settingsHelper; | ||||||
|     public static boolean sessionVolumeFull = false; |     public static boolean sessionVolumeFull = false; | ||||||
|     public static final MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton(); |     public static final MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton(); | ||||||
| @ -93,34 +99,34 @@ public final class Utils { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static Map<String, String> sign(final Map<String, Object> form) { |     public static Map<String, String> sign(final Map<String, Object> form) { | ||||||
| //        final String signed = sign(Constants.SIGNATURE_KEY, new JSONObject(form).toString()); |         // final String signed = sign(Constants.SIGNATURE_KEY, new JSONObject(form).toString()); | ||||||
| //        if (signed == null) { |         // if (signed == null) { | ||||||
| //            return null; |         //     return null; | ||||||
| //        } |         // } | ||||||
|         final Map<String, String> map = new HashMap<>(); |         final Map<String, String> map = new HashMap<>(); | ||||||
| //        map.put("ig_sig_key_version", Constants.SIGNATURE_VERSION); |         // map.put("ig_sig_key_version", Constants.SIGNATURE_VERSION); | ||||||
| //        map.put("signed_body", signed); |         // map.put("signed_body", signed); | ||||||
|         map.put("signed_body", "SIGNATURE." + new JSONObject(form).toString()); |         map.put("signed_body", "SIGNATURE." + new JSONObject(form).toString()); | ||||||
|         return map; |         return map; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| //    public static String sign(final String key, final String message) { |     // public static String sign(final String key, final String message) { | ||||||
| //        try { |     //     try { | ||||||
| //            final Mac hasher = Mac.getInstance("HmacSHA256"); |     //         final Mac hasher = Mac.getInstance("HmacSHA256"); | ||||||
| //            hasher.init(new SecretKeySpec(key.getBytes(), "HmacSHA256")); |     //         hasher.init(new SecretKeySpec(key.getBytes(), "HmacSHA256")); | ||||||
| //            byte[] hash = hasher.doFinal(message.getBytes()); |     //         byte[] hash = hasher.doFinal(message.getBytes()); | ||||||
| //            final StringBuilder hexString = new StringBuilder(); |     //         final StringBuilder hexString = new StringBuilder(); | ||||||
| //            for (byte b : hash) { |     //         for (byte b : hash) { | ||||||
| //                final String hex = Integer.toHexString(0xff & b); |     //             final String hex = Integer.toHexString(0xff & b); | ||||||
| //                if (hex.length() == 1) hexString.append('0'); |     //             if (hex.length() == 1) hexString.append('0'); | ||||||
| //                hexString.append(hex); |     //             hexString.append(hex); | ||||||
| //            } |     //         } | ||||||
| //            return hexString.toString() + "." + message; |     //         return hexString.toString() + "." + message; | ||||||
| //        } catch (Exception e) { |     //     } catch (Exception e) { | ||||||
| //            Log.e(TAG, "Error signing", e); |     //         Log.e(TAG, "Error signing", e); | ||||||
| //            return null; |     //         return null; | ||||||
| //        } |     //     } | ||||||
| //    } |     // } | ||||||
| 
 | 
 | ||||||
|     public static String getMimeType(@NonNull final Uri uri, final ContentResolver contentResolver) { |     public static String getMimeType(@NonNull final Uri uri, final ContentResolver contentResolver) { | ||||||
|         String mimeType; |         String mimeType; | ||||||
| @ -371,4 +377,101 @@ public final class Utils { | |||||||
|         if (window == null) return; |         if (window == null) return; | ||||||
|         window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); |         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(); | ||||||
|  | 
 | ||||||
|  |         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); | ||||||
|  |             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) { | ||||||
|  |         final String 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; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										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> | ||||||
| @ -48,6 +48,5 @@ | |||||||
|         android:layout_height="wrap_content" |         android:layout_height="wrap_content" | ||||||
|         android:layout_gravity="bottom" |         android:layout_gravity="bottom" | ||||||
|         app:labelVisibilityMode="auto" |         app:labelVisibilityMode="auto" | ||||||
|         app:layout_behavior="@string/hide_bottom_view_on_scroll_behavior" |         app:layout_behavior="@string/hide_bottom_view_on_scroll_behavior" /> | ||||||
|         app:menu="@menu/main_bottom_navigation_menu" /> |  | ||||||
| </androidx.coordinatorlayout.widget.CoordinatorLayout> | </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> | ||||||
							
								
								
									
										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> | ||||||
| @ -88,24 +88,46 @@ | |||||||
|         <item>HH:mm:ss</item> |         <item>HH:mm:ss</item> | ||||||
|         <item>H:mm:ss</item> |         <item>H:mm:ss</item> | ||||||
|     </string-array> |     </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> | ||||||
|  |     </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/direct_messages_nav_graph</item> | ||||||
|         <item>@navigation/feed_nav_graph</item> |         <item>@navigation/feed_nav_graph</item> | ||||||
|         <item>@navigation/profile_nav_graph</item> |         <item>@navigation/profile_nav_graph</item> | ||||||
|         <item>@navigation/discover_nav_graph</item> |         <item>@navigation/discover_nav_graph</item> | ||||||
|         <item>@navigation/more_nav_graph</item> |         <item>@navigation/more_nav_graph</item> | ||||||
|  |         <item>@navigation/favorites_nav_graph</item> | ||||||
|     </array> |     </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/title_dm</item> | ||||||
|         <item>@string/feed</item> |         <item>@string/feed</item> | ||||||
|         <item>@string/profile</item> |         <item>@string/profile</item> | ||||||
|         <item>@string/title_discover</item> |         <item>@string/title_discover</item> | ||||||
|         <item>@string/more</item> |         <item>@string/more</item> | ||||||
|  |         <item>@string/title_favorites</item> | ||||||
|     </string-array> |     </string-array> | ||||||
|     <array name="logged_out_main_nav_ids"> |     <!-- Drawable should correspond 1-to-1 with the above titles --> | ||||||
|         <item>@navigation/profile_nav_graph</item> |     <array name="main_nav_drawables" translatable="false"> | ||||||
|         <item>@navigation/more_nav_graph</item> |         <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> | ||||||
|     </array> |     </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"> |     <string-array name="light_themes" translatable="false"> | ||||||
|         <item>@string/light_white_theme</item> |         <item>@string/light_white_theme</item> | ||||||
|         <item>@string/light_barinsta_theme</item> |         <item>@string/light_barinsta_theme</item> | ||||||
|  | |||||||
| @ -473,4 +473,6 @@ | |||||||
|     <string name="removed_keywords">Removed keyword: %s from filter list</string> |     <string name="removed_keywords">Removed keyword: %s from filter list</string> | ||||||
|     <string name="marked_as_seen">Marked as seen</string> |     <string name="marked_as_seen">Marked as seen</string> | ||||||
|     <string name="delete_unsuccessful">Delete unsuccessful</string> |     <string name="delete_unsuccessful">Delete unsuccessful</string> | ||||||
|  |     <string name="tab_order">Screen order</string> | ||||||
|  |     <string name="other_tabs">Other tabs</string> | ||||||
| </resources> | </resources> | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user