mirror of
				https://github.com/KokaKiwi/BarInsta
				synced 2025-10-31 03:25: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.Intent; | ||||
| import android.content.ServiceConnection; | ||||
| import android.content.res.TypedArray; | ||||
| import android.database.MatrixCursor; | ||||
| import android.net.Uri; | ||||
| import android.os.AsyncTask; | ||||
| @ -25,6 +24,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,13 +50,12 @@ import com.google.android.material.appbar.AppBarLayout; | ||||
| import com.google.android.material.appbar.CollapsingToolbarLayout; | ||||
| 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.Deque; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| import awais.instagrabber.R; | ||||
| import awais.instagrabber.adapters.SuggestionsAdapter; | ||||
| @ -71,6 +70,7 @@ import awais.instagrabber.fragments.settings.PreferenceKeys; | ||||
| import awais.instagrabber.interfaces.FetchListener; | ||||
| import awais.instagrabber.models.IntentModel; | ||||
| import awais.instagrabber.models.SuggestionModel; | ||||
| import awais.instagrabber.models.Tab; | ||||
| import awais.instagrabber.models.enums.SuggestionType; | ||||
| import awais.instagrabber.services.ActivityCheckerService; | ||||
| import awais.instagrabber.services.DMSyncAlarmReceiver; | ||||
| @ -90,13 +90,13 @@ 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( | ||||
|     private static final List<Integer> SHOW_BOTTOM_VIEW_DESTINATIONS = ImmutableList.of( | ||||
|             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<>(); | ||||
|             R.id.morePreferencesFragment, | ||||
|             R.id.favoritesFragment); | ||||
|     private static final String FIRST_FRAGMENT_GRAPH_INDEX_KEY = "firstFragmentGraphIndex"; | ||||
| 
 | ||||
|     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 | ||||
|     protected void onCreate(@Nullable final Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
| @ -255,9 +247,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) { | ||||
| @ -431,36 +421,14 @@ 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.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); | ||||
|             } | ||||
|     private void setupBottomNavigationBar(final boolean setDefaultTabFromSettings) { | ||||
|         final List<Tab> tabs = !isLoggedIn ? setupAnonBottomNav() : setupMainBottomNav(); | ||||
| 
 | ||||
|         final List<Integer> mainNavList = tabs.stream() | ||||
|                                               .map(Tab::getNavigationResId) | ||||
|                                               .collect(Collectors.toList()); | ||||
|         if (setDefaultTabFromSettings) { | ||||
|             setSelectedTab(tabs); | ||||
|         } | ||||
|         final LiveData<NavController> navControllerLiveData = setupWithNavController( | ||||
|                 binding.bottomNavView, | ||||
| @ -483,27 +451,84 @@ 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) 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<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); | ||||
|         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 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); | ||||
| @ -631,7 +656,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); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										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.res.TypedArray; | ||||
| import android.util.Log; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.preference.ListPreference; | ||||
| @ -10,13 +11,14 @@ import androidx.preference.PreferenceScreen; | ||||
| import androidx.preference.SwitchPreferenceCompat; | ||||
| 
 | ||||
| import awais.instagrabber.R; | ||||
| import awais.instagrabber.dialogs.TabOrderPreferenceDialogFragment; | ||||
| import awais.instagrabber.utils.Constants; | ||||
| import awais.instagrabber.utils.CookieUtils; | ||||
| import awais.instagrabber.utils.TextUtils; | ||||
| 
 | ||||
| import static awais.instagrabber.utils.Utils.settingsHelper; | ||||
| 
 | ||||
| public class GeneralPreferencesFragment extends BasePreferencesFragment { | ||||
| public class GeneralPreferencesFragment extends BasePreferencesFragment implements TabOrderPreferenceDialogFragment.Callback { | ||||
| 
 | ||||
|     @Override | ||||
|     void setupPreferenceScreen(final PreferenceScreen screen) { | ||||
| @ -26,6 +28,7 @@ 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)); | ||||
| @ -34,24 +37,37 @@ 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]; | ||||
|         final TypedArray mainNavGraphs = getResources().obtainTypedArray(R.array.main_nav_graphs); | ||||
|         final int length = mainNavGraphs.length(); | ||||
|         final String[] navGraphFileNames = new String[length]; | ||||
|         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; | ||||
|             values[i] = getResources().getResourceEntryName(resourceId); | ||||
|             navGraphFileNames[i] = getResources().getResourceEntryName(resourceId); | ||||
|         } | ||||
|         mainNavIds.recycle(); | ||||
|         mainNavGraphs.recycle(); | ||||
|         preference.setKey(Constants.DEFAULT_TAB); | ||||
|         preference.setTitle(R.string.pref_start_screen); | ||||
|         preference.setDialogTitle(R.string.pref_start_screen); | ||||
|         preference.setEntries(R.array.main_nav_ids_values); | ||||
|         preference.setEntryValues(values); | ||||
|         preference.setEntries(R.array.main_nav_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); | ||||
| @ -72,4 +88,14 @@ public class GeneralPreferencesFragment extends BasePreferencesFragment { | ||||
|                     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_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_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_UNIT; | ||||
| 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_THEME; | ||||
| 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.FOLDER_PATH; | ||||
| 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.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; | ||||
| @ -150,7 +151,7 @@ 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, 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.Intent; | ||||
| import android.content.res.Resources; | ||||
| import android.content.res.TypedArray; | ||||
| import android.graphics.Color; | ||||
| import android.graphics.Rect; | ||||
| 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.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 +50,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(); | ||||
| @ -93,34 +99,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 +377,101 @@ 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(); | ||||
| 
 | ||||
|         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_gravity="bottom" | ||||
|         app:labelVisibilityMode="auto" | ||||
|         app:layout_behavior="@string/hide_bottom_view_on_scroll_behavior" | ||||
|         app:menu="@menu/main_bottom_navigation_menu" /> | ||||
|         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> | ||||
							
								
								
									
										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>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> | ||||
|     </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> | ||||
|     </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> | ||||
|     </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> | ||||
|     </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> | ||||
|  | ||||
| @ -473,4 +473,6 @@ | ||||
|     <string name="removed_keywords">Removed keyword: %s from filter list</string> | ||||
|     <string name="marked_as_seen">Marked as seen</string> | ||||
|     <string name="delete_unsuccessful">Delete unsuccessful</string> | ||||
|     <string name="tab_order">Screen order</string> | ||||
|     <string name="other_tabs">Other tabs</string> | ||||
| </resources> | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user