TunneledWebView is a sample app that demonstrates embedding the Psiphon Library in an Android app. TunneledWebView proxies a WebView through the Psiphon tunnel.
Uses the Psiphon Android Library.
Integration is illustrated in the main activity source file in the sample app. Here are the key parts.
/*
* Copyright (c) 2016, Psiphon Inc.
* All rights reserved.
*/
package ca.psiphon.tunneledwebview;
// ...
import ca.psiphon.PsiphonTunnel;
//----------------------------------------------------------------------------------------------
// TunneledWebView
//
// This sample app demonstrates tunneling a WebView through the
// Psiphon Library. This app's main activity shows a log of
// events and a WebView that is loaded once Psiphon is connected.
//
// The flow is as follows:
//
// - The Psiphon tunnel is started in onResume(). PsiphonTunnel.startTunneling()
// is an asynchronous call that returns immediately.
//
// - Once Psiphon has selected a local HTTP proxy listening port, the
// onListeningHttpProxyPort() callback is called. This app records the
// port to use for tunneling traffic.
//
// - Once Psiphon has established a tunnel, the onConnected() callback
// is called. This app now loads the WebView, after setting its proxy
// to point to Psiphon's local HTTP proxy.
//
// To adapt this sample into your own app:
//
// - Embed a Psiphon config file in app/src/main/res/raw/psiphon_config.
//
// - Add the Psiphon Library AAR module as a dependency (see this app's
// project settings; to build this sample project, you need to drop
// ca.psiphon.aar into app/libs).
//----------------------------------------------------------------------------------------------
public class MainActivity extends ActionBarActivity
implements PsiphonTunnel.HostService {
// ...
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
mPsiphonTunnel = PsiphonTunnel.newPsiphonTunnel(this);
}
@Override
protected void onResume() {
super.onResume();
// NOTE: for demonstration purposes, this sample app
// restarts Psiphon in onPause/onResume. Since it may take some
// time to connect, it's generally recommended to keep
// Psiphon running, so start/stop in onCreate/onDestroy or
// even consider running a background Service.
try {
mPsiphonTunnel.startTunneling("");
} catch (PsiphonTunnel.Exception e) {
logMessage("failed to start Psiphon");
}
}
@Override
protected void onPause() {
super.onPause();
// NOTE: stop() can block for a few seconds, so it's generally
// recommended to run PsiphonTunnel.start()/stop() in a background
// thread and signal the thread appropriately.
mPsiphonTunnel.stop();
}
private void setHttpProxyPort(int port) {
// NOTE: here we record the Psiphon proxy port for subsequent
// use in tunneling app traffic. In this sample app, we will
// use WebViewProxySettings.setLocalProxy to tunnel a WebView
// through Psiphon. By default, the local proxy port is selected
// dynamically, so it's important to record and use the correct
// port number.
mLocalHttpProxyPort.set(port);
}
private void loadWebView() {
// NOTE: functions called via PsiphonTunnel.HostService may be
// called on background threads. It's important to ensure that
// these threads are not blocked and that UI functions are not
// called directly from these threads. Here we use runOnUiThread
// to handle this.
runOnUiThread(new Runnable() {
public void run() {
WebViewProxySettings.setLocalProxy(
MainActivity.this, mLocalHttpProxyPort.get());
mWebView.loadUrl("https://ipinfo.io/");
}
});
}
// ...
//----------------------------------------------------------------------------------------------
// PsiphonTunnel.HostService implementation
//
// NOTE: these are callbacks from the Psiphon Library
//----------------------------------------------------------------------------------------------
// ...
@Override
public void onListeningSocksProxyPort(int port) {
logMessage("local SOCKS proxy listening on port: " + Integer.toString(port));
}
// ...
@Override
public void onConnected() {
logMessage("connected");
loadWebView();
}
// ...
}
See the LICENSE file.