Wip add camera from list

This commit is contained in:
Daniele Verducci (Slimpenguin) 2022-02-01 09:22:43 +01:00
parent 60791459c0
commit b1fa8713e2
14 changed files with 162 additions and 57 deletions

View File

@ -4,10 +4,11 @@
<option name="filePathToZoomLevelMap"> <option name="filePathToZoomLevelMap">
<map> <map>
<entry key="app/src/main/res/layout/activity_main.xml" value="0.45" /> <entry key="app/src/main/res/layout/activity_main.xml" value="0.45" />
<entry key="app/src/main/res/layout/fragment_add_stream.xml" value="0.536" /> <entry key="app/src/main/res/layout/fragment_add_stream.xml" value="0.524901185770751" />
<entry key="app/src/main/res/layout/fragment_settings_item.xml" value="0.536" /> <entry key="app/src/main/res/layout/fragment_settings_item.xml" value="0.536" />
<entry key="app/src/main/res/layout/fragment_settings_item_list.xml" value="0.19010416666666666" /> <entry key="app/src/main/res/layout/fragment_settings_item_list.xml" value="0.4" />
<entry key="app/src/main/res/layout/fragment_surveillance.xml" value="0.16354166666666667" /> <entry key="app/src/main/res/layout/fragment_surveillance.xml" value="0.16354166666666667" />
<entry key="app/src/main/res/menu/settings_menu.xml" value="0.3458333333333333" />
</map> </map>
</option> </option>
</component> </component>

View File

@ -11,6 +11,14 @@ public class Camera implements Serializable {
this.rtspUrl = rtspUrl; this.rtspUrl = rtspUrl;
} }
public void setName(String name) {
this.name = name;
}
public void setRtspUrl(String rtspUrl) {
this.rtspUrl = rtspUrl;
}
public String getName() { public String getName() {
return name; return name;
} }

View File

@ -44,11 +44,18 @@ public class MainActivity extends AppCompatActivity {
} }
public void navigateToFragment(int actionId) { public void navigateToFragment(int actionId) {
navigateToFragment(actionId, null);
}
public void navigateToFragment(int actionId, Bundle bundle) {
if (navController == null) { if (navController == null) {
Log.e(TAG, "Not initialized"); Log.e(TAG, "Not initialized");
return; return;
} }
if (bundle != null)
navController.navigate(actionId, bundle);
else
navController.navigate(actionId); navController.navigate(actionId);
} }
} }

View File

