New hidden services management screen and database

This commit is contained in:
arrase 2016-11-17 02:27:48 +01:00
parent 84d6a767ae
commit 26d938126f
19 changed files with 962 additions and 445 deletions

View File

@ -28,7 +28,8 @@ android {
dependencies { dependencies {
compile project(':orbotservice') compile project(':orbotservice')
compile 'com.android.support:support-v4:23.4.0' compile 'com.android.support:support-v4:23.4.0'
compile 'com.android.support:appcompat-v7:23.4.0' compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.android.support:design:23.4.0'
} }

View File

@ -1,11 +1,13 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.torproject.android" package="org.torproject.android"
android:versionName="15.2.0-RC-8-multi"
android:versionCode="15208000"
android:installLocation="auto" android:installLocation="auto"
> android:versionCode="15208000"
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="23"/> android:versionName="15.2.0-RC-8-multi">
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="23" />
<!-- <!--
<permission android:name="org.torproject.android.MANAGE_TOR" <permission android:name="org.torproject.android.MANAGE_TOR"
android:label="@string/permission_manage_tor_label" android:label="@string/permission_manage_tor_label"
@ -20,84 +22,83 @@
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.ACCESS_SUPERUSER" /> <uses-permission android:name="android.permission.ACCESS_SUPERUSER" />
<application android:name="org.torproject.android.OrbotApp" android:icon="@drawable/ic_launcher" <android:uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
android:label="@string/app_name" <android:uses-permission android:name="android.permission.READ_PHONE_STATE" />
android:description="@string/app_description" <android:uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
android:configChanges="locale|orientation|screenSize"
android:theme="@style/DefaultTheme" <application
android:name=".OrbotApp"
android:allowBackup="false" android:allowBackup="false"
android:allowClearUserData="true" android:allowClearUserData="true"
android:largeHeap="false" android:configChanges="locale|orientation|screenSize"
android:description="@string/app_description"
android:hardwareAccelerated="false" android:hardwareAccelerated="false"
android:icon="@drawable/ic_launcher"
> android:label="@string/app_name"
android:largeHeap="false"
<activity android:name=".OrbotMainActivity" android:theme="@style/DefaultTheme">
<activity
android:name=".OrbotMainActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:excludeFromRecents="true" android:excludeFromRecents="true"
android:launchMode="singleTop" android:launchMode="singleTop">
>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="bridge" /> <data android:scheme="bridge" />
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<action android:name="org.torproject.android.REQUEST_HS_PORT" /> <action android:name="org.torproject.android.REQUEST_HS_PORT" />
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<action android:name="org.torproject.android.START_TOR" /> <action android:name="org.torproject.android.START_TOR" />
</intent-filter> </intent-filter>
</activity> </activity>
<!-- <!-- This is for ensuring the background service still runs when/if the app is swiped away -->
This is for ensuring the background service still runs when/if the app is swiped away
-->
<activity <activity
android:name=".service.util.DummyActivity" android:name=".service.util.DummyActivity"
android:theme="@android:style/Theme.Translucent"
android:enabled="true"
android:allowTaskReparenting="true" android:allowTaskReparenting="true"
android:noHistory="true"
android:excludeFromRecents="true"
android:alwaysRetainTaskState="false" android:alwaysRetainTaskState="false"
android:stateNotNeeded="true"
android:clearTaskOnLaunch="true" android:clearTaskOnLaunch="true"
android:enabled="true"
android:excludeFromRecents="true"
android:finishOnTaskLaunch="true" android:finishOnTaskLaunch="true"
android:noHistory="true"
/> android:stateNotNeeded="true"
android:theme="@android:style/Theme.Translucent" />
<activity <activity
android:name=".vpn.VPNEnableActivity" android:label="@string/app_name" android:exported="false" android:name=".vpn.VPNEnableActivity"
/> android:exported="false"
android:label="@string/app_name" />
<activity
<activity android:name="org.torproject.android.ui.PromoAppsActivity" android:exported="false"/> android:name=".ui.PromoAppsActivity"
android:exported="false" />
<activity
<activity android:name=".settings.SettingsPreferences" android:label="@string/app_name"/> android:name=".settings.SettingsPreferences"
<activity android:name=".ui.AppManager" android:label="@string/app_name" android:label="@string/app_name" />
android:theme="@style/Theme.AppCompat" <activity
/> android:name=".ui.AppManager"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat" />
<service <service
android:name=".service.TorService" android:name=".service.TorService"
android:enabled="true" android:enabled="true"
android:permission="android.permission.BIND_VPN_SERVICE" android:permission="android.permission.BIND_VPN_SERVICE"
android:stopWithTask="false" > android:stopWithTask="false" />
</service>
<service <service
android:name=".service.vpn.TorVpnService" android:name=".service.vpn.TorVpnService"
android:enabled="true" android:enabled="true"
@ -114,24 +115,36 @@
<action android:name="org.torproject.android.intent.action.START" /> <action android:name="org.torproject.android.intent.action.START" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<receiver
<receiver android:name=".OnBootReceiver" android:name=".OnBootReceiver"
android:enabled="true" android:exported="true" android:enabled="true"
android:exported="true">
>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.HOME" /> <category android:name="android.intent.category.HOME" />
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.QUICKBOOT_POWERON" /> <action android:name="android.intent.action.QUICKBOOT_POWERON" />
<category android:name="android.intent.category.HOME" /> <category android:name="android.intent.category.HOME" />
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MEDIA_MOUNTED" /> <action android:name="android.intent.action.MEDIA_MOUNTED" />
<category android:name="android.intent.category.HOME" /> <category android:name="android.intent.category.HOME" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<activity
android:name=".ui.hs.HiddenServicesActivity"
android:label="@string/title_activity_hidden_services"
android:theme="@style/DefaultTheme" />
<provider
android:name=".ui.hs.providers.HSContentProvider"
android:exported="false"
android:authorities="org.torproject.android.ui.hs.providers" />
</application> </application>
</manifest> </manifest>

View File

@ -23,6 +23,7 @@ import org.torproject.android.service.TorServiceConstants;
import org.torproject.android.service.util.TorServiceUtils; import org.torproject.android.service.util.TorServiceUtils;
import org.torproject.android.settings.SettingsPreferences; import org.torproject.android.settings.SettingsPreferences;
import org.torproject.android.ui.AppManager; import org.torproject.android.ui.AppManager;
import org.torproject.android.ui.hs.HiddenServicesActivity;
import org.torproject.android.ui.ImageProgressView; import org.torproject.android.ui.ImageProgressView;
import org.torproject.android.ui.PromoAppsActivity; import org.torproject.android.ui.PromoAppsActivity;
import org.torproject.android.ui.Rotate3dAnimation; import org.torproject.android.ui.Rotate3dAnimation;
@ -478,6 +479,10 @@ public class OrbotMainActivity extends AppCompatActivity
} }
} }
}
else if (item.getItemId() == R.id.menu_hidden_services)
{
startActivity(new Intent(this, HiddenServicesActivity.class));
} }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);

