Floating window support and cleaning up some background behavior (#182)

* Trying to add PIP mode for floating playback

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* Trying to add PIP mode for floating playback

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.

* PIP mode for floating playback.
This commit is contained in:
Don Kimberlin 2020-06-27 12:45:46 -07:00 committed by GitHub
parent ae488615a4
commit 1ac99bcc2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 307 additions and 98 deletions

View File

@ -34,7 +34,8 @@
android:name=".activity.VideoPlayActivity" android:name=".activity.VideoPlayActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
android:label="@string/title_activity_video_play" android:label="@string/title_activity_video_play"
android:launchMode="singleTop" android:launchMode="singleInstance"
android:supportsPictureInPicture="true"
android:theme="@style/AppTheme.NoActionBar" /> android:theme="@style/AppTheme.NoActionBar" />
<activity <activity
android:name=".activity.SettingsActivity" android:name=".activity.SettingsActivity"

View File

@ -19,16 +19,24 @@
package net.schueller.peertube.activity; package net.schueller.peertube.activity;
import android.annotation.SuppressLint;
import android.app.AppOpsManager;
import android.app.PictureInPictureParams;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.util.Rational;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.WindowManager; import android.view.WindowManager;
@ -39,6 +47,7 @@ import android.widget.RelativeLayout;
import net.schueller.peertube.R; import net.schueller.peertube.R;
import net.schueller.peertube.fragment.VideoMetaDataFragment; import net.schueller.peertube.fragment.VideoMetaDataFragment;
import net.schueller.peertube.fragment.VideoPlayerFragment; import net.schueller.peertube.fragment.VideoPlayerFragment;
import net.schueller.peertube.service.VideoPlayerService;
import java.util.Objects; import java.util.Objects;
@ -71,13 +80,23 @@ public class VideoPlayActivity extends AppCompatActivity {
// get video ID // get video ID
Intent intent = getIntent(); Intent intent = getIntent();
String videoUuid = intent.getStringExtra(VideoListActivity.EXTRA_VIDEOID); String videoUuid = intent.getStringExtra(VideoListActivity.EXTRA_VIDEOID);
Log.v(TAG, "click: " + videoUuid);
VideoPlayerFragment videoPlayerFragment = (VideoPlayerFragment) VideoPlayerFragment videoPlayerFragment = (VideoPlayerFragment)
getSupportFragmentManager().findFragmentById(R.id.video_player_fragment); getSupportFragmentManager().findFragmentById(R.id.video_player_fragment);
assert videoPlayerFragment != null; assert videoPlayerFragment != null;
videoPlayerFragment.start(videoUuid); String playingVideo = videoPlayerFragment.getVideoUuid();
Log.v(TAG, "oncreate click: " + videoUuid +" is trying to replace: "+playingVideo);
if (TextUtils.isEmpty(playingVideo)){
Log.v(TAG,"oncreate no video currently playing");
videoPlayerFragment.start(videoUuid);
} else if(!playingVideo.equals(videoUuid)){
Log.v(TAG,"oncreate different video playing currently");
videoPlayerFragment.stopVideo();
videoPlayerFragment.start(videoUuid);
} else {
Log.v(TAG,"oncreate same video playing currently");
}
// if we are in landscape set the video to fullscreen // if we are in landscape set the video to fullscreen
int orientation = this.getResources().getConfiguration().orientation; int orientation = this.getResources().getConfiguration().orientation;
@ -86,6 +105,36 @@ public class VideoPlayActivity extends AppCompatActivity {
} }
} }
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
VideoPlayerFragment videoPlayerFragment = (VideoPlayerFragment)
getSupportFragmentManager().findFragmentById(R.id.video_player_fragment);
assert videoPlayerFragment != null;
String videoUuid = intent.getStringExtra(VideoListActivity.EXTRA_VIDEOID);
Log.v(TAG, "new intent click: " + videoUuid +" is trying to replace: "+videoPlayerFragment.getVideoUuid());
assert videoPlayerFragment != null;
String playingVideo = videoPlayerFragment.getVideoUuid();
if (TextUtils.isEmpty(playingVideo)){
Log.v(TAG,"new intent no video currently playing");
videoPlayerFragment.start(videoUuid);
} else if(!playingVideo.equals(videoUuid)){
Log.v(TAG,"new intent different video playing currently");
videoPlayerFragment.stopVideo();
videoPlayerFragment.start(videoUuid);
} else {
Log.v(TAG,"new intent same video playing currently");
}
// if we are in landscape set the video to fullscreen
int orientation = this.getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
setOrientation(true);
}
}
@Override @Override
public void onConfigurationChanged(Configuration newConfig) { public void onConfigurationChanged(Configuration newConfig) {
@ -199,16 +248,115 @@ public class VideoPlayActivity extends AppCompatActivity {
Log.v(TAG, "onStart()..."); Log.v(TAG, "onStart()...");
} }
@SuppressLint("NewApi")
@Override
public void onUserLeaveHint () {
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
FragmentManager fragmentManager = getSupportFragmentManager();
VideoPlayerFragment videoPlayerFragment = (VideoPlayerFragment) fragmentManager.findFragmentById(R.id.video_player_fragment);
VideoMetaDataFragment videoMetaFragment = (VideoMetaDataFragment) fragmentManager.findFragmentById(R.id.video_meta_data_fragment);
String backgroundBehavior = sharedPref.getString("pref_background_behavior","backgroundStop");
switch(backgroundBehavior){
case "backgroundStop":
Log.v(TAG,"stop the video");
videoPlayerFragment.pauseVideo();
stopService(new Intent(this, VideoPlayerService.class));
super.onBackPressed();
break;
case "backgroundAudio":
Log.v(TAG,"play the Audio");
super.onBackPressed();
break;
case "backgroundFloat":
Log.v(TAG,"play in floating video");
//canEnterPIPMode makes sure API level is high enough
if (canEnterPipMode(this)) {
Log.v(TAG, "enabling pip");
enterPipMode();
} else {
Log.v(TAG, "unable to use pip");
}
break;
}
Log.v(TAG, "onUserLeaveHint()...");
}
// @RequiresApi(api = Build.VERSION_CODES.O)
@SuppressLint("NewApi")
public void onBackPressed() { public void onBackPressed() {
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
VideoPlayerFragment videoPlayerFragment = (VideoPlayerFragment)
getSupportFragmentManager().findFragmentById(R.id.video_player_fragment);
//copying Youtube behavior to have back button exit full screen.
if (videoPlayerFragment.getIsFullscreen()){
Log.v(TAG,"exiting full screen");
videoPlayerFragment.fullScreenToggle();
return;
}
if (sharedPref.getBoolean("pref_back_pause", true)) { if (sharedPref.getBoolean("pref_back_pause", true)) {
VideoPlayerFragment videoPlayerFragment = (VideoPlayerFragment)
getSupportFragmentManager().findFragmentById(R.id.video_player_fragment);
assert videoPlayerFragment != null; assert videoPlayerFragment != null;
videoPlayerFragment.pauseVideo(); videoPlayerFragment.pauseVideo();
} }
String backgroundBehavior = sharedPref.getString("pref_background_behavior","backgroundStop");
switch (backgroundBehavior){
case "backgroundStop":
Log.v(TAG,"stop the video");
videoPlayerFragment.pauseVideo();
stopService(new Intent(this, VideoPlayerService.class));
super.onBackPressed();
break;
case "backgroundAudio":
Log.v(TAG,"play the Audio");
super.onBackPressed();
break;
case "backgroundFloat":
Log.v(TAG,"play in floating video");
//canEnterPIPMode makes sure API level is high enough
if (canEnterPipMode(this)) {
Log.v(TAG, "enabling pip");
enterPipMode();
//fixes problem where back press doesn't bring up video list after returning from PIP mode
Intent intentSettings = new Intent(this, VideoListActivity.class);
this.startActivity(intentSettings);
} else {
Log.v(TAG,"Unable to enter PIP mode");
super.onBackPressed();
}
break;
}
Log.v(TAG, "onBackPressed()..."); Log.v(TAG, "onBackPressed()...");
super.onBackPressed(); }
public boolean canEnterPipMode(Context context) {
Log.v(TAG,"api version "+Build.VERSION.SDK_INT);
if (Build.VERSION.SDK_INT<28){
return false;
}
AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
return (AppOpsManager.MODE_ALLOWED== appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_PICTURE_IN_PICTURE, android.os.Process.myUid(), context.getPackageName()));
}
@RequiresApi(api = Build.VERSION_CODES.O)
public void enterPipMode() {
Rational rational = new Rational(239, 100);
Log.v(TAG,rational.toString());
PictureInPictureParams mParams =
new PictureInPictureParams.Builder()
.setAspectRatio(rational)
// .setSourceRectHint(new Rect(0,500,400,600))
.build();
enterPictureInPictureMode(mParams);
}
@Override
public void onPictureInPictureModeChanged (boolean isInPictureInPictureMode, Configuration newConfig) {
if (isInPictureInPictureMode) {
Log.v(TAG,"switched to pip ");
} else {
Log.v(TAG,"switched to normal");
}
} }
} }

