Working tiles
This commit is contained in:
parent
876ab519e9
commit
12b243d710
@ -3,6 +3,7 @@
|
|||||||
<component name="DesignSurface">
|
<component name="DesignSurface">
|
||||||
<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/fragment_surveillance.xml" value="0.16354166666666667" />
|
<entry key="app/src/main/res/layout/fragment_surveillance.xml" value="0.16354166666666667" />
|
||||||
</map>
|
</map>
|
||||||
</option>
|
</option>
|
||||||
|
@ -13,6 +13,8 @@ android {
|
|||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0"
|
versionName "1.0"
|
||||||
|
|
||||||
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".ui.MainActivity"
|
android:name=".ui.MainActivity"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:theme="@style/Theme.Ojo.NoActionBar"
|
android:theme="@style/Theme.Ojo"
|
||||||
android:screenOrientation="landscape">
|
android:screenOrientation="landscape">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
@ -21,7 +21,6 @@ import android.view.MenuItem;
|
|||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
private AppBarConfiguration appBarConfiguration;
|
|
||||||
private ActivityMainBinding binding;
|
private ActivityMainBinding binding;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -31,11 +30,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
binding = ActivityMainBinding.inflate(getLayoutInflater());
|
binding = ActivityMainBinding.inflate(getLayoutInflater());
|
||||||
setContentView(binding.getRoot());
|
setContentView(binding.getRoot());
|
||||||
|
|
||||||
setSupportActionBar(binding.toolbar);
|
|
||||||
|
|
||||||
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
|
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
|
||||||
appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build();
|
|
||||||
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
|
|
||||||
|
|
||||||
binding.fab.setOnClickListener(new View.OnClickListener() {
|
binding.fab.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
@ -45,33 +40,4 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
|
||||||
// Inflate the menu; this adds items to the action bar if it is present.
|
|
||||||
getMenuInflater().inflate(R.menu.menu_main, menu);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
// Handle action bar item clicks here. The action bar will
|
|
||||||
// automatically handle clicks on the Home/Up button, so long
|
|
||||||
// as you specify a parent activity in AndroidManifest.xml.
|
|
||||||
int id = item.getItemId();
|
|
||||||
|
|
||||||
//noinspection SimplifiableIfStatement
|
|
||||||
if (id == R.id.action_settings) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onSupportNavigateUp() {
|
|
||||||
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
|
|
||||||
return NavigationUI.navigateUp(navController, appBarConfiguration)
|
|
||||||
|| super.onSupportNavigateUp();
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -24,6 +24,8 @@ import org.videolan.libvlc.Media;
|
|||||||
import org.videolan.libvlc.MediaPlayer;
|
import org.videolan.libvlc.MediaPlayer;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import it.danieleverducci.ojo.R;
|
import it.danieleverducci.ojo.R;
|
||||||
import it.danieleverducci.ojo.databinding.FragmentSurveillanceBinding;
|
import it.danieleverducci.ojo.databinding.FragmentSurveillanceBinding;
|
||||||
@ -31,9 +33,18 @@ import it.danieleverducci.ojo.entities.Camera;
|
|||||||
|
|
||||||
public class SurveillanceFragment extends Fragment implements MediaPlayer.EventListener, IVLCVout.Callback {
|
public class SurveillanceFragment extends Fragment implements MediaPlayer.EventListener, IVLCVout.Callback {
|
||||||
|
|
||||||
private FragmentSurveillanceBinding binding;
|
final static private String[] VLC_OPTIONS = new String[]{
|
||||||
|
"--aout=opensles",
|
||||||
|
"--audio-time-stretch", // time stretching
|
||||||
|
"-vvv", // verbosity
|
||||||
|
"--aout=opensles",
|
||||||
|
"--avcodec-codec=h264",
|
||||||
|
"--file-logging",
|
||||||
|
"--logfile=vlc-log.txt"
|
||||||
|
};
|
||||||
|
|
||||||
private CameraView cameraView;
|
private FragmentSurveillanceBinding binding;
|
||||||
|
private List<CameraView> cameraViews = new ArrayList<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(
|
public View onCreateView(
|
||||||
@ -59,30 +70,53 @@ public class SurveillanceFragment extends Fragment implements MediaPlayer.EventL
|
|||||||
|
|
||||||
// Start stream on surface view 1
|
// Start stream on surface view 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
List<Camera> cc = new ArrayList<>();
|
||||||
String rtspUrl = "rtsp://admin:Du4gdtPmXGCWT29@192.168.1.200:554/Streaming/Channels/101";
|
String rtspUrl = "rtsp://admin:Du4gdtPmXGCWT29@192.168.1.200:554/Streaming/Channels/101";
|
||||||
Camera camera = new Camera("Camera 1", rtspUrl);
|
Camera camera = new Camera("Camera 1", rtspUrl);
|
||||||
|
cc.add(camera);
|
||||||
|
rtspUrl = "rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov";
|
||||||
|
camera = new Camera("Bunny", rtspUrl);
|
||||||
|
cc.add(camera);
|
||||||
|
rtspUrl = "rtsp://demo:demo@ipvmdemo.dyndns.org:5541/onvif-media/media.amp?profile=profile_1_h264&sessiontimeout=60&streamtype=unicast";
|
||||||
|
camera = new Camera("Ipvm Demo", rtspUrl);
|
||||||
|
cc.add(camera);
|
||||||
|
|
||||||
ArrayList<String> options = new ArrayList<String>();
|
|
||||||
options.add("--aout=opensles");
|
|
||||||
options.add("--audio-time-stretch"); // time stretching
|
|
||||||
options.add("-vvv"); // verbosity
|
|
||||||
options.add("--aout=opensles");
|
|
||||||
options.add("--avcodec-codec=h264");
|
|
||||||
options.add("--file-logging");
|
|
||||||
options.add("--logfile=vlc-log.txt");
|
|
||||||
|
|
||||||
LibVLC libvlc = new LibVLC(getContext(), options);
|
int elemsPerSide = calcGridSideElements(cc.size());
|
||||||
|
int camIdx = 0;
|
||||||
|
for (int r = 0; r < elemsPerSide; r++) {
|
||||||
|
// Create row and add to row container
|
||||||
|
LinearLayout row = new LinearLayout(getContext());
|
||||||
|
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||||
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
0,
|
||||||
|
1.0f
|
||||||
|
);
|
||||||
|
binding.gridRowContainer.addView(row, params);
|
||||||
|
// Add camera viewers to the row
|
||||||
|
for (int c = 0; c < elemsPerSide; c++) {
|
||||||
|
if ( camIdx < cc.size() ) {
|
||||||
|
Camera cam = cc.get(camIdx);
|
||||||
|
addCameraView(cam, row);
|
||||||
|
} else {
|
||||||
|
// Cameras are less than the maximum number of cells in grid: fill remaining cells with empty views
|
||||||
|
View ev = new View(getContext());
|
||||||
|
ev.setBackgroundColor(Color.BLACK);
|
||||||
|
LinearLayout.LayoutParams evParams = new LinearLayout.LayoutParams(
|
||||||
|
0,
|
||||||
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
1.0f
|
||||||
|
);
|
||||||
|
row.addView(ev, evParams);
|
||||||
|
}
|
||||||
|
camIdx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
cameraView = new CameraView(
|
|
||||||
getContext(),
|
|
||||||
libvlc,
|
|
||||||
camera,
|
|
||||||
this
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add to layout
|
|
||||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(1000, 500);
|
|
||||||
binding.gridRowContainer.addView(cameraView.surfaceView, params);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -92,7 +126,27 @@ public class SurveillanceFragment extends Fragment implements MediaPlayer.EventL
|
|||||||
public void onStart() {
|
public void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
|
|
||||||
cameraView.startPlayback();
|
for (CameraView cv : cameraViews) {
|
||||||
|
cv.startPlayback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
|
||||||
|
for (CameraView cv : cameraViews) {
|
||||||
|
cv.pausePlayback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
|
||||||
|
for (CameraView cv : cameraViews) {
|
||||||
|
cv.destroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -122,21 +176,50 @@ public class SurveillanceFragment extends Fragment implements MediaPlayer.EventL
|
|||||||
public void onSurfacesDestroyed(IVLCVout vlcVout) {
|
public void onSurfacesDestroyed(IVLCVout vlcVout) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addCameraView(Camera camera) {
|
private void addCameraView(Camera camera, LinearLayout rowContainer) {
|
||||||
|
CameraView cv = new CameraView(
|
||||||
|
getContext(),
|
||||||
|
camera,
|
||||||
|
this
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add to layout
|
||||||
|
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||||
|
0,
|
||||||
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
1.0f
|
||||||
|
);
|
||||||
|
rowContainer.addView(cv.surfaceView, params);
|
||||||
|
|
||||||
|
cameraViews.add(cv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of elements per side needed to create a grid that can contain the provided elements number.
|
||||||
|
* Es: to display 3 elements is needed a 4-element grid, with 2 elements per side (a 2x2 grid)
|
||||||
|
* Es: to display 7 elements is needed a 9-element grid, with 3 elements per side (a 3x3 grid)
|
||||||
|
* @param elements
|
||||||
|
*/
|
||||||
|
private int calcGridSideElements(int elements) {
|
||||||
|
return (int)(Math.ceil(Math.sqrt(elements)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains all entities (views and java entities) related to a camera stream viewer
|
||||||
|
*/
|
||||||
private class CameraView {
|
private class CameraView {
|
||||||
protected SurfaceView surfaceView;
|
protected SurfaceView surfaceView;
|
||||||
protected MediaPlayer mediaPlayer;
|
protected MediaPlayer mediaPlayer;
|
||||||
protected IVLCVout ivlcVout;
|
protected IVLCVout ivlcVout;
|
||||||
protected Camera camera;
|
protected Camera camera;
|
||||||
protected LibVLC libvlc;
|
protected LibVLC libvlc;
|
||||||
|
protected IVLCVout.Callback callback;
|
||||||
|
|
||||||
public CameraView(Context context, LibVLC libvlc, Camera camera, IVLCVout.Callback callback) {
|
public CameraView(Context context, Camera camera, IVLCVout.Callback callback) {
|
||||||
this.camera = camera;
|
this.camera = camera;
|
||||||
this.libvlc = libvlc;
|
this.callback = callback;
|
||||||
|
this.libvlc = new LibVLC(context, new ArrayList<>(Arrays.asList(VLC_OPTIONS)));
|
||||||
|
|
||||||
surfaceView = new SurfaceView(context);
|
surfaceView = new SurfaceView(context);
|
||||||
SurfaceHolder holder = surfaceView.getHolder();
|
SurfaceHolder holder = surfaceView.getHolder();
|
||||||
@ -150,7 +233,7 @@ public class SurveillanceFragment extends Fragment implements MediaPlayer.EventL
|
|||||||
// Set up video output
|
// Set up video output
|
||||||
ivlcVout = mediaPlayer.getVLCVout();
|
ivlcVout = mediaPlayer.getVLCVout();
|
||||||
ivlcVout.setVideoView(surfaceView);
|
ivlcVout.setVideoView(surfaceView);
|
||||||
ivlcVout.addCallback(callback);
|
ivlcVout.addCallback(this.callback);
|
||||||
ivlcVout.attachViews();
|
ivlcVout.attachViews();
|
||||||
|
|
||||||
// Load media and start playing
|
// Load media and start playing
|
||||||
@ -167,10 +250,28 @@ public class SurveillanceFragment extends Fragment implements MediaPlayer.EventL
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the playback.
|
* Starts the playback.
|
||||||
* This must be called after the view has been laid out
|
|
||||||
*/
|
*/
|
||||||
public void startPlayback() {
|
public void startPlayback() {
|
||||||
mediaPlayer.play();
|
mediaPlayer.play();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pauses the playback.
|
||||||
|
*/
|
||||||
|
public void pausePlayback() {
|
||||||
|
mediaPlayer.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys the object and frees the memory
|
||||||
|
*/
|
||||||
|
public void destroy() {
|
||||||
|
mediaPlayer.stop();
|
||||||
|
final IVLCVout vout = mediaPlayer.getVLCVout();
|
||||||
|
vout.removeCallback(this.callback);
|
||||||
|
vout.detachViews();
|
||||||
|
libvlc.release();
|
||||||
|
libvlc = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
10
app/src/main/res/drawable/ic_add_camera.xml
Normal file
10
app/src/main/res/drawable/ic_add_camera.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="M3,4V1h2v3h3v2H5v3H3V6H0V4H3zM6,10V7h3V4h7l1.83,2H21c1.1,0 2,0.9 2,2v12c0,1.1 -0.9,2 -2,2H5c-1.1,0 -2,-0.9 -2,-2V10H6zM13,19c2.76,0 5,-2.24 5,-5s-2.24,-5 -5,-5s-5,2.24 -5,5S10.24,19 13,19zM9.8,14c0,1.77 1.43,3.2 3.2,3.2s3.2,-1.43 3.2,-3.2s-1.43,-3.2 -3.2,-3.2S9.8,12.23 9.8,14z"/>
|
||||||
|
</vector>
|
@ -6,20 +6,6 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".ui.MainActivity">
|
tools:context=".ui.MainActivity">
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:theme="@style/Theme.Ojo.AppBarOverlay">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="?attr/actionBarSize"
|
|
||||||
android:background="?attr/colorPrimary"
|
|
||||||
app:popupTheme="@style/Theme.Ojo.PopupOverlay" />
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
|
|
||||||
<include layout="@layout/content_main" />
|
<include layout="@layout/content_main" />
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
@ -28,6 +14,6 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="bottom|end"
|
android:layout_gravity="bottom|end"
|
||||||
android:layout_margin="@dimen/fab_margin"
|
android:layout_margin="@dimen/fab_margin"
|
||||||
app:srcCompat="@android:drawable/ic_dialog_email" />
|
app:srcCompat="@drawable/ic_add_camera" />
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
@ -1,10 +0,0 @@
|
|||||||
<menu 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"
|
|
||||||
tools:context="it.danieleverducci.ojo.ui.MainActivity">
|
|
||||||
<item
|
|
||||||
android:id="@+id/action_settings"
|
|
||||||
android:orderInCategory="100"
|
|
||||||
android:title="@string/action_settings"
|
|
||||||
app:showAsAction="never" />
|
|
||||||
</menu>
|
|
@ -1,6 +1,8 @@
|
|||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<!-- Base application theme. -->
|
<!-- Base application theme. -->
|
||||||
<style name="Theme.Ojo" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
<style name="Theme.Ojo" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||||
|
<item name="windowActionBar">false</item>
|
||||||
|
<item name="windowNoTitle">true</item>
|
||||||
<!-- Primary brand color. -->
|
<!-- Primary brand color. -->
|
||||||
<item name="colorPrimary">@color/purple_500</item>
|
<item name="colorPrimary">@color/purple_500</item>
|
||||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||||
@ -14,12 +16,4 @@
|
|||||||
<!-- Customize your theme here. -->
|
<!-- Customize your theme here. -->
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="Theme.Ojo.NoActionBar">
|
|
||||||
<item name="windowActionBar">false</item>
|
|
||||||
<item name="windowNoTitle">true</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="Theme.Ojo.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
|
|
||||||
|
|
||||||
<style name="Theme.Ojo.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
|
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in New Issue
Block a user