View File

@ -37,9 +37,6 @@ public class SettingsPreferences
private Preference prefTransProxyFlush = null; private Preference prefTransProxyFlush = null;
private Preference prefTransProxyApps = null; private Preference prefTransProxyApps = null;
private CheckBoxPreference prefHiddenServices = null;
private EditTextPreference prefHiddenServicesPorts;
private EditTextPreference prefHiddenServicesHostname;
private CheckBoxPreference prefRequestRoot = null; private CheckBoxPreference prefRequestRoot = null;
private ListPreference prefLocale = null; private ListPreference prefLocale = null;
@ -104,10 +101,6 @@ public class SettingsPreferences
prefTransProxyApps.setOnPreferenceClickListener(this); prefTransProxyApps.setOnPreferenceClickListener(this);
prefCBTransProxy.setOnPreferenceClickListener(this); prefCBTransProxy.setOnPreferenceClickListener(this);
prefcBTransProxyAll.setOnPreferenceClickListener(this); prefcBTransProxyAll.setOnPreferenceClickListener(this);
prefHiddenServices = (CheckBoxPreference) findPreference("pref_hs_enable");
prefHiddenServices.setOnPreferenceClickListener(this);
prefHiddenServicesHostname = (EditTextPreference) findPreference("pref_hs_hostname");
prefCBTransProxy.setEnabled(prefRequestRoot.isChecked()); prefCBTransProxy.setEnabled(prefRequestRoot.isChecked());
prefcBTransProxyAll.setEnabled(prefCBTransProxy.isChecked()); prefcBTransProxyAll.setEnabled(prefCBTransProxy.isChecked());
@ -116,10 +109,6 @@ public class SettingsPreferences
if (prefCBTransProxy.isChecked()) if (prefCBTransProxy.isChecked())
prefTransProxyApps.setEnabled((!prefcBTransProxyAll.isChecked())); prefTransProxyApps.setEnabled((!prefcBTransProxyAll.isChecked()));
prefHiddenServicesPorts = (EditTextPreference) findPreference("pref_hs_ports");
prefHiddenServicesHostname.setEnabled(prefHiddenServices.isChecked());
prefHiddenServicesPorts.setEnabled(prefHiddenServices.isChecked());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{ {
prefTransProxyApps.setEnabled(true); prefTransProxyApps.setEnabled(true);
@ -153,11 +142,6 @@ public class SettingsPreferences
{ {
startActivity(new Intent(this, AppManager.class)); startActivity(new Intent(this, AppManager.class));
}
else if (preference == prefHiddenServices)
{
prefHiddenServicesPorts.setEnabled(prefHiddenServices.isChecked());
prefHiddenServicesHostname.setEnabled(prefHiddenServices.isChecked());
} }
else else
{ {

View File

@ -0,0 +1,76 @@
package org.torproject.android.ui.hs;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.os.Bundle;
import android.os.Handler;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.View;
import org.torproject.android.R;
import org.torproject.android.ui.hs.adapters.HSAdapter;
import org.torproject.android.ui.hs.providers.HSContentProvider;
public class HiddenServicesActivity extends AppCompatActivity {
private HSAdapter mHiddenServices;
private ContentResolver mCR;
private HSObserver mHSObserver;
private String[] mProjection = new String[]{
HSContentProvider.HiddenService._ID,
HSContentProvider.HiddenService.NAME,
HSContentProvider.HiddenService.DOMAIN,
HSContentProvider.HiddenService.PORT};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hidden_services);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
mCR = getContentResolver();
// View adapter
mHiddenServices = new HSAdapter(
mCR.query(
HSContentProvider.CONTENT_URI, mProjection, null, null, null
));
mHSObserver = new HSObserver(new Handler());
mCR.registerContentObserver(HSContentProvider.CONTENT_URI, true, mHSObserver);
// Fill view
RecyclerView mRecyclerView = (RecyclerView) findViewById(R.id.onion_list);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setAdapter(mHiddenServices);
}
class HSObserver extends ContentObserver {
public HSObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
// New data
mHiddenServices.changeCursor(mCR.query(
HSContentProvider.CONTENT_URI, mProjection, null, null, null
));
}
}
}

View File

@ -0,0 +1,144 @@
package org.torproject.android.ui.hs.adapters;
/*
* Copyright (C) 2014 skyfish.jy@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import android.database.Cursor;
import android.database.DataSetObserver;
import android.support.v7.widget.RecyclerView;
/**
* Created by skyfishjy on 10/31/14.
*/
public abstract class CursorRecyclerViewAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {
private Cursor mCursor;
private boolean mDataValid;
private int mRowIdColumn;
private DataSetObserver mDataSetObserver;
public CursorRecyclerViewAdapter(Cursor cursor) {
mCursor = cursor;
mDataValid = cursor != null;
mRowIdColumn = mDataValid ? mCursor.getColumnIndex("_id") : -1;
mDataSetObserver = new NotifyingDataSetObserver();
if (mCursor != null) {
mCursor.registerDataSetObserver(mDataSetObserver);
}
}
@Override
public int getItemCount() {
if (mDataValid && mCursor != null) {
return mCursor.getCount();
}
return 0;
}
@Override
public long getItemId(int position) {
if (mDataValid && mCursor != null && mCursor.moveToPosition(position)) {
return mCursor.getLong(mRowIdColumn);
}
return 0;
}
@Override
public void setHasStableIds(boolean hasStableIds) {
super.setHasStableIds(true);
}
public abstract void onBindViewHolder(VH viewHolder, Cursor cursor);
@Override
public void onBindViewHolder(VH viewHolder, int position) {
if (!mDataValid) {
throw new IllegalStateException("this should only be called when the cursor is valid");
}
if (!mCursor.moveToPosition(position)) {
throw new IllegalStateException("couldn't move cursor to position " + position);
}
onBindViewHolder(viewHolder, mCursor);
}
/**
* Change the underlying cursor to a new cursor. If there is an existing cursor it will be
* closed.
*/
public void changeCursor(Cursor cursor) {
Cursor old = swapCursor(cursor);
if (old != null) {
old.close();
}
}
public void closeCursor() {
mCursor.unregisterDataSetObserver(mDataSetObserver);
mCursor.close();
}
/**
* Swap in a new Cursor, returning the old Cursor. Unlike
* {@link #changeCursor(Cursor)}, the returned old Cursor is <em>not</em>
* closed.
*/
private Cursor swapCursor(Cursor newCursor) {
if (newCursor == mCursor) {
return null;
}
final Cursor oldCursor = mCursor;
if (oldCursor != null && mDataSetObserver != null) {
oldCursor.unregisterDataSetObserver(mDataSetObserver);
}
mCursor = newCursor;
if (mCursor != null) {
if (mDataSetObserver != null) {
mCursor.registerDataSetObserver(mDataSetObserver);
}
mRowIdColumn = newCursor.getColumnIndexOrThrow("_id");
mDataValid = true;
notifyDataSetChanged();
} else {
mRowIdColumn = -1;
mDataValid = false;
notifyDataSetChanged();
//There is no notifyDataSetInvalidated() method in RecyclerView.Adapter
}
return oldCursor;
}
private class NotifyingDataSetObserver extends DataSetObserver {
@Override
public void onChanged() {
super.onChanged();
mDataValid = true;
notifyDataSetChanged();
}
@Override
public void onInvalidated() {
super.onInvalidated();
mDataValid = false;
notifyDataSetChanged();
//There is no notifyDataSetInvalidated() method in RecyclerView.Adapter
}
}
}

View File

@ -0,0 +1,54 @@
package org.torproject.android.ui.hs.adapters;
import android.database.Cursor;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import org.torproject.android.R;
import org.torproject.android.ui.hs.providers.HSContentProvider;
public class HSAdapter extends CursorRecyclerViewAdapter<HSAdapter.ViewHolder> {
public HSAdapter(Cursor cursor) {
super(cursor);
}
@Override
public HSAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.onion_item, parent, false);
ViewHolder vh = new ViewHolder(v);
return vh;
}
@Override
public void onBindViewHolder(ViewHolder viewHolder, Cursor cursor) {
viewHolder.id = cursor.getInt(cursor.getColumnIndex(HSContentProvider.HiddenService._ID));
String name_string = cursor.getString(cursor.getColumnIndex(HSContentProvider.HiddenService.NAME));
Integer port = cursor.getInt(cursor.getColumnIndex(HSContentProvider.HiddenService.PORT));
viewHolder.name.setText(name_string + ": " + port.toString());
viewHolder.domain.setText(
cursor.getString(cursor.getColumnIndex(HSContentProvider.HiddenService.DOMAIN))
);
}
class ViewHolder extends RecyclerView.ViewHolder {
TextView name;
TextView domain;
Integer id;
ViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.hs_name);
domain = (TextView) itemView.findViewById(R.id.hs_onion);
}
}
}