View File

@ -18,6 +18,7 @@
package net.schueller.peertube.fragment; package net.schueller.peertube.fragment;
import android.app.Activity; import android.app.Activity;
import android.app.AppOpsManager;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@ -25,12 +26,15 @@ import android.content.ServiceConnection;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment; import android.os.Environment;
import android.os.IBinder; import android.os.IBinder;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.util.Log; import android.util.Log;
import android.view.GestureDetector;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.Surface; import android.view.Surface;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -65,6 +69,7 @@ import net.schueller.peertube.service.VideoPlayerService;
import java.util.Objects; import java.util.Objects;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.Callback; import retrofit2.Callback;
@ -83,7 +88,7 @@ public class VideoPlayerFragment extends Fragment implements VideoRendererEventL
private LinearLayout torrentStatus; private LinearLayout torrentStatus;
private static final String TAG = "VideoPlayerFragment"; private static final String TAG = "VideoPlayerFragment";
private GestureDetector mDetector;
private ServiceConnection mConnection = new ServiceConnection() { private ServiceConnection mConnection = new ServiceConnection() {
@ -135,6 +140,9 @@ public class VideoPlayerFragment extends Fragment implements VideoRendererEventL
simpleExoPlayerView.setControllerShowTimeoutMs(1000); simpleExoPlayerView.setControllerShowTimeoutMs(1000);
simpleExoPlayerView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FIT); simpleExoPlayerView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FIT);
mDetector = new GestureDetector(context, new MyGestureListener());
simpleExoPlayerView.setOnTouchListener(touchListener);
torrentStatus = activity.findViewById(R.id.exo_torrent_status); torrentStatus = activity.findViewById(R.id.exo_torrent_status);
// Full screen Icon // Full screen Icon
@ -146,13 +154,7 @@ public class VideoPlayerFragment extends Fragment implements VideoRendererEventL
fullscreenButton.setOnClickListener(view -> { fullscreenButton.setOnClickListener(view -> {
Log.d(TAG, "Fullscreen"); Log.d(TAG, "Fullscreen");
if (!isFullscreen) { fullScreenToggle();
isFullscreen = true;
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} else {
isFullscreen = false;
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
}); });
if (!mBound) { if (!mBound) {
@ -182,7 +184,7 @@ public class VideoPlayerFragment extends Fragment implements VideoRendererEventL
mService.setCurrentVideo(video); mService.setCurrentVideo(video);
if (video == null) { if (video == null) {
Toast.makeText(context, "Something went wrong...Please try later!", Toast.LENGTH_SHORT).show(); Toast.makeText(context, "Unable to retrieve video information, try again later.", Toast.LENGTH_SHORT).show();
return; return;
} }
@ -193,7 +195,7 @@ public class VideoPlayerFragment extends Fragment implements VideoRendererEventL
@Override @Override
public void onFailure(@NonNull Call<Video> call, @NonNull Throwable t) { public void onFailure(@NonNull Call<Video> call, @NonNull Throwable t) {
Log.wtf(TAG, t.fillInStackTrace()); Log.wtf(TAG, t.fillInStackTrace());
Toast.makeText(context, "Something went wrong...Please try later!", Toast.LENGTH_SHORT).show(); Toast.makeText(context, "Something went wrong: "+t.getLocalizedMessage(), Toast.LENGTH_LONG).show();
} }
}); });
} }
@ -253,9 +255,15 @@ public class VideoPlayerFragment extends Fragment implements VideoRendererEventL
} }
public void pauseVideo() { public void pauseVideo() {
mService.player.setPlayWhenReady(false); if (mBound){
mService.player.setPlayWhenReady(false);
}
}
public void pauseToggle() {
if (mBound) {
mService.player.setPlayWhenReady(!mService.player.getPlayWhenReady());
}
} }
public void stopVideo() { public void stopVideo() {
if (mBound) { if (mBound) {
@ -279,7 +287,15 @@ public class VideoPlayerFragment extends Fragment implements VideoRendererEventL
public Boolean getIsFullscreen() { public Boolean getIsFullscreen() {
return isFullscreen; return isFullscreen;
} }
public void fullScreenToggle() {
if (!isFullscreen) {
setIsFullscreen(true);
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} else {
setIsFullscreen(false);
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
}
/** /**
* Torrent Playback * Torrent Playback
* *
@ -373,4 +389,84 @@ public class VideoPlayerFragment extends Fragment implements VideoRendererEventL
Log.v(TAG, "onVideoDisabled()..."); Log.v(TAG, "onVideoDisabled()...");
} }
public static boolean canEnterPipMode(Context context) {
Log.v(TAG,"api version "+Build.VERSION.SDK_INT);
if (Build.VERSION.SDK_INT<28){
return false;
}
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
if (!"BackgroundFloat".equals(sharedPref.getString("pref_background_behavior","backgroundStop"))){
return false;
}
AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
return (AppOpsManager.MODE_ALLOWED== appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_PICTURE_IN_PICTURE, android.os.Process.myUid(), context.getPackageName()));
}
View.OnTouchListener touchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return mDetector.onTouchEvent(event);
}
};
public String getVideoUuid(){
return mVideoUuid;
}
class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
/*
@Override
public boolean onDown(MotionEvent event) {
Log.d("TAG","onDown: ");
return true;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
Log.i("TAG", "onSingleTapConfirmed: ");
pauseToggle();
return true;
}
@Override
public void onLongPress(MotionEvent e) {
Log.i("TAG", "onLongPress: ");
}
@Override
public boolean onDoubleTap(MotionEvent e) {
Log.i("TAG", "onDoubleTap: ");
return true;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
Log.i("TAG", "onScroll: ");
return true;
}
*/
@RequiresApi(api = Build.VERSION_CODES.N)
@Override
public boolean onFling(MotionEvent event1, MotionEvent event2,
float velocityX, float velocityY) {
Log.d(TAG ,event1.toString());
Log.d(TAG,event2.toString());
Log.d(TAG, String.valueOf(velocityX));
Log.d(TAG , String.valueOf(velocityY));
//arbitrarily velocity speeds that seem to work to differentiate events.
if (velocityY>4000){
Log.d(TAG,"we have a drag down event");
if (canEnterPipMode(getContext())) {
getActivity().enterPictureInPictureMode();
}
}
if ((velocityX>2000) && (Math.abs(velocityY) <2000)){
Log.d(TAG,"swipe right "+velocityY);
}
if ((velocityX<2000) && (Math.abs(velocityY)<2000)){
Log.d(TAG,"swipe left "+velocityY);
}
return true;
}
}
} }

View File

@ -19,15 +19,18 @@ package net.schueller.peertube.service;
import android.app.Notification; import android.app.Notification;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.app.Service; import android.app.Service;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.media.AudioManager; import android.media.AudioManager;
import android.net.Uri; import android.net.Uri;
import android.os.Binder; import android.os.Binder;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -35,9 +38,11 @@ import androidx.annotation.Nullable;
import android.support.v4.media.MediaDescriptionCompat; import android.support.v4.media.MediaDescriptionCompat;
import android.support.v4.media.session.MediaSessionCompat; import android.support.v4.media.session.MediaSessionCompat;
import android.util.Log; import android.util.Log;
import android.util.Rational;
import android.widget.Toast; import android.widget.Toast;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
@ -124,6 +129,10 @@ public class VideoPlayerService extends Service {
if (playerNotificationManager != null) { if (playerNotificationManager != null) {
playerNotificationManager.setPlayer(null); playerNotificationManager.setPlayer(null);
} }
//Was seeing an error when exiting the program about about not unregistering the receiver.
if (null!=myNoisyAudioStreamReceiver) {
this.unregisterReceiver(myNoisyAudioStreamReceiver);
}
if (player != null) { if (player != null) {
player.release(); player.release();
player = null; player = null;

View File

@ -1,75 +1,2 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources></resources>
<string name="br">ব্রেটন</string>
<string name="bzs">ব্রাজিলিয়ান সাইন ভাষা</string>
<string name="bs">বসনিয়ান</string>
<string name="bi">বিসলামা</string>
<string name="bn">বাংলা</string>
<string name="be">বেলারুসিয়ান</string>
<string name="eu">বাস্ক</string>
<string name="ba">বাশকির</string>
<string name="bm">বাম্বারা</string>
<string name="az">আজারবাইজান</string>
<string name="ay">আয়মারা</string>
<string name="av">অভরিক</string>
<string name="as">অসমি</string>
<string name="hy">আর্মেনিয়ান</string>
<string name="an">আরাগনিস</string>
<string name="ar">আরবি</string>
<string name="am">আমহারিক</string>
<string name="ase">আমেরিকান সাইন ভাষা</string>
<string name="sq">আলবানিয়ান</string>
<string name="ak">আকান</string>
<string name="af">আফ্রিকান</string>
<string name="aa">আফার</string>
<string name="ab">আবখাজিয়ান</string>
<string name="pref_description_background_play">সক্রিয় থাকলে ব্যাকগ্রাউন্ডে ভিডিও প্লে করতে থাকবে।</string>
<string name="pref_title_background_play">ব্যাকগ্রাউন্ড প্লেব্যাক</string>
<string name="pref_description_language">ভিডিওর ভাষা পছন্দ করো, কিছু পছন্দ না করলে সব ভাষার ভিডিও দেখা যাবে।</string>
<string name="pref_language">ভাষা ফিল্টার</string>
<string name="pref_description_show_nsfw">নিষিদ্ধ কন্টেন্ট দেখাও</string>
<string name="pref_title_show_nsfw">নিষিদ্ধ কন্টেন্ট</string>
<string name="pref_title_version">সংস্করণ</string>
<string name="pref_description_license">"
\n&lt;b&gt;গাহ্নু অফেরও সাধারণ গণ অনুমতিপত্র সং.৩.&lt;/b&gt;
\n
\nএই শক্তিশালী কপিলেফট লাইসেন্সের অনুমতি এই চুক্তির উপর নির্ভরশীল যে অনুমতিপত্রের দ্বারা আবদ্ধ সকল কাজ ও পরিবর্তনের সোর্স কোড উপলব্ধ করার মাধ্যমে, যার আওতায় পড়ে অনুমতিপত্রের দ্বারা আবদ্ধ কাজের বৃহত্তর অংশ একই অনুমতিপত্রের আওতায় আনার মাধ্যমে। কপিরাইট এবং লাইসেন্স নোটিশ সংরক্ষণ করা আবশ্যক। অবদানকারীগণ তাদের পেটেন্টর অধিকার অবশ্যই দেয়, যখন একটি পরিমার্জিত সংস্করণ ব্যবহার করে একটি পরিসেবা একটি নেটওয়ার্ক দিয়ে দেয়া হয়, সম্পূর্ণ সোর্স কোড এর পরিমার্জিত সংস্করণ উপলব্ধ তৈরি করা আবশ্যক।"<b>GNU Affero General Public License v3.0</b>\n\nPermissions of this strongest copyleft license are conditioned on making available complete source code of licensed works and modifications, which include larger works using a licensed work, under the same license. Copyright and license notices must be preserved. Contributors provide an express grant of patent rights. When a modified version is used to provide a service over a network, the complete source code of the modified version must be made available.</string>
<string name="pref_description_torrent_player">একটি টরেন্ট স্ট্রিমের মাধ্যমে ভিডিও প্লেব্যাক করুন। এর জন্য স্টোরেজ অনুমতির প্রয়োজন । (আলফা, স্থিতিশীল নয়!)</string>
<string name="pref_title_torrent_player">টরেন্ট ভিডিও প্লেয়ার</string>
<string name="pref_description_app_theme">থিমটি কার্যকর হওয়ার জন্য অ্যাপ্লিকেশন পুনরায় চালু করো।</string>
<string name="pref_title_app_theme">অ্যাপ থিম</string>
<string name="pref_description_dark_mode">অন্ধকার মোড কার্যকর করার জন্য অ্যাপ্লিকেশন রিস্টার্ট করো।</string>
<string name="pref_title_dark_mode">অন্ধকার মোড</string>
<string name="invalid_url">অবৈধ ইউআরএল।</string>
<string name="menu_share">শেয়ার</string>
<string name="descr_overflow_button">আরও</string>
<string name="no_data_available">ফলাফল নেই</string>
<string name="title_activity_search">সার্চ</string>
<string name="search_hint">সার্চ পিয়ারটিউব</string>
<string name="title_activity_url_video_play">ইউআরএলভিডিওপ্লেক্রিয়া</string>
<string name="video_row_account_avatar">অ্যাকাউন্ট অবতার</string>
<string name="video_row_video_thumbnail">ভিডিও থাম্বনেইল</string>
<string name="meta_data_views">" দৃষ্ট"</string>
<string name="bottom_nav_title_account">অ্যাকাউন্ট</string>
<string name="bottom_nav_title_subscriptions">সাবস্ক্রিপশন</string>
<string name="bottom_nav_title_local">স্থানীয়</string>
<string name="bottom_nav_title_recent">সাম্প্রতিক</string>
<string name="bottom_nav_title_trending">আলোচিত</string>
<string name="bottom_nav_title_discover">আবিষ্কার</string>
<string name="action_bar_title_account">অ্যাকাউন্ট</string>
<string name="action_bar_title_logout">লগ আউট</string>
<string name="action_bar_title_settings">সেটিংস</string>
<string name="action_bar_title_search">সার্চ</string>
<string name="permission_rationale">ই-মেইল পূরণ করার জন্য কন্টাক্ট-এর অনুমতি দিন।</string>
<string name="error_field_required">এটি প্রয়োজনীয় ফিল্ড</string>
<string name="error_incorrect_password">ভুল পাসওয়ার্ড</string>
<string name="error_invalid_password">পাসওয়ার্ড খুব ছোট</string>
<string name="error_invalid_email">ভুল ইমেইল আইডি</string>
<string name="action_sign_in_short">সাইন ইন</string>
<string name="action_sign_in">সাইন ইন</string>
<string name="prompt_password">পাসওয়ার্ড</string>
<string name="prompt_email">ইমেল / ব্যবহারকারীর নাম</string>
<string name="prompt_server">সার্ভার</string>
<string name="title_activity_login">সাইন ইন</string>
<string name="title_activity_settings">সেটিংস</string>
</resources>

View File

@ -1,6 +1,19 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string-array name="backgroundBehavior">
<item>@string/pref_background_audio</item>
<item>@string/pref_background_stop</item>
<item>@string/pref_background_float</item>
</string-array>
<string-array name="backgroundBehaviorValues">
<item>backgroundAudio</item>
<item>backgroundStop</item>
<item>backgroundFloat</item>
</string-array>
<string-array name="themeArray"> <string-array name="themeArray">
<item>@string/red</item> <item>@string/red</item>
<item>@string/pink</item> <item>@string/pink</item>

View File

@ -65,6 +65,13 @@
<string name="pref_language_app">Application Language</string> <string name="pref_language_app">Application Language</string>
<string name="pref_description_language_app">Select language for application interface. Restart app for change to take effect.</string> <string name="pref_description_language_app">Select language for application interface. Restart app for change to take effect.</string>
<string name="pref_background_audio">Continue as a background audio stream</string>
<string name="pref_background_stop">Stop all playback</string>
<string name="pref_background_float">Continue playing video in floating window</string>
<string name="pref_background_behavior">Background playing Configuration</string>
<string name="settings_api_error_float">Android version does not support floating video</string>
<string name="settings_permissions_error_float">Picture in picture permission is disabled for this app in Android Settings</string>
<string name="pref_background_behavior_summary">How a playing video responds when going to background</string>
<string name="clear_search_history">Clear Search History</string> <string name="clear_search_history">Clear Search History</string>
<string name="clear_search_history_prompt">Do you want to permanently delete the search history?</string> <string name="clear_search_history_prompt">Do you want to permanently delete the search history?</string>
<!-- languages --> <!-- languages -->
@ -364,7 +371,5 @@
<string name="server_book_del_alert_title">Remove Server</string> <string name="server_book_del_alert_title">Remove Server</string>
<string name="server_book_del_alert_msg">Are you sure you want to remove this server from the address book?</string> <string name="server_book_del_alert_msg">Are you sure you want to remove this server from the address book?</string>
<!-- TODO: Remove or change this placeholder text -->
</resources> </resources>

View File

@ -36,6 +36,16 @@
android:title="@string/pref_language_app" /> android:title="@string/pref_language_app" />
/> />
<ListPreference
android:defaultValue="@array/empty_array"
android:entries="@array/backgroundBehavior"
android:entryValues="@array/backgroundBehaviorValues"
android:key="pref_background_behavior"
android:summary="@string/pref_background_behavior_summary"
android:title="@string/pref_background_behavior" />
/>
<ListPreference <ListPreference
android:title="@string/pref_title_app_theme" android:title="@string/pref_title_app_theme"
android:summary="@string/pref_description_app_theme" android:summary="@string/pref_description_app_theme"