Skip to content

Commit

Permalink
major feature enhancements
Browse files Browse the repository at this point in the history
* new 'Settings' menu item
  - offers 3 options to configure how video URLs will be watched:
    * internal video player w/ Chromecast sender
      - default option
      - same behavior as previous app versions
        * uses ExoPlayer to watch video url
          - all HTTP requests include the referer url
        * when the video url is cast to Chromecast
          - the referer url is ignored,
            because Chromecast receiver apps aren't allowed
            to change this HTTP request header
    * external video player
      - starts an implicit Intent
        * allows any app with a matching Intent filter to take over
        * only includes the video url (data) and mime-type (type)
    * ExoAirPlayer sender
      - works with the receiver app:
          github.com/warren-bank/Android-ExoPlayer-AirPlay-Receiver
      - loads the URL in a WebView:
          'http://webcast-reloaded.surge.sh/airplay_sender.html' +
          `#/watch/${base64_video}/referer/${base64_referer}`
      - this page prepopulates the form fields from URL hash:
        * video url
        * referer url
      - this page prepopulates the form fields from previous use:
        * host
        * port
        * https
      - this page provides a basic UI to control any receiver app
        that is reachable through the network

caveats

* when using the 'ExoAirPlayer sender'
  - the page that loads in a WebView uses modern javascript
    * this is incompatible with very old web browsers,
      such as the native WebView in Android prior to 5.0
    * this issue will be addressed in a future release
  • Loading branch information
warren-bank committed Aug 27, 2020
1 parent 2c2471c commit 980d87a
Show file tree
Hide file tree
Showing 11 changed files with 392 additions and 24 deletions.
10 changes: 10 additions & 0 deletions android-studio-project/WebCast/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@
</intent-filter>
</activity>

<activity
android:name=".webview.SettingsActivity"
android:theme="@style/AppTheme.NoActionBar"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode" />

<activity
android:name=".webview.ExoAirPlayerSenderActivity"
android:theme="@style/AppTheme.NoActionBar"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode" />