View File

@ -0,0 +1,33 @@
package org.torproject.android.ui.hs.database;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class HSDatabase extends SQLiteOpenHelper {
public static final String HS_DATA_TABLE_NAME = "hs_data";
private static final int DATABASE_VERSION = 2;
private static final String DATABASE_NAME = "hidden_services";
private static final String HS_DATA_TABLE_CREATE =
"CREATE TABLE " + HS_DATA_TABLE_NAME + " (" +
"_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"name TEXT, " +
"domain TEXT, " +
"port INTEGER);";
public HSDatabase(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(HS_DATA_TABLE_CREATE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}

View File

@ -0,0 +1,121 @@
package org.torproject.android.ui.hs.providers;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.provider.BaseColumns;
import android.support.annotation.Nullable;
import org.torproject.android.ui.hs.database.HSDatabase;
public class HSContentProvider extends ContentProvider {
private static final String AUTH = "org.torproject.android.ui.hs.providers";
public static final Uri CONTENT_URI =
Uri.parse("content://" + AUTH + "/hs");
//UriMatcher
private static final int ONIONS = 1;
private static final int ONION_ID = 2;
private static final UriMatcher uriMatcher;
//Inicializamos el UriMatcher
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTH, "hs", ONIONS);
uriMatcher.addURI(AUTH, "hs/#", ONION_ID);
}
private HSDatabase mServerDB;
private Context mContext;
@Override
public boolean onCreate() {
mContext = getContext();
mServerDB = new HSDatabase(mContext);
return true;
}
@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
//Si es una consulta a un ID concreto construimos el WHERE
String where = selection;
if (uriMatcher.match(uri) == ONION_ID) {
where = "_id=" + uri.getLastPathSegment();
}
SQLiteDatabase db = mServerDB.getReadableDatabase();
return db.query(HSDatabase.HS_DATA_TABLE_NAME, projection, where,
selectionArgs, null, null, sortOrder);
}
@Nullable
@Override
public String getType(Uri uri) {
int match = uriMatcher.match(uri);
switch (match) {
case ONIONS:
return "vnd.android.cursor.dir/vnd.torproject.onions";
case ONION_ID:
return "vnd.android.cursor.item/vnd.torproject.onion";
default:
return null;
}
}
@Nullable
@Override
public Uri insert(Uri uri, ContentValues values) {
long regId;
SQLiteDatabase db = mServerDB.getWritableDatabase();
regId = db.insert(HSDatabase.HS_DATA_TABLE_NAME, null, values);
mContext.getContentResolver().notifyChange(CONTENT_URI, null);
return ContentUris.withAppendedId(CONTENT_URI, regId);
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
//Si es una consulta a un ID concreto construimos el WHERE
String where = selection;
if (uriMatcher.match(uri) == ONION_ID) {
where = "_id=" + uri.getLastPathSegment();
}
SQLiteDatabase db = mServerDB.getWritableDatabase();
Integer rows = db.delete(HSDatabase.HS_DATA_TABLE_NAME, where, selectionArgs);
mContext.getContentResolver().notifyChange(CONTENT_URI, null);
return rows;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
public static final class HiddenService implements BaseColumns {
//Nombres de columnas
public static final String NAME = "name";
public static final String PORT = "port";
public static final String DOMAIN = "domain";
private HiddenService() {
}
}
}

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/hs_name" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/hs_onion" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="org.torproject.android.ui.hs.HiddenServicesActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/DefaultTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/DefaultTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include layout="@layout/content_hidden_services" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
app:srcCompat="@android:drawable/stat_notify_more" />
</android.support.design.widget.CoordinatorLayout>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/content_hidden_services"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="org.torproject.android.ui.hs.HiddenServicesActivity"
tools:showIn="@layout/activity_hidden_services">
<android.support.v7.widget.RecyclerView
android:id="@+id/onion_list"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>

View File

@ -44,6 +44,10 @@
</menu> </menu>
</item> </item>
<item android:id="@+id/menu_hidden_services"
android:title="@string/menu_hidden_services"
yourapp:showAsAction="never"
/>
<!-- <!--
<item android:id="@+id/menu_promo_apps" <item android:id="@+id/menu_promo_apps"

View File

@ -0,0 +1 @@
<resources></resources>

View File

@ -0,0 +1,6 @@
<resources>
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
(such as screen margins) for screens with more than 820dp of available width. This
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
<dimen name="activity_horizontal_margin">64dp</dimen>
</resources>

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?><!--
<!--
/* //device/apps/common/assets/res/any/dimens.xml /* //device/apps/common/assets/res/any/dimens.xml
** **
** Copyright 2006, The Android Open Source Project ** Copyright 2006, The Android Open Source Project
@ -118,26 +117,27 @@
<!-- The platform's desired minimum size for a dialog's width when it <!-- The platform's desired minimum size for a dialog's width when it
is along the major axis (that is the screen is landscape). This may is along the major axis (that is the screen is landscape). This may
be either a fraction or a dimension. --> be either a fraction or a dimension. -->
<item type="dimen" name="dialog_min_width_major">65%</item> <item name="dialog_min_width_major" type="dimen">65%</item>
<!-- The platform's desired fixed width for a dialog along the major axis <!-- The platform's desired fixed width for a dialog along the major axis
(the screen is in landscape). This may be either a fraction or a dimension.--> (the screen is in landscape). This may be either a fraction or a dimension.-->
<item type="dimen" name="dialog_fixed_width_major">320dp</item> <item name="dialog_fixed_width_major" type="dimen">320dp</item>
<!-- The platform's desired fixed width for a dialog along the minor axis <!-- The platform's desired fixed width for a dialog along the minor axis
(the screen is in portrait). This may be either a fraction or a dimension.--> (the screen is in portrait). This may be either a fraction or a dimension.-->
<item type="dimen" name="dialog_fixed_width_minor">320dp</item> <item name="dialog_fixed_width_minor" type="dimen">320dp</item>
<!-- The platform's desired fixed height for a dialog along the major axis <!-- The platform's desired fixed height for a dialog along the major axis
(the screen is in portrait). This may be either a fraction or a dimension.--> (the screen is in portrait). This may be either a fraction or a dimension.-->
<item type="dimen" name="dialog_fixed_height_major">80%</item> <item name="dialog_fixed_height_major" type="dimen">80%</item>
<!-- The platform's desired fixed height for a dialog along the minor axis <!-- The platform's desired fixed height for a dialog along the minor axis
(the screen is in landscape). This may be either a fraction or a dimension.--> (the screen is in landscape). This may be either a fraction or a dimension.-->
<item type="dimen" name="dialog_fixed_height_minor">100%</item> <item name="dialog_fixed_height_minor" type="dimen">100%</item>
<!-- Preference activity, vertical padding for the header list --> <!-- Preference activity, vertical padding for the header list -->
<dimen name="preference_screen_header_vertical_padding">0dp</dimen> <dimen name="preference_screen_header_vertical_padding">0dp</dimen>
<dimen name="preference_screen_header_padding_side">16dip</dimen> <dimen name="preference_screen_header_padding_side">16dip</dimen>
<integer name="preference_screen_header_scrollbarStyle">0x02000000</integer> <!-- outsideOverlay --> <integer name="preference_screen_header_scrollbarStyle">0x02000000
</integer> <!-- outsideOverlay -->
<integer name="preference_fragment_scrollbarStyle">0x02000000</integer> <!-- outsideOverlay --> <integer name="preference_fragment_scrollbarStyle">0x02000000</integer> <!-- outsideOverlay -->
@ -148,7 +148,7 @@
<!-- The platform's desired minimum size for a dialog's width when it <!-- The platform's desired minimum size for a dialog's width when it
is along the minor axis (that is the screen is portrait). This may is along the minor axis (that is the screen is portrait). This may
be either a fraction or a dimension. --> be either a fraction or a dimension. -->
<item type="dimen" name="dialog_min_width_minor">95%</item> <item name="dialog_min_width_minor" type="dimen">95%</item>
<!-- The width of the big icons in notifications. --> <!-- The width of the big icons in notifications. -->
<dimen name="notification_large_icon_width">64dp</dimen> <dimen name="notification_large_icon_width">64dp</dimen>
@ -358,4 +358,8 @@
<!-- width of ImmersiveModeConfirmation (-1 for match_parent) --> <!-- width of ImmersiveModeConfirmation (-1 for match_parent) -->
<dimen name="immersive_mode_cling_width">-1px</dimen> <dimen name="immersive_mode_cling_width">-1px</dimen>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="fab_margin">16dp</dimen>
</resources> </resources>

View File

@ -89,7 +89,6 @@
<!--END Welcome Wizard strings (DJH)--> <!--END Welcome Wizard strings (DJH)-->
<string name="connect_first_time">You\'ve successfully connected to the Tor network - but this does NOT mean your device is secure. You can use the \'Browser\' button to test your connection. \n\nVisit us at https://guardianproject.info/apps/orbot or send an email to help@guardianproject.info to learn more.</string> <string name="connect_first_time">You\'ve successfully connected to the Tor network - but this does NOT mean your device is secure. You can use the \'Browser\' button to test your connection. \n\nVisit us at https://guardianproject.info/apps/orbot or send an email to help@guardianproject.info to learn more.</string>
<string name="tor_check">This will open your web browser to https://check.torproject.org in order to see if Orbot is probably configured and you are connected to Tor.</string> <string name="tor_check">This will open your web browser to https://check.torproject.org in order to see if Orbot is probably configured and you are connected to Tor.</string>
<string name="pref_hs_group">Hidden Service Hosting</string>
<string name="pref_general_group">General</string> <string name="pref_general_group">General</string>
<string name="pref_start_boot_title">Start Orbot on Boot</string> <string name="pref_start_boot_title">Start Orbot on Boot</string>
<string name="pref_start_boot_summary">Automatically start Orbot and connect Tor when your Android device boots</string> <string name="pref_start_boot_summary">Automatically start Orbot and connect Tor when your Android device boots</string>
@ -156,7 +155,6 @@
<string name="pref_proxy_password_dialog">Enter Proxy Password</string> <string name="pref_proxy_password_dialog">Enter Proxy Password</string>
<string name="status">Status</string> <string name="status">Status</string>
<string name="setting_up_full_transparent_proxying_">Setting up full transparent proxying&#8230;</string> <string name="setting_up_full_transparent_proxying_">Setting up full transparent proxying&#8230;</string>
<string name="setting_up_app_based_transparent_proxying_">Setting up app-based transparent proxying&#8230;</string> <string name="setting_up_app_based_transparent_proxying_">Setting up app-based transparent proxying&#8230;</string>
@ -202,11 +200,7 @@
<string name="reachable_ports">Reachable ports</string> <string name="reachable_ports">Reachable ports</string>
<string name="ports_reachable_behind_a_restrictive_firewall">Ports reachable behind a restrictive firewall</string> <string name="ports_reachable_behind_a_restrictive_firewall">Ports reachable behind a restrictive firewall</string>
<string name="enter_ports">Enter ports</string> <string name="enter_ports">Enter ports</string>
<string name="enable_hidden_services">Hidden Service Hosting</string> <string name="menu_hidden_services">Hidden Services</string>
<string name="run_servers_accessible_via_the_tor_network">allow on-device server to be accessible via the Tor network</string>
<string name="enter_localhost_ports_for_hidden_services">enter localhost ports for hidden services</string>
<string name="hidden_service_ports">Hidden Service Ports</string>
<string name="the_addressable_name_for_your_hidden_service_generated_automatically_">the addressable name for your hidden service (generated automatically)</string>
<string name="enable_debug_log_to_output_must_use_adb_or_alogcat_to_view_">enable debug log to output (must use adb or aLogCat to view)</string> <string name="enable_debug_log_to_output_must_use_adb_or_alogcat_to_view_">enable debug log to output (must use adb or aLogCat to view)</string>
<string name="project_home">Project Home(s): </string> <string name="project_home">Project Home(s): </string>
<string name="project_urls">https://www.torproject.org/docs/android\nhttps://guardianproject.info/apps/orbot/</string> <string name="project_urls">https://www.torproject.org/docs/android\nhttps://guardianproject.info/apps/orbot/</string>
@ -336,4 +330,5 @@
<string name="note_only_standard_tor_bridges_work_on_intel_x86_atom_devices">NOTE: Only standard Tor bridges work on Intel X86/ATOM devices</string> <string name="note_only_standard_tor_bridges_work_on_intel_x86_atom_devices">NOTE: Only standard Tor bridges work on Intel X86/ATOM devices</string>
<string name="vpn_default_world">World (Location)</string> <string name="vpn_default_world">World (Location)</string>
<string name="title_activity_hidden_services">Hidden Services</string>
</resources> </resources>

View File

@ -1,5 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<style name="NotificationText" parent="android:TextAppearance.StatusBar.EventContent" /> <style name="NotificationText" parent="android:TextAppearance.StatusBar.EventContent" />
<style name="NotificationTitle" parent="android:TextAppearance.StatusBar.EventContent.Title" /> <style name="NotificationTitle" parent="android:TextAppearance.StatusBar.EventContent.Title" />
<style name="DefaultTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="DefaultTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
</resources> </resources>

View File

@ -157,17 +157,6 @@ android:dialogTitle="@string/enter_ports"
/> />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="@string/pref_hs_group">
<CheckBoxPreference android:title="@string/enable_hidden_services"
android:summary="@string/run_servers_accessible_via_the_tor_network" android:key="pref_hs_enable"></CheckBoxPreference>
<EditTextPreference android:summary="@string/enter_localhost_ports_for_hidden_services"
android:title="@string/hidden_service_ports" android:enabled="false" android:key="pref_hs_ports"></EditTextPreference>
<EditTextPreference android:key="pref_hs_hostname"
android:summary="@string/the_addressable_name_for_your_hidden_service_generated_automatically_"
android:title=".Onion Hostname"></EditTextPreference>
</PreferenceCategory>
<PreferenceCategory android:title="@string/pref_proxy_title"> <PreferenceCategory android:title="@string/pref_proxy_title">
<EditTextPreference android:key="pref_proxy_type" <EditTextPreference android:key="pref_proxy_type"
android:title="@string/pref_proxy_type_title" android:title="@string/pref_proxy_type_title"