Compare commits
No commits in common. "Test" and "master" have entirely different histories.
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="CompilerConfiguration">
|
<component name="CompilerConfiguration">
|
||||||
<bytecodeTargetLevel target="17" />
|
<bytecodeTargetLevel target="16" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
@ -7,7 +7,7 @@
|
|||||||
<option name="testRunner" value="GRADLE" />
|
<option name="testRunner" value="GRADLE" />
|
||||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
<option name="gradleJvm" value="jbr-17" />
|
<option name="gradleJvm" value="semeru-16" />
|
||||||
<option name="modules">
|
<option name="modules">
|
||||||
<set>
|
<set>
|
||||||
<option value="$PROJECT_DIR$" />
|
<option value="$PROJECT_DIR$" />
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="DesignSurface">
|
<component name="DesignSurface">
|
||||||
<option name="filePathToZoomLevelMap">
|
<option name="filePathToZoomLevelMap">
|
||||||
@ -15,5 +16,5 @@
|
|||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK" />
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_16_PREVIEW" project-jdk-name="11" project-jdk-type="JavaSDK" />
|
||||||
</project>
|
</project>
|
||||||
@ -6,7 +6,6 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace 'com.ray650128.gstreamer_demo_app'
|
|
||||||
ndkVersion "21.3.6528147"
|
ndkVersion "21.3.6528147"
|
||||||
compileSdk 33
|
compileSdk 33
|
||||||
|
|
||||||
@ -27,28 +26,29 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_17
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
targetCompatibility JavaVersion.VERSION_17
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = '17'
|
jvmTarget = '1.8'
|
||||||
}
|
}
|
||||||
lint {
|
lint {
|
||||||
abortOnError false
|
abortOnError false
|
||||||
checkReleaseBuilds false
|
checkReleaseBuilds false
|
||||||
}
|
}
|
||||||
viewBinding.enabled = true
|
viewBinding.enabled = true
|
||||||
|
namespace 'com.ray650128.gstreamer_demo_app'
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
implementation 'androidx.core:core-ktx:1.10.1'
|
implementation 'androidx.core:core-ktx:1.9.0'
|
||||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
implementation 'androidx.appcompat:appcompat:1.5.1'
|
||||||
implementation 'com.google.android.material:material:1.9.0'
|
implementation 'com.google.android.material:material:1.7.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||||
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
||||||
implementation 'androidx.core:core-ktx:1.10.1'
|
implementation 'androidx.core:core-ktx:+'
|
||||||
testImplementation 'junit:junit:4.13.2'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||||
@ -56,6 +56,6 @@ dependencies {
|
|||||||
implementation project(path: ':gstreamer_player')
|
implementation project(path: ':gstreamer_player')
|
||||||
|
|
||||||
// Android Jetpack lib
|
// Android Jetpack lib
|
||||||
implementation("androidx.fragment:fragment-ktx:1.5.7")
|
implementation("androidx.fragment:fragment-ktx:1.5.5")
|
||||||
implementation("androidx.activity:activity-ktx:1.7.1")
|
implementation("androidx.activity:activity-ktx:1.6.1")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,5 @@
|
|||||||
package com.ray650128.gstreamer_demo_app
|
package com.ray650128.gstreamer_demo_app
|
||||||
|
|
||||||
object Constants {
|
object Constants {
|
||||||
//region Split mode
|
|
||||||
const val SPLIT_MODE_SIXTEEN = 16
|
|
||||||
const val SPLIT_MODE_NINE = 9
|
|
||||||
const val SPLIT_MODE_FOUR = 4
|
|
||||||
const val SPLIT_MODE_SINGLE = 1
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
const val CONF_DELAY_BASE_MILLIS = 1000L
|
const val CONF_DELAY_BASE_MILLIS = 1000L
|
||||||
}
|
}
|
||||||
@ -1,94 +0,0 @@
|
|||||||
package com.ray650128.gstreamer_demo_app
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.os.Build
|
|
||||||
import android.util.DisplayMetrics
|
|
||||||
import android.view.WindowManager
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 螢幕參數工具類別
|
|
||||||
* @author Raymond Yang
|
|
||||||
*/
|
|
||||||
class DisplayUtils(private var context: Context) {
|
|
||||||
private val TAG = DisplayUtils::class.java.simpleName
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 取得螢幕寬度
|
|
||||||
* @return 螢幕寬度值
|
|
||||||
*/
|
|
||||||
fun getScreenWidth(): Int {
|
|
||||||
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
|
||||||
|
|
||||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
||||||
val windowMetrics = windowManager.currentWindowMetrics
|
|
||||||
windowMetrics.bounds.width()
|
|
||||||
} else {
|
|
||||||
val metric = DisplayMetrics()
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
windowManager.defaultDisplay.getMetrics(metric)
|
|
||||||
metric.widthPixels
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 取得螢幕高度
|
|
||||||
* @return 螢幕高度值
|
|
||||||
*/
|
|
||||||
fun getScreenHeight(): Int {
|
|
||||||
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
|
||||||
|
|
||||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
||||||
val windowMetrics = windowManager.currentWindowMetrics
|
|
||||||
windowMetrics.bounds.height()
|
|
||||||
} else {
|
|
||||||
val metric = DisplayMetrics()
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
windowManager.defaultDisplay.getMetrics(metric)
|
|
||||||
metric.heightPixels
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 取得狀態列高度
|
|
||||||
* @return 狀態列高度值
|
|
||||||
*/
|
|
||||||
fun getStatusBarHeight(): Int {
|
|
||||||
var result = 0
|
|
||||||
val resourceId: Int = context.resources.getIdentifier(
|
|
||||||
"status_bar_height",
|
|
||||||
"dimen",
|
|
||||||
"android"
|
|
||||||
)
|
|
||||||
if (resourceId > 0) {
|
|
||||||
result = context.resources.getDimensionPixelSize(resourceId)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 取得導航列高度
|
|
||||||
* @return 導航列高度值
|
|
||||||
*/
|
|
||||||
fun getNavigationBarHeight(): Int {
|
|
||||||
val resources = context.resources
|
|
||||||
val resourceId: Int = resources.getIdentifier("navigation_bar_height", "dimen", "android")
|
|
||||||
return if (resourceId > 0) {
|
|
||||||
resources.getDimensionPixelSize(resourceId)
|
|
||||||
} else 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 取得視窗範圍高度
|
|
||||||
* @return 視窗範圍高度值
|
|
||||||
*/
|
|
||||||
fun getWindowHeight(): Int {
|
|
||||||
val screen = getScreenHeight()
|
|
||||||
val statusBar = getStatusBarHeight()
|
|
||||||
val navBar = getNavigationBarHeight()
|
|
||||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
||||||
screen - statusBar - navBar
|
|
||||||
} else {
|
|
||||||
screen
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -19,6 +19,6 @@ object PreferenceUtil {
|
|||||||
set(value) = sharedPreferences.edit().putBoolean(IS_FIRST_OPEN_KEY, value).apply()
|
set(value) = sharedPreferences.edit().putBoolean(IS_FIRST_OPEN_KEY, value).apply()
|
||||||
|
|
||||||
var lastSplitMode: Int
|
var lastSplitMode: Int
|
||||||
get() = sharedPreferences.getInt(LAST_SPLIT_MODE, Constants.SPLIT_MODE_SINGLE)
|
get() = sharedPreferences.getInt(LAST_SPLIT_MODE, MainViewModel.PAGE_MODE_ONE)
|
||||||
set(value) = sharedPreferences.edit().putInt(LAST_SPLIT_MODE, value).apply()
|
set(value) = sharedPreferences.edit().putInt(LAST_SPLIT_MODE, value).apply()
|
||||||
}
|
}
|
||||||
@ -1,484 +0,0 @@
|
|||||||
package com.ray650128.gstreamer_demo_app.ui;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.util.DisplayMetrics;
|
|
||||||
import android.view.Display;
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.VelocityTracker;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewConfiguration;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.WindowManager;
|
|
||||||
import android.widget.Scroller;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A view group that allows users to switch between multiple screens (layouts) in the same way as
|
|
||||||
* the Android home screen (Launcher application).
|
|
||||||
* <p>
|
|
||||||
* You can add and remove views using the normal methods {@link ViewGroup#addView(View)},
|
|
||||||
* {@link ViewGroup#removeView(View)} etc. You may want to listen for updates by calling
|
|
||||||
* {@link HorizontalPager#setOnScreenSwitchListener(OnScreenSwitchListener)} in order to perform
|
|
||||||
* operations once a new screen has been selected.
|
|
||||||
*
|
|
||||||
* Modifications from original version (ysamlan): Animate argument in setCurrentScreen and duration
|
|
||||||
* in snapToScreen; onInterceptTouchEvent handling to support nesting a vertical Scrollview inside
|
|
||||||
* the RealViewSwitcher; allowing snapping to a view even during an ongoing scroll; snap to
|
|
||||||
* next/prev view on 25% scroll change; density-independent swipe sensitivity; width-independent
|
|
||||||
* pager animation durations on scrolling to properly handle large screens without excessively
|
|
||||||
* long animations.
|
|
||||||
*
|
|
||||||
* Other modifications:
|
|
||||||
* (aveyD) Handle orientation changes properly and fully snap to the right position.
|
|
||||||
*
|
|
||||||
* @author Marc Reichelt, <a href="http://www.marcreichelt.de/">http://www.marcreichelt.de/</a>
|
|
||||||
* @version 0.1.0
|
|
||||||
*/
|
|
||||||
public final class HorizontalPager extends ViewGroup {
|
|
||||||
/*
|
|
||||||
* How long to animate between screens when programmatically setting with setCurrentScreen using
|
|
||||||
* the animate parameter
|
|
||||||
*/
|
|
||||||
private static final int ANIMATION_SCREEN_SET_DURATION_MILLIS = 500;
|
|
||||||
// What fraction (1/x) of the screen the user must swipe to indicate a page change
|
|
||||||
private static final int FRACTION_OF_SCREEN_WIDTH_FOR_SWIPE = 4;
|
|
||||||
private static final int INVALID_SCREEN = -1;
|
|
||||||
/*
|
|
||||||
* Velocity of a swipe (in density-independent pixels per second) to force a swipe to the
|
|
||||||
* next/previous screen. Adjusted into mDensityAdjustedSnapVelocity on init.
|
|
||||||
*/
|
|
||||||
private static final int SNAP_VELOCITY_DIP_PER_SECOND = 600;
|
|
||||||
// Argument to getVelocity for units to give pixels per second (1 = pixels per millisecond).
|
|
||||||
private static final int VELOCITY_UNIT_PIXELS_PER_SECOND = 1000;
|
|
||||||
|
|
||||||
private static final int TOUCH_STATE_REST = 0;
|
|
||||||
private static final int TOUCH_STATE_HORIZONTAL_SCROLLING = 1;
|
|
||||||
private static final int TOUCH_STATE_VERTICAL_SCROLLING = -1;
|
|
||||||
private int mCurrentScreen;
|
|
||||||
private int mDensityAdjustedSnapVelocity;
|
|
||||||
private boolean mFirstLayout = true;
|
|
||||||
private float mLastMotionX;
|
|
||||||
private float mLastMotionY;
|
|
||||||
private OnScreenSwitchListener mOnScreenSwitchListener;
|
|
||||||
private int mMaximumVelocity;
|
|
||||||
private int mNextScreen = INVALID_SCREEN;
|
|
||||||
private Scroller mScroller;
|
|
||||||
private int mTouchSlop;
|
|
||||||
private int mTouchState = TOUCH_STATE_REST;
|
|
||||||
private VelocityTracker mVelocityTracker;
|
|
||||||
private int mLastSeenLayoutWidth = -1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple constructor to use when creating a view from code.
|
|
||||||
*
|
|
||||||
* @param context The Context the view is running in, through which it can
|
|
||||||
* access the current theme, resources, etc.
|
|
||||||
*/
|
|
||||||
public HorizontalPager(final Context context) {
|
|
||||||
super(context);
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor that is called when inflating a view from XML. This is called
|
|
||||||
* when a view is being constructed from an XML file, supplying attributes
|
|
||||||
* that were specified in the XML file. This version uses a default style of
|
|
||||||
* 0, so the only attribute values applied are those in the Context's Theme
|
|
||||||
* and the given AttributeSet.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* The method onFinishInflate() will be called after all children have been
|
|
||||||
* added.
|
|
||||||
*
|
|
||||||
* @param context The Context the view is running in, through which it can
|
|
||||||
* access the current theme, resources, etc.
|
|
||||||
* @param attrs The attributes of the XML tag that is inflating the view.
|
|
||||||
* @see #View(Context, AttributeSet, int)
|
|
||||||
*/
|
|
||||||
public HorizontalPager(final Context context, final AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets up the scroller and touch/fling sensitivity parameters for the pager.
|
|
||||||
*/
|
|
||||||
private void init() {
|
|
||||||
mScroller = new Scroller(getContext());
|
|
||||||
|
|
||||||
// Calculate the density-dependent snap velocity in pixels
|
|
||||||
DisplayMetrics displayMetrics = new DisplayMetrics();
|
|
||||||
((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay()
|
|
||||||
.getMetrics(displayMetrics);
|
|
||||||
mDensityAdjustedSnapVelocity =
|
|
||||||
(int) (displayMetrics.density * SNAP_VELOCITY_DIP_PER_SECOND);
|
|
||||||
|
|
||||||
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
|
|
||||||
mTouchSlop = configuration.getScaledTouchSlop();
|
|
||||||
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
|
|
||||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
|
||||||
|
|
||||||
final int width = MeasureSpec.getSize(widthMeasureSpec);
|
|
||||||
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
|
|
||||||
if (widthMode != MeasureSpec.EXACTLY) {
|
|
||||||
throw new IllegalStateException("ViewSwitcher can only be used in EXACTLY mode.");
|
|
||||||
}
|
|
||||||
|
|
||||||
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
|
|
||||||
if (heightMode != MeasureSpec.EXACTLY) {
|
|
||||||
throw new IllegalStateException("ViewSwitcher can only be used in EXACTLY mode.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// The children are given the same width and height as the workspace
|
|
||||||
final int count = getChildCount();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mFirstLayout) {
|
|
||||||
scrollTo(mCurrentScreen * width, 0);
|
|
||||||
mFirstLayout = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (width != mLastSeenLayoutWidth) { // Width has changed
|
|
||||||
/*
|
|
||||||
* Recalculate the width and scroll to the right position to be sure we're in the right
|
|
||||||
* place in the event that we had a rotation that didn't result in an activity restart
|
|
||||||
* (code by aveyD). Without this you can end up between two pages after a rotation.
|
|
||||||
*/
|
|
||||||
Display display =
|
|
||||||
((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE))
|
|
||||||
.getDefaultDisplay();
|
|
||||||
int displayWidth = display.getWidth();
|
|
||||||
|
|
||||||
mNextScreen = Math.max(0, Math.min(getCurrentScreen(), getChildCount() - 1));
|
|
||||||
final int newX = mNextScreen * displayWidth;
|
|
||||||
final int delta = newX - getScrollX();
|
|
||||||
|
|
||||||
mScroller.startScroll(getScrollX(), 0, delta, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
mLastSeenLayoutWidth = width;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onLayout(final boolean changed, final int l, final int t, final int r,
|
|
||||||
final int b) {
|
|
||||||
int childLeft = 0;
|
|
||||||
final int count = getChildCount();
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
final View child = getChildAt(i);
|
|
||||||
if (child.getVisibility() != View.GONE) {
|
|
||||||
final int childWidth = child.getMeasuredWidth();
|
|
||||||
child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight());
|
|
||||||
childLeft += childWidth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onInterceptTouchEvent(final MotionEvent ev) {
|
|
||||||
/*
|
|
||||||
* By Yoni Samlan: Modified onInterceptTouchEvent based on standard ScrollView's
|
|
||||||
* onIntercept. The logic is designed to support a nested vertically scrolling view inside
|
|
||||||
* this one; once a scroll registers for X-wise scrolling, handle it in this view and don't
|
|
||||||
* let the children, but once a scroll registers for y-wise scrolling, let the children
|
|
||||||
* handle it exclusively.
|
|
||||||
*/
|
|
||||||
final int action = ev.getAction();
|
|
||||||
boolean intercept = false;
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case MotionEvent.ACTION_MOVE:
|
|
||||||
/*
|
|
||||||
* If we're in a horizontal scroll event, take it (intercept further events). But if
|
|
||||||
* we're mid-vertical-scroll, don't even try; let the children deal with it. If we
|
|
||||||
* haven't found a scroll event yet, check for one.
|
|
||||||
*/
|
|
||||||
if (mTouchState == TOUCH_STATE_HORIZONTAL_SCROLLING) {
|
|
||||||
/*
|
|
||||||
* We've already started a horizontal scroll; set intercept to true so we can
|
|
||||||
* take the remainder of all touch events in onTouchEvent.
|
|
||||||
*/
|
|
||||||
intercept = true;
|
|
||||||
} else if (mTouchState == TOUCH_STATE_VERTICAL_SCROLLING) {
|
|
||||||
// Let children handle the events for the duration of the scroll event.
|
|
||||||
intercept = false;
|
|
||||||
} else { // We haven't picked up a scroll event yet; check for one.
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If we detected a horizontal scroll event, start stealing touch events (mark
|
|
||||||
* as scrolling). Otherwise, see if we had a vertical scroll event -- if so, let
|
|
||||||
* the children handle it and don't look to intercept again until the motion is
|
|
||||||
* done.
|
|
||||||
*/
|
|
||||||
|
|
||||||
final float x = ev.getX();
|
|
||||||
final int xDiff = (int) Math.abs(x - mLastMotionX);
|
|
||||||
boolean xMoved = xDiff > mTouchSlop;
|
|
||||||
|
|
||||||
if (xMoved) {
|
|
||||||
// Scroll if the user moved far enough along the X axis
|
|
||||||
mTouchState = TOUCH_STATE_HORIZONTAL_SCROLLING;
|
|
||||||
mLastMotionX = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
final float y = ev.getY();
|
|
||||||
final int yDiff = (int) Math.abs(y - mLastMotionY);
|
|
||||||
boolean yMoved = yDiff > mTouchSlop;
|
|
||||||
|
|
||||||
if (yMoved) {
|
|
||||||
mTouchState = TOUCH_STATE_VERTICAL_SCROLLING;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case MotionEvent.ACTION_CANCEL:
|
|
||||||
case MotionEvent.ACTION_UP:
|
|
||||||
// Release the drag.
|
|
||||||
mTouchState = TOUCH_STATE_REST;
|
|
||||||
break;
|
|
||||||
case MotionEvent.ACTION_DOWN:
|
|
||||||
/*
|
|
||||||
* No motion yet, but register the coordinates so we can check for intercept at the
|
|
||||||
* next MOVE event.
|
|
||||||
*/
|
|
||||||
mLastMotionY = ev.getY();
|
|
||||||
mLastMotionX = ev.getX();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return intercept;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onTouchEvent(final MotionEvent ev) {
|
|
||||||
|
|
||||||
if (mVelocityTracker == null) {
|
|
||||||
mVelocityTracker = VelocityTracker.obtain();
|
|
||||||
}
|
|
||||||
mVelocityTracker.addMovement(ev);
|
|
||||||
|
|
||||||
final int action = ev.getAction();
|
|
||||||
final float x = ev.getX();
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case MotionEvent.ACTION_DOWN:
|
|
||||||
/*
|
|
||||||
* If being flinged and user touches, stop the fling. isFinished will be false if
|
|
||||||
* being flinged.
|
|
||||||
*/
|
|
||||||
if (!mScroller.isFinished()) {
|
|
||||||
mScroller.abortAnimation();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remember where the motion event started
|
|
||||||
mLastMotionX = x;
|
|
||||||
|
|
||||||
if (mScroller.isFinished()) {
|
|
||||||
mTouchState = TOUCH_STATE_REST;
|
|
||||||
} else {
|
|
||||||
mTouchState = TOUCH_STATE_HORIZONTAL_SCROLLING;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case MotionEvent.ACTION_MOVE:
|
|
||||||
final int xDiff = (int) Math.abs(x - mLastMotionX);
|
|
||||||
boolean xMoved = xDiff > mTouchSlop;
|
|
||||||
|
|
||||||
if (xMoved) {
|
|
||||||
// Scroll if the user moved far enough along the X axis
|
|
||||||
mTouchState = TOUCH_STATE_HORIZONTAL_SCROLLING;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mTouchState == TOUCH_STATE_HORIZONTAL_SCROLLING) {
|
|
||||||
// Scroll to follow the motion event
|
|
||||||
final int deltaX = (int) (mLastMotionX - x);
|
|
||||||
mLastMotionX = x;
|
|
||||||
final int scrollX = getScrollX();
|
|
||||||
|
|
||||||
if (deltaX < 0) {
|
|
||||||
if (scrollX > 0) {
|
|
||||||
scrollBy(Math.max(-scrollX, deltaX), 0);
|
|
||||||
}
|
|
||||||
} else if (deltaX > 0) {
|
|
||||||
final int availableToScroll =
|
|
||||||
getChildAt(getChildCount() - 1).getRight() - scrollX - getWidth();
|
|
||||||
|
|
||||||
if (availableToScroll > 0) {
|
|
||||||
scrollBy(Math.min(availableToScroll, deltaX), 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MotionEvent.ACTION_UP:
|
|
||||||
if (mTouchState == TOUCH_STATE_HORIZONTAL_SCROLLING) {
|
|
||||||
final VelocityTracker velocityTracker = mVelocityTracker;
|
|
||||||
velocityTracker.computeCurrentVelocity(VELOCITY_UNIT_PIXELS_PER_SECOND,
|
|
||||||
mMaximumVelocity);
|
|
||||||
int velocityX = (int) velocityTracker.getXVelocity();
|
|
||||||
|
|
||||||
if (velocityX > mDensityAdjustedSnapVelocity && mCurrentScreen > 0) {
|
|
||||||
// Fling hard enough to move left
|
|
||||||
snapToScreen(mCurrentScreen - 1);
|
|
||||||
} else if (velocityX < -mDensityAdjustedSnapVelocity
|
|
||||||
&& mCurrentScreen < getChildCount() - 1) {
|
|
||||||
// Fling hard enough to move right
|
|
||||||
snapToScreen(mCurrentScreen + 1);
|
|
||||||
} else {
|
|
||||||
snapToDestination();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mVelocityTracker != null) {
|
|
||||||
mVelocityTracker.recycle();
|
|
||||||
mVelocityTracker = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mTouchState = TOUCH_STATE_REST;
|
|
||||||
|
|
||||||
break;
|
|
||||||
case MotionEvent.ACTION_CANCEL:
|
|
||||||
mTouchState = TOUCH_STATE_REST;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void computeScroll() {
|
|
||||||
if (mScroller.computeScrollOffset()) {
|
|
||||||
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
|
|
||||||
postInvalidate();
|
|
||||||
} else if (mNextScreen != INVALID_SCREEN) {
|
|
||||||
mCurrentScreen = Math.max(0, Math.min(mNextScreen, getChildCount() - 1));
|
|
||||||
|
|
||||||
// Notify observer about screen change
|
|
||||||
if (mOnScreenSwitchListener != null) {
|
|
||||||
mOnScreenSwitchListener.onScreenSwitched(mCurrentScreen);
|
|
||||||
}
|
|
||||||
|
|
||||||
mNextScreen = INVALID_SCREEN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the index of the currently displayed screen.
|
|
||||||
*
|
|
||||||
* @return The index of the currently displayed screen.
|
|
||||||
*/
|
|
||||||
public int getCurrentScreen() {
|
|
||||||
return mCurrentScreen;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the current screen.
|
|
||||||
*
|
|
||||||
* @param currentScreen The new screen.
|
|
||||||
* @param animate True to smoothly scroll to the screen, false to snap instantly
|
|
||||||
*/
|
|
||||||
public void setCurrentScreen(final int currentScreen, final boolean animate) {
|
|
||||||
mCurrentScreen = Math.max(0, Math.min(currentScreen, getChildCount() - 1));
|
|
||||||
if (animate) {
|
|
||||||
snapToScreen(currentScreen, ANIMATION_SCREEN_SET_DURATION_MILLIS);
|
|
||||||
} else {
|
|
||||||
scrollTo(mCurrentScreen * getWidth(), 0);
|
|
||||||
}
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the {@link OnScreenSwitchListener}.
|
|
||||||
*
|
|
||||||
* @param onScreenSwitchListener The listener for switch events.
|
|
||||||
*/
|
|
||||||
public void setOnScreenSwitchListener(final OnScreenSwitchListener onScreenSwitchListener) {
|
|
||||||
mOnScreenSwitchListener = onScreenSwitchListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Snaps to the screen we think the user wants (the current screen for very small movements; the
|
|
||||||
* next/prev screen for bigger movements).
|
|
||||||
*/
|
|
||||||
private void snapToDestination() {
|
|
||||||
final int screenWidth = getWidth();
|
|
||||||
int scrollX = getScrollX();
|
|
||||||
int whichScreen = mCurrentScreen;
|
|
||||||
int deltaX = scrollX - (screenWidth * mCurrentScreen);
|
|
||||||
|
|
||||||
// Check if they want to go to the prev. screen
|
|
||||||
if ((deltaX < 0) && mCurrentScreen != 0
|
|
||||||
&& ((screenWidth / FRACTION_OF_SCREEN_WIDTH_FOR_SWIPE) < -deltaX)) {
|
|
||||||
whichScreen--;
|
|
||||||
// Check if they want to go to the next screen
|
|
||||||
} else if ((deltaX > 0) && (mCurrentScreen + 1 != getChildCount())
|
|
||||||
&& ((screenWidth / FRACTION_OF_SCREEN_WIDTH_FOR_SWIPE) < deltaX)) {
|
|
||||||
whichScreen++;
|
|
||||||
}
|
|
||||||
|
|
||||||
snapToScreen(whichScreen);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Snap to a specific screen, animating automatically for a duration proportional to the
|
|
||||||
* distance left to scroll.
|
|
||||||
*
|
|
||||||
* @param whichScreen Screen to snap to
|
|
||||||
*/
|
|
||||||
private void snapToScreen(final int whichScreen) {
|
|
||||||
snapToScreen(whichScreen, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Snaps to a specific screen, animating for a specific amount of time to get there.
|
|
||||||
*
|
|
||||||
* @param whichScreen Screen to snap to
|
|
||||||
* @param duration -1 to automatically time it based on scroll distance; a positive number to
|
|
||||||
* make the scroll take an exact duration.
|
|
||||||
*/
|
|
||||||
private void snapToScreen(final int whichScreen, final int duration) {
|
|
||||||
/*
|
|
||||||
* Modified by Yoni Samlan: Allow new snapping even during an ongoing scroll animation. This
|
|
||||||
* is intended to make HorizontalPager work as expected when used in conjunction with a
|
|
||||||
* RadioGroup used as "tabbed" controls. Also, make the animation take a percentage of our
|
|
||||||
* normal animation time, depending how far they've already scrolled.
|
|
||||||
*/
|
|
||||||
mNextScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
|
|
||||||
final int newX = mNextScreen * getWidth();
|
|
||||||
final int delta = newX - getScrollX();
|
|
||||||
|
|
||||||
if (duration < 0) {
|
|
||||||
// E.g. if they've scrolled 80% of the way, only animation for 20% of the duration
|
|
||||||
mScroller.startScroll(getScrollX(), 0, delta, 0, (int) (Math.abs(delta)
|
|
||||||
/ (float) getWidth() * ANIMATION_SCREEN_SET_DURATION_MILLIS));
|
|
||||||
} else {
|
|
||||||
mScroller.startScroll(getScrollX(), 0, delta, 0, duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listener for the event that the HorizontalPager switches to a new view.
|
|
||||||
*/
|
|
||||||
public static interface OnScreenSwitchListener {
|
|
||||||
/**
|
|
||||||
* Notifies listeners about the new screen. Runs after the animation completed.
|
|
||||||
*
|
|
||||||
* @param screen The new screen index.
|
|
||||||
*/
|
|
||||||
void onScreenSwitched(int screen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -4,12 +4,9 @@ import android.content.Context
|
|||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.View
|
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.widget.FrameLayout
|
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.fragment.app.commit
|
|
||||||
import androidx.viewpager2.widget.ViewPager2
|
import androidx.viewpager2.widget.ViewPager2
|
||||||
import com.ray650128.gstreamer_demo_app.Constants
|
import com.ray650128.gstreamer_demo_app.Constants
|
||||||
import com.ray650128.gstreamer_demo_app.R
|
import com.ray650128.gstreamer_demo_app.R
|
||||||
@ -19,23 +16,24 @@ import kotlinx.coroutines.MainScope
|
|||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
/**
|
|
||||||
* MainActivity.kt
|
|
||||||
* 應用程式主畫面
|
|
||||||
*/
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
private lateinit var binding: ActivityMainBinding
|
private lateinit var binding: ActivityMainBinding
|
||||||
|
|
||||||
private val viewModel: MainViewModel by viewModels()
|
private val viewModel: MainViewModel by viewModels()
|
||||||
|
|
||||||
|
private val splitViewModel: SplitViewModel by viewModels()
|
||||||
|
|
||||||
private val mContext: Context by lazy { this }
|
private val mContext: Context by lazy { this }
|
||||||
|
|
||||||
private var splitMode = 1
|
private var splitMode = 1
|
||||||
|
private var oldSplitMode = 1
|
||||||
|
|
||||||
|
private lateinit var splitVideoViewAdapter: VideoViewAdapter
|
||||||
|
|
||||||
private var videos: List<List<Device>>? = null
|
private var videos: List<List<Device>>? = null
|
||||||
|
|
||||||
private var videoPageList: ArrayList<SplitViewFragment> = arrayListOf()
|
private var currentPage = 0
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
@ -54,31 +52,68 @@ class MainActivity : AppCompatActivity() {
|
|||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
for (i in 0 until splitVideoViewAdapter.itemCount) {
|
||||||
|
splitVideoViewAdapter.destroy(i)
|
||||||
|
}
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
private fun initContentView() = binding.apply {
|
private fun initContentView() = binding.apply {
|
||||||
//region Content area
|
//region Content area
|
||||||
|
splitVideoViewAdapter = VideoViewAdapter(supportFragmentManager, lifecycle)
|
||||||
|
|
||||||
|
viewPager.apply {
|
||||||
|
adapter = splitVideoViewAdapter
|
||||||
|
offscreenPageLimit = 100
|
||||||
|
setPageTransformer(null)
|
||||||
|
registerOnPageChangeCallback(object: ViewPager2.OnPageChangeCallback() {
|
||||||
|
private var oldPage = 0
|
||||||
|
override fun onPageScrollStateChanged(state: Int) {
|
||||||
|
super.onPageScrollStateChanged(state)
|
||||||
|
/*if (state == ViewPager2.SCROLL_STATE_DRAGGING) {
|
||||||
|
oldPage = currentPage
|
||||||
|
splitVideoViewAdapter.stop(currentPage)
|
||||||
|
}
|
||||||
|
if (state == ViewPager2.SCROLL_STATE_IDLE && currentPage == oldPage) {
|
||||||
|
splitVideoViewAdapter.play(currentPage)
|
||||||
|
}*/
|
||||||
|
//Log.d("Split", "oldPage: $oldPage, currentPage: $currentPage")
|
||||||
|
}
|
||||||
|
override fun onPageScrolled(
|
||||||
|
position: Int,
|
||||||
|
positionOffset: Float,
|
||||||
|
positionOffsetPixels: Int
|
||||||
|
) {
|
||||||
|
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
|
||||||
|
//Log.d("Split", "onPageScrolled: $currentPage")
|
||||||
|
}
|
||||||
|
override fun onPageSelected(position: Int) {
|
||||||
|
super.onPageSelected(position)
|
||||||
|
currentPage = position
|
||||||
|
splitViewModel.activePage.postValue(position)
|
||||||
|
//Log.d("Split", "currentPage: $currentPage")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
button.setOnClickListener {
|
button.setOnClickListener {
|
||||||
if (splitMode == Constants.SPLIT_MODE_SINGLE) return@setOnClickListener
|
if (splitMode == MainViewModel.PAGE_MODE_ONE) return@setOnClickListener
|
||||||
viewModel.setSplitMode(Constants.SPLIT_MODE_SINGLE)
|
viewModel.setSplitMode(MainViewModel.PAGE_MODE_ONE)
|
||||||
Log.e(TAG, "+++ split style: 1")
|
Log.e(TAG, "+++ split style: 1")
|
||||||
}
|
}
|
||||||
|
|
||||||
button2.setOnClickListener {
|
button2.setOnClickListener {
|
||||||
if (splitMode == Constants.SPLIT_MODE_FOUR) return@setOnClickListener
|
if (splitMode == MainViewModel.PAGE_MODE_FOUR) return@setOnClickListener
|
||||||
viewModel.setSplitMode(Constants.SPLIT_MODE_FOUR)
|
viewModel.setSplitMode(MainViewModel.PAGE_MODE_FOUR)
|
||||||
Log.e(TAG, "+++ split style: 4")
|
Log.e(TAG, "+++ split style: 4")
|
||||||
}
|
}
|
||||||
|
|
||||||
button3.setOnClickListener {
|
button3.setOnClickListener {
|
||||||
if (splitMode == Constants.SPLIT_MODE_NINE) return@setOnClickListener
|
if (splitMode == MainViewModel.PAGE_MODE_NINE) return@setOnClickListener
|
||||||
viewModel.setSplitMode(Constants.SPLIT_MODE_NINE)
|
viewModel.setSplitMode(MainViewModel.PAGE_MODE_NINE)
|
||||||
Log.e(TAG, "+++ split style: 9")
|
Log.e(TAG, "+++ split style: 9")
|
||||||
}
|
}
|
||||||
|
|
||||||
button4.setOnClickListener {
|
|
||||||
if (splitMode == Constants.SPLIT_MODE_SIXTEEN) return@setOnClickListener
|
|
||||||
viewModel.setSplitMode(Constants.SPLIT_MODE_SIXTEEN)
|
|
||||||
Log.e(TAG, "+++ split style: 16")
|
|
||||||
}
|
|
||||||
//endregion
|
//endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,18 +126,19 @@ class MainActivity : AppCompatActivity() {
|
|||||||
viewModel.cameraList.observe(this) { list ->
|
viewModel.cameraList.observe(this) { list ->
|
||||||
this.videos = list
|
this.videos = list
|
||||||
reloadVideoViews(this.videos)
|
reloadVideoViews(this.videos)
|
||||||
|
currentPage = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reloadVideoViews(list: List<List<Device>>?) {
|
private fun reloadVideoViews(list: List<List<Device>>?) = MainScope().launch {
|
||||||
for (videoPage in videoPageList) {
|
binding.viewPager.setCurrentItem(0, false)
|
||||||
supportFragmentManager.commit {
|
for (i in 0 until splitVideoViewAdapter.itemCount) {
|
||||||
remove(videoPage)
|
splitVideoViewAdapter.stop(i)
|
||||||
}
|
}
|
||||||
}
|
//delay(oldSplitMode * Constants.CONF_DELAY_BASE_MILLIS)
|
||||||
binding.viewPager.removeAllViews()
|
delay((oldSplitMode * 100) + Constants.CONF_DELAY_BASE_MILLIS)
|
||||||
videoPageList.clear()
|
oldSplitMode = splitMode
|
||||||
|
splitVideoViewAdapter.clear()
|
||||||
// 如果群組內沒有裝置,則顯示底圖
|
// 如果群組內沒有裝置,則顯示底圖
|
||||||
if (list.isNullOrEmpty()) {
|
if (list.isNullOrEmpty()) {
|
||||||
binding.viewPager.setBackgroundResource(R.drawable.bg_not_in_playing)
|
binding.viewPager.setBackgroundResource(R.drawable.bg_not_in_playing)
|
||||||
@ -114,16 +150,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
splitMode = splitMode,
|
splitMode = splitMode,
|
||||||
pageData = ArrayList(list[i])
|
pageData = ArrayList(list[i])
|
||||||
)
|
)
|
||||||
videoPageList.add(i, splitFragment)
|
splitVideoViewAdapter.add(i, splitFragment)
|
||||||
|
|
||||||
val frameLayout = FrameLayout(mContext).apply {
|
|
||||||
id = View.generateViewId()
|
|
||||||
}
|
|
||||||
binding.viewPager.addView(frameLayout)
|
|
||||||
supportFragmentManager.commit {
|
|
||||||
add(frameLayout.id, splitFragment, "$i")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
currentPage = 0
|
||||||
|
//binding.viewPager.currentItem = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,7 @@ class MainViewModel: ViewModel() {
|
|||||||
stream1 = "/media/video1",
|
stream1 = "/media/video1",
|
||||||
stream2 = "/media/video2",
|
stream2 = "/media/video2",
|
||||||
),
|
),
|
||||||
/*Device(
|
Device(
|
||||||
deviceName = "192.168.0.73",
|
deviceName = "192.168.0.73",
|
||||||
ip = "192.168.0.73",
|
ip = "192.168.0.73",
|
||||||
rtspPort = "554",
|
rtspPort = "554",
|
||||||
@ -27,8 +27,8 @@ class MainViewModel: ViewModel() {
|
|||||||
password = "hs22601576",
|
password = "hs22601576",
|
||||||
stream1 = "/media/video1",
|
stream1 = "/media/video1",
|
||||||
stream2 = "/media/video2",
|
stream2 = "/media/video2",
|
||||||
),*/
|
),
|
||||||
/*Device(
|
Device(
|
||||||
deviceName = "192.168.0.79",
|
deviceName = "192.168.0.79",
|
||||||
ip = "192.168.0.79",
|
ip = "192.168.0.79",
|
||||||
rtspPort = "554",
|
rtspPort = "554",
|
||||||
@ -36,7 +36,7 @@ class MainViewModel: ViewModel() {
|
|||||||
password = "1q2w3e4r!",
|
password = "1q2w3e4r!",
|
||||||
stream1 = "/media/video1",
|
stream1 = "/media/video1",
|
||||||
stream2 = "/media/video2",
|
stream2 = "/media/video2",
|
||||||
),*/
|
),
|
||||||
Device(
|
Device(
|
||||||
deviceName = "192.168.0.88",
|
deviceName = "192.168.0.88",
|
||||||
ip = "211.23.78.226",
|
ip = "211.23.78.226",
|
||||||
@ -64,7 +64,7 @@ class MainViewModel: ViewModel() {
|
|||||||
stream1 = "/v01",
|
stream1 = "/v01",
|
||||||
stream2 = "/v02",
|
stream2 = "/v02",
|
||||||
),
|
),
|
||||||
/*Device(
|
Device(
|
||||||
deviceName = "192.168.0.76",
|
deviceName = "192.168.0.76",
|
||||||
ip = "211.23.78.226",
|
ip = "211.23.78.226",
|
||||||
rtspPort = "8576",
|
rtspPort = "8576",
|
||||||
@ -72,7 +72,7 @@ class MainViewModel: ViewModel() {
|
|||||||
password = "123456",
|
password = "123456",
|
||||||
stream1 = "/profile1",
|
stream1 = "/profile1",
|
||||||
stream2 = "/profile2",
|
stream2 = "/profile2",
|
||||||
),*/
|
),
|
||||||
Device(
|
Device(
|
||||||
deviceName = "192.168.0.82",
|
deviceName = "192.168.0.82",
|
||||||
ip = "192.168.0.82",
|
ip = "192.168.0.82",
|
||||||
@ -91,7 +91,7 @@ class MainViewModel: ViewModel() {
|
|||||||
stream1 = "/profile1",
|
stream1 = "/profile1",
|
||||||
stream2 = "/profile2",
|
stream2 = "/profile2",
|
||||||
),
|
),
|
||||||
/*Device(
|
Device(
|
||||||
deviceName = "192.168.0.95",
|
deviceName = "192.168.0.95",
|
||||||
ip = "192.168.0.95",
|
ip = "192.168.0.95",
|
||||||
rtspPort = "554",
|
rtspPort = "554",
|
||||||
@ -99,7 +99,7 @@ class MainViewModel: ViewModel() {
|
|||||||
password = "123456",
|
password = "123456",
|
||||||
stream1 = "/profile1",
|
stream1 = "/profile1",
|
||||||
stream2 = "/profile2",
|
stream2 = "/profile2",
|
||||||
)*/
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,4 +137,10 @@ class MainViewModel: ViewModel() {
|
|||||||
}
|
}
|
||||||
return tmpData
|
return tmpData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val PAGE_MODE_ONE = 1
|
||||||
|
const val PAGE_MODE_FOUR = 4
|
||||||
|
const val PAGE_MODE_NINE = 9
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -6,12 +6,10 @@ import android.util.Log
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.gridlayout.widget.GridLayout
|
import androidx.gridlayout.widget.GridLayout
|
||||||
import com.ray650128.gstreamer_demo_app.Constants
|
import com.ray650128.gstreamer_demo_app.Constants
|
||||||
import com.ray650128.gstreamer_demo_app.DisplayUtils
|
|
||||||
import com.ray650128.gstreamer_demo_app.R
|
|
||||||
import com.ray650128.gstreamer_demo_app.databinding.FragmentSplitViewBinding
|
import com.ray650128.gstreamer_demo_app.databinding.FragmentSplitViewBinding
|
||||||
import com.ray650128.gstreamer_demo_app.dp
|
import com.ray650128.gstreamer_demo_app.dp
|
||||||
import com.ray650128.gstreamer_demo_app.model.Device
|
import com.ray650128.gstreamer_demo_app.model.Device
|
||||||
@ -24,8 +22,10 @@ import kotlin.math.sqrt
|
|||||||
|
|
||||||
class SplitViewFragment : Fragment() {
|
class SplitViewFragment : Fragment() {
|
||||||
|
|
||||||
|
val viewModel: SplitViewModel by activityViewModels()
|
||||||
|
|
||||||
private var mPageNum: Int = 0
|
private var mPageNum: Int = 0
|
||||||
private var splitMode = Constants.SPLIT_MODE_SINGLE
|
private var splitMode = MainViewModel.PAGE_MODE_ONE
|
||||||
private var streamType = VideoView.SUB_STREAM
|
private var streamType = VideoView.SUB_STREAM
|
||||||
private var isClickable = true
|
private var isClickable = true
|
||||||
|
|
||||||
@ -56,6 +56,18 @@ class SplitViewFragment : Fragment() {
|
|||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
initView()
|
initView()
|
||||||
|
|
||||||
|
/*viewModel.activePage.observe(viewLifecycleOwner) {
|
||||||
|
if (it == null) return@observe
|
||||||
|
if (it == this.mPageNum) {
|
||||||
|
MainScope().launch {
|
||||||
|
//delay(1000)
|
||||||
|
//playAll()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//stopAll()
|
||||||
|
}
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
@ -71,14 +83,13 @@ class SplitViewFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
//destroyAll()
|
//destroyAll()
|
||||||
stopAll()
|
stopAll()
|
||||||
Log.d("${TAG}_$mPageNum", "onDestroy()")
|
Log.d("${TAG}_$mPageNum", "onDestroy()")
|
||||||
super.onDestroy()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initView() {
|
private fun initView() {
|
||||||
val displayUtil = DisplayUtils(requireContext())
|
|
||||||
// 生成 VideoView 分割畫面
|
// 生成 VideoView 分割畫面
|
||||||
binding.apply {
|
binding.apply {
|
||||||
val maxRow = sqrt(splitMode.toFloat()).toInt()
|
val maxRow = sqrt(splitMode.toFloat()).toInt()
|
||||||
@ -87,20 +98,9 @@ class SplitViewFragment : Fragment() {
|
|||||||
|
|
||||||
baseView.rowCount = maxRow
|
baseView.rowCount = maxRow
|
||||||
baseView.columnCount = maxCol
|
baseView.columnCount = maxCol
|
||||||
|
Log.e("${TAG}_$mPageNum", "baseView.rowCount: ${baseView.rowCount}, baseView.columnCount: ${baseView.columnCount}")
|
||||||
|
|
||||||
val cellWidth: Int
|
baseView.post {
|
||||||
val cellHeight: Int
|
|
||||||
when (splitMode) {
|
|
||||||
Constants.SPLIT_MODE_SINGLE -> {
|
|
||||||
cellWidth = (displayUtil.getScreenWidth() / maxRow)
|
|
||||||
cellHeight = (cellWidth * 0.5625).toInt()
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
cellWidth = (displayUtil.getScreenWidth() / maxRow) - maxRow.dp
|
|
||||||
cellHeight = (cellWidth * 0.5625).toInt() - maxCol.dp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (col in 0 until maxCol) {
|
for (col in 0 until maxCol) {
|
||||||
for (row in 0 until maxRow) {
|
for (row in 0 until maxRow) {
|
||||||
val videoView = VideoView(requireContext())
|
val videoView = VideoView(requireContext())
|
||||||
@ -110,12 +110,9 @@ class SplitViewFragment : Fragment() {
|
|||||||
marginEnd = 0.dp
|
marginEnd = 0.dp
|
||||||
marginStart = 0.dp
|
marginStart = 0.dp
|
||||||
|
|
||||||
width = cellWidth
|
|
||||||
height = cellHeight
|
|
||||||
|
|
||||||
// 調整間距
|
// 調整間距
|
||||||
when (splitMode) {
|
when (splitMode) {
|
||||||
Constants.SPLIT_MODE_FOUR -> {
|
MainViewModel.PAGE_MODE_FOUR -> {
|
||||||
when (col) {
|
when (col) {
|
||||||
0 -> bottomMargin = 2.dp
|
0 -> bottomMargin = 2.dp
|
||||||
1 -> topMargin = 2.dp
|
1 -> topMargin = 2.dp
|
||||||
@ -124,8 +121,10 @@ class SplitViewFragment : Fragment() {
|
|||||||
0 -> marginEnd = 2.dp
|
0 -> marginEnd = 2.dp
|
||||||
1 -> marginStart = 2.dp
|
1 -> marginStart = 2.dp
|
||||||
}
|
}
|
||||||
|
width = (baseView.width / maxRow) - maxRow.dp
|
||||||
|
height = (baseView.height / maxCol) - maxCol.dp
|
||||||
}
|
}
|
||||||
Constants.SPLIT_MODE_NINE -> {
|
MainViewModel.PAGE_MODE_NINE -> {
|
||||||
if (col == 1) {
|
if (col == 1) {
|
||||||
topMargin = 4.dp
|
topMargin = 4.dp
|
||||||
bottomMargin = 4.dp
|
bottomMargin = 4.dp
|
||||||
@ -134,24 +133,12 @@ class SplitViewFragment : Fragment() {
|
|||||||
marginEnd = 4.dp
|
marginEnd = 4.dp
|
||||||
marginStart = 4.dp
|
marginStart = 4.dp
|
||||||
}
|
}
|
||||||
|
width = (baseView.width / maxRow) - maxRow.dp
|
||||||
|
height = (baseView.height / maxCol) - maxCol.dp
|
||||||
}
|
}
|
||||||
Constants.SPLIT_MODE_SIXTEEN -> {
|
MainViewModel.PAGE_MODE_ONE -> {
|
||||||
if (col == 1) {
|
width = (baseView.width / maxRow)
|
||||||
topMargin = 4.dp
|
height = (baseView.height / maxCol)
|
||||||
bottomMargin = 2.dp
|
|
||||||
}
|
|
||||||
if (col == 2) {
|
|
||||||
topMargin = 2.dp
|
|
||||||
bottomMargin = 4.dp
|
|
||||||
}
|
|
||||||
if (row == 1) {
|
|
||||||
marginStart = 4.dp
|
|
||||||
marginEnd = 2.dp
|
|
||||||
}
|
|
||||||
if (row == 2) {
|
|
||||||
marginStart = 2.dp
|
|
||||||
marginEnd = 4.dp
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,16 +146,25 @@ class SplitViewFragment : Fragment() {
|
|||||||
videoViews.add(videoView)
|
videoViews.add(videoView)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
setAllUrl()
|
||||||
|
|
||||||
if (isClickable) {
|
if (isClickable) {
|
||||||
videoViews.forEach { videoView ->
|
for (position in videoViews.indices) {
|
||||||
videoView.setOnClickListener {
|
videoViews[position].setOnClickListener {
|
||||||
if (!videoView.isPlaying) {
|
if (position >= data.size) return@setOnClickListener
|
||||||
|
if (!videoViews[position].isPlaying) {
|
||||||
|
if (!videoViews[position].isLoading) {
|
||||||
|
videoViews[position].resetRetryCount()
|
||||||
|
videoViews[position].play()
|
||||||
|
}
|
||||||
return@setOnClickListener
|
return@setOnClickListener
|
||||||
}
|
}
|
||||||
|
MainScope().launch {
|
||||||
stopAll()
|
stopAll()
|
||||||
val item = videoView.data
|
delay(splitMode * Constants.CONF_DELAY_BASE_MILLIS)
|
||||||
|
//delay((splitMode * 100) + Constants.CONF_DELAY_BASE_MILLIS)
|
||||||
|
val item = data[position]
|
||||||
val bundle = Bundle().apply {
|
val bundle = Bundle().apply {
|
||||||
//putInt(MonitoringActivity.BUNDLE_DEVICE_ID, item.id)
|
//putInt(MonitoringActivity.BUNDLE_DEVICE_ID, item.id)
|
||||||
//putInt(MonitoringActivity.BUNDLE_CHANNEL_ID, item.channelId)
|
//putInt(MonitoringActivity.BUNDLE_CHANNEL_ID, item.channelId)
|
||||||
@ -182,43 +178,46 @@ class SplitViewFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
setAllUrl()
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setAllUrl() {
|
private fun setAllUrl() {
|
||||||
for (index in data.indices) {
|
for (index in data.indices) {
|
||||||
videoViews[index].data = data[index]
|
videoViews[index].setData(data[index])
|
||||||
videoViews[index].setTextVisible(
|
videoViews[index].setTextVisible((splitMode != MainViewModel.PAGE_MODE_NINE))
|
||||||
(splitMode != Constants.SPLIT_MODE_NINE && splitMode != Constants.SPLIT_MODE_SIXTEEN)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun playAll() {
|
fun playAll() = MainScope().launch(Dispatchers.Main) {
|
||||||
videoViews.forEach { videoView ->
|
if (videoViews.isEmpty()) return@launch
|
||||||
if (videoView.isReady) {
|
delay(splitMode * Constants.CONF_DELAY_BASE_MILLIS)
|
||||||
videoView.resetRetryCount()
|
for (index in data.indices) {
|
||||||
videoView.play()
|
if (!videoViews[index].isReady) continue
|
||||||
}
|
videoViews[index].resetRetryCount()
|
||||||
}
|
videoViews[index].play()
|
||||||
|
//delay(300)
|
||||||
}
|
}
|
||||||
|
}.start()
|
||||||
|
|
||||||
fun stopAll() {
|
fun stopAll() = MainScope().launch(Dispatchers.Main) {
|
||||||
videoViews.forEach { videoView ->
|
if (videoViews.isEmpty()) return@launch
|
||||||
videoView.stopRetryCount()
|
for (index in data.indices) {
|
||||||
if (videoView.isPlaying || !videoView.isLoading) {
|
videoViews[index].stopRetryCount()
|
||||||
videoView.pause()
|
if (!videoViews[index].isPlaying || videoViews[index].isLoading) continue
|
||||||
}
|
videoViews[index].stop()
|
||||||
}
|
//delay(300)
|
||||||
}
|
}
|
||||||
|
}.start()
|
||||||
|
|
||||||
fun destroyAll() {
|
fun destroyAll() /*= MainScope().launch(Dispatchers.Main)*/ {
|
||||||
videoViews.forEach { videoView ->
|
if (videoViews.isEmpty()) return
|
||||||
//videoView.destroy()
|
for (index in data.indices) {
|
||||||
videoView.destroySurface()
|
videoViews[index].destroy()
|
||||||
}
|
//delay(100)
|
||||||
}
|
}
|
||||||
|
}//.start()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val TAG = SplitViewFragment::class.java.simpleName
|
private val TAG = SplitViewFragment::class.java.simpleName
|
||||||
|
|||||||
@ -9,7 +9,6 @@ import android.util.Log
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.SurfaceHolder
|
import android.view.SurfaceHolder
|
||||||
import android.view.SurfaceView
|
import android.view.SurfaceView
|
||||||
import android.view.TextureView
|
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import com.hisharp.gstreamer_player.GstCallback
|
import com.hisharp.gstreamer_player.GstCallback
|
||||||
@ -35,35 +34,13 @@ class VideoView : ConstraintLayout, GstCallback {
|
|||||||
|
|
||||||
private lateinit var view: ItemVideoViewBinding
|
private lateinit var view: ItemVideoViewBinding
|
||||||
|
|
||||||
var streamType: Int = MAIN_STREAM
|
private var data: Device? = null
|
||||||
|
|
||||||
var data: Device? = null
|
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
if (field == null) {
|
|
||||||
view.textDeviceName.isVisible = false
|
|
||||||
isPlaying = false
|
|
||||||
isLoading = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.tag = field?.deviceName
|
|
||||||
view.textDeviceName.text = field?.deviceName
|
|
||||||
view.textDeviceName.isVisible = true
|
|
||||||
val rtspUrl = this.data?.getStreamPath(streamType) ?: return // 如果 null 就不指派給 Gstreamer 了
|
|
||||||
gstLibrary.setTag(this.data!!.deviceName)
|
|
||||||
gstLibrary.setRtspUrl(rtspUrl)
|
|
||||||
Log.d("${TAG}_$tag", "Set device to: $field, rtspUrl = $rtspUrl")
|
|
||||||
}
|
|
||||||
|
|
||||||
var isReady: Boolean = false
|
var isReady: Boolean = false
|
||||||
|
|
||||||
var isLoading: Boolean = false
|
var isLoading: Boolean = false
|
||||||
set(value) {
|
set(value) {
|
||||||
view.pbLoading.isVisible = if (retryCount in 1..5) {
|
view.pbLoading.isVisible = value
|
||||||
true
|
|
||||||
} else {
|
|
||||||
value
|
|
||||||
}
|
|
||||||
field = value
|
field = value
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +58,7 @@ class VideoView : ConstraintLayout, GstCallback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val videoView: TextureView by lazy { view.videoView }
|
private val videoView: SurfaceView by lazy { view.videoView }
|
||||||
|
|
||||||
private lateinit var gstLibrary: GstLibrary
|
private lateinit var gstLibrary: GstLibrary
|
||||||
|
|
||||||
@ -94,23 +71,32 @@ class VideoView : ConstraintLayout, GstCallback {
|
|||||||
view = ItemVideoViewBinding.inflate(layoutInflater, this, true)
|
view = ItemVideoViewBinding.inflate(layoutInflater, this, true)
|
||||||
view.baseView.clipToOutline = true
|
view.baseView.clipToOutline = true
|
||||||
|
|
||||||
gstLibrary = GstLibrary(context)
|
|
||||||
gstLibrary.setTextureView(videoView)
|
|
||||||
gstLibrary.setOnStatusChangeListener(this)
|
|
||||||
|
|
||||||
//videoView.holder.addCallback(this)
|
//videoView.holder.addCallback(this)
|
||||||
//gstLibrary.setSurfaceHolder(videoView.holder)
|
gstLibrary = GstLibrary(context)
|
||||||
|
gstLibrary.setSurfaceHolder(videoView.holder)
|
||||||
|
gstLibrary.setOnStatusChangeListener(this)
|
||||||
|
|
||||||
// View 預設狀態
|
// View 預設狀態
|
||||||
view.textDeviceName.isVisible = false
|
view.textDeviceName.isVisible = false
|
||||||
isPlaying = false
|
isPlaying = false
|
||||||
isLoading = false
|
isLoading = false
|
||||||
|
|
||||||
view.btnRetry.setOnClickListener {
|
|
||||||
it.isVisible = false
|
|
||||||
resetRetryCount()
|
|
||||||
play()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setData(device: Device?, streamType: Int = SUB_STREAM) {
|
||||||
|
if (device == null) {
|
||||||
|
view.textDeviceName.isVisible = false
|
||||||
|
isPlaying = false
|
||||||
|
isLoading = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.data = device
|
||||||
|
this.tag = device.deviceName
|
||||||
|
view.textDeviceName.text = device.deviceName
|
||||||
|
view.textDeviceName.isVisible = true
|
||||||
|
val rtspUrl = this.data?.getStreamPath(streamType) ?: return // 如果 null 就不指派給 Gstreamer 了
|
||||||
|
gstLibrary.setTag(this.data!!.deviceName)
|
||||||
|
gstLibrary.setRtspUrl(rtspUrl)
|
||||||
|
Log.d("${TAG}_$tag", "Set device to: $device, rtspUrl = $rtspUrl")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setTextVisible(isVisible: Boolean) {
|
fun setTextVisible(isVisible: Boolean) {
|
||||||
@ -119,22 +105,15 @@ class VideoView : ConstraintLayout, GstCallback {
|
|||||||
|
|
||||||
fun play() {
|
fun play() {
|
||||||
if (data == null) return
|
if (data == null) return
|
||||||
if (this::gstLibrary.isInitialized) {
|
videoView.postInvalidate()
|
||||||
gstLibrary.play()
|
gstLibrary.play()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun pause() {
|
fun stop() {
|
||||||
if (data == null) return
|
if (data == null) return
|
||||||
isPlaying = false
|
isPlaying = false
|
||||||
if (this::gstLibrary.isInitialized) {
|
if (this::gstLibrary.isInitialized) {
|
||||||
gstLibrary.pause()
|
gstLibrary.stop()
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun destroySurface() {
|
|
||||||
if (this::gstLibrary.isInitialized) {
|
|
||||||
gstLibrary.releaseSurface()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,12 +169,8 @@ class VideoView : ConstraintLayout, GstCallback {
|
|||||||
if (retryCount != RETRY_OFF && retryCount in 0 until 5) {
|
if (retryCount != RETRY_OFF && retryCount in 0 until 5) {
|
||||||
mHandler.post(retryRunnable)
|
mHandler.post(retryRunnable)
|
||||||
retryCount++
|
retryCount++
|
||||||
isLoading = true
|
|
||||||
view.btnRetry.isVisible = false
|
|
||||||
} else {
|
} else {
|
||||||
stopRetryCount()
|
stopRetryCount()
|
||||||
view.btnRetry.isVisible = true
|
|
||||||
isLoading = false
|
|
||||||
Log.e("${TAG}_$tag", "Retry count = 5, stopped retry...")
|
Log.e("${TAG}_$tag", "Retry count = 5, stopped retry...")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,17 +41,11 @@ class VideoViewAdapter(
|
|||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun pause() {
|
fun stop(index: Int) {
|
||||||
for (i in fragments.indices) {
|
stop(fragments[index])
|
||||||
pause(i)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun pause(index: Int) {
|
private fun stop(fragment: SplitViewFragment) {
|
||||||
pause(fragments[index])
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun pause(fragment: SplitViewFragment) {
|
|
||||||
fragment.stopAll()
|
fragment.stopAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,12 +57,6 @@ class VideoViewAdapter(
|
|||||||
fragment.playAll()
|
fragment.playAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun destroy() {
|
|
||||||
for (i in fragments.indices) {
|
|
||||||
destroy(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun destroy(index: Int) {
|
fun destroy(index: Int) {
|
||||||
destroy(fragments[index])
|
destroy(fragments[index])
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ package com.ray650128.gstreamer_demo_app.ui.monitoringScreen
|
|||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import com.ray650128.gstreamer_demo_app.R
|
import com.ray650128.gstreamer_demo_app.databinding.ActivityMainBinding
|
||||||
import com.ray650128.gstreamer_demo_app.databinding.ActivityMonitoringBinding
|
import com.ray650128.gstreamer_demo_app.databinding.ActivityMonitoringBinding
|
||||||
|
|
||||||
class MonitoringActivity : AppCompatActivity() {
|
class MonitoringActivity : AppCompatActivity() {
|
||||||
@ -16,19 +16,8 @@ class MonitoringActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
binding = ActivityMonitoringBinding.inflate(layoutInflater)
|
binding = ActivityMonitoringBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
|
||||||
initToolbar()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initToolbar() {
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_back_arrow)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSupportNavigateUp(): Boolean {
|
|
||||||
finish()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<item android:state_enabled="false" android:color="#9b9b9b" />
|
|
||||||
<item android:state_enabled="true" android:color="#FF000000" />
|
|
||||||
</selector>
|
|
||||||
|
Before Width: | Height: | Size: 612 B |
|
Before Width: | Height: | Size: 455 B |
BIN
app/src/main/res/drawable-ldpi/bg_not_in_playing.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 117 KiB |
|
Before Width: | Height: | Size: 438 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 330 B |
|
Before Width: | Height: | Size: 321 B |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 355 B |
|
Before Width: | Height: | Size: 540 B |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 421 B |
|
Before Width: | Height: | Size: 334 B |
|
Before Width: | Height: | Size: 541 B |
|
Before Width: | Height: | Size: 529 B |
|
Before Width: | Height: | Size: 309 B |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 295 KiB |
|
Before Width: | Height: | Size: 790 B |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 544 B |
|
Before Width: | Height: | Size: 468 B |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 545 B |
|
Before Width: | Height: | Size: 906 B |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 616 B |
|
Before Width: | Height: | Size: 513 B |
|
Before Width: | Height: | Size: 736 B |
|
Before Width: | Height: | Size: 855 B |
|
Before Width: | Height: | Size: 505 B |
|
Before Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 517 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 651 B |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 750 B |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 891 B |
|
Before Width: | Height: | Size: 686 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 720 B |
|
Before Width: | Height: | Size: 121 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 126 KiB |
|
Before Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 755 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 884 B |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 968 B |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |