Ammar Githam
4 years ago
17 changed files with 1017 additions and 111 deletions
-
157app/src/main/java/awais/instagrabber/activities/MainActivity.java
-
156app/src/main/java/awais/instagrabber/adapters/TabsAdapter.java
-
88app/src/main/java/awais/instagrabber/adapters/viewholder/TabViewHolder.java
-
267app/src/main/java/awais/instagrabber/dialogs/TabOrderPreferenceDialogFragment.java
-
44app/src/main/java/awais/instagrabber/fragments/settings/GeneralPreferencesFragment.java
-
1app/src/main/java/awais/instagrabber/fragments/settings/PreferenceKeys.java
-
98app/src/main/java/awais/instagrabber/models/Tab.java
-
5app/src/main/java/awais/instagrabber/utils/SettingsHelper.java
-
111app/src/main/java/awais/instagrabber/utils/Utils.java
-
10app/src/main/res/drawable/ic_round_add_circle_24.xml
-
10app/src/main/res/drawable/ic_round_drag_handle_24.xml
-
10app/src/main/res/drawable/ic_round_remove_circle_24.xml
-
3app/src/main/res/layout/activity_main.xml
-
45app/src/main/res/layout/item_tab_order_pref.xml
-
43app/src/main/res/navigation/favorites_nav_graph.xml
-
32app/src/main/res/values/arrays.xml
-
2app/src/main/res/values/strings.xml
@ -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(); |
|||
} |
|||
} |
@ -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 + '\'' + |
|||
'}'; |
|||
} |
|||
} |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue