remove code only used for Orbot
|
@ -1,52 +0,0 @@
|
||||||
apply plugin: 'com.android.application'
|
|
||||||
|
|
||||||
android {
|
|
||||||
compileSdkVersion 25
|
|
||||||
buildToolsVersion '26.0.2'
|
|
||||||
defaultConfig {
|
|
||||||
applicationId "org.torproject.android"
|
|
||||||
minSdkVersion 16
|
|
||||||
targetSdkVersion 25
|
|
||||||
}
|
|
||||||
lintOptions {
|
|
||||||
checkReleaseBuilds false
|
|
||||||
// Or, if you prefer, you can continue to check for errors in release builds,
|
|
||||||
// but continue the build even when errors are found:
|
|
||||||
abortOnError false
|
|
||||||
}
|
|
||||||
buildTypes {
|
|
||||||
release {
|
|
||||||
minifyEnabled false
|
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
flavorDimensions "free"
|
|
||||||
|
|
||||||
productFlavors {
|
|
||||||
minimalperm {
|
|
||||||
dimension "free"
|
|
||||||
minSdkVersion 16
|
|
||||||
applicationId 'org.torproject.android'
|
|
||||||
targetSdkVersion 23
|
|
||||||
maxSdkVersion 23
|
|
||||||
versionCode 15510001
|
|
||||||
versionName '15.5.1-RC-2-multi-SDK16'
|
|
||||||
}
|
|
||||||
fullperm {
|
|
||||||
dimension "free"
|
|
||||||
minSdkVersion 23
|
|
||||||
applicationId 'org.torproject.android'
|
|
||||||
targetSdkVersion 25
|
|
||||||
versionCode 15510002
|
|
||||||
versionName '15.5.1-RC-2-multi-SDK23'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
compile project(':orbotservice')
|
|
||||||
compile 'com.android.support:support-v4:25.3.1'
|
|
||||||
compile 'com.android.support:appcompat-v7:25.3.1'
|
|
||||||
compile 'com.android.support:design:25.3.1'
|
|
||||||
}
|
|
|
@ -1,71 +0,0 @@
|
||||||
# Add project specific ProGuard rules here.
|
|
||||||
# By default, the flags in this file are appended to flags specified
|
|
||||||
# in /home/n8fr8/dev/android/sdk/tools/proguard/proguard-android.txt
|
|
||||||
# You can edit the include path and order by changing the proguardFiles
|
|
||||||
# directive in build.gradle.
|
|
||||||
#
|
|
||||||
# For more details, see
|
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
|
||||||
|
|
||||||
# Add any project specific keep options here:
|
|
||||||
|
|
||||||
# If your project uses WebView with JS, uncomment the following
|
|
||||||
# and specify the fully qualified class name to the JavaScript interface
|
|
||||||
# class:
|
|
||||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
|
||||||
# public *;
|
|
||||||
#}
|
|
||||||
|
|
||||||
-optimizationpasses 5
|
|
||||||
-dontusemixedcaseclassnames
|
|
||||||
-dontskipnonpubliclibraryclasses
|
|
||||||
-dontskipnonpubliclibraryclassmembers
|
|
||||||
-dontpreverify
|
|
||||||
-verbose
|
|
||||||
-repackageclasses ''
|
|
||||||
-allowaccessmodification
|
|
||||||
-keepattributes *Annotation*
|
|
||||||
|
|
||||||
-injars src/main/libs
|
|
||||||
|
|
||||||
-outjars bin/classes-processed.jar
|
|
||||||
|
|
||||||
-dontwarn javax.naming.**
|
|
||||||
-dontwarn android.support.**
|
|
||||||
|
|
||||||
|
|
||||||
####
|
|
||||||
-keep class org.** { *; }
|
|
||||||
-keep public class * extends android.app.Activity
|
|
||||||
-keep public class * extends android.app.Application
|
|
||||||
-keep public class * extends android.app.Service
|
|
||||||
-keep public class * extends android.content.BroadcastReceiver
|
|
||||||
-keep public class * extends android.content.ContentProvider
|
|
||||||
-keep public class * extends android.app.backup.BackupAgentHelper
|
|
||||||
-keep public class * extends android.preference.Preference
|
|
||||||
-keep public class com.android.vending.licensing.ILicensingService
|
|
||||||
|
|
||||||
-keepclasseswithmembernames class * {
|
|
||||||
native <methods>;
|
|
||||||
}
|
|
||||||
|
|
||||||
-keepclasseswithmembers class * {
|
|
||||||
public <init>(android.content.Context, android.util.AttributeSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
-keepclasseswithmembers class * {
|
|
||||||
public <init>(android.content.Context, android.util.AttributeSet, int);
|
|
||||||
}
|
|
||||||
|
|
||||||
-keepclassmembers class * extends android.app.Activity {
|
|
||||||
public void *(android.view.View);
|
|
||||||
}
|
|
||||||
|
|
||||||
-keepclassmembers enum * {
|
|
||||||
public static **[] values();
|
|
||||||
public static ** valueOf(java.lang.String);
|
|
||||||
}
|
|
||||||
|
|
||||||
-keep class * implements android.os.Parcelable {
|
|
||||||
public static final android.os.Parcelable$Creator *;
|
|
||||||
}
|
|
|
@ -1,166 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
package="org.torproject.android"
|
|
||||||
android:installLocation="auto">
|
|
||||||
|
|
||||||
<uses-sdk
|
|
||||||
android:minSdkVersion="16"
|
|
||||||
android:targetSdkVersion="25" />
|
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_SUPERUSER" />
|
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
|
||||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
|
||||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
|
||||||
|
|
||||||
<application
|
|
||||||
android:name=".OrbotApp"
|
|
||||||
android:allowBackup="false"
|
|
||||||
android:allowClearUserData="true"
|
|
||||||
android:configChanges="locale|orientation|screenSize"
|
|
||||||
android:description="@string/app_description"
|
|
||||||
android:hardwareAccelerated="false"
|
|
||||||
android:icon="@drawable/ic_launcher"
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:largeHeap="false"
|
|
||||||
android:theme="@style/DefaultTheme">
|
|
||||||
<activity
|
|
||||||
android:name=".OrbotMainActivity"
|
|
||||||
android:excludeFromRecents="true"
|
|
||||||
android:launchMode="singleTop">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.MAIN" />
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
|
||||||
</intent-filter>
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.VIEW" />
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
|
||||||
|
|
||||||
<data android:scheme="bridge" />
|
|
||||||
</intent-filter>
|
|
||||||
<intent-filter>
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
|
|
||||||
<action android:name="org.torproject.android.REQUEST_HS_PORT" />
|
|
||||||
</intent-filter>
|
|
||||||
<intent-filter>
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
|
|
||||||
<action android:name="org.torproject.android.START_TOR" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
|
|
||||||
<!-- This is for ensuring the background service still runs when/if the app is swiped away -->
|
|
||||||
<activity
|
|
||||||
android:name=".service.util.DummyActivity"
|
|
||||||
android:allowTaskReparenting="true"
|
|
||||||
android:alwaysRetainTaskState="false"
|
|
||||||
android:clearTaskOnLaunch="true"
|
|
||||||
android:enabled="true"
|
|
||||||
android:excludeFromRecents="true"
|
|
||||||
android:finishOnTaskLaunch="true"
|
|
||||||
android:noHistory="true"
|
|
||||||
android:stateNotNeeded="true"
|
|
||||||
android:theme="@android:style/Theme.Translucent" />
|
|
||||||
<activity
|
|
||||||
android:name=".vpn.VPNEnableActivity"
|
|
||||||
android:exported="false"
|
|
||||||
android:label="@string/app_name" />
|
|
||||||
<activity
|
|
||||||
android:name=".ui.PromoAppsActivity"
|
|
||||||
android:exported="false" />
|
|
||||||
<activity
|
|
||||||
android:name=".settings.SettingsPreferences"
|
|
||||||
android:label="@string/app_name" />
|
|
||||||
<activity
|
|
||||||
android:name=".ui.AppManagerActivity"
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:theme="@style/Theme.AppCompat" />
|
|
||||||
|
|
||||||
<service
|
|
||||||
android:name=".service.TorService"
|
|
||||||
android:enabled="true"
|
|
||||||
android:permission="android.permission.BIND_VPN_SERVICE"
|
|
||||||
android:stopWithTask="false"></service>
|
|
||||||
<service
|
|
||||||
android:name=".service.vpn.TorVpnService"
|
|
||||||
android:enabled="true"
|
|
||||||
android:permission="android.permission.BIND_VPN_SERVICE">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.net.VpnService" />
|
|
||||||
</intent-filter>
|
|
||||||
</service>
|
|
||||||
|
|
||||||
<receiver
|
|
||||||
android:name=".service.StartTorReceiver"
|
|
||||||
android:exported="true">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="org.torproject.android.intent.action.START" />
|
|
||||||
</intent-filter>
|
|
||||||
</receiver>
|
|
||||||
<receiver
|
|
||||||
android:name=".OnBootReceiver"
|
|
||||||
android:enabled="true"
|
|
||||||
android:exported="true">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.HOME" />
|
|
||||||
</intent-filter>
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.HOME" />
|
|
||||||
</intent-filter>
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.MEDIA_MOUNTED" />
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.HOME" />
|
|
||||||
</intent-filter>
|
|
||||||
</receiver>
|
|
||||||
|
|
||||||
<activity
|
|
||||||
android:name=".ui.hiddenservices.HiddenServicesActivity"
|
|
||||||
android:label="@string/title_activity_hidden_services"
|
|
||||||
android:theme="@style/DefaultTheme">
|
|
||||||
<meta-data
|
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
|
||||||
android:value=".OrbotMainActivity" />
|
|
||||||
</activity>
|
|
||||||
|
|
||||||
<provider
|
|
||||||
android:name=".ui.hiddenservices.providers.HSContentProvider"
|
|
||||||
android:authorities="org.torproject.android.ui.hiddenservices.providers"
|
|
||||||
android:exported="false" />
|
|
||||||
<provider
|
|
||||||
android:name="android.support.v4.content.FileProvider"
|
|
||||||
android:authorities="org.torproject.android.ui.hiddenservices.storage"
|
|
||||||
android:exported="false"
|
|
||||||
android:grantUriPermissions="true">
|
|
||||||
<meta-data
|
|
||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
|
||||||
android:resource="@xml/hidden_services_paths" />
|
|
||||||
</provider>
|
|
||||||
|
|
||||||
<activity
|
|
||||||
android:name=".ui.hiddenservices.ClientCookiesActivity"
|
|
||||||
android:label="@string/client_cookies"
|
|
||||||
android:theme="@style/DefaultTheme">
|
|
||||||
<meta-data
|
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
|
||||||
android:value=".OrbotMainActivity" />
|
|
||||||
</activity>
|
|
||||||
|
|
||||||
<provider
|
|
||||||
android:name=".ui.hiddenservices.providers.CookieContentProvider"
|
|
||||||
android:authorities="org.torproject.android.ui.hiddenservices.providers.cookie"
|
|
||||||
android:exported="false" />
|
|
||||||
</application>
|
|
||||||
|
|
||||||
</manifest>
|
|
|
@ -1,158 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
package="org.torproject.android"
|
|
||||||
android:installLocation="auto">
|
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_SUPERUSER" />
|
|
||||||
|
|
||||||
<application
|
|
||||||
android:name=".OrbotApp"
|
|
||||||
android:allowBackup="false"
|
|
||||||
android:allowClearUserData="true"
|
|
||||||
android:configChanges="locale|orientation|screenSize"
|
|
||||||
android:description="@string/app_description"
|
|
||||||
android:hardwareAccelerated="false"
|
|
||||||
android:icon="@drawable/ic_launcher"
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:largeHeap="false"
|
|
||||||
android:theme="@style/DefaultTheme">
|
|
||||||
<activity
|
|
||||||
android:name=".OrbotMainActivity"
|
|
||||||
android:excludeFromRecents="true"
|
|
||||||
android:launchMode="singleTop">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.MAIN" />
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
|
||||||
</intent-filter>
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.VIEW" />
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
|
||||||
|
|
||||||
<data android:scheme="bridge" />
|
|
||||||
</intent-filter>
|
|
||||||
<intent-filter>
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
|
|
||||||
<action android:name="org.torproject.android.REQUEST_HS_PORT" />
|
|
||||||
</intent-filter>
|
|
||||||
<intent-filter>
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
|
|
||||||
<action android:name="org.torproject.android.START_TOR" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
|
|
||||||
<!-- This is for ensuring the background service still runs when/if the app is swiped away -->
|
|
||||||
<activity
|
|
||||||
android:name=".service.util.DummyActivity"
|
|
||||||
android:allowTaskReparenting="true"
|
|
||||||
android:alwaysRetainTaskState="false"
|
|
||||||
android:clearTaskOnLaunch="true"
|
|
||||||
android:enabled="true"
|
|
||||||
android:excludeFromRecents="true"
|
|
||||||
android:finishOnTaskLaunch="true"
|
|
||||||
android:noHistory="true"
|
|
||||||
android:stateNotNeeded="true"
|
|
||||||
android:theme="@android:style/Theme.Translucent" />
|
|
||||||
<activity
|
|
||||||
android:name=".vpn.VPNEnableActivity"
|
|
||||||
android:exported="false"
|
|
||||||
android:label="@string/app_name" />
|
|
||||||
<activity
|
|
||||||
android:name=".ui.PromoAppsActivity"
|
|
||||||
android:exported="false" />
|
|
||||||
<activity
|
|
||||||
android:name=".settings.SettingsPreferences"
|
|
||||||
android:label="@string/app_name" />
|
|
||||||
<activity
|
|
||||||
android:name=".ui.AppManagerActivity"
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:theme="@style/Theme.AppCompat" />
|
|
||||||
|
|
||||||
<service
|
|
||||||
android:name=".service.TorService"
|
|
||||||
android:enabled="true"
|
|
||||||
android:permission="android.permission.BIND_VPN_SERVICE"
|
|
||||||
android:stopWithTask="false"></service>
|
|
||||||
<service
|
|
||||||
android:name=".service.vpn.TorVpnService"
|
|
||||||
android:enabled="true"
|
|
||||||
android:permission="android.permission.BIND_VPN_SERVICE">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.net.VpnService" />
|
|
||||||
</intent-filter>
|
|
||||||
</service>
|
|
||||||
|
|
||||||
<receiver
|
|
||||||
android:name=".service.StartTorReceiver"
|
|
||||||
android:exported="true">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="org.torproject.android.intent.action.START" />
|
|
||||||
</intent-filter>
|
|
||||||
</receiver>
|
|
||||||
<receiver
|
|
||||||
android:name=".OnBootReceiver"
|
|
||||||
android:enabled="true"
|
|
||||||
android:exported="true">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.HOME" />
|
|
||||||
</intent-filter>
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.HOME" />
|
|
||||||
</intent-filter>
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.MEDIA_MOUNTED" />
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.HOME" />
|
|
||||||
</intent-filter>
|
|
||||||
</receiver>
|
|
||||||
|
|
||||||
<activity
|
|
||||||
android:name=".ui.hiddenservices.HiddenServicesActivity"
|
|
||||||
android:label="@string/title_activity_hidden_services"
|
|
||||||
android:theme="@style/DefaultTheme">
|
|
||||||
<meta-data
|
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
|
||||||
android:value=".OrbotMainActivity" />
|
|
||||||
</activity>
|
|
||||||
|
|
||||||
<provider
|
|
||||||
android:name=".ui.hiddenservices.providers.HSContentProvider"
|
|
||||||
android:authorities="org.torproject.android.ui.hiddenservices.providers"
|
|
||||||
android:exported="false" />
|
|
||||||
<provider
|
|
||||||
android:name="android.support.v4.content.FileProvider"
|
|
||||||
android:authorities="org.torproject.android.ui.hiddenservices.storage"
|
|
||||||
android:exported="false"
|
|
||||||
android:grantUriPermissions="true">
|
|
||||||
<meta-data
|
|
||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
|
||||||
android:resource="@xml/hidden_services_paths" />
|
|
||||||
</provider>
|
|
||||||
|
|
||||||
<activity
|
|
||||||
android:name=".ui.hiddenservices.ClientCookiesActivity"
|
|
||||||
android:label="@string/client_cookies"
|
|
||||||
android:theme="@style/DefaultTheme">
|
|
||||||
<meta-data
|
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
|
||||||
android:value=".OrbotMainActivity" />
|
|
||||||
</activity>
|
|
||||||
|
|
||||||
<provider
|
|
||||||
android:name=".ui.hiddenservices.providers.CookieContentProvider"
|
|
||||||
android:authorities="org.torproject.android.ui.hiddenservices.providers.cookie"
|
|
||||||
android:exported="false" />
|
|
||||||
</application>
|
|
||||||
|
|
||||||
</manifest>
|
|
|
@ -1 +0,0 @@
|
||||||
../../../../LICENSE
|
|
|
@ -1,506 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2009 ZXing authors
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.google.zxing.integration.android;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.app.Fragment;
|
|
||||||
import android.content.ActivityNotFoundException;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.pm.ResolveInfo;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>A utility class which helps ease integration with Barcode Scanner via {@link Intent}s. This is a simple
|
|
||||||
* way to invoke barcode scanning and receive the result, without any need to integrate, modify, or learn the
|
|
||||||
* project's source code.</p>
|
|
||||||
*
|
|
||||||
* <h2>Initiating a barcode scan</h2>
|
|
||||||
*
|
|
||||||
* <p>To integrate, create an instance of {@code IntentIntegrator} and call {@link #initiateScan()} and wait
|
|
||||||
* for the result in your app.</p>
|
|
||||||
*
|
|
||||||
* <p>It does require that the Barcode Scanner (or work-alike) application is installed. The
|
|
||||||
* {@link #initiateScan()} method will prompt the user to download the application, if needed.</p>
|
|
||||||
*
|
|
||||||
* <p>There are a few steps to using this integration. First, your {@link Activity} must implement
|
|
||||||
* the method {@link Activity#onActivityResult(int, int, Intent)} and include a line of code like this:</p>
|
|
||||||
*
|
|
||||||
* <pre>{@code
|
|
||||||
* public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
|
||||||
* IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
|
|
||||||
* if (scanResult != null) {
|
|
||||||
* // handle scan result
|
|
||||||
* }
|
|
||||||
* // else continue with any other code you need in the method
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
* }</pre>
|
|
||||||
*
|
|
||||||
* <p>This is where you will handle a scan result.</p>
|
|
||||||
*
|
|
||||||
* <p>Second, just call this in response to a user action somewhere to begin the scan process:</p>
|
|
||||||
*
|
|
||||||
* <pre>{@code
|
|
||||||
* IntentIntegrator integrator = new IntentIntegrator(yourActivity);
|
|
||||||
* integrator.initiateScan();
|
|
||||||
* }</pre>
|
|
||||||
*
|
|
||||||
* <p>Note that {@link #initiateScan()} returns an {@link AlertDialog} which is non-null if the
|
|
||||||
* user was prompted to download the application. This lets the calling app potentially manage the dialog.
|
|
||||||
* In particular, ideally, the app dismisses the dialog if it's still active in its {@link Activity#onPause()}
|
|
||||||
* method.</p>
|
|
||||||
*
|
|
||||||
* <p>You can use {@link #setTitle(String)} to customize the title of this download prompt dialog (or, use
|
|
||||||
* {@link #setTitleByID(int)} to set the title by string resource ID.) Likewise, the prompt message, and
|
|
||||||
* yes/no button labels can be changed.</p>
|
|
||||||
*
|
|
||||||
* <p>Finally, you can use {@link #addExtra(String, Object)} to add more parameters to the Intent used
|
|
||||||
* to invoke the scanner. This can be used to set additional options not directly exposed by this
|
|
||||||
* simplified API.</p>
|
|
||||||
*
|
|
||||||
* <p>By default, this will only allow applications that are known to respond to this intent correctly
|
|
||||||
* do so. The apps that are allowed to response can be set with {@link #setTargetApplications(List)}.
|
|
||||||
* For example, set to {@link #TARGET_BARCODE_SCANNER_ONLY} to only target the Barcode Scanner app itself.</p>
|
|
||||||
*
|
|
||||||
* <h2>Sharing text via barcode</h2>
|
|
||||||
*
|
|
||||||
* <p>To share text, encoded as a QR Code on-screen, similarly, see {@link #shareText(CharSequence)}.</p>
|
|
||||||
*
|
|
||||||
* <p>Some code, particularly download integration, was contributed from the Anobiit application.</p>
|
|
||||||
*
|
|
||||||
* <h2>Enabling experimental barcode formats</h2>
|
|
||||||
*
|
|
||||||
* <p>Some formats are not enabled by default even when scanning with {@link #ALL_CODE_TYPES}, such as
|
|
||||||
* PDF417. Use {@link #initiateScan(java.util.Collection)} with
|
|
||||||
* a collection containing the names of formats to scan for explicitly, like "PDF_417", to use such
|
|
||||||
* formats.</p>
|
|
||||||
*
|
|
||||||
* @author Sean Owen
|
|
||||||
* @author Fred Lin
|
|
||||||
* @author Isaac Potoczny-Jones
|
|
||||||
* @author Brad Drehmer
|
|
||||||
* @author gcstang
|
|
||||||
*/
|
|
||||||
public class IntentIntegrator {
|
|
||||||
|
|
||||||
public static final int REQUEST_CODE = 0x0000c0de; // Only use bottom 16 bits
|
|
||||||
private static final String TAG = IntentIntegrator.class.getSimpleName();
|
|
||||||
|
|
||||||
public static final String DEFAULT_TITLE = "Install Barcode Scanner?";
|
|
||||||
public static final String DEFAULT_MESSAGE =
|
|
||||||
"This application requires Barcode Scanner. Would you like to install it?";
|
|
||||||
public static final String DEFAULT_YES = "Yes";
|
|
||||||
public static final String DEFAULT_NO = "No";
|
|
||||||
|
|
||||||
private static final String BS_PACKAGE = "com.google.zxing.client.android";
|
|
||||||
private static final String BSPLUS_PACKAGE = "com.srowen.bs.android";
|
|
||||||
|
|
||||||
// supported barcode formats
|
|
||||||
public static final Collection<String> PRODUCT_CODE_TYPES = list("UPC_A", "UPC_E", "EAN_8", "EAN_13", "RSS_14");
|
|
||||||
public static final Collection<String> ONE_D_CODE_TYPES =
|
|
||||||
list("UPC_A", "UPC_E", "EAN_8", "EAN_13", "CODE_39", "CODE_93", "CODE_128",
|
|
||||||
"ITF", "RSS_14", "RSS_EXPANDED");
|
|
||||||
public static final Collection<String> QR_CODE_TYPES = Collections.singleton("QR_CODE");
|
|
||||||
public static final Collection<String> DATA_MATRIX_TYPES = Collections.singleton("DATA_MATRIX");
|
|
||||||
|
|
||||||
public static final Collection<String> ALL_CODE_TYPES = null;
|
|
||||||
|
|
||||||
public static final List<String> TARGET_BARCODE_SCANNER_ONLY = Collections.singletonList(BS_PACKAGE);
|
|
||||||
public static final List<String> TARGET_ALL_KNOWN = list(
|
|
||||||
BSPLUS_PACKAGE, // Barcode Scanner+
|
|
||||||
BSPLUS_PACKAGE + ".simple", // Barcode Scanner+ Simple
|
|
||||||
BS_PACKAGE // Barcode Scanner
|
|
||||||
// What else supports this intent?
|
|
||||||
);
|
|
||||||
|
|
||||||
private final Activity activity;
|
|
||||||
private final Fragment fragment;
|
|
||||||
|
|
||||||
private String title;
|
|
||||||
private String message;
|
|
||||||
private String buttonYes;
|
|
||||||
private String buttonNo;
|
|
||||||
private List<String> targetApplications;
|
|
||||||
private final Map<String,Object> moreExtras = new HashMap<String,Object>(3);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param activity {@link Activity} invoking the integration
|
|
||||||
*/
|
|
||||||
public IntentIntegrator(Activity activity) {
|
|
||||||
this.activity = activity;
|
|
||||||
this.fragment = null;
|
|
||||||
initializeConfiguration();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param fragment {@link Fragment} invoking the integration.
|
|
||||||
* {@link #startActivityForResult(Intent, int)} will be called on the {@link Fragment} instead
|
|
||||||
* of an {@link Activity}
|
|
||||||
*/
|
|
||||||
public IntentIntegrator(Fragment fragment) {
|
|
||||||
this.activity = fragment.getActivity();
|
|
||||||
this.fragment = fragment;
|
|
||||||
initializeConfiguration();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeConfiguration() {
|
|
||||||
title = DEFAULT_TITLE;
|
|
||||||
message = DEFAULT_MESSAGE;
|
|
||||||
buttonYes = DEFAULT_YES;
|
|
||||||
buttonNo = DEFAULT_NO;
|
|
||||||
targetApplications = TARGET_ALL_KNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTitle() {
|
|
||||||
return title;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTitle(String title) {
|
|
||||||
this.title = title;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTitleByID(int titleID) {
|
|
||||||
title = activity.getString(titleID);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMessage() {
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMessage(String message) {
|
|
||||||
this.message = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMessageByID(int messageID) {
|
|
||||||
message = activity.getString(messageID);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getButtonYes() {
|
|
||||||
return buttonYes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setButtonYes(String buttonYes) {
|
|
||||||
this.buttonYes = buttonYes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setButtonYesByID(int buttonYesID) {
|
|
||||||
buttonYes = activity.getString(buttonYesID);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getButtonNo() {
|
|
||||||
return buttonNo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setButtonNo(String buttonNo) {
|
|
||||||
this.buttonNo = buttonNo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setButtonNoByID(int buttonNoID) {
|
|
||||||
buttonNo = activity.getString(buttonNoID);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<String> getTargetApplications() {
|
|
||||||
return targetApplications;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void setTargetApplications(List<String> targetApplications) {
|
|
||||||
if (targetApplications.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException("No target applications");
|
|
||||||
}
|
|
||||||
this.targetApplications = targetApplications;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSingleTargetApplication(String targetApplication) {
|
|
||||||
this.targetApplications = Collections.singletonList(targetApplication);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String,?> getMoreExtras() {
|
|
||||||
return moreExtras;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void addExtra(String key, Object value) {
|
|
||||||
moreExtras.put(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initiates a scan for all known barcode types with the default camera.
|
|
||||||
*
|
|
||||||
* @return the {@link AlertDialog} that was shown to the user prompting them to download the app
|
|
||||||
* if a prompt was needed, or null otherwise.
|
|
||||||
*/
|
|
||||||
public final AlertDialog initiateScan() {
|
|
||||||
return initiateScan(ALL_CODE_TYPES, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initiates a scan for all known barcode types with the specified camera.
|
|
||||||
*
|
|
||||||
* @param cameraId camera ID of the camera to use. A negative value means "no preference".
|
|
||||||
* @return the {@link AlertDialog} that was shown to the user prompting them to download the app
|
|
||||||
* if a prompt was needed, or null otherwise.
|
|
||||||
*/
|
|
||||||
public final AlertDialog initiateScan(int cameraId) {
|
|
||||||
return initiateScan(ALL_CODE_TYPES, cameraId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initiates a scan, using the default camera, only for a certain set of barcode types, given as strings corresponding
|
|
||||||
* to their names in ZXing's {@code BarcodeFormat} class like "UPC_A". You can supply constants
|
|
||||||
* like {@link #PRODUCT_CODE_TYPES} for example.
|
|
||||||
*
|
|
||||||
* @param desiredBarcodeFormats names of {@code BarcodeFormat}s to scan for
|
|
||||||
* @return the {@link AlertDialog} that was shown to the user prompting them to download the app
|
|
||||||
* if a prompt was needed, or null otherwise.
|
|
||||||
*/
|
|
||||||
public final AlertDialog initiateScan(Collection<String> desiredBarcodeFormats) {
|
|
||||||
return initiateScan(desiredBarcodeFormats, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initiates a scan, using the specified camera, only for a certain set of barcode types, given as strings corresponding
|
|
||||||
* to their names in ZXing's {@code BarcodeFormat} class like "UPC_A". You can supply constants
|
|
||||||
* like {@link #PRODUCT_CODE_TYPES} for example.
|
|
||||||
*
|
|
||||||
* @param desiredBarcodeFormats names of {@code BarcodeFormat}s to scan for
|
|
||||||
* @param cameraId camera ID of the camera to use. A negative value means "no preference".
|
|
||||||
* @return the {@link AlertDialog} that was shown to the user prompting them to download the app
|
|
||||||
* if a prompt was needed, or null otherwise
|
|
||||||
*/
|
|
||||||
public final AlertDialog initiateScan(Collection<String> desiredBarcodeFormats, int cameraId) {
|
|
||||||
Intent intentScan = new Intent(BS_PACKAGE + ".SCAN");
|
|
||||||
intentScan.addCategory(Intent.CATEGORY_DEFAULT);
|
|
||||||
|
|
||||||
// check which types of codes to scan for
|
|
||||||
if (desiredBarcodeFormats != null) {
|
|
||||||
// set the desired barcode types
|
|
||||||
StringBuilder joinedByComma = new StringBuilder();
|
|
||||||
for (String format : desiredBarcodeFormats) {
|
|
||||||
if (joinedByComma.length() > 0) {
|
|
||||||
joinedByComma.append(',');
|
|
||||||
}
|
|
||||||
joinedByComma.append(format);
|
|
||||||
}
|
|
||||||
intentScan.putExtra("SCAN_FORMATS", joinedByComma.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
// check requested camera ID
|
|
||||||
if (cameraId >= 0) {
|
|
||||||
intentScan.putExtra("SCAN_CAMERA_ID", cameraId);
|
|
||||||
}
|
|
||||||
|
|
||||||
String targetAppPackage = findTargetAppPackage(intentScan);
|
|
||||||
if (targetAppPackage == null) {
|
|
||||||
return showDownloadDialog();
|
|
||||||
}
|
|
||||||
intentScan.setPackage(targetAppPackage);
|
|
||||||
intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
|
||||||
intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
|
||||||
attachMoreExtras(intentScan);
|
|
||||||
startActivityForResult(intentScan, REQUEST_CODE);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start an activity. This method is defined to allow different methods of activity starting for
|
|
||||||
* newer versions of Android and for compatibility library.
|
|
||||||
*
|
|
||||||
* @param intent Intent to start.
|
|
||||||
* @param code Request code for the activity
|
|
||||||
* @see android.app.Activity#startActivityForResult(Intent, int)
|
|
||||||
* @see android.app.Fragment#startActivityForResult(Intent, int)
|
|
||||||
*/
|
|
||||||
protected void startActivityForResult(Intent intent, int code) {
|
|
||||||
if (fragment == null) {
|
|
||||||
activity.startActivityForResult(intent, code);
|
|
||||||
} else {
|
|
||||||
fragment.startActivityForResult(intent, code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String findTargetAppPackage(Intent intent) {
|
|
||||||
PackageManager pm = activity.getPackageManager();
|
|
||||||
List<ResolveInfo> availableApps = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
|
|
||||||
if (availableApps != null) {
|
|
||||||
for (String targetApp : targetApplications) {
|
|
||||||
if (contains(availableApps, targetApp)) {
|
|
||||||
return targetApp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean contains(Iterable<ResolveInfo> availableApps, String targetApp) {
|
|
||||||
for (ResolveInfo availableApp : availableApps) {
|
|
||||||
String packageName = availableApp.activityInfo.packageName;
|
|
||||||
if (targetApp.equals(packageName)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private AlertDialog showDownloadDialog() {
|
|
||||||
AlertDialog.Builder downloadDialog = new AlertDialog.Builder(activity);
|
|
||||||
downloadDialog.setTitle(title);
|
|
||||||
downloadDialog.setMessage(message);
|
|
||||||
downloadDialog.setPositiveButton(buttonYes, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialogInterface, int i) {
|
|
||||||
String packageName;
|
|
||||||
if (targetApplications.contains(BS_PACKAGE)) {
|
|
||||||
// Prefer to suggest download of BS if it's anywhere in the list
|
|
||||||
packageName = BS_PACKAGE;
|
|
||||||
} else {
|
|
||||||
// Otherwise, first option:
|
|
||||||
packageName = targetApplications.get(0);
|
|
||||||
}
|
|
||||||
Uri uri = Uri.parse("market://details?id=" + packageName);
|
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
|
||||||
try {
|
|
||||||
if (fragment == null) {
|
|
||||||
activity.startActivity(intent);
|
|
||||||
} else {
|
|
||||||
fragment.startActivity(intent);
|
|
||||||
}
|
|
||||||
} catch (ActivityNotFoundException anfe) {
|
|
||||||
// Hmm, market is not installed
|
|
||||||
Log.w(TAG, "Google Play is not installed; cannot install " + packageName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
downloadDialog.setNegativeButton(buttonNo, null);
|
|
||||||
downloadDialog.setCancelable(true);
|
|
||||||
return downloadDialog.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Call this from your {@link Activity}'s
|
|
||||||
* {@link Activity#onActivityResult(int, int, Intent)} method.</p>
|
|
||||||
*
|
|
||||||
* @param requestCode request code from {@code onActivityResult()}
|
|
||||||
* @param resultCode result code from {@code onActivityResult()}
|
|
||||||
* @param intent {@link Intent} from {@code onActivityResult()}
|
|
||||||
* @return null if the event handled here was not related to this class, or
|
|
||||||
* else an {@link IntentResult} containing the result of the scan. If the user cancelled scanning,
|
|
||||||
* the fields will be null.
|
|
||||||
*/
|
|
||||||
public static IntentResult parseActivityResult(int requestCode, int resultCode, Intent intent) {
|
|
||||||
if (requestCode == REQUEST_CODE) {
|
|
||||||
if (resultCode == Activity.RESULT_OK) {
|
|
||||||
String contents = intent.getStringExtra("SCAN_RESULT");
|
|
||||||
String formatName = intent.getStringExtra("SCAN_RESULT_FORMAT");
|
|
||||||
byte[] rawBytes = intent.getByteArrayExtra("SCAN_RESULT_BYTES");
|
|
||||||
int intentOrientation = intent.getIntExtra("SCAN_RESULT_ORIENTATION", Integer.MIN_VALUE);
|
|
||||||
Integer orientation = intentOrientation == Integer.MIN_VALUE ? null : intentOrientation;
|
|
||||||
String errorCorrectionLevel = intent.getStringExtra("SCAN_RESULT_ERROR_CORRECTION_LEVEL");
|
|
||||||
return new IntentResult(contents,
|
|
||||||
formatName,
|
|
||||||
rawBytes,
|
|
||||||
orientation,
|
|
||||||
errorCorrectionLevel);
|
|
||||||
}
|
|
||||||
return new IntentResult();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defaults to type "TEXT_TYPE".
|
|
||||||
*
|
|
||||||
* @param text the text string to encode as a barcode
|
|
||||||
* @return the {@link AlertDialog} that was shown to the user prompting them to download the app
|
|
||||||
* if a prompt was needed, or null otherwise
|
|
||||||
* @see #shareText(CharSequence, CharSequence)
|
|
||||||
*/
|
|
||||||
public final AlertDialog shareText(CharSequence text) {
|
|
||||||
return shareText(text, "TEXT_TYPE");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shares the given text by encoding it as a barcode, such that another user can
|
|
||||||
* scan the text off the screen of the device.
|
|
||||||
*
|
|
||||||
* @param text the text string to encode as a barcode
|
|
||||||
* @param type type of data to encode. See {@code com.google.zxing.client.android.Contents.Type} constants.
|
|
||||||
* @return the {@link AlertDialog} that was shown to the user prompting them to download the app
|
|
||||||
* if a prompt was needed, or null otherwise
|
|
||||||
*/
|
|
||||||
public final AlertDialog shareText(CharSequence text, CharSequence type) {
|
|
||||||
Intent intent = new Intent();
|
|
||||||
intent.addCategory(Intent.CATEGORY_DEFAULT);
|
|
||||||
intent.setAction(BS_PACKAGE + ".ENCODE");
|
|
||||||
intent.putExtra("ENCODE_TYPE", type);
|
|
||||||
intent.putExtra("ENCODE_DATA", text);
|
|
||||||
String targetAppPackage = findTargetAppPackage(intent);
|
|
||||||
if (targetAppPackage == null) {
|
|
||||||
return showDownloadDialog();
|
|
||||||
}
|
|
||||||
intent.setPackage(targetAppPackage);
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
|
||||||
attachMoreExtras(intent);
|
|
||||||
if (fragment == null) {
|
|
||||||
activity.startActivity(intent);
|
|
||||||
} else {
|
|
||||||
fragment.startActivity(intent);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<String> list(String... values) {
|
|
||||||
return Collections.unmodifiableList(Arrays.asList(values));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void attachMoreExtras(Intent intent) {
|
|
||||||
for (Map.Entry<String,Object> entry : moreExtras.entrySet()) {
|
|
||||||
String key = entry.getKey();
|
|
||||||
Object value = entry.getValue();
|
|
||||||
// Kind of hacky
|
|
||||||
if (value instanceof Integer) {
|
|
||||||
intent.putExtra(key, (Integer) value);
|
|
||||||
} else if (value instanceof Long) {
|
|
||||||
intent.putExtra(key, (Long) value);
|
|
||||||
} else if (value instanceof Boolean) {
|
|
||||||
intent.putExtra(key, (Boolean) value);
|
|
||||||
} else if (value instanceof Double) {
|
|
||||||
intent.putExtra(key, (Double) value);
|
|
||||||
} else if (value instanceof Float) {
|
|
||||||
intent.putExtra(key, (Float) value);
|
|
||||||
} else if (value instanceof Bundle) {
|
|
||||||
intent.putExtra(key, (Bundle) value);
|
|
||||||
} else {
|
|
||||||
intent.putExtra(key, value.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,95 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2009 ZXing authors
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.google.zxing.integration.android;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Encapsulates the result of a barcode scan invoked through {@link IntentIntegrator}.</p>
|
|
||||||
*
|
|
||||||
* @author Sean Owen
|
|
||||||
*/
|
|
||||||
public final class IntentResult {
|
|
||||||
|
|
||||||
private final String contents;
|
|
||||||
private final String formatName;
|
|
||||||
private final byte[] rawBytes;
|
|
||||||
private final Integer orientation;
|
|
||||||
private final String errorCorrectionLevel;
|
|
||||||
|
|
||||||
IntentResult() {
|
|
||||||
this(null, null, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
IntentResult(String contents,
|
|
||||||
String formatName,
|
|
||||||
byte[] rawBytes,
|
|
||||||
Integer orientation,
|
|
||||||
String errorCorrectionLevel) {
|
|
||||||
this.contents = contents;
|
|
||||||
this.formatName = formatName;
|
|
||||||
this.rawBytes = rawBytes;
|
|
||||||
this.orientation = orientation;
|
|
||||||
this.errorCorrectionLevel = errorCorrectionLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return raw content of barcode
|
|
||||||
*/
|
|
||||||
public String getContents() {
|
|
||||||
return contents;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return name of format, like "QR_CODE", "UPC_A". See {@code BarcodeFormat} for more format names.
|
|
||||||
*/
|
|
||||||
public String getFormatName() {
|
|
||||||
return formatName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return raw bytes of the barcode content, if applicable, or null otherwise
|
|
||||||
*/
|
|
||||||
public byte[] getRawBytes() {
|
|
||||||
return rawBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return rotation of the image, in degrees, which resulted in a successful scan. May be null.
|
|
||||||
*/
|
|
||||||
public Integer getOrientation() {
|
|
||||||
return orientation;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return name of the error correction level used in the barcode, if applicable
|
|
||||||
*/
|
|
||||||
public String getErrorCorrectionLevel() {
|
|
||||||
return errorCorrectionLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
StringBuilder dialogText = new StringBuilder(100);
|
|
||||||
dialogText.append("Format: ").append(formatName).append('\n');
|
|
||||||
dialogText.append("Contents: ").append(contents).append('\n');
|
|
||||||
int rawBytesLength = rawBytes == null ? 0 : rawBytes.length;
|
|
||||||
dialogText.append("Raw bytes: (").append(rawBytesLength).append(" bytes)\n");
|
|
||||||
dialogText.append("Orientation: ").append(orientation).append('\n');
|
|
||||||
dialogText.append("EC level: ").append(errorCorrectionLevel).append('\n');
|
|
||||||
return dialogText.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
package org.torproject.android;
|
|
||||||
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
import org.torproject.android.service.util.Prefs;
|
|
||||||
import org.torproject.android.service.TorService;
|
|
||||||
import org.torproject.android.service.TorServiceConstants;
|
|
||||||
import org.torproject.android.vpn.VPNEnableActivity;
|
|
||||||
|
|
||||||
public class OnBootReceiver extends BroadcastReceiver {
|
|
||||||
|
|
||||||
private static boolean sReceivedBoot = false;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
|
|
||||||
if (Prefs.startOnBoot() && (!sReceivedBoot))
|
|
||||||
{
|
|
||||||
if (Prefs.useVpn())
|
|
||||||
startVpnService(context); //VPN will start Tor once it is done
|
|
||||||
else
|
|
||||||
startService(TorServiceConstants.ACTION_START, context);
|
|
||||||
|
|
||||||
sReceivedBoot = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void startVpnService (final Context context)
|
|
||||||
{
|
|
||||||
Intent intent = new Intent(context,VPNEnableActivity.class);
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
context.startActivity(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startService (String action, Context context)
|
|
||||||
{
|
|
||||||
|
|
||||||
Intent torService = new Intent(context, TorService.class);
|
|
||||||
torService.setAction(action);
|
|
||||||
context.startService(torService);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,111 +0,0 @@
|
||||||
|
|
||||||
package org.torproject.android;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.Application;
|
|
||||||
import android.app.Notification;
|
|
||||||
import android.app.NotificationManager;
|
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.net.VpnService;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.support.v4.app.NotificationCompat;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.widget.RemoteViews;
|
|
||||||
|
|
||||||
import org.torproject.android.service.OrbotConstants;
|
|
||||||
import org.torproject.android.service.TorEventHandler;
|
|
||||||
import org.torproject.android.service.TorService;
|
|
||||||
import org.torproject.android.service.util.Prefs;
|
|
||||||
|
|
||||||
import org.torproject.android.settings.Languages;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class OrbotApp extends Application implements OrbotConstants
|
|
||||||
{
|
|
||||||
|
|
||||||
private Locale locale;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate() {
|
|
||||||
super.onCreate();
|
|
||||||
Prefs.setContext(this);
|
|
||||||
|
|
||||||
Languages.setup(OrbotMainActivity.class, R.string.menu_settings);
|
|
||||||
Languages.setLanguage(this, Prefs.getDefaultLocale(), true);
|
|
||||||
|
|
||||||
checkTransparentProxyingLegacy();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onConfigurationChanged(Configuration newConfig) {
|
|
||||||
super.onConfigurationChanged(newConfig);
|
|
||||||
Log.i(TAG, "onConfigurationChanged " + newConfig.locale.getLanguage());
|
|
||||||
Languages.setLanguage(this, Prefs.getDefaultLocale(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void forceChangeLanguage(Activity activity) {
|
|
||||||
Intent intent = activity.getIntent();
|
|
||||||
if (intent == null) // when launched as LAUNCHER
|
|
||||||
intent = new Intent(activity, OrbotMainActivity.class);
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
|
|
||||||
activity.finish();
|
|
||||||
activity.overridePendingTransition(0, 0);
|
|
||||||
activity.startActivity(intent);
|
|
||||||
activity.overridePendingTransition(0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Languages getLanguages(Activity activity) {
|
|
||||||
return Languages.get(activity);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkTransparentProxyingLegacy ()
|
|
||||||
{
|
|
||||||
if (Prefs.useTransparentProxying())
|
|
||||||
{
|
|
||||||
showToolbarNotification(getString(R.string.no_transproxy_warning_short),getString(R.string.no_transproxy_warning), 9999, org.torproject.android.service.R.drawable.ic_stat_notifyerr);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
|
||||||
protected void showToolbarNotification (String shortMsg, String notifyMsg, int notifyId, int icon)
|
|
||||||
{
|
|
||||||
|
|
||||||
NotificationCompat.Builder notifyBuilder;
|
|
||||||
|
|
||||||
//Reusable code.
|
|
||||||
PackageManager pm = getPackageManager();
|
|
||||||
Intent intent = pm.getLaunchIntentForPackage(getPackageName());
|
|
||||||
PendingIntent pendIntent = PendingIntent.getActivity(this, 0, intent, 0);
|
|
||||||
|
|
||||||
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
|
||||||
|
|
||||||
notifyBuilder = new NotificationCompat.Builder(this)
|
|
||||||
.setContentTitle(getString(org.torproject.android.service.R.string.app_name));
|
|
||||||
|
|
||||||
|
|
||||||
notifyBuilder.setContentIntent(pendIntent);
|
|
||||||
|
|
||||||
notifyBuilder.setContentText(shortMsg);
|
|
||||||
notifyBuilder.setSmallIcon(icon);
|
|
||||||
notifyBuilder.setTicker(notifyMsg);
|
|
||||||
|
|
||||||
notifyBuilder.setOngoing(false);
|
|
||||||
|
|
||||||
notifyBuilder.setStyle(new NotificationCompat.BigTextStyle()
|
|
||||||
.bigText(notifyMsg).setBigContentTitle(getString(org.torproject.android.service.R.string.app_name)));
|
|
||||||
|
|
||||||
Notification notification = notifyBuilder.build();
|
|
||||||
|
|
||||||
notificationManager.notify(notifyId, notification);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,232 +0,0 @@
|
||||||
package org.torproject.android.settings;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.ContextWrapper;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.res.AssetManager;
|
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.DisplayMetrics;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.TreeMap;
|
|
||||||
|
|
||||||
public class Languages {
|
|
||||||
public static final String TAG = "Languages";
|
|
||||||
|
|
||||||
public static final Locale defaultLocale;
|
|
||||||
public static final Locale TIBETAN = new Locale("bo");
|
|
||||||
static final Locale localesToTest[] = {
|
|
||||||
Locale.ENGLISH, Locale.FRENCH, Locale.GERMAN,
|
|
||||||
Locale.ITALIAN, Locale.JAPANESE, Locale.KOREAN,
|
|
||||||
Locale.TRADITIONAL_CHINESE, Locale.SIMPLIFIED_CHINESE,
|
|
||||||
TIBETAN, new Locale("af"), new Locale("am"),
|
|
||||||
new Locale("ar"), new Locale("az"), new Locale("bg"),
|
|
||||||
new Locale("bn"), new Locale("ca"), new Locale("cs"),
|
|
||||||
new Locale("da"), new Locale("el"), new Locale("es"),
|
|
||||||
new Locale("et"), new Locale("eu"), new Locale("fa"),
|
|
||||||
new Locale("fi"), new Locale("gl"), new Locale("hi"),
|
|
||||||
new Locale("hr"), new Locale("hu"), new Locale("hy"),
|
|
||||||
new Locale("in"), new Locale("hy"), new Locale("in"),
|
|
||||||
new Locale("is"), new Locale("it"), new Locale("iw"),
|
|
||||||
new Locale("ka"), new Locale("kk"), new Locale("km"),
|
|
||||||
new Locale("kn"), new Locale("ky"), new Locale("lo"),
|
|
||||||
new Locale("lt"), new Locale("lv"), new Locale("mk"),
|
|
||||||
new Locale("ml"), new Locale("mn"), new Locale("mr"),
|
|
||||||
new Locale("ms"), new Locale("my"), new Locale("nb"),
|
|
||||||
new Locale("ne"), new Locale("nl"), new Locale("pl"),
|
|
||||||
new Locale("pt"), new Locale("rm"), new Locale("ro"),
|
|
||||||
new Locale("ru"), new Locale("si"), new Locale("sk"),
|
|
||||||
new Locale("sl"), new Locale("sn"), new Locale("sr"),
|
|
||||||
new Locale("sv"), new Locale("sw"), new Locale("ta"),
|
|
||||||
new Locale("te"), new Locale("th"), new Locale("tl"),
|
|
||||||
new Locale("tr"), new Locale("uk"), new Locale("ur"),
|
|
||||||
new Locale("uz"), new Locale("vi"), new Locale("zu"),
|
|
||||||
};
|
|
||||||
|
|
||||||
private static final String USE_SYSTEM_DEFAULT = "";
|
|
||||||
private static final String defaultString = "Use System Default";
|
|
||||||
|
|
||||||
private static Locale locale;
|
|
||||||
private static Languages singleton;
|
|
||||||
private static Class<?> clazz;
|
|
||||||
private static int resId;
|
|
||||||
private static Map<String, String> tmpMap = new TreeMap<String, String>();
|
|
||||||
private static Map<String, String> nameMap;
|
|
||||||
|
|
||||||
static {
|
|
||||||
defaultLocale = Locale.getDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Languages(Activity activity) {
|
|
||||||
AssetManager assets = activity.getAssets();
|
|
||||||
Configuration config = activity.getResources().getConfiguration();
|
|
||||||
// Resources() requires DisplayMetrics, but they are only needed for drawables
|
|
||||||
DisplayMetrics ignored = new DisplayMetrics();
|
|
||||||
activity.getWindowManager().getDefaultDisplay().getMetrics(ignored);
|
|
||||||
Resources resources;
|
|
||||||
Set<Locale> localeSet = new LinkedHashSet<Locale>();
|
|
||||||
for (Locale locale : localesToTest) {
|
|
||||||
config.locale = locale;
|
|
||||||
resources = new Resources(assets, ignored, config);
|
|
||||||
if (!TextUtils.equals(defaultString, resources.getString(resId))
|
|
||||||
|| locale.equals(Locale.ENGLISH))
|
|
||||||
localeSet.add(locale);
|
|
||||||
}
|
|
||||||
for (Locale locale : localeSet) {
|
|
||||||
if (locale.equals(TIBETAN)) {
|
|
||||||
// include English name for devices without Tibetan font support
|
|
||||||
tmpMap.put(TIBETAN.getLanguage(), "Tibetan བོད་སྐད།"); // Tibetan
|
|
||||||
} else if (locale.equals(Locale.SIMPLIFIED_CHINESE)) {
|
|
||||||
tmpMap.put(Locale.SIMPLIFIED_CHINESE.toString(), "中文 (中国)"); // Chinese (China)
|
|
||||||
} else if (locale.equals(Locale.TRADITIONAL_CHINESE)) {
|
|
||||||
tmpMap.put(Locale.TRADITIONAL_CHINESE.toString(), "中文 (台灣)"); // Chinese (Taiwan)
|
|
||||||
} else {
|
|
||||||
tmpMap.put(locale.getLanguage(), locale.getDisplayLanguage(locale));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* USE_SYSTEM_DEFAULT is a fake one for displaying in a chooser menu. */
|
|
||||||
localeSet.add(null);
|
|
||||||
tmpMap.put(USE_SYSTEM_DEFAULT, activity.getString(resId));
|
|
||||||
nameMap = Collections.unmodifiableMap(tmpMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the instance of {@link Languages} to work with, providing the
|
|
||||||
* {@link Activity} that is will be working as part of, as well as the
|
|
||||||
* {@code resId} that has the exact string "Use System Default",
|
|
||||||
* i.e. {@code R.string.use_system_default}.
|
|
||||||
* <p/>
|
|
||||||
* That string resource {@code resId} is also used to find the supported
|
|
||||||
* translations: if an included translation has a translated string that
|
|
||||||
* matches that {@code resId}, then that language will be included as a
|
|
||||||
* supported language.
|
|
||||||
*
|
|
||||||
* @param clazz the {@link Class} of the default {@code Activity},
|
|
||||||
* usually the main {@code Activity} from where the
|
|
||||||
* Settings is launched from.
|
|
||||||
* @param resId the string resource ID to for the string "Use System Default",
|
|
||||||
* e.g. {@code R.string.use_system_default}
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static void setup(Class<?> clazz, int resId) {
|
|
||||||
if (Languages.clazz == null) {
|
|
||||||
Languages.clazz = clazz;
|
|
||||||
Languages.resId = resId;
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException("Languages singleton was already initialized, duplicate call to Languages.setup()!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the singleton to work with.
|
|
||||||
*
|
|
||||||
* @param activity the {@link Activity} this is working as part of
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static Languages get(Activity activity) {
|
|
||||||
if (singleton == null) {
|
|
||||||
singleton = new Languages(activity);
|
|
||||||
}
|
|
||||||
return singleton;
|
|
||||||
}
|
|
||||||
|
|
||||||
//@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
|
||||||
@SuppressLint("NewApi")
|
|
||||||
public static void setLanguage(final ContextWrapper contextWrapper, String language, boolean refresh) {
|
|
||||||
if (locale != null && TextUtils.equals(locale.getLanguage(), language) && (!refresh)) {
|
|
||||||
return; // already configured
|
|
||||||
} else if (language == null || language == USE_SYSTEM_DEFAULT) {
|
|
||||||
locale = defaultLocale;
|
|
||||||
} else {
|
|
||||||
/* handle locales with the country in it, i.e. zh_CN, zh_TW, etc */
|
|
||||||
String localeSplit[] = language.split("_");
|
|
||||||
if (localeSplit.length > 1) {
|
|
||||||
locale = new Locale(localeSplit[0], localeSplit[1]);
|
|
||||||
} else {
|
|
||||||
locale = new Locale(language);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final Resources resources = contextWrapper.getBaseContext().getResources();
|
|
||||||
Configuration config = resources.getConfiguration();
|
|
||||||
if (Build.VERSION.SDK_INT >= 17) {
|
|
||||||
config.setLocale(locale);
|
|
||||||
} else {
|
|
||||||
config.locale = locale;
|
|
||||||
}
|
|
||||||
resources.updateConfiguration(config, resources.getDisplayMetrics());
|
|
||||||
Locale.setDefault(locale);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Force reload the {@link Activity to make language changes take effect.}
|
|
||||||
*
|
|
||||||
* @param activity the {@code Activity} to force reload
|
|
||||||
*/
|
|
||||||
public static void forceChangeLanguage(Activity activity) {
|
|
||||||
Intent intent = activity.getIntent();
|
|
||||||
if (intent == null) // when launched as LAUNCHER
|
|
||||||
return;
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
|
|
||||||
activity.finish();
|
|
||||||
activity.overridePendingTransition(0, 0);
|
|
||||||
activity.startActivity(intent);
|
|
||||||
activity.overridePendingTransition(0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the name of the language based on the locale.
|
|
||||||
*
|
|
||||||
* @param locale
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public String getName(String locale) {
|
|
||||||
String ret = nameMap.get(locale);
|
|
||||||
// if no match, try to return a more general name (i.e. English for
|
|
||||||
// en_IN)
|
|
||||||
if (ret == null && locale.contains("_"))
|
|
||||||
ret = nameMap.get(locale.split("_")[0]);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an array of the names of all the supported languages, sorted to
|
|
||||||
* match what is returned by {@link Languages#getSupportedLocales()}.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public String[] getAllNames() {
|
|
||||||
return nameMap.values().toArray(new String[nameMap.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getPosition(Locale locale) {
|
|
||||||
String localeName = locale.getLanguage();
|
|
||||||
int i = 0;
|
|
||||||
for (String key : nameMap.keySet())
|
|
||||||
if (TextUtils.equals(key, localeName))
|
|
||||||
return i;
|
|
||||||
else
|
|
||||||
i++;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get sorted list of supported locales.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public String[] getSupportedLocales() {
|
|
||||||
Set<String> keys = nameMap.keySet();
|
|
||||||
return keys.toArray(new String[keys.size()]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,74 +0,0 @@
|
||||||
/* Copyright (c) 2009, Nathan Freitas, Orbot / The Guardian Project - http://openideals.com/guardian */
|
|
||||||
/* See LICENSE for licensing information */
|
|
||||||
|
|
||||||
package org.torproject.android.settings;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.preference.ListPreference;
|
|
||||||
import android.preference.Preference;
|
|
||||||
import android.preference.Preference.OnPreferenceChangeListener;
|
|
||||||
import android.preference.Preference.OnPreferenceClickListener;
|
|
||||||
import android.preference.PreferenceActivity;
|
|
||||||
|
|
||||||
import org.torproject.android.OrbotApp;
|
|
||||||
import org.torproject.android.R;
|
|
||||||
import org.torproject.android.service.util.TorServiceUtils;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
|
|
||||||
public class SettingsPreferences
|
|
||||||
extends PreferenceActivity implements OnPreferenceClickListener {
|
|
||||||
private static final String TAG = "SettingsPreferences";
|
|
||||||
|
|
||||||
private ListPreference prefLocale = null;
|
|
||||||
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
addPreferencesFromResource(R.xml.preferences);
|
|
||||||
getPreferenceManager().setSharedPreferencesMode(Context.MODE_MULTI_PROCESS);
|
|
||||||
SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext());
|
|
||||||
|
|
||||||
prefLocale = (ListPreference) findPreference("pref_default_locale");
|
|
||||||
prefLocale.setOnPreferenceClickListener(this);
|
|
||||||
Languages languages = Languages.get(this);
|
|
||||||
prefLocale.setEntries(languages.getAllNames());
|
|
||||||
prefLocale.setEntryValues(languages.getSupportedLocales());
|
|
||||||
prefLocale.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
|
||||||
String language = (String) newValue;
|
|
||||||
if (preference == prefLocale) {
|
|
||||||
SharedPreferences settings = TorServiceUtils
|
|
||||||
.getSharedPrefs(getApplicationContext());
|
|
||||||
|
|
||||||
String lang = settings.getString("pref_default_locale",
|
|
||||||
Locale.getDefault().getLanguage());
|
|
||||||
OrbotApp app = (OrbotApp) getApplication();
|
|
||||||
Languages.setLanguage(app, language, true);
|
|
||||||
lang = settings.getString("pref_default_locale",
|
|
||||||
Locale.getDefault().getLanguage());
|
|
||||||
OrbotApp.forceChangeLanguage(SettingsPreferences.this);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean onPreferenceClick(Preference preference) {
|
|
||||||
|
|
||||||
setResult(RESULT_OK);
|
|
||||||
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,361 +0,0 @@
|
||||||
/* Copyright (c) 2009, Nathan Freitas, Orbot / The Guardian Project - http://openideals.com/guardian */
|
|
||||||
/* See LICENSE for licensing information */
|
|
||||||
|
|
||||||
package org.torproject.android.ui;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.StringTokenizer;
|
|
||||||
|
|
||||||
import org.torproject.android.service.OrbotConstants;
|
|
||||||
import org.torproject.android.R;
|
|
||||||
import org.torproject.android.service.util.TorServiceUtils;
|
|
||||||
import org.torproject.android.service.vpn.TorifiedApp;
|
|
||||||
|
|
||||||
import android.app.ProgressDialog;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.content.SharedPreferences.Editor;
|
|
||||||
import android.content.pm.ApplicationInfo;
|
|
||||||
import android.content.pm.PackageInfo;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.View.OnClickListener;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.CheckBox;
|
|
||||||
import android.widget.CompoundButton;
|
|
||||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
|
||||||
import android.widget.GridView;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.ListAdapter;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
public class AppManagerActivity extends AppCompatActivity implements OnClickListener, OrbotConstants {
|
|
||||||
|
|
||||||
private GridView listApps;
|
|
||||||
private ListAdapter adapterApps;
|
|
||||||
private final static String TAG = "Orbot";
|
|
||||||
PackageManager pMgr = null;
|
|
||||||
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
pMgr = getPackageManager();
|
|
||||||
|
|
||||||
this.setContentView(R.layout.layout_apps);
|
|
||||||
setTitle(R.string.apps_mode);
|
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
if (item.getItemId() == android.R.id.home)
|
|
||||||
{
|
|
||||||
finish();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
listApps = (GridView) findViewById(R.id.applistview);
|
|
||||||
mPrefs = TorServiceUtils.getSharedPrefs(getApplicationContext());
|
|
||||||
|
|
||||||
|
|
||||||
new AsyncTask<Void, Void, Void>() {
|
|
||||||
private ProgressDialog dialog;
|
|
||||||
|
|
||||||
protected void onPreExecute() {
|
|
||||||
// Pre Code
|
|
||||||
dialog = new ProgressDialog(AppManagerActivity.this, android.support.v4.app.DialogFragment.STYLE_NO_TITLE);
|
|
||||||
dialog.show();
|
|
||||||
}
|
|
||||||
protected Void doInBackground(Void... unused) {
|
|
||||||
loadApps(mPrefs);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
protected void onPostExecute(Void unused) {
|
|
||||||
listApps.setAdapter(adapterApps);
|
|
||||||
dialog.cancel();
|
|
||||||
}
|
|
||||||
}.execute();
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
SharedPreferences mPrefs = null;
|
|
||||||
ArrayList<TorifiedApp> mApps = null;
|
|
||||||
|
|
||||||
private void loadApps (SharedPreferences prefs)
|
|
||||||
{
|
|
||||||
|
|
||||||
mApps = getApps(getApplicationContext(), prefs);
|
|
||||||
Collections.sort(mApps,new Comparator<TorifiedApp>() {
|
|
||||||
public int compare(TorifiedApp o1, TorifiedApp o2) {
|
|
||||||
if (o1.isTorified() == o2.isTorified()) return o1.getName().compareTo(o2.getName());
|
|
||||||
if (o1.isTorified()) return -1;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
final LayoutInflater inflater = getLayoutInflater();
|
|
||||||
|
|
||||||
adapterApps = new ArrayAdapter<TorifiedApp>(this, R.layout.layout_apps_item, R.id.itemtext,mApps) {
|
|
||||||
|
|
||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
|
||||||
|
|
||||||
ListEntry entry = null;
|
|
||||||
|
|
||||||
if (convertView == null)
|
|
||||||
convertView = inflater.inflate(R.layout.layout_apps_item, parent, false);
|
|
||||||
else
|
|
||||||
entry = (ListEntry) convertView.getTag();
|
|
||||||
|
|
||||||
if (entry == null) {
|
|
||||||
// Inflate a new view
|
|
||||||
entry = new ListEntry();
|
|
||||||
entry.icon = (ImageView) convertView.findViewById(R.id.itemicon);
|
|
||||||
entry.box = (CheckBox) convertView.findViewById(R.id.itemcheck);
|
|
||||||
entry.text = (TextView) convertView.findViewById(R.id.itemtext);
|
|
||||||
convertView.setTag(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
final TorifiedApp app = mApps.get(position);
|
|
||||||
|
|
||||||
if (entry.icon != null) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
entry.icon.setImageDrawable(pMgr.getApplicationIcon(app.getPackageName()));
|
|
||||||
entry.icon.setOnClickListener(AppManagerActivity.this);
|
|
||||||
entry.icon.setTag(entry.box);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.text != null) {
|
|
||||||
entry.text.setText(app.getName());
|
|
||||||
entry.text.setOnClickListener(AppManagerActivity.this);
|
|
||||||
entry.text.setTag(entry.box);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.box != null) {
|
|
||||||
entry.box.setOnClickListener(AppManagerActivity.this);
|
|
||||||
entry.box.setChecked(app.isTorified());
|
|
||||||
entry.box.setTag(app);
|
|
||||||
}
|
|
||||||
|
|
||||||
return convertView;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ListEntry {
|
|
||||||
private CheckBox box;
|
|
||||||
private TextView text;
|
|
||||||
private ImageView icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see android.app.Activity#onStop()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void onStop() {
|
|
||||||
super.onStop();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public ArrayList<TorifiedApp> getApps (Context context, SharedPreferences prefs)
|
|
||||||
{
|
|
||||||
|
|
||||||
String tordAppString = prefs.getString(PREFS_KEY_TORIFIED, "");
|
|
||||||
String[] tordApps;
|
|
||||||
|
|
||||||
StringTokenizer st = new StringTokenizer(tordAppString,"|");
|
|
||||||
tordApps = new String[st.countTokens()];
|
|
||||||
int tordIdx = 0;
|
|
||||||
while (st.hasMoreTokens())
|
|
||||||
{
|
|
||||||
tordApps[tordIdx++] = st.nextToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
Arrays.sort(tordApps);
|
|
||||||
|
|
||||||
//else load the apps up
|
|
||||||
|
|
||||||
List<ApplicationInfo> lAppInfo = pMgr.getInstalledApplications(0);
|
|
||||||
|
|
||||||
Iterator<ApplicationInfo> itAppInfo = lAppInfo.iterator();
|
|
||||||
|
|
||||||
ArrayList<TorifiedApp> apps = new ArrayList<TorifiedApp>();
|
|
||||||
|
|
||||||
ApplicationInfo aInfo = null;
|
|
||||||
|
|
||||||
int appIdx = 0;
|
|
||||||
TorifiedApp app = null;
|
|
||||||
|
|
||||||
while (itAppInfo.hasNext())
|
|
||||||
{
|
|
||||||
aInfo = itAppInfo.next();
|
|
||||||
|
|
||||||
app = new TorifiedApp();
|
|
||||||
|
|
||||||
try {
|
|
||||||
PackageInfo pInfo = pMgr.getPackageInfo(aInfo.packageName, PackageManager.GET_PERMISSIONS);
|
|
||||||
|
|
||||||
if (pInfo != null && pInfo.requestedPermissions != null)
|
|
||||||
{
|
|
||||||
for (String permInfo:pInfo.requestedPermissions)
|
|
||||||
{
|
|
||||||
if (permInfo.equals("android.permission.INTERNET"))
|
|
||||||
{
|
|
||||||
app.setUsesInternet(true);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
if ((aInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 1)
|
|
||||||
{
|
|
||||||
//System app
|
|
||||||
app.setUsesInternet(true);
|
|
||||||
}**/
|
|
||||||
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
app.setName(pMgr.getApplicationLabel(aInfo).toString());
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
// no name
|
|
||||||
continue; //we only show apps with names
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (!app.usesInternet())
|
|
||||||
continue;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
apps.add(app);
|
|
||||||
}
|
|
||||||
|
|
||||||
app.setEnabled(aInfo.enabled);
|
|
||||||
app.setUid(aInfo.uid);
|
|
||||||
app.setUsername(pMgr.getNameForUid(app.getUid()));
|
|
||||||
app.setProcname(aInfo.processName);
|
|
||||||
app.setPackageName(aInfo.packageName);
|
|
||||||
|
|
||||||
|
|
||||||
// check if this application is allowed
|
|
||||||
if (Arrays.binarySearch(tordApps, app.getUsername()) >= 0) {
|
|
||||||
app.setTorified(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
app.setTorified(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
appIdx++;
|
|
||||||
}
|
|
||||||
|
|
||||||
Collections.sort(apps);
|
|
||||||
|
|
||||||
return apps;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void saveAppSettings (Context context)
|
|
||||||
{
|
|
||||||
|
|
||||||
StringBuilder tordApps = new StringBuilder();
|
|
||||||
Intent response = new Intent();
|
|
||||||
|
|
||||||
for (TorifiedApp tApp:mApps)
|
|
||||||
{
|
|
||||||
if (tApp.isTorified())
|
|
||||||
{
|
|
||||||
tordApps.append(tApp.getUsername());
|
|
||||||
tordApps.append("|");
|
|
||||||
response.putExtra(tApp.getUsername(),true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Editor edit = mPrefs.edit();
|
|
||||||
edit.putString(PREFS_KEY_TORIFIED, tordApps.toString());
|
|
||||||
edit.commit();
|
|
||||||
|
|
||||||
setResult(RESULT_OK,response);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called an application is check/unchecked
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
|
||||||
final TorifiedApp app = (TorifiedApp) buttonView.getTag();
|
|
||||||
if (app != null) {
|
|
||||||
app.setTorified(isChecked);
|
|
||||||
}
|
|
||||||
|
|
||||||
saveAppSettings(this);
|
|
||||||
|
|
||||||
}**/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void onClick(View v) {
|
|
||||||
|
|
||||||
CheckBox cbox = null;
|
|
||||||
|
|
||||||
if (v instanceof CheckBox)
|
|
||||||
cbox = (CheckBox)v;
|
|
||||||
else if (v.getTag() instanceof CheckBox)
|
|
||||||
cbox = (CheckBox)v.getTag();
|
|
||||||
|
|
||||||
if (cbox != null) {
|
|
||||||
final TorifiedApp app = (TorifiedApp) cbox.getTag();
|
|
||||||
if (app != null) {
|
|
||||||
app.setTorified(!app.isTorified());
|
|
||||||
cbox.setChecked(app.isTorified());
|
|
||||||
}
|
|
||||||
|
|
||||||
saveAppSettings(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
package org.torproject.android.ui;
|
|
||||||
|
|
||||||
public class BridgeSetupActivity {
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
package org.torproject.android.ui;
|
|
||||||
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.graphics.RectF;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
|
|
||||||
public class ImageProgressView extends ImageView
|
|
||||||
{
|
|
||||||
|
|
||||||
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
|
||||||
|
|
||||||
private float progress = 0f; // 0 to 1
|
|
||||||
|
|
||||||
private RectF circle;
|
|
||||||
|
|
||||||
public ImageProgressView(Context context) {
|
|
||||||
super(context);
|
|
||||||
// TODO Auto-generated constructor stub
|
|
||||||
init();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public ImageProgressView(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ImageProgressView(Context context, AttributeSet attrs, int defStyle) {
|
|
||||||
super(context, attrs, defStyle);
|
|
||||||
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init(){
|
|
||||||
paint.setStyle(Paint.Style.STROKE);
|
|
||||||
paint.setColor(Color.GREEN);
|
|
||||||
paint.setAntiAlias(true);
|
|
||||||
paint.setStrokeWidth(20);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
||||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
|
||||||
|
|
||||||
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
|
|
||||||
MeasureSpec.getSize(heightMeasureSpec));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDraw(Canvas canvas) {
|
|
||||||
|
|
||||||
super.onDraw(canvas);
|
|
||||||
|
|
||||||
if (circle == null)
|
|
||||||
{
|
|
||||||
circle = new RectF(getWidth()/2,getHeight()/2+getHeight()/8, getWidth()/3,getHeight()/3);
|
|
||||||
}
|
|
||||||
|
|
||||||
float sweepAngle = 360f * progress;
|
|
||||||
|
|
||||||
canvas.drawArc(circle, 0, sweepAngle, true, paint);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,328 +0,0 @@
|
||||||
package org.torproject.android.ui;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
|
|
||||||
import org.torproject.android.R;
|
|
||||||
import org.torproject.android.service.util.TorResourceInstaller;
|
|
||||||
import org.torproject.android.service.TorServiceConstants;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Environment;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Message;
|
|
||||||
import android.os.StatFs;
|
|
||||||
import android.text.format.Formatter;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
|
|
||||||
public class OrbotDiagnosticsActivity extends Activity {
|
|
||||||
|
|
||||||
private TextView mTextView = null;
|
|
||||||
private final static String TAG = "OrbotDiag";
|
|
||||||
private StringBuffer log = new StringBuffer();
|
|
||||||
Process mProcess;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
setContentView(R.layout.layout_diag);
|
|
||||||
|
|
||||||
mTextView = (TextView)findViewById(R.id.diaglog);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getFreeStorage ()
|
|
||||||
{
|
|
||||||
File path = Environment.getDataDirectory();
|
|
||||||
StatFs stat = new StatFs(path.getPath());
|
|
||||||
long blockSize = stat.getBlockSize();
|
|
||||||
long availableBlocks = stat.getAvailableBlocks();
|
|
||||||
return Formatter.formatFileSize(this, availableBlocks * blockSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
|
|
||||||
stopTor();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDestroy() {
|
|
||||||
|
|
||||||
super.onDestroy();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void stopTor ()
|
|
||||||
{
|
|
||||||
File appBinHome = this.getDir("bin", Context.MODE_PRIVATE);
|
|
||||||
|
|
||||||
File fileTor= new File(appBinHome, TorServiceConstants.TOR_ASSET_KEY);
|
|
||||||
|
|
||||||
if (mProcess != null)
|
|
||||||
mProcess.destroy();
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
|
|
||||||
|
|
||||||
log("Hello, Orbot!");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
log(android.os.Build.DEVICE);
|
|
||||||
log(android.os.Build.HARDWARE);
|
|
||||||
log(android.os.Build.MANUFACTURER);
|
|
||||||
log(android.os.Build.MODEL);
|
|
||||||
log(android.os.Build.VERSION.CODENAME);
|
|
||||||
log(android.os.Build.VERSION.RELEASE);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
log("error getting device info");
|
|
||||||
}
|
|
||||||
|
|
||||||
showFileTree ();
|
|
||||||
|
|
||||||
runTorTest();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void runTorTest ()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
File appBinHome = this.getDir("bin", Context.MODE_PRIVATE);
|
|
||||||
File appDataHome = this.getDir("data", Context.MODE_PRIVATE);
|
|
||||||
|
|
||||||
File fileTor= new File(appBinHome, TorServiceConstants.TOR_ASSET_KEY);
|
|
||||||
enableBinExec (fileTor, appBinHome);
|
|
||||||
|
|
||||||
InputStream is = getResources().openRawResource(R.raw.torrc);
|
|
||||||
File fileTorrc = new File(appBinHome, TorServiceConstants.TORRC_ASSET_KEY + "diag");
|
|
||||||
TorResourceInstaller.streamToFile(is,fileTorrc, false, false);
|
|
||||||
|
|
||||||
/**
|
|
||||||
ArrayList<String> alEnv = new ArrayList<String>();
|
|
||||||
alEnv.add("HOME=" + appBinHome.getAbsolutePath());
|
|
||||||
Shell shell = Shell.startShell(alEnv,appBinHome.getAbsolutePath());
|
|
||||||
SimpleCommand cmdTor = new SimpleCommand(fileTor.getAbsolutePath() + " DataDirectory " + appDataHome.getAbsolutePath() + " -f " + fileTorrc.getAbsolutePath());
|
|
||||||
shell.add(cmdTor);
|
|
||||||
**/
|
|
||||||
|
|
||||||
String cmd = fileTor.getAbsolutePath() + " DataDirectory " + appDataHome.getAbsolutePath() + " -f " + fileTorrc.getAbsolutePath();
|
|
||||||
|
|
||||||
log ("Executing command> " + cmd);
|
|
||||||
|
|
||||||
mProcess = Runtime.getRuntime().exec(cmd);
|
|
||||||
|
|
||||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(mProcess.getInputStream()));
|
|
||||||
StreamGobbler sg = new StreamGobbler();
|
|
||||||
sg.reader = bufferedReader;
|
|
||||||
sg.process = mProcess;
|
|
||||||
new Thread(sg).start();
|
|
||||||
|
|
||||||
if (mProcess.getErrorStream() != null)
|
|
||||||
{
|
|
||||||
bufferedReader = new BufferedReader(new InputStreamReader(mProcess.getErrorStream()));
|
|
||||||
sg = new StreamGobbler();
|
|
||||||
sg.reader = bufferedReader;
|
|
||||||
sg.process = mProcess;
|
|
||||||
new Thread(sg).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.d(TAG,"runTorTest exception",e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class StreamGobbler implements Runnable
|
|
||||||
{
|
|
||||||
BufferedReader reader;
|
|
||||||
Process process;
|
|
||||||
|
|
||||||
public void run ()
|
|
||||||
{
|
|
||||||
String line = null;
|
|
||||||
try {
|
|
||||||
while ( (line = reader.readLine()) != null)
|
|
||||||
{
|
|
||||||
Message msg = mHandler.obtainMessage(0);
|
|
||||||
msg.getData().putString("log", line);
|
|
||||||
mHandler.sendMessage(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.d(TAG, "error reading line",e);
|
|
||||||
}
|
|
||||||
|
|
||||||
//log("Tor exit code=" + process.exitValue() + ";");
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean enableBinExec (File fileBin, File appBinHome) throws Exception
|
|
||||||
{
|
|
||||||
|
|
||||||
log(fileBin.getName() + ": PRE: Is binary exec? " + fileBin.canExecute());
|
|
||||||
|
|
||||||
if (!fileBin.canExecute())
|
|
||||||
{
|
|
||||||
log("(re)Setting permission on binary: " + fileBin.getAbsolutePath());
|
|
||||||
|
|
||||||
Runtime.getRuntime().exec("chmod " + TorServiceConstants.CHMOD_EXE_VALUE + ' ' + fileBin.getAbsolutePath()).waitFor();
|
|
||||||
|
|
||||||
File fileTest = new File(fileBin.getAbsolutePath());
|
|
||||||
log(fileTest.getName() + ": POST: Is binary exec? " + fileTest.canExecute());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return fileBin.canExecute();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showFileTree ()
|
|
||||||
{
|
|
||||||
|
|
||||||
File fileDir = this.getDir("bin", Context.MODE_PRIVATE);
|
|
||||||
|
|
||||||
if (fileDir.exists())
|
|
||||||
{
|
|
||||||
log("checking file tree: " + fileDir.getAbsolutePath());
|
|
||||||
printDir (fileDir.getName(), fileDir);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
log("app_bin does not exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
fileDir = this.getDir("data", Context.MODE_PRIVATE);
|
|
||||||
if (fileDir.exists())
|
|
||||||
{
|
|
||||||
log("checking file tree: " + fileDir.getAbsolutePath());
|
|
||||||
printDir (fileDir.getName(), fileDir);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
log ("app_data does not exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void printDir (String path, File fileDir)
|
|
||||||
{
|
|
||||||
File[] files = fileDir.listFiles();
|
|
||||||
|
|
||||||
if (files != null && files.length > 0)
|
|
||||||
{
|
|
||||||
for (File file : files)
|
|
||||||
{
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (file.isDirectory())
|
|
||||||
{
|
|
||||||
printDir(path + '/' + file.getName(), file);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
log(path + '/' + file.getName() + " len:" + file.length() + " exec:" + file.canExecute());
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
log("problem printing out file information");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Handler mHandler = new Handler ()
|
|
||||||
{
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleMessage(Message msg) {
|
|
||||||
|
|
||||||
super.handleMessage(msg);
|
|
||||||
|
|
||||||
String logMsg = msg.getData().getString("log");
|
|
||||||
log(logMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
private void log (String msg)
|
|
||||||
{
|
|
||||||
Log.d(TAG, msg);
|
|
||||||
mTextView.append(msg + '\n');
|
|
||||||
log.append(msg + '\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
|
||||||
// Inflate menu resource file.
|
|
||||||
getMenuInflater().inflate(R.menu.share_menu, menu);
|
|
||||||
|
|
||||||
// Locate MenuItem with ShareActionProvider
|
|
||||||
MenuItem item = menu.findItem(R.id.menu_item_share);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
switch (item.getItemId()) {
|
|
||||||
|
|
||||||
case R.id.menu_item_share:
|
|
||||||
sendLog();
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendLog ()
|
|
||||||
{
|
|
||||||
int maxLength = 5000;
|
|
||||||
|
|
||||||
String logShare = null;
|
|
||||||
|
|
||||||
if (log.length() > maxLength)
|
|
||||||
logShare = log.substring(0, maxLength);
|
|
||||||
else
|
|
||||||
logShare = log.toString();
|
|
||||||
|
|
||||||
Intent sendIntent = new Intent();
|
|
||||||
sendIntent.setAction(Intent.ACTION_SEND);
|
|
||||||
sendIntent.putExtra(Intent.EXTRA_TEXT, logShare);
|
|
||||||
sendIntent.setType("text/plain");
|
|
||||||
startActivity(sendIntent);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,216 +0,0 @@
|
||||||
package org.torproject.android.ui;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
|
||||||
import android.content.pm.ResolveInfo;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.View.OnClickListener;
|
|
||||||
import android.widget.Button;
|
|
||||||
|
|
||||||
import org.torproject.android.service.OrbotConstants;
|
|
||||||
import org.torproject.android.R;
|
|
||||||
import org.torproject.android.service.TorServiceConstants;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class PromoAppsActivity extends Activity implements OrbotConstants {
|
|
||||||
|
|
||||||
final static String MARKET_URI = "market://details?id=";
|
|
||||||
final static String FDROID_APP_URI = "https://f-droid.org/repository/browse/?fdid=";
|
|
||||||
final static String PLAY_APP_URI = "https://play.google.com/store/apps/details?id=";
|
|
||||||
final static String FDROID_URI = "https://f-droid.org/repository/browse/?fdfilter=info.guardianproject";
|
|
||||||
final static String PLAY_URI = "https://play.google.com/store/apps/developer?id=The+Guardian+Project";
|
|
||||||
|
|
||||||
private final static String FDROID_PACKAGE_NAME = "org.fdroid.fdroid";
|
|
||||||
private final static String PLAY_PACKAGE_NAME = "com.android.vending";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState)
|
|
||||||
{
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStart() {
|
|
||||||
|
|
||||||
super.onStart();
|
|
||||||
setContentView(R.layout.layout_promo_apps);
|
|
||||||
|
|
||||||
stepFive();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void stepFive(){
|
|
||||||
|
|
||||||
|
|
||||||
String title = getString(R.string.wizard_tips_title);
|
|
||||||
|
|
||||||
setTitle(title);
|
|
||||||
|
|
||||||
Button btnLink = (Button)findViewById(R.id.WizardRootButtonInstallGibberbot);
|
|
||||||
|
|
||||||
btnLink.setOnClickListener(new OnClickListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
finish();
|
|
||||||
startActivity(getInstallIntent("info.guardianproject.otr.app.im",PromoAppsActivity.this));
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
btnLink = (Button)findViewById(R.id.WizardRootButtonInstallOrweb);
|
|
||||||
|
|
||||||
btnLink.setOnClickListener(new OnClickListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
finish();
|
|
||||||
startActivity(getInstallIntent(TorServiceConstants.BROWSER_APP_USERNAME,PromoAppsActivity.this));
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
btnLink = (Button)findViewById(R.id.WizardRootButtonInstallDuckgo);
|
|
||||||
|
|
||||||
btnLink.setOnClickListener(new OnClickListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
|
|
||||||
finish();
|
|
||||||
startActivity(getInstallIntent("com.duckduckgo.mobile.android",PromoAppsActivity.this));
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
btnLink = (Button)findViewById(R.id.WizardRootButtonInstallTwitter);
|
|
||||||
|
|
||||||
btnLink.setOnClickListener(new OnClickListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
|
|
||||||
String url = getString(R.string.twitter_setup_url);
|
|
||||||
finish();
|
|
||||||
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
btnLink = (Button)findViewById(R.id.WizardRootButtonInstallStoryMaker);
|
|
||||||
|
|
||||||
btnLink.setOnClickListener(new OnClickListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
finish();
|
|
||||||
startActivity(getInstallIntent("info.guardianproject.mrapp",PromoAppsActivity.this));
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
btnLink = (Button)findViewById(R.id.WizardRootButtonInstallMartus);
|
|
||||||
|
|
||||||
btnLink.setOnClickListener(new OnClickListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
finish();
|
|
||||||
startActivity(getInstallIntent("org.martus.android",PromoAppsActivity.this));
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
btnLink = (Button)findViewById(R.id.WizardRootButtonGooglePlay);
|
|
||||||
PackageManager pm = getPackageManager();
|
|
||||||
final Intent intent = new Intent(Intent.ACTION_VIEW);
|
|
||||||
// change text and icon based on which app store is installed (or not)
|
|
||||||
try {
|
|
||||||
if (isAppInstalled(pm, FDROID_PACKAGE_NAME)) {
|
|
||||||
Drawable icon = pm.getApplicationIcon(FDROID_PACKAGE_NAME);
|
|
||||||
btnLink.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
|
|
||||||
btnLink.setText(R.string.wizard_tips_fdroid);
|
|
||||||
intent.setPackage(FDROID_PACKAGE_NAME);
|
|
||||||
intent.setData(Uri.parse(FDROID_URI));
|
|
||||||
} else if (isAppInstalled(pm, PLAY_PACKAGE_NAME)) {
|
|
||||||
Drawable icon = pm.getApplicationIcon(PLAY_PACKAGE_NAME);
|
|
||||||
btnLink.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
|
|
||||||
btnLink.setText(R.string.wizard_tips_play);
|
|
||||||
intent.setPackage(PLAY_PACKAGE_NAME);
|
|
||||||
intent.setData(Uri.parse(PLAY_URI));
|
|
||||||
}
|
|
||||||
} catch (NameNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
btnLink.setText(R.string.wizard_tips_fdroid_org);
|
|
||||||
intent.setData(Uri.parse(FDROID_URI));
|
|
||||||
}
|
|
||||||
|
|
||||||
btnLink.setOnClickListener(new OnClickListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
startActivity(intent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Button next = ((Button)findViewById(R.id.btnWizard2));
|
|
||||||
next.setOnClickListener(new View.OnClickListener() {
|
|
||||||
|
|
||||||
public void onClick(View v) {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isAppInstalled(PackageManager pm, String packageName) {
|
|
||||||
try {
|
|
||||||
pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
|
|
||||||
return true;
|
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Intent getInstallIntent(String packageName, Context context) {
|
|
||||||
final Intent intent = new Intent(Intent.ACTION_VIEW);
|
|
||||||
intent.setData(Uri.parse(MARKET_URI + packageName));
|
|
||||||
|
|
||||||
PackageManager pm = context.getPackageManager();
|
|
||||||
List<ResolveInfo> resInfos = pm.queryIntentActivities(intent, 0);
|
|
||||||
|
|
||||||
String foundPackageName = null;
|
|
||||||
for (ResolveInfo r : resInfos) {
|
|
||||||
Log.i(TAG, "market: " + r.activityInfo.packageName);
|
|
||||||
if (TextUtils.equals(r.activityInfo.packageName, FDROID_PACKAGE_NAME)
|
|
||||||
|| TextUtils.equals(r.activityInfo.packageName, PLAY_PACKAGE_NAME)) {
|
|
||||||
foundPackageName = r.activityInfo.packageName;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (foundPackageName == null) {
|
|
||||||
intent.setData(Uri.parse(FDROID_APP_URI + packageName));
|
|
||||||
} else {
|
|
||||||
intent.setPackage(foundPackageName);
|
|
||||||
}
|
|
||||||
return intent;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,76 +0,0 @@
|
||||||
package org.torproject.android.ui;
|
|
||||||
|
|
||||||
import android.graphics.Camera;
|
|
||||||
import android.graphics.Matrix;
|
|
||||||
import android.view.animation.Animation;
|
|
||||||
import android.view.animation.Transformation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An animation that rotates the view on the Y axis between two specified angles.
|
|
||||||
* This animation also adds a translation on the Z axis (depth) to improve the effect.
|
|
||||||
*/
|
|
||||||
public class Rotate3dAnimation extends Animation {
|
|
||||||
private final float mFromDegrees;
|
|
||||||
private final float mToDegrees;
|
|
||||||
private final float mCenterX;
|
|
||||||
private final float mCenterY;
|
|
||||||
private final float mDepthZ;
|
|
||||||
private final boolean mReverse;
|
|
||||||
private Camera mCamera;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new 3D rotation on the Y axis. The rotation is defined by its
|
|
||||||
* start angle and its end angle. Both angles are in degrees. The rotation
|
|
||||||
* is performed around a center point on the 2D space, definied by a pair
|
|
||||||
* of X and Y coordinates, called centerX and centerY. When the animation
|
|
||||||
* starts, a translation on the Z axis (depth) is performed. The length
|
|
||||||
* of the translation can be specified, as well as whether the translation
|
|
||||||
* should be reversed in time.
|
|
||||||
*
|
|
||||||
* @param fromDegrees the start angle of the 3D rotation
|
|
||||||
* @param toDegrees the end angle of the 3D rotation
|
|
||||||
* @param centerX the X center of the 3D rotation
|
|
||||||
* @param centerY the Y center of the 3D rotation
|
|
||||||
* @param reverse true if the translation should be reversed, false otherwise
|
|
||||||
*/
|
|
||||||
public Rotate3dAnimation(float fromDegrees, float toDegrees,
|
|
||||||
float centerX, float centerY, float depthZ, boolean reverse) {
|
|
||||||
mFromDegrees = fromDegrees;
|
|
||||||
mToDegrees = toDegrees;
|
|
||||||
mCenterX = centerX;
|
|
||||||
mCenterY = centerY;
|
|
||||||
mDepthZ = depthZ;
|
|
||||||
mReverse = reverse;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize(int width, int height, int parentWidth, int parentHeight) {
|
|
||||||
super.initialize(width, height, parentWidth, parentHeight);
|
|
||||||
mCamera = new Camera();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void applyTransformation(float interpolatedTime, Transformation t) {
|
|
||||||
final float fromDegrees = mFromDegrees;
|
|
||||||
float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);
|
|
||||||
|
|
||||||
final float centerX = mCenterX;
|
|
||||||
final float centerY = mCenterY;
|
|
||||||
final Camera camera = mCamera;
|
|
||||||
|
|
||||||
final Matrix matrix = t.getMatrix();
|
|
||||||
|
|
||||||
camera.save();
|
|
||||||
if (mReverse) {
|
|
||||||
camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);
|
|
||||||
} else {
|
|
||||||
camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));
|
|
||||||
}
|
|
||||||
camera.rotateY(degrees);
|
|
||||||
camera.getMatrix(matrix);
|
|
||||||
camera.restore();
|
|
||||||
|
|
||||||
matrix.preTranslate(-centerX, -centerY);
|
|
||||||
matrix.postTranslate(centerX, centerY);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
package org.torproject.android.ui;
|
|
||||||
|
|
||||||
public class VPNSetupActivity {
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,199 +0,0 @@
|
||||||
package org.torproject.android.ui.hiddenservices;
|
|
||||||
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.database.ContentObserver;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.support.design.widget.FloatingActionButton;
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
|
||||||
import android.support.v7.widget.Toolbar;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.ListView;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.google.zxing.integration.android.IntentIntegrator;
|
|
||||||
import com.google.zxing.integration.android.IntentResult;
|
|
||||||
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
import org.torproject.android.R;
|
|
||||||
import org.torproject.android.ui.hiddenservices.adapters.ClientCookiesAdapter;
|
|
||||||
import org.torproject.android.ui.hiddenservices.dialogs.AddCookieDialog;
|
|
||||||
import org.torproject.android.ui.hiddenservices.dialogs.CookieActionsDialog;
|
|
||||||
import org.torproject.android.ui.hiddenservices.dialogs.SelectCookieBackupDialog;
|
|
||||||
import org.torproject.android.ui.hiddenservices.permissions.PermissionManager;
|
|
||||||
import org.torproject.android.ui.hiddenservices.providers.CookieContentProvider;
|
|
||||||
|
|
||||||
public class ClientCookiesActivity extends AppCompatActivity {
|
|
||||||
public final int WRITE_EXTERNAL_STORAGE_FROM_COOKIE_ACTIONBAR = 3;
|
|
||||||
|
|
||||||
private ContentResolver mResolver;
|
|
||||||
private ClientCookiesAdapter mAdapter;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setContentView(R.layout.layout_activity_client_cookies);
|
|
||||||
|
|
||||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
|
||||||
setSupportActionBar(toolbar);
|
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
|
||||||
|
|
||||||
mResolver = getContentResolver();
|
|
||||||
|
|
||||||
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
|
|
||||||
fab.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
AddCookieDialog dialog = new AddCookieDialog();
|
|
||||||
dialog.show(getSupportFragmentManager(), "AddCookieDialog");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
mAdapter = new ClientCookiesAdapter(
|
|
||||||
this,
|
|
||||||
mResolver.query(CookieContentProvider.CONTENT_URI, CookieContentProvider.PROJECTION, null, null, null)
|
|
||||||
, 0);
|
|
||||||
|
|
||||||
mResolver.registerContentObserver(
|
|
||||||
CookieContentProvider.CONTENT_URI, true, new HSObserver(new Handler())
|
|
||||||
);
|
|
||||||
|
|
||||||
ListView cookies = (ListView) findViewById(R.id.clien_cookies_list);
|
|
||||||
cookies.setAdapter(mAdapter);
|
|
||||||
|
|
||||||
cookies.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
|
||||||
Cursor item = (Cursor) parent.getItemAtPosition(position);
|
|
||||||
|
|
||||||
Bundle arguments = new Bundle();
|
|
||||||
arguments.putInt(
|
|
||||||
"_id", item.getInt(item.getColumnIndex(CookieContentProvider.ClientCookie._ID))
|
|
||||||
);
|
|
||||||
|
|
||||||
arguments.putString(
|
|
||||||
"domain", item.getString(item.getColumnIndex(CookieContentProvider.ClientCookie.DOMAIN))
|
|
||||||
);
|
|
||||||
|
|
||||||
arguments.putString(
|
|
||||||
"auth_cookie_value", item.getString(item.getColumnIndex(CookieContentProvider.ClientCookie.AUTH_COOKIE_VALUE))
|
|
||||||
);
|
|
||||||
|
|
||||||
arguments.putInt(
|
|
||||||
"enabled", item.getInt(item.getColumnIndex(CookieContentProvider.ClientCookie.ENABLED))
|
|
||||||
);
|
|
||||||
|
|
||||||
CookieActionsDialog dialog = new CookieActionsDialog();
|
|
||||||
dialog.setArguments(arguments);
|
|
||||||
dialog.show(getSupportFragmentManager(), "CookieActionsDialog");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
|
||||||
getMenuInflater().inflate(R.menu.cookie_menu, menu);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
int id = item.getItemId();
|
|
||||||
|
|
||||||
if (id == R.id.cookie_restore_backup) {
|
|
||||||
if (PermissionManager.isLollipopOrHigher()
|
|
||||||
&& !PermissionManager.hasExternalWritePermission(this)) {
|
|
||||||
PermissionManager.requestExternalWritePermissions(this, WRITE_EXTERNAL_STORAGE_FROM_COOKIE_ACTIONBAR);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
SelectCookieBackupDialog dialog = new SelectCookieBackupDialog();
|
|
||||||
dialog.show(getSupportFragmentManager(), "SelectCookieBackupDialog");
|
|
||||||
|
|
||||||
} else if (id == R.id.cookie_from_qr) {
|
|
||||||
IntentIntegrator integrator = new IntentIntegrator(ClientCookiesActivity.this);
|
|
||||||
integrator.initiateScan();
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRequestPermissionsResult(int requestCode,
|
|
||||||
String permissions[], int[] grantResults) {
|
|
||||||
if (grantResults.length < 1
|
|
||||||
|| grantResults[0] != PackageManager.PERMISSION_GRANTED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (requestCode) {
|
|
||||||
case WRITE_EXTERNAL_STORAGE_FROM_COOKIE_ACTIONBAR: {
|
|
||||||
SelectCookieBackupDialog dialog = new SelectCookieBackupDialog();
|
|
||||||
dialog.show(getSupportFragmentManager(), "SelectCookieBackupDialog");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CookieActionsDialog.WRITE_EXTERNAL_STORAGE_FROM_COOKIE_ACTION_DIALOG: {
|
|
||||||
Toast.makeText(this, R.string.click_again_for_backup, Toast.LENGTH_LONG).show();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onActivityResult(int request, int response, Intent data) {
|
|
||||||
super.onActivityResult(request, response, data);
|
|
||||||
|
|
||||||
IntentResult scanResult = IntentIntegrator.parseActivityResult(request, response, data);
|
|
||||||
|
|
||||||
if (scanResult == null) return;
|
|
||||||
|
|
||||||
String results = scanResult.getContents();
|
|
||||||
|
|
||||||
if (results == null || results.length() < 1) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
JSONObject savedValues = new JSONObject(results);
|
|
||||||
ContentValues fields = new ContentValues();
|
|
||||||
|
|
||||||
fields.put(
|
|
||||||
CookieContentProvider.ClientCookie.DOMAIN,
|
|
||||||
savedValues.getString(CookieContentProvider.ClientCookie.DOMAIN)
|
|
||||||
);
|
|
||||||
|
|
||||||
fields.put(
|
|
||||||
CookieContentProvider.ClientCookie.AUTH_COOKIE_VALUE,
|
|
||||||
savedValues.getString(CookieContentProvider.ClientCookie.AUTH_COOKIE_VALUE)
|
|
||||||
);
|
|
||||||
|
|
||||||
mResolver.insert(CookieContentProvider.CONTENT_URI, fields);
|
|
||||||
|
|
||||||
} catch (JSONException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
Toast.makeText(this, R.string.error, Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class HSObserver extends ContentObserver {
|
|
||||||
HSObserver(Handler handler) {
|
|
||||||
super(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onChange(boolean selfChange) {
|
|
||||||
mAdapter.changeCursor(mResolver.query(
|
|
||||||
CookieContentProvider.CONTENT_URI, CookieContentProvider.PROJECTION, null, null, null
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,214 +0,0 @@
|
||||||
package org.torproject.android.ui.hiddenservices;
|
|
||||||
|
|
||||||
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.database.ContentObserver;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.support.design.widget.FloatingActionButton;
|
|
||||||
import android.support.v4.view.MenuItemCompat;
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
|
||||||
import android.support.v7.widget.Toolbar;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.ListView;
|
|
||||||
import android.widget.Spinner;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.torproject.android.R;
|
|
||||||
import org.torproject.android.ui.hiddenservices.adapters.OnionListAdapter;
|
|
||||||
import org.torproject.android.ui.hiddenservices.dialogs.HSActionsDialog;
|
|
||||||
import org.torproject.android.ui.hiddenservices.dialogs.HSDataDialog;
|
|
||||||
import org.torproject.android.ui.hiddenservices.dialogs.SelectHSBackupDialog;
|
|
||||||
import org.torproject.android.ui.hiddenservices.permissions.PermissionManager;
|
|
||||||
import org.torproject.android.ui.hiddenservices.providers.HSContentProvider;
|
|
||||||
|
|
||||||
public class HiddenServicesActivity extends AppCompatActivity {
|
|
||||||
public final int WRITE_EXTERNAL_STORAGE_FROM_ACTIONBAR = 1;
|
|
||||||
private ContentResolver mResolver;
|
|
||||||
private OnionListAdapter mAdapter;
|
|
||||||
private FloatingActionButton fab;
|
|
||||||
|
|
||||||
private String mWhere = HSContentProvider.HiddenService.CREATED_BY_USER + "=1";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setContentView(R.layout.layout_hs_list_view);
|
|
||||||
|
|
||||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
|
||||||
setSupportActionBar(toolbar);
|
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
|
||||||
|
|
||||||
mResolver = getContentResolver();
|
|
||||||
|
|
||||||
fab = (FloatingActionButton) findViewById(R.id.fab);
|
|
||||||
fab.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
HSDataDialog dialog = new HSDataDialog();
|
|
||||||
dialog.show(getSupportFragmentManager(), "HSDataDialog");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
mAdapter = new OnionListAdapter(
|
|
||||||
this,
|
|
||||||
mResolver.query(
|
|
||||||
HSContentProvider.CONTENT_URI, HSContentProvider.PROJECTION, mWhere, null, null
|
|
||||||
),
|
|
||||||
0
|
|
||||||
);
|
|
||||||
|
|
||||||
mResolver.registerContentObserver(
|
|
||||||
HSContentProvider.CONTENT_URI, true, new HSObserver(new Handler())
|
|
||||||
);
|
|
||||||
|
|
||||||
ListView onion_list = (ListView) findViewById(R.id.onion_list);
|
|
||||||
onion_list.setAdapter(mAdapter);
|
|
||||||
|
|
||||||
onion_list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
|
||||||
Cursor item = (Cursor) parent.getItemAtPosition(position);
|
|
||||||
|
|
||||||
Bundle arguments = new Bundle();
|
|
||||||
arguments.putInt(
|
|
||||||
"_id", item.getInt(item.getColumnIndex(HSContentProvider.HiddenService._ID))
|
|
||||||
);
|
|
||||||
|
|
||||||
arguments.putString(
|
|
||||||
"port", item.getString(item.getColumnIndex(HSContentProvider.HiddenService.PORT))
|
|
||||||
);
|
|
||||||
|
|
||||||
arguments.putString(
|
|
||||||
"onion", item.getString(item.getColumnIndex(HSContentProvider.HiddenService.DOMAIN))
|
|
||||||
);
|
|
||||||
|
|
||||||
arguments.putInt(
|
|
||||||
"auth_cookie", item.getInt(item.getColumnIndex(HSContentProvider.HiddenService.AUTH_COOKIE))
|
|
||||||
);
|
|
||||||
|
|
||||||
arguments.putString(
|
|
||||||
"auth_cookie_value", item.getString(item.getColumnIndex(HSContentProvider.HiddenService.AUTH_COOKIE_VALUE))
|
|
||||||
);
|
|
||||||
|
|
||||||
HSActionsDialog dialog = new HSActionsDialog();
|
|
||||||
dialog.setArguments(arguments);
|
|
||||||
dialog.show(getSupportFragmentManager(), "HSActionsDialog");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
|
||||||
getMenuInflater().inflate(R.menu.hs_menu, menu);
|
|
||||||
|
|
||||||
MenuItem item = menu.findItem(R.id.hs_type);
|
|
||||||
Spinner spinner = (Spinner) MenuItemCompat.getActionView(item);
|
|
||||||
|
|
||||||
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
|
|
||||||
this, R.array.array_hs_types, android.R.layout.simple_spinner_item);
|
|
||||||
|
|
||||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
|
||||||
|
|
||||||
spinner.setAdapter(adapter);
|
|
||||||
|
|
||||||
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onItemSelected(AdapterView<?> arg0, View v, int pos, long id) {
|
|
||||||
if (pos == 0) {
|
|
||||||
mWhere = HSContentProvider.HiddenService.CREATED_BY_USER + "=1";
|
|
||||||
fab.show();
|
|
||||||
} else {
|
|
||||||
mWhere = HSContentProvider.HiddenService.CREATED_BY_USER + "=0";
|
|
||||||
fab.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
mAdapter.changeCursor(mResolver.query(
|
|
||||||
HSContentProvider.CONTENT_URI, HSContentProvider.PROJECTION, mWhere, null, null
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNothingSelected(AdapterView<?> arg0) {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
int id = item.getItemId();
|
|
||||||
|
|
||||||
if (id == R.id.menu_restore_backup) {
|
|
||||||
if (PermissionManager.isLollipopOrHigher()
|
|
||||||
&& !PermissionManager.hasExternalWritePermission(this)) {
|
|
||||||
PermissionManager.requestExternalWritePermissions(this, WRITE_EXTERNAL_STORAGE_FROM_ACTIONBAR);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
SelectHSBackupDialog dialog = new SelectHSBackupDialog();
|
|
||||||
dialog.show(getSupportFragmentManager(), "SelectHSBackupDialog");
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRequestPermissionsResult(int requestCode,
|
|
||||||
String permissions[], int[] grantResults) {
|
|
||||||
if (grantResults.length < 1
|
|
||||||
|| grantResults[0] != PackageManager.PERMISSION_GRANTED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (requestCode) {
|
|
||||||
case WRITE_EXTERNAL_STORAGE_FROM_ACTIONBAR: {
|
|
||||||
SelectHSBackupDialog dialog = new SelectHSBackupDialog();
|
|
||||||
dialog.show(getSupportFragmentManager(), "SelectHSBackupDialog");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case HSActionsDialog.WRITE_EXTERNAL_STORAGE_FROM_ACTION_DIALOG: {
|
|
||||||
Toast.makeText(this, R.string.click_again_for_backup, Toast.LENGTH_LONG).show();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class HSObserver extends ContentObserver {
|
|
||||||
HSObserver(Handler handler) {
|
|
||||||
super(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onChange(boolean selfChange) {
|
|
||||||
mAdapter.changeCursor(mResolver.query(
|
|
||||||
HSContentProvider.CONTENT_URI, HSContentProvider.PROJECTION, mWhere, null, null
|
|
||||||
));
|
|
||||||
|
|
||||||
if (PermissionManager.isLollipopOrHigher()) {
|
|
||||||
Cursor active = mResolver.query(
|
|
||||||
HSContentProvider.CONTENT_URI, HSContentProvider.PROJECTION, HSContentProvider.HiddenService.ENABLED + "=1", null, null
|
|
||||||
);
|
|
||||||
|
|
||||||
if (active == null) return;
|
|
||||||
|
|
||||||
if (active.getCount() > 0) // Call only if there running services
|
|
||||||
PermissionManager.requestBatteryPermmssions(HiddenServicesActivity.this, getApplicationContext());
|
|
||||||
else // Drop whe not needed
|
|
||||||
PermissionManager.requestDropBatteryPermmssions(HiddenServicesActivity.this, getApplicationContext());
|
|
||||||
|
|
||||||
active.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
package org.torproject.android.ui.hiddenservices.adapters;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import org.torproject.android.R;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class BackupAdapter extends ArrayAdapter<File> {
|
|
||||||
private int mResource;
|
|
||||||
|
|
||||||
public BackupAdapter(Context context, int resource) {
|
|
||||||
super(context, resource);
|
|
||||||
mResource = resource;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BackupAdapter(Context context, int resource, List<File> zips) {
|
|
||||||
super(context, resource, zips);
|
|
||||||
mResource = resource;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
|
||||||
|
|
||||||
View v = convertView;
|
|
||||||
|
|
||||||
if (v == null) {
|
|
||||||
LayoutInflater vi;
|
|
||||||
vi = LayoutInflater.from(getContext());
|
|
||||||
v = vi.inflate(mResource, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
File p = getItem(position);
|
|
||||||
|
|
||||||
if (p != null) {
|
|
||||||
TextView name = (TextView) v.findViewById(R.id.backup_name);
|
|
||||||
|
|
||||||
if (name != null)
|
|
||||||
name.setText(p.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
package org.torproject.android.ui.hiddenservices.adapters;
|
|
||||||
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.support.v4.widget.CursorAdapter;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.CompoundButton;
|
|
||||||
import android.widget.Switch;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.torproject.android.R;
|
|
||||||
import org.torproject.android.ui.hiddenservices.providers.CookieContentProvider;
|
|
||||||
|
|
||||||
public class ClientCookiesAdapter extends CursorAdapter {
|
|
||||||
private LayoutInflater cursorInflater;
|
|
||||||
|
|
||||||
public ClientCookiesAdapter(Context context, Cursor c, int flags) {
|
|
||||||
super(context, c, flags);
|
|
||||||
|
|
||||||
cursorInflater = (LayoutInflater) context.getSystemService(
|
|
||||||
Context.LAYOUT_INFLATER_SERVICE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bindView(View view, Context context, Cursor cursor) {
|
|
||||||
final Context mContext = context;
|
|
||||||
int id = cursor.getInt(cursor.getColumnIndex(CookieContentProvider.ClientCookie._ID));
|
|
||||||
final String where = CookieContentProvider.ClientCookie._ID + "=" + id;
|
|
||||||
|
|
||||||
TextView domain = (TextView) view.findViewById(R.id.cookie_onion);
|
|
||||||
domain.setText(cursor.getString(cursor.getColumnIndex(CookieContentProvider.ClientCookie.DOMAIN)));
|
|
||||||
|
|
||||||
Switch enabled = (Switch) view.findViewById(R.id.cookie_switch);
|
|
||||||
enabled.setChecked(
|
|
||||||
cursor.getInt(cursor.getColumnIndex(CookieContentProvider.ClientCookie.ENABLED)) == 1
|
|
||||||
);
|
|
||||||
|
|
||||||
enabled.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
|
||||||
@Override
|
|
||||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
|
||||||
ContentResolver resolver = mContext.getContentResolver();
|
|
||||||
ContentValues fields = new ContentValues();
|
|
||||||
fields.put(CookieContentProvider.ClientCookie.ENABLED, isChecked);
|
|
||||||
resolver.update(
|
|
||||||
CookieContentProvider.CONTENT_URI, fields, where, null
|
|
||||||
);
|
|
||||||
|
|
||||||
Toast.makeText(
|
|
||||||
mContext, R.string.please_restart_Orbot_to_enable_the_changes, Toast.LENGTH_LONG
|
|
||||||
).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
|
||||||
return cursorInflater.inflate(R.layout.layout_client_cookie_list_item, parent, false);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
package org.torproject.android.ui.hiddenservices.adapters;
|
|
||||||
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.support.v4.widget.CursorAdapter;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.CompoundButton;
|
|
||||||
import android.widget.Switch;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.torproject.android.R;
|
|
||||||
import org.torproject.android.ui.hiddenservices.providers.HSContentProvider;
|
|
||||||
|
|
||||||
public class OnionListAdapter extends CursorAdapter {
|
|
||||||
private LayoutInflater cursorInflater;
|
|
||||||
|
|
||||||
public OnionListAdapter(Context context, Cursor c, int flags) {
|
|
||||||
super(context, c, flags);
|
|
||||||
|
|
||||||
cursorInflater = (LayoutInflater) context.getSystemService(
|
|
||||||
Context.LAYOUT_INFLATER_SERVICE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bindView(View view, Context context, Cursor cursor) {
|
|
||||||
final Context mContext = context;
|
|
||||||
int id = cursor.getInt(cursor.getColumnIndex(HSContentProvider.HiddenService._ID));
|
|
||||||
final String where = HSContentProvider.HiddenService._ID + "=" + id;
|
|
||||||
|
|
||||||
TextView port = (TextView) view.findViewById(R.id.hs_port);
|
|
||||||
port.setText(cursor.getString(cursor.getColumnIndex(HSContentProvider.HiddenService.PORT)));
|
|
||||||
TextView name = (TextView) view.findViewById(R.id.hs_name);
|
|
||||||
name.setText(cursor.getString(cursor.getColumnIndex(HSContentProvider.HiddenService.NAME)));
|
|
||||||
TextView domain = (TextView) view.findViewById(R.id.hs_onion);
|
|
||||||
domain.setText(cursor.getString(cursor.getColumnIndex(HSContentProvider.HiddenService.DOMAIN)));
|
|
||||||
|
|
||||||
Switch enabled = (Switch) view.findViewById(R.id.hs_switch);
|
|
||||||
enabled.setChecked(
|
|
||||||
cursor.getInt(cursor.getColumnIndex(HSContentProvider.HiddenService.ENABLED)) == 1
|
|
||||||
);
|
|
||||||
|
|
||||||
enabled.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
|
||||||
@Override
|
|
||||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
|
||||||
ContentResolver resolver = mContext.getContentResolver();
|
|
||||||
ContentValues fields = new ContentValues();
|
|
||||||
fields.put(HSContentProvider.HiddenService.ENABLED, isChecked);
|
|
||||||
resolver.update(
|
|
||||||
HSContentProvider.CONTENT_URI, fields, where, null
|
|
||||||
);
|
|
||||||
|
|
||||||
Toast.makeText(
|
|
||||||
mContext, R.string.please_restart_Orbot_to_enable_the_changes, Toast.LENGTH_LONG
|
|
||||||
).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
|
||||||
return cursorInflater.inflate(R.layout.layout_hs_list_item, parent, false);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,336 +0,0 @@
|
||||||
package org.torproject.android.ui.hiddenservices.backup;
|
|
||||||
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.ParcelFileDescriptor;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
import org.torproject.android.R;
|
|
||||||
import org.torproject.android.service.TorServiceConstants;
|
|
||||||
import org.torproject.android.ui.hiddenservices.providers.CookieContentProvider;
|
|
||||||
import org.torproject.android.ui.hiddenservices.providers.HSContentProvider;
|
|
||||||
import org.torproject.android.ui.hiddenservices.storage.ExternalStorage;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.FileWriter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.nio.MappedByteBuffer;
|
|
||||||
import java.nio.channels.FileChannel;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
|
|
||||||
public class BackupUtils {
|
|
||||||
private final String configFileName = "config.json";
|
|
||||||
private Context mContext;
|
|
||||||
private ContentResolver mResolver;
|
|
||||||
|
|
||||||
public BackupUtils(Context context) {
|
|
||||||
mContext = context;
|
|
||||||
mResolver = mContext.getContentResolver();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String createZipBackup(Integer port) {
|
|
||||||
File mHSBasePath = new File(
|
|
||||||
mContext.getFilesDir().getAbsolutePath(),
|
|
||||||
TorServiceConstants.HIDDEN_SERVICES_DIR
|
|
||||||
);
|
|
||||||
|
|
||||||
String configFilePath = mHSBasePath + "/hs" + port + "/" + configFileName;
|
|
||||||
String hostnameFilePath = mHSBasePath + "/hs" + port + "/hostname";
|
|
||||||
String keyFilePath = mHSBasePath + "/hs" + port + "/private_key";
|
|
||||||
|
|
||||||
File storage_path = ExternalStorage.getOrCreateBackupDir();
|
|
||||||
|
|
||||||
if (storage_path == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
Cursor portData = mResolver.query(
|
|
||||||
HSContentProvider.CONTENT_URI,
|
|
||||||
HSContentProvider.PROJECTION,
|
|
||||||
HSContentProvider.HiddenService.PORT + "=" + port,
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
JSONObject config = new JSONObject();
|
|
||||||
try {
|
|
||||||
if (portData == null || portData.getCount() != 1)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
portData.moveToNext();
|
|
||||||
|
|
||||||
config.put(
|
|
||||||
HSContentProvider.HiddenService.NAME,
|
|
||||||
portData.getString(portData.getColumnIndex(HSContentProvider.HiddenService.NAME))
|
|
||||||
);
|
|
||||||
|
|
||||||
config.put(
|
|
||||||
HSContentProvider.HiddenService.PORT,
|
|
||||||
portData.getInt(portData.getColumnIndex(HSContentProvider.HiddenService.PORT))
|
|
||||||
);
|
|
||||||
|
|
||||||
config.put(
|
|
||||||
HSContentProvider.HiddenService.ONION_PORT,
|
|
||||||
portData.getInt(portData.getColumnIndex(HSContentProvider.HiddenService.ONION_PORT))
|
|
||||||
);
|
|
||||||
|
|
||||||
config.put(
|
|
||||||
HSContentProvider.HiddenService.DOMAIN,
|
|
||||||
portData.getString(portData.getColumnIndex(HSContentProvider.HiddenService.DOMAIN))
|
|
||||||
);
|
|
||||||
|
|
||||||
config.put(
|
|
||||||
HSContentProvider.HiddenService.AUTH_COOKIE,
|
|
||||||
portData.getInt(portData.getColumnIndex(HSContentProvider.HiddenService.AUTH_COOKIE))
|
|
||||||
);
|
|
||||||
|
|
||||||
config.put(
|
|
||||||
HSContentProvider.HiddenService.AUTH_COOKIE_VALUE,
|
|
||||||
portData.getString(portData.getColumnIndex(HSContentProvider.HiddenService.AUTH_COOKIE_VALUE))
|
|
||||||
);
|
|
||||||
|
|
||||||
config.put(
|
|
||||||
HSContentProvider.HiddenService.CREATED_BY_USER,
|
|
||||||
portData.getInt(portData.getColumnIndex(HSContentProvider.HiddenService.CREATED_BY_USER))
|
|
||||||
);
|
|
||||||
|
|
||||||
config.put(
|
|
||||||
HSContentProvider.HiddenService.ENABLED,
|
|
||||||
portData.getInt(portData.getColumnIndex(HSContentProvider.HiddenService.ENABLED))
|
|
||||||
);
|
|
||||||
} catch (JSONException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
} catch (NullPointerException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
portData.close();
|
|
||||||
|
|
||||||
try {
|
|
||||||
FileWriter file = new FileWriter(configFilePath);
|
|
||||||
file.write(config.toString());
|
|
||||||
file.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
String zip_path = storage_path.getAbsolutePath() + "/hs" + port + ".zip";
|
|
||||||
String files[] = {hostnameFilePath, keyFilePath, configFilePath};
|
|
||||||
|
|
||||||
ZipIt zip = new ZipIt(files, zip_path);
|
|
||||||
|
|
||||||
if (!zip.zip())
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return zip_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void restoreZipBackup(File backup) {
|
|
||||||
|
|
||||||
File mHSBasePath = new File(
|
|
||||||
mContext.getFilesDir().getAbsolutePath(),
|
|
||||||
TorServiceConstants.HIDDEN_SERVICES_DIR
|
|
||||||
);
|
|
||||||
|
|
||||||
int port;
|
|
||||||
Cursor service;
|
|
||||||
String backupName = backup.getName();
|
|
||||||
String hsDir = backupName.substring(0, backupName.lastIndexOf('.'));
|
|
||||||
String configFilePath = mHSBasePath + "/" + hsDir + "/" + configFileName;
|
|
||||||
String jString = null;
|
|
||||||
|
|
||||||
File hsPath = new File(mHSBasePath.getAbsolutePath(), hsDir);
|
|
||||||
if (!hsPath.isDirectory())
|
|
||||||
hsPath.mkdirs();
|
|
||||||
|
|
||||||
ZipIt zip = new ZipIt(null, backup.getAbsolutePath());
|
|
||||||
zip.unzip(hsPath.getAbsolutePath());
|
|
||||||
|
|
||||||
File config = new File(configFilePath);
|
|
||||||
FileInputStream stream;
|
|
||||||
|
|
||||||
try {
|
|
||||||
stream = new FileInputStream(config);
|
|
||||||
FileChannel fc = stream.getChannel();
|
|
||||||
MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
|
|
||||||
jString = Charset.defaultCharset().decode(bb).toString();
|
|
||||||
stream.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jString == null)
|
|
||||||
Toast.makeText(mContext, R.string.error, Toast.LENGTH_LONG).show();
|
|
||||||
|
|
||||||
try {
|
|
||||||
JSONObject savedValues = new JSONObject(jString);
|
|
||||||
ContentValues fields = new ContentValues();
|
|
||||||
|
|
||||||
fields.put(
|
|
||||||
HSContentProvider.HiddenService.NAME,
|
|
||||||
savedValues.getString(HSContentProvider.HiddenService.NAME)
|
|
||||||
);
|
|
||||||
|
|
||||||
fields.put(
|
|
||||||
HSContentProvider.HiddenService.ONION_PORT,
|
|
||||||
savedValues.getInt(HSContentProvider.HiddenService.ONION_PORT)
|
|
||||||
);
|
|
||||||
|
|
||||||
fields.put(
|
|
||||||
HSContentProvider.HiddenService.DOMAIN,
|
|
||||||
savedValues.getString(HSContentProvider.HiddenService.DOMAIN)
|
|
||||||
);
|
|
||||||
|
|
||||||
fields.put(
|
|
||||||
HSContentProvider.HiddenService.AUTH_COOKIE,
|
|
||||||
savedValues.getInt(HSContentProvider.HiddenService.AUTH_COOKIE)
|
|
||||||
);
|
|
||||||
|
|
||||||
fields.put(
|
|
||||||
HSContentProvider.HiddenService.CREATED_BY_USER,
|
|
||||||
savedValues.getInt(HSContentProvider.HiddenService.CREATED_BY_USER)
|
|
||||||
);
|
|
||||||
|
|
||||||
fields.put(
|
|
||||||
HSContentProvider.HiddenService.ENABLED,
|
|
||||||
savedValues.getInt(HSContentProvider.HiddenService.ENABLED)
|
|
||||||
);
|
|
||||||
|
|
||||||
port = savedValues.getInt(HSContentProvider.HiddenService.PORT);
|
|
||||||
fields.put(HSContentProvider.HiddenService.PORT, port);
|
|
||||||
|
|
||||||
service = mResolver.query(
|
|
||||||
HSContentProvider.CONTENT_URI,
|
|
||||||
HSContentProvider.PROJECTION,
|
|
||||||
HSContentProvider.HiddenService.PORT + "=" + port,
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
if (service == null || service.getCount() == 0) {
|
|
||||||
mResolver.insert(HSContentProvider.CONTENT_URI, fields);
|
|
||||||
} else {
|
|
||||||
mResolver.update(
|
|
||||||
HSContentProvider.CONTENT_URI,
|
|
||||||
fields,
|
|
||||||
HSContentProvider.HiddenService.PORT + "=" + port,
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
service.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (JSONException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
Toast.makeText(mContext, R.string.error, Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
Toast.makeText(mContext, R.string.backup_restored, Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void restoreKeyBackup(int hsPort, Uri hsKeyPath) {
|
|
||||||
File mHSBasePath = new File(
|
|
||||||
mContext.getFilesDir().getAbsolutePath(),
|
|
||||||
TorServiceConstants.HIDDEN_SERVICES_DIR
|
|
||||||
);
|
|
||||||
|
|
||||||
File serviceDir = new File(mHSBasePath, "hs" + hsPort);
|
|
||||||
|
|
||||||
if (!serviceDir.isDirectory())
|
|
||||||
serviceDir.mkdirs();
|
|
||||||
|
|
||||||
try {
|
|
||||||
ParcelFileDescriptor mInputPFD = mContext.getContentResolver().openFileDescriptor(hsKeyPath, "r");
|
|
||||||
InputStream fileStream = new FileInputStream(mInputPFD.getFileDescriptor());
|
|
||||||
OutputStream file = new FileOutputStream(serviceDir.getAbsolutePath() + "/private_key");
|
|
||||||
|
|
||||||
byte[] buffer = new byte[1024];
|
|
||||||
int length;
|
|
||||||
while ((length = fileStream.read(buffer)) > 0) {
|
|
||||||
file.write(buffer, 0, length);
|
|
||||||
}
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
} catch (IOException | NullPointerException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void restoreCookieBackup(File p) {
|
|
||||||
File config = new File(p.getAbsolutePath());
|
|
||||||
FileInputStream stream;
|
|
||||||
String jString = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
stream = new FileInputStream(config);
|
|
||||||
FileChannel fc = stream.getChannel();
|
|
||||||
MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
|
|
||||||
jString = Charset.defaultCharset().decode(bb).toString();
|
|
||||||
stream.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jString == null)
|
|
||||||
Toast.makeText(mContext, R.string.error, Toast.LENGTH_LONG).show();
|
|
||||||
|
|
||||||
try {
|
|
||||||
JSONObject savedValues = new JSONObject(jString);
|
|
||||||
ContentValues fields = new ContentValues();
|
|
||||||
|
|
||||||
fields.put(
|
|
||||||
CookieContentProvider.ClientCookie.DOMAIN,
|
|
||||||
savedValues.getString(CookieContentProvider.ClientCookie.DOMAIN)
|
|
||||||
);
|
|
||||||
|
|
||||||
fields.put(
|
|
||||||
CookieContentProvider.ClientCookie.AUTH_COOKIE_VALUE,
|
|
||||||
savedValues.getString(CookieContentProvider.ClientCookie.AUTH_COOKIE_VALUE)
|
|
||||||
);
|
|
||||||
|
|
||||||
fields.put(
|
|
||||||
CookieContentProvider.ClientCookie.ENABLED,
|
|
||||||
savedValues.getInt(CookieContentProvider.ClientCookie.ENABLED)
|
|
||||||
);
|
|
||||||
|
|
||||||
mResolver.insert(CookieContentProvider.CONTENT_URI, fields);
|
|
||||||
|
|
||||||
} catch (JSONException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
Toast.makeText(mContext, R.string.error, Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
Toast.makeText(mContext, R.string.backup_restored, Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String createCookieBackup(String domain, String cookie, Integer enabled) {
|
|
||||||
File storage_path = ExternalStorage.getOrCreateBackupDir();
|
|
||||||
String backupFile = storage_path.getAbsolutePath() + '/' + domain.replace(".onion", ".json");
|
|
||||||
|
|
||||||
JSONObject backup = new JSONObject();
|
|
||||||
try {
|
|
||||||
backup.put(CookieContentProvider.ClientCookie.DOMAIN, domain);
|
|
||||||
backup.put(CookieContentProvider.ClientCookie.AUTH_COOKIE_VALUE, cookie);
|
|
||||||
backup.put(CookieContentProvider.ClientCookie.ENABLED, enabled);
|
|
||||||
FileWriter file = new FileWriter(backupFile);
|
|
||||||
file.write(backup.toString());
|
|
||||||
file.close();
|
|
||||||
} catch (JSONException | IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return backupFile;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
package org.torproject.android.ui.hiddenservices.backup;
|
|
||||||
|
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.BufferedOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.zip.ZipEntry;
|
|
||||||
import java.util.zip.ZipInputStream;
|
|
||||||
import java.util.zip.ZipOutputStream;
|
|
||||||
|
|
||||||
public class ZipIt {
|
|
||||||
private static final int BUFFER = 2048;
|
|
||||||
|
|
||||||
private String[] _files;
|
|
||||||
private String _zipFile;
|
|
||||||
|
|
||||||
public ZipIt(@Nullable String[] files, @NonNull String zipFile) {
|
|
||||||
_files = files;
|
|
||||||
_zipFile = zipFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean zip() {
|
|
||||||
try {
|
|
||||||
BufferedInputStream origin;
|
|
||||||
FileOutputStream dest = new FileOutputStream(_zipFile);
|
|
||||||
|
|
||||||
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest));
|
|
||||||
|
|
||||||
byte data[] = new byte[BUFFER];
|
|
||||||
|
|
||||||
for (String _file : _files) {
|
|
||||||
FileInputStream fi = new FileInputStream(_file);
|
|
||||||
origin = new BufferedInputStream(fi, BUFFER);
|
|
||||||
ZipEntry entry = new ZipEntry(_file.substring(_file.lastIndexOf("/") + 1));
|
|
||||||
out.putNextEntry(entry);
|
|
||||||
int count;
|
|
||||||
while ((count = origin.read(data, 0, BUFFER)) != -1) {
|
|
||||||
out.write(data, 0, count);
|
|
||||||
}
|
|
||||||
origin.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
out.close();
|
|
||||||
} catch (Exception e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean unzip(String output_path) {
|
|
||||||
InputStream is;
|
|
||||||
ZipInputStream zis;
|
|
||||||
|
|
||||||
try {
|
|
||||||
String filename;
|
|
||||||
is = new FileInputStream(_zipFile);
|
|
||||||
zis = new ZipInputStream(new BufferedInputStream(is));
|
|
||||||
ZipEntry ze;
|
|
||||||
byte[] buffer = new byte[1024];
|
|
||||||
int count;
|
|
||||||
|
|
||||||
while ((ze = zis.getNextEntry()) != null) {
|
|
||||||
// zapis do souboru
|
|
||||||
filename = ze.getName();
|
|
||||||
|
|
||||||
// Need to create directories if not exists, or
|
|
||||||
// it will generate an Exception...
|
|
||||||
if (ze.isDirectory()) {
|
|
||||||
File fmd = new File(output_path + "/" + filename);
|
|
||||||
fmd.mkdirs();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
FileOutputStream fout = new FileOutputStream(output_path + "/" + filename);
|
|
||||||
|
|
||||||
// cteni zipu a zapis
|
|
||||||
while ((count = zis.read(buffer)) != -1) {
|
|
||||||
fout.write(buffer, 0, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
fout.close();
|
|
||||||
zis.closeEntry();
|
|
||||||
}
|
|
||||||
|
|
||||||
zis.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
package org.torproject.android.ui.hiddenservices.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";
|
|
||||||
public static final String HS_CLIENT_COOKIE_TABLE_NAME = "hs_client_cookie";
|
|
||||||
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, " +
|
|
||||||
"onion_port INTEGER, " +
|
|
||||||
"auth_cookie INTEGER DEFAULT 0, " +
|
|
||||||
"auth_cookie_value TEXT, " +
|
|
||||||
"created_by_user INTEGER DEFAULT 0, " +
|
|
||||||
"enabled INTEGER DEFAULT 1, " +
|
|
||||||
"port INTEGER);";
|
|
||||||
|
|
||||||
private static final String HS_CLIENT_COOKIE_TABLE_CREATE =
|
|
||||||
"CREATE TABLE " + HS_CLIENT_COOKIE_TABLE_NAME + " (" +
|
|
||||||
"_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
|
|
||||||
"domain TEXT, " +
|
|
||||||
"auth_cookie_value TEXT, " +
|
|
||||||
"enabled INTEGER DEFAULT 1);";
|
|
||||||
|
|
||||||
public HSDatabase(Context context) {
|
|
||||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(SQLiteDatabase db) {
|
|
||||||
db.execSQL(HS_DATA_TABLE_CREATE);
|
|
||||||
db.execSQL(HS_CLIENT_COOKIE_TABLE_CREATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,84 +0,0 @@
|
||||||
package org.torproject.android.ui.hiddenservices.dialogs;
|
|
||||||
|
|
||||||
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v4.app.DialogFragment;
|
|
||||||
import android.support.v7.app.AlertDialog;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.torproject.android.R;
|
|
||||||
import org.torproject.android.ui.hiddenservices.providers.CookieContentProvider;
|
|
||||||
|
|
||||||
public class AddCookieDialog extends DialogFragment {
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
|
||||||
|
|
||||||
final View dialog_view = getActivity().getLayoutInflater().inflate(R.layout.layout_add_client_cookie_dialog, null);
|
|
||||||
|
|
||||||
final AlertDialog addCookieDialog = new AlertDialog.Builder(getActivity())
|
|
||||||
.setView(dialog_view)
|
|
||||||
.setTitle(R.string.client_cookies)
|
|
||||||
.create();
|
|
||||||
|
|
||||||
Button save = (Button) dialog_view.findViewById(R.id.cookie_dialog_save);
|
|
||||||
save.setOnClickListener(new View.OnClickListener() {
|
|
||||||
public void onClick(View v) {
|
|
||||||
String onion = ((EditText) dialog_view.findViewById(R.id.cookie_onion)).getText().toString();
|
|
||||||
String cookie = ((EditText) dialog_view.findViewById(R.id.cookie_value)).getText().toString();
|
|
||||||
|
|
||||||
if (checkInput(onion, cookie)) {
|
|
||||||
saveData(onion, cookie);
|
|
||||||
Toast.makeText(
|
|
||||||
v.getContext(), R.string.please_restart_Orbot_to_enable_the_changes, Toast.LENGTH_LONG
|
|
||||||
).show();
|
|
||||||
addCookieDialog.dismiss();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Button cancel = (Button) dialog_view.findViewById(R.id.cookie_dialog_cancel);
|
|
||||||
cancel.setOnClickListener(new View.OnClickListener() {
|
|
||||||
public void onClick(View v) {
|
|
||||||
addCookieDialog.cancel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return addCookieDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkInput(String onion, String cookie) {
|
|
||||||
|
|
||||||
boolean is_set = ((onion != null && onion.length() > 0) && (cookie != null && cookie.length() > 0));
|
|
||||||
if (!is_set) {
|
|
||||||
Toast.makeText(getContext(), R.string.fields_can_t_be_empty, Toast.LENGTH_SHORT).show();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!onion.matches("([a-z0-9]{16})\\.onion")) {
|
|
||||||
Toast.makeText(getContext(), R.string.invalid_onion_address, Toast.LENGTH_SHORT).show();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveData(String domain, String cookie) {
|
|
||||||
|
|
||||||
ContentValues fields = new ContentValues();
|
|
||||||
fields.put(CookieContentProvider.ClientCookie.DOMAIN, domain);
|
|
||||||
fields.put(CookieContentProvider.ClientCookie.AUTH_COOKIE_VALUE, cookie);
|
|
||||||
|
|
||||||
ContentResolver cr = getContext().getContentResolver();
|
|
||||||
|
|
||||||
cr.insert(CookieContentProvider.CONTENT_URI, fields);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,95 +0,0 @@
|
||||||
package org.torproject.android.ui.hiddenservices.dialogs;
|
|
||||||
|
|
||||||
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v4.app.DialogFragment;
|
|
||||||
import android.support.v7.app.AlertDialog;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.torproject.android.R;
|
|
||||||
import org.torproject.android.ui.hiddenservices.backup.BackupUtils;
|
|
||||||
import org.torproject.android.ui.hiddenservices.permissions.PermissionManager;
|
|
||||||
|
|
||||||
public class CookieActionsDialog extends DialogFragment {
|
|
||||||
public static final int WRITE_EXTERNAL_STORAGE_FROM_COOKIE_ACTION_DIALOG = 4;
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
|
||||||
final Bundle arguments = getArguments();
|
|
||||||
|
|
||||||
final View dialog_view = getActivity().getLayoutInflater().inflate(R.layout.layout_cookie_actions, null);
|
|
||||||
final AlertDialog actionDialog = new AlertDialog.Builder(getActivity())
|
|
||||||
.setView(dialog_view)
|
|
||||||
.setTitle(R.string.client_cookies)
|
|
||||||
.create();
|
|
||||||
|
|
||||||
Button backup = (Button) dialog_view.findViewById(R.id.btn_cookie_backup);
|
|
||||||
backup.setOnClickListener(new View.OnClickListener() {
|
|
||||||
public void onClick(View v) {
|
|
||||||
Context mContext = v.getContext();
|
|
||||||
|
|
||||||
if (PermissionManager.isLollipopOrHigher()
|
|
||||||
&& !PermissionManager.hasExternalWritePermission(mContext)) {
|
|
||||||
|
|
||||||
PermissionManager.requestExternalWritePermissions(
|
|
||||||
getActivity(), WRITE_EXTERNAL_STORAGE_FROM_COOKIE_ACTION_DIALOG);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
BackupUtils backup_utils = new BackupUtils(mContext);
|
|
||||||
String backupPath = backup_utils.createCookieBackup(
|
|
||||||
arguments.getString("domain"),
|
|
||||||
arguments.getString("auth_cookie_value"),
|
|
||||||
arguments.getInt("enabled")
|
|
||||||
);
|
|
||||||
|
|
||||||
if (backupPath == null || backupPath.length() < 1) {
|
|
||||||
Toast.makeText(mContext, R.string.error, Toast.LENGTH_LONG).show();
|
|
||||||
actionDialog.dismiss();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Toast.makeText(mContext, R.string.backup_saved_at_external_storage, Toast.LENGTH_LONG).show();
|
|
||||||
|
|
||||||
Uri selectedUri = Uri.parse(backupPath.substring(0, backupPath.lastIndexOf("/")));
|
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
|
||||||
intent.setDataAndType(selectedUri, "resource/folder");
|
|
||||||
|
|
||||||
if (intent.resolveActivityInfo(mContext.getPackageManager(), 0) != null) {
|
|
||||||
startActivity(intent);
|
|
||||||
} else {
|
|
||||||
Toast.makeText(mContext, R.string.filemanager_not_available, Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
actionDialog.dismiss();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Button delete = (Button) dialog_view.findViewById(R.id.btn_cookie_delete);
|
|
||||||
delete.setOnClickListener(new View.OnClickListener() {
|
|
||||||
public void onClick(View v) {
|
|
||||||
CookieDeleteDialog dialog = new CookieDeleteDialog();
|
|
||||||
dialog.setArguments(arguments);
|
|
||||||
dialog.show(getFragmentManager(), "CookieDeleteDialog");
|
|
||||||
actionDialog.dismiss();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Button cancel = (Button) dialog_view.findViewById(R.id.btn_cookie_cancel);
|
|
||||||
cancel.setOnClickListener(new View.OnClickListener() {
|
|
||||||
public void onClick(View v) {
|
|
||||||
actionDialog.dismiss();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return actionDialog;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
package org.torproject.android.ui.hiddenservices.dialogs;
|
|
||||||
|
|
||||||
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v4.app.DialogFragment;
|
|
||||||
import android.support.v7.app.AlertDialog;
|
|
||||||
|
|
||||||
import org.torproject.android.R;
|
|
||||||
import org.torproject.android.ui.hiddenservices.providers.CookieContentProvider;
|
|
||||||
|
|
||||||
public class CookieDeleteDialog extends DialogFragment {
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
|
||||||
final Bundle arguments = getArguments();
|
|
||||||
final Context context = getContext();
|
|
||||||
|
|
||||||
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
switch (which) {
|
|
||||||
case DialogInterface.BUTTON_POSITIVE:
|
|
||||||
// Delete from db
|
|
||||||
context.getContentResolver().delete(
|
|
||||||
CookieContentProvider.CONTENT_URI,
|
|
||||||
CookieContentProvider.ClientCookie._ID + "=" + arguments.getInt("_id"),
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DialogInterface.BUTTON_NEGATIVE:
|
|
||||||
// Do nothing
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return new AlertDialog.Builder(context)
|
|
||||||
.setMessage(R.string.confirm_cookie_deletion)
|
|
||||||
.setPositiveButton(R.string.btn_okay, dialogClickListener)
|
|
||||||
.setNegativeButton(R.string.btn_cancel, dialogClickListener)
|
|
||||||
.create();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,130 +0,0 @@
|
||||||
package org.torproject.android.ui.hiddenservices.dialogs;
|
|
||||||
|
|
||||||
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.ClipData;
|
|
||||||
import android.content.ClipboardManager;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v4.app.DialogFragment;
|
|
||||||
import android.support.v7.app.AlertDialog;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.torproject.android.R;
|
|
||||||
import org.torproject.android.ui.hiddenservices.backup.BackupUtils;
|
|
||||||
import org.torproject.android.ui.hiddenservices.permissions.PermissionManager;
|
|
||||||
|
|
||||||
public class HSActionsDialog extends DialogFragment {
|
|
||||||
public static final int WRITE_EXTERNAL_STORAGE_FROM_ACTION_DIALOG = 2;
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
|
||||||
final Bundle arguments = getArguments();
|
|
||||||
|
|
||||||
final View dialog_view = getActivity().getLayoutInflater().inflate(R.layout.layout_hs_actions, null);
|
|
||||||
final AlertDialog actionDialog = new AlertDialog.Builder(getActivity())
|
|
||||||
.setView(dialog_view)
|
|
||||||
.setTitle(R.string.hidden_services)
|
|
||||||
.create();
|
|
||||||
|
|
||||||
Button backup = (Button) dialog_view.findViewById(R.id.btn_hs_backup);
|
|
||||||
backup.setOnClickListener(new View.OnClickListener() {
|
|
||||||
public void onClick(View v) {
|
|
||||||
Context mContext = v.getContext();
|
|
||||||
|
|
||||||
if (PermissionManager.isLollipopOrHigher()
|
|
||||||
&& !PermissionManager.hasExternalWritePermission(mContext)) {
|
|
||||||
|
|
||||||
PermissionManager.requestExternalWritePermissions(
|
|
||||||
getActivity(), WRITE_EXTERNAL_STORAGE_FROM_ACTION_DIALOG);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
BackupUtils hsutils = new BackupUtils(mContext);
|
|
||||||
String backupPath = hsutils.createZipBackup(Integer.parseInt(arguments.getString("port")));
|
|
||||||
|
|
||||||
if (backupPath == null || backupPath.length() < 1) {
|
|
||||||
Toast.makeText(mContext, R.string.error, Toast.LENGTH_LONG).show();
|
|
||||||
actionDialog.dismiss();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Toast.makeText(mContext, R.string.backup_saved_at_external_storage, Toast.LENGTH_LONG).show();
|
|
||||||
|
|
||||||
Uri selectedUri = Uri.parse(backupPath.substring(0, backupPath.lastIndexOf("/")));
|
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
|
||||||
intent.setDataAndType(selectedUri, "resource/folder");
|
|
||||||
|
|
||||||
if (intent.resolveActivityInfo(mContext.getPackageManager(), 0) != null) {
|
|
||||||
startActivity(intent);
|
|
||||||
} else {
|
|
||||||
Toast.makeText(mContext, R.string.filemanager_not_available, Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
actionDialog.dismiss();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Button copy = (Button) dialog_view.findViewById(R.id.btn_hs_clipboard);
|
|
||||||
copy.setOnClickListener(new View.OnClickListener() {
|
|
||||||
public void onClick(View v) {
|
|
||||||
Context mContext = v.getContext();
|
|
||||||
ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
|
|
||||||
ClipData clip = ClipData.newPlainText("onion", arguments.getString("onion"));
|
|
||||||
clipboard.setPrimaryClip(clip);
|
|
||||||
Toast.makeText(mContext, R.string.done, Toast.LENGTH_LONG).show();
|
|
||||||
actionDialog.dismiss();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Button showAuth = (Button) dialog_view.findViewById(R.id.bt_hs_show_auth);
|
|
||||||
showAuth.setOnClickListener(new View.OnClickListener() {
|
|
||||||
public void onClick(View v) {
|
|
||||||
String auth_cookie_value = arguments.getString("auth_cookie_value");
|
|
||||||
|
|
||||||
if (arguments.getInt("auth_cookie") == 1) {
|
|
||||||
if (auth_cookie_value == null || auth_cookie_value.length() < 1) {
|
|
||||||
Toast.makeText(
|
|
||||||
v.getContext(), R.string.please_restart_Orbot_to_enable_the_changes, Toast.LENGTH_LONG
|
|
||||||
).show();
|
|
||||||
} else {
|
|
||||||
HSCookieDialog dialog = new HSCookieDialog();
|
|
||||||
dialog.setArguments(arguments);
|
|
||||||
dialog.show(getFragmentManager(), "HSCookieDialog");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Toast.makeText(
|
|
||||||
v.getContext(), R.string.auth_cookie_was_not_configured, Toast.LENGTH_LONG
|
|
||||||
).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
actionDialog.dismiss();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Button delete = (Button) dialog_view.findViewById(R.id.btn_hs_delete);
|
|
||||||
delete.setOnClickListener(new View.OnClickListener() {
|
|
||||||
public void onClick(View v) {
|
|
||||||
HSDeleteDialog dialog = new HSDeleteDialog();
|
|
||||||
dialog.setArguments(arguments);
|
|
||||||
dialog.show(getFragmentManager(), "HSDeleteDialog");
|
|
||||||
actionDialog.dismiss();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Button cancel = (Button) dialog_view.findViewById(R.id.btn_hs_cancel);
|
|
||||||
cancel.setOnClickListener(new View.OnClickListener() {
|
|
||||||
public void onClick(View v) {
|
|
||||||
actionDialog.dismiss();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return actionDialog;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
package org.torproject.android.ui.hiddenservices.dialogs;
|
|
||||||
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.ClipData;
|
|
||||||
import android.content.ClipboardManager;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v4.app.DialogFragment;
|
|
||||||
import android.support.v7.app.AlertDialog;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.google.zxing.integration.android.IntentIntegrator;
|
|
||||||
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
import org.torproject.android.R;
|
|
||||||
import org.torproject.android.ui.hiddenservices.providers.CookieContentProvider;
|
|
||||||
|
|
||||||
public class HSCookieDialog extends DialogFragment {
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
|
||||||
final View dialog_view = getActivity().getLayoutInflater().inflate(R.layout.layout_hs_cookie, null);
|
|
||||||
|
|
||||||
final Bundle arguments = getArguments();
|
|
||||||
final String auth_cookie_value = arguments.getString("auth_cookie_value");
|
|
||||||
|
|
||||||
final AlertDialog cookieDialog = new AlertDialog.Builder(getActivity())
|
|
||||||
.setView(dialog_view)
|
|
||||||
.create();
|
|
||||||
|
|
||||||
TextView cookie = (TextView) dialog_view.findViewById(R.id.hs_cookie);
|
|
||||||
cookie.setText(auth_cookie_value);
|
|
||||||
|
|
||||||
Button clipboard = (Button) dialog_view.findViewById(R.id.hs_cookie_to_clipboard);
|
|
||||||
clipboard.setOnClickListener(new View.OnClickListener() {
|
|
||||||
public void onClick(View v) {
|
|
||||||
Context mContext = v.getContext();
|
|
||||||
ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
|
|
||||||
ClipData clip = ClipData.newPlainText("cookie", auth_cookie_value);
|
|
||||||
clipboard.setPrimaryClip(clip);
|
|
||||||
Toast.makeText(mContext, R.string.done, Toast.LENGTH_LONG).show();
|
|
||||||
cookieDialog.dismiss();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Button shareQR = (Button) dialog_view.findViewById(R.id.hs_cookie_to_qr);
|
|
||||||
shareQR.setOnClickListener(new View.OnClickListener() {
|
|
||||||
public void onClick(View v) {
|
|
||||||
try {
|
|
||||||
JSONObject backup = new JSONObject();
|
|
||||||
backup.put(CookieContentProvider.ClientCookie.DOMAIN, arguments.getString("onion"));
|
|
||||||
backup.put(CookieContentProvider.ClientCookie.AUTH_COOKIE_VALUE, arguments.getString("auth_cookie_value"));
|
|
||||||
|
|
||||||
IntentIntegrator integrator = new IntentIntegrator(getActivity());
|
|
||||||
integrator.shareText(backup.toString());
|
|
||||||
|
|
||||||
} catch (JSONException e) {
|
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
cookieDialog.dismiss();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Button cancel = (Button) dialog_view.findViewById(R.id.hs_cookie_cancel);
|
|
||||||
cancel.setOnClickListener(new View.OnClickListener() {
|
|
||||||
public void onClick(View v) {
|
|
||||||
cookieDialog.dismiss();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return cookieDialog;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,102 +0,0 @@
|
||||||
package org.torproject.android.ui.hiddenservices.dialogs;
|
|
||||||
|
|
||||||
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v4.app.DialogFragment;
|
|
||||||
import android.support.v7.app.AlertDialog;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.CheckBox;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.torproject.android.R;
|
|
||||||
import org.torproject.android.ui.hiddenservices.providers.HSContentProvider;
|
|
||||||
|
|
||||||
public class HSDataDialog extends DialogFragment {
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
|
||||||
// Get the layout
|
|
||||||
final View dialog_view = getActivity().getLayoutInflater().inflate(R.layout.layout_hs_data_dialog, null);
|
|
||||||
|
|
||||||
// Use the Builder class for convenient dialog construction
|
|
||||||
final AlertDialog serviceDataDialog = new AlertDialog.Builder(getActivity())
|
|
||||||
.setView(dialog_view)
|
|
||||||
.setTitle(R.string.hidden_services)
|
|
||||||
.create();
|
|
||||||
|
|
||||||
// Buttons action
|
|
||||||
Button save = (Button) dialog_view.findViewById(R.id.HSDialogSave);
|
|
||||||
save.setOnClickListener(new View.OnClickListener() {
|
|
||||||
public void onClick(View v) {
|
|
||||||
String serverName = ((EditText) dialog_view.findViewById(R.id.hsName)).getText().toString();
|
|
||||||
Integer localPort = Integer.parseInt(
|
|
||||||
((EditText) dialog_view.findViewById(R.id.hsLocalPort)).getText().toString()
|
|
||||||
);
|
|
||||||
Integer onionPort = Integer.parseInt(
|
|
||||||
((EditText) dialog_view.findViewById(R.id.hsOnionPort)).getText().toString()
|
|
||||||
);
|
|
||||||
|
|
||||||
Boolean authCookie = ((CheckBox) dialog_view.findViewById(R.id.hsAuth)).isChecked();
|
|
||||||
|
|
||||||
if (checkInput(serverName, localPort, onionPort)) {
|
|
||||||
saveData(serverName, localPort, onionPort, authCookie);
|
|
||||||
Toast.makeText(
|
|
||||||
v.getContext(), R.string.please_restart_Orbot_to_enable_the_changes, Toast.LENGTH_LONG
|
|
||||||
).show();
|
|
||||||
serviceDataDialog.dismiss();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Button cancel = (Button) dialog_view.findViewById(R.id.HSDialogCancel);
|
|
||||||
cancel.setOnClickListener(new View.OnClickListener() {
|
|
||||||
public void onClick(View v) {
|
|
||||||
serviceDataDialog.cancel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return serviceDataDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkInput(String serverName, Integer local, Integer remote) {
|
|
||||||
boolean is_ok = true;
|
|
||||||
Integer error_msg = 0;
|
|
||||||
|
|
||||||
if ((local < 1 || local > 65535) || (remote < 1 || remote > 65535)) {
|
|
||||||
error_msg = R.string.invalid_port;
|
|
||||||
is_ok = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (serverName == null || serverName.length() < 1) {
|
|
||||||
error_msg = R.string.name_can_t_be_empty;
|
|
||||||
is_ok = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_ok) {
|
|
||||||
Toast.makeText(getContext(), error_msg, Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
return is_ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveData(String name, Integer local, Integer remote, Boolean authCookie) {
|
|
||||||
|
|
||||||
ContentValues fields = new ContentValues();
|
|
||||||
fields.put(HSContentProvider.HiddenService.NAME, name);
|
|
||||||
fields.put(HSContentProvider.HiddenService.PORT, local);
|
|
||||||
fields.put(HSContentProvider.HiddenService.ONION_PORT, remote);
|
|
||||||
fields.put(HSContentProvider.HiddenService.AUTH_COOKIE, authCookie);
|
|
||||||
fields.put(HSContentProvider.HiddenService.CREATED_BY_USER, 1);
|
|
||||||
|
|
||||||
ContentResolver cr = getContext().getContentResolver();
|
|
||||||
|
|
||||||
cr.insert(HSContentProvider.CONTENT_URI, fields);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
package org.torproject.android.ui.hiddenservices.dialogs;
|
|
||||||
|
|
||||||
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v4.app.DialogFragment;
|
|
||||||
import android.support.v7.app.AlertDialog;
|
|
||||||
|
|
||||||
import org.torproject.android.R;
|
|
||||||
import org.torproject.android.service.TorServiceConstants;
|
|
||||||
import org.torproject.android.ui.hiddenservices.providers.HSContentProvider;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
public class HSDeleteDialog extends DialogFragment {
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
|
||||||
final Bundle arguments = getArguments();
|
|
||||||
final Context context = getContext();
|
|
||||||
|
|
||||||
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
switch (which) {
|
|
||||||
case DialogInterface.BUTTON_POSITIVE:
|
|
||||||
// Delete from db
|
|
||||||
context.getContentResolver().delete(
|
|
||||||
HSContentProvider.CONTENT_URI,
|
|
||||||
HSContentProvider.HiddenService._ID + "=" + arguments.getInt("_id"),
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
// Delete from interal storage
|
|
||||||
String base = context.getFilesDir().getAbsolutePath() + "/" + TorServiceConstants.HIDDEN_SERVICES_DIR;
|
|
||||||
File dir = new File(base, "hs" + arguments.getString("port"));
|
|
||||||
|
|
||||||
if (dir.isDirectory()) {
|
|
||||||
String[] children = dir.list();
|
|
||||||
for (String aChildren : children) {
|
|
||||||
new File(dir, aChildren).delete();
|
|
||||||
}
|
|
||||||
dir.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DialogInterface.BUTTON_NEGATIVE:
|
|
||||||
// Do nothing
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return new AlertDialog.Builder(context)
|
|
||||||
.setMessage(R.string.confirm_service_deletion)
|
|
||||||
.setPositiveButton(R.string.btn_okay, dialogClickListener)
|
|
||||||
.setNegativeButton(R.string.btn_cancel, dialogClickListener)
|
|
||||||
.create();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,84 +0,0 @@
|
||||||
package org.torproject.android.ui.hiddenservices.dialogs;
|
|
||||||
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v4.app.DialogFragment;
|
|
||||||
import android.support.v7.app.AlertDialog;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.ListView;
|
|
||||||
|
|
||||||
import org.torproject.android.R;
|
|
||||||
import org.torproject.android.ui.hiddenservices.adapters.BackupAdapter;
|
|
||||||
import org.torproject.android.ui.hiddenservices.backup.BackupUtils;
|
|
||||||
import org.torproject.android.ui.hiddenservices.storage.ExternalStorage;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FilenameFilter;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class SelectCookieBackupDialog extends DialogFragment {
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
|
||||||
AlertDialog.Builder cookieBackupDialog = new AlertDialog.Builder(getActivity());
|
|
||||||
|
|
||||||
cookieBackupDialog.setTitle(R.string.restore_backup);
|
|
||||||
|
|
||||||
File backupDir = ExternalStorage.getOrCreateBackupDir();
|
|
||||||
File[] files = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
files = backupDir.listFiles(new FilenameFilter() {
|
|
||||||
@Override
|
|
||||||
public boolean accept(File dir, String name) {
|
|
||||||
return name.toLowerCase().endsWith(".json");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (NullPointerException e) {
|
|
||||||
// Silent block
|
|
||||||
}
|
|
||||||
|
|
||||||
if (files == null || files.length < 1) {
|
|
||||||
cookieBackupDialog.setMessage(R.string.create_a_backup_first);
|
|
||||||
cookieBackupDialog.setNegativeButton(R.string.btn_cancel, new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
|
||||||
dialog.dismiss();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return cookieBackupDialog.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
final View dialog_view = getActivity().getLayoutInflater().inflate(R.layout.layout_hs_backups_list, null);
|
|
||||||
|
|
||||||
cookieBackupDialog.setView(dialog_view);
|
|
||||||
cookieBackupDialog.setPositiveButton(R.string.btn_okay, new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
|
||||||
dialog.dismiss();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ListView backups = (ListView) dialog_view.findViewById(R.id.listview_hs_backups);
|
|
||||||
|
|
||||||
List<File> json_backups = new ArrayList<>();
|
|
||||||
Collections.addAll(json_backups, files);
|
|
||||||
|
|
||||||
backups.setAdapter(new BackupAdapter(getContext(), R.layout.layout_hs_backups_list_item, json_backups));
|
|
||||||
backups.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
|
||||||
BackupUtils backupUtils = new BackupUtils(view.getContext().getApplicationContext());
|
|
||||||
File p = (File) parent.getItemAtPosition(position);
|
|
||||||
backupUtils.restoreCookieBackup(p);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return cookieBackupDialog.create();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,84 +0,0 @@
|
||||||
package org.torproject.android.ui.hiddenservices.dialogs;
|
|
||||||
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v4.app.DialogFragment;
|
|
||||||
import android.support.v7.app.AlertDialog;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.ListView;
|
|
||||||
|
|
||||||
import org.torproject.android.R;
|
|
||||||
import org.torproject.android.ui.hiddenservices.adapters.BackupAdapter;
|
|
||||||
import org.torproject.android.ui.hiddenservices.backup.BackupUtils;
|
|
||||||
import org.torproject.android.ui.hiddenservices.storage.ExternalStorage;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FilenameFilter;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class SelectHSBackupDialog extends DialogFragment {
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
|
||||||
AlertDialog.Builder backupsDialog = new AlertDialog.Builder(getActivity());
|
|
||||||
|
|
||||||
backupsDialog.setTitle(R.string.restore_backup);
|
|
||||||
|
|
||||||
File backupDir = ExternalStorage.getOrCreateBackupDir();
|
|
||||||
File[] files = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
files = backupDir.listFiles(new FilenameFilter() {
|
|
||||||
@Override
|
|
||||||
public boolean accept(File dir, String name) {
|
|
||||||
return name.toLowerCase().endsWith(".zip");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (NullPointerException e) {
|
|
||||||
// Silent block
|
|
||||||
}
|
|
||||||
|
|
||||||
if (files == null || files.length < 1) {
|
|
||||||
backupsDialog.setMessage(R.string.create_a_backup_first);
|
|
||||||
backupsDialog.setNegativeButton(R.string.btn_cancel, new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
|
||||||
dialog.dismiss();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return backupsDialog.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
final View dialog_view = getActivity().getLayoutInflater().inflate(R.layout.layout_hs_backups_list, null);
|
|
||||||
|
|
||||||
backupsDialog.setView(dialog_view);
|
|
||||||
backupsDialog.setPositiveButton(R.string.btn_okay, new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
|
||||||
dialog.dismiss();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ListView backups = (ListView) dialog_view.findViewById(R.id.listview_hs_backups);
|
|
||||||
|
|
||||||
List<File> zips = new ArrayList<>();
|
|
||||||
Collections.addAll(zips, files);
|
|
||||||
|
|
||||||
backups.setAdapter(new BackupAdapter(getContext(), R.layout.layout_hs_backups_list_item, zips));
|
|
||||||
backups.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
|
||||||
BackupUtils backupUtils = new BackupUtils(view.getContext().getApplicationContext());
|
|
||||||
File p = (File) parent.getItemAtPosition(position);
|
|
||||||
backupUtils.restoreZipBackup(p);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return backupsDialog.create();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,103 +0,0 @@
|
||||||
package org.torproject.android.ui.hiddenservices.permissions;
|
|
||||||
|
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.PowerManager;
|
|
||||||
import android.provider.Settings;
|
|
||||||
import android.support.design.widget.Snackbar;
|
|
||||||
import android.support.v4.app.ActivityCompat;
|
|
||||||
import android.support.v4.app.FragmentActivity;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import org.torproject.android.R;
|
|
||||||
|
|
||||||
public class PermissionManager {
|
|
||||||
public static int VERY_LONG_LENGTH = 6000;
|
|
||||||
|
|
||||||
public static boolean isLollipopOrHigher() {
|
|
||||||
return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
|
||||||
public static boolean hasExternalWritePermission(Context context) {
|
|
||||||
return (context.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void requestExternalWritePermissions(FragmentActivity activity, int action) {
|
|
||||||
final int mAction = action;
|
|
||||||
final FragmentActivity mActivity = activity;
|
|
||||||
|
|
||||||
if (ActivityCompat.shouldShowRequestPermissionRationale
|
|
||||||
(mActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
|
||||||
Snackbar.make(mActivity.findViewById(android.R.id.content),
|
|
||||||
R.string.please_grant_permissions_for_external_storage,
|
|
||||||
Snackbar.LENGTH_INDEFINITE).setAction(R.string.activate,
|
|
||||||
new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
ActivityCompat.requestPermissions(mActivity,
|
|
||||||
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
|
||||||
mAction);
|
|
||||||
}
|
|
||||||
}).show();
|
|
||||||
} else {
|
|
||||||
ActivityCompat.requestPermissions(mActivity,
|
|
||||||
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
|
||||||
mAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.M)
|
|
||||||
public static void requestBatteryPermmssions(FragmentActivity activity, Context context) {
|
|
||||||
final Context mContext = context;
|
|
||||||
final String packageName = mContext.getPackageName();
|
|
||||||
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
|
|
||||||
|
|
||||||
if (pm.isIgnoringBatteryOptimizations(packageName))
|
|
||||||
return;
|
|
||||||
|
|
||||||
Snackbar.make(activity.findViewById(android.R.id.content),
|
|
||||||
R.string.consider_disable_battery_optimizations,
|
|
||||||
VERY_LONG_LENGTH).setAction(R.string.disable,
|
|
||||||
new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
Intent intent = new Intent();
|
|
||||||
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
|
|
||||||
intent.setData(Uri.parse("package:" + packageName));
|
|
||||||
mContext.startActivity(intent);
|
|
||||||
}
|
|
||||||
}).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.M)
|
|
||||||
public static void requestDropBatteryPermmssions(FragmentActivity activity, Context context) {
|
|
||||||
final Context mContext = context;
|
|
||||||
|
|
||||||
final String packageName = context.getPackageName();
|
|
||||||
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
|
||||||
|
|
||||||
if (!pm.isIgnoringBatteryOptimizations(packageName))
|
|
||||||
return;
|
|
||||||
|
|
||||||
Snackbar.make(activity.findViewById(android.R.id.content),
|
|
||||||
R.string.consider_enable_battery_optimizations,
|
|
||||||
VERY_LONG_LENGTH).setAction(R.string.enable,
|
|
||||||
new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
Intent intent = new Intent();
|
|
||||||
intent.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
|
|
||||||
mContext.startActivity(intent);
|
|
||||||
}
|
|
||||||
}).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,134 +0,0 @@
|
||||||
package org.torproject.android.ui.hiddenservices.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.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.torproject.android.ui.hiddenservices.database.HSDatabase;
|
|
||||||
|
|
||||||
|
|
||||||
public class CookieContentProvider extends ContentProvider {
|
|
||||||
public static final String[] PROJECTION = new String[]{
|
|
||||||
ClientCookie._ID,
|
|
||||||
ClientCookie.DOMAIN,
|
|
||||||
ClientCookie.AUTH_COOKIE_VALUE,
|
|
||||||
ClientCookie.ENABLED
|
|
||||||
};
|
|
||||||
private static final String AUTH = "org.torproject.android.ui.hiddenservices.providers.cookie";
|
|
||||||
public static final Uri CONTENT_URI =
|
|
||||||
Uri.parse("content://" + AUTH + "/cookie");
|
|
||||||
//UriMatcher
|
|
||||||
private static final int COOKIES = 1;
|
|
||||||
private static final int COOKIE_ID = 2;
|
|
||||||
|
|
||||||
private static final UriMatcher uriMatcher;
|
|
||||||
|
|
||||||
static {
|
|
||||||
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
|
||||||
uriMatcher.addURI(AUTH, "hs", COOKIES);
|
|
||||||
uriMatcher.addURI(AUTH, "hs/#", COOKIE_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
private HSDatabase mServervices;
|
|
||||||
private Context mContext;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreate() {
|
|
||||||
mContext = getContext();
|
|
||||||
mServervices = new HSDatabase(mContext);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
|
||||||
String where = selection;
|
|
||||||
if (uriMatcher.match(uri) == COOKIE_ID) {
|
|
||||||
where = "_id=" + uri.getLastPathSegment();
|
|
||||||
}
|
|
||||||
|
|
||||||
SQLiteDatabase db = mServervices.getReadableDatabase();
|
|
||||||
|
|
||||||
return db.query(HSDatabase.HS_CLIENT_COOKIE_TABLE_NAME, projection, where,
|
|
||||||
selectionArgs, null, null, sortOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public String getType(@NonNull Uri uri) {
|
|
||||||
int match = uriMatcher.match(uri);
|
|
||||||
|
|
||||||
switch (match) {
|
|
||||||
case COOKIES:
|
|
||||||
return "vnd.android.cursor.dir/vnd.torproject.cookies";
|
|
||||||
case COOKIE_ID:
|
|
||||||
return "vnd.android.cursor.item/vnd.torproject.cookie";
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public Uri insert(@NonNull Uri uri, ContentValues values) {
|
|
||||||
long regId;
|
|
||||||
|
|
||||||
SQLiteDatabase db = mServervices.getWritableDatabase();
|
|
||||||
|
|
||||||
regId = db.insert(HSDatabase.HS_CLIENT_COOKIE_TABLE_NAME, null, values);
|
|
||||||
|
|
||||||
mContext.getContentResolver().notifyChange(CONTENT_URI, null);
|
|
||||||
|
|
||||||
return ContentUris.withAppendedId(CONTENT_URI, regId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
|
|
||||||
|
|
||||||
String where = selection;
|
|
||||||
if (uriMatcher.match(uri) == COOKIE_ID) {
|
|
||||||
where = "_id=" + uri.getLastPathSegment();
|
|
||||||
}
|
|
||||||
|
|
||||||
SQLiteDatabase db = mServervices.getWritableDatabase();
|
|
||||||
|
|
||||||
Integer rows = db.delete(HSDatabase.HS_CLIENT_COOKIE_TABLE_NAME, where, selectionArgs);
|
|
||||||
|
|
||||||
mContext.getContentResolver().notifyChange(CONTENT_URI, null);
|
|
||||||
|
|
||||||
return rows;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
|
||||||
SQLiteDatabase db = mServervices.getWritableDatabase();
|
|
||||||
|
|
||||||
String where = selection;
|
|
||||||
if (uriMatcher.match(uri) == COOKIE_ID) {
|
|
||||||
where = "_id=" + uri.getLastPathSegment();
|
|
||||||
}
|
|
||||||
|
|
||||||
Integer rows = db.update(HSDatabase.HS_CLIENT_COOKIE_TABLE_NAME, values, where, null);
|
|
||||||
mContext.getContentResolver().notifyChange(CONTENT_URI, null);
|
|
||||||
|
|
||||||
return rows;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class ClientCookie implements BaseColumns {
|
|
||||||
public static final String DOMAIN = "domain";
|
|
||||||
public static final String AUTH_COOKIE_VALUE = "auth_cookie_value";
|
|
||||||
public static final String ENABLED = "enabled";
|
|
||||||
|
|
||||||
private ClientCookie() {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,144 +0,0 @@
|
||||||
package org.torproject.android.ui.hiddenservices.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.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.torproject.android.ui.hiddenservices.database.HSDatabase;
|
|
||||||
|
|
||||||
|
|
||||||
public class HSContentProvider extends ContentProvider {
|
|
||||||
public static final String[] PROJECTION = new String[]{
|
|
||||||
HiddenService._ID,
|
|
||||||
HiddenService.NAME,
|
|
||||||
HiddenService.PORT,
|
|
||||||
HiddenService.DOMAIN,
|
|
||||||
HiddenService.ONION_PORT,
|
|
||||||
HiddenService.AUTH_COOKIE,
|
|
||||||
HiddenService.AUTH_COOKIE_VALUE,
|
|
||||||
HiddenService.CREATED_BY_USER,
|
|
||||||
HiddenService.ENABLED
|
|
||||||
};
|
|
||||||
private static final String AUTH = "org.torproject.android.ui.hiddenservices.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;
|
|
||||||
|
|
||||||
static {
|
|
||||||
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
|
||||||
uriMatcher.addURI(AUTH, "hs", ONIONS);
|
|
||||||
uriMatcher.addURI(AUTH, "hs/#", ONION_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
private HSDatabase mServervices;
|
|
||||||
private Context mContext;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreate() {
|
|
||||||
mContext = getContext();
|
|
||||||
mServervices = new HSDatabase(mContext);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
|
||||||
String where = selection;
|
|
||||||
if (uriMatcher.match(uri) == ONION_ID) {
|
|
||||||
where = "_id=" + uri.getLastPathSegment();
|
|
||||||
}
|
|
||||||
|
|
||||||
SQLiteDatabase db = mServervices.getReadableDatabase();
|
|
||||||
|
|
||||||
return db.query(HSDatabase.HS_DATA_TABLE_NAME, projection, where,
|
|
||||||
selectionArgs, null, null, sortOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public String getType(@NonNull 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(@NonNull Uri uri, ContentValues values) {
|
|
||||||
long regId;
|
|
||||||
|
|
||||||
SQLiteDatabase db = mServervices.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(@NonNull Uri uri, String selection, String[] selectionArgs) {
|
|
||||||
|
|
||||||
String where = selection;
|
|
||||||
if (uriMatcher.match(uri) == ONION_ID) {
|
|
||||||
where = "_id=" + uri.getLastPathSegment();
|
|
||||||
}
|
|
||||||
|
|
||||||
SQLiteDatabase db = mServervices.getWritableDatabase();
|
|
||||||
|
|
||||||
Integer rows = db.delete(HSDatabase.HS_DATA_TABLE_NAME, where, selectionArgs);
|
|
||||||
|
|
||||||
mContext.getContentResolver().notifyChange(CONTENT_URI, null);
|
|
||||||
|
|
||||||
return rows;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
|
||||||
SQLiteDatabase db = mServervices.getWritableDatabase();
|
|
||||||
|
|
||||||
String where = selection;
|
|
||||||
if (uriMatcher.match(uri) == ONION_ID) {
|
|
||||||
where = "_id=" + uri.getLastPathSegment();
|
|
||||||
}
|
|
||||||
|
|
||||||
Integer rows = db.update(HSDatabase.HS_DATA_TABLE_NAME, values, where, null);
|
|
||||||
mContext.getContentResolver().notifyChange(CONTENT_URI, null);
|
|
||||||
|
|
||||||
return rows;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class HiddenService implements BaseColumns {
|
|
||||||
public static final String NAME = "name";
|
|
||||||
public static final String PORT = "port";
|
|
||||||
public static final String ONION_PORT = "onion_port";
|
|
||||||
public static final String DOMAIN = "domain";
|
|
||||||
public static final String AUTH_COOKIE = "auth_cookie";
|
|
||||||
public static final String AUTH_COOKIE_VALUE = "auth_cookie_value";
|
|
||||||
public static final String CREATED_BY_USER = "created_by_user";
|
|
||||||
public static final String ENABLED = "enabled";
|
|
||||||
|
|
||||||
private HiddenService() {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
package org.torproject.android.ui.hiddenservices.storage;
|
|
||||||
|
|
||||||
import android.os.Environment;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
public class ExternalStorage {
|
|
||||||
private static final String ORBOT_BACKUPS_DIR = "Orbot";
|
|
||||||
|
|
||||||
public static File getOrCreateBackupDir() {
|
|
||||||
if (!isExternalStorageWritable())
|
|
||||||
return null;
|
|
||||||
|
|
||||||
File dir = new File(Environment.getExternalStorageDirectory(), ORBOT_BACKUPS_DIR);
|
|
||||||
|
|
||||||
if (!dir.isDirectory() && !dir.mkdirs())
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Checks if external storage is available for read and write */
|
|
||||||
public static boolean isExternalStorageWritable() {
|
|
||||||
String state = Environment.getExternalStorageState();
|
|
||||||
return Environment.MEDIA_MOUNTED.equals(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Checks if external storage is available to at least read */
|
|
||||||
public static boolean isExternalStorageReadable() {
|
|
||||||
String state = Environment.getExternalStorageState();
|
|
||||||
return Environment.MEDIA_MOUNTED.equals(state) ||
|
|
||||||
Environment.MEDIA_MOUNTED_READ_ONLY.equals(state);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,163 +0,0 @@
|
||||||
package org.torproject.android.vpn;
|
|
||||||
|
|
||||||
import org.torproject.android.R;
|
|
||||||
import org.torproject.android.service.util.Prefs;
|
|
||||||
import org.torproject.android.service.TorService;
|
|
||||||
import org.torproject.android.service.TorServiceConstants;
|
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.VpnService;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.Window;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* To combat background service being stopped/swiped
|
|
||||||
*/
|
|
||||||
|
|
||||||
@TargetApi(14)
|
|
||||||
public class VPNEnableActivity extends AppCompatActivity {
|
|
||||||
|
|
||||||
private final static int REQUEST_VPN = 7777;
|
|
||||||
private Intent intent = null;
|
|
||||||
private boolean checkVpn = true;
|
|
||||||
private Handler h = new Handler();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate( Bundle icicle ) {
|
|
||||||
|
|
||||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
|
||||||
//getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
|
||||||
|
|
||||||
super.onCreate( icicle );
|
|
||||||
|
|
||||||
Log.d("VPNEnableActivity","prompting user to start Orbot VPN");
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onResume ()
|
|
||||||
{
|
|
||||||
super.onResume();
|
|
||||||
|
|
||||||
if (checkVpn)
|
|
||||||
{
|
|
||||||
intent = VpnService.prepare(this);
|
|
||||||
|
|
||||||
if (intent != null)
|
|
||||||
promptStartVpnService();
|
|
||||||
else
|
|
||||||
startVpnService ();
|
|
||||||
|
|
||||||
checkVpn = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void promptStartVpnService ()
|
|
||||||
{
|
|
||||||
|
|
||||||
AlertDialog dialog = new AlertDialog.Builder(this)
|
|
||||||
.setTitle(getString(R.string.app_name) + ' ' + getString(R.string.apps_mode))
|
|
||||||
.setMessage(getString(R.string.you_can_enable_all_apps_on_your_device_to_run_through_the_tor_network_using_the_vpn_feature_of_android_))
|
|
||||||
.setPositiveButton(R.string.activate, new Dialog.OnClickListener ()
|
|
||||||
{
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
Prefs.putUseVpn(true);
|
|
||||||
startVpnService();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
})
|
|
||||||
.setNegativeButton(R.string.btn_cancel, new Dialog.OnClickListener ()
|
|
||||||
{
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
|
|
||||||
h.postDelayed(new Runnable () {
|
|
||||||
|
|
||||||
public void run ()
|
|
||||||
{
|
|
||||||
VPNEnableActivity.this.finish();
|
|
||||||
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
}).create();
|
|
||||||
|
|
||||||
dialog.show();
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startVpnService ()
|
|
||||||
{
|
|
||||||
if (intent == null)
|
|
||||||
{
|
|
||||||
Log.d("VPNEnableActivity","VPN enabled, starting Tor...");
|
|
||||||
sendIntentToService(TorServiceConstants.CMD_VPN);
|
|
||||||
|
|
||||||
Handler h = new Handler();
|
|
||||||
h.postDelayed(new Runnable () {
|
|
||||||
|
|
||||||
public void run ()
|
|
||||||
{
|
|
||||||
sendIntentToService(TorServiceConstants.ACTION_START);
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.w("VPNEnableActivity","prompt for VPN");
|
|
||||||
startActivityForResult(intent,REQUEST_VPN);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onActivityResult(int request, int response, Intent data) {
|
|
||||||
super.onActivityResult(request, response, data);
|
|
||||||
|
|
||||||
if (request == REQUEST_VPN && response == RESULT_OK)
|
|
||||||
{
|
|
||||||
sendIntentToService(TorServiceConstants.CMD_VPN);
|
|
||||||
|
|
||||||
h.postDelayed(new Runnable () {
|
|
||||||
|
|
||||||
public void run ()
|
|
||||||
{
|
|
||||||
sendIntentToService(TorServiceConstants.ACTION_START);
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void sendIntentToService(String action) {
|
|
||||||
Intent torService = new Intent(this, TorService.class);
|
|
||||||
torService.setAction(action);
|
|
||||||
startService(torService);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Before Width: | Height: | Size: 480 B |
Before Width: | Height: | Size: 802 B |
Before Width: | Height: | Size: 744 B |
Before Width: | Height: | Size: 967 B |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 616 B |
Before Width: | Height: | Size: 713 B |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 553 B |
Before Width: | Height: | Size: 546 B |
Before Width: | Height: | Size: 232 B |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 221 B |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 852 B |
Before Width: | Height: | Size: 990 B |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 294 B |
Before Width: | Height: | Size: 390 B |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 342 B |
Before Width: | Height: | Size: 358 B |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 319 B |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 647 B |
Before Width: | Height: | Size: 606 B |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 357 B |
Before Width: | Height: | Size: 517 B |
Before Width: | Height: | Size: 607 B |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 458 B |
Before Width: | Height: | Size: 475 B |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 364 B |
Before Width: | Height: | Size: 369 B |
Before Width: | Height: | Size: 198 B |
Before Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 163 B |
Before Width: | Height: | Size: 883 B |
Before Width: | Height: | Size: 535 B |
Before Width: | Height: | Size: 600 B |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 618 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 747 B |
Before Width: | Height: | Size: 970 B |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 716 B |
Before Width: | Height: | Size: 682 B |
Before Width: | Height: | Size: 252 B |
Before Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 236 B |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.3 KiB |