Compare commits

...

30 Commits

Author SHA1 Message Date
Daniele Verducci (Slimpenguin)
cf357b462c Bumped version, changelogs 2024-04-22 08:38:38 +02:00
Daniele Verducci (Slimpenguin)
4646da3cd2 Translators in info page 2024-04-22 08:32:59 +02:00
Daniele Verducci
7d166f9d51
Merge pull request #35 from yurtpage/fastlane-i18n
ru i18n
2024-04-22 08:12:30 +02:00
Yurt Page
3848cc7759 values-ru 2024-04-18 23:43:07 +03:00
Yurt Page
d4b77d2d33 fastlane: i18n ru 2024-04-18 23:25:14 +03:00
Yurt Page
042699ef77 fastlane: full_description.txt fix typos 2024-04-18 23:24:57 +03:00
Daniele Verducci
cd4646c7c4
Spacing README.md 2024-02-10 10:25:06 +01:00
Daniele Verducci
6a1499d356
Update README.md with new contributors 2024-02-10 10:24:32 +01:00
Daniele Verducci
dd59aa129b
Merge pull request #31 from free-bots/feature/stream-focus-highlighting
added border for selected camera view
2024-02-10 10:20:32 +01:00
Daniele Verducci
96a91a6c3d
Merge pull request #32 from free-bots/feature/intent
added intent for direct camera access and leanback support
2024-02-10 10:19:55 +01:00
free-bots
7a52c24fea
added description for intent usage 2024-02-10 09:07:13 +01:00
free-bots
23535448a5
added intent for direct camera access and leanback support 2024-02-08 17:21:11 +01:00
free-bots
482f69e003
added border for selected camera view 2024-02-06 19:03:13 +01:00
Daniele Verducci (Slimpenguin)
4aa17ec8f3 Release 0.1.3 2024-01-26 08:53:20 +01:00
Daniele Verducci (Slimpenguin)
0fd2474e57 Trying to backport Ojo to Android 4.2 2024-01-23 08:12:32 +01:00
Daniele Verducci
da78648064
Merge pull request #30 from jayfan0/add-intent
Deep link app
2024-01-19 08:05:00 +01:00
jayfan0
906698cf53 Deep link app to be able to open from external source with ojo://view 2024-01-17 19:11:44 +00:00
Daniele Verducci (Slimpenguin)
0f13e23f09 Bumped version 2023-04-28 09:09:51 +02:00
Daniele Verducci (Slimpenguin)
02bac4671e Separated Settings activity, fullscreen under notch in camera wall 2023-03-10 08:07:54 +01:00
Daniele Verducci (Slimpenguin)
9d6d97b10f Fixed missing FAB icon on Android 5 2023-02-21 08:57:02 +01:00
Daniele Verducci (Slimpenguin)
5ec000d1c9 Fixed crash on Android 5 due to unavailable resource 2023-02-21 08:42:01 +01:00
Daniele Verducci (Slimpenguin)
d3cb5abe27 True fullscreen under notches 2023-02-21 08:32:04 +01:00
Daniele Verducci (Slimpenguin)
66d0a1081a Bumped version number 2023-02-15 09:14:51 +01:00
Daniele Verducci (Slimpenguin)
1647895a2a Updated build 2023-02-15 09:12:52 +01:00
Daniele Verducci
1ad6500882
Merge pull request #22 from davquar/stable
Fix full screen view on Android 13
2023-02-15 09:08:01 +01:00
Davide Quaranta
dfb30bef9e Fix bad single camera view on Android 13 2023-01-07 18:36:36 +01:00
Daniele Verducci (Slimpenguin)
2f9af2e1f1 Fixed #11 2022-05-13 08:54:38 +02:00
Daniele Verducci
333b44cba3
Merge pull request #14 from brenard/stable
Allow for uneven grids (columns != rows)
2022-05-13 06:29:44 +00:00
Benjamin Renard
182a9a9b89 Remove dependency on javafx 2022-05-02 19:23:21 +02:00
Benjamin Renard
ff5b6b8101 Improve surveillance grid dimensions calculation method 2022-05-02 15:20:26 +02:00
80 changed files with 510 additions and 163 deletions

2
.idea/compiler.xml generated
View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" />
<bytecodeTargetLevel target="17" />
</component>
</project>

10
.idea/deploymentTargetDropDown.xml generated Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetDropDown">
<value>
<entry key="app">
<State />
</entry>
</value>
</component>
</project>

10
.idea/migrations.xml generated Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectMigrations">
<option name="MigrateToGradleLocalJavaHome">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</component>
</project>

2
.idea/misc.xml generated
View File

