Skip to content

Commit

Permalink
1. Add VR feature. Screen will be splitted to 2 when mode is On. (#10)
Browse files Browse the repository at this point in the history
2. Add record button to OSD, now user can move that button to main
   screen to interact
  • Loading branch information
bangdc90 authored Sep 25, 2024
1 parent 39fc36f commit 8d31aa7
Show file tree
Hide file tree
Showing 11 changed files with 345 additions and 85 deletions.
186 changes: 179 additions & 7 deletions app/src/main/java/com/openipc/pixelpilot/VideoActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,19 @@
import android.util.Base64;
import android.util.Log;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SubMenu;
import android.view.View;
import android.view.WindowManager;
import android.widget.PopupMenu;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
import androidx.documentfile.provider.DocumentFile;

import com.github.mikephil.charting.charts.PieChart;
Expand Down Expand Up @@ -87,6 +91,24 @@ public void run() {
private OSDManager osdManager;
private ParcelFileDescriptor dvrFd = null;
private Timer dvrIconTimer = null;
private Timer recordTimer = null;
private int seconds = 0;
private boolean isVRMode = false;
private boolean isStreaming = false;
private ConstraintLayout constraintLayout;
private ConstraintSet constraintSet;

public boolean getVRSetting() {
return getSharedPreferences("general", Context.MODE_PRIVATE).getBoolean("vr-mode", false);
}

public void setVRSetting(boolean v)
{
SharedPreferences prefs = getSharedPreferences("general", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean("vr-mode", v);
editor.apply();
}

public static int getChannel(Context context) {
return context.getSharedPreferences("general",
Expand Down Expand Up @@ -119,6 +141,19 @@ public static String bytesToHex(byte[] bytes) {
return hexString.toString();
}

private void resetApp()
{
// Restart the app
Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName());
if (intent != null) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
finish();
System.exit(0); // Ensure the app is fully restarted
}
}

@SuppressLint("ClickableViewAccessibility")
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "lifecycle onCreate");
Expand All @@ -139,7 +174,84 @@ protected void onCreate(Bundle savedInstanceState) {
setContentView(binding.getRoot());
videoPlayer = new VideoPlayer(this);
videoPlayer.setIVideoParamsChanged(this);
binding.mainVideo.getHolder().addCallback(videoPlayer.configure1());
isVRMode = getVRSetting();
if(isVRMode) {
binding.mainVideo.setVisibility(View.GONE);
binding.surfaceViewLeft.getHolder().addCallback(videoPlayer.configure1(0));
binding.surfaceViewRight.getHolder().addCallback(videoPlayer.configure1(1));

SeekBar seekBar = binding.seekBar;
// Retrieve saved progress value
SharedPreferences sharedPreferences = getSharedPreferences("SeekBarPrefs", MODE_PRIVATE);
int savedProgress = sharedPreferences.getInt("seekBarProgress", 0); // Default to 0 if no value is found
seekBar.setProgress(savedProgress);
seekBar.setVisibility(View.VISIBLE);
constraintLayout = binding.frameLayout;
constraintSet = new ConstraintSet();
constraintSet.clone(constraintLayout);

// Apply the saved margin
int margin = savedProgress * 10; // Adjust the multiplier as needed
constraintSet.setMargin(R.id.surfaceViewLeft, ConstraintSet.END, margin);
constraintSet.setMargin(R.id.surfaceViewRight, ConstraintSet.START, margin);
constraintSet.applyTo(constraintLayout);

// Hide SeekBar after 3 seconds
handler.postDelayed(new Runnable() {
@Override
public void run() {
seekBar.setVisibility(View.GONE);
}
}, 3000);

// Show SeekBar when touched
constraintLayout.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
seekBar.setVisibility(View.VISIBLE);
// Hide SeekBar again after 3 seconds of inactivity
handler.postDelayed(new Runnable() {
@Override
public void run() {
seekBar.setVisibility(View.GONE);
}
}, 3000);
}
return false;
}
});

seekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
int margin = progress * 10; // Adjust the multiplier as needed
constraintSet.setMargin(R.id.surfaceViewLeft, ConstraintSet.END, margin);
constraintSet.setMargin(R.id.surfaceViewRight, ConstraintSet.START, margin);
constraintSet.applyTo(constraintLayout);
// Save progress value
SharedPreferences sharedPreferences = getSharedPreferences("SeekBarPrefs", MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putInt("seekBarProgress", progress);
editor.apply();
}

