diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index 61a9130..fb7f4a8 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 33ceabf..a52e9fd 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -5,12 +5,14 @@
-
+
diff --git a/app/build.gradle b/app/build.gradle
index aba222f..a8b9649 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -41,6 +41,8 @@ dependencies {
implementation 'androidx.navigation:navigation-ui:2.3.5'
//implementation 'org.videolan.android:libvlc-all:3.4.1'
implementation 'de.mrmaffen:libvlc-android:2.1.12@aar'
+ implementation 'androidx.legacy:legacy-support-v4:1.0.0'
+ implementation 'androidx.recyclerview:recyclerview:1.2.1'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index cfae129..1edacc3 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -14,8 +14,8 @@
+ android:theme="@style/Theme.Ojo">
+
diff --git a/app/src/main/java/it/danieleverducci/ojo/ui/SettingsFragment.java b/app/src/main/java/it/danieleverducci/ojo/ui/SettingsFragment.java
new file mode 100644
index 0000000..14f4a88
--- /dev/null
+++ b/app/src/main/java/it/danieleverducci/ojo/ui/SettingsFragment.java
@@ -0,0 +1,61 @@
+package it.danieleverducci.ojo.ui;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.ItemTouchHelper;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.List;
+
+import it.danieleverducci.ojo.R;
+import it.danieleverducci.ojo.Settings;
+import it.danieleverducci.ojo.entities.Camera;
+import it.danieleverducci.ojo.ui.adapters.SettingsRecyclerViewAdapter;
+import it.danieleverducci.ojo.utils.ItemMoveCallback;
+
+/**
+ * A fragment representing a list of Items.
+ */
+public class SettingsFragment extends Fragment {
+
+ public SettingsFragment() {
+ }
+
+ public static SettingsFragment newInstance() {
+ SettingsFragment fragment = new SettingsFragment();
+ return fragment;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_settings_item_list, container, false);
+
+ // Load cameras
+ Settings settings = Settings.fromDisk(getContext());
+ List cams = settings.getCameras();
+
+ // Set the adapter
+ if (view instanceof RecyclerView) {
+ Context context = view.getContext();
+ RecyclerView recyclerView = (RecyclerView) view;
+ recyclerView.setLayoutManager(new LinearLayoutManager(context));
+ SettingsRecyclerViewAdapter adapter = new SettingsRecyclerViewAdapter(cams);
+ ItemTouchHelper.Callback callback =
+ new ItemMoveCallback(adapter);
+ ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
+ touchHelper.attachToRecyclerView(recyclerView);
+ adapter.setOnDragListener(touchHelper::startDrag);
+ recyclerView.setAdapter(adapter);
+ }
+ return view;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/it/danieleverducci/ojo/ui/adapters/SettingsRecyclerViewAdapter.java b/app/src/main/java/it/danieleverducci/ojo/ui/adapters/SettingsRecyclerViewAdapter.java
new file mode 100644
index 0000000..5628cef
--- /dev/null
+++ b/app/src/main/java/it/danieleverducci/ojo/ui/adapters/SettingsRecyclerViewAdapter.java
@@ -0,0 +1,106 @@
+package it.danieleverducci.ojo.ui.adapters;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import it.danieleverducci.ojo.R;
+import it.danieleverducci.ojo.databinding.FragmentSettingsItemBinding;
+import it.danieleverducci.ojo.entities.Camera;
+import it.danieleverducci.ojo.utils.ItemMoveCallback;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * {@link RecyclerView.Adapter} that can display a {@link Camera}.
+ * TODO: Replace the implementation with code for your data type.
+ */
+public class SettingsRecyclerViewAdapter extends RecyclerView.Adapter implements ItemMoveCallback.ItemTouchHelperContract {
+
+ private final List mValues;
+ private OnDragListener dragListener;
+
+ public SettingsRecyclerViewAdapter(List items) {
+ mValues = items;
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ ViewHolder vh = new ViewHolder(FragmentSettingsItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
+ vh.dragHandle.setOnTouchListener((v, event) -> {
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ dragListener.onItemDrag(vh);
+ }
+ return false;
+ });
+ return vh;
+ }
+
+ @Override
+ public void onBindViewHolder(final ViewHolder holder, int position) {
+ String cameraName = mValues.get(position).getName();
+ if (cameraName == null || cameraName.length() == 0)
+ cameraName = holder.name.getContext().getString(R.string.stream_list_default_camera_name).replace("{camNo}", (position+1)+"");
+ holder.name.setText(cameraName);
+ holder.url.setText(mValues.get(position).getRtspUrl());
+ }
+
+ @Override
+ public int getItemCount() {
+ return mValues.size();
+ }
+
+
+ // ============= Drag&Drop TouchHelper methods =============
+
+ @Override
+ public void onRowMoved(int fromPosition, int toPosition) {
+ if (fromPosition < toPosition) {
+ for (int i = fromPosition; i < toPosition; i++) {
+ Collections.swap(mValues, i, i + 1);
+ }
+ } else {
+ for (int i = fromPosition; i > toPosition; i--) {
+ Collections.swap(mValues, i, i - 1);
+ }
+ }
+ notifyItemMoved(fromPosition, toPosition);
+ }
+
+ @Override
+ public void onRowSelected(RecyclerView.ViewHolder myViewHolder) {
+
+ }
+
+ @Override
+ public void onRowClear(RecyclerView.ViewHolder myViewHolder) {
+
+ }
+
+ public void setOnDragListener(OnDragListener dragListener) {
+ this.dragListener = dragListener;
+ }
+
+ public class ViewHolder extends RecyclerView.ViewHolder {
+ public TextView name;
+ public TextView url;
+ public View dragHandle;
+
+ public ViewHolder(FragmentSettingsItemBinding binding) {
+ super(binding.getRoot());
+
+ this.name = binding.cameraName;
+ this.url = binding.cameraUrl;
+ this.dragHandle = binding.cameraDragHandle;
+ }
+ }
+
+ public interface OnDragListener {
+ void onItemDrag(ViewHolder vh);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/it/danieleverducci/ojo/utils/ItemMoveCallback.java b/app/src/main/java/it/danieleverducci/ojo/utils/ItemMoveCallback.java
new file mode 100644
index 0000000..54d2002
--- /dev/null
+++ b/app/src/main/java/it/danieleverducci/ojo/utils/ItemMoveCallback.java
@@ -0,0 +1,67 @@
+package it.danieleverducci.ojo.utils;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.ItemTouchHelper;
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * Implementation of recycleview drag&drop
+ */
+public class ItemMoveCallback extends ItemTouchHelper.Callback {
+
+ private final ItemTouchHelperContract mAdapter;
+
+ public ItemMoveCallback(ItemTouchHelperContract adapter) {
+ mAdapter = adapter;
+ }
+
+ @Override
+ public boolean isLongPressDragEnabled() {
+ return false;
+ }
+
+ @Override
+ public boolean isItemViewSwipeEnabled() {
+ return false;
+ }
+
+ @Override
+ public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int i) {}
+
+ @Override
+ public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
+ int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
+ return makeMovementFlags(dragFlags, 0);
+ }
+
+ @Override
+ public boolean onMove(@NonNull RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
+ RecyclerView.ViewHolder target) {
+ mAdapter.onRowMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
+ return true;
+ }
+
+ @Override
+ public void onSelectedChanged(RecyclerView.ViewHolder viewHolder,
+ int actionState) {
+ if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
+ mAdapter.onRowSelected(viewHolder);
+ }
+ super.onSelectedChanged(viewHolder, actionState);
+ }
+
+ @Override
+ public void clearView(@NonNull RecyclerView recyclerView,
+ @NonNull RecyclerView.ViewHolder viewHolder) {
+ super.clearView(recyclerView, viewHolder);
+ mAdapter.onRowClear(viewHolder);
+ }
+
+ public interface ItemTouchHelperContract {
+ void onRowMoved(int fromPosition, int toPosition);
+ void onRowSelected(RecyclerView.ViewHolder myViewHolder);
+ void onRowClear(RecyclerView.ViewHolder myViewHolder);
+ }
+
+}
+
diff --git a/app/src/main/res/drawable/ic_drag_handle.xml b/app/src/main/res/drawable/ic_drag_handle.xml
new file mode 100644
index 0000000..88fe5b4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_drag_handle.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_network_camera.xml b/app/src/main/res/drawable/ic_network_camera.xml
new file mode 100644
index 0000000..02f1cc5
--- /dev/null
+++ b/app/src/main/res/drawable/ic_network_camera.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_settings_item.xml b/app/src/main/res/layout/fragment_settings_item.xml
new file mode 100644
index 0000000..81668e7
--- /dev/null
+++ b/app/src/main/res/layout/fragment_settings_item.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_settings_item_list.xml b/app/src/main/res/layout/fragment_settings_item_list.xml
new file mode 100644
index 0000000..8adc437
--- /dev/null
+++ b/app/src/main/res/layout/fragment_settings_item_list.xml
@@ -0,0 +1,13 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml
index 8377101..4967b66 100644
--- a/app/src/main/res/navigation/nav_graph.xml
+++ b/app/src/main/res/navigation/nav_graph.xml
@@ -13,13 +13,19 @@
+ app:destination="@id/SettingsFragment" />
+
+
First Fragment
Second Fragment
+ Videocamera n°{camNo}
+
rtsp://username:password@192.168.1.123:554
Inserisci l\'url dello stream RTSP della tua IP Camera. Nota che questo differisce tra un modello e l\'altro. Consulta il pannello di configurazione o il manuale della tua IP Camera.
Salva
@@ -17,4 +19,5 @@
Questa app è rilasciata sotto licenza GNU GENERAL PUBLIC LICENSE v3+. Puoi ottenerne una copia qui: https://raw.githubusercontent.com/penguin86/ojo/master/LICENSE
Puoi trovare il codice sorgente al repository: https://github.com/penguin86/ojo
Questa app è resa possibile dal magnifico lavoro dei team vlc and vlc-android! Per saperne di più o ottenere il codice sorgente: https://code.videolan.org/videolan/vlc-android
+
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 125df87..cf00d3f 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -1,3 +1,4 @@
16dp
+ 16dp
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c8a5ce6..a7bdbb7 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -4,6 +4,8 @@
First Fragment
Second Fragment
+ Camera {camNo}
+
rtsp://username:password@192.168.1.123:554
Please insert your camera\'s RTSP stream. Note that the URL differs from camera to camera: you can find the complete URL in your camera\'s settings or user manual.
Save