1
0
mirror of https://github.com/KokaKiwi/BarInsta synced 2024-11-26 08:37:29 +00:00

Merge pull request #850 from ammargitham/bottombar_redesign

Bottombar redesign
This commit is contained in:
Austin Huang 2021-04-02 12:01:41 -04:00 committed by GitHub
commit 5c46edc949
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 1247 additions and 257 deletions

View File

@ -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.Build; import android.os.Build;
@ -24,6 +23,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,13 @@ import com.google.android.material.appbar.CollapsingToolbarLayout;
import com.google.android.material.badge.BadgeDrawable; import com.google.android.material.badge.BadgeDrawable;
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.ArrayList;
import java.util.Arrays; import java.util.Collections;
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 java.util.stream.Collectors;
import awais.instagrabber.R; import awais.instagrabber.R;
@ -69,6 +69,7 @@ import awais.instagrabber.fragments.directmessages.DirectMessageInboxFragmentDir
import awais.instagrabber.fragments.main.FeedFragment; import awais.instagrabber.fragments.main.FeedFragment;
import awais.instagrabber.fragments.settings.PreferenceKeys; import awais.instagrabber.fragments.settings.PreferenceKeys;
import awais.instagrabber.models.IntentModel; import awais.instagrabber.models.IntentModel;
import awais.instagrabber.models.Tab;
import awais.instagrabber.models.enums.SuggestionType; import awais.instagrabber.models.enums.SuggestionType;
import awais.instagrabber.repositories.responses.search.SearchItem; import awais.instagrabber.repositories.responses.search.SearchItem;
import awais.instagrabber.repositories.responses.search.SearchResponse; import awais.instagrabber.repositories.responses.search.SearchResponse;
@ -94,15 +95,8 @@ 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(
R.id.directMessagesInboxFragment,
R.id.feedFragment,
R.id.profileFragment,
R.id.discoverFragment,
R.id.morePreferencesFragment);
private static final Map<Integer, Integer> NAV_TO_MENU_ID_MAP = new HashMap<>();
private static final String FIRST_FRAGMENT_GRAPH_INDEX_KEY = "firstFragmentGraphIndex"; private static final String FIRST_FRAGMENT_GRAPH_INDEX_KEY = "firstFragmentGraphIndex";
private static final String LAST_SELECT_NAV_MENU_ID = "lastSelectedNavMenuId";
private ActivityMainBinding binding; private ActivityMainBinding binding;
private LiveData<NavController> currentNavControllerLiveData; private LiveData<NavController> currentNavControllerLiveData;
@ -114,10 +108,13 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
private boolean showSearch = true; private boolean showSearch = true;
private Handler suggestionsFetchHandler; private Handler suggestionsFetchHandler;
private int firstFragmentGraphIndex; private int firstFragmentGraphIndex;
private int lastSelectedNavMenuId;
private boolean isActivityCheckerServiceBound = false; private boolean isActivityCheckerServiceBound = false;
private boolean isBackStackEmpty = false; private boolean isBackStackEmpty = false;
private boolean isLoggedIn; private boolean isLoggedIn;
private HideBottomViewOnScrollBehavior<BottomNavigationView> behavior; private HideBottomViewOnScrollBehavior<BottomNavigationView> behavior;
private List<Tab> currentTabs;
private List<Integer> showBottomViewDestinations = Collections.emptyList();
private final ServiceConnection serviceConnection = new ServiceConnection() { private final ServiceConnection serviceConnection = new ServiceConnection() {
@Override @Override
@ -133,14 +130,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);
@ -227,6 +216,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
@Override @Override
protected void onSaveInstanceState(@NonNull final Bundle outState) { protected void onSaveInstanceState(@NonNull final Bundle outState) {
outState.putString(FIRST_FRAGMENT_GRAPH_INDEX_KEY, String.valueOf(firstFragmentGraphIndex)); outState.putString(FIRST_FRAGMENT_GRAPH_INDEX_KEY, String.valueOf(firstFragmentGraphIndex));
outState.putString(LAST_SELECT_NAV_MENU_ID, String.valueOf(binding.bottomNavView.getSelectedItemId()));
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
} }
@ -239,6 +229,12 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
firstFragmentGraphIndex = Integer.parseInt(key); firstFragmentGraphIndex = Integer.parseInt(key);
} catch (NumberFormatException ignored) { } } catch (NumberFormatException ignored) { }
} }
final String lastSelected = (String) savedInstanceState.get(LAST_SELECT_NAV_MENU_ID);
if (lastSelected != null) {
try {
lastSelectedNavMenuId = Integer.parseInt(lastSelected);
} catch (NumberFormatException ignored) { }
}
setupBottomNavigationBar(false); setupBottomNavigationBar(false);
} }
@ -273,11 +269,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;
@ -328,7 +322,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
final Bundle bundle = new Bundle(); final Bundle bundle = new Bundle();
switch (type) { switch (type) {
case TYPE_LOCATION: case TYPE_LOCATION:
bundle.putLong("locationId", Long.valueOf(query)); bundle.putLong("locationId", Long.parseLong(query));
navController.navigate(R.id.action_global_locationFragment, bundle); navController.navigate(R.id.action_global_locationFragment, bundle);
break; break;
case TYPE_HASHTAG: case TYPE_HASHTAG:
@ -378,14 +372,16 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
cursor = null; cursor = null;
return; return;
} }
final List<SearchItem> result = new ArrayList<SearchItem>(); final List<SearchItem> result = new ArrayList<>();
if (isLoggedIn) { if (isLoggedIn) {
if (body.getList() != null) result.addAll(searchHash ? body.getList() if (body.getList() != null) {
result.addAll(searchHash ? body.getList()
.stream() .stream()
.filter(i -> i.getUser() == null) .filter(i -> i.getUser() == null)
.collect(Collectors.toList()) : body.getList()); .collect(Collectors.toList())
: body.getList());
} }
else { } else {
if (body.getUsers() != null && !searchHash) result.addAll(body.getUsers()); if (body.getUsers() != null && !searchHash) result.addAll(body.getUsers());
if (body.getHashtags() != null) result.addAll(body.getHashtags()); if (body.getHashtags() != null) result.addAll(body.getHashtags());
if (body.getPlaces() != null) result.addAll(body.getPlaces()); if (body.getPlaces() != null) result.addAll(body.getPlaces());
@ -430,10 +426,11 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
@Override @Override
public void onFailure(@NonNull final Call<SearchResponse> call, public void onFailure(@NonNull final Call<SearchResponse> call,
Throwable t) { @NonNull Throwable t) {
if (!call.isCanceled() && t != null) if (!call.isCanceled()) {
Log.e(TAG, "Exception on search:", t); Log.e(TAG, "Exception on search:", t);
} }
}
}; };
private final Runnable runnable = () -> { private final Runnable runnable = () -> {
@ -484,36 +481,18 @@ 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; currentTabs = !isLoggedIn ? setupAnonBottomNav() : setupMainBottomNav();
if (!isLoggedIn) { final List<Integer> mainNavList = currentTabs.stream()
main_nav_ids = R.array.logged_out_main_nav_ids; .map(Tab::getNavigationResId)
final int selectedItemId = binding.bottomNavView.getSelectedItemId(); .collect(Collectors.toList());
binding.bottomNavView.getMenu().clear(); showBottomViewDestinations = currentTabs.stream()
binding.bottomNavView.inflateMenu(R.menu.logged_out_bottom_navigation_menu); .map(Tab::getStartDestinationFragmentId)
if (selectedItemId == R.id.profile_nav_graph .collect(Collectors.toList());
|| selectedItemId == R.id.more_nav_graph) { if (setDefaultTabFromSettings) {
binding.bottomNavView.setSelectedItemId(selectedItemId); setSelectedTab(currentTabs);
} else { } else {
setBottomNavSelectedItem(R.navigation.profile_nav_graph); binding.bottomNavView.setSelectedItemId(lastSelectedNavMenuId);
}
}
final List<Integer> mainNavList = getMainNavList(main_nav_ids);
if (setDefaultFromSettings) {
final String defaultTabResNameString = settingsHelper.getString(Constants.DEFAULT_TAB);
try {
int navId = 0;
if (!TextUtils.isEmpty(defaultTabResNameString)) {
navId = getResources().getIdentifier(defaultTabResNameString, "navigation", getPackageName());
}
final int defaultNavId = navId <= 0 ? R.navigation.profile_nav_graph
: navId;
final int index = mainNavList.indexOf(defaultNavId);
if (index >= 0) firstFragmentGraphIndex = index;
setBottomNavSelectedItem(defaultNavId);
} catch (NumberFormatException e) {
Log.e(TAG, "Error parsing id", e);
}
} }
final LiveData<NavController> navControllerLiveData = setupWithNavController( final LiveData<NavController> navControllerLiveData = setupWithNavController(
binding.bottomNavView, binding.bottomNavView,
@ -536,27 +515,86 @@ 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 || index >= tabs.size()) index = 0;
firstFragmentGraphIndex = index;
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); R.id.profileFragment);
final Tab moreTab = new Tab(R.drawable.ic_more_horiz_24,
getString(R.string.more),
false,
"more_nav_graph",
R.navigation.more_nav_graph,
R.id.more_nav_graph,
R.id.morePreferencesFragment);
final Menu menu = binding.bottomNavView.getMenu();
menu.clear();
menu.add(0, profileTab.getNavigationRootId(), 0, profileTab.getTitle()).setIcon(profileTab.getIconResId());
menu.add(0, moreTab.getNavigationRootId(), 0, moreTab.getTitle()).setIcon(moreTab.getIconResId());
if (selectedItemId != R.id.profile_nav_graph && selectedItemId != R.id.more_nav_graph) {
setBottomNavSelectedTab(profileTab);
} }
navIds.recycle(); return 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);
@ -574,7 +612,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
final int destinationId = destination.getId(); final int destinationId = destination.getId();
@SuppressLint("RestrictedApi") final Deque<NavBackStackEntry> backStack = navController.getBackStack(); @SuppressLint("RestrictedApi") final Deque<NavBackStackEntry> backStack = navController.getBackStack();
setupMenu(backStack.size(), destinationId); setupMenu(backStack.size(), destinationId);
final boolean contains = SHOW_BOTTOM_VIEW_DESTINATIONS.contains(destinationId); final boolean contains = showBottomViewDestinations.contains(destinationId);
binding.bottomNavView.setVisibility(contains ? View.VISIBLE : View.GONE); binding.bottomNavView.setVisibility(contains ? View.VISIBLE : View.GONE);
if (contains && behavior != null) { if (contains && behavior != null) {
behavior.slideUp(binding.bottomNavView); behavior.slideUp(binding.bottomNavView);
@ -684,7 +722,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);
} }
} }
@ -756,7 +794,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
final NavController navController = currentNavControllerLiveData.getValue(); final NavController navController = currentNavControllerLiveData.getValue();
if (navController == null) return; if (navController == null) return;
final Bundle bundle = new Bundle(); final Bundle bundle = new Bundle();
bundle.putLong("locationId", Long.valueOf(locationId)); bundle.putLong("locationId", Long.parseLong(locationId));
navController.navigate(R.id.action_global_locationFragment, bundle); navController.navigate(R.id.action_global_locationFragment, bundle);
} }
@ -864,6 +902,14 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
return binding.toolbar; return binding.toolbar;
} }
public List<Tab> getCurrentTabs() {
return currentTabs;
}
// public boolean isNavRootInCurrentTabs(@IdRes final int navRootId) {
// return showBottomViewDestinations.stream().anyMatch(id -> id == navRootId);
// }
private void setNavBarDMUnreadCountBadge(final int unseenCount) { private void setNavBarDMUnreadCountBadge(final int unseenCount) {
final BadgeDrawable badge = binding.bottomNavView.getOrCreateBadge(R.id.direct_messages_nav_graph); final BadgeDrawable badge = binding.bottomNavView.getOrCreateBadge(R.id.direct_messages_nav_graph);
if (badge == null) return; if (badge == null) return;

View File

@ -10,7 +10,6 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.cursoradapter.widget.CursorAdapter; import androidx.cursoradapter.widget.CursorAdapter;
import awais.instagrabber.R;
import awais.instagrabber.databinding.ItemSuggestionBinding; import awais.instagrabber.databinding.ItemSuggestionBinding;
import awais.instagrabber.models.enums.SuggestionType; import awais.instagrabber.models.enums.SuggestionType;

View 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);
}
}

View File

@ -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);
}
}

View File

@ -16,7 +16,6 @@ import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmRavenMediaBinding; import awais.instagrabber.databinding.LayoutDmRavenMediaBinding;
import awais.instagrabber.models.enums.MediaItemType; import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.models.enums.RavenMediaViewMode; import awais.instagrabber.models.enums.RavenMediaViewMode;
import awais.instagrabber.repositories.responses.ImageVersions2;
import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User; import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem; import awais.instagrabber.repositories.responses.directmessages.DirectItem;

View File

@ -47,7 +47,7 @@ public class FavoriteDataSource {
} }
public final void insertOrUpdateFavorite(@NonNull final Favorite favorite) { public final void insertOrUpdateFavorite(@NonNull final Favorite favorite) {
if (favorite.getId() > 0) { if (favorite.getId() != 0) {
favoriteDao.updateFavorites(favorite); favoriteDao.updateFavorites(favorite);
return; return;
} }

View File

@ -0,0 +1,275 @@
package awais.instagrabber.dialogs;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Bundle;
import android.util.Pair;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.ItemTouchHelper.SimpleCallback;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.stream.Collectors;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectUsersAdapter;
import awais.instagrabber.adapters.TabsAdapter;
import awais.instagrabber.adapters.viewholder.TabViewHolder;
import awais.instagrabber.fragments.settings.PreferenceKeys;
import awais.instagrabber.models.Tab;
import awais.instagrabber.utils.Utils;
import static androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_DRAG;
import static androidx.recyclerview.widget.ItemTouchHelper.DOWN;
import static androidx.recyclerview.widget.ItemTouchHelper.UP;
public class TabOrderPreferenceDialogFragment extends DialogFragment {
private Callback callback;
private Context context;
private List<Tab> tabsInPref;
private ItemTouchHelper itemTouchHelper;
private AlertDialog dialog;
private List<Tab> newOrderTabs;
private List<Tab> newOtherTabs;
private final TabsAdapter.TabAdapterCallback tabAdapterCallback = new TabsAdapter.TabAdapterCallback() {
@Override
public void onStartDrag(final TabViewHolder viewHolder) {
if (itemTouchHelper == null || viewHolder == null) return;
itemTouchHelper.startDrag(viewHolder);
}
@Override
public void onOrderChange(final List<Tab> newOrderTabs) {
if (newOrderTabs == null || tabsInPref == null || dialog == null) return;
TabOrderPreferenceDialogFragment.this.newOrderTabs = newOrderTabs;
setSaveButtonState(newOrderTabs);
}
@Override
public void onAdd(final Tab tab) {
// Add this tab to newOrderTabs
newOrderTabs = ImmutableList.<Tab>builder()
.addAll(newOrderTabs)
.add(tab)
.build();
// Remove this tab from newOtherTabs
if (newOtherTabs != null) {
newOtherTabs = newOtherTabs.stream()
.filter(t -> !t.equals(tab))
.collect(Collectors.toList());
}
setSaveButtonState(newOrderTabs);
// submit these tab lists to adapter
if (adapter == null) return;
adapter.submitList(newOrderTabs, newOtherTabs, () -> list.postDelayed(() -> adapter.notifyDataSetChanged(), 300));
}
@Override
public void onRemove(final Tab tab) {
// Remove this tab from newOrderTabs
newOrderTabs = newOrderTabs.stream()
.filter(t -> !t.equals(tab))
.collect(Collectors.toList());
// Add this tab to newOtherTabs
if (newOtherTabs != null) {
newOtherTabs = ImmutableList.<Tab>builder()
.addAll(newOtherTabs)
.add(tab)
.build();
}
setSaveButtonState(newOrderTabs);
// submit these tab lists to adapter
if (adapter == null) return;
adapter.submitList(newOrderTabs, newOtherTabs, () -> list.postDelayed(() -> {
adapter.notifyDataSetChanged();
if (tab.getNavigationRootId() == R.id.direct_messages_nav_graph) {
final ConfirmDialogFragment dialogFragment = ConfirmDialogFragment.newInstance(
111, 0, R.string.dm_remove_warning, R.string.ok, 0, 0
);
dialogFragment.show(getChildFragmentManager(), "dm_warning_dialog");
}
}, 500));
}
private void setSaveButtonState(final List<Tab> newOrderTabs) {
dialog.getButton(AlertDialog.BUTTON_POSITIVE)
.setEnabled(!newOrderTabs.equals(tabsInPref));
}
};
private final SimpleCallback simpleCallback = new SimpleCallback(UP | DOWN, 0) {
private int movePosition = RecyclerView.NO_POSITION;
@Override
public int getMovementFlags(@NonNull final RecyclerView recyclerView, @NonNull final RecyclerView.ViewHolder viewHolder) {
if (viewHolder instanceof DirectUsersAdapter.HeaderViewHolder) return 0;
if (viewHolder instanceof TabViewHolder && !((TabViewHolder) viewHolder).isDraggable()) return 0;
return super.getMovementFlags(recyclerView, viewHolder);
}
@Override
public void onChildDraw(@NonNull final Canvas c,
@NonNull final RecyclerView recyclerView,
@NonNull final RecyclerView.ViewHolder viewHolder,
final float dX,
final float dY,
final int actionState,
final boolean isCurrentlyActive) {
if (actionState != ACTION_STATE_DRAG) {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
return;
}
final TabsAdapter adapter = (TabsAdapter) recyclerView.getAdapter();
if (adapter == null) {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
return;
}
// Do not allow dragging into 'Other tabs' category
float edgeY = dY;
final int lastPosition = adapter.getCurrentCount() - 1;
final View view = viewHolder.itemView;
// final int topEdge = recyclerView.getTop();
final int bottomEdge = view.getHeight() * adapter.getCurrentCount() - view.getBottom();
// if (movePosition == 0 && dY < topEdge) {
// edgeY = topEdge;
// } else
if (movePosition >= lastPosition && dY >= bottomEdge) {
edgeY = bottomEdge;
}
super.onChildDraw(c, recyclerView, viewHolder, dX, edgeY, actionState, isCurrentlyActive);
}
@Override
public boolean onMove(@NonNull final RecyclerView recyclerView,
@NonNull final RecyclerView.ViewHolder viewHolder,
@NonNull final RecyclerView.ViewHolder target) {
final TabsAdapter adapter = (TabsAdapter) recyclerView.getAdapter();
if (adapter == null) return false;
movePosition = target.getBindingAdapterPosition();
if (movePosition >= adapter.getCurrentCount()) {
return false;
}
final int from = viewHolder.getBindingAdapterPosition();
final int to = target.getBindingAdapterPosition();
adapter.moveItem(from, to);
// adapter.notifyItemMoved(from, to);
return true;
}
@Override
public void onSwiped(@NonNull final RecyclerView.ViewHolder viewHolder, final int direction) {}
@Override
public void onSelectedChanged(@Nullable final RecyclerView.ViewHolder viewHolder, final int actionState) {
super.onSelectedChanged(viewHolder, actionState);
if (!(viewHolder instanceof TabViewHolder)) {
movePosition = RecyclerView.NO_POSITION;
return;
}
if (actionState == ACTION_STATE_DRAG) {
((TabViewHolder) viewHolder).setDragging(true);
movePosition = viewHolder.getBindingAdapterPosition();
}
}
@Override
public void clearView(@NonNull final RecyclerView recyclerView,
@NonNull final RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
((TabViewHolder) viewHolder).setDragging(false);
movePosition = RecyclerView.NO_POSITION;
}
};
private TabsAdapter adapter;
private RecyclerView list;
public static TabOrderPreferenceDialogFragment newInstance() {
final Bundle args = new Bundle();
final TabOrderPreferenceDialogFragment fragment = new TabOrderPreferenceDialogFragment();
fragment.setArguments(args);
return fragment;
}
public TabOrderPreferenceDialogFragment() {}
@Override
public void onAttach(@NonNull final Context context) {
super.onAttach(context);
try {
callback = (Callback) getParentFragment();
} catch (ClassCastException e) {
// throw new ClassCastException("Calling fragment must implement TabOrderPreferenceDialogFragment.Callback interface");
}
this.context = context;
}
@NonNull
@Override
public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {
return new MaterialAlertDialogBuilder(context)
.setView(createView())
.setPositiveButton(R.string.save, (d, w) -> {
final boolean hasChanged = newOrderTabs != null && !newOrderTabs.equals(tabsInPref);
if (hasChanged) {
saveNewOrder();
}
if (callback == null) return;
callback.onSave(hasChanged);
})
.setNegativeButton(R.string.cancel, (dialog, which) -> {
if (callback == null) return;
callback.onCancel();
})
.create();
}
private void saveNewOrder() {
final String newOrderString = newOrderTabs.stream()
.map(Tab::getGraphName)
.collect(Collectors.joining(","));
Utils.settingsHelper.putString(PreferenceKeys.PREF_TAB_ORDER, newOrderString);
}
@Override
public void onStart() {
super.onStart();
final Dialog dialog = getDialog();
if (!(dialog instanceof AlertDialog)) return;
this.dialog = (AlertDialog) dialog;
this.dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
}
@NonNull
private View createView() {
list = new RecyclerView(context);
list.setLayoutManager(new LinearLayoutManager(context));
itemTouchHelper = new ItemTouchHelper(simpleCallback);
itemTouchHelper.attachToRecyclerView(list);
adapter = new TabsAdapter(tabAdapterCallback);
list.setAdapter(adapter);
final Pair<List<Tab>, List<Tab>> navTabListPair = Utils.getNavTabList(context);
tabsInPref = navTabListPair.first;
// initially set newOrderTabs and newOtherTabs same as current tabs
newOrderTabs = navTabListPair.first;
newOtherTabs = navTabListPair.second;
adapter.submitList(navTabListPair.first, navTabListPair.second);
return list;
}
public interface Callback {
void onSave(final boolean orderHasChanged);
void onCancel();
}
}

View File

@ -65,13 +65,14 @@ import awais.instagrabber.webservices.GraphQLService;
import awais.instagrabber.webservices.LocationService; import awais.instagrabber.webservices.LocationService;
import awais.instagrabber.webservices.ServiceCallback; import awais.instagrabber.webservices.ServiceCallback;
import awais.instagrabber.webservices.StoriesService; import awais.instagrabber.webservices.StoriesService;
//import awaisomereport.LogCollector;
import static androidx.core.content.PermissionChecker.checkSelfPermission; import static androidx.core.content.PermissionChecker.checkSelfPermission;
import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION; import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION;
//import static awais.instagrabber.utils.Utils.logCollector;
import static awais.instagrabber.utils.Utils.settingsHelper; import static awais.instagrabber.utils.Utils.settingsHelper;
//import awaisomereport.LogCollector;
//import static awais.instagrabber.utils.Utils.logCollector;
public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener { public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = "LocationFragment"; private static final String TAG = "LocationFragment";
private static final int STORAGE_PERM_REQUEST_CODE = 8020; private static final int STORAGE_PERM_REQUEST_CODE = 8020;

View File

@ -306,6 +306,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
private AccountRepository accountRepository; private AccountRepository accountRepository;
private FavoriteRepository favoriteRepository; private FavoriteRepository favoriteRepository;
private AppStateViewModel appStateViewModel; private AppStateViewModel appStateViewModel;
private boolean disableDm = false;
@Override @Override
public void onCreate(@Nullable final Bundle savedInstanceState) { public void onCreate(@Nullable final Bundle savedInstanceState) {
@ -321,8 +322,10 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
mediaService = isLoggedIn ? MediaService.getInstance(null, null, 0) : null; mediaService = isLoggedIn ? MediaService.getInstance(null, null, 0) : null;
userService = isLoggedIn ? UserService.getInstance() : null; userService = isLoggedIn ? UserService.getInstance() : null;
graphQLService = isLoggedIn ? null : GraphQLService.getInstance(); graphQLService = isLoggedIn ? null : GraphQLService.getInstance();
accountRepository = AccountRepository.getInstance(AccountDataSource.getInstance(getContext())); final Context context = getContext();
favoriteRepository = FavoriteRepository.getInstance(FavoriteDataSource.getInstance(getContext())); if (context == null) return;
accountRepository = AccountRepository.getInstance(AccountDataSource.getInstance(context));
favoriteRepository = FavoriteRepository.getInstance(FavoriteDataSource.getInstance(context));
appStateViewModel = new ViewModelProvider(fragmentActivity).get(AppStateViewModel.class); appStateViewModel = new ViewModelProvider(fragmentActivity).get(AppStateViewModel.class);
setHasOptionsMenu(true); setHasOptionsMenu(true);
} }
@ -577,6 +580,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
} }
private void init() { private void init() {
disableDm = !Utils.isNavRootInCurrentTabs("direct_messages_nav_graph");
if (getArguments() != null) { if (getArguments() != null) {
final ProfileFragmentArgs fragmentArgs = ProfileFragmentArgs.fromBundle(getArguments()); final ProfileFragmentArgs fragmentArgs = ProfileFragmentArgs.fromBundle(getArguments());
username = fragmentArgs.getUsername(); username = fragmentArgs.getUsername();
@ -938,7 +942,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
} }
profileDetailsBinding.btnSaved.setVisibility(View.GONE); profileDetailsBinding.btnSaved.setVisibility(View.GONE);
profileDetailsBinding.btnLiked.setVisibility(View.GONE); profileDetailsBinding.btnLiked.setVisibility(View.GONE);
profileDetailsBinding.btnDM.setVisibility(View.VISIBLE); profileDetailsBinding.btnDM.setVisibility(disableDm ? View.GONE : View.VISIBLE);
profileDetailsBinding.btnFollow.setVisibility(View.VISIBLE); profileDetailsBinding.btnFollow.setVisibility(View.VISIBLE);
final Context context = getContext(); final Context context = getContext();
if (context == null) return; if (context == null) return;
@ -1116,6 +1120,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
PostItemType.TAGGED); PostItemType.TAGGED);
NavHostFragment.findNavController(this).navigate(action); NavHostFragment.findNavController(this).navigate(action);
}); });
if (!disableDm) {
profileDetailsBinding.btnDM.setOnClickListener(v -> { profileDetailsBinding.btnDM.setOnClickListener(v -> {
profileDetailsBinding.btnDM.setEnabled(false); profileDetailsBinding.btnDM.setEnabled(false);
new CreateThreadAction(cookie, profileModel.getPk(), thread -> { new CreateThreadAction(cookie, profileModel.getPk(), thread -> {
@ -1133,6 +1138,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
profileDetailsBinding.btnDM.setEnabled(true); profileDetailsBinding.btnDM.setEnabled(true);
}).execute(); }).execute();
}); });
}
profileDetailsBinding.mainProfileImage.setOnClickListener(v -> { profileDetailsBinding.mainProfileImage.setOnClickListener(v -> {
if (!hasStories) { if (!hasStories) {
// show profile pic // show profile pic

View File

@ -1,7 +1,7 @@
package awais.instagrabber.fragments.settings; package awais.instagrabber.fragments.settings;
import android.content.Context; import android.content.Context;
import android.content.res.TypedArray; import android.util.Pair;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.preference.ListPreference; import androidx.preference.ListPreference;
@ -12,13 +12,17 @@ import androidx.preference.SwitchPreferenceCompat;
import java.util.List; import java.util.List;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.dialogs.ConfirmDialogFragment;
import awais.instagrabber.dialogs.TabOrderPreferenceDialogFragment;
import awais.instagrabber.models.Tab;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
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) {
@ -28,10 +32,12 @@ 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));
final List<Preference> preferences = FlavorSettings.getInstance().getPreferences(context, final List<Preference> preferences = FlavorSettings.getInstance()
.getPreferences(context,
getChildFragmentManager(), getChildFragmentManager(),
SettingCategory.GENERAL); SettingCategory.GENERAL);
if (preferences != null) { if (preferences != null) {
@ -44,24 +50,36 @@ 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 Pair<List<Tab>, List<Tab>> listPair = Utils.getNavTabList(context);
final int length = mainNavIds.length(); final List<Tab> tabs = listPair.first;
final String[] values = new String[length]; final String[] titles = tabs.stream()
for (int i = 0; i < length; i++) { .map(Tab::getTitle)
final int resourceId = mainNavIds.getResourceId(i, -1); .toArray(String[]::new);
if (resourceId < 0) continue; final String[] navGraphFileNames = tabs.stream()
values[i] = getResources().getResourceEntryName(resourceId); .map(Tab::getGraphName)
} .toArray(String[]::new);
mainNavIds.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(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);
@ -82,4 +100,22 @@ public class GeneralPreferencesFragment extends BasePreferencesFragment {
return true; return true;
}); });
} }
@Override
public void onSave(final boolean orderHasChanged) {
if (!orderHasChanged) return;
final ConfirmDialogFragment dialogFragment = ConfirmDialogFragment.newInstance(
111,
0,
R.string.tab_order_start_next_launch,
R.string.ok,
0,
0);
dialogFragment.show(getChildFragmentManager(), "tab_order_set_dialog");
}
@Override
public void onCancel() {
}
} }