@Override
public void onStartTrackingTouch(SeekBar seekBar) {

}

@Override
public void onStopTrackingTouch(SeekBar seekBar) {

}
});
}
else {
binding.surfaceViewRight.setVisibility(View.GONE);
binding.surfaceViewLeft.setVisibility(View.GONE);
binding.mainVideo.getHolder().addCallback(videoPlayer.configure1(0));
}

osdManager = new OSDManager(this, binding);
osdManager.setUp();
Expand All @@ -160,8 +272,37 @@ protected void onCreate(Bundle savedInstanceState) {
PieData noData = new PieData(new PieDataSet(new ArrayList<>(), ""));
chart.setData(noData);

binding.imgBtnRecord.setOnClickListener(item -> {
if(!isStreaming) return;

if (dvrFd == null) {
Uri dvrUri = openDvrFile();
if (dvrUri != null) {
startDvr(dvrUri);
} else {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.addCategory(Intent.CATEGORY_DEFAULT);
startActivityForResult(intent, PICK_DVR_REQUEST_CODE);
}
} else {
stopDvr();
}
});

binding.btnSettings.setOnClickListener(v -> {
PopupMenu popup = new PopupMenu(this, v);
SubMenu vrMenu = popup.getMenu().addSubMenu("VR mode");
MenuItem vrItem = vrMenu.add(getVRSetting() ? "On" : "Off");
vrItem.setOnMenuItemClickListener(item -> {
isVRMode = !getVRSetting();
setVRSetting(isVRMode);
vrItem.setTitle(isVRMode ? "On" : "Off");
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
item.setActionView(new View(this));
resetApp();
return false;
});

SubMenu chnMenu = popup.getMenu().addSubMenu("Channel");
int channelPref = getChannel(this);
chnMenu.setHeaderTitle("Current: " + channelPref);
Expand Down Expand Up @@ -211,6 +352,7 @@ protected void onCreate(Bundle savedInstanceState) {
SubMenu recording = popup.getMenu().addSubMenu("Recording");
MenuItem dvrBtn = recording.add(dvrFd == null ? "Start" : "Stop");
dvrBtn.setOnMenuItemClickListener(item -> {
if(!isStreaming) return false;
if (dvrFd == null) {
Uri dvrUri = openDvrFile();
if (dvrUri != null) {
Expand Down Expand Up @@ -285,6 +427,7 @@ private Uri openDvrFile() {
String dvrFolder = getSharedPreferences("general",
Context.MODE_PRIVATE).getString("dvr_folder_", "");
if (dvrFolder.isEmpty()) {
Log.e(TAG, "dvrFolder is empty");
return null;
}
Uri uri = Uri.parse(dvrFolder);
Expand All @@ -297,6 +440,8 @@ private Uri openDvrFile() {
String filename = "pixelpilot_" + formattedNow + ".mp4";
DocumentFile newFile = pickedDir.createFile("video/mp4", filename);
Toast.makeText(this, "Recording to " + filename, Toast.LENGTH_SHORT).show();
if(newFile == null)
Log.e(TAG, "dvr newFile null");
return newFile != null ? newFile.getUri() : null;
}
return null;
Expand All @@ -309,10 +454,26 @@ private void startDvr(Uri dvrUri) {
try {
dvrFd = getContentResolver().openFileDescriptor(dvrUri, "rw");
videoPlayer.startDvr(dvrFd.getFd(), getDvrMP4());
binding.imgBtnRecord.setImageResource(R.drawable.recording);
} catch (IOException e) {
Log.e(TAG, "Failed to open dvr file ", e);
dvrFd = null;
}

binding.txtRecordLabel.setVisibility(View.VISIBLE);
recordTimer = new Timer();
recordTimer.schedule(new TimerTask() {
@Override
public void run() {
int minutes = seconds / 60;
int secs = seconds % 60;

String timeFormatted = String.format("%02d:%02d", minutes, secs);
runOnUiThread(() -> binding.txtRecordLabel.setText(timeFormatted));
seconds++;
}
}, 0, 1000);

dvrIconTimer = new Timer();
dvrIconTimer.schedule(new TimerTask() {
@Override
Expand All @@ -328,10 +489,20 @@ private void stopDvr() {
return;
}
binding.imgRecIndicator.setVisibility(View.INVISIBLE);
binding.imgBtnRecord.setImageResource(R.drawable.record);
videoPlayer.stopDvr();
dvrIconTimer.cancel();
dvrIconTimer.purge();
dvrIconTimer = null;
if(recordTimer != null) {
recordTimer.cancel();
recordTimer.purge();
recordTimer = null;
seconds = 0;
binding.txtRecordLabel.setVisibility(View.GONE);
}
if(dvrIconTimer != null) {
dvrIconTimer.cancel();
dvrIconTimer.purge();
dvrIconTimer = null;
}
try {
dvrFd.close();
} catch (IOException e) {
Expand Down Expand Up @@ -511,9 +682,6 @@ public void onDecodingInfoChanged(final DecodingInfo decodingInfo) {
runOnUiThread(() -> {
if (lastCodec != decodingInfo.nCodec) {
lastCodec = decodingInfo.nCodec;
videoPlayer.stopAndRemoveReceiverDecoder();
videoPlayer.addAndStartDecoderReceiver(binding.mainVideo.getHolder().getSurface());
videoPlayer.start();
}
if (decodingInfo.currentFPS > 0) {
binding.tvMessage.setVisibility(View.GONE);
Expand Down Expand Up @@ -574,9 +742,13 @@ public void onWfbNgStatsChanged(WfbNGStats data) {
paddedDigits(data.count_p_dec_ok, 6),
paddedDigits(data.count_p_fec_recovered, 6),
paddedDigits(data.count_p_lost, 6)));
isStreaming = true;
}
} else {
binding.tvLinkStatus.setText("No wfb-ng data.");
isStreaming = false;
binding.imgBtnRecord.setImageResource(R.drawable.record);
stopDvr();
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ private void init(Context context) {
defaultY = (float) displaySize.y / 2 - ((float) displaySize.y / 4);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// Intercept touch events and pass them to onTouchEvent
if (isMovable) {
return true;
}
return false;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isMovable) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ public void onFinish() {
listOSDItems.add(new OSDElement("Pitch", binding.itemPitch));
listOSDItems.add(new OSDElement("RC Link", binding.itemRCLink));
listOSDItems.add(new OSDElement("Recording Indicator", binding.itemRecIndicator));
listOSDItems.add(new OSDElement("Recording Button", binding.btnRecord));
listOSDItems.add(new OSDElement("Roll", binding.itemRoll));
listOSDItems.add(new OSDElement("Satellites", binding.itemSat));
listOSDItems.add(new OSDElement("Status", binding.itemStatus));
Expand Down
Binary file added app/src/main/res/drawable/record.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable/recording.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
57 changes: 57 additions & 0 deletions app/src/main/res/layout/activity_video.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,38 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<SurfaceView
android:id="@+id/surfaceViewLeft"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="16:9"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/surfaceViewRight"/>

<SurfaceView
android:id="@+id/surfaceViewRight"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="16:9"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/surfaceViewLeft"
app:layout_constraintEnd_toEndOf="parent"/>

<SeekBar
android:id="@+id/seekBar"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/surfaceViewLeft"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginStart="300dp"
android:layout_marginEnd="300dp"
android:visibility="gone"
android:max="20" />

<Button
android:id="@+id/btnSettings"
style="@style/Widget.Material3.Button.IconButton"
Expand Down Expand Up @@ -636,6 +668,31 @@
android:textColor="@color/colorWhite"
android:textSize="14sp" />
</com.openipc.pixelpilot.osd.MovableLayout>

<com.openipc.pixelpilot.osd.MovableLayout
android:id="@+id/btnRecord"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start"
android:orientation="vertical"
android:visibility="visible">

<ImageView
android:id="@+id/imgBtnRecord"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/record" />

<TextView
android:id="@+id/txtRecordLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text=""
android:textSize="14sp"
android:visibility="gone"
android:textColor="@android:color/white" />
</com.openipc.pixelpilot.osd.MovableLayout>
</FrameLayout>

</androidx.constraintlayout.widget.ConstraintLayout>
Loading

0 comments on commit 8d31aa7

Please sign in to comment.