mirror of
https://github.com/KokaKiwi/BarInsta
synced 2024-12-23 13:26:59 +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…
Reference in New Issue
Block a user