Compare commits
44 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
cf357b462c | ||
|
4646da3cd2 | ||
|
7d166f9d51 | ||
|
3848cc7759 | ||
|
d4b77d2d33 | ||
|
042699ef77 | ||
|
cd4646c7c4 | ||
|
6a1499d356 | ||
|
dd59aa129b | ||
|
96a91a6c3d | ||
|
7a52c24fea | ||
|
23535448a5 | ||
|
482f69e003 | ||
|
4aa17ec8f3 | ||
|
0fd2474e57 | ||
|
da78648064 | ||
|
906698cf53 | ||
|
0f13e23f09 | ||
|
02bac4671e | ||
|
9d6d97b10f | ||
|
5ec000d1c9 | ||
|
d3cb5abe27 | ||
|
66d0a1081a | ||
|
1647895a2a | ||
|
1ad6500882 | ||
|
dfb30bef9e | ||
|
2f9af2e1f1 | ||
|
333b44cba3 | ||
|
182a9a9b89 | ||
|
ff5b6b8101 | ||
|
24fc195ff5 | ||
|
559c831d08 | ||
|
a750dbbb00 | ||
|
ab7f844447 | ||
|
e4fa5a7169 | ||
|
19e01068ef | ||
|
a99a657cc1 | ||
|
85be9af409 | ||
|
b1fa8713e2 | ||
|
60791459c0 | ||
|
63b08fe718 | ||
|
8dc6da8b98 | ||
|
505f8bf54b | ||
|
3c37c50843 |
2
.idea/compiler.xml
generated
@ -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
@ -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
@ -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>
|
11
.idea/misc.xml
generated
@ -3,14 +3,21 @@
|
||||
<component name="DesignSurface">
|
||||
<option name="filePathToZoomLevelMap">
|
||||
<map>
|
||||
<entry key="../../../../layout/custom_preview.xml" value="0.36974358974358973" />
|
||||
<entry key="app/src/main/res/drawable/ic_add.xml" value="0.1565" />
|
||||
<entry key="app/src/main/res/drawable/ic_add_camera.xml" value="0.1565" />
|
||||
<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/dialog_info.xml" value="0.7119565217391305" />
|
||||
<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_list.xml" value="0.4" />
|
||||
<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>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="1.8" 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">
|
||||
|
21
README.md
@ -2,9 +2,8 @@
|
||||
|
||||
# Ojo: the FLOSS RTSP Surveillance camera viewer for Android
|
||||
|
||||
[<img src="https://raw.githubusercontent.com/andOTP/andOTP/master/assets/badges/get-it-on-github.png" height="80">](https://github.com/penguin86/ojo/releases/latest)
|
||||
|
||||
F-droid build **coming soon!**
|
||||
[<img src="https://raw.githubusercontent.com/andOTP/andOTP/master/assets/badges/get-it-on-github.png" height="80">](https://github.com/penguin86/ojo/releases/latest)
|
||||
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" height="80">](https://f-droid.org/it/packages/it.danieleverducci.ojo)
|
||||
|
||||
(Always prefer [F-Droid](https://f-droid.org) build, when possible).
|
||||
|
||||
@ -15,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>
|
||||
```
|
||||
|
||||
|
||||
  
|
||||
|
||||
## 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
|
||||
|
@ -3,15 +3,14 @@ plugins {
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion "30.0.2"
|
||||
compileSdkVersion 33
|
||||
|
||||
defaultConfig {
|
||||
applicationId "it.danieleverducci.ojo"
|
||||
minSdkVersion 17
|
||||
targetSdkVersion 30
|
||||
versionCode 2
|
||||
versionName "0.0.2"
|
||||
minSdkVersion 15
|
||||
targetSdkVersion 33
|
||||
versionCode 9
|
||||
versionName "0.1.4"
|
||||
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
|
||||
@ -31,16 +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'
|
||||
}
|
@ -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": 1,
|
||||
"versionName": "0.0.1",
|
||||
"attributes": [],
|
||||
"versionCode": 6,
|
||||
"versionName": "0.1.1",
|
||||
"outputFile": "app-release.apk"
|
||||
}
|
||||
]
|
||||
],
|
||||
"elementType": "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:screenOrientation="landscape">
|
||||
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>
|
||||
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
@ -13,7 +13,6 @@ import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import it.danieleverducci.ojo.entities.Camera;
|
||||
|
||||
@ -21,6 +20,7 @@ import it.danieleverducci.ojo.entities.Camera;
|
||||
* Manages the settings persistence
|
||||
*/
|
||||
public class Settings implements Serializable {
|
||||
private static final long serialVersionUID = 1081285022445419696L;
|
||||
private static final String FILENAME = "settings.bin";
|
||||
private static final String TAG = "Settings";
|
||||
|
||||
|
@ -0,0 +1,22 @@
|
||||
package it.danieleverducci.ojo;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
public class SharedPreferencesManager {
|
||||
private static final String SP_ROTATION_ENABLED = "rot_en";
|
||||
|
||||
public static void saveRotationEnabled(Context ctx, boolean enabled) {
|
||||
SharedPreferences sharedPref = ctx.getSharedPreferences(SP_ROTATION_ENABLED, Context.MODE_PRIVATE);
|
||||
sharedPref.edit().putBoolean(SP_ROTATION_ENABLED, enabled).apply();
|
||||
}
|
||||
|
||||
public static boolean loadRotationEnabled(Context ctx) {
|
||||
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));
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package it.danieleverducci.ojo.entities;
|
||||
import java.io.Serializable;
|
||||
|
||||
public class Camera implements Serializable {
|
||||
private static final long serialVersionUID = -3837361587400158910L;
|
||||
private String name;
|
||||
private String rtspUrl;
|
||||
|
||||
@ -11,6 +12,14 @@ public class Camera implements Serializable {
|
||||
this.rtspUrl = rtspUrl;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setRtspUrl(String rtspUrl) {
|
||||
this.rtspUrl = rtspUrl;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
@ -0,0 +1,18 @@
|
||||
package it.danieleverducci.ojo.ui;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import it.danieleverducci.ojo.databinding.FragmentInfoBinding;
|
||||
|
||||
|
||||
public class InfoFragment extends Fragment {
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return FragmentInfoBinding.inflate(inflater, container, false).getRoot();
|
||||
}
|
||||
}
|
@ -1,49 +1,62 @@
|
||||
package it.danieleverducci.ojo.ui;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.navigation.NavController;
|
||||
import androidx.navigation.Navigation;
|
||||
import androidx.navigation.ui.AppBarConfiguration;
|
||||
import androidx.navigation.ui.NavigationUI;
|
||||
|
||||
import it.danieleverducci.ojo.R;
|
||||
import it.danieleverducci.ojo.SharedPreferencesManager;
|
||||
import it.danieleverducci.ojo.databinding.ActivityMainBinding;
|
||||
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
private static final String TAG = "MainActivity";
|
||||
|
||||
private ActivityMainBinding binding;
|
||||
private OnBackButtonPressedListener onBackButtonPressedListener;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// 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 navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
|
||||
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
|
||||
if (destination.getId() == R.id.FirstFragment)
|
||||
binding.fab.show();
|
||||
else
|
||||
binding.fab.hide();
|
||||
});
|
||||
|
||||
binding.fab.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
navController.navigate(R.id.action_FirstFragment_to_SecondFragment);
|
||||
}
|
||||
});
|
||||
binding.fab.setOnClickListener(view -> openSettings());
|
||||
}
|
||||
|
||||
@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) {
|
||||
this.onBackButtonPressedListener = onBackButtonPressedListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (this.onBackButtonPressedListener != null && this.onBackButtonPressedListener.onBackPressed())
|
||||
return;
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
private void openSettings() {
|
||||
Intent i = new Intent(this, SettingsActivity.class);
|
||||
startActivity(i);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package it.danieleverducci.ojo.ui;
|
||||
|
||||
public interface OnBackButtonPressedListener {
|
||||
public boolean onBackPressed();
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
package it.danieleverducci.ojo.ui;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
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.SharedPreferencesManager;
|
||||
import it.danieleverducci.ojo.databinding.FragmentSettingsItemListBinding;
|
||||
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 {
|
||||
|
||||
private FragmentSettingsItemListBinding binding;
|
||||
private Settings settings;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
binding = FragmentSettingsItemListBinding.inflate(inflater, container, false);
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
// Setup toolbar
|
||||
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(((SettingsActivity)getActivity()).getRotationEnabledSetting() ? R.string.menuitem_deny_rotation : R.string.menuitem_allow_rotation);
|
||||
|
||||
// Register for item click
|
||||
binding.settingsToolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menuitem_add_camera:
|
||||
((SettingsActivity)getActivity()).navigateToFragment(R.id.action_settingsToCameraUrl);
|
||||
return true;
|
||||
case R.id.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:
|
||||
((SettingsActivity)getActivity()).navigateToFragment(R.id.action_SettingsToInfoFragment);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// Load cameras
|
||||
settings = Settings.fromDisk(getContext());
|
||||
List<Camera> cams = settings.getCameras();
|
||||
|
||||
// Set the adapter
|
||||
RecyclerView recyclerView = binding.list;
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
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);
|
||||
// Onclick listener
|
||||
adapter.setOnClickListener(new SettingsRecyclerViewAdapter.OnClickListener() {
|
||||
@Override
|
||||
public void onItemClick(int pos) {
|
||||
Bundle b = new Bundle();
|
||||
b.putInt(StreamUrlFragment.ARG_CAMERA, pos);
|
||||
((SettingsActivity)getActivity()).navigateToFragment(R.id.action_settingsToCameraUrl, b);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
|
||||
// Save cameras
|
||||
List<Camera> cams = ((SettingsRecyclerViewAdapter)binding.list.getAdapter()).getItems();
|
||||
this.settings.setCameras(cams);
|
||||
this.settings.save();
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
|
||||
@ -16,9 +17,20 @@ import it.danieleverducci.ojo.Settings;
|
||||
import it.danieleverducci.ojo.databinding.FragmentAddStreamBinding;
|
||||
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 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
|
||||
public View onCreateView(
|
||||
@ -27,6 +39,18 @@ public class AddStreamFragment extends Fragment {
|
||||
) {
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
@ -45,10 +69,19 @@ public class AddStreamFragment extends Fragment {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load existing settings (if any)
|
||||
Settings settings = Settings.fromDisk(getContext());
|
||||
// Add stream to list
|
||||
settings.addCamera(new Camera("", url));
|
||||
// Name can be empty
|
||||
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
|
||||
settings.addCamera(new Camera(name, url));
|
||||
}
|
||||
|
||||
// Save
|
||||
if (!settings.save()) {
|
||||
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
|
||||
NavHostFragment.findNavController(AddStreamFragment.this)
|
||||
NavHostFragment.findNavController(StreamUrlFragment.this)
|
||||
.popBackStack();
|
||||
}
|
||||
});
|
@ -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,13 +13,15 @@ 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.interfaces.IVLCVout;
|
||||
import org.videolan.libvlc.IVLCVout;
|
||||
import org.videolan.libvlc.LibVLC;
|
||||
import org.videolan.libvlc.Media;
|
||||
import org.videolan.libvlc.MediaPlayer;
|
||||
@ -76,48 +79,73 @@ 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();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
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();
|
||||
|
||||
// Start playback for all streams
|
||||
for (CameraView cv : cameraViews) {
|
||||
cv.startPlayback();
|
||||
}
|
||||
|
||||
expandToCameraViewIfRequired();
|
||||
|
||||
// Register for back pressed events
|
||||
((MainActivity)getActivity()).setOnBackButtonPressedListener(new OnBackButtonPressedListener() {
|
||||
@Override
|
||||
public boolean onBackPressed() {
|
||||
if(fullscreenCameraView && cameraViews.size() > 1) {
|
||||
fullscreenCameraView = false;
|
||||
showAllCameras();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
leanbackMode(false);
|
||||
|
||||
disposeAllCameras();
|
||||
}
|
||||
|
||||
@ -126,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);
|
||||
@ -212,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -242,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);
|
||||
|
@ -0,0 +1,137 @@
|
||||
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<SettingsRecyclerViewAdapter.ViewHolder> implements ItemMoveCallback.ItemTouchHelperContract {
|
||||
|
||||
private final List<Camera> mValues;
|
||||
private OnDragListener dragListener;
|
||||
private OnClickListener clickListener;
|
||||
|
||||
public SettingsRecyclerViewAdapter(List<Camera> 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());
|
||||
|
||||
holder.root.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
clickListener.onItemClick(holder.getBindingAdapterPosition());
|
||||
}
|
||||
});
|
||||
|
||||
holder.deleteButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
mValues.remove(holder.getBindingAdapterPosition());
|
||||
notifyItemRemoved(holder.getBindingAdapterPosition());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@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 void setOnClickListener(OnClickListener clickListener) {
|
||||
this.clickListener = clickListener;
|
||||
}
|
||||
|
||||
public List<Camera> getItems() {
|
||||
return mValues;
|
||||
}
|
||||
|
||||
public class ViewHolder extends RecyclerView.ViewHolder {
|
||||
public View root;
|
||||
public TextView name;
|
||||
public TextView url;
|
||||
public View deleteButton;
|
||||
public View dragHandle;
|
||||
|
||||
public ViewHolder(FragmentSettingsItemBinding binding) {
|
||||
super(binding.getRoot());
|
||||
|
||||
this.root = binding.getRoot();
|
||||
this.name = binding.cameraName;
|
||||
this.url = binding.cameraUrl;
|
||||
this.deleteButton = binding.cameraDelete;
|
||||
this.dragHandle = binding.cameraDragHandle;
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnDragListener {
|
||||
void onItemDrag(ViewHolder vh);
|
||||
}
|
||||
|
||||
public interface OnClickListener {
|
||||
void onItemClick(int pos);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
11
app/src/main/res/drawable-anydpi-v17/ic_add.xml
Normal 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>
|
11
app/src/main/res/drawable-anydpi-v17/ic_delete.xml
Normal 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>
|
11
app/src/main/res/drawable-anydpi-v17/ic_drag_handle.xml
Normal 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>
|
17
app/src/main/res/drawable-anydpi-v17/ic_network_camera.xml
Normal 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>
|
11
app/src/main/res/drawable-anydpi-v17/ic_settings.xml
Normal 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>
|
11
app/src/main/res/drawable-anydpi/ic_eye.xml
Normal 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>
|
BIN
app/src/main/res/drawable-hdpi-v17/ic_add.png
Normal file
After Width: | Height: | Size: 178 B |
BIN
app/src/main/res/drawable-hdpi-v17/ic_delete.png
Normal file
After Width: | Height: | Size: 267 B |
BIN
app/src/main/res/drawable-hdpi-v17/ic_drag_handle.png
Normal file
After Width: | Height: | Size: 145 B |
BIN
app/src/main/res/drawable-hdpi-v17/ic_network_camera.png
Normal file
After Width: | Height: | Size: 538 B |
BIN
app/src/main/res/drawable-hdpi-v17/ic_settings.png
Normal file
After Width: | Height: | Size: 483 B |
BIN
app/src/main/res/drawable-hdpi/ic_eye.png
Normal file
After Width: | Height: | Size: 517 B |
BIN
app/src/main/res/drawable-mdpi-v17/ic_add.png
Normal file
After Width: | Height: | Size: 120 B |
BIN
app/src/main/res/drawable-mdpi-v17/ic_delete.png
Normal file
After Width: | Height: | Size: 189 B |
BIN
app/src/main/res/drawable-mdpi-v17/ic_drag_handle.png
Normal file
After Width: | Height: | Size: 113 B |
BIN
app/src/main/res/drawable-mdpi-v17/ic_network_camera.png
Normal file
After Width: | Height: | Size: 327 B |
BIN
app/src/main/res/drawable-mdpi-v17/ic_settings.png
Normal file
After Width: | Height: | Size: 337 B |
BIN
app/src/main/res/drawable-mdpi/ic_eye.png
Normal file
After Width: | Height: | Size: 328 B |
BIN
app/src/main/res/drawable-xhdpi-v17/ic_add.png
Normal file
After Width: | Height: | Size: 159 B |
BIN
app/src/main/res/drawable-xhdpi-v17/ic_delete.png
Normal file
After Width: | Height: | Size: 294 B |
BIN
app/src/main/res/drawable-xhdpi-v17/ic_drag_handle.png
Normal file
After Width: | Height: | Size: 142 B |
BIN
app/src/main/res/drawable-xhdpi-v17/ic_network_camera.png
Normal file
After Width: | Height: | Size: 682 B |
BIN
app/src/main/res/drawable-xhdpi-v17/ic_settings.png
Normal file
After Width: | Height: | Size: 653 B |
BIN
app/src/main/res/drawable-xhdpi/ic_eye.png
Normal file
After Width: | Height: | Size: 627 B |
BIN
app/src/main/res/drawable-xxhdpi-v17/ic_add.png
Normal file
After Width: | Height: | Size: 260 B |
BIN
app/src/main/res/drawable-xxhdpi-v17/ic_delete.png
Normal file
After Width: | Height: | Size: 399 B |
BIN
app/src/main/res/drawable-xxhdpi-v17/ic_drag_handle.png
Normal file
After Width: | Height: | Size: 176 B |
BIN
app/src/main/res/drawable-xxhdpi-v17/ic_network_camera.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
app/src/main/res/drawable-xxhdpi-v17/ic_settings.png
Normal file
After Width: | Height: | Size: 913 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_eye.png
Normal file
After Width: | Height: | Size: 908 B |
6
app/src/main/res/drawable/focus_border.xml
Normal 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>
|
9
app/src/main/res/drawable/ic_add.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
||||
</vector>
|
@ -1,10 +0,0 @@
|
||||
<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>
|
10
app/src/main/res/drawable/ic_delete.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="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>
|
10
app/src/main/res/drawable/ic_drag_handle.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="M20,9H4v2h16V9zM4,15h16v-2H4V15z"/>
|
||||
</vector>
|
@ -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"/>
|
||||
|
16
app/src/main/res/drawable/ic_network_camera.xml
Normal file
@ -0,0 +1,16 @@
|
||||
<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="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>
|
10
app/src/main/res/drawable/ic_settings.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="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>
|
@ -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"
|
||||
app:srcCompat="@drawable/ic_add_camera"
|
||||
app:tint="@color/purple_500"/>
|
||||
android:src="@drawable/ic_settings"
|
||||
app:srcCompat="@drawable/ic_settings"
|
||||
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>
|
11
app/src/main/res/layout/activity_settings.xml
Normal 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>
|
@ -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"
|
@ -14,14 +14,32 @@
|
||||
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"
|
||||
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_name"
|
||||
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"/>
|
||||
|
||||
<EditText
|
||||
@ -41,48 +59,6 @@
|
||||
android:layout_marginTop="30dp"
|
||||
android:text="@string/add_stream_save"/>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_margin="50dp"
|
||||
android:background="@color/purple_200"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Title"
|
||||
android:textColor="@color/purple_500"
|
||||
android:text="@string/app_info_title"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/app_info_creator_desc"
|
||||
android:autoLink="web"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/app_info_license_desc"
|
||||
android:autoLink="web"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/app_info_repo_desc"
|
||||
android:autoLink="web"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/app_info_lib_desc"
|
||||
android:autoLink="web"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
76
app/src/main/res/layout/fragment_info.xml
Normal file
@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center_horizontal"
|
||||
android:padding="50dp">
|
||||
|
||||
<ImageView
|
||||
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"
|
||||
android:layout_height="1dp"
|
||||
android:layout_margin="50dp"
|
||||
android:background="@color/purple_200"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Title"
|
||||
android:textColor="@color/purple_500"
|
||||
android:text="@string/app_info_title"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/app_info_creator_desc"
|
||||
android:autoLink="web"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/app_info_license_desc"
|
||||
android:autoLink="web"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/app_info_repo_desc"
|
||||
android:autoLink="web"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
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>
|
64
app/src/main/res/layout/fragment_settings_item.xml
Normal file
@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="@dimen/text_margin"
|
||||
android:paddingBottom="@dimen/text_margin"
|
||||
android:orientation="horizontal"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="10dp"
|
||||
android:layout_weight="0"
|
||||
app:srcCompat="@drawable/ic_network_camera"
|
||||
app:tint="@color/design_default_color_primary"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/camera_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="5sp"
|
||||
android:textAppearance="?attr/textAppearanceListItem"
|
||||
android:textColor="@color/design_default_color_primary"
|
||||
android:lines="1"
|
||||
android:text="@tools:sample/lorem/random"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/camera_url"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?attr/textAppearanceListItemSecondary"
|
||||
android:lines="1"
|
||||
android:text="@tools:sample/lorem/random" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/camera_delete"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="10dp"
|
||||
android:layout_weight="0"
|
||||
app:srcCompat="@drawable/ic_delete"
|
||||
app:tint="@color/purple_200" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/camera_drag_handle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="10dp"
|
||||
android:layout_weight="0"
|
||||
app:srcCompat="@drawable/ic_drag_handle"
|
||||
app:tint="@color/purple_200" />
|
||||
|
||||
</LinearLayout>
|
30
app/src/main/res/layout/fragment_settings_item_list.xml
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout 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"
|
||||
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"
|
||||
style="@style/ToolBarStyle"
|
||||
android:focusable="true"/>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list"
|
||||
android:name="it.danieleverducci.ojo.ui.SettingsFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
app:layoutManager="LinearLayoutManager"
|
||||
tools:context=".ui.SettingsFragment"
|
||||
tools:listitem="@layout/fragment_settings_item"
|
||||
android:focusable="true" />
|
||||
|
||||
</LinearLayout>
|
@ -6,7 +6,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context=".ui.AddStreamFragment"
|
||||
tools:context=".ui.StreamUrlFragment"
|
||||
android:background="@color/purple_500">
|
||||
|
||||
</LinearLayout>
|
||||
|
18
app/src/main/res/menu/settings_menu.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<?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"
|
||||
android:title="@string/menuitem_add_camera"
|
||||
app:showAsAction="always"/>
|
||||
|
||||
<item android:id="@+id/menuitem_allow_rotation"
|
||||
android:title="@string/menuitem_allow_rotation"
|
||||
app:showAsAction="never"/>
|
||||
|
||||
<item android:id="@+id/menuitem_info"
|
||||
android:title="@string/menuitem_info"
|
||||
app:showAsAction="never"/>
|
||||
|
||||
</menu>
|
BIN
app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 8.8 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 13 KiB |
@ -3,26 +3,32 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/nav_graph"
|
||||
app:startDestination="@id/FirstFragment">
|
||||
app:startDestination="@id/SettingsFragment">
|
||||
|
||||
<fragment
|
||||
android:id="@+id/FirstFragment"
|
||||
android:name="it.danieleverducci.ojo.ui.SurveillanceFragment"
|
||||
android:label="@string/first_fragment_label"
|
||||
tools:layout="@layout/fragment_surveillance">
|
||||
|
||||
<action
|
||||
android:id="@+id/action_FirstFragment_to_SecondFragment"
|
||||
app:destination="@id/SecondFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/SecondFragment"
|
||||
android:name="it.danieleverducci.ojo.ui.AddStreamFragment"
|
||||
android:id="@+id/CameraUrlFragment"
|
||||
android:name="it.danieleverducci.ojo.ui.StreamUrlFragment"
|
||||
android:label="@string/second_fragment_label"
|
||||
tools:layout="@layout/fragment_add_stream">
|
||||
<action
|
||||
android:id="@+id/action_cameraUrlToSettings"
|
||||
app:destination="@id/SettingsFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/SettingsFragment"
|
||||
android:name="it.danieleverducci.ojo.ui.SettingsFragment"
|
||||
android:label="fragment_settings_item_list"
|
||||
tools:layout="@layout/fragment_settings_item_list" >
|
||||
|
||||
<action
|
||||
android:id="@+id/action_SecondFragment_to_FirstFragment"
|
||||
app:destination="@id/FirstFragment" />
|
||||
android:id="@+id/action_settingsToCameraUrl"
|
||||
app:destination="@id/CameraUrlFragment" />
|
||||
<action
|
||||
android:id="@+id/action_SettingsToInfoFragment"
|
||||
app:destination="@id/infoFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/infoFragment"
|
||||
android:name="it.danieleverducci.ojo.ui.InfoFragment"
|
||||
android:label="InfoFragment" />
|
||||
</navigation>
|
@ -5,16 +5,29 @@
|
||||
<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>
|
||||
<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_placeholder_name">Nome della IP Camera</string>
|
||||
<string name="add_stream_name">Nome della IP Camera</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_dismiss">Chiudi</string>
|
||||
<string name="add_stream_error_saving">Si è verificato un errore durante il salvataggio della configurazione.</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="menuitem_allow_rotation">Allow screen rotation</string>
|
||||
<string name="menuitem_deny_rotation">Landscape only</string>
|
||||
<string name="menuitem_info">Info</string>
|
||||
<string name="menuitem_add_camera">Aggiungi</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_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>
|
31
app/src/main/res/values-ru/strings.xml
Normal 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>
|
@ -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>
|
@ -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>
|
@ -1,3 +1,4 @@
|
||||
<resources>
|
||||
<dimen name="fab_margin">16dp</dimen>
|
||||
<dimen name="text_margin">16dp</dimen>
|
||||
</resources>
|
@ -4,16 +4,28 @@
|
||||
<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>
|
||||
<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_placeholder_name">Camera name</string>
|
||||
<string name="add_stream_name">Camera name</string>
|
||||
<string name="add_stream_save">Save</string>
|
||||
<string name="add_stream_invalid_url">Invalid RTSP url</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">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="menuitem_allow_rotation">Allow screen rotation</string>
|
||||
<string name="menuitem_deny_rotation">Landscape only</string>
|
||||
<string name="menuitem_info">Info</string>
|
||||
<string name="menuitem_add_camera">Add</string>
|
||||
|
||||
<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>
|
@ -12,6 +12,16 @@
|
||||
<item name="colorSecondary">@color/purple_500</item>
|
||||
<item name="colorSecondaryVariant">@color/purple_700</item>
|
||||
<item name="colorOnSecondary">@color/white</item>
|
||||
|
||||
<item name="colorAccent">@color/white</item>
|
||||
</style>
|
||||
|
||||
<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>
|
@ -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
|
||||
|
1
fastlane/metadata/android/en-US/changelogs/3.txt
Normal file
@ -0,0 +1 @@
|
||||
Downgraded LibVLC library to fix black screen bug on certain devices
|
3
fastlane/metadata/android/en-US/changelogs/4.txt
Normal file
@ -0,0 +1,3 @@
|
||||
RTSP Streams can be removed, edited and reordered.
|
||||
Now supporting screen rotation (disabled by default).
|
||||
Better back button handling in full screen mode.
|
2
fastlane/metadata/android/en-US/changelogs/8.txt
Normal file
@ -0,0 +1,2 @@
|
||||
App can be opened with url intent ojo://view
|
||||
Backported to Android >= 4.0.3
|
2
fastlane/metadata/android/en-US/changelogs/9.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Added russian translation (thanks to Yurt Page!)
|
||||
|
@ -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>
|
||||
|
1
fastlane/metadata/android/it-IT/changelogs/3.txt
Normal file
@ -0,0 +1 @@
|
||||
Downgrade della libreria libVLC per risolvere il bug della schermata nera in determinati dispositivi
|
3
fastlane/metadata/android/it-IT/changelogs/4.txt
Normal file
@ -0,0 +1,3 @@
|
||||
Possibilità di rimuovere, modificare e riordinare gli stream RTSP.
|
||||
Supporto per la rotazione dello schermo (disabilitato di default).
|
||||
Migliorato il comportamento quando si preme il bottone Indietro in fullscreen.
|
2
fastlane/metadata/android/it-IT/changelogs/8.txt
Normal 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
|
1
fastlane/metadata/android/it-IT/changelogs/9.txt
Normal file
@ -0,0 +1 @@
|
||||
Aggiunta traduzione in russo (grazie a Yurt Page!)
|
19
fastlane/metadata/android/ru/full_description.txt
Normal 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 <НОМЕР_ВАШЕЙ_КАМЕРЫ>
|
||||
|
1
fastlane/metadata/android/ru/short_description.txt
Normal file
@ -0,0 +1 @@
|
||||
OJO это базовый просмотрщик IP камер наблюдения.
|
1
fastlane/metadata/android/ru/title.txt
Normal file
@ -0,0 +1 @@
|
||||
Ojo RTSP просмотрщик IP Камер
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
@ -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
|
||||
|