Compare commits
183 Commits
15.4.0-bet
...
master
Author | SHA1 | Date |
---|---|---|
n8fr8 | e822160b00 | |
n8fr8 | b0e62e58cc | |
n8fr8 | 43b10491a1 | |
n8fr8 | c4cf800488 | |
n8fr8 | 0417acf570 | |
n8fr8 | ff7ecbdba9 | |
n8fr8 | 3e8e5ace65 | |
n8fr8 | 0fda8f7779 | |
n8fr8 | 6548d3630b | |
n8fr8 | d1ee65a795 | |
n8fr8 | a2fba85a5b | |
n8fr8 | 6ef0a45050 | |
n8fr8 | b82b6a4cb1 | |
n8fr8 | de522e2ec8 | |
n8fr8 | 0756d058f7 | |
n8fr8 | b98cad65d4 | |
n8fr8 | cd678980f9 | |
n8fr8 | 8a66040c4f | |
n8fr8 | 75bd78b287 | |
n8fr8 | fa1ed5505d | |
n8fr8 | 84ab10731c | |
n8fr8 | 63656611dc | |
n8fr8 | 84d7745cde | |
n8fr8 | b0e2d4e63b | |
n8fr8 | e97d5a1016 | |
n8fr8 | 0ba24f8ca4 | |
n8fr8 | 93663355fa | |
n8fr8 | 63723e06ef | |
n8fr8 | ae61770960 | |
n8fr8 | 45ecbcb298 | |
n8fr8 | d2342ed7ec | |
n8fr8 | 549fb14077 | |
n8fr8 | 5366016c86 | |
n8fr8 | de630b45cb | |
n8fr8 | 292dcda8e0 | |
n8fr8 | 87efd233e5 | |
n8fr8 | ff59b5595f | |
n8fr8 | 9e428e1e09 | |
n8fr8 | a46fd778b5 | |
n8fr8 | db82e6b889 | |
n8fr8 | 7a9cbe8fc7 | |
n8fr8 | d364a5493c | |
n8fr8 | 988a87c8ef | |
n8fr8 | 51c7a076a5 | |
n8fr8 | 1b1f64d376 | |
n8fr8 | 9abac285db | |
n8fr8 | 808f9594f5 | |
n8fr8 | 810eafd6e5 | |
n8fr8 | c0bca9f762 | |
Nathan Freitas | 9897c1208e | |
Nathan Freitas | d194eb8064 | |
Unpublished | 97990103aa | |
Unpublished | 8383c1abbc | |
Unpublished | d43fbf96b9 | |
Unpublished | 99eb75aeaf | |
Unpublished | 5e0169d762 | |
n8fr8 | 93f5239bea | |
n8fr8 | 57ba718e75 | |
n8fr8 | b4cdbff06b | |
n8fr8 | b9a30b71d0 | |
n8fr8 | 3d0e9a93a6 | |
n8fr8 | 1ebf0a5c2c | |
n8fr8 | 3403b02dd8 | |
n8fr8 | 13d0bddc8c | |
n8fr8 | fa5aff40cc | |
Igor Oliveira | 1b1c0b9537 | |
n8fr8 | d64f15533f | |
n8fr8 | 3528afd114 | |
n8fr8 | 3b766484bf | |
n8fr8 | fc10fb1a9e | |
n8fr8 | ae5420426d | |
n8fr8 | b85e0b32f2 | |
n8fr8 | c2189d4a2c | |
n8fr8 | f15c153bf0 | |
n8fr8 | 9b8d8719b6 | |
n8fr8 | 673e45d60f | |
n8fr8 | 498364c25d | |
n8fr8 | ffb8dd0b55 | |
Nathan Freitas | cdf3180291 | |
n8fr8 | 3d951b9dd8 | |
n8fr8 | f8dbfacdb3 | |
n8fr8 | 17f03d1f42 | |
n8fr8 | 7e98222e47 | |
n8fr8 | f9c1af9127 | |
n8fr8 | 85aaff7152 | |
n8fr8 | 7fbd72b0c8 | |
n8fr8 | a27480a208 | |
n8fr8 | ff1979643c | |
n8fr8 | dd51960b24 | |
n8fr8 | a810a5add9 | |
n8fr8 | 91b73b1f6b | |
n8fr8 | fce5bdede8 | |
Nathan Freitas | 72c7e0445c | |
Nathan Freitas | 8cf412ad62 | |
goapunk | c1dfc29e02 | |
goapunk | 9ca36e1bff | |
Tad | 6e4b700803 | |
Nathan Freitas | 71e3464358 | |
Unpublished | cbffc1d6ca | |
n8fr8 | 1b904125f3 | |
n8fr8 | 61aff8ce82 | |
n8fr8 | b2de89b2cb | |
n8fr8 | 5d218adc14 | |
n8fr8 | 2c99e9efa4 | |
n8fr8 | c53204f9c3 | |
n8fr8 | 5b9eedba1d | |
n8fr8 | 1c7eedd4e4 | |
Nathan Freitas | 19e927effa | |
Nathan Freitas | 54688e2806 | |
Nathan Freitas | 14367e0add | |
n8fr8 | 8bbf0ba799 | |
n8fr8 | 750a282e20 | |
Alexander Færøy | d591cdb0df | |
Alexander Færøy | 87edea9ce6 | |
n8fr8 | eff2f765db | |
n8fr8 | aa9ed7fbe9 | |
n8fr8 | 0323216569 | |
n8fr8 | e1ba02a426 | |
n8fr8 | a685bf1488 | |
n8fr8 | 091037427a | |
n8fr8 | d24aab8b47 | |
n8fr8 | 165e95a880 | |
n8fr8 | dc7aee1e8e | |
n8fr8 | 440290e9d7 | |
n8fr8 | 9140ba6a47 | |
n8fr8 | f09508def5 | |
n8fr8 | e6003f670f | |
n8fr8 | d86ace63da | |
n8fr8 | 90975fb18a | |
n8fr8 | 25425a622e | |
n8fr8 | b0b6b68c84 | |
n8fr8 | dceea11782 | |
n8fr8 | b98a0ff160 | |
n8fr8 | 1e2074c818 | |
n8fr8 | a5015dc691 | |
n8fr8 | b322e530da | |
n8fr8 | 2bd5614210 | |
n8fr8 | 01176b1d38 | |
n8fr8 | 70693bfc6a | |
n8fr8 | c4867ba8d2 | |
n8fr8 | 6ca89b30ba | |
n8fr8 | cc3c4514e8 | |
n8fr8 | 632824bd5f | |
n8fr8 | d190f3a142 | |
n8fr8 | ebc362c4d7 | |
Akshat Agrawal | d94657d1f4 | |
Tad | 18dd53d419 | |
Igor Koznin | 2922e48730 | |
n8fr8 | 5b255df92d | |
n8fr8 | df29aaad8d | |
n8fr8 | e18a7937d2 | |
n8fr8 | c874e70fa6 | |
n8fr8 | b1ff4d5271 | |
n8fr8 | fb7eea9560 | |
n8fr8 | 132c00a224 | |
n8fr8 | fe491b0ec5 | |
n8fr8 | 34043f9ef4 | |
n8fr8 | 674f8ff4aa | |
n8fr8 | 67cb36ef63 | |
n8fr8 | 78984bd344 | |
Nathan Freitas | 028f76e62e | |
Nathan Freitas | ad1bcb8d5d | |
Nathan Freitas | 2403e8c333 | |
Nathan Freitas | 62fec5343c | |
Nathan Freitas | 5d966447ce | |
Nathan Freitas | bb8373bdd6 | |
Nathan Freitas | 837d812916 | |
Nathan Freitas | 6d0d4a3d20 | |
Nathan Freitas | 5b9a34199e | |
Nathan Freitas | 248bcc979d | |
Nathan Freitas | 7dbae837de | |
Nathan Freitas | c0d082ed78 | |
Nathan Freitas | 4af61b609e | |
Nathan Freitas | 301ef3948e | |
Nathan Freitas | 11c6abf59d | |
Nathan Freitas | ea40bb4510 | |
Nathan Freitas | 9019ceb898 | |
Nathan Freitas | b8aaa2c7d9 | |
Nathan Freitas | 1d3a99ea6c | |
Nathan Freitas | 128cfe8394 | |
Nathan Freitas | 6496cb11d6 | |
Nathan Freitas | 4d4fb82419 | |
Nathan Freitas | 7eb7a76bbe |
13
.classpath
|
@ -1,13 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
|
||||
<classpathentry kind="src" path="src">
|
||||
<attributes>
|
||||
<attribute name="ignore_optional_problems" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry kind="output" path="bin/classes"/>
|
||||
</classpath>
|
|
@ -66,5 +66,3 @@ app/src/main/jniLibs/
|
|||
app/src/main/libs/
|
||||
orbotservice/src/main/libs/
|
||||
orbotservice/src/main/jniLibs/
|
||||
orbotservice/src/main/assets/armeabi/
|
||||
orbotservice/src/main/assets/x86/
|
||||
|
|
|
@ -6,31 +6,15 @@
|
|||
path = external/libevent
|
||||
url = https://github.com/libevent/libevent.git
|
||||
ignore = dirty
|
||||
[submodule "external/jtorctl"]
|
||||
path = external/jtorctl
|
||||
url = https://github.com/guardianproject/jtorctl
|
||||
ignore = dirty
|
||||
[submodule "external/openssl"]
|
||||
path = external/openssl
|
||||
url = https://github.com/openssl/openssl.git
|
||||
ignore = dirty
|
||||
[submodule "external/iptables"]
|
||||
path = external/iptables
|
||||
url = git://git.netfilter.org/iptables
|
||||
[submodule "external/zstd"]
|
||||
path = external/zstd
|
||||
url = https://github.com/facebook/zstd.git
|
||||
ignore = dirty
|
||||
[submodule "external/polipo"]
|
||||
path = external/polipo
|
||||
url = https://github.com/jech/polipo.git
|
||||
ignore = dirty
|
||||
[submodule "external/badvpn"]
|
||||
path = external/badvpn
|
||||
url = https://github.com/guardianproject/badvpn.git
|
||||
ignore = dirty
|
||||
[submodule "external/pluto"]
|
||||
path = external/pluto
|
||||
url = https://github.com/guardianproject/pluto.git
|
||||
ignore = dirty
|
||||
[submodule "external/jsocks"]
|
||||
path = external/jsocks
|
||||
url = https://github.com/guardianproject/jsocks.git
|
||||
[submodule "external/xz"]
|
||||
path = external/xz
|
||||
url = https://git.tukaani.org/xz.git
|
||||
ignore = dirty
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.WebProject">
|
||||
<attributes>
|
||||
<attribute name="hide" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.baseBrowserLibrary"/>
|
||||
<classpathentry kind="output" path=""/>
|
||||
</classpath>
|
|
@ -1 +0,0 @@
|
|||
org.eclipse.wst.jsdt.launching.baseBrowserLibrary
|
|
@ -1 +0,0 @@
|
|||
Window
|
23
.tx/config
|
@ -1,23 +0,0 @@
|
|||
[main]
|
||||
host = https://www.transifex.com
|
||||
lang_map = af_ZA: af-rZA, am_ET: am-rET, ar_AE: ar-rAE, ar_BH: ar-rBH, ar_DZ: ar-rDZ, ar_EG: ar-rEG, ar_IQ: ar-rIQ, ar_JO: ar-rJO, ar_KW: ar-rKW, ar_LB: ar-rLB, ar_LY: ar-rLY, ar_MA: ar-rMA, ar_OM: ar-rOM, ar_QA: ar-rQA, ar_SA: ar-rSA, ar_SY: ar-rSY, ar_TN: ar-rTN, ar_YE: ar-rYE, arn_CL: arn-rCL, as_IN: as-rIN, az_AZ: az-rAZ, ba_RU: ba-rRU, be_BY: be-rBY, bg_BG: bg-rBG, bn_BD: bn-rBD, bn_IN: bn-rIN, bo_CN: bo-rCN, br_FR: br-rFR, bs_BA: bs-rBA, ca_ES: ca-rES, co_FR: co-rFR, cs_CZ: cs-rCZ, cy_GB: cy-rGB, da_DK: da-rDK, de_AT: de-rAT, de_CH: de-rCH, de_DE: de-rDE, de_LI: de-rLI, de_LU: de-rLU, dsb_DE: dsb-rDE, dv_MV: dv-rMV, el_GR: el-rGR, en_AU: en-rAU, en_BZ: en-rBZ, en_CA: en-rCA, en_GB: en-rGB, en_IE: en-rIE, en_IN: en-rIN, en_JM: en-rJM, en_MY: en-rMY, en_NZ: en-rNZ, en_PH: en-rPH, en_SG: en-rSG, en_TT: en-rTT, en_US: en-rUS, en_ZA: en-rZA, en_ZW: en-rZW, es_AR: es-rAR, es_BO: es-rBO, es_CL: es-rCL, es_CO: es-rCO, es_CR: es-rCR, es_DO: es-rDO, es_EC: es-rEC, es_ES: es-rES, es_GT: es-rGT, es_HN: es-rHN, es_MX: es-rMX, es_NI: es-rNI, es_PA: es-rPA, es_PE: es-rPE, es_PR: es-rPR, es_PY: es-rPY, es_SV: es-rSV, es_US: es-rUS, es_UY: es-rUY, es_VE: es-rVE, et_EE: et-rEE, eu_ES: eu-rES, fa_IR: fa-rIR, fi_FI: fi-rFI, fil: tl, fil_PH: tl-rPH, fo_FO: fo-rFO, fr_BE: fr-rBE, fr_CA: fr-rCA, fr_CH: fr-rCH, fr_FR: fr-rFR, fr_LU: fr-rLU, fr_MC: fr-rMC, fy_NL: fy-rNL, ga_IE: ga-rIE, gd_GB: gd-rGB, gl_ES: gl-rES, gsw_FR: gsw-rFR, gu_IN: gu-rIN, ha_NG: ha-rNG, he: iw, he_IL: iw-rIL, hi_IN: hi-rIN, hr_BA: hr-rBA, hr_HR: hr-rHR, hsb_DE: hsb-rDE, hu_HU: hu-rHU, hy_AM: hy-rAM, id: in, id_ID: in-rID, ig_NG: ig-rNG, ii_CN: ii-rCN, is_IS: is-rIS, it_CH: it-rCH, it_IT: it-rIT, iu_CA: iu-rCA, ja_JP: ja-rJP, ka_GE: ka-rGE, kk_KZ: kk-rKZ, kl_GL: kl-rGL, km_KH: km-rKH, kn_IN: kn-rIN, ko_KR: ko-rKR, kok_IN: kok-rIN, ky_KG: ky-rKG, lb_LU: lb-rLU, lo_LA: lo-rLA, lt_LT: lt-rLT, lv_LV: lv-rLV, mi_NZ: mi-rNZ, mk_MK: mk-rMK, ml_IN: ml-rIN, mn_CN: mn-rCN, mn_MN: mn-rMN, moh_CA: moh-rCA, mr_IN: mr-rIN, ms_BN: ms-rBN, ms_MY: ms-rMY, mt_MT: mt-rMT, nb_NO: nb-rNO, ne_NP: ne-rNP, nl_BE: nl-rBE, nl_NL: nl-rNL, nn_NO: nn-rNO, nso_ZA: nso-rZA, oc_FR: oc-rFR, or_IN: or-rIN, pa_IN: pa-rIN, pl_PL: pl-rPL, prs_AF: prs-rAF, ps_AF: ps-rAF, pt_BR: pt-rBR, pt_PT: pt-rPT, qut_GT: qut-rGT, quz_BO: quz-rBO, quz_EC: quz-rEC, quz_PE: quz-rPE, rm_CH: rm-rCH, ro_RO: ro-rRO, ru_RU: ru-rRU, rw_RW: rw-rRW, sa_IN: sa-rIN, sah_RU: sah-rRU, se_FI: se-rFI, se_NO: se-rNO, se_SE: se-rSE, si_LK: si-rLK, sk_SK: sk-rSK, sl_SI: sl-rSI, sma_NO: sma-rNO, sma_SE: sma-rSE, smj_NO: smj-rNO, smj_SE: smj-rSE, smn_FI: smn-rFI, sms_FI: sms-rFI, sq_AL: sq-rAL, sr: sr, sv_FI: sv-rFI, sv_SE: sv-rSE, sw_KE: sw-rKE, syr_SY: syr-rSY, ta_IN: ta-rIN, te_IN: te-rIN, tg_TJ: tg-rTJ, th_TH: th-rTH, tk_TM: tk-rTM, tn_ZA: tn-rZA, tr_TR: tr-rTR, tt_RU: tt-rRU, tzm_DZ: tzm-rDZ, ug_CN: ug-rCN, uk_UA: uk-rUA, ur_PK: ur-rPK, uz_UZ: uz-rUZ, vi_VN: vi-rVN, wo_SN: wo-rSN, xh_ZA: xh-rZA, yo_NG: yo-rNG, zh: zh-rCN, zh_HK: zh-rHK, zh_MO: zh-rMO, zh_SG: zh-rSG, zh_TW: zh-rTW, zu_ZA: zu-rZA
|
||||
|
||||
[orbot.stringsxml]
|
||||
file_filter = res/values-<lang>/strings.xml
|
||||
host = https://www.transifex.com
|
||||
source_file = res/values/strings.xml
|
||||
source_lang = en
|
||||
type = ANDROID
|
||||
|
||||
[orbot.description]
|
||||
file_filter = description/<lang>.xlf
|
||||
host = https://www.transifex.com
|
||||
source_file = description/source.xlf
|
||||
source_lang = en
|
||||
type = XLIFF
|
||||
|
||||
#[orbot.olddescription]
|
||||
#file_filter = releases/assets/<lang>-description.txt
|
||||
#host = https://www.transifex.com
|
||||
#source_file = releases/assets/description.txt
|
||||
#source_lang = en
|
|
@ -1,133 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.torproject.android"
|
||||
android:versionName="15.2.0-alpha-1"
|
||||
android:versionCode="15200001"
|
||||
android:installLocation="auto"
|
||||
>
|
||||
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="23"/>
|
||||
<!--
|
||||
<permission android:name="org.torproject.android.MANAGE_TOR"
|
||||
android:label="@string/permission_manage_tor_label"
|
||||
android:description="@string/permission_manage_tor_description"
|
||||
android:protectionLevel="signature"/>
|
||||
|
||||
<uses-permission android:name="org.torproject.android.MANAGE_TOR"/>
|
||||
-->
|
||||
|
||||
<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" />
|
||||
|
||||
<application android:name="org.torproject.android.OrbotApp" android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:description="@string/app_description"
|
||||
android:configChanges="locale|orientation|screenSize"
|
||||
android:theme="@style/DefaultTheme"
|
||||
android:allowBackup="false"
|
||||
android:allowClearUserData="true"
|
||||
android:persistent="true"
|
||||
android:stopWithTask="false"
|
||||
android:largeHeap="false"
|
||||
>
|
||||
|
||||
<activity android:name=".OrbotMainActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
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.DummyActivity"
|
||||
android:theme="@android:style/Theme.Translucent"
|
||||
android:enabled="true"
|
||||
android:allowTaskReparenting="true"
|
||||
android:noHistory="true"
|
||||
android:excludeFromRecents="true"
|
||||
android:alwaysRetainTaskState="false"
|
||||
android:stateNotNeeded="true"
|
||||
android:clearTaskOnLaunch="true"
|
||||
android:finishOnTaskLaunch="true"
|
||||
|
||||
/>
|
||||
|
||||
<activity
|
||||
android:name=".vpn.VPNEnableActivity" android:label="@string/app_name" android:exported="false"
|
||||
android:theme="@android:style/Theme.Translucent"
|
||||
/>
|
||||
|
||||
|
||||
<activity android:name="org.torproject.android.ui.PromoAppsActivity" android:exported="false"/>
|
||||
|
||||
|
||||
<activity android:name=".settings.SettingsPreferences" android:label="@string/app_name"/>
|
||||
<activity android:name=".settings.AppManager" android:label="@string/app_name"/>
|
||||
|
||||
<service
|
||||
android:name=".service.TorService"
|
||||
android:enabled="true"
|
||||
android:permission="android.permission.BIND_VPN_SERVICE"
|
||||
android:stopWithTask="false" >
|
||||
</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="org.torproject.android.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>
|
||||
|
||||
<!--
|
||||
<service android:name="org.torproject.android.vpn.OrbotVpnService"
|
||||
android:permission="android.permission.BIND_VPN_SERVICE">
|
||||
<intent-filter>
|
||||
<action android:name="android.net.VpnService"/>
|
||||
</intent-filter>
|
||||
</service>
|
||||
-->
|
||||
</application>
|
||||
</manifest>
|
32
BUILD
|
@ -8,44 +8,28 @@ Orbot includes, in the external directory, git repo submodules of:
|
|||
- LibEvent
|
||||
- JTorControl: The Tor Control Library for Java
|
||||
|
||||
The Orbot repo also includes the Polipo source code of a recent stable release.
|
||||
|
||||
Please install the following prerequisites (instructions for each follows):
|
||||
ant: http://ant.apache.org/
|
||||
Android Native Dev Kit or NDK (for C/C++ code):
|
||||
http://developer.android.com/sdk/ndk/index.html
|
||||
Android Software Dev Kit or SDK (for Java code):
|
||||
http://developer.android.com/sdk/index.html
|
||||
AutoMake and AutoConf tool
|
||||
sudo apt-get install autotools-dev
|
||||
sudo apt-get install automake
|
||||
sudo apt-get install autogen autoconf libtool gettext-base autopoint
|
||||
|
||||
You will need to run the 'android' command in the SDK to install the necessary
|
||||
Android platform supports (ICS 4.x or android-15)
|
||||
|
||||
Be sure that you have all of the git submodules up-to-date:
|
||||
|
||||
git submodule update --init --recursive
|
||||
./tor-droid-make.sh fetch
|
||||
|
||||
To begin building, from the Orbot root directory, you first need to build all
|
||||
external C/native dependencies:
|
||||
To begin building, from the Orbot root directory, it builds all submodules and
|
||||
the project.
|
||||
|
||||
export ANDROID_NDK_HOME={PATH TO YOUR NDK INSTALL}
|
||||
make -C external
|
||||
|
||||
At this point, you'll have Tor and Polipo binaries that can be run on an
|
||||
Android handset. You can verify the ARM binary was properly built using the
|
||||
following command:
|
||||
|
||||
file external/bin/tor external/bin/polipo
|
||||
|
||||
You should see something like:
|
||||
external/bin/tor: ELF 32-bit LSB executable, ARM, version 1 (SYSV),
|
||||
dynamically linked (uses shared libs), not stripped
|
||||
external/bin/polipo: ELF 32-bit LSB executable, ARM, version 1 (SYSV),
|
||||
dynamically linked (uses shared libs), not stripped
|
||||
|
||||
This isn't enough though and we'll now sew up the binary into a small package
|
||||
that will handle basic Tor controlling features.
|
||||
|
||||
android update project --name Orbot --target android-15 --path .
|
||||
./tor-droid-make.sh build
|
||||
|
||||
Now build the Android app
|
||||
|
||||
|
|
40
INSTALL
|
@ -1,40 +0,0 @@
|
|||
|
||||
Orbot: Android Onion Routing Robot
|
||||
***********************************************
|
||||
Android is a freely licensed open-source application developed for the
|
||||
Android platform. It acts as a front-end to the Tor binary application,
|
||||
and also provides an HTTP Proxy for connecting web browsers and other
|
||||
HTTP client applications into the Tor SOCKS interface.
|
||||
|
||||
*** PreReqs ***
|
||||
You need the Android Developer SDK to compile and build the APK file:
|
||||
http://developer.android.com/
|
||||
The project files including in this distribution are meant to be used
|
||||
with Eclipse: http://eclipse.org/
|
||||
|
||||
*** How to use ***
|
||||
Currently, Orbot will work with any application that supports using
|
||||
an HTTP or SOCKS Proxy. Once you start Tor using Orbot, you can then
|
||||
point your application to HTTP port 8118 or SOCKS port 9050 on localhost
|
||||
IP 127.0.0.1.
|
||||
|
||||
If you have root access, and a version of Android with iptables that
|
||||
supports the netfilter owner module, then Orbot can setup transparent
|
||||
proxying for any application.
|
||||
|
||||
Finally, Firefox on Android is available, and we have released an
|
||||
add-on named ProxyMob for configuring SOCKS proxy settings for use
|
||||
with the local Tor socks proxy.
|
||||
|
||||
** A Few Notes **
|
||||
|
||||
To ensure you are connected to the Tor network, you should browse to
|
||||
https://check.torproject.org/
|
||||
|
||||
The first time you activate Orbot, it may take a while to connect to
|
||||
Tor. Check the message log to ensure you are "100% Bootstrapped".
|
||||
|
||||
***********************************************
|
||||
|
||||
/* Copyright (c) 2009,2010,2011, Nathan Freitas, The Guardian Project
|
||||
* http://guardianproject.info/ */
|
35
LICENSE
|
@ -41,6 +41,41 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*****
|
||||
|
||||
===============================================================================
|
||||
Orbot includes the Obfs4 pluggable transport:
|
||||
https://gitweb.torproject.org/pluggable-transports/obfs4.git
|
||||
|
||||
Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
===============================================================================
|
||||
|
||||
|
||||
*****
|
||||
|
||||
*****
|
||||
Orbot contains a binary distribution of Privoxy (http://www.privoxy.org/)
|
||||
Copyright © 2001-2010 by Privoxy Developers <ijbswa-developers@lists.sourceforge.net>
|
||||
|
|
38
README
|
@ -1,15 +1,28 @@
|
|||
|
||||
Orbot: Android Onion Routing Robot
|
||||
Tor Android
|
||||
***********************************************
|
||||
Orbot is a freely licensed open-source application developed for the
|
||||
Android platform. It acts as a front-end to the Tor binary application,
|
||||
and also provides an HTTP Proxy for connecting web browsers and other
|
||||
HTTP client applications into the Tor SOCKS interface.
|
||||
This is a project forked from Orbot for building the tor binary for Android
|
||||
|
||||
How to Build
|
||||
***********************************************
|
||||
Orbot is a component of the Guardian Project, an effort to develop
|
||||
a secure, anonymous smartphone for use by human rights activists, journalists
|
||||
and others around the world. Learn more: https://guardianproject.info/
|
||||
Please see: https://raw.githubusercontent.com/n8fr8/tor-android/master/BUILD
|
||||
|
||||
How to Use via Gradle
|
||||
***********************************************
|
||||
|
||||
Add the repository your list as shown:
|
||||
|
||||
repositories {
|
||||
maven { url "https://raw.githubusercontent.com/guardianproject/gpmaven/master" }
|
||||
}
|
||||
|
||||
and then add the dependency, setting it to the latest version (or any version) we have made available, as a release:
|
||||
|
||||
dependencies {
|
||||
compile 'org.torproject:tor-android-binary:0.3.1.9'
|
||||
}
|
||||
|
||||
|
||||
|
||||
***********************************************
|
||||
Tor protects your privacy on the internet by hiding the connection
|
||||
|
@ -21,12 +34,3 @@ Tor Frequently Asked Questions:
|
|||
https://wiki.torproject.org/noreply/TheOnionRouter/TorFAQ
|
||||
https://www.torproject.org/faq.html.en
|
||||
|
||||
***********************************************
|
||||
/* Copyright (c) 2009-2013, Nathan Freitas, The Guardian Project
|
||||
* https://guardianproject.info/ */
|
||||
|
||||
*****
|
||||
|
||||
Some icons thanks to:
|
||||
https://drslash.com/flat-osx-pack/
|
||||
FLAT OS by DrSlash.com is licensed under a Creative Commons Attribution – NonCommercial 4.0 International License.
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
#Android specific
|
||||
bin
|
||||
gen
|
||||
obj
|
||||
libs/armeabi
|
||||
lint.xml
|
||||
local.properties
|
||||
release.properties
|
||||
ant.properties
|
||||
*.class
|
||||
*.apk
|
||||
|
||||
#Gradle
|
||||
.gradle
|
||||
build
|
||||
gradle.properties
|
||||
gradlew
|
||||
gradlew.bat
|
||||
gradle
|
||||
|
||||
#Maven
|
||||
target
|
||||
pom.xml.*
|
||||
|
||||
#Eclipse
|
||||
.project
|
||||
.classpath
|
||||
.settings
|
||||
.metadata
|
||||
|
||||
#IntelliJ IDEA
|
||||
.idea
|
||||
*.iml
|
|
@ -1,28 +0,0 @@
|
|||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 25
|
||||
buildToolsVersion "25"
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
jni.srcDirs = []
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 9
|
||||
targetSdkVersion 25
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.sufficientlysecure.rootcommands"
|
||||
android:versionCode="3"
|
||||
android:versionName="1.2" >
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="7"
|
||||
android:targetSdkVersion="17" />
|
||||
|
||||
<application />
|
||||
|
||||
</manifest>
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (c) 2012 Stephen Erickson, Chris Ravenscroft, Adam Shanks (RootTools)
|
||||
*
|
||||
* 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 org.sufficientlysecure.rootcommands;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class Mount {
|
||||
protected final File mDevice;
|
||||
protected final File mMountPoint;
|
||||
protected final String mType;
|
||||
protected final Set<String> mFlags;
|
||||
|
||||
Mount(File device, File path, String type, String flagsStr) {
|
||||
mDevice = device;
|
||||
mMountPoint = path;
|
||||
mType = type;
|
||||
mFlags = new HashSet<String>(Arrays.asList(flagsStr.split(",")));
|
||||
}
|
||||
|
||||
public File getDevice() {
|
||||
return mDevice;
|
||||
}
|
||||
|
||||
public File getMountPoint() {
|
||||
return mMountPoint;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
public Set<String> getFlags() {
|
||||
return mFlags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s on %s type %s %s", mDevice, mMountPoint, mType, mFlags);
|
||||
}
|
||||
}
|
|
@ -1,191 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (c) 2012 Stephen Erickson, Chris Ravenscroft, Adam Shanks (RootTools)
|
||||
*
|
||||
* 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 org.sufficientlysecure.rootcommands;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.LineNumberReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.sufficientlysecure.rootcommands.command.SimpleCommand;
|
||||
import org.sufficientlysecure.rootcommands.util.Log;
|
||||
|
||||
//no modifier, this means it is package-private. Only our internal classes can use this.
|
||||
class Remounter {
|
||||
|
||||
private Shell shell;
|
||||
|
||||
public Remounter(Shell shell) {
|
||||
super();
|
||||
this.shell = shell;
|
||||
}
|
||||
|
||||
/**
|
||||
* This will take a path, which can contain the file name as well, and attempt to remount the
|
||||
* underlying partition.
|
||||
* <p/>
|
||||
* For example, passing in the following string:
|
||||
* "/system/bin/some/directory/that/really/would/never/exist" will result in /system ultimately
|
||||
* being remounted. However, keep in mind that the longer the path you supply, the more work
|
||||
* this has to do, and the slower it will run.
|
||||
*
|
||||
* @param file
|
||||
* file path
|
||||
* @param mountType
|
||||
* mount type: pass in RO (Read only) or RW (Read Write)
|
||||
* @return a <code>boolean</code> which indicates whether or not the partition has been
|
||||
* remounted as specified.
|
||||
*/
|
||||
protected boolean remount(String file, String mountType) {
|
||||
|
||||
// if the path has a trailing slash get rid of it.
|
||||
if (file.endsWith("/") && !file.equals("/")) {
|
||||
file = file.substring(0, file.lastIndexOf("/"));
|
||||
}
|
||||
// Make sure that what we are trying to remount is in the mount list.
|
||||
boolean foundMount = false;
|
||||
while (!foundMount) {
|
||||
try {
|
||||
for (Mount mount : getMounts()) {
|
||||
Log.d(RootCommands.TAG, mount.getMountPoint().toString());
|
||||
|
||||
if (file.equals(mount.getMountPoint().toString())) {
|
||||
foundMount = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(RootCommands.TAG, "Exception", e);
|
||||
return false;
|
||||
}
|
||||
if (!foundMount) {
|
||||
try {
|
||||
file = (new File(file).getParent()).toString();
|
||||
} catch (Exception e) {
|
||||
Log.e(RootCommands.TAG, "Exception", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Mount mountPoint = findMountPointRecursive(file);
|
||||
|
||||
Log.d(RootCommands.TAG, "Remounting " + mountPoint.getMountPoint().getAbsolutePath()
|
||||
+ " as " + mountType.toLowerCase(Locale.US));
|
||||
final boolean isMountMode = mountPoint.getFlags().contains(mountType.toLowerCase(Locale.US));
|
||||
|
||||
if (!isMountMode) {
|
||||
// grab an instance of the internal class
|
||||
try {
|
||||
SimpleCommand command = new SimpleCommand("busybox mount -o remount,"
|
||||
+ mountType.toLowerCase(Locale.US) + " " + mountPoint.getDevice().getAbsolutePath()
|
||||
+ " " + mountPoint.getMountPoint().getAbsolutePath(),
|
||||
"toolbox mount -o remount," + mountType.toLowerCase(Locale.US) + " "
|
||||
+ mountPoint.getDevice().getAbsolutePath() + " "
|
||||
+ mountPoint.getMountPoint().getAbsolutePath(), "mount -o remount,"
|
||||
+ mountType.toLowerCase(Locale.US) + " "
|
||||
+ mountPoint.getDevice().getAbsolutePath() + " "
|
||||
+ mountPoint.getMountPoint().getAbsolutePath(),
|
||||
"/system/bin/toolbox mount -o remount," + mountType.toLowerCase(Locale.US) + " "
|
||||
+ mountPoint.getDevice().getAbsolutePath() + " "
|
||||
+ mountPoint.getMountPoint().getAbsolutePath());
|
||||
|
||||
// execute on shell
|
||||
shell.add(command).waitForFinish();
|
||||
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
mountPoint = findMountPointRecursive(file);
|
||||
}
|
||||
|
||||
if (mountPoint != null) {
|
||||
Log.d(RootCommands.TAG, mountPoint.getFlags() + " AND " + mountType.toLowerCase(Locale.US));
|
||||
if (mountPoint.getFlags().contains(mountType.toLowerCase(Locale.US))) {
|
||||
Log.d(RootCommands.TAG, mountPoint.getFlags().toString());
|
||||
return true;
|
||||
} else {
|
||||
Log.d(RootCommands.TAG, mountPoint.getFlags().toString());
|
||||
}
|
||||
} else {
|
||||
Log.d(RootCommands.TAG, "mountPoint is null");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Mount findMountPointRecursive(String file) {
|
||||
try {
|
||||
ArrayList<Mount> mounts = getMounts();
|
||||
for (File path = new File(file); path != null;) {
|
||||
for (Mount mount : mounts) {
|
||||
if (mount.getMountPoint().equals(path)) {
|
||||
return mount;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (Exception e) {
|
||||
Log.e(RootCommands.TAG, "Exception", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This will return an ArrayList of the class Mount. The class mount contains the following
|
||||
* property's: device mountPoint type flags
|
||||
* <p/>
|
||||
* These will provide you with any information you need to work with the mount points.
|
||||
*
|
||||
* @return <code>ArrayList<Mount></code> an ArrayList of the class Mount.
|
||||
* @throws Exception
|
||||
* if we cannot return the mount points.
|
||||
*/
|
||||
protected static ArrayList<Mount> getMounts() throws Exception {
|
||||
|
||||
final String tempFile = "/data/local/RootToolsMounts";
|
||||
|
||||
// copy /proc/mounts to tempfile. Directly reading it does not work on 4.3
|
||||
Shell shell = Shell.startRootShell();
|
||||
Toolbox tb = new Toolbox(shell);
|
||||
tb.copyFile("/proc/mounts", tempFile, false, false);
|
||||
tb.setFilePermissions(tempFile, "777");
|
||||
shell.close();
|
||||
|
||||
LineNumberReader lnr = null;
|
||||
lnr = new LineNumberReader(new FileReader(tempFile));
|
||||
String line;
|
||||
ArrayList<Mount> mounts = new ArrayList<Mount>();
|
||||
while ((line = lnr.readLine()) != null) {
|
||||
|
||||
Log.d(RootCommands.TAG, line);
|
||||
|
||||
String[] fields = line.split(" ");
|
||||
mounts.add(new Mount(new File(fields[0]), // device
|
||||
new File(fields[1]), // mountPoint
|
||||
fields[2], // fstype
|
||||
fields[3] // flags
|
||||
));
|
||||
}
|
||||
lnr.close();
|
||||
|
||||
return mounts;
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* 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 org.sufficientlysecure.rootcommands;
|
||||
|
||||
import org.sufficientlysecure.rootcommands.util.Log;
|
||||
|
||||
public class RootCommands {
|
||||
public static boolean DEBUG = false;
|
||||
public static int DEFAULT_TIMEOUT = 10000;
|
||||
|
||||
public static final String TAG = "RootCommands";
|
||||
|
||||
/**
|
||||
* General method to check if user has su binary and accepts root access for this program!
|
||||
*
|
||||
* @return true if everything worked
|
||||
*/
|
||||
public static boolean rootAccessGiven() {
|
||||
boolean rootAccess = false;
|
||||
|
||||
try {
|
||||
Shell rootShell = Shell.startRootShell();
|
||||
|
||||
Toolbox tb = new Toolbox(rootShell);
|
||||
if (tb.isRootAccessGiven()) {
|
||||
rootAccess = true;
|
||||
}
|
||||
|
||||
rootShell.close();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Problem while checking for root access!", e);
|
||||
}
|
||||
|
||||
return rootAccess;
|
||||
}
|
||||
}
|
|
@ -1,350 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (c) 2012 Stephen Erickson, Chris Ravenscroft, Adam Shanks, Jeremy Lakeman (RootTools)
|
||||
*
|
||||
* 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 org.sufficientlysecure.rootcommands;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.Closeable;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.sufficientlysecure.rootcommands.command.Command;
|
||||
import org.sufficientlysecure.rootcommands.util.Log;
|
||||
import org.sufficientlysecure.rootcommands.util.RootAccessDeniedException;
|
||||
import org.sufficientlysecure.rootcommands.util.Utils;
|
||||
|
||||
public class Shell implements Closeable {
|
||||
private final Process shellProcess;
|
||||
private final BufferedReader stdOutErr;
|
||||
private final DataOutputStream outputStream;
|
||||
private final List<Command> commands = new ArrayList<Command>();
|
||||
private boolean close = false;
|
||||
|
||||
private static final String LD_LIBRARY_PATH = System.getenv("LD_LIBRARY_PATH");
|
||||
private static final String token = "F*D^W@#FGF";
|
||||
|
||||
/**
|
||||
* Start root shell
|
||||
*
|
||||
* @param customEnv
|
||||
* @param baseDirectory
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static Shell startRootShell(ArrayList<String> customEnv, String baseDirectory)
|
||||
throws IOException, RootAccessDeniedException {
|
||||
Log.d(RootCommands.TAG, "Starting Root Shell!");
|
||||
|
||||
// On some versions of Android (ICS) LD_LIBRARY_PATH is unset when using su
|
||||
// We need to pass LD_LIBRARY_PATH over su for some commands to work correctly.
|
||||
if (customEnv == null) {
|
||||
customEnv = new ArrayList<String>();
|
||||
}
|
||||
customEnv.add("LD_LIBRARY_PATH=" + LD_LIBRARY_PATH);
|
||||
|
||||
Shell shell = new Shell(Utils.getSuPath(), customEnv, baseDirectory);
|
||||
|
||||
return shell;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start root shell without custom environment and base directory
|
||||
*
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static Shell startRootShell() throws IOException, RootAccessDeniedException {
|
||||
return startRootShell(null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start default sh shell
|
||||
*
|
||||
* @param customEnv
|
||||
* @param baseDirectory
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static Shell startShell(ArrayList<String> customEnv, String baseDirectory)
|
||||
throws IOException {
|
||||
Log.d(RootCommands.TAG, "Starting Shell!");
|
||||
Shell shell = new Shell("sh", customEnv, baseDirectory);
|
||||
return shell;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start default sh shell without custom environment and base directory
|
||||
*
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static Shell startShell() throws IOException {
|
||||
return startShell(null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start custom shell defined by shellPath
|
||||
*
|
||||
* @param shellPath
|
||||
* @param customEnv
|
||||
* @param baseDirectory
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static Shell startCustomShell(String shellPath, ArrayList<String> customEnv,
|
||||
String baseDirectory) throws IOException {
|
||||
Log.d(RootCommands.TAG, "Starting Custom Shell!");
|
||||
Shell shell = new Shell(shellPath, customEnv, baseDirectory);
|
||||
|
||||
return shell;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start custom shell without custom environment and base directory
|
||||
*
|
||||
* @param shellPath
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static Shell startCustomShell(String shellPath) throws IOException {
|
||||
return startCustomShell(shellPath, null, null);
|
||||
}
|
||||
|
||||
private Shell(String shell, ArrayList<String> customEnv, String baseDirectory)
|
||||
throws IOException, RootAccessDeniedException {
|
||||
Log.d(RootCommands.TAG, "Starting shell: " + shell);
|
||||
|
||||
// start shell process!
|
||||
shellProcess = Utils.runWithEnv(shell, customEnv, baseDirectory);
|
||||
|
||||
// StdErr is redirected to StdOut, defined in Command.getCommand()
|
||||
stdOutErr = new BufferedReader(new InputStreamReader(shellProcess.getInputStream()));
|
||||
outputStream = new DataOutputStream(shellProcess.getOutputStream());
|
||||
|
||||
outputStream.write("echo Started\n".getBytes());
|
||||
outputStream.flush();
|
||||
|
||||
while (true) {
|
||||
String line = stdOutErr.readLine();
|
||||
if (line == null)
|
||||
throw new RootAccessDeniedException(
|
||||
"stdout line is null! Access was denied or this executeable is not a shell!");
|
||||
if ("".equals(line))
|
||||
continue;
|
||||
if ("Started".equals(line))
|
||||
break;
|
||||
|
||||
destroyShellProcess();
|
||||
throw new IOException("Unable to start shell, unexpected output \"" + line + "\"");
|
||||
}
|
||||
|
||||
new Thread(inputRunnable, "Shell Input").start();
|
||||
new Thread(outputRunnable, "Shell Output").start();
|
||||
}
|
||||
|
||||
private Runnable inputRunnable = new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
writeCommands();
|
||||
} catch (IOException e) {
|
||||
Log.e(RootCommands.TAG, "IO Exception", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private Runnable outputRunnable = new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
readOutput();
|
||||
} catch (IOException e) {
|
||||
Log.e(RootCommands.TAG, "IOException", e);
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(RootCommands.TAG, "InterruptedException", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy shell process considering that the process could already be terminated
|
||||
*/
|
||||
private void destroyShellProcess() {
|
||||
try {
|
||||
// Yes, this really is the way to check if the process is
|
||||
// still running.
|
||||
shellProcess.exitValue();
|
||||
} catch (IllegalThreadStateException e) {
|
||||
// Only call destroy() if the process is still running;
|
||||
// Calling it for a terminated process will not crash, but
|
||||
// (starting with at least ICS/4.0) spam the log with INFO
|
||||
// messages ala "Failed to destroy process" and "kill
|
||||
// failed: ESRCH (No such process)".
|
||||
shellProcess.destroy();
|
||||
}
|
||||
|
||||
Log.d(RootCommands.TAG, "Shell destroyed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes queued commands one after another into the opened shell. After an execution a token is
|
||||
* written to seperate command output on read
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private void writeCommands() throws IOException {
|
||||
try {
|
||||
int commandIndex = 0;
|
||||
while (true) {
|
||||
DataOutputStream out;
|
||||
synchronized (commands) {
|
||||
while (!close && commandIndex >= commands.size()) {
|
||||
commands.wait();
|
||||
}
|
||||
out = this.outputStream;
|
||||
}
|
||||
if (commandIndex < commands.size()) {
|
||||
Command next = commands.get(commandIndex);
|
||||
next.writeCommand(out);
|
||||
String line = "\necho " + token + " " + commandIndex + " $?\n";
|
||||
out.write(line.getBytes());
|
||||
out.flush();
|
||||
commandIndex++;
|
||||
} else if (close) {
|
||||
out.write("\nexit 0\n".getBytes());
|
||||
out.flush();
|
||||
Log.d(RootCommands.TAG, "Closing shell");
|
||||
shellProcess.waitFor();
|
||||
out.close();
|
||||
return;
|
||||
} else {
|
||||
Thread.sleep(50);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(RootCommands.TAG, "interrupted while writing command", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads output line by line, seperated by token written after every command
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
private void readOutput() throws IOException, InterruptedException {
|
||||
Command command = null;
|
||||
|
||||
// index of current command
|
||||
int commandIndex = 0;
|
||||
while (true) {
|
||||
String lineStdOut = stdOutErr.readLine();
|
||||
|
||||
// terminate on EOF
|
||||
if (lineStdOut == null)
|
||||
break;
|
||||
|
||||
if (command == null) {
|
||||
|
||||
// break on close after last command
|
||||
if (commandIndex >= commands.size()) {
|
||||
if (close)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
// get current command
|
||||
command = commands.get(commandIndex);
|
||||
}
|
||||
|
||||
int pos = lineStdOut.indexOf(token);
|
||||
if (pos > 0) {
|
||||
command.processOutput(lineStdOut.substring(0, pos));
|
||||
}
|
||||
if (pos >= 0) {
|
||||
lineStdOut = lineStdOut.substring(pos);
|
||||
String fields[] = lineStdOut.split(" ");
|
||||
int id = Integer.parseInt(fields[1]);
|
||||
if (id == commandIndex) {
|
||||
command.setExitCode(Integer.parseInt(fields[2]));
|
||||
|
||||
// go to next command
|
||||
commandIndex++;
|
||||
command = null;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
command.processOutput(lineStdOut);
|
||||
}
|
||||
Log.d(RootCommands.TAG, "Read all output");
|
||||
shellProcess.waitFor();
|
||||
stdOutErr.close();
|
||||
destroyShellProcess();
|
||||
|
||||
while (commandIndex < commands.size()) {
|
||||
if (command == null) {
|
||||
command = commands.get(commandIndex);
|
||||
}
|
||||
command.terminated("Unexpected Termination!");
|
||||
commandIndex++;
|
||||
command = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add command to shell queue
|
||||
*
|
||||
* @param command
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public Command add(Command command) throws IOException {
|
||||
if (close)
|
||||
throw new IOException("Unable to add commands to a closed shell");
|
||||
synchronized (commands) {
|
||||
commands.add(command);
|
||||
// set shell on the command object, to know where the command is running on
|
||||
command.addedToShell(this, (commands.size() - 1));
|
||||
commands.notifyAll();
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close shell
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
synchronized (commands) {
|
||||
this.close = true;
|
||||
commands.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns number of queued commands
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getCommandsSize() {
|
||||
return commands.size();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* 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 org.sufficientlysecure.rootcommands;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.location.LocationManager;
|
||||
import android.os.PowerManager;
|
||||
import android.provider.Settings;
|
||||
|
||||
/**
|
||||
* This methods work when the apk is installed as a system app (under /system/app)
|
||||
*/
|
||||
public class SystemCommands {
|
||||
Context context;
|
||||
|
||||
public SystemCommands(Context context) {
|
||||
super();
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get GPS status
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean getGPS() {
|
||||
return ((LocationManager) context.getSystemService(Context.LOCATION_SERVICE))
|
||||
.isProviderEnabled(LocationManager.GPS_PROVIDER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/Disable GPS
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
@TargetApi(8)
|
||||
public void setGPS(boolean value) {
|
||||
ContentResolver localContentResolver = context.getContentResolver();
|
||||
Settings.Secure.setLocationProviderEnabled(localContentResolver,
|
||||
LocationManager.GPS_PROVIDER, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Not ready yet
|
||||
*/
|
||||
@TargetApi(8)
|
||||
public void reboot() {
|
||||
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||||
pm.reboot("recovery");
|
||||
pm.reboot(null);
|
||||
|
||||
// not working:
|
||||
// reboot(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reboot the device immediately, passing 'reason' (may be null) to the underlying __reboot
|
||||
* system call. Should not return.
|
||||
*
|
||||
* Taken from com.android.server.PowerManagerService.reboot
|
||||
*/
|
||||
// public void reboot(String reason) {
|
||||
//
|
||||
// // final String finalReason = reason;
|
||||
// Runnable runnable = new Runnable() {
|
||||
// public void run() {
|
||||
// synchronized (this) {
|
||||
// // ShutdownThread.reboot(mContext, finalReason, false);
|
||||
// try {
|
||||
// Class<?> clazz = Class.forName("com.android.internal.app.ShutdownThread");
|
||||
//
|
||||
// // if (mReboot) {
|
||||
// Method method = clazz.getMethod("reboot", Context.class, String.class,
|
||||
// Boolean.TYPE);
|
||||
// method.invoke(null, context, null, false);
|
||||
//
|
||||
// // if (mReboot) {
|
||||
// // Method method = clazz.getMethod("reboot", Context.class, String.class,
|
||||
// // Boolean.TYPE);
|
||||
// // method.invoke(null, mContext, mReason, mConfirm);
|
||||
// // } else {
|
||||
// // Method method = clazz.getMethod("shutdown", Context.class, Boolean.TYPE);
|
||||
// // method.invoke(null, mContext, mConfirm);
|
||||
// // }
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// };
|
||||
// // ShutdownThread must run on a looper capable of displaying the UI.
|
||||
// mHandler.post(runnable);
|
||||
//
|
||||
// // PowerManager.reboot() is documented not to return so just wait for the inevitable.
|
||||
// // synchronized (runnable) {
|
||||
// // while (true) {
|
||||
// // try {
|
||||
// // runnable.wait();
|
||||
// // } catch (InterruptedException e) {
|
||||
// // }
|
||||
// // }
|
||||
// // }
|
||||
// }
|
||||
|
||||
}
|
|
@ -1,824 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (c) 2012 Stephen Erickson, Chris Ravenscroft, Adam Shanks (RootTools)
|
||||
*
|
||||
* 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 org.sufficientlysecure.rootcommands;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.sufficientlysecure.rootcommands.command.ExecutableCommand;
|
||||
import org.sufficientlysecure.rootcommands.command.Command;
|
||||
import org.sufficientlysecure.rootcommands.command.SimpleCommand;
|
||||
import org.sufficientlysecure.rootcommands.util.BrokenBusyboxException;
|
||||
import org.sufficientlysecure.rootcommands.util.Log;
|
||||
|
||||
import android.os.StatFs;
|
||||
import android.os.SystemClock;
|
||||
|
||||
/**
|
||||
* All methods in this class are working with Androids toolbox. Toolbox is similar to busybox, but
|
||||
* normally shipped on every Android OS. You can find toolbox commands on
|
||||
* https://github.com/CyanogenMod/android_system_core/tree/ics/toolbox
|
||||
*
|
||||
* This means that these commands are designed to work on every Android OS, with a _working_ toolbox
|
||||
* binary on it. They don't require busybox!
|
||||
*
|
||||
*/
|
||||
public class Toolbox {
|
||||
private Shell shell;
|
||||
|
||||
/**
|
||||
* All methods in this class are working with Androids toolbox. Toolbox is similar to busybox,
|
||||
* but normally shipped on every Android OS.
|
||||
*
|
||||
* @param shell
|
||||
* where to execute commands on
|
||||
*/
|
||||
public Toolbox(Shell shell) {
|
||||
super();
|
||||
this.shell = shell;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if user accepted root access
|
||||
*
|
||||
* (commands: id)
|
||||
*
|
||||
* @return true if user has given root access
|
||||
* @throws IOException
|
||||
* @throws TimeoutException
|
||||
* @throws BrokenBusyboxException
|
||||
*/
|
||||
public boolean isRootAccessGiven() throws BrokenBusyboxException, TimeoutException, IOException {
|
||||
SimpleCommand idCommand = new SimpleCommand("id");
|
||||
shell.add(idCommand).waitForFinish();
|
||||
|
||||
if (idCommand.getOutput().contains("uid=0")) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This command class gets all pids to a given process name
|
||||
*/
|
||||
private class PsCommand extends Command {
|
||||
private String processName;
|
||||
private ArrayList<String> pids;
|
||||
private String psRegex;
|
||||
private Pattern psPattern;
|
||||
|
||||
public PsCommand(String processName) {
|
||||
super("ps");
|
||||
this.processName = processName;
|
||||
pids = new ArrayList<String>();
|
||||
|
||||
/**
|
||||
* regex to get pid out of ps line, example:
|
||||
*
|
||||
* <pre>
|
||||
* root 24736 1 12140 584 ffffffff 40010d14 S /data/data/org.adaway/files/blank_webserver
|
||||
* ^\\S \\s ([0-9]+) .* processName $
|
||||
* </pre>
|
||||
*/
|
||||
psRegex = "^\\S+\\s+([0-9]+).*" + Pattern.quote(processName) + "$";
|
||||
psPattern = Pattern.compile(psRegex);
|
||||
}
|
||||
|
||||
public ArrayList<String> getPids() {
|
||||
return pids;
|
||||
}
|
||||
|
||||
public String getPidsString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String s : pids) {
|
||||
sb.append(s);
|
||||
sb.append(" ");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void output(int id, String line) {
|
||||
// general check if line contains processName
|
||||
if (line.contains(processName)) {
|
||||
Matcher psMatcher = psPattern.matcher(line);
|
||||
|
||||
// try to match line exactly
|
||||
try {
|
||||
if (psMatcher.find()) {
|
||||
String pid = psMatcher.group(1);
|
||||
// add to pids list
|
||||
pids.add(pid);
|
||||
Log.d(RootCommands.TAG, "Found pid: " + pid);
|
||||
} else {
|
||||
Log.d(RootCommands.TAG, "Matching in ps command failed!");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(RootCommands.TAG, "Error with regex!", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterExecution(int id, int exitCode) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method can be used to kill a running process
|
||||
*
|
||||
* (commands: ps, kill)
|
||||
*
|
||||
* @param processName
|
||||
* name of process to kill
|
||||
* @return <code>true</code> if process was found and killed successfully
|
||||
* @throws IOException
|
||||
* @throws TimeoutException
|
||||
* @throws BrokenBusyboxException
|
||||
*/
|
||||
public boolean killAll(String processName) throws BrokenBusyboxException, TimeoutException,
|
||||
IOException {
|
||||
Log.d(RootCommands.TAG, "Killing process " + processName);
|
||||
|
||||
PsCommand psCommand = new PsCommand(processName);
|
||||
shell.add(psCommand).waitForFinish();
|
||||
|
||||
// kill processes
|
||||
if (!psCommand.getPids().isEmpty()) {
|
||||
// example: kill -9 1234 1222 5343
|
||||
SimpleCommand killCommand = new SimpleCommand("kill -9 "
|
||||
+ psCommand.getPidsString());
|
||||
shell.add(killCommand).waitForFinish();
|
||||
|
||||
if (killCommand.getExitCode() == 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
Log.d(RootCommands.TAG, "No pid found! Nothing was killed!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Kill a running executable
|
||||
*
|
||||
* See README for more information how to use your own executables!
|
||||
*
|
||||
* @param executableName
|
||||
* @return
|
||||
* @throws BrokenBusyboxException
|
||||
* @throws TimeoutException
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean killAllExecutable(String executableName) throws BrokenBusyboxException,
|
||||
TimeoutException, IOException {
|
||||
return killAll(ExecutableCommand.EXECUTABLE_PREFIX + executableName + ExecutableCommand.EXECUTABLE_SUFFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method can be used to to check if a process is running
|
||||
*
|
||||
* @param processName
|
||||
* name of process to check
|
||||
* @return <code>true</code> if process was found
|
||||
* @throws IOException
|
||||
* @throws BrokenBusyboxException
|
||||
* @throws TimeoutException
|
||||
* (Could not determine if the process is running)
|
||||
*/
|
||||
public boolean isProcessRunning(String processName) throws BrokenBusyboxException,
|
||||
TimeoutException, IOException {
|
||||
PsCommand psCommand = new PsCommand(processName);
|
||||
shell.add(psCommand).waitForFinish();
|
||||
|
||||
// if pids are available process is running!
|
||||
if (!psCommand.getPids().isEmpty()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if binary is running
|
||||
*
|
||||
* @param binaryName
|
||||
* @return
|
||||
* @throws BrokenBusyboxException
|
||||
* @throws TimeoutException
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean isBinaryRunning(String binaryName) throws BrokenBusyboxException,
|
||||
TimeoutException, IOException {
|
||||
return isProcessRunning(ExecutableCommand.EXECUTABLE_PREFIX + binaryName
|
||||
+ ExecutableCommand.EXECUTABLE_SUFFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ls command to get permissions or symlinks
|
||||
*/
|
||||
private class LsCommand extends Command {
|
||||
private String fileName;
|
||||
private String permissionRegex;
|
||||
private Pattern permissionPattern;
|
||||
private String symlinkRegex;
|
||||
private Pattern symlinkPattern;
|
||||
|
||||
private String symlink;
|
||||
private String permissions;
|
||||
|
||||
public String getSymlink() {
|
||||
return symlink;
|
||||
}
|
||||
|
||||
public String getPermissions() {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
public LsCommand(String file) {
|
||||
super("ls -l " + file);
|
||||
|
||||
// get only filename:
|
||||
this.fileName = (new File(file)).getName();
|
||||
Log.d(RootCommands.TAG, "fileName: " + fileName);
|
||||
|
||||
/**
|
||||
* regex to get pid out of ps line, example:
|
||||
*
|
||||
* <pre>
|
||||
* with busybox:
|
||||
* lrwxrwxrwx 1 root root 15 Aug 13 12:14 dev/stdin -> /proc/self/fd/0
|
||||
*
|
||||
* with toolbox:
|
||||
* lrwxrwxrwx root root 15 Aug 13 12:14 stdin -> /proc/self/fd/0
|
||||
*
|
||||
* Regex:
|
||||
* ^.*?(\\S{10}) .* $
|
||||
* </pre>
|
||||
*/
|
||||
permissionRegex = "^.*?(\\S{10}).*$";
|
||||
permissionPattern = Pattern.compile(permissionRegex);
|
||||
|
||||
/**
|
||||
* regex to get symlink
|
||||
*
|
||||
* <pre>
|
||||
* -> /proc/self/fd/0
|
||||
* ^.*?\\-\\> \\s+ (.*) $
|
||||
* </pre>
|
||||
*/
|
||||
symlinkRegex = "^.*?\\-\\>\\s+(.*)$";
|
||||
symlinkPattern = Pattern.compile(symlinkRegex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts permission string from ls command to numerical value. Example: -rwxrwxrwx gets
|
||||
* to 777
|
||||
*
|
||||
* @param permissions
|
||||
* @return
|
||||
*/
|
||||
private String convertPermissions(String permissions) {
|
||||
int owner = getGroupPermission(permissions.substring(1, 4));
|
||||
int group = getGroupPermission(permissions.substring(4, 7));
|
||||
int world = getGroupPermission(permissions.substring(7, 10));
|
||||
|
||||
return "" + owner + group + world;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates permission for one group
|
||||
*
|
||||
* @param permission
|
||||
* @return value of permission string
|
||||
*/
|
||||
private int getGroupPermission(String permission) {
|
||||
int value = 0;
|
||||
|
||||
if (permission.charAt(0) == 'r') {
|
||||
value += 4;
|
||||
}
|
||||
if (permission.charAt(1) == 'w') {
|
||||
value += 2;
|
||||
}
|
||||
if (permission.charAt(2) == 'x') {
|
||||
value += 1;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void output(int id, String line) {
|
||||
// general check if line contains file
|
||||
if (line.contains(fileName)) {
|
||||
|
||||
// try to match line exactly
|
||||
try {
|
||||
Matcher permissionMatcher = permissionPattern.matcher(line);
|
||||
if (permissionMatcher.find()) {
|
||||
permissions = convertPermissions(permissionMatcher.group(1));
|
||||
|
||||
Log.d(RootCommands.TAG, "Found permissions: " + permissions);
|
||||
} else {
|
||||
Log.d(RootCommands.TAG, "Permissions were not found in ls command!");
|
||||
}
|
||||
|
||||
// try to parse for symlink
|
||||
Matcher symlinkMatcher = symlinkPattern.matcher(line);
|
||||
if (symlinkMatcher.find()) {
|
||||
/*
|
||||
* TODO: If symlink points to a file in the same directory the path is not
|
||||
* absolute!!!
|
||||
*/
|
||||
symlink = symlinkMatcher.group(1);
|
||||
Log.d(RootCommands.TAG, "Symlink found: " + symlink);
|
||||
} else {
|
||||
Log.d(RootCommands.TAG, "No symlink found!");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(RootCommands.TAG, "Error with regex!", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterExecution(int id, int exitCode) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param file
|
||||
* String that represent the file, including the full path to the file and its name.
|
||||
* @param followSymlinks
|
||||
* @return File permissions as String, for example: 777, returns null on error
|
||||
* @throws IOException
|
||||
* @throws TimeoutException
|
||||
* @throws BrokenBusyboxException
|
||||
*
|
||||
*/
|
||||
public String getFilePermissions(String file) throws BrokenBusyboxException, TimeoutException,
|
||||
IOException {
|
||||
Log.d(RootCommands.TAG, "Checking permissions for " + file);
|
||||
|
||||
String permissions = null;
|
||||
|
||||
if (fileExists(file)) {
|
||||
Log.d(RootCommands.TAG, file + " was found.");
|
||||
|
||||
LsCommand lsCommand = new LsCommand(file);
|
||||
shell.add(lsCommand).waitForFinish();
|
||||
|
||||
permissions = lsCommand.getPermissions();
|
||||
}
|
||||
|
||||
return permissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets permission of file
|
||||
*
|
||||
* @param file
|
||||
* absolute path to file
|
||||
* @param permissions
|
||||
* String like 777
|
||||
* @return true if command worked
|
||||
* @throws BrokenBusyboxException
|
||||
* @throws TimeoutException
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean setFilePermissions(String file, String permissions)
|
||||
throws BrokenBusyboxException, TimeoutException, IOException {
|
||||
Log.d(RootCommands.TAG, "Set permissions of " + file + " to " + permissions);
|
||||
|
||||
SimpleCommand chmodCommand = new SimpleCommand("chmod " + permissions + " " + file);
|
||||
shell.add(chmodCommand).waitForFinish();
|
||||
|
||||
if (chmodCommand.getExitCode() == 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This will return a String that represent the symlink for a specified file.
|
||||
*
|
||||
* @param file
|
||||
* The path to the file to get the Symlink for. (must have absolute path)
|
||||
*
|
||||
* @return A String that represent the symlink for a specified file or null if no symlink
|
||||
* exists.
|
||||
* @throws IOException
|
||||
* @throws TimeoutException
|
||||
* @throws BrokenBusyboxException
|
||||
*/
|
||||
public String getSymlink(String file) throws BrokenBusyboxException, TimeoutException,
|
||||
IOException {
|
||||
Log.d(RootCommands.TAG, "Find symlink for " + file);
|
||||
|
||||
String symlink = null;
|
||||
|
||||
LsCommand lsCommand = new LsCommand(file);
|
||||
shell.add(lsCommand).waitForFinish();
|
||||
|
||||
symlink = lsCommand.getSymlink();
|
||||
|
||||
return symlink;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copys a file to a destination. Because cp is not available on all android devices, we use dd
|
||||
* or cat.
|
||||
*
|
||||
* @param source
|
||||
* example: /data/data/org.adaway/files/hosts
|
||||
* @param destination
|
||||
* example: /system/etc/hosts
|
||||
* @param remountAsRw
|
||||
* remounts the destination as read/write before writing to it
|
||||
* @param preserveFileAttributes
|
||||
* tries to copy file attributes from source to destination, if only cat is available
|
||||
* only permissions are preserved
|
||||
* @return true if it was successfully copied
|
||||
* @throws BrokenBusyboxException
|
||||
* @throws IOException
|
||||
* @throws TimeoutException
|
||||
*/
|
||||
public boolean copyFile(String source, String destination, boolean remountAsRw,
|
||||
boolean preservePermissions) throws BrokenBusyboxException, IOException,
|
||||
TimeoutException {
|
||||
|
||||
/*
|
||||
* dd can only copy files, but we can not check if the source is a file without invoking
|
||||
* shell commands, because from Java we probably have no read access, thus we only check if
|
||||
* they are ending with trailing slashes
|
||||
*/
|
||||
if (source.endsWith("/") || destination.endsWith("/")) {
|
||||
throw new FileNotFoundException("dd can only copy files!");
|
||||
}
|
||||
|
||||
// remount destination as read/write before copying to it
|
||||
if (remountAsRw) {
|
||||
if (!remount(destination, "RW")) {
|
||||
Log.d(RootCommands.TAG,
|
||||
"Remounting failed! There is probably no need to remount this partition!");
|
||||
}
|
||||
}
|
||||
|
||||
// get permissions of source before overwriting
|
||||
String permissions = null;
|
||||
if (preservePermissions) {
|
||||
permissions = getFilePermissions(source);
|
||||
}
|
||||
|
||||
boolean commandSuccess = false;
|
||||
|
||||
SimpleCommand ddCommand = new SimpleCommand("dd if=" + source + " of="
|
||||
+ destination);
|
||||
shell.add(ddCommand).waitForFinish();
|
||||
|
||||
if (ddCommand.getExitCode() == 0) {
|
||||
commandSuccess = true;
|
||||
} else {
|
||||
// try cat if dd fails
|
||||
SimpleCommand catCommand = new SimpleCommand("cat " + source + " > "
|
||||
+ destination);
|
||||
shell.add(catCommand).waitForFinish();
|
||||
|
||||
if (catCommand.getExitCode() == 0) {
|
||||
commandSuccess = true;
|
||||
}
|
||||
}
|
||||
|
||||
// set back permissions from source to destination
|
||||
if (preservePermissions) {
|
||||
setFilePermissions(destination, permissions);
|
||||
}
|
||||
|
||||
// remount destination back to read only
|
||||
if (remountAsRw) {
|
||||
if (!remount(destination, "RO")) {
|
||||
Log.d(RootCommands.TAG,
|
||||
"Remounting failed! There is probably no need to remount this partition!");
|
||||
}
|
||||
}
|
||||
|
||||
return commandSuccess;
|
||||
}
|
||||
|
||||
public static final int REBOOT_HOTREBOOT = 1;
|
||||
public static final int REBOOT_REBOOT = 2;
|
||||
public static final int REBOOT_SHUTDOWN = 3;
|
||||
public static final int REBOOT_RECOVERY = 4;
|
||||
|
||||
/**
|
||||
* Shutdown or reboot device. Possible actions are REBOOT_HOTREBOOT, REBOOT_REBOOT,
|
||||
* REBOOT_SHUTDOWN, REBOOT_RECOVERY
|
||||
*
|
||||
* @param action
|
||||
* @throws IOException
|
||||
* @throws TimeoutException
|
||||
* @throws BrokenBusyboxException
|
||||
*/
|
||||
public void reboot(int action) throws BrokenBusyboxException, TimeoutException, IOException {
|
||||
if (action == REBOOT_HOTREBOOT) {
|
||||
killAll("system_server");
|
||||
// or: killAll("zygote");
|
||||
} else {
|
||||
String command;
|
||||
switch (action) {
|
||||
case REBOOT_REBOOT:
|
||||
command = "reboot";
|
||||
break;
|
||||
case REBOOT_SHUTDOWN:
|
||||
command = "reboot -p";
|
||||
break;
|
||||
case REBOOT_RECOVERY:
|
||||
command = "reboot recovery";
|
||||
break;
|
||||
default:
|
||||
command = "reboot";
|
||||
break;
|
||||
}
|
||||
|
||||
SimpleCommand rebootCommand = new SimpleCommand(command);
|
||||
shell.add(rebootCommand).waitForFinish();
|
||||
|
||||
if (rebootCommand.getExitCode() == -1) {
|
||||
Log.e(RootCommands.TAG, "Reboot failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This command checks if a file exists
|
||||
*/
|
||||
private class FileExistsCommand extends Command {
|
||||
private String file;
|
||||
private boolean fileExists = false;
|
||||
|
||||
public FileExistsCommand(String file) {
|
||||
super("ls " + file);
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public boolean isFileExists() {
|
||||
return fileExists;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void output(int id, String line) {
|
||||
if (line.trim().equals(file)) {
|
||||
fileExists = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterExecution(int id, int exitCode) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to check whether or not a file exists on the filesystem.
|
||||
*
|
||||
* @param file
|
||||
* String that represent the file, including the full path to the file and its name.
|
||||
*
|
||||
* @return a boolean that will indicate whether or not the file exists.
|
||||
* @throws IOException
|
||||
* @throws TimeoutException
|
||||
* @throws BrokenBusyboxException
|
||||
*
|
||||
*/
|
||||
public boolean fileExists(String file) throws BrokenBusyboxException, TimeoutException,
|
||||
IOException {
|
||||
FileExistsCommand fileExistsCommand = new FileExistsCommand(file);
|
||||
shell.add(fileExistsCommand).waitForFinish();
|
||||
|
||||
if (fileExistsCommand.isFileExists()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class WithPermissions {
|
||||
abstract void whileHavingPermissions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute user defined Java code while having temporary permissions on a file
|
||||
*
|
||||
* @param file
|
||||
* @param withPermissions
|
||||
* @throws BrokenBusyboxException
|
||||
* @throws TimeoutException
|
||||
* @throws IOException
|
||||
*/
|
||||
public void withPermission(String file, String permission, WithPermissions withPermissions)
|
||||
throws BrokenBusyboxException, TimeoutException, IOException {
|
||||
String oldPermissions = getFilePermissions(file);
|
||||
|
||||
// set permissions (If set to 666, then Dalvik VM can also write to that file!)
|
||||
setFilePermissions(file, permission);
|
||||
|
||||
// execute user defined code
|
||||
withPermissions.whileHavingPermissions();
|
||||
|
||||
// set back to old permissions
|
||||
setFilePermissions(file, oldPermissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute user defined Java code while having temporary write permissions on a file using chmod
|
||||
* 666
|
||||
*
|
||||
* @param file
|
||||
* @param withWritePermissions
|
||||
* @throws BrokenBusyboxException
|
||||
* @throws TimeoutException
|
||||
* @throws IOException
|
||||
*/
|
||||
public void withWritePermissions(String file, WithPermissions withWritePermissions)
|
||||
throws BrokenBusyboxException, TimeoutException, IOException {
|
||||
withPermission(file, "666", withWritePermissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets system clock using /dev/alarm
|
||||
*
|
||||
* @param millis
|
||||
* @throws BrokenBusyboxException
|
||||
* @throws TimeoutException
|
||||
* @throws IOException
|
||||
*/
|
||||
public void setSystemClock(final long millis) throws BrokenBusyboxException, TimeoutException,
|
||||
IOException {
|
||||
withWritePermissions("/dev/alarm", new WithPermissions() {
|
||||
|
||||
@Override
|
||||
void whileHavingPermissions() {
|
||||
SystemClock.setCurrentTimeMillis(millis);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust system clock by offset using /dev/alarm
|
||||
*
|
||||
* @param offset
|
||||
* @throws BrokenBusyboxException
|
||||
* @throws TimeoutException
|
||||
* @throws IOException
|
||||
*/
|
||||
public void adjustSystemClock(final long offset) throws BrokenBusyboxException,
|
||||
TimeoutException, IOException {
|
||||
withWritePermissions("/dev/alarm", new WithPermissions() {
|
||||
|
||||
@Override
|
||||
void whileHavingPermissions() {
|
||||
SystemClock.setCurrentTimeMillis(System.currentTimeMillis() + offset);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This will take a path, which can contain the file name as well, and attempt to remount the
|
||||
* underlying partition.
|
||||
*
|
||||
* For example, passing in the following string:
|
||||
* "/system/bin/some/directory/that/really/would/never/exist" will result in /system ultimately
|
||||
* being remounted. However, keep in mind that the longer the path you supply, the more work
|
||||
* this has to do, and the slower it will run.
|
||||
*
|
||||
* @param file
|
||||
* file path
|
||||
* @param mountType
|
||||
* mount type: pass in RO (Read only) or RW (Read Write)
|
||||
* @return a <code>boolean</code> which indicates whether or not the partition has been
|
||||
* remounted as specified.
|
||||
*/
|
||||
public boolean remount(String file, String mountType) {
|
||||
// Recieved a request, get an instance of Remounter
|
||||
Remounter remounter = new Remounter(shell);
|
||||
// send the request
|
||||
return (remounter.remount(file, mountType));
|
||||
}
|
||||
|
||||
/**
|
||||
* This will tell you how the specified mount is mounted. rw, ro, etc...
|
||||
*
|
||||
* @param The
|
||||
* mount you want to check
|
||||
*
|
||||
* @return <code>String</code> What the mount is mounted as.
|
||||
* @throws Exception
|
||||
* if we cannot determine how the mount is mounted.
|
||||
*/
|
||||
public String getMountedAs(String path) throws Exception {
|
||||
ArrayList<Mount> mounts = Remounter.getMounts();
|
||||
if (mounts != null) {
|
||||
for (Mount mount : mounts) {
|
||||
if (path.contains(mount.getMountPoint().getAbsolutePath())) {
|
||||
Log.d(RootCommands.TAG, (String) mount.getFlags().toArray()[0]);
|
||||
return (String) mount.getFlags().toArray()[0];
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception();
|
||||
} else {
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there is enough space on partition where target is located
|
||||
*
|
||||
* @param size
|
||||
* size of file to put on partition
|
||||
* @param target
|
||||
* path where to put the file
|
||||
*
|
||||
* @return true if it will fit on partition of target, false if it will not fit.
|
||||
*/
|
||||
public boolean hasEnoughSpaceOnPartition(String target, long size) {
|
||||
try {
|
||||
// new File(target).getFreeSpace() (API 9) is not working on data partition
|
||||
|
||||
// get directory without file
|
||||
String directory = new File(target).getParent().toString();
|
||||
|
||||
StatFs stat = new StatFs(directory);
|
||||
long blockSize = stat.getBlockSize();
|
||||
long availableBlocks = stat.getAvailableBlocks();
|
||||
long availableSpace = availableBlocks * blockSize;
|
||||
|
||||
Log.i(RootCommands.TAG, "Checking for enough space: Target: " + target
|
||||
+ ", directory: " + directory + " size: " + size + ", availableSpace: "
|
||||
+ availableSpace);
|
||||
|
||||
if (size < availableSpace) {
|
||||
return true;
|
||||
} else {
|
||||
Log.e(RootCommands.TAG, "Not enough space on partition!");
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// if new StatFs(directory) fails catch IllegalArgumentException and just return true as
|
||||
// workaround
|
||||
Log.e(RootCommands.TAG, "Problem while getting available space on partition!", e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Not tested!
|
||||
*
|
||||
* @param toggle
|
||||
* @throws IOException
|
||||
* @throws TimeoutException
|
||||
* @throws BrokenBusyboxException
|
||||
*/
|
||||
public void toggleAdbDaemon(boolean toggle) throws BrokenBusyboxException, TimeoutException,
|
||||
IOException {
|
||||
SimpleCommand disableAdb = new SimpleCommand("setprop persist.service.adb.enable 0",
|
||||
"stop adbd");
|
||||
SimpleCommand enableAdb = new SimpleCommand("setprop persist.service.adb.enable 1",
|
||||
"stop adbd", "sleep 1", "start adbd");
|
||||
|
||||
if (toggle) {
|
||||
shell.add(enableAdb).waitForFinish();
|
||||
} else {
|
||||
shell.add(disableAdb).waitForFinish();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,170 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (c) 2012 Stephen Erickson, Chris Ravenscroft, Adam Shanks, Jeremy Lakeman (RootTools)
|
||||
*
|
||||
* 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 org.sufficientlysecure.rootcommands.command;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.sufficientlysecure.rootcommands.RootCommands;
|
||||
import org.sufficientlysecure.rootcommands.Shell;
|
||||
import org.sufficientlysecure.rootcommands.util.BrokenBusyboxException;
|
||||
import org.sufficientlysecure.rootcommands.util.Log;
|
||||
|
||||
public abstract class Command {
|
||||
final String command[];
|
||||
boolean finished = false;
|
||||
boolean brokenBusyboxDetected = false;
|
||||
int exitCode;
|
||||
int id;
|
||||
int timeout = RootCommands.DEFAULT_TIMEOUT;
|
||||
Shell shell = null;
|
||||
|
||||
public Command(String... command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
public Command(int timeout, String... command) {
|
||||
this.command = command;
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called from Shell after adding it
|
||||
*
|
||||
* @param shell
|
||||
* @param id
|
||||
*/
|
||||
public void addedToShell(Shell shell, int id) {
|
||||
this.shell = shell;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets command string executed on the shell
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getCommand() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < command.length; i++) {
|
||||
// redirect stderr to stdout
|
||||
sb.append(command[i] + " 2>&1");
|
||||
sb.append('\n');
|
||||
}
|
||||
Log.d(RootCommands.TAG, "Sending command(s): " + sb.toString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public void writeCommand(OutputStream out) throws IOException {
|
||||
out.write(getCommand().getBytes());
|
||||
}
|
||||
|
||||
public void processOutput(String line) {
|
||||
Log.d(RootCommands.TAG, "ID: " + id + ", Output: " + line);
|
||||
|
||||
/*
|
||||
* Try to detect broken toolbox/busybox binaries (see
|
||||
* https://code.google.com/p/busybox-android/issues/detail?id=1)
|
||||
*
|
||||
* It is giving "Value too large for defined data type" on certain file operations (e.g. ls
|
||||
* and chown) in certain directories (e.g. /data/data)
|
||||
*/
|
||||
if (line.contains("Value too large for defined data type")) {
|
||||
Log.e(RootCommands.TAG, "Busybox is broken with high probability due to line: " + line);
|
||||
brokenBusyboxDetected = true;
|
||||
}
|
||||
|
||||
// now execute specific output parsing
|
||||
output(id, line);
|
||||
}
|
||||
|
||||
public abstract void output(int id, String line);
|
||||
|
||||
public void processAfterExecution(int exitCode) {
|
||||
Log.d(RootCommands.TAG, "ID: " + id + ", ExitCode: " + exitCode);
|
||||
|
||||
afterExecution(id, exitCode);
|
||||
}
|
||||
|
||||
public abstract void afterExecution(int id, int exitCode);
|
||||
|
||||
public void commandFinished(int id) {
|
||||
Log.d(RootCommands.TAG, "Command " + id + " finished.");
|
||||
}
|
||||
|
||||
public void setExitCode(int code) {
|
||||
synchronized (this) {
|
||||
exitCode = code;
|
||||
finished = true;
|
||||
commandFinished(id);
|
||||
this.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the shell
|
||||
*
|
||||
* @param reason
|
||||
*/
|
||||
public void terminate(String reason) {
|
||||
try {
|
||||
shell.close();
|
||||
Log.d(RootCommands.TAG, "Terminating the shell.");
|
||||
terminated(reason);
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
public void terminated(String reason) {
|
||||
setExitCode(-1);
|
||||
Log.d(RootCommands.TAG, "Command " + id + " did not finish, because of " + reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for this command to finish and forwards exitCode into afterExecution method
|
||||
*
|
||||
* @param timeout
|
||||
* @throws TimeoutException
|
||||
* @throws BrokenBusyboxException
|
||||
*/
|
||||
public void waitForFinish() throws TimeoutException, BrokenBusyboxException {
|
||||
synchronized (this) {
|
||||
while (!finished) {
|
||||
try {
|
||||
this.wait(timeout);
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(RootCommands.TAG, "InterruptedException in waitForFinish()", e);
|
||||
}
|
||||
|
||||
if (!finished) {
|
||||
finished = true;
|
||||
terminate("Timeout");
|
||||
throw new TimeoutException("Timeout has occurred.");
|
||||
}
|
||||
}
|
||||
|
||||
if (brokenBusyboxDetected) {
|
||||
throw new BrokenBusyboxException();
|
||||
}
|
||||
|
||||
processAfterExecution(exitCode);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* 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 org.sufficientlysecure.rootcommands.command;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
|
||||
public abstract class ExecutableCommand extends Command {
|
||||
public static final String EXECUTABLE_PREFIX = "lib";
|
||||
public static final String EXECUTABLE_SUFFIX = "_exec.so";
|
||||
|
||||
/**
|
||||
* This class provides a way to use your own binaries!
|
||||
*
|
||||
* Include your own executables, renamed from * to lib*_exec.so, in your libs folder under the
|
||||
* architecture directories. Now they will be deployed by Android the same way libraries are
|
||||
* deployed!
|
||||
*
|
||||
* See README for more information how to use your own executables!
|
||||
*
|
||||
* @param context
|
||||
* @param executableName
|
||||
* @param parameters
|
||||
*/
|
||||
public ExecutableCommand(Context context, String executableName, String parameters) {
|
||||
super(getLibDirectory(context) + File.separator + EXECUTABLE_PREFIX + executableName
|
||||
+ EXECUTABLE_SUFFIX + " " + parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get full path to lib directory of app
|
||||
*
|
||||
* @return dir as String
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
private static String getLibDirectory(Context context) {
|
||||
if (Build.VERSION.SDK_INT >= 9) {
|
||||
return context.getApplicationInfo().nativeLibraryDir;
|
||||
} else {
|
||||
return context.getApplicationInfo().dataDir + File.separator + "lib";
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void output(int id, String line);
|
||||
|
||||
public abstract void afterExecution(int id, int exitCode);
|
||||
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* 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 org.sufficientlysecure.rootcommands.command;
|
||||
|
||||
public class SimpleCommand extends Command {
|
||||
private StringBuilder sb = new StringBuilder();
|
||||
|
||||
public SimpleCommand(String... command) {
|
||||
super(command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void output(int id, String line) {
|
||||
sb.append(line).append('\n');
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterExecution(int id, int exitCode) {
|
||||
}
|
||||
|
||||
public String getOutput() {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public int getExitCode() {
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* 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 org.sufficientlysecure.rootcommands.command;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public class SimpleExecutableCommand extends ExecutableCommand {
|
||||
private StringBuilder sb = new StringBuilder();
|
||||
|
||||
public SimpleExecutableCommand(Context context, String executableName, String parameters) {
|
||||
super(context, executableName, parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void output(int id, String line) {
|
||||
sb.append(line).append('\n');
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterExecution(int id, int exitCode) {
|
||||
}
|
||||
|
||||
public String getOutput() {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public int getExitCode() {
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* 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 org.sufficientlysecure.rootcommands.util;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class BrokenBusyboxException extends IOException {
|
||||
private static final long serialVersionUID = 8337358201589488409L;
|
||||
|
||||
public BrokenBusyboxException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public BrokenBusyboxException(String detailMessage) {
|
||||
super(detailMessage);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* 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 org.sufficientlysecure.rootcommands.util;
|
||||
|
||||
import org.sufficientlysecure.rootcommands.RootCommands;
|
||||
|
||||
/**
|
||||
* Wraps Android Logging to enable or disable debug output using Constants
|
||||
*
|
||||
*/
|
||||
public final class Log {
|
||||
|
||||
public static void v(String tag, String msg) {
|
||||
if (RootCommands.DEBUG) {
|
||||
android.util.Log.v(tag, msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void v(String tag, String msg, Throwable tr) {
|
||||
if (RootCommands.DEBUG) {
|
||||
android.util.Log.v(tag, msg, tr);
|
||||
}
|
||||
}
|
||||
|
||||
public static void d(String tag, String msg) {
|
||||
if (RootCommands.DEBUG) {
|
||||
android.util.Log.d(tag, msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void d(String tag, String msg, Throwable tr) {
|
||||
if (RootCommands.DEBUG) {
|
||||
android.util.Log.d(tag, msg, tr);
|
||||
}
|
||||
}
|
||||
|
||||
public static void i(String tag, String msg) {
|
||||
if (RootCommands.DEBUG) {
|
||||
android.util.Log.i(tag, msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void i(String tag, String msg, Throwable tr) {
|
||||
if (RootCommands.DEBUG) {
|
||||
android.util.Log.i(tag, msg, tr);
|
||||
}
|
||||
}
|
||||
|
||||
public static void w(String tag, String msg) {
|
||||
android.util.Log.w(tag, msg);
|
||||
}
|
||||
|
||||
public static void w(String tag, String msg, Throwable tr) {
|
||||
android.util.Log.w(tag, msg, tr);
|
||||
}
|
||||
|
||||
public static void w(String tag, Throwable tr) {
|
||||
android.util.Log.w(tag, tr);
|
||||
}
|
||||
|
||||
public static void e(String tag, String msg) {
|
||||
android.util.Log.e(tag, msg);
|
||||
}
|
||||
|
||||
public static void e(String tag, String msg, Throwable tr) {
|
||||
android.util.Log.e(tag, msg, tr);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* 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 org.sufficientlysecure.rootcommands.util;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class RootAccessDeniedException extends IOException {
|
||||
private static final long serialVersionUID = 9088998884166225540L;
|
||||
|
||||
public RootAccessDeniedException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public RootAccessDeniedException(String detailMessage) {
|
||||
super(detailMessage);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* 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 org.sufficientlysecure.rootcommands.util;
|
||||
|
||||
public class UnsupportedArchitectureException extends Exception {
|
||||
private static final long serialVersionUID = 7826528799780001655L;
|
||||
|
||||
public UnsupportedArchitectureException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public UnsupportedArchitectureException(String detailMessage) {
|
||||
super(detailMessage);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (c) 2012 Michael Elsdörfer (Android Autostarts)
|
||||
* Copyright (c) 2012 Stephen Erickson, Chris Ravenscroft, Adam Shanks (RootTools)
|
||||
*
|
||||
* 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 org.sufficientlysecure.rootcommands.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
import org.sufficientlysecure.rootcommands.RootCommands;
|
||||
|
||||
public class Utils {
|
||||
/*
|
||||
* The emulator and ADP1 device both have a su binary in /system/xbin/su, but it doesn't allow
|
||||
* apps to use it (su app_29 $ su su: uid 10029 not allowed to su).
|
||||
*
|
||||
* Cyanogen used to have su in /system/bin/su, in newer versions it's a symlink to
|
||||
* /system/xbin/su.
|
||||
*
|
||||
* The Archos tablet has it in /data/bin/su, since they don't have write access to /system yet.
|
||||
*/
|
||||
static final String[] BinaryPlaces = { "/data/bin/", "/system/bin/", "/system/xbin/", "/sbin/",
|
||||
"/data/local/xbin/", "/data/local/bin/", "/system/sd/xbin/", "/system/bin/failsafe/",
|
||||
"/data/local/" };
|
||||
|
||||
/**
|
||||
* Determine the path of the su executable.
|
||||
*
|
||||
* Code from https://github.com/miracle2k/android-autostarts, use under Apache License was
|
||||
* agreed by Michael Elsdörfer
|
||||
*/
|
||||
public static String getSuPath() {
|
||||
for (String p : BinaryPlaces) {
|
||||
File su = new File(p + "su");
|
||||
if (su.exists()) {
|
||||
Log.d(RootCommands.TAG, "su found at: " + p);
|
||||
return su.getAbsolutePath();
|
||||
} else {
|
||||
Log.v(RootCommands.TAG, "No su in: " + p);
|
||||
}
|
||||
}
|
||||
Log.d(RootCommands.TAG, "No su found in a well-known location, " + "will just use \"su\".");
|
||||
return "su";
|
||||
}
|
||||
|
||||
/**
|
||||
* This code is adapted from java.lang.ProcessBuilder.start().
|
||||
*
|
||||
* The problem is that Android doesn't allow us to modify the map returned by
|
||||
* ProcessBuilder.environment(), even though the docstring indicates that it should. This is
|
||||
* because it simply returns the SystemEnvironment object that System.getenv() gives us. The
|
||||
* relevant portion in the source code is marked as "// android changed", so presumably it's not
|
||||
* the case in the original version of the Apache Harmony project.
|
||||
*
|
||||
* Note that simply passing the environment variables we want to Process.exec won't be good
|
||||
* enough, since that would override the environment we inherited completely.
|
||||
*
|
||||
* We needed to be able to set a CLASSPATH environment variable for our new process in order to
|
||||
* use the "app_process" command directly. Note: "app_process" takes arguments passed on to the
|
||||
* Dalvik VM as well; this might be an alternative way to set the class path.
|
||||
*
|
||||
* Code from https://github.com/miracle2k/android-autostarts, use under Apache License was
|
||||
* agreed by Michael Elsdörfer
|
||||
*/
|
||||
public static Process runWithEnv(String command, ArrayList<String> customAddedEnv,
|
||||
String baseDirectory) throws IOException {
|
||||
|
||||
Map<String, String> environment = System.getenv();
|
||||
String[] envArray = new String[environment.size()
|
||||
+ (customAddedEnv != null ? customAddedEnv.size() : 0)];
|
||||
int i = 0;
|
||||
for (Map.Entry<String, String> entry : environment.entrySet()) {
|
||||
envArray[i++] = entry.getKey() + "=" + entry.getValue();
|
||||
}
|
||||
if (customAddedEnv != null) {
|
||||
for (String entry : customAddedEnv) {
|
||||
envArray[i++] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
Process process;
|
||||
if (baseDirectory == null) {
|
||||
process = Runtime.getRuntime().exec(command, envArray, null);
|
||||
} else {
|
||||
process = Runtime.getRuntime().exec(command, envArray, new File(baseDirectory));
|
||||
}
|
||||
return process;
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
<!-- Proguard Properties -->
|
||||
<property name="obfuscate.dir" value="obf" />
|
||||
<property name="obfuscate.absolute.dir" location="${obfuscate.dir}" />
|
||||
<property name="android-jar-preobfuscate" value="${obfuscate.absolute.dir}/original.jar" />
|
||||
<property name="android-jar-postobfuscate" value="${obfuscate.absolute.dir}/postobf.jar" />
|
||||
<property name="out.dex.input.absolute.dir" value="${android-jar-postobfuscate}" />
|
||||
|
||||
<!-- replaces the post-compile step from ant_rules_r3 -->
|
||||
<target name="-post-compile" depends="-dex-obfuscate,-dex-no-obfuscate">
|
||||
</target>
|
||||
|
||||
<target name="-dex-no-obfuscate" unless="build.mode.release">
|
||||
<mkdir dir="${obfuscate.absolute.dir}" />
|
||||
<jar basedir="${out.dir}/classes" destfile="${android-jar-postobfuscate}" />
|
||||
</target>
|
||||
|
||||
<!-- Converts this project's .class files into .dex files -->
|
||||
<target name="-dex-obfuscate" if="build.mode.release">
|
||||
<property name="proguard-jar" value="${proguard.dir}/proguard.jar" />
|
||||
<property name="proguard-conf.dir" value="" />
|
||||
<property name="proguard-conf.absolute.dir" location="${proguard-conf.dir}" />
|
||||
<property name="proguard-conf" value="${proguard-conf.absolute.dir}/procfg.txt" />
|
||||
<path id="fullclasspath">
|
||||
<path refid="android.target.classpath"/>
|
||||
<pathelement path="${external.libs.dir}"/>
|
||||
</path>
|
||||
<property name="libraryjarpath" refid="fullclasspath"/>
|
||||
<!-- Add Proguard Task -->
|
||||
<taskdef resource="proguard/ant/task.properties" classpath="${proguard-jar}" />
|
||||
|
||||
<mkdir dir="${obfuscate.absolute.dir}" />
|
||||
<delete file="${android-jar-preobfuscate}"/>
|
||||
<delete file="${android-jar-postobfuscate}"/>
|
||||
<jar basedir="${out.classes.dir}" destfile="${android-jar-preobfuscate}" />
|
||||
<proguard>
|
||||
@${proguard-conf}
|
||||
-injars ${android-jar-preobfuscate}
|
||||
-outjars ${android-jar-postobfuscate}
|
||||
-libraryjars ${libraryjarpath}
|
||||
-dump ${obfuscate.absolute.dir}/dump.txt
|
||||
-printseeds ${obfuscate.absolute.dir}/seeds.txt
|
||||
-printusage ${obfuscate.absolute.dir}/usage.txt
|
||||
-printmapping ${obfuscate.absolute.dir}/mapping.txt
|
||||
</proguard>
|
||||
</target>
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 25
|
||||
buildToolsVersion "25"
|
||||
|
||||
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'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':orbotservice')
|
||||
compile 'com.android.support:support-v4:25.0.0'
|
||||
compile 'com.android.support:appcompat-v7:25.0.0'
|
||||
compile 'com.android.support:design:25.0.0'
|
||||
}
|
|
@ -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,175 +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"
|
||||
android:versionCode="15401000"
|
||||
android:versionName="15.4.0-beta-1-multi">
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="16"
|
||||
android:targetSdkVersion="25" />
|
||||
<!--
|
||||
<permission android:name="org.torproject.android.MANAGE_TOR"
|
||||
android:label="@string/permission_manage_tor_label"
|
||||
android:description="@string/permission_manage_tor_description"
|
||||
android:protectionLevel="signature"/>
|
||||
|
||||
<uses-permission android:name="org.torproject.android.MANAGE_TOR"/>
|
||||
-->
|
||||
<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.AppManager"
|
||||
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,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,56 +0,0 @@
|
|||
|
||||
package org.torproject.android;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.net.VpnService;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import org.torproject.android.service.OrbotConstants;
|
||||
import org.torproject.android.service.util.Prefs;
|
||||
|
||||
import org.torproject.android.settings.Languages;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
|
@ -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,158 +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.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.preference.CheckBoxPreference;
|
||||
import android.preference.EditTextPreference;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceChangeListener;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.util.Log;
|
||||
|
||||
import org.torproject.android.OrbotApp;
|
||||
import org.torproject.android.R;
|
||||
import org.torproject.android.service.transproxy.TorTransProxy;
|
||||
import org.torproject.android.ui.AppManager;
|
||||
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 CheckBoxPreference prefCBTransProxy = null;
|
||||
private CheckBoxPreference prefcBTransProxyAll = null;
|
||||
private CheckBoxPreference prefcbTransTethering = null;
|
||||
|
||||
private Preference prefTransProxyFlush = null;
|
||||
|
||||
private Preference prefTransProxyApps = null;
|
||||
private CheckBoxPreference prefRequestRoot = null;
|
||||
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());
|
||||
|
||||
prefRequestRoot = (CheckBoxPreference) findPreference("has_root");
|
||||
prefRequestRoot.setOnPreferenceClickListener(this);
|
||||
|
||||
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;
|
||||
}
|
||||
});
|
||||
|
||||
prefCBTransProxy = (CheckBoxPreference) findPreference("pref_transparent");
|
||||
prefcBTransProxyAll = (CheckBoxPreference) findPreference("pref_transparent_all");
|
||||
prefcbTransTethering = (CheckBoxPreference) findPreference("pref_transparent_tethering");
|
||||
|
||||
prefTransProxyFlush = (Preference) findPreference("pref_transproxy_flush");
|
||||
prefTransProxyFlush.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference arg0) {
|
||||
|
||||
Intent data = new Intent();
|
||||
data.putExtra("transproxywipe", true);
|
||||
setResult(RESULT_OK, data);
|
||||
|
||||
finish();
|
||||
return false;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
prefTransProxyApps = findPreference("pref_transparent_app_list");
|
||||
prefTransProxyApps.setOnPreferenceClickListener(this);
|
||||
prefCBTransProxy.setOnPreferenceClickListener(this);
|
||||
prefcBTransProxyAll.setOnPreferenceClickListener(this);
|
||||
|
||||
prefCBTransProxy.setEnabled(prefRequestRoot.isChecked());
|
||||
prefcBTransProxyAll.setEnabled(prefCBTransProxy.isChecked());
|
||||
prefcbTransTethering.setEnabled(prefCBTransProxy.isChecked());
|
||||
|
||||
if (prefCBTransProxy.isChecked())
|
||||
prefTransProxyApps.setEnabled((!prefcBTransProxyAll.isChecked()));
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
{
|
||||
prefTransProxyApps.setEnabled(true);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
|
||||
setResult(RESULT_OK);
|
||||
|
||||
if (preference == prefRequestRoot)
|
||||
{
|
||||
if (prefRequestRoot.isChecked())
|
||||
{
|
||||
|
||||
try {
|
||||
TorTransProxy.testRoot();
|
||||
prefCBTransProxy.setEnabled(true);
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.d(OrbotApp.TAG,"root not yet enabled");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else if (preference == prefTransProxyApps)
|
||||
{
|
||||
startActivity(new Intent(this, AppManager.class));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
prefcBTransProxyAll.setEnabled(prefCBTransProxy.isChecked());
|
||||
prefTransProxyApps.setEnabled(prefCBTransProxy.isChecked() && (!prefcBTransProxyAll.isChecked()));
|
||||
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,414 +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.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.transproxy.TorifiedApp;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
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.Bundle;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class AppManager extends AppCompatActivity implements OnCheckedChangeListener, OnClickListener, OrbotConstants {
|
||||
|
||||
private ListView listApps;
|
||||
private final static String TAG = "Orbot";
|
||||
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
this.setContentView(R.layout.layout_apps);
|
||||
setTitle(R.string.apps_mode);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
View buttonSelectAll, buttonSelectNone, buttonInvert;
|
||||
|
||||
buttonSelectAll = findViewById(R.id.button_proxy_all);
|
||||
buttonSelectNone = findViewById(R.id.button_proxy_none);
|
||||
buttonInvert = findViewById(R.id.button_invert_selection);
|
||||
|
||||
buttonSelectAll.setOnClickListener(new OnAutoClickListener(0));
|
||||
buttonSelectNone.setOnClickListener(new OnAutoClickListener(1));
|
||||
buttonInvert.setOnClickListener(new OnAutoClickListener(2));
|
||||
}
|
||||
|
||||
class OnAutoClickListener implements Button.OnClickListener {
|
||||
private int status;
|
||||
public OnAutoClickListener(int status){
|
||||
this.status = status;
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onClick(View button){
|
||||
ListView listView;
|
||||
ViewGroup viewGroup;
|
||||
View parentView, currentView;
|
||||
ArrayAdapter<TorifiedApp> adapter;
|
||||
TorifiedApp app;
|
||||
CheckBox box;
|
||||
float buttonId;
|
||||
boolean[] isSelected;
|
||||
int posI, selectedI, lvSz;
|
||||
|
||||
buttonId = button.getId();
|
||||
listView = (ListView) findViewById(R.id.applistview);
|
||||
lvSz = listView.getCount();
|
||||
isSelected = new boolean[lvSz];
|
||||
|
||||
selectedI = -1;
|
||||
|
||||
if (this.status == 0){
|
||||
Log.d(TAG, "Proxifying ALL");
|
||||
}else if (this.status == 1){
|
||||
Log.d(TAG, "Proxifying NONE");
|
||||
}else {
|
||||
Log.d(TAG, "Proxifying invert");
|
||||
}
|
||||
|
||||
Context context = getApplicationContext();
|
||||
SharedPreferences prefs = TorServiceUtils.getSharedPrefs(context);
|
||||
ArrayList<TorifiedApp> apps = getApps(context, prefs);
|
||||
parentView = (View) findViewById(R.id.applistview);
|
||||
viewGroup = (ViewGroup) listView;
|
||||
|
||||
adapter = (ArrayAdapter<TorifiedApp>) listApps.getAdapter();
|
||||
if (adapter == null){
|
||||
Log.w(TAG, "List adapter is null. Getting apps.");
|
||||
loadApps(prefs);
|
||||
adapter = (ArrayAdapter<TorifiedApp>) listApps.getAdapter();
|
||||
}
|
||||
|
||||
for (int i = 0 ; i < adapter.getCount(); ++i){
|
||||
app = (TorifiedApp) adapter.getItem(i);
|
||||
currentView = adapter.getView(i, parentView, viewGroup);
|
||||
box = (CheckBox) currentView.findViewById(R.id.itemcheck);
|
||||
|
||||
if (this.status == 0){
|
||||
app.setTorified(true);
|
||||
}else if (this.status == 1){
|
||||
app.setTorified(false);
|
||||
}else {
|
||||
app.setTorified(!app.isTorified());
|
||||
}
|
||||
|
||||
if (box != null)
|
||||
box.setChecked(app.isTorified());
|
||||
|
||||
}
|
||||
saveAppSettings(context);
|
||||
loadApps(prefs);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
setResult(RESULT_OK);
|
||||
finish();
|
||||
return true;
|
||||
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
listApps = (ListView)findViewById(R.id.applistview);
|
||||
|
||||
mPrefs = TorServiceUtils.getSharedPrefs(getApplicationContext());
|
||||
loadApps(mPrefs);
|
||||
}
|
||||
|
||||
SharedPreferences mPrefs = null;
|
||||
ArrayList<TorifiedApp> mApps = null;
|
||||
|
||||
private void loadApps (SharedPreferences prefs)
|
||||
{
|
||||
|
||||
mApps = getApps(getApplicationContext(), prefs);
|
||||
|
||||
/*
|
||||
Arrays.sort(apps, 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();
|
||||
|
||||
ListAdapter adapter = 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) {
|
||||
if (app.getIcon() != null)
|
||||
entry.icon.setImageDrawable(app.getIcon());
|
||||
else
|
||||
entry.icon.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (entry.text != null) {
|
||||
entry.text.setText(app.getName());
|
||||
entry.text.setOnClickListener(AppManager.this);
|
||||
entry.text.setOnClickListener(AppManager.this);
|
||||
|
||||
if (entry.box != null)
|
||||
entry.text.setTag(entry.box);
|
||||
}
|
||||
|
||||
|
||||
if (entry.box != null) {
|
||||
entry.box.setOnCheckedChangeListener(AppManager.this);
|
||||
entry.box.setTag(app);
|
||||
entry.box.setChecked(app.isTorified());
|
||||
|
||||
|
||||
}
|
||||
|
||||
return convertView;
|
||||
}
|
||||
};
|
||||
|
||||
listApps.setAdapter(adapter);
|
||||
|
||||
}
|
||||
|
||||
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 static 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
|
||||
PackageManager pMgr = context.getPackageManager();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
try
|
||||
{
|
||||
app.setName(pMgr.getApplicationLabel(aInfo).toString());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
app.setName(aInfo.packageName);
|
||||
}
|
||||
|
||||
|
||||
//app.setIcon(pMgr.getApplicationIcon(aInfo));
|
||||
|
||||
// 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();
|
||||
|
||||
for (TorifiedApp tApp:mApps)
|
||||
{
|
||||
if (tApp.isTorified())
|
||||
{
|
||||
tordApps.append(tApp.getUsername());
|
||||
tordApps.append("|");
|
||||
}
|
||||
}
|
||||
|
||||
Editor edit = mPrefs.edit();
|
||||
edit.putString(PREFS_KEY_TORIFIED, tordApps.toString());
|
||||
edit.commit();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 = (CheckBox)v.getTag();
|
||||
|
||||
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 |