Commit 74afdc0a authored by Thomas's avatar Thomas
Browse files

Some changes

parent 66161f4e
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
package app.fedilab.android;

import static org.junit.Assert.assertEquals;

import android.content.Context;

import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;

import org.junit.Test;
import org.junit.runner.RunWith;

import static org.junit.Assert.*;

/**
 * Instrumented test, which will execute on an Android device.
 *
+207 −0
Original line number Diff line number Diff line
package app.fedilab.android.activities;
/* Copyright 2021 Thomas Schneider
 *
 * This file is a part of TubeLab
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the
 * GNU General Public License as published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 *
 * TubeLab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with TubeLab; if not,
 * see <http://www.gnu.org/licenses>. */


import static app.fedilab.android.peertube.helper.Helper.CAST_ID;

import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.webkit.MimeTypeMap;

import androidx.appcompat.app.AlertDialog;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;

import com.google.android.exoplayer2.SimpleExoPlayer;

import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.security.GeneralSecurityException;

import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityPeertubeBinding;
import app.fedilab.android.mastodon.activities.BaseBarActivity;
import app.fedilab.android.peertube.client.data.VideoData;
import app.fedilab.android.peertube.helper.Helper;
import su.litvak.chromecast.api.v2.ChromeCast;
import su.litvak.chromecast.api.v2.MediaStatus;
import su.litvak.chromecast.api.v2.Status;


public class BasePeertubeActivity extends BaseBarActivity {

    protected ActivityPeertubeBinding binding;
    protected VideoData.Video peertube;
    protected SimpleExoPlayer player;
    protected String videoURL;
    protected String subtitlesStr;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityPeertubeBinding.inflate(getLayoutInflater());
        View view = binding.getRoot();
        setContentView(view);
        binding.minController.castPlay.setOnClickListener(v -> {
            binding.minController.castLoader.setVisibility(View.VISIBLE);
            if (PeertubeBaseMainActivity.chromeCast != null) {
                new Thread(() -> {
                    try {
                        int icon = -1;
                        if (PeertubeBaseMainActivity.chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PLAYING) {
                            PeertubeBaseMainActivity.chromeCast.pause();
                            icon = R.drawable.ic_baseline_play_arrow_32;
                        } else if (PeertubeBaseMainActivity.chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PAUSED) {
                            PeertubeBaseMainActivity.chromeCast.play();
                            icon = R.drawable.ic_baseline_pause_32;
                        }
                        if (icon != -1) {
                            Handler mainHandler = new Handler(Looper.getMainLooper());
                            int finalIcon = icon;
                            Runnable myRunnable = () -> binding.minController.castPlay.setImageResource(finalIcon);
                            mainHandler.post(myRunnable);
                        }
                        Handler mainHandler = new Handler(Looper.getMainLooper());
                        Runnable myRunnable = () -> binding.minController.castLoader.setVisibility(View.GONE);
                        mainHandler.post(myRunnable);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }).start();
            }
        });
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.action_cast) {
            if (PeertubeBaseMainActivity.chromeCasts != null && PeertubeBaseMainActivity.chromeCasts.size() > 0) {
                String[] chromecast_choice = new String[PeertubeBaseMainActivity.chromeCasts.size()];
                AlertDialog.Builder alt_bld = new AlertDialog.Builder(this);
                alt_bld.setTitle(R.string.chromecast_choice);
                int i = 0;
                for (ChromeCast cc : PeertubeBaseMainActivity.chromeCasts) {
                    chromecast_choice[i] = cc.getTitle();
                    i++;
                }
                i = 0;
                for (ChromeCast cc : PeertubeBaseMainActivity.chromeCasts) {
                    if (PeertubeBaseMainActivity.chromecastActivated && cc.isConnected()) {
                        break;
                    }
                    i++;
                }

                alt_bld.setSingleChoiceItems(chromecast_choice, i, (dialog, position) -> {
                    PeertubeBaseMainActivity.chromeCast = PeertubeBaseMainActivity.chromeCasts.get(position);
                    new Thread(() -> {
                        if (PeertubeBaseMainActivity.chromeCast != null) {
                            Intent intentBC = new Intent(Helper.RECEIVE_CAST_SETTINGS);
                            Bundle b = new Bundle();
                            if (PeertubeBaseMainActivity.chromecastActivated) {
                                b.putInt("displayed", 0);
                                intentBC.putExtras(b);
                                LocalBroadcastManager.getInstance(BasePeertubeActivity.this).sendBroadcast(intentBC);
                                Handler mainHandler = new Handler(Looper.getMainLooper());
                                Runnable myRunnable = () -> {
                                    binding.doubleTapPlayerView.setVisibility(View.VISIBLE);
                                    binding.minController.castMiniController.setVisibility(View.GONE);
                                };
                                mainHandler.post(myRunnable);

                            } else {
                                b.putInt("displayed", 1);
                                b.putParcelable("castedTube", peertube);
                                intentBC.putExtras(b);
                                LocalBroadcastManager.getInstance(BasePeertubeActivity.this).sendBroadcast(intentBC);
                                try {
                                    Handler mainHandler = new Handler(Looper.getMainLooper());
                                    Runnable myRunnable = () -> {
                                        invalidateOptionsMenu();
                                        binding.minController.castLoader.setVisibility(View.VISIBLE);
                                        player.setPlayWhenReady(false);
                                        binding.doubleTapPlayerView.setVisibility(View.GONE);
                                        binding.minController.castMiniController.setVisibility(View.VISIBLE);
                                        dialog.dismiss();
                                        if (videoURL != null) {
                                            if (player != null && player.getCurrentPosition() > 0) {
                                                videoURL += "?start=" + (player.getCurrentPosition() / 1000);
                                            }
                                        }
                                    };
                                    mainHandler.post(myRunnable);
                                    if (!PeertubeBaseMainActivity.chromeCast.isConnected()) {
                                        PeertubeBaseMainActivity.chromeCast.connect();
                                    }
                                    myRunnable = this::invalidateOptionsMenu;
                                    mainHandler.post(myRunnable);
                                    Status status = PeertubeBaseMainActivity.chromeCast.getStatus();
                                    if (PeertubeBaseMainActivity.chromeCast.isAppAvailable(CAST_ID) && !status.isAppRunning(CAST_ID)) {
                                        PeertubeBaseMainActivity.chromeCast.launchApp(CAST_ID);
                                    }
                                    if (videoURL != null) {
                                        String mime = MimeTypeMap.getFileExtensionFromUrl(videoURL);
                                        PeertubeBaseMainActivity.chromeCast.setRequestTimeout(60000);
                                        PeertubeBaseMainActivity.chromeCast.load(peertube.getTitle(), null, videoURL, mime);
                                        PeertubeBaseMainActivity.chromeCast.play();
                                        binding.minController.castPlay.setImageResource(R.drawable.ic_baseline_pause_32);
                                    }
                                    myRunnable = () -> binding.minController.castLoader.setVisibility(View.GONE);
                                    mainHandler.post(myRunnable);
                                } catch (IOException | GeneralSecurityException e) {
                                    e.printStackTrace();
                                }
                            }
                            Handler mainHandler = new Handler(Looper.getMainLooper());
                            Runnable myRunnable = () -> {
                                invalidateOptionsMenu();
                                dialog.dismiss();
                            };
                            mainHandler.post(myRunnable);
                        }
                    }).start();
                });
                alt_bld.setPositiveButton(R.string.close, (dialog, id) -> dialog.dismiss());
                AlertDialog alert = alt_bld.create();
                alert.show();
            }
        }
        return super.onOptionsItemSelected(item);
    }


    @Override
    public boolean onCreateOptionsMenu(@NotNull Menu menu) {
        getMenuInflater().inflate(R.menu.video_menu, menu);
        MenuItem castItem = menu.findItem(R.id.action_cast);
        if (PeertubeBaseMainActivity.chromeCasts != null && PeertubeBaseMainActivity.chromeCasts.size() > 0) {
            castItem.setVisible(true);
            if (PeertubeBaseMainActivity.chromeCast != null && PeertubeBaseMainActivity.chromeCast.isConnected()) {
                castItem.setIcon(R.drawable.ic_baseline_cast_connected_24);
            } else {
                castItem.setIcon(R.drawable.ic_baseline_cast_24);
            }
        }
        return true;
    }

}
+262 −0
Original line number Diff line number Diff line
package app.fedilab.android.activities;
/* Copyright 2021 Thomas Schneider
 *
 * This file is a part of TubeLab
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the
 * GNU General Public License as published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 *
 * TubeLab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with TubeLab; if not,
 * see <http://www.gnu.org/licenses>. */

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.View;

import androidx.appcompat.app.AppCompatActivity;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;

import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;

import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityMainPeertubeBinding;
import app.fedilab.android.peertube.client.data.VideoData;
import app.fedilab.android.peertube.helper.Helper;
import su.litvak.chromecast.api.v2.ChromeCast;
import su.litvak.chromecast.api.v2.ChromeCasts;
import su.litvak.chromecast.api.v2.ChromeCastsListener;
import su.litvak.chromecast.api.v2.MediaStatus;

public abstract class PeertubeBaseMainActivity extends AppCompatActivity implements ChromeCastsListener {

    public static List<ChromeCast> chromeCasts;
    public static ChromeCast chromeCast;
    public static boolean chromecastActivated = false;
    protected ActivityMainPeertubeBinding binding;
    private BroadcastReceiver manage_chromecast;
    private VideoData.Video castedTube;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityMainPeertubeBinding.inflate(getLayoutInflater());
        View view = binding.getRoot();
        setContentView(view);
        ChromeCastsListener chromeCastsListener = this;
        ChromeCasts.registerListener(chromeCastsListener);


        binding.castClose.setOnClickListener(v -> {
            Intent intentBC = new Intent(Helper.RECEIVE_CAST_SETTINGS);
            Bundle b = new Bundle();
            b.putInt("displayed", 0);
            intentBC.putExtras(b);
            LocalBroadcastManager.getInstance(PeertubeBaseMainActivity.this).sendBroadcast(intentBC);
        });

        binding.castTogglePlay.setOnClickListener(v -> {
            if (chromeCast != null) {
                new Thread(() -> {
                    try {
                        Handler mainHandler = new Handler(Looper.getMainLooper());
                        Runnable myRunnable = () -> binding.castTogglePlay.setVisibility(View.GONE);
                        mainHandler.post(myRunnable);
                        int icon = -1;
                        if (chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PLAYING) {
                            chromeCast.pause();
                            icon = R.drawable.ic_baseline_play_arrow_32;
                        } else if (chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PAUSED) {
                            chromeCast.play();
                            icon = R.drawable.ic_baseline_pause_32;
                        }
                        if (icon != -1) {
                            int finalIcon = icon;
                            myRunnable = () -> binding.castTogglePlay.setImageResource(finalIcon);
                            mainHandler.post(myRunnable);
                        }
                        myRunnable = () -> binding.castTogglePlay.setVisibility(View.VISIBLE);
                        mainHandler.post(myRunnable);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }).start();
            }
        });


        manage_chromecast = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                Bundle b = intent.getExtras();
                assert b != null;
                int state = b.getInt("state_asked", -1);
                int displayed = b.getInt("displayed", -1);
                castedTube = b.getParcelable("castedTube");

                if (state == 1) {
                    discoverCast();
                } else if (state == 0) {
                    new Thread(() -> {
                        try {
                            if (chromeCast != null) {
                                chromeCast.stopApp();
                                chromeCast.disconnect();
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }).start();
                }
                if (displayed == 1) {
                    chromecastActivated = true;
                    if (castedTube != null) {
                        binding.castInfo.setVisibility(View.VISIBLE);
                        Helper.loadGiF(PeertubeBaseMainActivity.this, castedTube.getThumbnailPath(), binding.castView);
                        binding.castTitle.setText(castedTube.getTitle());
                        binding.castDescription.setText(castedTube.getDescription());
                    }
                } else if (displayed == 0) {
                    chromecastActivated = false;
                    binding.castInfo.setVisibility(View.GONE);
                    new Thread(() -> {
                        try {
                            if (chromeCast != null) {
                                chromeCast.stopApp();
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }).start();
                }
            }
        };
        LocalBroadcastManager.getInstance(PeertubeBaseMainActivity.this).registerReceiver(manage_chromecast, new IntentFilter(Helper.RECEIVE_CAST_SETTINGS));
    }

    @Override
    public void newChromeCastDiscovered(ChromeCast chromeCast) {
        if (chromeCasts == null) {
            chromeCasts = new ArrayList<>();
            chromeCasts.add(chromeCast);
        } else {
            boolean canBeAdded = true;
            for (ChromeCast cast : chromeCasts) {
                if (cast.getName().compareTo(chromeCast.getName()) == 0) {
                    canBeAdded = false;
                    break;
                }
            }
            if (canBeAdded) {
                chromeCasts.add(chromeCast);
            }
        }
        try {
            if (chromeCast.isAppRunning(Helper.CAST_ID) && chromeCast.getMediaStatus() != null && chromeCast.getMediaStatus().playerState != null) {
                if (binding.castInfo.getVisibility() == View.GONE) {
                    binding.castInfo.setVisibility(View.VISIBLE);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void chromeCastRemoved(ChromeCast chromeCast) {

    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        ChromeCasts.unregisterListener(this);
        if (manage_chromecast != null) {
            LocalBroadcastManager.getInstance(PeertubeBaseMainActivity.this).unregisterReceiver(manage_chromecast);

            new Thread(() -> {
                if (chromeCasts != null && chromeCasts.size() > 0) {
                    for (ChromeCast cast : chromeCasts) {
                        try {
                            cast.stopApp();
                            cast.disconnect();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
        }
        if (chromeCasts != null) {
            chromeCasts = null;
        }
        if (chromeCast != null) {
            chromeCast = null;
        }
    }


    //Method for discovering cast devices
    public void discoverCast() {

        new Thread(() -> {
            if (chromeCasts != null) {
                for (ChromeCast cast : chromeCasts) {
                    try {
                        cast.disconnect();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                chromeCasts = null;
            }
            chromeCasts = new ArrayList<>();
            try {
                List<NetworkInterface> interfaces;
                interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
                for (NetworkInterface ni : interfaces) {
                    if ((!ni.isLoopback()) && ni.isUp() && (ni.getName().equals("wlan0"))) {
                        Enumeration<InetAddress> inetAddressEnumeration = ni.getInetAddresses();
                        while (inetAddressEnumeration.hasMoreElements()) {
                            InetAddress inetAddress = inetAddressEnumeration.nextElement();
                            ChromeCasts.restartDiscovery(inetAddress);
                            int tryFind = 0;
                            while (ChromeCasts.get().isEmpty() && tryFind < 5) {
                                try {
                                    //noinspection BusyWait
                                    Thread.sleep(1000);
                                    tryFind++;
                                } catch (InterruptedException ignored) {
                                }
                            }
                        }
                    }
                }
                ChromeCasts.stopDiscovery();
                Handler mainHandler = new Handler(Looper.getMainLooper());
                Runnable myRunnable = this::invalidateOptionsMenu;
                mainHandler.post(myRunnable);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
    }


}
+10 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/action_cast"
        android:icon="@drawable/ic_baseline_cast_24"
        android:title="@string/cast"
        android:visible="false"
        app:showAsAction="always" />
</menu>
+1 −1
Original line number Diff line number Diff line
@@ -28,9 +28,9 @@
        android:icon="@mipmap/ic_launcher_bubbles"
        android:label="@string/app_name"
        android:largeHeap="true"
        android:requestLegacyExternalStorage="true"
        android:roundIcon="@mipmap/ic_launcher_bubbles_round"
        android:supportsRtl="true"
        android:requestLegacyExternalStorage="true"
        android:theme="@style/AppTheme"
        android:usesCleartextTraffic="true"
        tools:replace="android:allowBackup">
Loading