From 8dc6da8b98d8adccb9f49570861c33792d24acf9 Mon Sep 17 00:00:00 2001 From: "Daniele Verducci (Slimpenguin)" Date: Fri, 21 Jan 2022 08:56:04 +0100 Subject: [PATCH] WIP camera list --- .idea/compiler.xml | 2 +- .idea/misc.xml | 4 +- app/build.gradle | 2 + app/src/main/AndroidManifest.xml | 4 +- .../ojo/ui/SettingsFragment.java | 61 ++++++++++ .../adapters/SettingsRecyclerViewAdapter.java | 106 ++++++++++++++++++ .../ojo/utils/ItemMoveCallback.java | 67 +++++++++++ app/src/main/res/drawable/ic_drag_handle.xml | 10 ++ .../main/res/drawable/ic_network_camera.xml | 16 +++ .../res/layout/fragment_settings_item.xml | 53 +++++++++ .../layout/fragment_settings_item_list.xml | 13 +++ app/src/main/res/navigation/nav_graph.xml | 8 +- app/src/main/res/values-it/strings.xml | 3 + app/src/main/res/values/dimens.xml | 1 + app/src/main/res/values/strings.xml | 2 + 15 files changed, 347 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/it/danieleverducci/ojo/ui/SettingsFragment.java create mode 100644 app/src/main/java/it/danieleverducci/ojo/ui/adapters/SettingsRecyclerViewAdapter.java create mode 100644 app/src/main/java/it/danieleverducci/ojo/utils/ItemMoveCallback.java create mode 100644 app/src/main/res/drawable/ic_drag_handle.xml create mode 100644 app/src/main/res/drawable/ic_network_camera.xml create mode 100644 app/src/main/res/layout/fragment_settings_item.xml create mode 100644 app/src/main/res/layout/fragment_settings_item_list.xml 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