@ -3,6 +3,8 @@ package it.danieleverducci.ojo.ui;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.ItemTouchHelper;
@ -10,6 +12,7 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -17,6 +20,7 @@ import java.util.List;
import it.danieleverducci.ojo.R; import it.danieleverducci.ojo.R;
import it.danieleverducci.ojo.Settings; import it.danieleverducci.ojo.Settings;
import it.danieleverducci.ojo.databinding.FragmentSettingsItemListBinding;
import it.danieleverducci.ojo.entities.Camera; import it.danieleverducci.ojo.entities.Camera;
import it.danieleverducci.ojo.ui.adapters.SettingsRecyclerViewAdapter; import it.danieleverducci.ojo.ui.adapters.SettingsRecyclerViewAdapter;
import it.danieleverducci.ojo.utils.ItemMoveCallback; import it.danieleverducci.ojo.utils.ItemMoveCallback;
@ -26,28 +30,20 @@ import it.danieleverducci.ojo.utils.ItemMoveCallback;
*/ */
public class SettingsFragment extends Fragment { public class SettingsFragment extends Fragment {
public SettingsFragment() { private FragmentSettingsItemListBinding binding;
}
public static SettingsFragment newInstance() {
SettingsFragment fragment = new SettingsFragment();
return fragment;
}
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_settings_item_list, container, false); binding = FragmentSettingsItemListBinding.inflate(inflater, container, false);
// Load cameras // Load cameras
Settings settings = Settings.fromDisk(getContext()); Settings settings = Settings.fromDisk(getContext());
List<Camera> cams = settings.getCameras(); List<Camera> cams = settings.getCameras();
// Set the adapter // Set the adapter
if (view instanceof RecyclerView) { RecyclerView recyclerView = binding.list;
Context context = view.getContext(); recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
RecyclerView recyclerView = (RecyclerView) view;
recyclerView.setLayoutManager(new LinearLayoutManager(context));
SettingsRecyclerViewAdapter adapter = new SettingsRecyclerViewAdapter(cams); SettingsRecyclerViewAdapter adapter = new SettingsRecyclerViewAdapter(cams);
ItemTouchHelper.Callback callback = ItemTouchHelper.Callback callback =
new ItemMoveCallback(adapter); new ItemMoveCallback(adapter);
@ -58,11 +54,27 @@ public class SettingsFragment extends Fragment {
// Onclick listener // Onclick listener
adapter.setOnClickListener(new SettingsRecyclerViewAdapter.OnClickListener() { adapter.setOnClickListener(new SettingsRecyclerViewAdapter.OnClickListener() {
@Override @Override
public void onItemClick(Camera c) { public void onItemClick(int pos) {
((MainActivity)getActivity()).navigateToFragment(R.id.action_homeToSettings); Bundle b = new Bundle();
b.putInt(StreamUrlFragment.ARG_CAMERA, pos);
((MainActivity)getActivity()).navigateToFragment(R.id.action_settingsToCameraUrl, b);
} }
}); });
return binding.getRoot();
} }
return view;
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
binding.settingsToolbar.inflateMenu(R.menu.settings_menu);
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == R.id.menuitem_add_camera) {
((MainActivity)getActivity()).navigateToFragment(R.id.action_settingsToCameraUrl);
return true;
}
return super.onOptionsItemSelected(item);
} }
} }

View File

@ -6,6 +6,7 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.fragment.NavHostFragment;
@ -16,9 +17,20 @@ import it.danieleverducci.ojo.Settings;
import it.danieleverducci.ojo.databinding.FragmentAddStreamBinding; import it.danieleverducci.ojo.databinding.FragmentAddStreamBinding;
import it.danieleverducci.ojo.entities.Camera; import it.danieleverducci.ojo.entities.Camera;
public class AddStreamFragment extends Fragment { public class StreamUrlFragment extends Fragment {
public static final String ARG_CAMERA = "arg_camera";
private FragmentAddStreamBinding binding; private FragmentAddStreamBinding binding;
private Settings settings;
private Integer selectedCamera = null;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load existing settings (if any)
settings = Settings.fromDisk(getContext());
}
@Override @Override
public View onCreateView( public View onCreateView(
@ -27,6 +39,18 @@ public class AddStreamFragment extends Fragment {
) { ) {
binding = FragmentAddStreamBinding.inflate(inflater, container, false); binding = FragmentAddStreamBinding.inflate(inflater, container, false);
// If passed an url, fill the details
Bundle args = getArguments();
if (args != null && args.containsKey(ARG_CAMERA)) {
this.selectedCamera = args.getInt(ARG_CAMERA);
Camera c = settings.getCameras().get(this.selectedCamera);
binding.streamName.setText(c.getName());
binding.streamName.setHint(getContext().getString(R.string.stream_list_default_camera_name).replace("{camNo}", (this.selectedCamera+1)+""));
binding.streamUrl.setText(c.getRtspUrl());
}
return binding.getRoot(); return binding.getRoot();
} }
@ -45,10 +69,19 @@ public class AddStreamFragment extends Fragment {
return; return;
} }
// Load existing settings (if any) // Name can be empty
Settings settings = Settings.fromDisk(getContext()); String name = binding.streamName.getText().toString();
if (StreamUrlFragment.this.selectedCamera != null) {
// Update camera
Camera c = settings.getCameras().get(StreamUrlFragment.this.selectedCamera);
c.setName(name);
c.setRtspUrl(url);
} else {
// Add stream to list // Add stream to list
settings.addCamera(new Camera("", url)); settings.addCamera(new Camera(name, url));
}
// Save // Save
if (!settings.save()) { if (!settings.save()) {
Snackbar.make(view, R.string.add_stream_error_saving, Snackbar.LENGTH_LONG).show(); Snackbar.make(view, R.string.add_stream_error_saving, Snackbar.LENGTH_LONG).show();
@ -56,7 +89,7 @@ public class AddStreamFragment extends Fragment {
} }
// Back to first fragment // Back to first fragment
NavHostFragment.findNavController(AddStreamFragment.this) NavHostFragment.findNavController(StreamUrlFragment.this)
.popBackStack(); .popBackStack();
} }
}); });

