mirror of
https://github.com/mediathekview/zapp.git
synced 2024-09-20 04:12:14 +02:00
Convert StreamPageFragment to kotlin
This commit is contained in:
parent
1b7d1f4ac1
commit
94f86af588
@ -2,9 +2,8 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectModuleManager">
|
<component name="ProjectModuleManager">
|
||||||
<modules>
|
<modules>
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/2016-09_Zapp.iml" filepath="$PROJECT_DIR$/.idea/modules/2016-09_Zapp.iml" group="2016-09_Zapp" />
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/2016-09_Zapp.iml" filepath="$PROJECT_DIR$/.idea/modules/2016-09_Zapp.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" group="2016-09_Zapp/app" />
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/app/2016-09_Zapp.app.iml" filepath="$PROJECT_DIR$/.idea/modules/app/2016-09_Zapp.app.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/zapp.iml" filepath="$PROJECT_DIR$/zapp.iml" />
|
|
||||||
</modules>
|
</modules>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
@ -1,386 +0,0 @@
|
|||||||
package de.christinecoenen.code.zapp.app.livestream.ui.detail;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.ServiceConnection;
|
|
||||||
import android.content.pm.ActivityInfo;
|
|
||||||
import android.graphics.PorterDuff;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import android.view.KeyEvent;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.Window;
|
|
||||||
import android.widget.ProgressBar;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.appcompat.widget.Toolbar;
|
|
||||||
import androidx.viewpager.widget.ViewPager;
|
|
||||||
|
|
||||||
import de.christinecoenen.code.zapp.R;
|
|
||||||
import de.christinecoenen.code.zapp.app.ZappApplicationBase;
|
|
||||||
import de.christinecoenen.code.zapp.app.livestream.ui.views.ProgramInfoViewBase;
|
|
||||||
import de.christinecoenen.code.zapp.app.player.BackgroundPlayerService;
|
|
||||||
import de.christinecoenen.code.zapp.app.player.Player;
|
|
||||||
import de.christinecoenen.code.zapp.app.player.VideoInfo;
|
|
||||||
import de.christinecoenen.code.zapp.app.settings.repository.SettingsRepository;
|
|
||||||
import de.christinecoenen.code.zapp.databinding.ActivityChannelDetailBinding;
|
|
||||||
import de.christinecoenen.code.zapp.models.channels.ChannelModel;
|
|
||||||
import de.christinecoenen.code.zapp.models.channels.IChannelList;
|
|
||||||
import de.christinecoenen.code.zapp.utils.system.MultiWindowHelper;
|
|
||||||
import de.christinecoenen.code.zapp.utils.system.ShortcutHelper;
|
|
||||||
import de.christinecoenen.code.zapp.utils.video.SwipeablePlayerView;
|
|
||||||
import de.christinecoenen.code.zapp.utils.view.ClickableViewPager;
|
|
||||||
import de.christinecoenen.code.zapp.utils.view.ColorHelper;
|
|
||||||
import de.christinecoenen.code.zapp.utils.view.FullscreenActivity;
|
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
|
||||||
import io.reactivex.disposables.Disposable;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
public class ChannelDetailActivity extends FullscreenActivity implements StreamPageFragment.Listener {
|
|
||||||
|
|
||||||
private static final String EXTRA_CHANNEL_ID = "de.christinecoenen.code.zapp.EXTRA_CHANNEL_ID";
|
|
||||||
|
|
||||||
private Toolbar toolbar;
|
|
||||||
private ClickableViewPager viewPager;
|
|
||||||
private SwipeablePlayerView videoView;
|
|
||||||
private ProgressBar progressView;
|
|
||||||
private ProgramInfoViewBase programInfoView;
|
|
||||||
|
|
||||||
private int playStreamDelayMillis;
|
|
||||||
|
|
||||||
private final Handler playHandler = new Handler();
|
|
||||||
private ChannelDetailAdapter channelDetailAdapter;
|
|
||||||
private ChannelModel currentChannel;
|
|
||||||
private Window window;
|
|
||||||
private IChannelList channelList;
|
|
||||||
private Player player;
|
|
||||||
private BackgroundPlayerService.Binder binder;
|
|
||||||
private SettingsRepository settings;
|
|
||||||
private final CompositeDisposable disposable = new CompositeDisposable();
|
|
||||||
|
|
||||||
private final Runnable playRunnable = this::play;
|
|
||||||
|
|
||||||
private final ChannelDetailAdapter.Listener channelDetailListener =
|
|
||||||
new ChannelDetailAdapter.Listener() {
|
|
||||||
@Override
|
|
||||||
public void onItemSelected(ChannelModel channel) {
|
|
||||||
currentChannel = channel;
|
|
||||||
setTitle(channel.getName());
|
|
||||||
setColor(channel.getColor());
|
|
||||||
|
|
||||||
playDelayed();
|
|
||||||
programInfoView.setChannel(channel);
|
|
||||||
|
|
||||||
ShortcutHelper.reportShortcutUsageGuarded(ChannelDetailActivity.this, channel.getId());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final ViewPager.OnPageChangeListener onPageChangeListener =
|
|
||||||
new ViewPager.SimpleOnPageChangeListener() {
|
|
||||||
@Override
|
|
||||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
|
||||||
int color1 = channelDetailAdapter.getChannel(position).getColor();
|
|
||||||
|
|
||||||
if (positionOffset == 0) {
|
|
||||||
setColor(color1);
|
|
||||||
} else {
|
|
||||||
int color2 = channelDetailAdapter.getChannel(position + 1).getColor();
|
|
||||||
int color = ColorHelper.interpolate(positionOffset, color1, color2);
|
|
||||||
setColor(color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final ServiceConnection backgroundPlayerServiceConnection = new ServiceConnection() {
|
|
||||||
@Override
|
|
||||||
public void onServiceConnected(ComponentName componentName, IBinder service) {
|
|
||||||
binder = (BackgroundPlayerService.Binder) service;
|
|
||||||
binder.setForegroundActivityIntent(getIntent());
|
|
||||||
player = binder.getPlayer();
|
|
||||||
player.setView(videoView);
|
|
||||||
|
|
||||||
Disposable bufferingDisposable = player.isBuffering()
|
|
||||||
.subscribe(ChannelDetailActivity.this::onBufferingChanged, Timber::e);
|
|
||||||
Disposable errorDisposable = player.getErrorResourceId()
|
|
||||||
.subscribe(ChannelDetailActivity.this::onVideoError, Timber::e);
|
|
||||||
disposable.addAll(bufferingDisposable, errorDisposable);
|
|
||||||
|
|
||||||
channelDetailListener.onItemSelected(currentChannel);
|
|
||||||
|
|
||||||
binder.movePlaybackToForeground();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onServiceDisconnected(ComponentName componentName) {
|
|
||||||
player.pause();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Intent getStartIntent(Context context, String channelId) {
|
|
||||||
Intent intent = new Intent(context, ChannelDetailActivity.class);
|
|
||||||
intent.setAction(Intent.ACTION_VIEW);
|
|
||||||
intent.putExtra(EXTRA_CHANNEL_ID, channelId);
|
|
||||||
return intent;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
ActivityChannelDetailBinding binding = ActivityChannelDetailBinding.inflate(getLayoutInflater());
|
|
||||||
setContentView(binding.getRoot());
|
|
||||||
|
|
||||||
toolbar = binding.toolbar;
|
|
||||||
viewPager = binding.viewpagerChannels;
|
|
||||||
videoView = binding.video;
|
|
||||||
programInfoView = binding.programInfo;
|
|
||||||
progressView = binding.progressbarVideo;
|
|
||||||
|
|
||||||
playStreamDelayMillis = getResources().getInteger(R.integer.activity_channel_detail_play_stream_delay_millis);
|
|
||||||
|
|
||||||
setSupportActionBar(toolbar);
|
|
||||||
window = getWindow();
|
|
||||||
|
|
||||||
if (getSupportActionBar() != null) {
|
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
channelList = ((ZappApplicationBase) getApplication()).getChannelRepository().getChannelList();
|
|
||||||
settings = new SettingsRepository(this);
|
|
||||||
|
|
||||||
// pager
|
|
||||||
channelDetailAdapter = new ChannelDetailAdapter(
|
|
||||||
getSupportFragmentManager(), channelList, channelDetailListener);
|
|
||||||
viewPager.setAdapter(channelDetailAdapter);
|
|
||||||
viewPager.addOnPageChangeListener(onPageChangeListener);
|
|
||||||
viewPager.setOnClickListener(view -> contentView.performClick());
|
|
||||||
viewPager.setOnTouchListener(this::onPagerTouch);
|
|
||||||
videoView.setTouchOverlay(viewPager);
|
|
||||||
|
|
||||||
parseIntent(getIntent());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onNewIntent(Intent intent) {
|
|
||||||
super.onNewIntent(intent);
|
|
||||||
// called when coming back from picture in picture mode
|
|
||||||
parseIntent(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStart() {
|
|
||||||
super.onStart();
|
|
||||||
if (MultiWindowHelper.isInsideMultiWindow(this)) {
|
|
||||||
resumeActivity();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("SourceLockedOrientationActivity")
|
|
||||||
@Override
|
|
||||||
protected void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
if (!MultiWindowHelper.isInsideMultiWindow(this)) {
|
|
||||||
resumeActivity();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settings.getLockVideosInLandcapeFormat()) {
|
|
||||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
|
|
||||||
} else {
|
|
||||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
if (!MultiWindowHelper.isInsideMultiWindow(this)) {
|
|
||||||
pauseActivity();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStop() {
|
|
||||||
super.onStop();
|
|
||||||
pauseActivity();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
|
|
||||||
super.onPictureInPictureModeChanged(isInPictureInPictureMode);
|
|
||||||
if (isInPictureInPictureMode) {
|
|
||||||
hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
|
||||||
getMenuInflater().inflate(R.menu.activity_channel_detail, menu);
|
|
||||||
|
|
||||||
if (!MultiWindowHelper.supportsPictureInPictureMode(this)) {
|
|
||||||
menu.removeItem(R.id.menu_pip);
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.onCreateOptionsMenu(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
|
||||||
switch (item.getItemId()) {
|
|
||||||
case R.id.menu_share:
|
|
||||||
startActivity(Intent.createChooser(currentChannel.getVideoShareIntent(), getString(R.string.action_share)));
|
|
||||||
return true;
|
|
||||||
case R.id.menu_play_in_background:
|
|
||||||
binder.movePlaybackToBackground();
|
|
||||||
finish();
|
|
||||||
return true;
|
|
||||||
case R.id.menu_pip:
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
||||||
enterPictureInPictureMode();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
case android.R.id.home:
|
|
||||||
finish();
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
|
||||||
boolean handled = false;
|
|
||||||
|
|
||||||
switch (keyCode) {
|
|
||||||
case KeyEvent.KEYCODE_DPAD_LEFT:
|
|
||||||
handled = prevChannel();
|
|
||||||
break;
|
|
||||||
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
|
||||||
handled = nextChannel();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return handled || super.onKeyUp(keyCode, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onVideoError(Integer messageResourceId) {
|
|
||||||
if (messageResourceId == null || messageResourceId == -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
player.pause();
|
|
||||||
progressView.setVisibility(View.GONE);
|
|
||||||
|
|
||||||
if (channelDetailAdapter.getCurrentFragment() != null) {
|
|
||||||
channelDetailAdapter.getCurrentFragment().onVideoError(getString(messageResourceId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onBufferingChanged(boolean isBuffering) {
|
|
||||||
if (isBuffering) {
|
|
||||||
progressView.setVisibility(View.VISIBLE);
|
|
||||||
} else if (!player.isIdle()) {
|
|
||||||
progressView.setVisibility(View.INVISIBLE);
|
|
||||||
channelDetailAdapter.getCurrentFragment().onVideoStart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onErrorViewClicked() {
|
|
||||||
player.recreate();
|
|
||||||
player.resume();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean onPagerTouch(View view, MotionEvent motionEvent) {
|
|
||||||
delayHide();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void parseIntent(Intent intent) {
|
|
||||||
Bundle extras = intent.getExtras();
|
|
||||||
//noinspection ConstantConditions
|
|
||||||
String channelId = extras.getString(EXTRA_CHANNEL_ID);
|
|
||||||
int channelPosition = channelList.indexOf(channelId);
|
|
||||||
|
|
||||||
viewPager.removeOnPageChangeListener(onPageChangeListener);
|
|
||||||
viewPager.setCurrentItem(channelPosition);
|
|
||||||
viewPager.addOnPageChangeListener(onPageChangeListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void pauseActivity() {
|
|
||||||
disposable.clear();
|
|
||||||
try {
|
|
||||||
unbindService(backgroundPlayerServiceConnection);
|
|
||||||
} catch (IllegalArgumentException ignored) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void resumeActivity() {
|
|
||||||
programInfoView.resume();
|
|
||||||
BackgroundPlayerService.bind(this, backgroundPlayerServiceConnection);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void playDelayed() {
|
|
||||||
if (player != null) {
|
|
||||||
player.pause();
|
|
||||||
}
|
|
||||||
|
|
||||||
playHandler.removeCallbacks(playRunnable);
|
|
||||||
playHandler.postDelayed(playRunnable, playStreamDelayMillis);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void play() {
|
|
||||||
if (currentChannel == null || binder == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Intent currentIntent = ChannelDetailActivity.getStartIntent(this, currentChannel.getId());
|
|
||||||
binder.setForegroundActivityIntent(currentIntent);
|
|
||||||
|
|
||||||
Timber.d("play: %s", currentChannel.getName());
|
|
||||||
player.load(VideoInfo.fromChannel(currentChannel));
|
|
||||||
player.resume();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean prevChannel() {
|
|
||||||
int nextItemIndex = viewPager.getCurrentItem() - 1;
|
|
||||||
|
|
||||||
if (nextItemIndex < 0) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
delayHide();
|
|
||||||
viewPager.setCurrentItem(nextItemIndex, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean nextChannel() {
|
|
||||||
int nextItemIndex = viewPager.getCurrentItem() + 1;
|
|
||||||
|
|
||||||
if (nextItemIndex == channelDetailAdapter.getCount()) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
delayHide();
|
|
||||||
viewPager.setCurrentItem(nextItemIndex, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setColor(int color) {
|
|
||||||
progressView.getIndeterminateDrawable().setColorFilter(color, PorterDuff.Mode.SRC_IN);
|
|
||||||
toolbar.setBackgroundColor(color);
|
|
||||||
|
|
||||||
int colorDarker = ColorHelper.darker(color, 0.075f);
|
|
||||||
window.setStatusBarColor(colorDarker);
|
|
||||||
|
|
||||||
int colorAlpha = ColorHelper.darker(ColorHelper.withAlpha(color, 150), 0.25f);
|
|
||||||
controlsView.setBackgroundColor(colorAlpha);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,354 @@
|
|||||||
|
package de.christinecoenen.code.zapp.app.livestream.ui.detail
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.ComponentName
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.ServiceConnection
|
||||||
|
import android.content.pm.ActivityInfo
|
||||||
|
import android.graphics.PorterDuff
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.IBinder
|
||||||
|
import android.view.*
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.viewpager.widget.ViewPager.OnPageChangeListener
|
||||||
|
import androidx.viewpager.widget.ViewPager.SimpleOnPageChangeListener
|
||||||
|
import de.christinecoenen.code.zapp.R
|
||||||
|
import de.christinecoenen.code.zapp.app.ZappApplicationBase
|
||||||
|
import de.christinecoenen.code.zapp.app.player.BackgroundPlayerService
|
||||||
|
import de.christinecoenen.code.zapp.app.player.BackgroundPlayerService.Companion.bind
|
||||||
|
import de.christinecoenen.code.zapp.app.player.Player
|
||||||
|
import de.christinecoenen.code.zapp.app.player.VideoInfo.Companion.fromChannel
|
||||||
|
import de.christinecoenen.code.zapp.app.settings.repository.SettingsRepository
|
||||||
|
import de.christinecoenen.code.zapp.databinding.ActivityChannelDetailBinding
|
||||||
|
import de.christinecoenen.code.zapp.models.channels.ChannelModel
|
||||||
|
import de.christinecoenen.code.zapp.models.channels.IChannelList
|
||||||
|
import de.christinecoenen.code.zapp.utils.system.MultiWindowHelper.isInsideMultiWindow
|
||||||
|
import de.christinecoenen.code.zapp.utils.system.MultiWindowHelper.supportsPictureInPictureMode
|
||||||
|
import de.christinecoenen.code.zapp.utils.system.ShortcutHelper.reportShortcutUsageGuarded
|
||||||
|
import de.christinecoenen.code.zapp.utils.view.ColorHelper.darker
|
||||||
|
import de.christinecoenen.code.zapp.utils.view.ColorHelper.interpolate
|
||||||
|
import de.christinecoenen.code.zapp.utils.view.ColorHelper.withAlpha
|
||||||
|
import de.christinecoenen.code.zapp.utils.view.FullscreenActivity
|
||||||
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
|
class ChannelDetailActivity : FullscreenActivity(), StreamPageFragment.Listener {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private const val EXTRA_CHANNEL_ID = "de.christinecoenen.code.zapp.EXTRA_CHANNEL_ID"
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getStartIntent(context: Context?, channelId: String?): Intent {
|
||||||
|
return Intent(context, ChannelDetailActivity::class.java).apply {
|
||||||
|
action = Intent.ACTION_VIEW
|
||||||
|
putExtra(EXTRA_CHANNEL_ID, channelId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var binding: ActivityChannelDetailBinding
|
||||||
|
private lateinit var channelList: IChannelList
|
||||||
|
private lateinit var settings: SettingsRepository
|
||||||
|
private lateinit var channelDetailAdapter: ChannelDetailAdapter
|
||||||
|
|
||||||
|
private val disposable = CompositeDisposable()
|
||||||
|
private val playRunnable = Runnable { play() }
|
||||||
|
private val playHandler = Handler()
|
||||||
|
|
||||||
|
private var playStreamDelayMillis = 0
|
||||||
|
private var currentChannel: ChannelModel? = null
|
||||||
|
private var player: Player? = null
|
||||||
|
private var binder: BackgroundPlayerService.Binder? = null
|
||||||
|
|
||||||
|
private val channelDetailListener: ChannelDetailAdapter.Listener = object : ChannelDetailAdapter.Listener {
|
||||||
|
override fun onItemSelected(channel: ChannelModel) {
|
||||||
|
currentChannel = channel
|
||||||
|
title = channel.name
|
||||||
|
|
||||||
|
setColor(channel.color)
|
||||||
|
playDelayed()
|
||||||
|
|
||||||
|
binding.programInfo.setChannel(channel)
|
||||||
|
|
||||||
|
reportShortcutUsageGuarded(this@ChannelDetailActivity, channel.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val onPageChangeListener: OnPageChangeListener = object : SimpleOnPageChangeListener() {
|
||||||
|
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
|
||||||
|
val currentChannelColor = channelDetailAdapter.getChannel(position).color
|
||||||
|
|
||||||
|
if (positionOffset == 0f) {
|
||||||
|
setColor(currentChannelColor)
|
||||||
|
} else {
|
||||||
|
val nextChannelColor = channelDetailAdapter.getChannel(position + 1).color
|
||||||
|
val interpolatedColor = interpolate(positionOffset, currentChannelColor, nextChannelColor)
|
||||||
|
setColor(interpolatedColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val backgroundPlayerServiceConnection: ServiceConnection = object : ServiceConnection {
|
||||||
|
override fun onServiceConnected(componentName: ComponentName, service: IBinder) {
|
||||||
|
binder = service as BackgroundPlayerService.Binder
|
||||||
|
binder!!.setForegroundActivityIntent(intent)
|
||||||
|
|
||||||
|
player = binder!!.getPlayer()
|
||||||
|
player!!.setView(binding.video)
|
||||||
|
|
||||||
|
player!!.isBuffering
|
||||||
|
.subscribe(::onBufferingChanged, Timber::e)
|
||||||
|
.also(disposable::add)
|
||||||
|
|
||||||
|
player!!.errorResourceId
|
||||||
|
.subscribe(::onVideoError, Timber::e)
|
||||||
|
.also(disposable::add)
|
||||||
|
|
||||||
|
channelDetailListener.onItemSelected(currentChannel!!)
|
||||||
|
|
||||||
|
binder!!.movePlaybackToForeground()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onServiceDisconnected(componentName: ComponentName) {
|
||||||
|
player?.pause()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
binding = ActivityChannelDetailBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
playStreamDelayMillis = resources.getInteger(R.integer.activity_channel_detail_play_stream_delay_millis)
|
||||||
|
|
||||||
|
setSupportActionBar(binding.toolbar)
|
||||||
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
|
||||||
|
channelList = (application as ZappApplicationBase).channelRepository.getChannelList()
|
||||||
|
settings = SettingsRepository(this)
|
||||||
|
|
||||||
|
// pager
|
||||||
|
channelDetailAdapter = ChannelDetailAdapter(
|
||||||
|
supportFragmentManager, channelList, channelDetailListener)
|
||||||
|
|
||||||
|
binding.viewpager.adapter = channelDetailAdapter
|
||||||
|
binding.viewpager.addOnPageChangeListener(onPageChangeListener)
|
||||||
|
binding.viewpager.setOnClickListener { contentView.performClick() }
|
||||||
|
binding.viewpager.setOnTouchListener(::onPagerTouch)
|
||||||
|
|
||||||
|
binding.video.setTouchOverlay(binding.viewpager)
|
||||||
|
|
||||||
|
parseIntent(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNewIntent(intent: Intent) {
|
||||||
|
super.onNewIntent(intent)
|
||||||
|
|
||||||
|
// called when coming back from picture in picture mode
|
||||||
|
parseIntent(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
super.onStart()
|
||||||
|
|
||||||
|
if (isInsideMultiWindow(this)) {
|
||||||
|
resumeActivity()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SourceLockedOrientationActivity")
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
|
||||||
|
if (!isInsideMultiWindow(this)) {
|
||||||
|
resumeActivity()
|
||||||
|
}
|
||||||
|
|
||||||
|
requestedOrientation = if (settings.lockVideosInLandcapeFormat) {
|
||||||
|
ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
|
||||||
|
} else {
|
||||||
|
ActivityInfo.SCREEN_ORIENTATION_SENSOR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
|
||||||
|
if (!isInsideMultiWindow(this)) {
|
||||||
|
pauseActivity()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStop() {
|
||||||
|
super.onStop()
|
||||||
|
|
||||||
|
pauseActivity()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean) {
|
||||||
|
super.onPictureInPictureModeChanged(isInPictureInPictureMode)
|
||||||
|
if (isInPictureInPictureMode) {
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
|
menuInflater.inflate(R.menu.activity_channel_detail, menu)
|
||||||
|
|
||||||
|
if (!supportsPictureInPictureMode(this)) {
|
||||||
|
menu.removeItem(R.id.menu_pip)
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.onCreateOptionsMenu(menu)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
return when (item.itemId) {
|
||||||
|
R.id.menu_share -> {
|
||||||
|
startActivity(Intent.createChooser(currentChannel!!.videoShareIntent, getString(R.string.action_share)))
|
||||||
|
true
|
||||||
|
}
|
||||||
|
R.id.menu_play_in_background -> {
|
||||||
|
binder!!.movePlaybackToBackground()
|
||||||
|
finish()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
R.id.menu_pip -> {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
enterPictureInPictureMode()
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
android.R.id.home -> {
|
||||||
|
finish()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
|
||||||
|
var handled = false
|
||||||
|
|
||||||
|
when (keyCode) {
|
||||||
|
KeyEvent.KEYCODE_DPAD_LEFT -> handled = prevChannel()
|
||||||
|
KeyEvent.KEYCODE_DPAD_RIGHT -> handled = nextChannel()
|
||||||
|
}
|
||||||
|
|
||||||
|
return handled || super.onKeyUp(keyCode, event)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onVideoError(messageResourceId: Int?) {
|
||||||
|
if (messageResourceId == null || messageResourceId == -1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
player?.pause()
|
||||||
|
binding.videoProgress.isVisible = false
|
||||||
|
channelDetailAdapter.currentFragment?.onVideoError(getString(messageResourceId))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onBufferingChanged(isBuffering: Boolean) {
|
||||||
|
if (isBuffering) {
|
||||||
|
binding.videoProgress.isVisible = true
|
||||||
|
} else if (!player!!.isIdle) {
|
||||||
|
binding.videoProgress.isVisible = false
|
||||||
|
channelDetailAdapter.currentFragment?.onVideoStart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onErrorViewClicked() {
|
||||||
|
player?.recreate()
|
||||||
|
player?.resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onPagerTouch(view: View, motionEvent: MotionEvent): Boolean {
|
||||||
|
delayHide()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseIntent(intent: Intent) {
|
||||||
|
val extras = intent.extras
|
||||||
|
val channelId = extras!!.getString(EXTRA_CHANNEL_ID)
|
||||||
|
val channelPosition = channelList.indexOf(channelId)
|
||||||
|
|
||||||
|
binding.viewpager.removeOnPageChangeListener(onPageChangeListener)
|
||||||
|
binding.viewpager.currentItem = channelPosition
|
||||||
|
binding.viewpager.addOnPageChangeListener(onPageChangeListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun pauseActivity() {
|
||||||
|
disposable.clear()
|
||||||
|
try {
|
||||||
|
unbindService(backgroundPlayerServiceConnection)
|
||||||
|
} catch (ignored: IllegalArgumentException) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resumeActivity() {
|
||||||
|
binding.programInfo.resume()
|
||||||
|
bind(this, backgroundPlayerServiceConnection)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun playDelayed() {
|
||||||
|
if (player != null) {
|
||||||
|
player!!.pause()
|
||||||
|
}
|
||||||
|
|
||||||
|
playHandler.removeCallbacks(playRunnable)
|
||||||
|
playHandler.postDelayed(playRunnable, playStreamDelayMillis.toLong())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun play() {
|
||||||
|
if (currentChannel == null || binder == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val currentIntent = getStartIntent(this, currentChannel!!.id)
|
||||||
|
binder!!.setForegroundActivityIntent(currentIntent)
|
||||||
|
|
||||||
|
Timber.d("play: %s", currentChannel!!.name)
|
||||||
|
player!!.load(fromChannel(currentChannel!!))
|
||||||
|
player!!.resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun prevChannel(): Boolean {
|
||||||
|
val nextItemIndex = binding.viewpager.currentItem - 1
|
||||||
|
|
||||||
|
return if (nextItemIndex < 0) {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
delayHide()
|
||||||
|
binding.viewpager.setCurrentItem(nextItemIndex, true)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun nextChannel(): Boolean {
|
||||||
|
val nextItemIndex = binding.viewpager.currentItem + 1
|
||||||
|
|
||||||
|
return if (nextItemIndex == channelDetailAdapter.count) {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
delayHide()
|
||||||
|
binding.viewpager.setCurrentItem(nextItemIndex, true)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setColor(color: Int) {
|
||||||
|
binding.videoProgress.indeterminateDrawable.setColorFilter(color, PorterDuff.Mode.SRC_IN)
|
||||||
|
binding.toolbar.setBackgroundColor(color)
|
||||||
|
|
||||||
|
window.statusBarColor = darker(color, 0.075f)
|
||||||
|
|
||||||
|
val colorAlpha = darker(withAlpha(color, 150), 0.25f)
|
||||||
|
controlsView.setBackgroundColor(colorAlpha)
|
||||||
|
}
|
||||||
|
}
|
@ -1,69 +0,0 @@
|
|||||||
package de.christinecoenen.code.zapp.app.livestream.ui.detail;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.fragment.app.Fragment;
|
|
||||||
import androidx.fragment.app.FragmentManager;
|
|
||||||
import androidx.fragment.app.FragmentStatePagerAdapter;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
|
|
||||||
import de.christinecoenen.code.zapp.models.channels.ChannelModel;
|
|
||||||
import de.christinecoenen.code.zapp.models.channels.IChannelList;
|
|
||||||
|
|
||||||
|
|
||||||
class ChannelDetailAdapter extends FragmentStatePagerAdapter {
|
|
||||||
|
|
||||||
private final IChannelList channelList;
|
|
||||||
private final Listener listener;
|
|
||||||
private StreamPageFragment currentFragment;
|
|
||||||
|
|
||||||
ChannelDetailAdapter(FragmentManager fragmentManager, IChannelList channelList,
|
|
||||||
Listener listener) {
|
|
||||||
super(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
|
|
||||||
this.channelList = channelList;
|
|
||||||
this.listener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
StreamPageFragment getCurrentFragment() {
|
|
||||||
return currentFragment;
|
|
||||||
}
|
|
||||||
|
|
||||||
ChannelModel getChannel(int index) {
|
|
||||||
return channelList.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Fragment getItem(int position) {
|
|
||||||
ChannelModel channelModel = channelList.get(position);
|
|
||||||
return StreamPageFragment.newInstance(channelModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getCount() {
|
|
||||||
return channelList.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CharSequence getPageTitle(int position) {
|
|
||||||
return channelList.get(position).getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
|
|
||||||
if (currentFragment != object) {
|
|
||||||
if (currentFragment != null) {
|
|
||||||
// tell old fragment it's no longer visible
|
|
||||||
currentFragment.onHide();
|
|
||||||
}
|
|
||||||
|
|
||||||
currentFragment = ((StreamPageFragment) object);
|
|
||||||
listener.onItemSelected(channelList.get(position));
|
|
||||||
}
|
|
||||||
|
|
||||||
super.setPrimaryItem(container, position, object);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Listener {
|
|
||||||
void onItemSelected(ChannelModel model);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,54 @@
|
|||||||
|
package de.christinecoenen.code.zapp.app.livestream.ui.detail
|
||||||
|
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.FragmentManager
|
||||||
|
import androidx.fragment.app.FragmentStatePagerAdapter
|
||||||
|
import de.christinecoenen.code.zapp.models.channels.ChannelModel
|
||||||
|
import de.christinecoenen.code.zapp.models.channels.IChannelList
|
||||||
|
|
||||||
|
internal class ChannelDetailAdapter(
|
||||||
|
fragmentManager: FragmentManager,
|
||||||
|
private val channelList: IChannelList,
|
||||||
|
private val listener: Listener
|
||||||
|
) : FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
|
||||||
|
|
||||||
|
var currentFragment: StreamPageFragment? = null
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun getChannel(index: Int): ChannelModel {
|
||||||
|
return channelList[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItem(position: Int): Fragment {
|
||||||
|
val channelModel = channelList[position]
|
||||||
|
return StreamPageFragment.newInstance(channelModel)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getCount(): Int {
|
||||||
|
return channelList.size()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getPageTitle(position: Int): CharSequence? {
|
||||||
|
return channelList[position].name
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setPrimaryItem(container: ViewGroup, position: Int, fragmentArgument: Any) {
|
||||||
|
super.setPrimaryItem(container, position, fragmentArgument)
|
||||||
|
|
||||||
|
if (currentFragment == fragmentArgument) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// tell old fragment it's no longer visible
|
||||||
|
currentFragment?.onHide()
|
||||||
|
|
||||||
|
currentFragment = fragmentArgument as StreamPageFragment
|
||||||
|
|
||||||
|
listener.onItemSelected(channelList[position])
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Listener {
|
||||||
|
fun onItemSelected(channel: ChannelModel)
|
||||||
|
}
|
||||||
|
}
|
@ -1,137 +0,0 @@
|
|||||||
package de.christinecoenen.code.zapp.app.livestream.ui.detail;
|
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.animation.Animation;
|
|
||||||
import android.view.animation.AnimationUtils;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.fragment.app.Fragment;
|
|
||||||
|
|
||||||
import de.christinecoenen.code.zapp.databinding.FragmentStreamPageBinding;
|
|
||||||
import de.christinecoenen.code.zapp.models.channels.ChannelModel;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
public class StreamPageFragment extends Fragment {
|
|
||||||
|
|
||||||
private static final String ARGUMENT_CHANNEL_MODEL = "ARGUMENT_CHANNEL_MODEL";
|
|
||||||
|
|
||||||
private View rootView;
|
|
||||||
private TextView errorText;
|
|
||||||
private Listener listener;
|
|
||||||
|
|
||||||
static StreamPageFragment newInstance(ChannelModel channelModel) {
|
|
||||||
StreamPageFragment fragment = new StreamPageFragment();
|
|
||||||
Bundle args = new Bundle();
|
|
||||||
args.putSerializable(ARGUMENT_CHANNEL_MODEL, channelModel);
|
|
||||||
fragment.setArguments(args);
|
|
||||||
return fragment;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
|
||||||
FragmentStreamPageBinding binding = FragmentStreamPageBinding.inflate(inflater, container, false);
|
|
||||||
|
|
||||||
rootView = binding.getRoot();
|
|
||||||
errorText = binding.textError;
|
|
||||||
|
|
||||||
Bundle args = requireArguments();
|
|
||||||
ChannelModel channel = (ChannelModel) args.getSerializable(ARGUMENT_CHANNEL_MODEL);
|
|
||||||
|
|
||||||
if (channel != null) {
|
|
||||||
ImageView logoView = binding.logo;
|
|
||||||
logoView.setImageResource(channel.getDrawableId());
|
|
||||||
logoView.setContentDescription(channel.getName());
|
|
||||||
errorText.setBackgroundColor(channel.getColor());
|
|
||||||
|
|
||||||
errorText.setOnClickListener(view -> onErrorViewClick());
|
|
||||||
|
|
||||||
if (channel.getSubtitle() != null) {
|
|
||||||
TextView subtitleText = binding.subtitle;
|
|
||||||
subtitleText.setText(channel.getSubtitle());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Timber.w("channel argument is null");
|
|
||||||
}
|
|
||||||
|
|
||||||
return rootView;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAttach(@NonNull Context context) {
|
|
||||||
super.onAttach(context);
|
|
||||||
|
|
||||||
if (context instanceof Listener) {
|
|
||||||
listener = (Listener) context;
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException("Activity must implement StreamPageFragment.Listener.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDetach() {
|
|
||||||
super.onDetach();
|
|
||||||
listener = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStop() {
|
|
||||||
super.onStop();
|
|
||||||
// don't use onPause to support multiwindow feature
|
|
||||||
rootView.setVisibility(View.VISIBLE);
|
|
||||||
errorText.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void onHide() {
|
|
||||||
rootView.setVisibility(View.VISIBLE);
|
|
||||||
errorText.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void onVideoStart() {
|
|
||||||
fadeOutLogo();
|
|
||||||
}
|
|
||||||
|
|
||||||
void onVideoError(String message) {
|
|
||||||
rootView.setVisibility(View.VISIBLE);
|
|
||||||
errorText.setVisibility(View.VISIBLE);
|
|
||||||
errorText.setText(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onErrorViewClick() {
|
|
||||||
listener.onErrorViewClicked();
|
|
||||||
onHide();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fadeOutLogo() {
|
|
||||||
if (rootView.getVisibility() == View.VISIBLE) {
|
|
||||||
Animation fadeOutAnimation = AnimationUtils.
|
|
||||||
loadAnimation(getContext(), android.R.anim.fade_out);
|
|
||||||
fadeOutAnimation.setAnimationListener(new Animation.AnimationListener() {
|
|
||||||
@Override
|
|
||||||
public void onAnimationStart(Animation animation) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAnimationEnd(Animation animation) {
|
|
||||||
rootView.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAnimationRepeat(Animation animation) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
rootView.startAnimation(fadeOutAnimation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface Listener {
|
|
||||||
void onErrorViewClicked();
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,122 @@
|
|||||||
|
package de.christinecoenen.code.zapp.app.livestream.ui.detail
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.animation.Animation
|
||||||
|
import android.view.animation.AnimationUtils
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import de.christinecoenen.code.zapp.databinding.FragmentStreamPageBinding
|
||||||
|
import de.christinecoenen.code.zapp.models.channels.ChannelModel
|
||||||
|
|
||||||
|
class StreamPageFragment : Fragment() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val ARGUMENT_CHANNEL_MODEL = "ARGUMENT_CHANNEL_MODEL"
|
||||||
|
|
||||||
|
fun newInstance(channelModel: ChannelModel?): StreamPageFragment {
|
||||||
|
return StreamPageFragment().apply {
|
||||||
|
arguments = Bundle().apply {
|
||||||
|
putSerializable(ARGUMENT_CHANNEL_MODEL, channelModel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _binding: FragmentStreamPageBinding? = null
|
||||||
|
private val binding: FragmentStreamPageBinding get() = _binding!!
|
||||||
|
|
||||||
|
private var listener: Listener? = null
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
|
_binding = FragmentStreamPageBinding.inflate(inflater, container, false)
|
||||||
|
|
||||||
|
val channel = requireArguments().getSerializable(ARGUMENT_CHANNEL_MODEL) as ChannelModel?
|
||||||
|
?: throw IllegalArgumentException("channel argument is null")
|
||||||
|
|
||||||
|
binding.logo.setImageResource(channel.drawableId)
|
||||||
|
binding.logo.contentDescription = channel.name
|
||||||
|
|
||||||
|
binding.textError.setBackgroundColor(channel.color)
|
||||||
|
binding.textError.setOnClickListener { onErrorViewClick() }
|
||||||
|
|
||||||
|
if (channel.subtitle != null) {
|
||||||
|
binding.subtitle.text = channel.subtitle
|
||||||
|
}
|
||||||
|
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAttach(context: Context) {
|
||||||
|
super.onAttach(context)
|
||||||
|
|
||||||
|
listener = if (context is Listener) {
|
||||||
|
context
|
||||||
|
} else {
|
||||||
|
throw RuntimeException("Activity must implement StreamPageFragment.Listener.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDetach() {
|
||||||
|
super.onDetach()
|
||||||
|
listener = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStop() {
|
||||||
|
super.onStop()
|
||||||
|
|
||||||
|
// don't use onPause to support multiwindow feature
|
||||||
|
binding.root.isVisible = true
|
||||||
|
binding.textError.isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onHide() {
|
||||||
|
binding.root.isVisible = true
|
||||||
|
binding.textError.isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onVideoStart() {
|
||||||
|
fadeOutLogo()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onVideoError(message: String?) {
|
||||||
|
binding.root.isVisible = true
|
||||||
|
binding.textError.isVisible = true
|
||||||
|
binding.textError.text = message
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onErrorViewClick() {
|
||||||
|
listener?.onErrorViewClicked()
|
||||||
|
onHide()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fadeOutLogo() {
|
||||||
|
if (!binding.root.isVisible) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val fadeOutAnimation = AnimationUtils.loadAnimation(context, android.R.anim.fade_out)
|
||||||
|
|
||||||
|
fadeOutAnimation.setAnimationListener(object : Animation.AnimationListener {
|
||||||
|
override fun onAnimationRepeat(animation: Animation) {}
|
||||||
|
override fun onAnimationStart(animation: Animation) {}
|
||||||
|
override fun onAnimationEnd(animation: Animation) {
|
||||||
|
binding.root.isVisible = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
binding.root.startAnimation(fadeOutAnimation)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Listener {
|
||||||
|
fun onErrorViewClicked()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user