tor-android/src/org/torproject/android/OrbotMainActivity.java

1542 lines
46 KiB
Java
Raw Normal View History

2011-06-02 22:21:50 +02:00
/* Copyright (c) 2009, Nathan Freitas, Orbot / The Guardian Project - https://guardianproject.info */
/* See LICENSE for licensing information */
package org.torproject.android;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.NumberFormat;
2012-02-24 06:13:27 +01:00
import java.util.Locale;
import org.torproject.android.service.TorService;
import org.torproject.android.service.TorServiceConstants;
import org.torproject.android.service.TorServiceUtils;
2011-10-28 06:31:08 +02:00
import org.torproject.android.settings.SettingsPreferences;
2015-02-02 17:04:32 +01:00
import org.torproject.android.ui.ImageProgressView;
import org.torproject.android.ui.Rotate3dAnimation;
import org.torproject.android.ui.wizard.PromoAppsActivity;
import android.annotation.TargetApi;
2015-02-02 17:04:32 +01:00
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
2013-07-22 21:18:02 +02:00
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
2014-04-17 19:39:40 +02:00
import android.content.pm.PackageInfo;
2013-08-19 17:17:20 +02:00
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
2012-02-24 06:13:27 +01:00
import android.content.res.Configuration;
import android.net.Uri;
import android.net.VpnService;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
import android.support.v4.content.LocalBroadcastManager;
2015-02-02 17:04:32 +01:00
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
2015-02-02 17:04:32 +01:00
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.View.OnTouchListener;
import android.view.animation.AccelerateInterpolator;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
2015-02-04 20:55:42 +01:00
import android.widget.ToggleButton;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
public class OrbotMainActivity extends Activity implements OrbotConstants, OnLongClickListener, OnTouchListener, OnSharedPreferenceChangeListener
{
2015-01-20 15:47:24 +01:00
/* Useful UI bits */
private TextView lblStatus = null; //the main text display widget
private ImageProgressView imgStatus = null; //the main touchable image for activating Orbot
2015-01-20 15:47:24 +01:00
private MenuItem mItemOnOff = null;
2011-07-03 10:39:49 +02:00
private TextView downloadText = null;
private TextView uploadText = null;
private TextView mTxtOrbotLog = null;
2015-02-02 05:36:19 +01:00
private Button mBtnBrowser = null;
2015-02-04 20:55:42 +01:00
private ToggleButton mBtnVPN = null;
2015-02-14 08:30:02 +01:00
private ToggleButton mBtnBridges = null;
2015-02-04 20:55:42 +01:00
private DrawerLayout mDrawer;
private ActionBarDrawerToggle mDrawerToggle;
private Toolbar mToolbar;
2015-02-14 06:43:36 +01:00
2015-01-20 15:47:24 +01:00
/* Some tracking bits */
private int torStatus = TorServiceConstants.STATUS_OFF; //latest status reported from the tor service
private SharedPreferences mPrefs = null;
2014-06-13 18:59:02 +02:00
2015-01-20 15:47:24 +01:00
private boolean autoStartFromIntent = false;
private final static long INIT_DELAY = 100;
2015-02-14 06:43:36 +01:00
private final static int REQUEST_VPN = 8888;
2011-07-03 10:39:49 +02:00
/** Called when the activity is first created. */
2015-01-20 15:47:24 +01:00
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
2014-06-06 15:59:03 +02:00
2014-06-13 18:59:02 +02:00
mPrefs = TorServiceUtils.getSharedPrefs(getApplicationContext());
mPrefs.registerOnSharedPreferenceChangeListener(this);
2015-01-20 15:47:24 +01:00
2011-07-03 10:39:49 +02:00
setLocale();
2013-04-03 21:47:55 +02:00
doLayout();
// Register to receive messages.
// We are registering an observer (mMessageReceiver) to receive Intents
// with actions named "custom-event-name".
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
new IntentFilter("status"));
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
new IntentFilter("log"));
mHandler.postDelayed(new Runnable ()
{
public void run ()
{
startService(TorServiceConstants.CMD_INIT);
}
},INIT_DELAY);
}
private void startService (String action)
{
Intent torService = new Intent(this, TorService.class);
torService.setAction(action);
2014-06-12 02:24:08 +02:00
startService(torService);
}
private void stopService ()
{
Intent torService = new Intent(this, TorService.class);
stopService(torService);
}
2015-01-20 15:47:24 +01:00
// Our handler for received Intents. This will be called whenever an Intent
// with an action named "custom-event-name" is broadcasted.
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// Get extra data included in the Intent
if (intent.hasExtra("log"))
{
String log = intent.getStringExtra("log");
updateStatus(log);
}
else if (intent.hasExtra("up"))
{
long upload = intent.getLongExtra("up",0);
long download = intent.getLongExtra("down",0);
long written = intent.getLongExtra("written",0);
long read = intent.getLongExtra("read",0);
Message msg = mHandler.obtainMessage(TorServiceConstants.MESSAGE_TRAFFIC_COUNT);
msg.getData().putLong("download", download);
msg.getData().putLong("upload", upload);
msg.getData().putLong("readTotal", read);
msg.getData().putLong("writeTotal", written);
mHandler.sendMessage(msg);
}
else if (intent.hasExtra("status"))
{
torStatus = intent.getIntExtra("status", TorServiceConstants.STATUS_OFF);
updateStatus("");
}
}
};
2015-01-20 15:47:24 +01:00
private void doLayout ()
{
setContentView(R.layout.layout_main);
mToolbar = (Toolbar) findViewById(R.id.toolbar);
mToolbar.inflateMenu(R.menu.orbot_main);
mToolbar.setTitle(R.string.app_name);
mDrawer = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerToggle = new ActionBarDrawerToggle(
this, mDrawer, mToolbar,
android.R.string.ok, android.R.string.cancel
);
mDrawer.setDrawerListener(mDrawerToggle);
mDrawerToggle.setDrawerIndicatorEnabled(true);
mDrawerToggle.syncState();
mDrawerToggle.setToolbarNavigationClickListener(new OnClickListener ()
{
@Override
public void onClick(View v) {
}
});
setupMenu();
mTxtOrbotLog = (TextView)findViewById(R.id.orbotLog);
2015-01-20 15:47:24 +01:00
lblStatus = (TextView)findViewById(R.id.lblStatus);
lblStatus.setOnLongClickListener(this);
imgStatus = (ImageProgressView)findViewById(R.id.imgStatus);
imgStatus.setOnLongClickListener(this);
imgStatus.setOnTouchListener(this);
downloadText = (TextView)findViewById(R.id.trafficDown);
2011-07-03 10:39:49 +02:00
uploadText = (TextView)findViewById(R.id.trafficUp);
2015-01-20 15:47:24 +01:00
2015-01-20 15:47:24 +01:00
downloadText.setText(formatCount(0) + " / " + formatTotal(0));
uploadText.setText(formatCount(0) + " / " + formatTotal(0));
// Gesture detection
mGestureDetector = new GestureDetector(this, new MyGestureDetector());
2015-02-02 05:36:19 +01:00
mBtnBrowser = (Button)findViewById(R.id.btnBrowser);
mBtnBrowser.setOnClickListener(new View.OnClickListener ()
{
@Override
public void onClick(View v) {
doTorCheck();
}
});
2015-02-15 07:22:38 +01:00
mBtnBrowser.setEnabled(false);
2015-02-04 20:55:42 +01:00
mBtnVPN = (ToggleButton)findViewById(R.id.btnVPN);
2015-02-14 06:43:36 +01:00
boolean useVPN = mPrefs.getBoolean("pref_vpn", false);
mBtnVPN.setChecked(useVPN);
if (useVPN)
{
startVpnService ();
}
2015-02-02 05:36:19 +01:00
mBtnVPN.setOnClickListener(new View.OnClickListener ()
{
@Override
public void onClick(View v) {
2015-02-04 20:55:42 +01:00
if (mBtnVPN.isChecked())
promptStartVpnService();
2015-02-04 20:55:42 +01:00
else
stopVpnService();
}
});
2015-02-14 08:30:02 +01:00
mBtnBridges = (ToggleButton)findViewById(R.id.btnBridges);
boolean useBridges = mPrefs.getBoolean("pref_bridges_enabled", false);
mBtnBridges.setChecked(useBridges);
2015-02-14 08:30:02 +01:00
mBtnBridges.setOnClickListener(new View.OnClickListener ()
{
@Override
public void onClick(View v) {
if (Build.CPU_ABI.contains("arm"))
{
promptSetupBridges (); //if ARM processor, show all bridge options
2015-02-14 08:30:02 +01:00
}
else
{
Toast.makeText(OrbotMainActivity.this, R.string.note_only_standard_tor_bridges_work_on_intel_x86_atom_devices, Toast.LENGTH_LONG).show();
showGetBridgePrompt(""); //if other chip ar, only stock bridges are supported
}
2015-02-14 08:30:02 +01:00
}
});
2015-02-14 06:43:36 +01:00
}
2015-01-20 15:47:24 +01:00
GestureDetector mGestureDetector;
2015-01-20 15:47:24 +01:00
@Override
public boolean onTouch(View v, MotionEvent event) {
return mGestureDetector.onTouchEvent(event);
}
/*
* Create the UI Options Menu (non-Javadoc)
* @see android.app.Activity#onCreateOptionsMenu(android.view.Menu)
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.orbot_main, menu);
2012-02-27 04:48:30 +01:00
mItemOnOff = menu.getItem(0);
return true;
}
/**
private void appConflictChecker ()
{
SharedPreferences sprefs = TorServiceUtils.getSharedPrefs(getApplicationContext());
2014-08-22 17:09:11 +02:00
boolean showAppConflict = true;//sprefs.getBoolean("pref_show_conflict",true);
2014-08-22 17:09:11 +02:00
String[] badApps = {"com.sec.msc.nts.android.proxy:com.sec.msc.nts.android.proxy","com.sec.pcw:Samsung Link"};
for (String badApp : badApps)
{
2014-08-22 17:09:11 +02:00
String[] badAppParts = badApp.split(":");
if (appInstalledOrNot(badAppParts[0]))
{
String msg = getString(R.string.please_disable_this_app_in_android_settings_apps_if_you_are_having_problems_with_orbot_) + badAppParts[1];
if (showAppConflict)
showAlert(getString(R.string.app_conflict),msg,true);
2014-04-17 19:39:40 +02:00
// appendLogTextAndScroll(msg);
}
}
sprefs.edit().putBoolean("pref_show_conflict", false).commit();
}*/
private void showAbout ()
2011-06-02 22:21:50 +02:00
{
2015-01-20 15:47:24 +01:00
LayoutInflater li = LayoutInflater.from(this);
View view = li.inflate(R.layout.layout_about, null);
String version = "";
try {
version = getPackageManager().getPackageInfo(getPackageName(), 0).versionName + " (Tor " + TorServiceConstants.BINARY_TOR_VERSION + ")";
} catch (NameNotFoundException e) {
version = "Version Not Found";
}
TextView versionName = (TextView)view.findViewById(R.id.versionName);
versionName.setText(version);
new AlertDialog.Builder(this)
.setTitle(getString(R.string.button_about))
.setView(view)
.show();
2011-06-02 22:21:50 +02:00
}
2014-04-04 05:23:20 +02:00
private void setupMenu ()
{
mToolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener ()
{
@Override
public boolean onMenuItemClick(MenuItem item) {
2011-06-02 22:21:50 +02:00
if (item.getItemId() == R.id.menu_settings)
2011-06-02 22:21:50 +02:00
{
showSettings();
}
2012-02-27 04:48:30 +01:00
else if (item.getItemId() == R.id.menu_wizard)
2011-06-02 22:21:50 +02:00
{
startActivity(new Intent(OrbotMainActivity.this, PromoAppsActivity.class));
2011-06-02 22:21:50 +02:00
}
2012-02-27 04:48:30 +01:00
else if (item.getItemId() == R.id.menu_exit)
2011-06-02 22:21:50 +02:00
{
//exit app
doExit();
}
2012-02-27 04:48:30 +01:00
else if (item.getItemId() == R.id.menu_about)
2011-06-02 22:21:50 +02:00
{
showAbout();
}
else if (item.getItemId() == R.id.menu_scan)
{
IntentIntegrator integrator = new IntentIntegrator(OrbotMainActivity.this);
integrator.initiateScan();
}
else if (item.getItemId() == R.id.menu_share_bridge)
{
String bridges = mPrefs.getString(OrbotConstants.PREF_BRIDGES_LIST, null);
if (bridges != null && bridges.length() > 0)
{
try {
bridges = "bridge://" + URLEncoder.encode(bridges,"UTF-8");
IntentIntegrator integrator = new IntentIntegrator(OrbotMainActivity.this);
integrator.shareText(bridges);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
2015-02-02 05:36:19 +01:00
}
2015-02-02 05:36:19 +01:00
return true;
}
});
2011-06-02 22:21:50 +02:00
}
2011-06-02 22:21:50 +02:00
/**
* This is our attempt to REALLY exit Orbot, and stop the background service
* However, Android doesn't like people "quitting" apps, and/or our code may not
* be quite right b/c no matter what we do, it seems like the TorService still exists
**/
private void doExit ()
{
try {
2015-01-20 15:47:24 +01:00
2011-06-02 22:21:50 +02:00
//one of the confusing things about all of this code is the multiple
//places where things like "stopTor" are called, both in the Activity and the Service
//not something to tackle in your first iteration, but i thin we can talk about fixing
//terminology but also making sure there are clear distinctions in control
stopTor();
stopService ();
2011-06-02 22:21:50 +02:00
} catch (RemoteException e) {
Log.w(TAG, e);
}
//Kill all the wizard activities
setResult(RESULT_CLOSE_ALL);
2011-06-02 22:21:50 +02:00
finish();
}
protected void onPause() {
try
{
super.onPause();
if (aDialog != null)
aDialog.dismiss();
}
catch (IllegalStateException ise)
{
//can happen on exit/shutdown
}
}
private void doTorCheck ()
{
openBrowser(URL_TOR_CHECK,false);
2011-06-02 22:21:50 +02:00
}
private void enableHiddenServicePort (int hsPort) throws RemoteException, InterruptedException
{
2013-08-19 17:17:20 +02:00
Editor pEdit = mPrefs.edit();
2013-08-19 17:17:20 +02:00
String hsPortString = mPrefs.getString("pref_hs_ports", "");
String onionHostname = mPrefs.getString("pref_hs_hostname","");
if (hsPortString.indexOf(hsPort+"")==-1)
{
if (hsPortString.length() > 0 && hsPortString.indexOf(hsPort+"")==-1)
hsPortString += ',' + hsPort;
else
hsPortString = hsPort + "";
pEdit.putString("pref_hs_ports", hsPortString);
pEdit.putBoolean("pref_hs_enable", true);
pEdit.commit();
}
2011-06-02 22:21:50 +02:00
if (onionHostname == null || onionHostname.length() == 0)
{
stopTor();
startTor();
new Thread () {
public void run ()
{
String onionHostname = mPrefs.getString("pref_hs_hostname","");
while (onionHostname.length() == 0)
{
//we need to stop and start Tor
try {
Thread.sleep(3000); //wait three seconds
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
onionHostname = mPrefs.getString("pref_hs_hostname","");
}
Intent nResult = new Intent();
nResult.putExtra("hs_host", onionHostname);
setResult(RESULT_OK, nResult);
finish();
}
}.start();
}
else
{
Intent nResult = new Intent();
nResult.putExtra("hs_host", onionHostname);
setResult(RESULT_OK, nResult);
finish();
}
}
2013-04-03 21:47:55 +02:00
2014-06-19 22:31:17 +02:00
private synchronized void handleIntents ()
{
if (getIntent() == null)
return;
// Get intent, action and MIME type
Intent intent = getIntent();
String action = intent.getAction();
String type = intent.getType();
if (action == null)
return;
if (action.equals("org.torproject.android.REQUEST_HS_PORT"))
{
final int hiddenServicePortRequest = getIntent().getIntExtra("hs_port", -1);
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
2012-01-26 02:09:33 +01:00
public void onClick(DialogInterface dialog, int which) {
switch (which){
case DialogInterface.BUTTON_POSITIVE:
try {
enableHiddenServicePort (hiddenServicePortRequest);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
2011-06-02 22:21:50 +02:00
case DialogInterface.BUTTON_NEGATIVE:
//No button clicked
finish();
break;
}
}
};
2011-06-02 22:21:50 +02:00
String requestMsg = getString(R.string.hidden_service_request, hiddenServicePortRequest);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(requestMsg).setPositiveButton("Allow", dialogClickListener)
.setNegativeButton("Deny", dialogClickListener).show();
return; //don't null the setIntent() as we need it later
}
else if (action.equals("org.torproject.android.START_TOR"))
{
autoStartFromIntent = true;
try {
Log.i(TAG, "action equals org.torproject.android.START_TOR");
startTor();
Intent resultIntent = new Intent(intent);
resultIntent.putExtra("socks_proxy", "socks://127.0.0.1:" + TorServiceConstants.PORT_SOCKS_DEFAULT);
resultIntent.putExtra("socks_proxy_host", "127.0.0.1");
resultIntent.putExtra("socks_proxy_port", TorServiceConstants.PORT_SOCKS_DEFAULT);
resultIntent.putExtra("http_proxy", "http://127.0.0.1" + TorServiceConstants.PORT_HTTP);
resultIntent.putExtra("http_proxy_host", "127.0.0.1");
resultIntent.putExtra("http_proxy_port", TorServiceConstants.PORT_HTTP);
setResult(RESULT_OK, resultIntent);
finish();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else if (action.equals(Intent.ACTION_VIEW))
{
String urlString = intent.getDataString();
if (urlString != null)
{
if (urlString.toLowerCase().startsWith("bridge://"))
{
String newBridgeValue = urlString.substring(9); //remove the bridge protocol piece
newBridgeValue = URLDecoder.decode(newBridgeValue); //decode the value here
setNewBridges(newBridgeValue);
}
}
}
else
{
2012-01-26 02:09:33 +01:00
showWizard = mPrefs.getBoolean("show_wizard",showWizard);
//let's not show the wizard now for new users
if (showWizard)
{
2013-08-19 17:17:20 +02:00
Editor pEdit = mPrefs.edit();
pEdit.putBoolean("show_wizard",false);
2014-06-13 18:59:02 +02:00
pEdit.commit();
showWizard = false;
showAlert(getString(R.string.app_name),getString(R.string.wizard_final_msg),true);
}
}
updateStatus("");
setIntent(null);
}
private void setNewBridges (String newBridgeValue)
{
showAlert(getString(R.string.bridges_updated),getString(R.string.restart_orbot_to_use_this_bridge_) + newBridgeValue,false);
Editor pEdit = mPrefs.edit();
pEdit.putString(OrbotConstants.PREF_BRIDGES_LIST,newBridgeValue); //set the string to a preference
pEdit.putBoolean(OrbotConstants.PREF_BRIDGES_ENABLED,true);
pEdit.commit();
setResult(RESULT_OK);
mBtnBridges.setChecked(true);
enableBridges(true);
}
2011-06-02 22:21:50 +02:00
private boolean showWizard = true;
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// doLayout();
//updateStatus("");
}
2011-06-02 22:21:50 +02:00
/*
* Launch the system activity for Uri viewing with the provided url
*/
private void openBrowser(final String browserLaunchUrl,boolean forceExternal)
{
boolean isOrwebInstalled = appInstalledOrNot("info.guardianproject.browser");
boolean isTransProxy = mPrefs.getBoolean("pref_transparent", false);
if (mBtnVPN.isChecked()||forceExternal)
2013-08-19 17:17:20 +02:00
{
//use the system browser since VPN is on
2013-08-19 17:17:20 +02:00
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(browserLaunchUrl));
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP|Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
else if (isTransProxy)
{
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(browserLaunchUrl));
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP|Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
else if (isOrwebInstalled)
{
startIntent("info.guardianproject.browser",Intent.ACTION_VIEW,Uri.parse(browserLaunchUrl));
}
2013-08-19 17:17:20 +02:00
else
{
AlertDialog aDialog = new AlertDialog.Builder(OrbotMainActivity.this)
.setIcon(R.drawable.onion32)
.setTitle(R.string.install_apps_)
.setMessage(R.string.it_doesn_t_seem_like_you_have_orweb_installed_want_help_with_that_or_should_we_just_open_the_browser_)
.setPositiveButton(R.string.install_orweb, new Dialog.OnClickListener ()
{
@Override
public void onClick(DialogInterface dialog, int which) {
//prompt to install Orweb
Intent intent = new Intent(OrbotMainActivity.this,PromoAppsActivity.class);
startActivity(intent);
}
})
.setNegativeButton(R.string.standard_browser, new Dialog.OnClickListener ()
{
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(browserLaunchUrl));
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP|Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
})
.show();
}
}
2015-01-20 15:47:24 +01:00
private void startIntent (String pkg, String action, Uri data)
{
Intent i;
PackageManager manager = getPackageManager();
try {
i = manager.getLaunchIntentForPackage(pkg);
if (i == null)
throw new PackageManager.NameNotFoundException();
i.setAction(action);
i.setData(data);
startActivity(i);
} catch (PackageManager.NameNotFoundException e) {
}
}
private boolean appInstalledOrNot(String uri)
2013-08-19 17:17:20 +02:00
{
PackageManager pm = getPackageManager();
try
{
2014-08-22 17:09:11 +02:00
PackageInfo pi = pm.getPackageInfo(uri, PackageManager.GET_ACTIVITIES);
2014-05-05 06:00:01 +02:00
return pi.applicationInfo.enabled;
2013-08-19 17:17:20 +02:00
}
catch (PackageManager.NameNotFoundException e)
{
2014-05-05 06:00:01 +02:00
return false;
2013-08-19 17:17:20 +02:00
}
}
2015-01-20 15:47:24 +01:00
/*
* Load the basic settings application to display torrc
*/
private void showSettings ()
{
startActivityForResult(new Intent(this, SettingsPreferences.class), 1);
}
@Override
2015-01-20 15:47:24 +01:00
protected void onActivityResult(int request, int response, Intent data) {
super.onActivityResult(request, response, data);
if (request == 1 && response == RESULT_OK)
{
if (data != null && data.getBooleanExtra("transproxywipe", false))
{
boolean result = flushTransProxy();
if (result)
{
2015-01-20 15:47:24 +01:00
Toast.makeText(this, R.string.transparent_proxy_rules_flushed_, Toast.LENGTH_SHORT).show();
}
else
{
2015-01-20 15:47:24 +01:00
Toast.makeText(this, R.string.you_do_not_have_root_access_enabled, Toast.LENGTH_SHORT).show();
}
}
else if (torStatus == TorServiceConstants.STATUS_ON)
{
updateSettings();
Toast.makeText(this, R.string.you_may_need_to_stop_and_start_orbot_for_settings_change_to_be_enabled_, Toast.LENGTH_SHORT).show();
2015-01-20 15:47:24 +01:00
}
}
else if (request == REQUEST_VPN && response == RESULT_OK)
{
startService(TorServiceConstants.CMD_VPN);
// if (torStatus == TorServiceConstants.STATUS_ON)
// restartTor ();
2015-01-20 15:47:24 +01:00
}
IntentResult scanResult = IntentIntegrator.parseActivityResult(request, response, data);
if (scanResult != null) {
// handle scan result
String results = scanResult.getContents();
if (results != null && results.length() > 0)
{
try {
results = URLDecoder.decode(results, "UTF-8");
int urlIdx = results.indexOf("://");
if (urlIdx!=-1)
results = results.substring(urlIdx+3);
setNewBridges(results);
} catch (UnsupportedEncodingException e) {
Log.e(TAG,"unsupported",e);
}
}
}
2015-01-20 15:47:24 +01:00
}
public void promptSetupBridges ()
{
LayoutInflater li = LayoutInflater.from(this);
View view = li.inflate(R.layout.layout_diag, null);
TextView versionName = (TextView)view.findViewById(R.id.diaglog);
versionName.setText(R.string.if_your_mobile_network_actively_blocks_tor_you_can_use_a_tor_bridge_to_access_the_network_another_way_to_get_bridges_is_to_send_an_email_to_bridges_torproject_org_please_note_that_you_must_send_the_email_using_an_address_from_one_of_the_following_email_providers_riseup_gmail_or_yahoo_);
if (mBtnBridges.isChecked())
{
new AlertDialog.Builder(this)
.setTitle(R.string.bridge_mode)
.setView(view)
.setItems(R.array.bridge_options, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// The 'which' argument contains the index position
// of the selected item
switch (which)
{
case 0: //obfs 4;
showGetBridgePrompt("obfs4");
break;
case 1: //obfs3
showGetBridgePrompt("obfs3");
break;
case 2: //scramblesuit
showGetBridgePrompt("scramblesuit");
break;
case 3: //azure
mPrefs.edit().putString(OrbotConstants.PREF_BRIDGES_LIST,"2").commit();
enableBridges(true);
break;
case 4: //amazon
mPrefs.edit().putString(OrbotConstants.PREF_BRIDGES_LIST,"1").commit();
enableBridges(true);
break;
case 5: //google
mPrefs.edit().putString(OrbotConstants.PREF_BRIDGES_LIST,"0").commit();
enableBridges(true);
break;
}
}
}).setNegativeButton(android.R.string.cancel, new Dialog.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which) {
//mBtnBridges.setChecked(false);
}
})
.show();
}
else
{
enableBridges(false);
}
}
private void showGetBridgePrompt (final String type)
{
LayoutInflater li = LayoutInflater.from(this);
View view = li.inflate(R.layout.layout_diag, null);
TextView versionName = (TextView)view.findViewById(R.id.diaglog);
versionName.setText(R.string.you_must_get_a_bridge_address_by_email_web_or_from_a_friend_once_you_have_this_address_please_paste_it_into_the_bridges_preference_in_orbot_s_setting_and_restart_);
new AlertDialog.Builder(this)
.setTitle(R.string.bridge_mode)
.setView(view)
.setNegativeButton(android.R.string.cancel, new Dialog.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which) {
//do nothing
}
})
.setNeutralButton(R.string.get_bridges_email, new Dialog.OnClickListener ()
{
@Override
public void onClick(DialogInterface dialog, int which) {
sendGetBridgeEmail(type);
}
})
.setPositiveButton(R.string.get_bridges_web, new Dialog.OnClickListener ()
{
@Override
public void onClick(DialogInterface dialog, int which) {
openBrowser(URL_TOR_BRIDGES + type,true);
}
}).show();
}
private void sendGetBridgeEmail (String type)
{
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("message/rfc822");
intent.putExtra(Intent.EXTRA_EMAIL , new String[]{"bridges@torproject.org"});
if (type != null)
{
intent.putExtra(Intent.EXTRA_SUBJECT, "get transport " + type);
intent.putExtra(Intent.EXTRA_TEXT, "get transport " + type);
}
else
{
intent.putExtra(Intent.EXTRA_SUBJECT, "get bridges");
intent.putExtra(Intent.EXTRA_TEXT, "get bridges");
}
startActivity(Intent.createChooser(intent, getString(R.string.send_email)));
}
private void enableBridges (boolean enable)
{
Editor edit = mPrefs.edit();
edit.putBoolean(OrbotConstants.PREF_BRIDGES_ENABLED, enable);
edit.commit();
updateSettings();
if (torStatus == TorServiceConstants.STATUS_ON)
{
String bridgeList = mPrefs.getString(OrbotConstants.PREF_BRIDGES_LIST,null);
if (bridgeList != null && bridgeList.length() > 0)
{
2015-04-08 23:01:09 +02:00
restartTor ();
}
}
}
2015-04-08 23:01:09 +02:00
private void restartTor ()
{
try
{
//do auto restart
stopTor ();
mHandler.postDelayed(new Runnable () {
public void run ()
{
try
{
startTor();
}
catch (Exception e)
{
Log.e(TAG,"can't start orbot",e);
}
}
}, 2000);
}
catch (Exception e)
{
Log.e(TAG,"can't stop orbot",e);
}
}
public void promptStartVpnService ()
{
LayoutInflater li = LayoutInflater.from(this);
View view = li.inflate(R.layout.layout_diag, null);
TextView versionName = (TextView)view.findViewById(R.id.diaglog);
versionName.setText(R.string.you_can_enable_all_apps_on_your_device_to_run_through_the_tor_network_using_the_vpn_feature_of_android_);
new AlertDialog.Builder(this)
.setTitle(R.string.apps_mode)
.setView(view)
.setPositiveButton(R.string.activate, new Dialog.OnClickListener ()
{
@Override
public void onClick(DialogInterface dialog, int which) {
mPrefs.edit().putBoolean("pref_vpn", true).commit();
startVpnService();
}
})
.setNegativeButton(android.R.string.cancel, new Dialog.OnClickListener ()
{
@Override
public void onClick(DialogInterface dialog, int which) {
mBtnVPN.setChecked(false);
}
})
.show();
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
2015-01-20 15:47:24 +01:00
public void startVpnService ()
{
Intent intent = VpnService.prepare(this);
if (intent != null) {
startActivityForResult(intent,REQUEST_VPN);
}
else
{
startService(TorServiceConstants.CMD_VPN);
// if (torStatus == TorServiceConstants.STATUS_ON)
// restartTor ();
}
}
2015-02-04 20:55:42 +01:00
public void stopVpnService ()
{
startService(TorServiceConstants.CMD_VPN_CLEAR);
// restartTor ();
2015-02-04 20:55:42 +01:00
}
private boolean flushTransProxy ()
{
2015-01-20 15:47:24 +01:00
startService(TorServiceConstants.CMD_FLUSH);
return true;
}
private boolean updateSettings ()
{
2015-01-20 15:47:24 +01:00
//todo send service command
startService(TorServiceConstants.CMD_UPDATE);
return true;
}
2015-01-20 15:47:24 +01:00
@Override
protected void onResume() {
super.onResume();
2014-06-06 20:19:06 +02:00
if (mPrefs != null)
{
boolean useVPN = mPrefs.getBoolean("pref_vpn", false);
mBtnVPN.setChecked(useVPN);
boolean useBridges = mPrefs.getBoolean("pref_bridges_enabled", false);
mBtnBridges.setChecked(useBridges);
}
2015-01-20 15:47:24 +01:00
mHandler.postDelayed(new Runnable ()
{
public void run ()
{
2015-01-20 15:47:24 +01:00
setLocale();
handleIntents();
2015-01-20 15:47:24 +01:00
}
}
, 500);
}
2014-06-06 20:19:06 +02:00
2015-01-20 15:47:24 +01:00
AlertDialog aDialog = null;
//general alert dialog for mostly Tor warning messages
//sometimes this can go haywire or crazy with too many error
//messages from Tor, and the user cannot stop or exit Orbot
//so need to ensure repeated error messages are not spamming this method
private void showAlert(String title, String msg, boolean button)
{
try
{
if (aDialog != null && aDialog.isShowing())
aDialog.dismiss();
}
catch (Exception e){} //swallow any errors
if (button)
{
2015-02-02 17:04:32 +01:00
aDialog = new AlertDialog.Builder(OrbotMainActivity.this)
.setIcon(R.drawable.onion32)
.setTitle(title)
.setMessage(msg)
.setPositiveButton(android.R.string.ok, null)
.show();
}
else
{
2015-02-02 17:04:32 +01:00
aDialog = new AlertDialog.Builder(OrbotMainActivity.this)
.setIcon(R.drawable.onion32)
.setTitle(title)
.setMessage(msg)
.show();
}
aDialog.setCanceledOnTouchOutside(true);
}
2015-01-20 15:47:24 +01:00
private void updateStatus (String torServiceMsg)
{
//now update the layout_main UI based on the status
if (imgStatus != null)
2011-06-02 22:21:50 +02:00
{
if (torStatus == TorServiceConstants.STATUS_ON)
2011-06-02 22:21:50 +02:00
{
2015-01-20 15:47:24 +01:00
imgStatus.setImageResource(R.drawable.toron);
2015-02-15 07:22:38 +01:00
mBtnBrowser.setEnabled(true);
if (mItemOnOff != null)
mItemOnOff.setTitle(R.string.menu_stop);
2011-06-02 22:21:50 +02:00
if (lblStatus != null && torServiceMsg != null)
if (torServiceMsg.indexOf('%')!=-1)
lblStatus.setText(torServiceMsg);
else
lblStatus.setText("");
boolean showFirstTime = mPrefs.getBoolean("connect_first_time",true);
if (showFirstTime)
{
Editor pEdit = mPrefs.edit();
2011-06-02 22:21:50 +02:00
pEdit.putBoolean("connect_first_time",false);
2011-06-02 22:21:50 +02:00
pEdit.commit();
2011-06-02 22:21:50 +02:00
showAlert(getString(R.string.status_activated),getString(R.string.connect_first_time),true);
2011-06-02 22:21:50 +02:00
}
if (autoStartFromIntent)
2011-06-02 22:21:50 +02:00
{
autoStartFromIntent = false;
2015-01-20 15:47:24 +01:00
finish();
2011-06-02 22:21:50 +02:00
}
2011-06-02 22:21:50 +02:00
}
else if (torStatus == TorServiceConstants.STATUS_CONNECTING)
{
2015-01-20 15:47:24 +01:00
imgStatus.setImageResource(R.drawable.torstarting);
if (mItemOnOff != null)
mItemOnOff.setTitle(R.string.menu_stop);
if (lblStatus != null && torServiceMsg != null)
if (torServiceMsg.indexOf('%')!=-1)
lblStatus.setText(torServiceMsg);
2015-02-02 17:04:32 +01:00
}
else if (torStatus == TorServiceConstants.STATUS_OFF)
{
imgStatus.setImageResource(R.drawable.toroff);
lblStatus.setText(getString(R.string.press_to_start));
2015-02-15 07:22:38 +01:00
mBtnBrowser.setEnabled(false);
if (mItemOnOff != null)
mItemOnOff.setTitle(R.string.menu_start);
2015-02-02 05:36:19 +01:00
}
if (torServiceMsg != null && torServiceMsg.length() > 0)
{
mTxtOrbotLog.append(torServiceMsg + '\n');
}
}
}
2015-01-20 15:47:24 +01:00
2014-06-27 19:02:41 +02:00
2011-06-02 22:21:50 +02:00
// guess what? this start's Tor! actually no it just requests via the local ITorService to the remote TorService instance
// to start Tor
private void startTor () throws RemoteException
{
2011-06-02 22:21:50 +02:00
startService (TorServiceConstants.CMD_START);
torStatus = TorServiceConstants.STATUS_CONNECTING;
mTxtOrbotLog.setText("");
//here we update the UI which is a bit sloppy and mixed up code wise
//might be best to just call updateStatus() instead of directly manipulating UI in this method - yep makes sense
imgStatus.setImageResource(R.drawable.torstarting);
lblStatus.setText(getString(R.string.status_starting_up));
//we send a message here to the progressDialog i believe, but we can clarify that shortly
Message msg = mHandler.obtainMessage(TorServiceConstants.ENABLE_TOR_MSG);
msg.getData().putString(HANDLER_TOR_MSG, getString(R.string.status_starting_up));
mHandler.sendMessage(msg);
}
2011-06-02 22:21:50 +02:00
//now we stop Tor! amazing!
private void stopTor () throws RemoteException
{
startService (TorServiceConstants.CMD_STOP);
torStatus = TorServiceConstants.STATUS_OFF;
2015-02-02 17:04:32 +01:00
Message msg = mHandler.obtainMessage(TorServiceConstants.DISABLE_TOR_MSG);
mHandler.sendMessage(msg);
}
2011-06-02 22:21:50 +02:00
/*
* (non-Javadoc)
* @see android.view.View.OnClickListener#onClick(android.view.View)
*/
2011-06-02 22:21:50 +02:00
public boolean onLongClick(View view) {
2015-02-02 17:04:32 +01:00
try
{
if (torStatus == TorServiceConstants.STATUS_OFF)
{
startTor();
}
else
{
stopTor();
stopService ();
}
2011-06-02 22:21:50 +02:00
2015-02-02 17:04:32 +01:00
return true;
}
catch (Exception e)
{
Log.d(TAG,"error onclick",e);
}
2015-01-20 15:47:24 +01:00
return false;
2011-10-28 06:31:08 +02:00
2011-06-02 22:21:50 +02:00
}
2015-01-20 15:47:24 +01:00
2011-06-02 22:21:50 +02:00
// this is what takes messages or values from the callback threads or other non-mainUI threads
//and passes them back into the main UI thread for display to the user
private Handler mHandler = new Handler() {
2015-01-20 15:47:24 +01:00
private String lastServiceMsg = null;
public void handleMessage(Message msg) {
switch (msg.what) {
case TorServiceConstants.STATUS_MSG:
case TorServiceConstants.LOG_MSG:
2011-06-02 22:21:50 +02:00
String torServiceMsg = (String)msg.getData().getString(HANDLER_TOR_MSG);
2011-10-28 06:31:08 +02:00
if (lastServiceMsg == null || !lastServiceMsg.equals(torServiceMsg))
{
2015-01-20 15:47:24 +01:00
updateStatus(torServiceMsg);
2011-10-28 06:31:08 +02:00
2015-01-20 15:47:24 +01:00
lastServiceMsg = torServiceMsg;
2011-10-28 06:31:08 +02:00
}
2011-06-02 22:21:50 +02:00
break;
case TorServiceConstants.ENABLE_TOR_MSG:
2011-06-02 22:21:50 +02:00
updateStatus((String)msg.getData().getString(HANDLER_TOR_MSG));
break;
case TorServiceConstants.DISABLE_TOR_MSG:
2015-01-20 15:47:24 +01:00
updateStatus((String)msg.getData().getString(HANDLER_TOR_MSG));
break;
2011-07-03 10:39:49 +02:00
2015-01-20 15:47:24 +01:00
case TorServiceConstants.MESSAGE_TRAFFIC_COUNT :
Bundle data = msg.getData();
DataCount datacount = new DataCount(data.getLong("upload"),data.getLong("download"));
2011-07-06 18:01:36 +02:00
2015-01-20 15:47:24 +01:00
long totalRead = data.getLong("readTotal");
long totalWrite = data.getLong("writeTotal");
downloadText.setText(formatCount(datacount.Download) + " / " + formatTotal(totalRead));
uploadText.setText(formatCount(datacount.Upload) + " / " + formatTotal(totalWrite));
2015-01-20 15:47:24 +01:00
if (torStatus != TorServiceConstants.STATUS_ON)
{
updateStatus("");
}
default:
super.handleMessage(msg);
}
}
};
2011-06-02 22:21:50 +02:00
// this is the connection that gets called back when a successfull bind occurs
// we should use this to activity monitor unbind so that we don't have to call
// bindService() a million times
2012-02-24 06:13:27 +01:00
private void setLocale ()
{
Configuration config = getResources().getConfiguration();
2013-08-19 17:17:20 +02:00
String lang = mPrefs.getString(PREF_DEFAULT_LOCALE, "");
2012-02-24 06:13:27 +01:00
if (! "".equals(lang) && ! config.locale.getLanguage().equals(lang))
{
2015-01-20 15:47:24 +01:00
Locale locale = new Locale(lang);
2012-02-24 06:13:27 +01:00
Locale.setDefault(locale);
config.locale = locale;
getResources().updateConfiguration(config, getResources().getDisplayMetrics());
}
}
2011-07-03 10:39:49 +02:00
2015-01-20 15:47:24 +01:00
@Override
protected void onDestroy() {
super.onDestroy();
LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
2015-01-20 15:47:24 +01:00
}
2015-01-20 15:47:24 +01:00
public class DataCount {
// data uploaded
public long Upload;
// data downloaded
public long Download;
DataCount(long Upload, long Download){
this.Upload = Upload;
this.Download = Download;
}
}
private String formatCount(long count) {
NumberFormat numberFormat = NumberFormat.getInstance(Locale.getDefault());
2015-01-20 15:47:24 +01:00
// Converts the supplied argument into a string.
// Under 2Mb, returns "xxx.xKb"
// Over 2Mb, returns "xxx.xxMb"
if (count < 1e6)
return numberFormat.format(Math.round(((float) ((int) (count * 10 / 1024)) / 10)))
+ getString(R.string.kbps);
else
return numberFormat.format(Math
.round(((float) ((int) (count * 100 / 1024 / 1024)) / 100)))
+ getString(R.string.mbps);
2015-01-20 15:47:24 +01:00
}
private String formatTotal(long count) {
NumberFormat numberFormat = NumberFormat.getInstance(Locale.getDefault());
2015-01-20 15:47:24 +01:00
// Converts the supplied argument into a string.
// Under 2Mb, returns "xxx.xKb"
// Over 2Mb, returns "xxx.xxMb"
if (count < 1e6)
return numberFormat.format(Math.round(((float) ((int) (count * 10 / 1024)) / 10)))
+ getString(R.string.kb);
else
return numberFormat.format(Math
.round(((float) ((int) (count * 100 / 1024 / 1024)) / 100)))
+ getString(R.string.mb);
2015-01-20 15:47:24 +01:00
}
2015-01-20 15:47:24 +01:00
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
}
private static final float ROTATE_FROM = 0.0f;
private static final float ROTATE_TO = 360.0f*4f;// 3.141592654f * 32.0f;
2015-01-20 15:47:24 +01:00
public void spinOrbot (float direction)
{
startService (TorServiceConstants.CMD_NEWNYM);
Toast.makeText(this, R.string.newnym, Toast.LENGTH_SHORT).show();
// Rotate3dAnimation rotation = new Rotate3dAnimation(ROTATE_FROM, ROTATE_TO*direction, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
Rotate3dAnimation rotation = new Rotate3dAnimation(ROTATE_FROM, ROTATE_TO*direction, imgStatus.getWidth()/2f,imgStatus.getWidth()/2f,20f,false);
rotation.setFillAfter(true);
rotation.setInterpolator(new AccelerateInterpolator());
rotation.setDuration((long) 2*1000);
rotation.setRepeatCount(0);
imgStatus.startAnimation(rotation);
}
class MyGestureDetector extends SimpleOnGestureListener {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
try {
if (torStatus == TorServiceConstants.STATUS_ON)
{
float direction = 1f;
if (velocityX < 0)
direction = -1f;
spinOrbot (direction);
}
} catch (Exception e) {
// nothing
}
return false;
}
}
}