View File

@ -39,12 +39,6 @@ public class SettingsRecyclerViewAdapter extends RecyclerView.Adapter<SettingsRe
} }
return false; return false;
}); });
vh.root.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
clickListener.onItemClick((Camera)view.getTag());
}
});
return vh; return vh;
} }
@ -55,8 +49,13 @@ public class SettingsRecyclerViewAdapter extends RecyclerView.Adapter<SettingsRe
cameraName = holder.name.getContext().getString(R.string.stream_list_default_camera_name).replace("{camNo}", (position+1)+""); cameraName = holder.name.getContext().getString(R.string.stream_list_default_camera_name).replace("{camNo}", (position+1)+"");
holder.name.setText(cameraName); holder.name.setText(cameraName);
holder.url.setText(mValues.get(position).getRtspUrl()); holder.url.setText(mValues.get(position).getRtspUrl());
// Save item
holder.root.setTag(mValues.get(position)); holder.root.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
clickListener.onItemClick(position);
}
});
} }
@Override @Override
@ -119,6 +118,6 @@ public class SettingsRecyclerViewAdapter extends RecyclerView.Adapter<SettingsRe
} }
public interface OnClickListener { public interface OnClickListener {
void onItemClick(Camera c); void onItemClick(int pos);
} }
} }

View File

@ -22,6 +22,22 @@
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/add_stream_name"/>
<EditText
android:id="@+id/stream_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:hint="@string/add_stream_placeholder_url"
android:lines="1"
android:maxLines="1"
android:inputType="textUri"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:text="@string/add_stream"/> android:text="@string/add_stream"/>
<EditText <EditText

View File

@ -1,7 +1,18 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/settingsToolbar"
app:title="@string/app_name" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list" android:id="@+id/list"
android:name="it.danieleverducci.ojo.ui.SettingsFragment" android:name="it.danieleverducci.ojo.ui.SettingsFragment"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -11,3 +22,5 @@
app:layoutManager="LinearLayoutManager" app:layoutManager="LinearLayoutManager"
tools:context=".ui.SettingsFragment" tools:context=".ui.SettingsFragment"
tools:listitem="@layout/fragment_settings_item" /> tools:listitem="@layout/fragment_settings_item" />
</LinearLayout>

View File

@ -6,7 +6,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
tools:context=".ui.AddStreamFragment" tools:context=".ui.StreamUrlFragment"
android:background="@color/purple_500"> android:background="@color/purple_500">
</LinearLayout> </LinearLayout>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/menuitem_add_camera"
android:icon="@drawable/ic_add_camera"
android:title="@string/add_stream"
app:showAsAction="always"/>
</menu>

View File

@ -17,7 +17,7 @@
</fragment> </fragment>
<fragment <fragment
android:id="@+id/CameraUrlFragment" android:id="@+id/CameraUrlFragment"
android:name="it.danieleverducci.ojo.ui.AddStreamFragment" android:name="it.danieleverducci.ojo.ui.StreamUrlFragment"
android:label="@string/second_fragment_label" android:label="@string/second_fragment_label"
tools:layout="@layout/fragment_add_stream"> tools:layout="@layout/fragment_add_stream">
<action <action

View File