View File

@ -27,6 +27,7 @@ import java.util.List;
import awais.instagrabber.BuildConfig; import awais.instagrabber.BuildConfig;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.activities.Login; import awais.instagrabber.activities.Login;
import awais.instagrabber.activities.MainActivity;
import awais.instagrabber.databinding.PrefAccountSwitcherBinding; import awais.instagrabber.databinding.PrefAccountSwitcherBinding;
import awais.instagrabber.db.datasources.AccountDataSource; import awais.instagrabber.db.datasources.AccountDataSource;
import awais.instagrabber.db.entities.Account; import awais.instagrabber.db.entities.Account;
@ -38,6 +39,7 @@ import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.FlavorTown; import awais.instagrabber.utils.FlavorTown;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.webservices.ServiceCallback; import awais.instagrabber.webservices.ServiceCallback;
import awais.instagrabber.webservices.UserService; import awais.instagrabber.webservices.UserService;
@ -55,8 +57,10 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
void setupPreferenceScreen(final PreferenceScreen screen) { void setupPreferenceScreen(final PreferenceScreen screen) {
final String cookie = settingsHelper.getString(Constants.COOKIE); final String cookie = settingsHelper.getString(Constants.COOKIE);
final boolean isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0; final boolean isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) > 0;
final MainActivity activity = (MainActivity) getActivity();
// screen.addPreference(new MoreHeaderPreference(getContext())); // screen.addPreference(new MoreHeaderPreference(getContext()));
final Context context = getContext(); final Context context = getContext();
final Resources resources = context.getResources();
if (context == null) return; if (context == null) return;
accountRepository = AccountRepository.getInstance(AccountDataSource.getInstance(context)); accountRepository = AccountRepository.getInstance(AccountDataSource.getInstance(context));
final PreferenceCategory accountCategory = new PreferenceCategory(context); final PreferenceCategory accountCategory = new PreferenceCategory(context);
@ -135,6 +139,13 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
screen.addPreference(getDivider(context)); screen.addPreference(getDivider(context));
final NavController navController = NavHostFragment.findNavController(this); final NavController navController = NavHostFragment.findNavController(this);
if (isLoggedIn) { if (isLoggedIn) {
boolean showActivity = true;
boolean showExplore = false;
if (activity != null) {
showActivity = !Utils.isNavRootInCurrentTabs("notification_viewer_nav_graph");
showExplore = !Utils.isNavRootInCurrentTabs("discover_nav_graph");
}
if (showActivity) {
screen.addPreference(getPreference(R.string.action_notif, R.drawable.ic_not_liked, preference -> { screen.addPreference(getPreference(R.string.action_notif, R.drawable.ic_not_liked, preference -> {
if (isSafeToNavigate(navController)) { if (isSafeToNavigate(navController)) {
final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalNotificationsViewerFragment("notif"); final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalNotificationsViewerFragment("notif");
@ -142,6 +153,16 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
} }
return true; return true;
})); }));
}
if (showExplore) {
screen.addPreference(getPreference(R.string.title_discover, R.drawable.ic_explore_24, preference -> {
if (isSafeToNavigate(navController)) {
navController.navigate(R.id.discover_nav_graph);
}
return true;
}));
}
screen.addPreference(getPreference(R.string.action_ayml, R.drawable.ic_suggested_users, preference -> { screen.addPreference(getPreference(R.string.action_ayml, R.drawable.ic_suggested_users, preference -> {
if (isSafeToNavigate(navController)) { if (isSafeToNavigate(navController)) {
final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalNotificationsViewerFragment("ayml"); final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalNotificationsViewerFragment("ayml");
@ -157,6 +178,13 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
return true; return true;
})); }));
} }
// Check if favorites has been added as a tab. And if so, do not add in this list
boolean showFavorites = true;
if (activity != null) {
showFavorites = !Utils.isNavRootInCurrentTabs("favorites_nav_graph");
}
if (showFavorites) {
screen.addPreference(getPreference(R.string.title_favorites, R.drawable.ic_star_24, preference -> { screen.addPreference(getPreference(R.string.title_favorites, R.drawable.ic_star_24, preference -> {
if (isSafeToNavigate(navController)) { if (isSafeToNavigate(navController)) {
final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToFavoritesFragment(); final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToFavoritesFragment();
@ -164,6 +192,7 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
} }
return true; return true;
})); }));
}
screen.addPreference(getDivider(context)); screen.addPreference(getDivider(context));
screen.addPreference(getPreference(R.string.action_settings, R.drawable.ic_outline_settings_24, preference -> { screen.addPreference(getPreference(R.string.action_settings, R.drawable.ic_outline_settings_24, preference -> {

View File

@ -6,4 +6,5 @@ public final class PreferenceKeys {
public static final String PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT = "enable_dm_auto_refresh_freq_unit"; public static final String PREF_ENABLE_DM_AUTO_REFRESH_FREQ_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_ENABLE_SENTRY = "enable_sentry"; public static final String PREF_ENABLE_SENTRY = "enable_sentry";
public static final String PREF_TAB_ORDER = "tab_order";
} }

View File

@ -0,0 +1,110 @@
package awais.instagrabber.models;
import androidx.annotation.DrawableRes;
import androidx.annotation.IdRes;
import androidx.annotation.NavigationRes;
import androidx.annotation.NonNull;
import java.util.Objects;
public class Tab {
private final int iconResId;
private final String title;
private final boolean removable;
/**
* This is name part of the navigation resource
* eg: @navigation/<b>graphName</b>
*/
private final String graphName;
/**
* This is the actual resource id of the navigation resource (R.navigation.graphName = navigationResId)
*/
private final int navigationResId;
/**
* This is the resource id of the root navigation tag of the navigation resource.
* <p>eg: inside R.navigation.direct_messages_nav_graph, the id of the root tag is R.id.direct_messages_nav_graph.
* <p>So this field would equal to the value of R.id.direct_messages_nav_graph
*/
private final int navigationRootId;
/**
* This is the start destination of the nav graph
*/
private final int startDestinationFragmentId;
public Tab(@DrawableRes final int iconResId,
@NonNull final String title,
final boolean removable,
@NonNull final String graphName,
@NavigationRes final int navigationResId,
@IdRes final int navigationRootId,
@IdRes final int startDestinationFragmentId) {
this.iconResId = iconResId;
this.title = title;
this.removable = removable;
this.graphName = graphName;
this.navigationResId = navigationResId;
this.navigationRootId = navigationRootId;
this.startDestinationFragmentId = startDestinationFragmentId;
}
public int getIconResId() {
return iconResId;
}
public String getTitle() {
return title;
}
public boolean isRemovable() {
return removable;
}
public String getGraphName() {
return graphName;
}
public int getNavigationResId() {
return navigationResId;
}
public int getNavigationRootId() {
return navigationRootId;
}
public int getStartDestinationFragmentId() {
return startDestinationFragmentId;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final Tab tab = (Tab) o;
return iconResId == tab.iconResId &&
removable == tab.removable &&
navigationResId == tab.navigationResId &&
navigationRootId == tab.navigationRootId &&
startDestinationFragmentId == tab.startDestinationFragmentId &&
Objects.equals(title, tab.title) &&
Objects.equals(graphName, tab.graphName);
}
@Override
public int hashCode() {
return Objects.hash(iconResId, title, removable, graphName, navigationResId, navigationRootId, startDestinationFragmentId);
}
@NonNull
@Override
public String toString() {
return "Tab{" +
"title='" + title + '\'' +
", removable=" + removable +
", graphName='" + graphName + '\'' +
'}';
}
}

View File

@ -3,7 +3,6 @@ package awais.instagrabber.repositories;
import java.util.Map; import java.util.Map;
import awais.instagrabber.repositories.responses.search.SearchResponse; import awais.instagrabber.repositories.responses.search.SearchResponse;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.GET; import retrofit2.http.GET;
import retrofit2.http.QueryMap; import retrofit2.http.QueryMap;

View File

@ -1,7 +1,5 @@
package awais.instagrabber.repositories.responses.notification; package awais.instagrabber.repositories.responses.notification;
import androidx.annotation.NonNull;
public class NotificationCounts { public class NotificationCounts {
private final int commentLikes; private final int commentLikes;
private final int usertags; private final int usertags;

View File

@ -2,8 +2,6 @@ package awais.instagrabber.repositories.responses.search;
import java.util.List; import java.util.List;
import awais.instagrabber.repositories.responses.User;
public class SearchResponse { public class SearchResponse {
// app // app
private final List<SearchItem> list; private final List<SearchItem> list;

View File

@ -16,6 +16,7 @@ import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_D
import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT; import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_DM_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_ENABLE_SENTRY; import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_ENABLE_SENTRY;
import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_TAB_ORDER;
import static awais.instagrabber.utils.Constants.APP_LANGUAGE; import static awais.instagrabber.utils.Constants.APP_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;
@ -37,12 +38,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;
@ -155,7 +156,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,

View File

@ -2,8 +2,6 @@ package awais.instagrabber.utils;
import android.content.Context; import android.content.Context;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.format.DateFormat; import android.text.format.DateFormat;
import android.text.format.DateUtils; import android.text.format.DateUtils;
import android.text.style.URLSpan; import android.text.style.URLSpan;
@ -11,17 +9,12 @@ import android.util.Patterns;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern;
import awais.instagrabber.customviews.CommentMentionClickSpan;
public final class TextUtils { public final class TextUtils {
// extracted from String class // extracted from String class

View File

@ -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;
@ -30,6 +31,7 @@ import android.webkit.MimeTypeMap;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.DrawableRes; import androidx.annotation.DrawableRes;
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;
@ -40,6 +42,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,16 +51,19 @@ 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";
@ -74,6 +81,7 @@ public final class Utils {
private static int actionBarHeight; private static int actionBarHeight;
public static Handler applicationHandler; public static Handler applicationHandler;
public static String cacheDir; public static String cacheDir;
public static String tabOrderString;
private static int defaultStatusBarColor; private static int defaultStatusBarColor;
public static int convertDpToPx(final float dp) { public static int convertDpToPx(final float dp) {
@ -371,4 +379,116 @@ 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();
typedArray = resources.obtainTypedArray(R.array.main_nav_start_dest_frag_ids);
length = typedArray.length();
final int[] startDestFragIds = new int[length];
for (int i = 0; i < length; i++) {
final int resourceId = typedArray.getResourceId(i, 0);
if (resourceId == 0) continue;
startDestFragIds[i] = resourceId;
}
typedArray.recycle();
final List<String> currentOrderGraphNames = getCurrentOrderOfGraphNamesFromPref(navGraphNames);
if (titleArray.length != iconIds.length || titleArray.length != navGraphNames.length) {
throw new RuntimeException(String.format("Array lengths don't match!: titleArray%s, navGraphNames: %s, iconIds: %s",
Arrays.toString(titleArray), Arrays.toString(navGraphNames), Arrays.toString(iconIds)));
}
final List<Tab> tabs = new ArrayList<>();
final List<Tab> otherTabs = new ArrayList<>(); // Will contain tabs not in current list
for (int i = 0; i < length; i++) {
final String navGraphName = navGraphNames[i];
final int navRootId = navRootIds[i];
final Tab tab = new Tab(iconIds[i],
titleArray[i],
!NON_REMOVABLE_NAV_ROOT_IDS.contains(navRootId),
navGraphName,
navigationResIds[i],
navRootId,
startDestFragIds[i]);
if (!currentOrderGraphNames.contains(navGraphName)) {
otherTabs.add(tab);
continue;
}
tabs.add(tab);
}
Collections.sort(tabs, Ordering.explicit(currentOrderGraphNames).onResultOf(tab -> {
if (tab == null) return null;
return tab.getGraphName();
}));
return new Pair<>(tabs, otherTabs);
}
@NonNull
private static List<String> getCurrentOrderOfGraphNamesFromPref(@NonNull final String[] navGraphNames) {
tabOrderString = settingsHelper.getString(PreferenceKeys.PREF_TAB_ORDER);
final List<String> navGraphNameList = Arrays.asList(navGraphNames);
if (TextUtils.isEmpty(tabOrderString)) {
// Use top 5 entries for default list
return navGraphNameList.subList(0, 5);
}
// Make sure that the list from preference does not contain any invalid values
final List<String> orderGraphNames = Arrays.stream(tabOrderString.split(","))
.filter(s -> !TextUtils.isEmpty(s))
.filter(navGraphNameList::contains)
.collect(Collectors.toList());
if (orderGraphNames.isEmpty()) {
// Use top 5 entries for default list
return navGraphNameList.subList(0, 5);
}
return orderGraphNames;
}
public static boolean isNavRootInCurrentTabs(final String navRootString) {
return tabOrderString.contains(navRootString);
}
} }

View File

@ -12,12 +12,12 @@ import java.util.Map;
import java.util.UUID; import java.util.UUID;
import awais.instagrabber.repositories.FeedRepository; import awais.instagrabber.repositories.FeedRepository;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.PostsFetchResponse;
import awais.instagrabber.repositories.responses.feed.EndOfFeedDemarcator; import awais.instagrabber.repositories.responses.feed.EndOfFeedDemarcator;
import awais.instagrabber.repositories.responses.feed.EndOfFeedGroup; import awais.instagrabber.repositories.responses.feed.EndOfFeedGroup;
import awais.instagrabber.repositories.responses.feed.EndOfFeedGroupSet; import awais.instagrabber.repositories.responses.feed.EndOfFeedGroupSet;
import awais.instagrabber.repositories.responses.feed.FeedFetchResponse; import awais.instagrabber.repositories.responses.feed.FeedFetchResponse;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.PostsFetchResponse;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.Callback; import retrofit2.Callback;

View File

@ -12,12 +12,12 @@ import java.util.stream.Collectors;
import awais.instagrabber.repositories.NewsRepository; import awais.instagrabber.repositories.NewsRepository;
import awais.instagrabber.repositories.responses.AymlResponse; import awais.instagrabber.repositories.responses.AymlResponse;
import awais.instagrabber.repositories.responses.AymlUser; import awais.instagrabber.repositories.responses.AymlUser;
import awais.instagrabber.repositories.responses.notification.NotificationCounts;
import awais.instagrabber.repositories.responses.UserSearchResponse;
import awais.instagrabber.repositories.responses.NewsInboxResponse; import awais.instagrabber.repositories.responses.NewsInboxResponse;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.UserSearchResponse;
import awais.instagrabber.repositories.responses.notification.Notification; import awais.instagrabber.repositories.responses.notification.Notification;
import awais.instagrabber.repositories.responses.notification.NotificationArgs; import awais.instagrabber.repositories.responses.notification.NotificationArgs;
import awais.instagrabber.repositories.responses.User; import awais.instagrabber.repositories.responses.notification.NotificationCounts;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.Callback; import retrofit2.Callback;

View File

@ -1,15 +1,10 @@
package awais.instagrabber.webservices; package awais.instagrabber.webservices;
import androidx.annotation.NonNull;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import awais.instagrabber.repositories.SearchRepository; import awais.instagrabber.repositories.SearchRepository;
import awais.instagrabber.repositories.responses.search.SearchResponse; import awais.instagrabber.repositories.responses.search.SearchResponse;
import awais.instagrabber.utils.TextUtils;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit; import retrofit2.Retrofit;
public class SearchService extends BaseService { public class SearchService extends BaseService {

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM11,19.93c-3.95,-0.49 -7,-3.85 -7,-7.93 0,-0.62 0.08,-1.21 0.21,-1.79L9,15v1c0,1.1 0.9,2 2,2v1.93zM17.9,17.39c-0.26,-0.81 -1,-1.39 -1.9,-1.39h-1v-3c0,-0.55 -0.45,-1 -1,-1L8,12v-2h2c0.55,0 1,-0.45 1,-1L11,7h2c1.1,0 2,-0.9 2,-2v-0.41c2.93,1.19 5,4.06 5,7.41 0,2.08 -0.8,3.97 -2.1,5.39z" />
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,10.9c-0.61,0 -1.1,0.49 -1.1,1.1s0.49,1.1 1.1,1.1c0.61,0 1.1,-0.49 1.1,-1.1s-0.49,-1.1 -1.1,-1.1zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM14.19,14.19L6,18l3.81,-8.19L18,6l-3.81,8.19z"/>
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M13.5,0.67s0.74,2.65 0.74,4.8c0,2.06 -1.35,3.73 -3.41,3.73 -2.07,0 -3.63,-1.67 -3.63,-3.73l0.03,-0.36C5.21,7.51 4,10.62 4,14c0,4.42 3.58,8 8,8s8,-3.58 8,-8C20,8.61 17.41,3.8 13.5,0.67zM11.71,19c-1.78,0 -3.22,-1.4 -3.22,-3.14 0,-1.62 1.05,-2.76 2.81,-3.12 1.77,-0.36 3.6,-1.21 4.62,-2.58 0.39,1.29 0.59,2.65 0.59,4.04 0,2.65 -2.15,4.8 -4.8,4.8z" />
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM18,14L6,14v-2h12v2zM18,11L6,11L6,9h12v2zM18,8L6,8L6,6h12v2z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
</vector>

View 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>

View 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>

View 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>

View File

@ -47,7 +47,6 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom" android:layout_gravity="bottom"
app:labelVisibilityMode="labeled" 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>

View 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>

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/profile_nav_graph"
android:icon="@drawable/ic_profile_24"
android:title="@string/profile" />
<item
android:id="@+id/more_nav_graph"
android:icon="@drawable/ic_more_horiz_24"
android:title="@string/more" />
</menu>

View File

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/direct_messages_nav_graph"
android:icon="@drawable/ic_round_send_24"
android:title="@string/title_dm" />
<item
android:id="@+id/feed_nav_graph"
android:icon="@drawable/ic_feed"
android:title="@string/feed" />
<item
android:id="@+id/profile_nav_graph"
android:icon="@drawable/ic_profile_24"
android:title="@string/profile" />
<item
android:id="@+id/discover_nav_graph"
android:icon="@drawable/ic_discover"
android:title="@string/title_discover" />
<!--<item-->
<!-- android:id="@+id/favouritesFragment"-->
<!-- android:icon="@drawable/ic_star_24"-->
<!-- android:title="@string/title_favorites"/>-->
<item
android:id="@+id/more_nav_graph"
android:icon="@drawable/ic_more_horiz_24"
android:title="@string/more" />
</menu>

View 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>

View File

@ -11,6 +11,7 @@
<include app:graph="@navigation/likes_nav_graph" /> <include app:graph="@navigation/likes_nav_graph" />
<include app:graph="@navigation/notification_viewer_nav_graph" /> <include app:graph="@navigation/notification_viewer_nav_graph" />
<include app:graph="@navigation/story_list_nav_graph" /> <include app:graph="@navigation/story_list_nav_graph" />
<include app:graph="@navigation/discover_nav_graph" />
<action <action
android:id="@+id/action_global_profileFragment" android:id="@+id/action_global_profileFragment"

View File

@ -13,7 +13,8 @@
<argument <argument
android:name="type" android:name="type"
app:argType="string" app:argType="string"
app:nullable="false" /> app:nullable="false"
android:defaultValue="notif"/>
<argument <argument
android:name="targetId" android:name="targetId"
android:defaultValue="0L" android:defaultValue="0L"

View File

@ -88,24 +88,61 @@
<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>
<item>@id/notification_viewer_nav_graph</item>
</array>
<!-- Nav graphs should correspond 1-to-1 with the above nav graph ids -->
<array name="main_nav_graphs">
<item>@navigation/direct_messages_nav_graph</item> <item>@navigation/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>
<item>@navigation/notification_viewer_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>
<item>@string/title_notifications</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>
<item>@drawable/ic_not_liked</item>
</array> </array>
<!-- fragmentIds should correspond 1-to-1 with the above drawabled -->
<!-- these are the start destination of the corresponding nav graphs -->
<array name="main_nav_start_dest_frag_ids" translatable="false">
<item>@id/directMessagesInboxFragment</item>
<item>@id/feedFragment</item>
<item>@id/profileFragment</item>
<item>@id/discoverFragment</item>
<item>@id/morePreferencesFragment</item>
<item>@id/favoritesFragment</item>
<item>@id/notificationsViewer</item>
</array>
<!--<array name="logged_out_main_nav_graphs">-->
<!-- <item>@navigation/profile_nav_graph</item>-->
<!-- <item>@navigation/more_nav_graph</item>-->
<!--</array>-->
<string-array name="light_themes" translatable="false"> <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>

View File

@ -475,6 +475,10 @@
<string name="delete_unsuccessful">Delete unsuccessful</string> <string name="delete_unsuccessful">Delete unsuccessful</string>
<string name="crash_report_subject">Barinsta Crash Report</string> <string name="crash_report_subject">Barinsta Crash Report</string>
<string name="crash_report_title">Select an email app to send crash logs</string> <string name="crash_report_title">Select an email app to send crash logs</string>
<string name="tab_order">Screen order</string>
<string name="other_tabs">Other tabs</string>
<string name="tab_order_start_next_launch">The tab order will be reflected on next launch</string>
<string name="dm_remove_warning">If saved, all DM related features will be disabled on next launch</string>
<string name="copy_caption">Copy caption</string> <string name="copy_caption">Copy caption</string>
<string name="copy_reply">Copy reply</string> <string name="copy_reply">Copy reply</string>
</resources> </resources>