<activity
android:name=".exoplayer2.VideoActivity"
android:theme="@style/AppTheme.NoActionBar"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.view.Menu;
Expand Down Expand Up @@ -137,6 +138,7 @@ public static boolean contains(ArrayList<DrawerListItem> items, String uri) {

private String current_page_url;
private WebView webView;
private boolean shouldClearWebView;
private BrowserWebViewClient webViewClient;
private BrowserDownloadListener downloadListener;
private ProgressBar progressBar;
Expand Down Expand Up @@ -224,6 +226,7 @@ public boolean onQueryTextChange(String newText) {
@Override
public void onStart() {
super.onStart();
shouldClearWebView = true;

updateCurrentPage(current_page_url, false);

Expand All @@ -249,21 +252,11 @@ public void onPause() {
}

@Override
public void onDestroy() {
super.onDestroy();
public void onStop() {
super.onStop();

webView.clearCache(true);
webView.clearHistory();
webView.clearFormData();
webView.clearSslPreferences();
WebStorage.getInstance().deleteAllData();
if (Build.VERSION.SDK_INT >= 21) {
CookieManager.getInstance().removeAllCookies(null);
CookieManager.getInstance().flush();
}
else {
CookieManager.getInstance().removeAllCookie();
}
if (shouldClearWebView)
clearWebView();
}

// ---------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -511,7 +504,7 @@ protected void addSavedVideo(String uri, String mimeType, String referer) {
drawer_right_videos_arrayAdapter.notifyDataSetChanged();

// conditionally show icon
if (drawer_right_videos_arrayList.size() > 1) {
if ((drawer_right_videos_arrayList.size() > 1) && (BrowserUtils.getVideoPlayerPreferenceIndex(BrowserActivity.this) == 0)) {
drawer_right_videos_icon_watch_all.setVisibility(View.VISIBLE);
}
}
Expand Down Expand Up @@ -877,6 +870,21 @@ private void updateCurrentPage(String uri, boolean loadUrl) {
}
}

private void clearWebView() {
webView.clearCache(true);
webView.clearHistory();
webView.clearFormData();
webView.clearSslPreferences();
WebStorage.getInstance().deleteAllData();
if (Build.VERSION.SDK_INT >= 21) {
CookieManager.getInstance().removeAllCookies(null);
CookieManager.getInstance().flush();
}
else {
CookieManager.getInstance().removeAllCookie();
}
}

// ---------------------------------------------------------------------------------------------
// Intent:
// ---------------------------------------------------------------------------------------------
Expand All @@ -892,11 +900,50 @@ private void openVideos(ArrayList<String> arrSources) {
private void openVideo(DrawerListItem item) {
if (item == null) return;

ArrayList<String> arrSources = new ArrayList<String>(3);
arrSources.add(item.uri);
arrSources.add(item.mimeType);
arrSources.add(item.referer);
openVideos(arrSources);
int videoPlayerPreferenceIndex = BrowserUtils.getVideoPlayerPreferenceIndex(BrowserActivity.this);

switch(videoPlayerPreferenceIndex) {

case 2: {
// ExoAirPlayer sender
Intent in = new Intent(BrowserActivity.this, ExoAirPlayerSenderActivity.class);
in.putExtra("video", item.uri);
in.putExtra("referer", item.referer);
startActivity(in);
shouldClearWebView = false;
return;
}

case 1: {
// external video player
Intent in = new Intent();
Uri data = Uri.parse(item.uri);
String type = Intent.normalizeMimeType(item.mimeType);

in.setAction(Intent.ACTION_VIEW);
in.setDataAndType(data, type);

if (in.resolveActivity(getPackageManager()) != null) {
// add title to chooser dialog
in = Intent.createChooser(in, getString(R.string.title_intent_chooser));

startActivity(in);
return;
}
// else: fall through to default case
}

case 0:
default: {
// internal video player w/ Chromecast sender
ArrayList<String> arrSources = new ArrayList<String>(3);
arrSources.add(item.uri);
arrSources.add(item.mimeType);
arrSources.add(item.referer);
openVideos(arrSources);
return;
}
}
}

private void openAllVideos() {
Expand Down Expand Up @@ -962,6 +1009,12 @@ public boolean onOptionsItemSelected(MenuItem menuItem) {
return true;
}

case R.id.action_settings: {
Intent in = new Intent(BrowserActivity.this, SettingsActivity.class);
startActivity(in);
return true;
}

case R.id.action_exit: {
ExitActivity.exit(BrowserActivity.this);
return true;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package com.github.warren_bank.webcast.webview;

import com.github.warren_bank.webcast.R;

import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Base64;
import android.view.inputmethod.InputMethodManager;
import android.view.View;
import android.view.ViewGroup;
Expand Down Expand Up @@ -33,4 +38,43 @@ public static void resizeDrawerWidthByPercentOfScreen(Context context, View view
view.setLayoutParams(params);
}

public static String getVideoPlayerPreference(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
String pref_key = context.getString(R.string.pref_videoplayer_key);
String pref_default = context.getString(R.string.pref_videoplayer_default);

return prefs.getString(pref_key, pref_default);
}

public static int getIndexOfArray(Object needle, Object[] haystack) {
for (int i=0; i < haystack.length; i++) {
if (
((haystack[i] != null) && haystack[i].equals(needle))
|| ((haystack[i] == null) && (needle == null))
) {
return i;
}
}
return -1;
}

public static int getVideoPlayerPreferenceIndex(Context context) {
String pref_value = BrowserUtils.getVideoPlayerPreference(context);
String[] pref_values = context.getResources().getStringArray(R.array.pref_videoplayer_array_values);

return BrowserUtils.getIndexOfArray(pref_value, pref_values);
}

public static String base64_encode(String input) {
try {
byte[] bytes = input.getBytes("UTF-8");
int flags = Base64.NO_WRAP | Base64.URL_SAFE;
String output = Base64.encodeToString(bytes, flags);
return output;
}
catch (Exception e) {
return null;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package com.github.warren_bank.webcast.webview;

import com.github.warren_bank.webcast.R;

import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.webkit.CookieManager;
import android.webkit.WebSettings;
import android.webkit.WebViewClient;
import android.webkit.WebView;
import android.webkit.WebResourceRequest;
import androidx.appcompat.app.AppCompatActivity;

public class ExoAirPlayerSenderActivity extends AppCompatActivity {
private static final String AIRPLAY_SENDER = "http://webcast-reloaded.surge.sh/airplay_sender.html";

private String page_domain;
private String page_path;
private String page_url;
private WebView webView;

// ---------------------------------------------------------------------------------------------
// Lifecycle Events:
// ---------------------------------------------------------------------------------------------

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

getPageUrl();
if (page_url == null)
finish();

setContentView(R.layout.exoairplayer_sender_activity);
webView = (WebView)findViewById(R.id.webView);
initWebView();
}

@Override
public void onStart() {
super.onStart();

restoreCookies();
webView.loadUrl(page_url);
}

@Override
public void onStop() {
super.onStop();

saveCookies();
}

// ---------------------------------------------------------------------------------------------
// Intent:
// ---------------------------------------------------------------------------------------------

private void getPageUrl() {
Uri page = Uri.parse(AIRPLAY_SENDER);
Intent intent = getIntent();
String video = intent.getStringExtra("video");
String referer = intent.getStringExtra("referer");

page_domain = page.getHost();
page_path = page.getPath();
page_url = (video == null)
? null
: (
AIRPLAY_SENDER +
"#/watch/" +
BrowserUtils.base64_encode(video) +
((referer == null)
? ""
: (
"/referer/" +
BrowserUtils.base64_encode(referer)
)
)
)
;
}

// ---------------------------------------------------------------------------------------------
// WebView:
// ---------------------------------------------------------------------------------------------

private void initWebView() {
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return true;
}

@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
return true;
}
});

WebSettings webSettings = webView.getSettings();
webSettings.setLoadWithOverviewMode(true);
webSettings.setSupportZoom(true);
webSettings.setBuiltInZoomControls(true);
webSettings.setDisplayZoomControls(true);
webSettings.setUseWideViewPort(false);
webSettings.setJavaScriptEnabled(true);
webSettings.setDomStorageEnabled(false);
webSettings.setUserAgentString(
getResources().getString(R.string.user_agent)
);
if (Build.VERSION.SDK_INT >= 21) {
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}

webView.setInitialScale(0);
webView.setHorizontalScrollBarEnabled(false);
webView.setVerticalScrollBarEnabled(false);
}

private void restoreCookies() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
String pref_key = getString(R.string.pref_persistentcookies_key);
String cookies = prefs.getString(pref_key, null);

if (cookies != null) {
String[] cookiesArr = cookies.split("\\s*;\\s+");
CookieManager CM = CookieManager.getInstance();
String cookie;

for (int i=0; i < cookiesArr.length; i++) {
cookie = cookiesArr[i] + "; Domain=" + page_domain + "; Path=" + page_path;
CM.setCookie(AIRPLAY_SENDER, cookie);
}
}
}

private void saveCookies() {
String cookies = CookieManager.getInstance().getCookie(AIRPLAY_SENDER);

if (cookies != null) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor editor = prefs.edit();
String pref_key = getString(R.string.pref_persistentcookies_key);

editor.putString(pref_key, cookies);
editor.commit();
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.github.warren_bank.webcast.webview;

import android.os.Bundle;
import android.preference.PreferenceActivity;

public class SettingsActivity extends PreferenceActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getFragmentManager().beginTransaction().replace(android.R.id.content, new SettingsFragment()).commit();
}
}
Loading

0 comments on commit 980d87a

Please sign in to comment.