@ -5,15 +5,18 @@
<string name="first_fragment_label">First Fragment</string> <string name="first_fragment_label">First Fragment</string>
<string name="second_fragment_label">Second Fragment</string> <string name="second_fragment_label">Second Fragment</string>
<string name="stream_list_default_camera_name">Videocamera n°{camNo}</string> <string name="stream_list_default_camera_name">Videocamera senza nome n°{camNo}</string>
<string name="add_stream_placeholder_url">rtsp://username:password@192.168.1.123:554</string> <string name="add_stream_placeholder_url">rtsp://username:password@192.168.1.123:554</string>
<string name="add_stream_name">Nome della IP Camera</string>
<string name="add_stream">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.</string> <string name="add_stream">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.</string>
<string name="add_stream_save">Salva</string> <string name="add_stream_save">Salva</string>
<string name="add_stream_invalid_url">L\'URL RTSP non è valido</string> <string name="add_stream_invalid_url">L\'URL RTSP non è valido</string>
<string name="add_stream_invalid_url_dismiss">Chiudi</string> <string name="add_stream_invalid_url_dismiss">Chiudi</string>
<string name="add_stream_error_saving">Si è verificato un errore durante il salvataggio della configurazione.</string> <string name="add_stream_error_saving">Si è verificato un errore durante il salvataggio della configurazione.</string>
<string name="menu_add_camera">Aggiungi</string>
<string name="app_info_title">Informazioni su Ojo</string> <string name="app_info_title">Informazioni su Ojo</string>
<string name="app_info_creator_desc">Creato da Daniele Verducci.</string> <string name="app_info_creator_desc">Creato da Daniele Verducci.</string>
<string name="app_info_license_desc">Questa app è rilasciata sotto licenza GNU GENERAL PUBLIC LICENSE v3+. Puoi ottenerne una copia qui: https://raw.githubusercontent.com/penguin86/ojo/master/LICENSE</string> <string name="app_info_license_desc">Questa app è rilasciata sotto licenza GNU GENERAL PUBLIC LICENSE v3+. Puoi ottenerne una copia qui: https://raw.githubusercontent.com/penguin86/ojo/master/LICENSE</string>

View File

@ -4,15 +4,18 @@
<string name="first_fragment_label">First Fragment</string> <string name="first_fragment_label">First Fragment</string>
<string name="second_fragment_label">Second Fragment</string> <string name="second_fragment_label">Second Fragment</string>
<string name="stream_list_default_camera_name">Camera {camNo}</string> <string name="stream_list_default_camera_name">Unnamed camera {camNo}</string>
<string name="add_stream_placeholder_url">rtsp://username:password@192.168.1.123:554</string> <string name="add_stream_placeholder_url">rtsp://username:password@192.168.1.123:554</string>
<string name="add_stream_name">Camera name</string>
<string name="add_stream">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.</string> <string name="add_stream">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.</string>
<string name="add_stream_save">Save</string> <string name="add_stream_save">Save</string>
<string name="add_stream_invalid_url">Invalid RTSP url</string> <string name="add_stream_invalid_url">Invalid RTSP url</string>
<string name="add_stream_invalid_url_dismiss">Dismiss</string> <string name="add_stream_invalid_url_dismiss">Dismiss</string>
<string name="add_stream_error_saving">An error has occurred while saving configuration</string> <string name="add_stream_error_saving">An error has occurred while saving configuration</string>
<string name="menu_add_camera">Add</string>
<string name="app_info_title">About Ojo</string> <string name="app_info_title">About Ojo</string>
<string name="app_info_creator_desc">Created by Daniele Verducci.</string> <string name="app_info_creator_desc">Created by Daniele Verducci.</string>
<string name="app_info_license_desc">This application is licensed under the GNU GENERAL PUBLIC LICENSE v3+. You can obtain a copy here: https://raw.githubusercontent.com/penguin86/ojo/master/LICENSE</string> <string name="app_info_license_desc">This application is licensed under the GNU GENERAL PUBLIC LICENSE v3+. You can obtain a copy here: https://raw.githubusercontent.com/penguin86/ojo/master/LICENSE</string>

View File

@ -14,4 +14,6 @@
<item name="colorOnSecondary">@color/white</item> <item name="colorOnSecondary">@color/white</item>
</style> </style>
</resources> </resources>