@ -17,7 +17,7 @@
</option>
</component>
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View File

@ -14,5 +14,21 @@ The maximum number of cameras is determined by the device's capabilities.
The stream decoding and rendering is demanded to [VLC's library](https://code.videolan.org/videolan/vlc-android): without their effort this app wouldn't be possible.
This app was specifically developed for F-Droid, as I couldn't find any open source RTSP vievers in the main repository.
The app can be opened deeplinking to url ojo://view.
To open the app with focus on a specific camera, you can use an intent (it.danieleverducci.ojo.OPEN_CAMERA) to specify which camera you want to view.
The extra argument it.danieleverducci.ojo.CAMERA_NAME will open the app with the camera with the name you specified while adding the camera.
The extra argument it.danieleverducci.ojo.CAMERA_NUMBER starting at 1 could be used as well, if you have multiple cameras with the same name.
See belows example how to use the intent. The flag (-f 268468224) could be useful if you want to switch to an other camera while the app is running.
```shell
adb -s <YOUR_DEVICE> shell am start -a it.danieleverducci.ojo.OPEN_CAMERA -f 268468224 --es it.danieleverducci.ojo.CAMERA_NAME <YOUR_CAMERA_NAME>
adb -s <YOUR_DEVICE> shell am start -a it.danieleverducci.ojo.OPEN_CAMERA -f 268468224 --es it.danieleverducci.ojo.CAMERA_NUMBER <YOUR_CAMERA_NUMBER>
```
![Screenshot 1](media/screenshots/1.png) ![Screenshot 2](media/screenshots/2.png) ![Screenshot 3](media/screenshots/3.png)
## Contributors
- Thanks to [brenard](https://github.com/brenard) for the new grid sizing method
- Thanks to [davquar](https://github.com/davquar) for the fullscreen compatibility fix on Android 11
- Thanks to [jayfan0](https://github.com/jayfan0) for the first deep link implementation
- Thanks to [free-bots](https://github.com/free-bots) for the selection border on Android TV, intents for direct camera access and leanback support

View File

@ -3,15 +3,14 @@ plugins {
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
compileSdkVersion 33
defaultConfig {
applicationId "it.danieleverducci.ojo"
minSdkVersion 21
targetSdkVersion 30
versionCode 4
versionName "0.1.0"
minSdkVersion 15
targetSdkVersion 33
versionCode 9
versionName "0.1.4"
vectorDrawables.useSupportLibrary = true
@ -31,19 +30,19 @@ android {
buildFeatures {
viewBinding true
}
namespace 'it.danieleverducci.ojo'
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.navigation:navigation-fragment:2.3.5'
implementation 'androidx.navigation:navigation-ui:2.3.5'
//implementation 'org.videolan.android:libvlc-all:3.4.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.8.0'
implementation 'androidx.navigation:navigation-fragment:2.5.3'
implementation 'androidx.navigation:navigation-ui:2.5.3'
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'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}

View File

@ -1,18 +1,20 @@
{
"version": 2,
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "it.danieleverducci.ojo",
"applicationId": "it.danieleverducci.ojo.googleplay",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"versionCode": 4,
"versionName": "0.1.0",
"attributes": [],
"versionCode": 6,
"versionName": "0.1.1",
"outputFile": "app-release.apk"
}
]
],
"elementType": "File"
}

View File

@ -1,8 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="it.danieleverducci.ojo">
<manifest xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-feature
android:name="android.software.leanback"
android:required="false" />
<application
android:allowBackup="true"
@ -12,14 +15,31 @@
android:supportsRtl="true"
android:theme="@style/Theme.Ojo">
<activity
android:name=".ui.MainActivity"
android:name=".ui.SettingsActivity"
android:banner="@mipmap/ic_launcher"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.Ojo"
android:configChanges="orientation|screenSize">
android:theme="@style/Theme.Ojo">
</activity>
<activity
android:name=".ui.MainActivity"
android:configChanges="orientation|screenSize"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.Ojo">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="ojo" android:host="view"/>
</intent-filter>
<intent-filter>
<action android:name="it.danieleverducci.ojo.OPEN_CAMERA"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
</application>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -15,4 +15,8 @@ public class SharedPreferencesManager {
SharedPreferences sharedPref = ctx.getSharedPreferences(SP_ROTATION_ENABLED, Context.MODE_PRIVATE);
return sharedPref.getBoolean(SP_ROTATION_ENABLED, false);
}
public static void toggleRotationEnabled(Context ctx) {
saveRotationEnabled(ctx, ! loadRotationEnabled(ctx));
}
}

View File

@ -1,18 +1,13 @@
package it.danieleverducci.ojo.ui;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Build;
import android.os.Bundle;
import android.view.WindowManager;
import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import it.danieleverducci.ojo.R;
import it.danieleverducci.ojo.SharedPreferencesManager;
import it.danieleverducci.ojo.databinding.ActivityMainBinding;
@ -20,35 +15,32 @@ public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private ActivityMainBinding binding;
private NavController navController;
private boolean rotationEnabledSetting;
private OnBackButtonPressedListener onBackButtonPressedListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
rotationEnabledSetting = SharedPreferencesManager.loadRotationEnabled(this);
this.setRequestedOrientation(this.rotationEnabledSetting ? ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR : ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
// Interface can go below notches
if (Build.VERSION.SDK_INT >= 28) {
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
);
}
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
// Show FAB only on first fragment
navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
if (destination.getId() == R.id.HomeFragment)
binding.fab.show();
else
binding.fab.hide();
});
binding.fab.setOnClickListener(view -> openSettings());
}
binding.fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
navigateToFragment(R.id.action_homeToSettings);
}
});
@Override
protected void onStart() {
boolean rotationEnabledSetting = SharedPreferencesManager.loadRotationEnabled(this);
this.setRequestedOrientation(rotationEnabledSetting ? ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR : ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
super.onStart();
}
public void setOnBackButtonPressedListener(OnBackButtonPressedListener onBackButtonPressedListener) {
@ -62,28 +54,9 @@ public class MainActivity extends AppCompatActivity {
super.onBackPressed();
}
public void navigateToFragment(int actionId) {
navigateToFragment(actionId, null);
private void openSettings() {
Intent i = new Intent(this, SettingsActivity.class);
startActivity(i);
}
public void navigateToFragment(int actionId, Bundle bundle) {
if (navController == null) {
Log.e(TAG, "Not initialized");
return;
}
if (bundle != null)
navController.navigate(actionId, bundle);
else
navController.navigate(actionId);
}
public boolean getRotationEnabledSetting() {
return this.rotationEnabledSetting;
}
public void toggleRotationEnabledSetting() {
this.rotationEnabledSetting = !this.rotationEnabledSetting;
this.setRequestedOrientation(this.rotationEnabledSetting ? ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR : ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
}

View File

@ -0,0 +1,71 @@
package it.danieleverducci.ojo.ui;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.WindowManager;
import androidx.appcompat.app.AppCompatActivity;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import it.danieleverducci.ojo.R;
import it.danieleverducci.ojo.SharedPreferencesManager;
import it.danieleverducci.ojo.databinding.ActivitySettingsBinding;
public class SettingsActivity extends AppCompatActivity {
private static final String TAG = "SettingsActivity";
private ActivitySettingsBinding binding;
private NavController navController;
private OnBackButtonPressedListener onBackButtonPressedListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivitySettingsBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_settings);
}
public void setOnBackButtonPressedListener(OnBackButtonPressedListener onBackButtonPressedListener) {
this.onBackButtonPressedListener = onBackButtonPressedListener;
}
@Override
public void onBackPressed() {
if (this.onBackButtonPressedListener != null && this.onBackButtonPressedListener.onBackPressed())
return;
super.onBackPressed();
}
public void navigateToFragment(int actionId) {
navigateToFragment(actionId, null);
}
public void navigateToFragment(int actionId, Bundle bundle) {
if (navController == null) {
Log.e(TAG, "Not initialized");
return;
}
try {
if (bundle != null)
navController.navigate(actionId, bundle);
else
navController.navigate(actionId);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Unable to navigate to fragment: " + e.getMessage());
}
}
public void toggleRotationEnabledSetting() {
SharedPreferencesManager.toggleRotationEnabled(this);
}
public boolean getRotationEnabledSetting() {
return SharedPreferencesManager.loadRotationEnabled(this);
}
}

View File

@ -1,6 +1,7 @@
package it.danieleverducci.ojo.ui;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.NonNull;
@ -45,10 +46,12 @@ public class SettingsFragment extends Fragment {
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
// Setup toolbar
binding.settingsToolbar.getOverflowIcon().setTint(Color.WHITE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
binding.settingsToolbar.getOverflowIcon().setTint(Color.WHITE);
}
binding.settingsToolbar.inflateMenu(R.menu.settings_menu);
MenuItem rotMenuItem = binding.settingsToolbar.getMenu().findItem(R.id.menuitem_allow_rotation);
rotMenuItem.setTitle(((MainActivity)getActivity()).getRotationEnabledSetting() ? R.string.menuitem_deny_rotation : R.string.menuitem_allow_rotation);
rotMenuItem.setTitle(((SettingsActivity)getActivity()).getRotationEnabledSetting() ? R.string.menuitem_deny_rotation : R.string.menuitem_allow_rotation);
// Register for item click
binding.settingsToolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
@ -56,15 +59,15 @@ public class SettingsFragment extends Fragment {
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.menuitem_add_camera:
((MainActivity)getActivity()).navigateToFragment(R.id.action_settingsToCameraUrl);
((SettingsActivity)getActivity()).navigateToFragment(R.id.action_settingsToCameraUrl);
return true;
case R.id.menuitem_allow_rotation:
((MainActivity)getActivity()).toggleRotationEnabledSetting();
SharedPreferencesManager.saveRotationEnabled(getContext(), ((MainActivity)getActivity()).getRotationEnabledSetting());
item.setTitle(((MainActivity)getActivity()).getRotationEnabledSetting() ? R.string.menuitem_deny_rotation : R.string.menuitem_allow_rotation);
((SettingsActivity)getActivity()).toggleRotationEnabledSetting();
SharedPreferencesManager.saveRotationEnabled(getContext(), ((SettingsActivity)getActivity()).getRotationEnabledSetting());
item.setTitle(((SettingsActivity)getActivity()).getRotationEnabledSetting() ? R.string.menuitem_deny_rotation : R.string.menuitem_allow_rotation);
return true;
case R.id.menuitem_info:
((MainActivity)getActivity()).navigateToFragment(R.id.action_SettingsToInfoFragment);
((SettingsActivity)getActivity()).navigateToFragment(R.id.action_SettingsToInfoFragment);
return true;
}
return false;
@ -96,7 +99,7 @@ public class SettingsFragment extends Fragment {
public void onItemClick(int pos) {
Bundle b = new Bundle();
b.putInt(StreamUrlFragment.ARG_CAMERA, pos);
((MainActivity)getActivity()).navigateToFragment(R.id.action_settingsToCameraUrl, b);
((SettingsActivity)getActivity()).navigateToFragment(R.id.action_settingsToCameraUrl, b);
}
});
}

View File

@ -1,6 +1,7 @@
package it.danieleverducci.ojo.ui;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@ -12,10 +13,12 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowInsetsController;
import android.view.WindowManager;
import android.widget.LinearLayout;
import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.core.view.WindowInsetsControllerCompat;
import androidx.fragment.app.Fragment;
import org.videolan.libvlc.IVLCVout;
@ -76,7 +79,8 @@ public class SurveillanceFragment extends Fragment {
1.0f
);
hiddenLayoutParams = new LinearLayout.LayoutParams(0, 0);
// 1,1 instead of 0,0 because the latter doesn't work on android 13+
hiddenLayoutParams = new LinearLayout.LayoutParams(1, 1);
binding = FragmentSurveillanceBinding.inflate(inflater, container, false);
return binding.getRoot();
@ -86,24 +90,7 @@ public class SurveillanceFragment extends Fragment {
public void onResume() {
super.onResume();
// Leanback mode (fullscreen)
Window window = getActivity().getWindow();
if (window != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
final WindowInsetsController controller = window.getInsetsController();
if (controller != null)
controller.hide(WindowInsets.Type.statusBars());
} else {
window.getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_IMMERSIVE
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
}
}
leanbackMode(true);
fullscreenCameraView = false;
addAllCameras();
@ -113,6 +100,8 @@ public class SurveillanceFragment extends Fragment {
cv.startPlayback();
}
expandToCameraViewIfRequired();
// Register for back pressed events
((MainActivity)getActivity()).setOnBackButtonPressedListener(new OnBackButtonPressedListener() {
@Override
@ -127,23 +116,35 @@ public class SurveillanceFragment extends Fragment {
});
}
/**
* Goes fullscreen igoring the device screen insets (camera etc)
*/
private void leanbackMode(boolean leanback) {
Window w = requireActivity().getWindow();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P)
return;
if (leanback) {
w.getAttributes().layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
// Hide system bar
WindowInsetsControllerCompat windowInsetsController = WindowCompat.getInsetsController(w, w.getDecorView());
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars());
// System bar is hidden when not touched for a while
windowInsetsController.setSystemBarsBehavior(WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
} else {
// Show system bar
//WindowInsetsControllerCompat windowInsetsController = WindowCompat.getInsetsController(w, w.getDecorView());
//windowInsetsController.show(WindowInsetsCompat.Type.systemBars());
}
}
@Override
public void onPause() {
super.onPause();
// Disable Leanback mode (fullscreen)
Window window = getActivity().getWindow();
if (window != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
final WindowInsetsController controller = window.getInsetsController();
if (controller != null)
controller.show(WindowInsets.Type.statusBars());
} else {
window.getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_VISIBLE);
}
}
leanbackMode(false);
disposeAllCameras();
}
@ -153,14 +154,14 @@ public class SurveillanceFragment extends Fragment {
Settings settings = Settings.fromDisk(getContext());
List<Camera> cc = settings.getCameras();
int elemsPerSide = calcGridSideElements(cc.size());
int[] gridSize = calcGridDimensionsBasedOnNumberOfElements(cc.size());
int camIdx = 0;
for (int r = 0; r < elemsPerSide; r++) {
for (int r = 0; r < gridSize[0]; r++) {
// Create row and add to row container
LinearLayout row = new LinearLayout(getContext());
binding.gridRowContainer.addView(row, rowLayoutParams);
// Add camera viewers to the row
for (int c = 0; c < elemsPerSide; c++) {
for (int c = 0; c < gridSize[1]; c++) {
if ( camIdx < cc.size() ) {
Camera cam = cc.get(camIdx);
CameraView cv = addCameraView(cam, row);
@ -239,13 +240,60 @@ public class SurveillanceFragment extends Fragment {
}
/**
* Returns the number of elements per side needed to create a grid that can contain the provided elements number.
* Returns the dimensions of the grid based on the number of elements.
* Es: to display 3 elements is needed a 4-element grid, with 2 elements per side (a 2x2 grid)
* Es: to display 6 elements is needed a 9-element grid, with 3 elements per side (a 2x3 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)));
private int[] calcGridDimensionsBasedOnNumberOfElements(int elements) {
int rows = 1;
int cols = 1;
while (rows * cols < elements) {
cols += 1;
if (rows * cols >= elements) break;
rows += 1;
}
int[] dimensions = {rows, cols};
return dimensions;
}
private void expandToCameraViewIfRequired() {
final String EXTRA_CAMERA_NUMBER = "it.danieleverducci.ojo.CAMERA_NUMBER";
final String EXTRA_CAMERA_NAME = "it.danieleverducci.ojo.CAMERA_NAME";
final String OPEN_CAMERA = "it.danieleverducci.ojo.OPEN_CAMERA";
if (this.getActivity() == null) {
return;
}
Intent intent = this.getActivity().getIntent();
if (OPEN_CAMERA.equals(intent.getAction())) {
String cameraName = intent.getStringExtra(EXTRA_CAMERA_NAME);
if (cameraName == null) {
int cameraNumber = intent.getIntExtra(EXTRA_CAMERA_NUMBER, 0) - 1;
expandByIndex(cameraNumber);
return;
}
expandByName(cameraName);
}
}
private void expandByIndex(int index) {
if (index < 0 || cameraViews.size() <= index) {
return;
}
hideAllCameraViewsButNot(cameraViews.get(index).surfaceView);
}
private void expandByName(String name) {
for(CameraView cameraView: cameraViews) {
if (cameraView.camera.getName().equals(name)) {
hideAllCameraViewsButNot(cameraView.surfaceView);
break;
}
}
}
/**
@ -269,6 +317,7 @@ public class SurveillanceFragment extends Fragment {
}
});
surfaceView.setOnFocusChangeListener((view, hasFocus) -> view.setBackgroundResource(hasFocus ? R.drawable.focus_border : 0));
SurfaceHolder holder = surfaceView.getHolder();
holder.setKeepScreenOn(true);

View File

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#FFFFFF"
android:alpha="0.8">
<path
android:fillColor="@android:color/white"
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</vector>

View File

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#FFFFFF"
android:alpha="0.8">
<path
android:fillColor="@android:color/white"
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
</vector>

View File

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#FFFFFF"
android:alpha="0.8">
<path
android:fillColor="@android:color/white"
android:pathData="M20,9H4v2h16V9zM4,15h16v-2H4V15z"/>
</vector>

View File

@ -0,0 +1,17 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#FFFFFF"
android:alpha="0.8">
<path
android:fillColor="@android:color/white"
android:pathData="M12,14m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
<path
android:fillColor="@android:color/white"
android:pathData="M16,3.33c2.58,0 4.67,2.09 4.67,4.67H22c0,-3.31 -2.69,-6 -6,-6v1.33M16,6c1.11,0 2,0.89 2,2h1.33c0,-1.84 -1.49,-3.33 -3.33,-3.33V6"/>
<path
android:fillColor="@android:color/white"
android:pathData="M17,9c0,-1.11 -0.89,-2 -2,-2L15,4L9,4L7.17,6L4,6c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,9h-5zM12,19c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z"/>
</vector>

View File

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#FFFFFF"
android:alpha="0.8">
<path
android:fillColor="@android:color/white"
android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z"/>
</vector>

View File

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#FFFFFF"
android:alpha="0.8">
<path
android:fillColor="@android:color/white"
android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 538 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 483 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 682 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 627 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 913 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 908 B

View File

@ -0,0 +1,6 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:width="1dip"
android:color="@color/white" />
</shape>

View File

@ -1,13 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#FFFFFF">
<group android:scaleX="2.61"
android:scaleY="2.61"
android:translateX="22.68"
android:translateY="22.68">
<group android:scaleX="0.58"
android:scaleY="0.58"
android:translateX="5.04"
android:translateY="5.04">
<path
android:fillColor="@android:color/white"
android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>

View File

@ -6,7 +6,12 @@
android:layout_height="match_parent"
tools:context=".ui.MainActivity">
<include layout="@layout/content_main" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_surveillance"
android:name="it.danieleverducci.ojo.ui.SurveillanceFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout="@layout/fragment_surveillance" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
@ -14,7 +19,12 @@
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
android:src="@drawable/ic_settings"
app:srcCompat="@drawable/ic_settings"
app:tint="@color/purple_500"/>
app:tint="@color/white"
android:tint="@color/white"
android:backgroundTint="@color/fab_background"
android:hint="@string/fab_add_camera"
android:contentDescription="@string/fab_add_camera"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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="match_parent"
tools:context=".ui.SettingsActivity">
<include layout="@layout/content_settings" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -6,7 +6,7 @@
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<fragment
android:id="@+id/nav_host_fragment_content_main"
android:id="@+id/nav_host_fragment_content_settings"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"

View File

@ -14,10 +14,12 @@
android:gravity="center_horizontal">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="100dp"
android:layout_height="100dp"
android:padding="20dp"
android:layout_marginBottom="50dp"
android:src="@mipmap/ic_launcher_round"/>
android:background="@color/ic_launcher_background"
android:src="@drawable/ic_eye"/>
<TextView
android:layout_width="match_parent"

View File

@ -11,9 +11,11 @@
android:padding="50dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher_round"/>
android:layout_width="100dp"
android:layout_height="100dp"
android:padding="20dp"
android:background="@color/ic_launcher_background"
android:src="@drawable/ic_eye"/>
<View
android:layout_width="match_parent"
@ -56,6 +58,19 @@
android:layout_marginTop="20dp"
android:text="@string/app_info_lib_desc"
android:autoLink="web"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:textStyle="bold"
android:text="@string/app_info_translators_title"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:text="@string/app_info_translators_names"/>
</LinearLayout>
</ScrollView>

View File

@ -5,7 +5,9 @@
android:layout_height="wrap_content"
android:paddingTop="@dimen/text_margin"
android:paddingBottom="@dimen/text_margin"
android:orientation="horizontal">
android:orientation="horizontal"
android:focusable="true"
android:focusableInTouchMode="true">
<ImageView
android:layout_width="wrap_content"

View File

@ -11,7 +11,8 @@
android:layout_height="wrap_content"
android:id="@+id/settingsToolbar"
app:title="@string/app_name"
style="@style/ToolBarStyle" />
style="@style/ToolBarStyle"
android:focusable="true"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list"
@ -23,6 +24,7 @@
android:layout_marginRight="16dp"
app:layoutManager="LinearLayoutManager"
tools:context=".ui.SettingsFragment"
tools:listitem="@layout/fragment_settings_item" />
tools:listitem="@layout/fragment_settings_item"
android:focusable="true" />
</LinearLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -3,18 +3,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph"
app:startDestination="@id/HomeFragment">
app:startDestination="@id/SettingsFragment">
<fragment
android:id="@+id/HomeFragment"
android:name="it.danieleverducci.ojo.ui.SurveillanceFragment"
android:label="@string/first_fragment_label"
tools:layout="@layout/fragment_surveillance">
<action
android:id="@+id/action_homeToSettings"
app:destination="@id/SettingsFragment" />
</fragment>
<fragment
android:id="@+id/CameraUrlFragment"
android:name="it.danieleverducci.ojo.ui.StreamUrlFragment"
@ -30,9 +20,6 @@
android:label="fragment_settings_item_list"
tools:layout="@layout/fragment_settings_item_list" >
<action
android:id="@+id/action_settingsToHome"
app:destination="@id/HomeFragment" />
<action
android:id="@+id/action_settingsToCameraUrl"
app:destination="@id/CameraUrlFragment" />

View File

@ -5,6 +5,8 @@
<string name="first_fragment_label">First Fragment</string>
<string name="second_fragment_label">Second Fragment</string>
<string name="fab_add_camera">Add</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>
@ -25,5 +27,7 @@
<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_repo_desc">Puoi trovare il codice sorgente al repository: https://github.com/penguin86/ojo</string>
<string name="app_info_lib_desc">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</string>
<string name="app_info_translators_title">Traduttori:</string>
<string name="app_info_translators_names">Yurt Page (Russian)</string>
</resources>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Ojo</string>
<!-- Strings used for fragments for navigation -->
<string name="first_fragment_label">Первый Фрагмент</string>
<string name="second_fragment_label">Второй Фрагмент</string>
<string name="fab_add_camera">Добавить</string>
<string name="stream_list_default_camera_name">VidБезымянная камера №{camNo}</string>
<string name="add_stream_placeholder_url">rtsp://username:password@192.168.1.123:554</string>
<string name="add_stream_placeholder_name">Название IP камеры</string>
<string name="add_stream_name">Нзвание потока IP камеры</string>
<string name="add_stream_save">Сохранить</string>
<string name="add_stream_invalid_url">Неправильный RTSP URL</string>
<string name="add_stream_invalid_url_dismiss">Закрыть</string>
<string name="add_stream_error_saving">Произошла ошибка при сохранении конфигурации</string>
<string name="add_stream">Пожалуйста введите RTSP поток вашей камеры. Обратите внимание, что URL отличается от камеры к камере: вы можете найти полный URL адрес в настройках вашей камеры или руководстве пользователя.</string>
<string name="menuitem_allow_rotation">Разрешить вращение экрана</string>
<string name="menuitem_deny_rotation">Только альбомная</string>
<string name="menuitem_info">Инфо</string>
<string name="menuitem_add_camera">Добавить</string>
<string name="app_info_title">О программе</string>
<string name="app_info_creator_desc">Автор: Daniele Verducci.</string>
<string name="app_info_license_desc">Это приложение лицензировано в соответствии с GNU GENERAL PUBLIC LICENSE v3+. Ознакомьтесь тут: https://raw.githubusercontent.com/penguin86/ojo/master/LICENSE</string>
<string name="app_info_repo_desc">Исходный код в репозитории на GitHub:: https://github.com/penguin86/ojo</string>
<string name="app_info_lib_desc">Это приложение стало возможным благодаря усилиям команды VLC и VLC-Android! Вы можете узнать больше или получить исходный код тут: https://code.videolan.org/videolan/vlc-android</string>
</resources>

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="Theme.Ojo" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<!-- Primary brand color. -->
@ -13,7 +12,8 @@
<item name="colorSecondaryVariant">@color/purple_700</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>

View File

@ -5,4 +5,7 @@
<color name="purple_700">#FF3700B3</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="fab_foreground">@color/purple_500</color>
<color name="fab_background">@color/purple_200</color>
</resources>

View File

@ -4,6 +4,8 @@
<string name="first_fragment_label">First Fragment</string>
<string name="second_fragment_label">Second Fragment</string>
<string name="fab_add_camera">Add</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>
@ -22,6 +24,8 @@
<string name="app_info_title">About Ojo</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_repo_desc">The source code can be obtained at the github repository: https://github.com/penguin86/ojo</string>
<string name="app_info_lib_desc">This app is made possible by the gourgeous vlc and vlc-android teams effort! You can know more or obtain the source code at https://code.videolan.org/videolan/vlc-android</string>
<string name="app_info_repo_desc">The source code can be obtained at the GitHub repository: https://github.com/penguin86/ojo</string>
<string name="app_info_lib_desc">This app is made possible by the gorgeous VLC and VLC-Android teams effort! You can know more or obtain the source code at https://code.videolan.org/videolan/vlc-android</string>
<string name="app_info_translators_title">Translators:</string>
<string name="app_info_translators_names" translatable="false">Yurt Page (Russian)</string>
</resources>

View File

@ -19,6 +19,9 @@
<style name="ToolBarStyle" parent="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<item name="android:background">@color/purple_500</item>
<item name="titleTextColor">@color/white</item>
<item name="actionMenuTextColor">@color/white</item>
<item name="android:actionMenuTextColor">@color/white</item>
<item name="colorOnPrimary">@color/white</item>
</style>
</resources>

View File

@ -5,7 +5,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:4.2.2"
classpath 'com.android.tools.build:gradle:7.4.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

View File

@ -0,0 +1,2 @@
App can be opened with url intent ojo://view
Backported to Android >= 4.0.3

View File

@ -0,0 +1,2 @@
Added russian translation (thanks to Yurt Page!)

View File

@ -1,6 +1,16 @@
Ojo is a basic IP Camera surveillance wall.
IP camera's RTSP streams are added via its url and shown in the classic tile configuration. The number of tiles is automatically choosen based on the number of configured cameras: a single camera goes full screen, adding more cameras the app switches to a grid view: 2x2, 3x3, 4x4 and so on.
IP camera's RTSP streams are added via its url and shown in the classic tile configuration. The number of tiles is automatically chosen based on the number of configured cameras: a single camera goes full screen, adding more cameras the app switches to a grid view: 2x2, 3x3, 4x4 and so on.
The maximum number of cameras is determined by the device's capabilities.
The stream decoding and rendering is demanded to VLC's library: without their effort this app wouldn't be possible.
This app was specifically developed for F-Droid, as I couldn't find any open source RTSP vievers in the main repository.
This app was specifically developed for F-Droid, as I couldn't find any open source RTSP viewers in the main repository.
The app can be opened deeplinking to url ojo://view
To open the app with focus on a specific camera, you can use an intent (it.danieleverducci.ojo.OPEN_CAMERA) to specify which camera you want to view.
The extra argument it.danieleverducci.ojo.CAMERA_NAME will open the app with the camera with the name you specified while adding the camera.
The extra argument it.danieleverducci.ojo.CAMERA_NUMBER starting at 1 could be used as well, if you have multiple cameras with the same name.
See below an example how to use the intent. The flag (-f 268468224) could be useful if you want to switch to another camera while the app is running:
adb -s <YOUR_DEVICE> shell am start -a it.danieleverducci.ojo.OPEN_CAMERA -f 268468224 --es it.danieleverducci.ojo.CAMERA_NAME <YOUR_CAMERA_NAME>
adb -s <YOUR_DEVICE> shell am start -a it.danieleverducci.ojo.OPEN_CAMERA -f 268468224 --es it.danieleverducci.ojo.CAMERA_NUMBER <YOUR_CAMERA_NUMBER>

View File

@ -0,0 +1,2 @@
L'app può essere aperta con intent url ojo://view
Aggiunta compatibilità con vecchie versioni di Android >= 4.0.3

View File

@ -0,0 +1 @@
Aggiunta traduzione in russo (grazie a Yurt Page!)

View File

@ -0,0 +1,19 @@
OJO это базовый просмотрщик IP камер наблюдения.
RTSP потоки IP камеры добавляются через его URL адрес и показаны в классической плиточной конфигурации. Количество плиток автоматически выбирается на основе количества настроенных камер: одна камера показана на весь экран, добавление больше камер переключает на сетку: 2x2, 3x3, 4x4 и т. д.
Максимальное количество камер определяется возможностями устройства.
Декодирование и рендеринг потока требует библиотеки VLC: без их стараний это приложение не было бы возможно.
Это приложение было специально разработано для F-Droid, так как я не мог найти каких-либо просмотрщиков RTSP с открытым исходным кодом в основном репозитории.
Приложение может быть открыто с глубокой ссылкой по URL ojo://view.
Чтобы открыть приложение с фокусом на определенной камере, вы можете использовать intent (it.danieleverducci.ojo.OPEN_CAMERA) чтобы указать какую камеру вы хотите просмотреть.
Дополнительный аргумент it.danieleverducci.ojo.CAMERA_NAME откроет приложение с камерой с указанным вами названием при добавлении камеры.
Дополнительный аргумент it.danieleverducci.ojo.CAMERA_NUMBER начиная с 1 также может использоваться, если у вас есть несколько камер с одним и тем же названием.
Ниже пример как использовать intent. Флаг (-F 268468224) может быть полезен, если вы хотите переключиться на другую камеру во время работы приложения:
adb -s <ВАШЕ_УСТРОЙСТВО> shell am start -a it.danieleverducci.ojo.OPEN_CAMERA -f 268468224 --es it.danieleverducci.ojo.CAMERA_NAME <ИМЯ_ВАШЕЙ_КАМЕРЫ>
adb -s <ВАШЕ_УСТРОЙСТВО> shell am start -a it.danieleverducci.ojo.OPEN_CAMERA -f 268468224 --es it.danieleverducci.ojo.CAMERA_NUMBER <НОМЕР_ВАШЕЙ_КАМЕРЫ>

View File

@ -0,0 +1 @@
OJO это базовый просмотрщик IP камер наблюдения.

View File

@ -0,0 +1 @@
Ojo RTSP просмотрщик IP Камер

View File

@ -1,6 +1,6 @@
#Fri Aug 13 07:42:34 CEST 2021
#Wed Feb 15 08:51:54 CET 2023
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME