tbb-commits
Threads by month
- ----- 2025 -----
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- 1 participants
- 18498 discussions
commit a890699d47e08a640af4d56bb16533c96f7f2f8d
Author: Georg Koppen <gk(a)torproject.org>
Date: Sun Mar 17 09:20:16 2019 +0000
Translations update
---
src/chrome/locale/da/aboutTBUpdate.dtd | 6 +++---
src/chrome/locale/da/aboutTor.dtd | 2 +-
src/chrome/locale/da/browserOnboarding.properties | 2 +-
src/chrome/locale/de/aboutTBUpdate.dtd | 2 +-
src/chrome/locale/de/aboutTor.dtd | 2 +-
src/chrome/locale/de/browserOnboarding.properties | 2 +-
src/chrome/locale/es/aboutTBUpdate.dtd | 2 +-
src/chrome/locale/es/torbutton.dtd | 6 +++---
src/chrome/locale/he/browserOnboarding.properties | 2 +-
src/chrome/locale/is/aboutTBUpdate.dtd | 6 +++---
src/chrome/locale/is/aboutTor.dtd | 2 +-
src/chrome/locale/is/browserOnboarding.properties | 2 +-
src/chrome/locale/it/browserOnboarding.properties | 2 +-
src/chrome/locale/nl/aboutTBUpdate.dtd | 10 +++++-----
src/chrome/locale/nl/aboutTor.dtd | 2 +-
src/chrome/locale/nl/browserOnboarding.properties | 2 +-
src/chrome/locale/pl/browserOnboarding.properties | 2 +-
src/chrome/locale/pt-BR/aboutTBUpdate.dtd | 4 ++--
src/chrome/locale/pt-BR/browserOnboarding.properties | 2 +-
src/chrome/locale/tr/browserOnboarding.properties | 2 +-
20 files changed, 31 insertions(+), 31 deletions(-)
diff --git a/src/chrome/locale/da/aboutTBUpdate.dtd b/src/chrome/locale/da/aboutTBUpdate.dtd
index c0d74bb2..0b1afb03 100644
--- a/src/chrome/locale/da/aboutTBUpdate.dtd
+++ b/src/chrome/locale/da/aboutTBUpdate.dtd
@@ -1,8 +1,8 @@
-<!ENTITY aboutTBUpdate.changelogTitle "Tor Browser Changelog">
+<!ENTITY aboutTBUpdate.changelogTitle "Ændringslog for Tor Browser">
<!ENTITY aboutTBUpdate.updated "Tor Browser er blevet opdateret.">
<!ENTITY aboutTBUpdate.linkPrefix "For den mest aktuelle information om denne udgivelse,">
<!ENTITY aboutTBUpdate.linkLabel "Besøg vores webside">
<!ENTITY aboutTBUpdate.linkSuffix ".">
<!ENTITY aboutTBUpdate.version "Version">
-<!ENTITY aboutTBUpdate.releaseDate "Release Date">
-<!ENTITY aboutTBUpdate.releaseNotes "Release Notes">
+<!ENTITY aboutTBUpdate.releaseDate "Udgivelsesdato">
+<!ENTITY aboutTBUpdate.releaseNotes "Udgivelsesnoter">
diff --git a/src/chrome/locale/da/aboutTor.dtd b/src/chrome/locale/da/aboutTor.dtd
index 18c0275c..499fa13a 100644
--- a/src/chrome/locale/da/aboutTor.dtd
+++ b/src/chrome/locale/da/aboutTor.dtd
@@ -6,7 +6,7 @@
<!ENTITY aboutTor.title "Om Tor">
-<!ENTITY aboutTor.viewChangelog.label "View Changelog">
+<!ENTITY aboutTor.viewChangelog.label "Vis ændringslog">
<!ENTITY aboutTor.ready.label "Udforsk. Privat.">
<!ENTITY aboutTor.ready2.label "Du er klar til verdenens mest private browseroplevelse.">
diff --git a/src/chrome/locale/da/browserOnboarding.properties b/src/chrome/locale/da/browserOnboarding.properties
index 717dc4b2..aec6723b 100644
--- a/src/chrome/locale/da/browserOnboarding.properties
+++ b/src/chrome/locale/da/browserOnboarding.properties
@@ -25,7 +25,7 @@ onboarding.tour-tor-circuit-display.button=Se min sti
onboarding.tour-tor-security=Sikkerhed
onboarding.tour-tor-security.title=Vælg din oplevelse.
onboarding.tour-tor-security.description=Vi giver dig også yderligere sikkerhedsindstillinger for at øge din browsersikkerhed. Vores sikkerhedsindstillinger giver dig mulighed for at blokere elementer der kan bruges til at angribe din computer. Klik nedenfor for at se hvad de forskellige valgmuligheder gør.
-onboarding.tour-tor-security-level.button=See Your Security Level
+onboarding.tour-tor-security-level.button=Se dit sikkerhedsniveau
onboarding.tour-tor-expect-differences=Oplevelesestips
onboarding.tour-tor-expect-differences.title=Forvent nogen forskelle.
diff --git a/src/chrome/locale/de/aboutTBUpdate.dtd b/src/chrome/locale/de/aboutTBUpdate.dtd
index cc1ab47c..607aac91 100644
--- a/src/chrome/locale/de/aboutTBUpdate.dtd
+++ b/src/chrome/locale/de/aboutTBUpdate.dtd
@@ -1,4 +1,4 @@
-<!ENTITY aboutTBUpdate.changelogTitle "Tor Browser Changelog">
+<!ENTITY aboutTBUpdate.changelogTitle "Tor Browser Änderungsprotokoll">
<!ENTITY aboutTBUpdate.updated "Tor Browser wurde aktualisiert.">
<!ENTITY aboutTBUpdate.linkPrefix "Für die aktuellsten Informationen zu diesem Release, ">
<!ENTITY aboutTBUpdate.linkLabel "besuche unsere Website">
diff --git a/src/chrome/locale/de/aboutTor.dtd b/src/chrome/locale/de/aboutTor.dtd
index c4681b7b..dcb397ef 100644
--- a/src/chrome/locale/de/aboutTor.dtd
+++ b/src/chrome/locale/de/aboutTor.dtd
@@ -6,7 +6,7 @@
<!ENTITY aboutTor.title "Über Tor">
-<!ENTITY aboutTor.viewChangelog.label "View Changelog">
+<!ENTITY aboutTor.viewChangelog.label "Änderungsprotokoll anzeigen">
<!ENTITY aboutTor.ready.label "Entdecken. Privat.">
<!ENTITY aboutTor.ready2.label "Du bist bereit für das privateste Browsing-Erlebnis der Welt.">
diff --git a/src/chrome/locale/de/browserOnboarding.properties b/src/chrome/locale/de/browserOnboarding.properties
index c3856b8d..53a23c03 100644
--- a/src/chrome/locale/de/browserOnboarding.properties
+++ b/src/chrome/locale/de/browserOnboarding.properties
@@ -25,7 +25,7 @@ onboarding.tour-tor-circuit-display.button=Meinen Pfad sehen
onboarding.tour-tor-security=Sicherheit
onboarding.tour-tor-security.title=Wähle deine Erfahrung
onboarding.tour-tor-security.description=Wir bieten zusätzliche Einstellungen, um die Sicherheit zu erhöhen. In den Sicherheitseinstellungen können verschiedene Objekte und Funktionen deaktiviert werden, welche die Sicherheit des Computers gefährden können. Klicken Sie, um zu erfahren, was die verschiedenen Optionen bewirken.
-onboarding.tour-tor-security-level.button=See Your Security Level
+onboarding.tour-tor-security-level.button=Siehe deine Sicherheitsstufe
onboarding.tour-tor-expect-differences=Erfahrungswerte
onboarding.tour-tor-expect-differences.title=Erwarten Sie einige Unterschiede.
diff --git a/src/chrome/locale/es/aboutTBUpdate.dtd b/src/chrome/locale/es/aboutTBUpdate.dtd
index 49470e29..70b46fae 100644
--- a/src/chrome/locale/es/aboutTBUpdate.dtd
+++ b/src/chrome/locale/es/aboutTBUpdate.dtd
@@ -1,4 +1,4 @@
-<!ENTITY aboutTBUpdate.changelogTitle "Tor Browser Changelog">
+<!ENTITY aboutTBUpdate.changelogTitle "Registro de cambios de Tor Browser">
<!ENTITY aboutTBUpdate.updated "El Tor Browser ha sido actualizado.">
<!ENTITY aboutTBUpdate.linkPrefix "Para ver la información más actualizada de esta versión, ">
<!ENTITY aboutTBUpdate.linkLabel "visite nuestro sitio web">
diff --git a/src/chrome/locale/es/torbutton.dtd b/src/chrome/locale/es/torbutton.dtd
index a44dfc6f..2c7a8921 100644
--- a/src/chrome/locale/es/torbutton.dtd
+++ b/src/chrome/locale/es/torbutton.dtd
@@ -25,12 +25,12 @@
<!ENTITY torbutton.prefs.sec_caption_tooltip "El control deslizante del nivel de seguridad te permite deshabilitar ciertas características del navegador que pueden hacerlo más vulnerable a tentativas de hackeo.">
<!ENTITY torbutton.prefs.sec_standard_label "Estándar">
<!ENTITY torbutton.prefs.sec_standard_description "Están habilitadas todas las características de Tor Browser y sitio web.">
-<!ENTITY torbutton.prefs.sec_safer_label "Más segura">
+<!ENTITY torbutton.prefs.sec_safer_label "Más seguro">
<!ENTITY torbutton.prefs.sec_safer_description "Deshabilita características del sitio web que a menudo son peligrosas, lo que causa que algunos sitios pierdan funcionalidad.">
<!ENTITY torbutton.prefs.sec_safer_list_label "En la configuración 'más segura':">
-<!ENTITY torbutton.prefs.sec_safest_label "La más segura">
+<!ENTITY torbutton.prefs.sec_safest_label "El más seguro de todos">
<!ENTITY torbutton.prefs.sec_safest_description "Sólo permite las características de sitio web requeridas para sitios estáticos y servicios básicos. Estos cambios afectan a imágenes, medios, y scripts.">
-<!ENTITY torbutton.prefs.sec_safest_list_label "En la configuración 'la más segura':">
+<!ENTITY torbutton.prefs.sec_safest_list_label "En la configuración de Nivel de seguridad 'El más seguro de todos':">
<!ENTITY torbutton.prefs.sec_learn_more_label "Conocer más">
<!ENTITY torbutton.prefs.sec_js_on_https_sites_only "JavaScript está deshabilitado en sitios no-HTTPS.">
<!ENTITY torbutton.prefs.sec_js_disabled "JavaScript está deshabilitado por defecto en todos los sitios.">
diff --git a/src/chrome/locale/he/browserOnboarding.properties b/src/chrome/locale/he/browserOnboarding.properties
index 05d0cf48..906e3725 100644
--- a/src/chrome/locale/he/browserOnboarding.properties
+++ b/src/chrome/locale/he/browserOnboarding.properties
@@ -25,7 +25,7 @@ onboarding.tour-tor-circuit-display.button=ראה את הנתיב שלי
onboarding.tour-tor-security=אבטחה
onboarding.tour-tor-security.title=בחר את חוויתך.
onboarding.tour-tor-security.description=אנחנו מספקים לך גם הגדרות נוספות עבור הגברת אבטחת הדפדפן שלך. הגדרות האבטחה שלנו מתירות לך לחסום יסודות שעלולים לשמש כדי לתקוף את המחשב שלך. לחץ למטה כדי לראות מה האפשרויות השונות עושות.
-onboarding.tour-tor-security-level.button=See Your Security Level
+onboarding.tour-tor-security-level.button=ראה את רמת האבטחה שלך
onboarding.tour-tor-expect-differences=עצות חוויה
onboarding.tour-tor-expect-differences.title=צפה למספר הבדלים.
diff --git a/src/chrome/locale/is/aboutTBUpdate.dtd b/src/chrome/locale/is/aboutTBUpdate.dtd
index 65480323..46157f63 100644
--- a/src/chrome/locale/is/aboutTBUpdate.dtd
+++ b/src/chrome/locale/is/aboutTBUpdate.dtd
@@ -1,8 +1,8 @@
-<!ENTITY aboutTBUpdate.changelogTitle "Tor Browser Changelog">
+<!ENTITY aboutTBUpdate.changelogTitle "Breytingaannáll Tor-vafrans">
<!ENTITY aboutTBUpdate.updated "Tor-vafrinn var uppfærður.">
<!ENTITY aboutTBUpdate.linkPrefix "Til að sjá allra nýjustu upplýsingar um þessa útgáfu,">
<!ENTITY aboutTBUpdate.linkLabel "heimsæktu vefsvæðið okkar">
<!ENTITY aboutTBUpdate.linkSuffix ".">
<!ENTITY aboutTBUpdate.version "Útgáfa">
-<!ENTITY aboutTBUpdate.releaseDate "Release Date">
-<!ENTITY aboutTBUpdate.releaseNotes "Release Notes">
+<!ENTITY aboutTBUpdate.releaseDate "Útgáfudagsetning">
+<!ENTITY aboutTBUpdate.releaseNotes "Útgáfuupplýsingar">
diff --git a/src/chrome/locale/is/aboutTor.dtd b/src/chrome/locale/is/aboutTor.dtd
index 39820ded..e54ee49b 100644
--- a/src/chrome/locale/is/aboutTor.dtd
+++ b/src/chrome/locale/is/aboutTor.dtd
@@ -6,7 +6,7 @@
<!ENTITY aboutTor.title "Um Tor">
-<!ENTITY aboutTor.viewChangelog.label "View Changelog">
+<!ENTITY aboutTor.viewChangelog.label "Skoða breytingaannál">
<!ENTITY aboutTor.ready.label "Vafraðu í friði fyrir hnýsni.">
<!ENTITY aboutTor.ready2.label "Nú er allt klárt fyrir mestu fáanlegu vernd við vafur á netinu.">
diff --git a/src/chrome/locale/is/browserOnboarding.properties b/src/chrome/locale/is/browserOnboarding.properties
index 15078a7d..04648a75 100644
--- a/src/chrome/locale/is/browserOnboarding.properties
+++ b/src/chrome/locale/is/browserOnboarding.properties
@@ -25,7 +25,7 @@ onboarding.tour-tor-circuit-display.button=Skoða slóðina mína
onboarding.tour-tor-security=Öryggi
onboarding.tour-tor-security.title=Veldu hvernig þú vilt upplifa þetta.
onboarding.tour-tor-security.description=Við höfum einnig útbúið viðbótarstillingar þar sem þú getur breytt öryggisstigi vafrans. Öryggisstillingarnar gera kleift að loka á atriði sem hægt væri að nota til árása á tölvuna þína. Smelltu hér fyrir neðan til að skoða hvernig hinir mismunandi valkostir virka.
-onboarding.tour-tor-security-level.button=See Your Security Level
+onboarding.tour-tor-security-level.button=Skoðaðu öryggisstig þitt
onboarding.tour-tor-expect-differences=Ábendingar fyrir upplifunina
onboarding.tour-tor-expect-differences.title=Gerður ráð fyrir að eitthvað verði öðruvísi.
diff --git a/src/chrome/locale/it/browserOnboarding.properties b/src/chrome/locale/it/browserOnboarding.properties
index 4dae1440..a8bb329d 100644
--- a/src/chrome/locale/it/browserOnboarding.properties
+++ b/src/chrome/locale/it/browserOnboarding.properties
@@ -25,7 +25,7 @@ onboarding.tour-tor-circuit-display.button=Vedi il mio percorso
onboarding.tour-tor-security=Sicurezza
onboarding.tour-tor-security.title=Scegli la tua esperienza.
onboarding.tour-tor-security.description=Ti forniamo anche impostazioni aggiuntive per aumentare la sicurezza del tuo browser. Le nostre impostazioni di sicurezza ti permettono di bloccare elementi che potrebbero essere utilizzati per attaccare il tuo computer. Clicca qui sotto per vedere cosa fanno le diverse opzioni.
-onboarding.tour-tor-security-level.button=See Your Security Level
+onboarding.tour-tor-security-level.button=Vedi il tuo livello di sicurezza
onboarding.tour-tor-expect-differences=Consigli per l'esperienza
onboarding.tour-tor-expect-differences.title=Aspettati delle differenze.
diff --git a/src/chrome/locale/nl/aboutTBUpdate.dtd b/src/chrome/locale/nl/aboutTBUpdate.dtd
index 6b095994..56f0e81d 100644
--- a/src/chrome/locale/nl/aboutTBUpdate.dtd
+++ b/src/chrome/locale/nl/aboutTBUpdate.dtd
@@ -1,8 +1,8 @@
-<!ENTITY aboutTBUpdate.changelogTitle "Tor Browser Changelog">
+<!ENTITY aboutTBUpdate.changelogTitle "Tor Browser-changelog">
<!ENTITY aboutTBUpdate.updated "Tor Browser is bijgewerkt.">
-<!ENTITY aboutTBUpdate.linkPrefix "Voor de meest recentste informatie over deze release,">
-<!ENTITY aboutTBUpdate.linkLabel "bezoek onze website">
-<!ENTITY aboutTBUpdate.linkSuffix ".">
+<!ENTITY aboutTBUpdate.linkPrefix "Bezoek ">
+<!ENTITY aboutTBUpdate.linkLabel "onze website">
+<!ENTITY aboutTBUpdate.linkSuffix " voor de meest actuele informatie over deze release.">
<!ENTITY aboutTBUpdate.version "Versie">
-<!ENTITY aboutTBUpdate.releaseDate "Release datum">
+<!ENTITY aboutTBUpdate.releaseDate "Releasedatum">
<!ENTITY aboutTBUpdate.releaseNotes "Uitgaveopmerkingen">
diff --git a/src/chrome/locale/nl/aboutTor.dtd b/src/chrome/locale/nl/aboutTor.dtd
index b01a2f92..9f60afbf 100644
--- a/src/chrome/locale/nl/aboutTor.dtd
+++ b/src/chrome/locale/nl/aboutTor.dtd
@@ -6,7 +6,7 @@
<!ENTITY aboutTor.title "Over Tor">
-<!ENTITY aboutTor.viewChangelog.label "View Changelog">
+<!ENTITY aboutTor.viewChangelog.label "Bekijk wijzigingslogboek">
<!ENTITY aboutTor.ready.label "Onderzoek. Privé.">
<!ENTITY aboutTor.ready2.label "Je bent klaar voor de meest private browsing ervaring van de wereld">
diff --git a/src/chrome/locale/nl/browserOnboarding.properties b/src/chrome/locale/nl/browserOnboarding.properties
index db8aa2d4..88b1b4ea 100644
--- a/src/chrome/locale/nl/browserOnboarding.properties
+++ b/src/chrome/locale/nl/browserOnboarding.properties
@@ -25,7 +25,7 @@ onboarding.tour-tor-circuit-display.button=Zie Mijn Pad
onboarding.tour-tor-security=Beveiliging
onboarding.tour-tor-security.title=Kies jouw ervaring.
onboarding.tour-tor-security.description=We bieden ook additionele instellingen om de veiligheid van uw browser te verhogen. Onze 'Beveiligingsinstellingen' maken het mogelijk om elementen te blokkeren welk gebruikt zouden kunnen worden om uw computer aan te vallen. Klik hieronder om te zien wat de verschillende opties doen.
-onboarding.tour-tor-security-level.button=See Your Security Level
+onboarding.tour-tor-security-level.button=Bekijk je beveiligingsniveau
onboarding.tour-tor-expect-differences=Ervaring tips
onboarding.tour-tor-expect-differences.title=Verwacht enige verschillen.
diff --git a/src/chrome/locale/pl/browserOnboarding.properties b/src/chrome/locale/pl/browserOnboarding.properties
index 4e3f88f6..b0324a9b 100644
--- a/src/chrome/locale/pl/browserOnboarding.properties
+++ b/src/chrome/locale/pl/browserOnboarding.properties
@@ -25,7 +25,7 @@ onboarding.tour-tor-circuit-display.button=Zobacz moją ścieżkę
onboarding.tour-tor-security=Bezpieczeństwo
onboarding.tour-tor-security.title=Wybierz swoje doświadczenie.
onboarding.tour-tor-security.description=Umożliwiamy także dodatkowe ustawienia dla zwiększenia bezpieczeństwa Twojej przeglądarki. Nasze Ustawienia Bezpieczeństwa pozwalają Ci blokować elementy które mogą być użyte do ataku Twojego komputera. Kliknij poniżej aby zobaczyć co różne opcje robią.
-onboarding.tour-tor-security-level.button=See Your Security Level
+onboarding.tour-tor-security-level.button=Zobacz swój poziom bezpieczeństwa
onboarding.tour-tor-expect-differences=Wskazówki do doświadczenia
onboarding.tour-tor-expect-differences.title=Spodziewaj się różnic.
diff --git a/src/chrome/locale/pt-BR/aboutTBUpdate.dtd b/src/chrome/locale/pt-BR/aboutTBUpdate.dtd
index 3458c99d..ac45c18c 100644
--- a/src/chrome/locale/pt-BR/aboutTBUpdate.dtd
+++ b/src/chrome/locale/pt-BR/aboutTBUpdate.dtd
@@ -1,8 +1,8 @@
-<!ENTITY aboutTBUpdate.changelogTitle "Tor Browser Changelog">
+<!ENTITY aboutTBUpdate.changelogTitle "Registro de mudanças do navegador Tor">
<!ENTITY aboutTBUpdate.updated "O Navegador Tor foi atualizado.">
<!ENTITY aboutTBUpdate.linkPrefix "Para obter as informações mais recentes sobre esta versão">
<!ENTITY aboutTBUpdate.linkLabel "visite nosso site.">
<!ENTITY aboutTBUpdate.linkSuffix ".">
<!ENTITY aboutTBUpdate.version "Versão">
-<!ENTITY aboutTBUpdate.releaseDate "Release Date">
+<!ENTITY aboutTBUpdate.releaseDate "Data de lançamento">
<!ENTITY aboutTBUpdate.releaseNotes "Notas da Versão">
diff --git a/src/chrome/locale/pt-BR/browserOnboarding.properties b/src/chrome/locale/pt-BR/browserOnboarding.properties
index 8d309797..fb3935b0 100644
--- a/src/chrome/locale/pt-BR/browserOnboarding.properties
+++ b/src/chrome/locale/pt-BR/browserOnboarding.properties
@@ -25,7 +25,7 @@ onboarding.tour-tor-circuit-display.button=Ver Meu Caminho
onboarding.tour-tor-security=Segurança
onboarding.tour-tor-security.title=Escolha sua experiência.
onboarding.tour-tor-security.description=Nós também fornecemos a você configurações adicionais para aumentar a segurança do seu browser. Nossas Configurações de Segurança permitem você bloquear elementos que podem ser utilizados para atacar o seu computador. Clique abaixo para ver o que as diferentes opções fazem.
-onboarding.tour-tor-security-level.button=See Your Security Level
+onboarding.tour-tor-security-level.button=Veja seu nível de segurança
onboarding.tour-tor-expect-differences=Dicas de Experiência
onboarding.tour-tor-expect-differences.title=Espere algumas diferenças.
diff --git a/src/chrome/locale/tr/browserOnboarding.properties b/src/chrome/locale/tr/browserOnboarding.properties
index 2abd38c2..ea52e092 100644
--- a/src/chrome/locale/tr/browserOnboarding.properties
+++ b/src/chrome/locale/tr/browserOnboarding.properties
@@ -25,7 +25,7 @@ onboarding.tour-tor-circuit-display.button=Yolumu Göster
onboarding.tour-tor-security=Güvenlik
onboarding.tour-tor-security.title=Deneyiminizi özelleştirin.
onboarding.tour-tor-security.description=Ek ayarları kullanarak web tarayıcınızın güvenliğini arttırabilirsiniz. Güvenlik Ayarlarımız bilgisayarınıza saldırmak için kullanılabilecek bileşenleri engellemenizi sağlar. Seçeneklerinizi görebilmek için aşağıya tıklayın.
-onboarding.tour-tor-security-level.button=See Your Security Level
+onboarding.tour-tor-security-level.button=Güvenlik Düzeyinize Bakın
onboarding.tour-tor-expect-differences=Deneyim İpuçları
onboarding.tour-tor-expect-differences.title=Bazı farklılıklara açık olun.
1
0
commit d6471d51f7b2b90ff4dc043b9280a01914a6294c
Author: Georg Koppen <gk(a)torproject.org>
Date: Sat Mar 16 09:10:46 2019 +0000
Picking up tor-browser build2
---
projects/firefox/config | 2 +-
rbm.conf | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/projects/firefox/config b/projects/firefox/config
index ee6ba3d..9c85fcb 100644
--- a/projects/firefox/config
+++ b/projects/firefox/config
@@ -1,7 +1,7 @@
# vim: filetype=yaml sw=2
version: '[% c("abbrev") %]'
filename: 'firefox-[% c("version") %]-[% c("var/osname") %]-[% c("var/build_id") %]'
-git_hash: 'tor-browser-[% c("var/firefox_version") %]-[% c("var/torbrowser_branch") %]-1-build1'
+git_hash: 'tor-browser-[% c("var/firefox_version") %]-[% c("var/torbrowser_branch") %]-1-build2'
tag_gpg_id: 1
git_url: https://git.torproject.org/tor-browser.git
git_submodule: 1
diff --git a/rbm.conf b/rbm.conf
index 1d72524..ce4b260 100644
--- a/rbm.conf
+++ b/rbm.conf
@@ -25,7 +25,7 @@ buildconf:
var:
torbrowser_version: '8.5a9'
- torbrowser_build: 'build1'
+ torbrowser_build: 'build2'
torbrowser_incremental_from:
- 8.5a8
project_name: tor-browser
1
0

[tor-browser/tor-browser-60.6.0esr-8.5-1] squash! Omnibox: Add DDG, Startpage, Disconnect, Youtube, Twitter; remove Amazon, eBay, bing
by gk@torproject.org 16 Mar '19
by gk@torproject.org 16 Mar '19
16 Mar '19
commit 30d158c260404e073c681f91838cc8ba4a650284
Author: Georg Koppen <gk(a)torproject.org>
Date: Sat Mar 16 09:05:47 2019 +0000
squash! Omnibox: Add DDG, Startpage, Disconnect, Youtube, Twitter; remove Amazon, eBay, bing
Also fixes bug 29798 by making sure we only specify the Google search
engine we actually ship an .xml file for.
---
mobile/locales/search/list.json | 96 ++++++++++++++++++++---------------------
1 file changed, 48 insertions(+), 48 deletions(-)
diff --git a/mobile/locales/search/list.json b/mobile/locales/search/list.json
index 498e136182a9..a0ee93d4f7f7 100644
--- a/mobile/locales/search/list.json
+++ b/mobile/locales/search/list.json
@@ -247,49 +247,49 @@
"et": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "amazon-co-uk", "twitter", "wikipedia-et"
+ "google", "amazon-co-uk", "twitter", "wikipedia-et"
]
}
},
"eu": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "elebila", "wikipedia-eu"
+ "google", "bing", "elebila", "wikipedia-eu"
]
}
},
"fa": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "wikipedia-fa"
+ "google", "bing", "wikipedia-fa"
]
}
},
"ff": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "amazon-fr", "wikipedia-fr"
+ "google", "bing", "amazon-fr", "wikipedia-fr"
]
}
},
"fi": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "amazondotcom", "twitter", "wikipedia-fi"
+ "google", "amazondotcom", "twitter", "wikipedia-fi"
]
}
},
"fr": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "ddg", "qwant", "twitter", "wikipedia-fr"
+ "google", "bing", "ddg", "qwant", "twitter", "wikipedia-fr"
]
}
},
"fy-NL": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "wikipedia-fy-NL", "bolcom-fy-NL"
+ "google", "wikipedia-fy-NL", "bolcom-fy-NL"
]
},
"experimental-hidden": {
@@ -301,133 +301,133 @@
"ga-IE": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "amazon-co-uk", "tearma", "twitter", "wikipedia-ga-IE"
+ "google", "amazon-co-uk", "tearma", "twitter", "wikipedia-ga-IE"
]
}
},
"gd": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "ddg", "faclair-beag", "wikipedia-gd"
+ "google", "bing", "ddg", "faclair-beag", "wikipedia-gd"
]
}
},
"gl": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "amazondotcom", "twitter", "wikipedia-gl"
+ "google", "bing", "amazondotcom", "twitter", "wikipedia-gl"
]
}
},
"gn": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "amazondotcom", "twitter", "wikipedia-gn"
+ "google", "bing", "amazondotcom", "twitter", "wikipedia-gn"
]
}
},
"gu-IN": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "amazon-in", "wikipedia-gu"
+ "google", "bing", "amazon-in", "wikipedia-gu"
]
}
},
"he": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "amazondotcom", "twitter", "wikipedia-he"
+ "google", "bing", "amazondotcom", "twitter", "wikipedia-he"
]
}
},
"hi-IN": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "amazon-in", "twitter", "wikipedia-hi"
+ "google", "bing", "amazon-in", "twitter", "wikipedia-hi"
]
}
},
"hr": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "amazon-co-uk", "ddg", "twitter", "wikipedia-hr"
+ "google", "bing", "amazon-co-uk", "ddg", "twitter", "wikipedia-hr"
]
}
},
"hsb": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "amazon-de", "twitter", "wikipedia-hsb"
+ "google", "bing", "amazon-de", "twitter", "wikipedia-hsb"
]
}
},
"hu": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "sztaki-en-hu", "vatera", "twitter", "wikipedia-hu"
+ "google", "sztaki-en-hu", "vatera", "twitter", "wikipedia-hu"
]
}
},
"hy-AM": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "amazondotcom", "list-am", "wikipedia-hy-AM"
+ "google", "bing", "amazondotcom", "list-am", "wikipedia-hy-AM"
]
}
},
"ia": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "amazondotcom", "ddg", "twitter", "wikipedia-ia"
+ "google", "bing", "amazondotcom", "ddg", "twitter", "wikipedia-ia"
]
}
},
"id": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "twitter", "wikipedia-id"
+ "google", "bing", "twitter", "wikipedia-id"
]
}
},
"is": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "amazondotcom", "leit-is", "wikipedia-is"
+ "google", "bing", "amazondotcom", "leit-is", "wikipedia-is"
]
}
},
"it": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "ddg", "twitter", "wikipedia-it"
+ "google", "bing", "ddg", "twitter", "wikipedia-it"
]
}
},
"ja": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "amazon-jp", "bing", "twitter-ja", "wikipedia-ja", "yahoo-jp"
+ "google", "amazon-jp", "bing", "twitter-ja", "wikipedia-ja", "yahoo-jp"
]
}
},
"ka": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "amazondotcom", "ddg", "wikipedia-ka"
+ "google", "bing", "amazondotcom", "ddg", "wikipedia-ka"
]
}
},
"kab": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "wikipedia-kab"
+ "google", "bing", "wikipedia-kab"
]
}
},
"kk": {
"default": {
"visibleDefaultEngines": [
- "yandex", "google-b-m", "bing", "twitter", "wikipedia-kk"
+ "yandex", "google", "bing", "twitter", "wikipedia-kk"
]
}
},
@@ -558,42 +558,42 @@
"nn-NO": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "ddg", "gulesider-mobile-NO", "twitter", "wikipedia-NN"
+ "google", "ddg", "gulesider-mobile-NO", "twitter", "wikipedia-NN"
]
}
},
"oc": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "wikipedia-oc", "wiktionary-oc"
+ "google", "bing", "wikipedia-oc", "wiktionary-oc"
]
}
},
"or": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "amazon-in", "wikipedia-or", "wiktionary-or"
+ "google", "bing", "amazon-in", "wikipedia-or", "wiktionary-or"
]
}
},
"pa-IN": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "wikipedia-pa"
+ "google", "bing", "wikipedia-pa"
]
}
},
"pl": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "ddg", "twitter", "wikipedia-pl"
+ "google", "bing", "ddg", "twitter", "wikipedia-pl"
]
}
},
"pt-BR": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "twitter", "wikipedia-pt"
+ "google", "bing", "twitter", "wikipedia-pt"
]
},
"experimental-hidden": {
@@ -605,105 +605,105 @@
"pt-PT": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "wikipedia-pt"
+ "google", "wikipedia-pt"
]
}
},
"rm": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "ddg", "leo_ende_de", "pledarigrond", "wikipedia-rm"
+ "google", "bing", "ddg", "leo_ende_de", "pledarigrond", "wikipedia-rm"
]
}
},
"ro": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "twitter", "wikipedia-ro"
+ "google", "twitter", "wikipedia-ro"
]
}
},
"ru": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "yandex-ru", "twitter", "wikipedia-ru"
+ "google", "yandex-ru", "twitter", "wikipedia-ru"
]
}
},
"sk": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "azet-sk", "slovnik-sk", "twitter", "wikipedia-sk"
+ "google", "azet-sk", "slovnik-sk", "twitter", "wikipedia-sk"
]
}
},
"sl": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "ceneje", "odpiralni", "twitter", "wikipedia-sl"
+ "google", "ceneje", "odpiralni", "twitter", "wikipedia-sl"
]
}
},
"son": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "amazon-fr", "twitter", "wikipedia-fr"
+ "google", "bing", "amazon-fr", "twitter", "wikipedia-fr"
]
}
},
"sq": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "amazon-co-uk", "twitter", "wikipedia-sq"
+ "google", "bing", "amazon-co-uk", "twitter", "wikipedia-sq"
]
}
},
"sr": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "ddg", "twitter", "wikipedia-sr"
+ "google", "bing", "ddg", "twitter", "wikipedia-sr"
]
}
},
"sv-SE": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "prisjakt-sv-SE", "twitter", "wikipedia-sv-SE"
+ "google", "prisjakt-sv-SE", "twitter", "wikipedia-sv-SE"
]
}
},
"ta": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "amazon-in", "ddg", "wikipedia-ta", "wiktionary-ta"
+ "google", "bing", "amazon-in", "ddg", "wikipedia-ta", "wiktionary-ta"
]
}
},
"te": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "amazon-in", "wikipedia-te", "wiktionary-te"
+ "google", "bing", "amazon-in", "wikipedia-te", "wiktionary-te"
]
}
},
"th": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "twitter", "wikipedia-th"
+ "google", "twitter", "wikipedia-th"
]
}
},
"tl": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "amazondotcom", "ddg", "twitter"
+ "google", "bing", "amazondotcom", "ddg", "twitter"
]
}
},
"tr": {
"default": {
"visibleDefaultEngines": [
- "yandex-tr", "google-b-m", "twitter", "wikipedia-tr"
+ "yandex-tr", "google", "twitter", "wikipedia-tr"
]
}
},
@@ -774,7 +774,7 @@
"zh-TW": {
"default": {
"visibleDefaultEngines": [
- "google-b-m", "bing", "ddg", "wikipedia-zh-TW"
+ "google", "bing", "ddg", "wikipedia-zh-TW"
]
},
"experimental-hidden": {
1
0

15 Mar '19
commit 4728614433f2c3248a0a60891ded27ff7003f267
Author: Georg Koppen <gk(a)torproject.org>
Date: Fri Mar 15 22:30:43 2019 +0000
Release preparations for 8.5a9
Versions bump and Changelog update
---
projects/firefox-langpacks/config | 2 +-
projects/firefox/config | 4 +-
.../tor-browser/Bundle-Data/Docs/ChangeLog.txt | 50 ++++++++++++++++++++++
projects/tor-launcher/config | 2 +-
projects/torbutton/config | 2 +-
rbm.conf | 6 +--
6 files changed, 58 insertions(+), 8 deletions(-)
diff --git a/projects/firefox-langpacks/config b/projects/firefox-langpacks/config
index 34d6fad..17a707a 100644
--- a/projects/firefox-langpacks/config
+++ b/projects/firefox-langpacks/config
@@ -4,7 +4,7 @@ filename: '[% project %]-[% c("version") %]-[% c("var/osname") %]-[% c("var/buil
var:
ff_version: '[% pc("firefox", "var/firefox_version") %]'
- ff_build: build1
+ ff_build: build2
input_filename: 'dl-langpack-[% c("var/ff_arch") %]-[% c("version") %]'
targets:
diff --git a/projects/firefox/config b/projects/firefox/config
index a56f3f9..ee6ba3d 100644
--- a/projects/firefox/config
+++ b/projects/firefox/config
@@ -1,14 +1,14 @@
# vim: filetype=yaml sw=2
version: '[% c("abbrev") %]'
filename: 'firefox-[% c("version") %]-[% c("var/osname") %]-[% c("var/build_id") %]'
-git_hash: 'tor-browser-[% c("var/firefox_version") %]-[% c("var/torbrowser_branch") %]-1-build2'
+git_hash: 'tor-browser-[% c("var/firefox_version") %]-[% c("var/torbrowser_branch") %]-1-build1'
tag_gpg_id: 1
git_url: https://git.torproject.org/tor-browser.git
git_submodule: 1
gpg_keyring: torbutton.gpg
var:
- firefox_platform_version: 60.5.1
+ firefox_platform_version: 60.6.0
firefox_version: '[% c("var/firefox_platform_version") %]esr'
torbrowser_branch: 8.5
torbrowser_update_channel: alpha
diff --git a/projects/tor-browser/Bundle-Data/Docs/ChangeLog.txt b/projects/tor-browser/Bundle-Data/Docs/ChangeLog.txt
index a68ff69..1054827 100644
--- a/projects/tor-browser/Bundle-Data/Docs/ChangeLog.txt
+++ b/projects/tor-browser/Bundle-Data/Docs/ChangeLog.txt
@@ -1,9 +1,59 @@
+Tor Browser 8.5a9 -- March 19 2019
+ * All platforms
+ * Update Firefox to 60.6.0esr
+ * Update Torbutton to 2.1.5
+ * Bug 25658: Replace security slider with security level UI
+ * Bug 28628: Change onboarding Security panel to open new Security Level panel
+ * Bug 29440: Update about:tor when Tor Browser is updated
+ * Bug 27478: Improved Torbutton icons for dark theme
+ * Bug 29021: Tell NoScript it is running within Tor Browser
+ * Bug 29239: Don't ship the Torbutton .xpi on mobile
+ * Translations update
+ * Bug 29120: Enable media cache in memory
+ * Bug 29445: Enable support for enterprise policies
+ * Windows + OS X + Linux
+ * Update Tor to 0.4.0.2-alpha
+ * Bug 29660: XMPP can not connect to SOCKS5 anymore
+ * Update OpenSSL to 1.0.2r
+ * Update Tor Launcher to 0.2.18.1
+ * Bug 29328: Account for Tor 0.4.0.x's revised bootstrap status reporting
+ * Bug 22402: Improve "For assistance" link
+ * Translations update
+ * Bug 25658+29554: Replace security slider with security level UI
+ * Bug 28885: notify users that update is downloading
+ * Bug 29180: MAR download stalls when about dialog is opened
+ * Bug 27485: Users are not taught how to open security-slider dialog
+ * Bug 27486: Avoid about:blank tabs when opening onboarding pages
+ * Bug 29440: Update about:tor when Tor Browser is updated
+ * Bug 23359: WebExtensions icons are not shown on first start
+ * Bug 28628: Change onboarding Security panel to open new Security Level panel
+ * Android
+ * Bug 28329: Design Tor Browser for Android configuration UI
+ * Bug 28802: Support PTs in Tor Browser for Android
+ * Bug 29794: Update TBA built-in bridges
+ * Bug 27210: Add support for x86 on Android
+ * Bug 29633: Don't ship pdnsd anymore
+ * Bug 28708: about:tor is not the default homepage after upgrade
+ * Bug 29626: Application name is now "Always-On Notifications"
+ * Bug 29467: Backport fix for arc4random_buf bustage
+ * Build System
+ * All platforms
+ * Bug 25876: Generate source tarballs during build
+ * Bug 28685: Set Build ID based on Tor Browser version
+ * Bug 29194: Set DEBIAN_FRONTEND=noninteractive
+ * Linux
+ * Bug 26323: Build 32bit Linux bundles on 64bit Debian Wheezy
+ * Bug 29758: Build firefox debug symbols for linux-i686
+ * Android
+ * Bug 29632: Use HTTPS for downloading Gradle
+
Tor Browser 8.5a8 -- February 13 2019
* All platforms
* Update Firefox to 60.5.1esr
* Update HTTPS Everywhere to 2019.1.31
* Bug 29378: Remove 83.212.101.3 from default bridges
* Bug 29349: Remove network.http.spdy.* overrides from meek helper user.js
+ * Bug 29327: TypeError: hostName is null on about:tor page
* Build System
* All Platforms
* Bug 29235: Build our own version of python3.6 for HTTPS Everywhere
diff --git a/projects/tor-launcher/config b/projects/tor-launcher/config
index 8087727..600b048 100644
--- a/projects/tor-launcher/config
+++ b/projects/tor-launcher/config
@@ -1,5 +1,5 @@
# vim: filetype=yaml sw=2
-version: 0.2.18
+version: 0.2.18.1
git_url: https://git.torproject.org/tor-launcher.git
git_hash: '[% c("version") %]'
gpg_keyring: torbutton.gpg
diff --git a/projects/torbutton/config b/projects/torbutton/config
index 369d7e4..a834417 100644
--- a/projects/torbutton/config
+++ b/projects/torbutton/config
@@ -1,5 +1,5 @@
# vim: filetype=yaml sw=2
-version: 2.1.4
+version: 2.1.5
git_url: https://git.torproject.org/torbutton.git
git_hash: '[% c("version") %]'
gpg_keyring: torbutton.gpg
diff --git a/rbm.conf b/rbm.conf
index d39316c..1d72524 100644
--- a/rbm.conf
+++ b/rbm.conf
@@ -24,10 +24,10 @@ buildconf:
git_signtag_opt: '-s'
var:
- torbrowser_version: '8.5a8'
- torbrowser_build: 'build2'
+ torbrowser_version: '8.5a9'
+ torbrowser_build: 'build1'
torbrowser_incremental_from:
- - 8.5a7
+ - 8.5a8
project_name: tor-browser
multi_lingual: 0
build_mar: 1
1
0

[tor-browser/tor-browser-60.6.0esr-8.5-1] Pull in latest Torbutton code
by gk@torproject.org 15 Mar '19
by gk@torproject.org 15 Mar '19
15 Mar '19
commit 16cd9b21911c5405381dafbd9566cc4ce8a78fa2
Author: Georg Koppen <gk(a)torproject.org>
Date: Fri Mar 15 22:22:28 2019 +0000
Pull in latest Torbutton code
---
toolkit/torproject/torbutton | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/toolkit/torproject/torbutton b/toolkit/torproject/torbutton
index 52c12d905ff4..498d64784659 160000
--- a/toolkit/torproject/torbutton
+++ b/toolkit/torproject/torbutton
@@ -1 +1 @@
-Subproject commit 52c12d905ff4f6d35d5788b5cc379e474be429e9
+Subproject commit 498d64784659082877bc83ecca24fbf8d137125f
1
0

[tor-browser/tor-browser-60.5.1esr-8.5-1] Bug 28329 - Part 1. Add new Tor resources
by gk@torproject.org 15 Mar '19
by gk@torproject.org 15 Mar '19
15 Mar '19
commit 0db4c9243320d2c516329949ee17e8b0a5b13624
Author: Matthew Finkel <Matthew.Finkel(a)gmail.com>
Date: Tue Feb 19 22:45:46 2019 +0000
Bug 28329 - Part 1. Add new Tor resources
---
.../main/res/color/tor_bridges_enabled_colors.xml | 10 ++++++
.../main/res/color/tor_bridges_select_builtin.xml | 8 +++++
.../res/drawable-nodpi/tor_bootstrap_onion.gif | Bin 0 -> 137518 bytes
.../app/src/main/res/drawable/ic_settings_24px.xml | 36 +++++++++++++++++++++
.../res/drawable/list_section_divider_material.xml | 19 +++++++++++
.../drawable/list_section_divider_mtrl_alpha.9.png | Bin 0 -> 164 bytes
.../app/src/main/res/drawable/rounded_corners.xml | 11 +++++++
.../src/main/res/drawable/tor_spinning_onion.xml | 12 +++++++
mobile/android/app/src/main/res/xml/separator.xml | 13 ++++++++
.../android/app/src/photon/res/values/colors.xml | 2 ++
.../base/locales/en-US/torbrowser_strings.dtd | 32 ++++++++++++++++++
mobile/android/base/strings.xml.in | 29 +++++++++++++++++
12 files changed, 172 insertions(+)
diff --git a/mobile/android/app/src/main/res/color/tor_bridges_enabled_colors.xml b/mobile/android/app/src/main/res/color/tor_bridges_enabled_colors.xml
new file mode 100644
index 000000000000..a51ffd547c04
--- /dev/null
+++ b/mobile/android/app/src/main/res/color/tor_bridges_enabled_colors.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- Used by the Bridges Enabled switch for correct coloring -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_checked="true"
+ android:color="@color/tor_bootstrap_background" />
+ <item android:color="#808080" />
+</selector>
diff --git a/mobile/android/app/src/main/res/color/tor_bridges_select_builtin.xml b/mobile/android/app/src/main/res/color/tor_bridges_select_builtin.xml
new file mode 100644
index 000000000000..948ed552d539
--- /dev/null
+++ b/mobile/android/app/src/main/res/color/tor_bridges_select_builtin.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- Used for changing the color of the radio button -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="#808080" />
+</selector>
diff --git a/mobile/android/app/src/main/res/drawable-nodpi/tor_bootstrap_onion.gif b/mobile/android/app/src/main/res/drawable-nodpi/tor_bootstrap_onion.gif
new file mode 100755
index 000000000000..b9478997b5d6
Binary files /dev/null and b/mobile/android/app/src/main/res/drawable-nodpi/tor_bootstrap_onion.gif differ
diff --git a/mobile/android/app/src/main/res/drawable/ic_settings_24px.xml b/mobile/android/app/src/main/res/drawable/ic_settings_24px.xml
new file mode 100644
index 000000000000..c582193aa12d
--- /dev/null
+++ b/mobile/android/app/src/main/res/drawable/ic_settings_24px.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2018 The Android Open Source Project
+
+ 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
+
+ https://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.
+ -->
+
+<!--
+ Downloaded from:
+ https://raw.githubusercontent.com/material-components/material-components-a…
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="20"
+ android:viewportHeight="20"
+ tools:ignore="NewApi">
+
+ <path
+ android:pathData="M0,0 L20,0 L20,20 L0,20 L0,0 Z" />
+ <path
+ android:fillColor="?attr/colorControlNormal"
+ android:pathData="M15.95,10.78 C15.98,10.53,16,10.27,16,10 S15.98,9.47,15.94,9.22 L17.63,7.9 C17.78,7.78,17.82,7.56,17.73,7.39 L16.13,4.62 C16.03,4.44,15.82,4.38,15.64,4.44 L13.65,5.24 C13.23,4.92,12.79,4.66,12.3,4.46 L12,2.34 C11.97,2.14,11.8,2,11.6,2 L8.4,2 C8.2,2,8.04,2.14,8.01,2.34 L7.71,4.46 C7.22,4.66,6.77,4.93,6.36,5.24 L4.37,4.44 C4.19,4.37,3.98,4.44,3.88,4.62 L2.28,7.39 C2.18,7.57,2.22,7.78,2.38,7.9 L4.07,9.22 C4.03,9.47,4,9.74,4,10 S4.02,10.53,4.06,10.78 L2.37,12.1 C2.22,12.22,2.18,12.44,2.27,12.61 L3.87,15.38 C3.97,15.56,4.18,15.62,4.36,15.56 L6.35,14.76 C6.77,15.08,7.21,15.34,7.7,15.54 L8,17.66 C8.04,17.86,8.2,18,8.4,18 L11.6,18 C11.8,18,11.97,17.86,11.99,17.66 L12.29,15.54 C12.78,15.34,13.23,15.07,13.64,14.76 L15.63,15.56 C15.81,15.63,16.02,15.56,16.12,15.38 L17.72,12.61 C17.82,12.43,17.78,12.22,17.62,12.1 L15.95,10.78 Z M10,13 C8.35,13,7,11.65,7,10 S8.35,7,10,7 S13,8.35,13,10 S11.65,13,10,13 Z" />
+</vector>
diff --git a/mobile/android/app/src/main/res/drawable/list_section_divider_material.xml b/mobile/android/app/src/main/res/drawable/list_section_divider_material.xml
new file mode 100644
index 000000000000..dd66b88381fa
--- /dev/null
+++ b/mobile/android/app/src/main/res/drawable/list_section_divider_material.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+ 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.
+-->
+<!-- Based on:
+ https://android.googlesource.com/platform/frameworks/base/+/refs/heads/pie-…
+-->
+<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/list_section_divider_mtrl_alpha"
+ android:tint="#ff000000"
+ android:alpha="0.12" />
diff --git a/mobile/android/app/src/main/res/drawable/list_section_divider_mtrl_alpha.9.png b/mobile/android/app/src/main/res/drawable/list_section_divider_mtrl_alpha.9.png
new file mode 100644
index 000000000000..12d8b2f4aa00
Binary files /dev/null and b/mobile/android/app/src/main/res/drawable/list_section_divider_mtrl_alpha.9.png differ
diff --git a/mobile/android/app/src/main/res/drawable/rounded_corners.xml b/mobile/android/app/src/main/res/drawable/rounded_corners.xml
new file mode 100644
index 000000000000..670da2fed66e
--- /dev/null
+++ b/mobile/android/app/src/main/res/drawable/rounded_corners.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- Used for rounding the corners of a button -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="#FFFFFF" />
+ <corners android:radius="5dp" />
+</shape>
diff --git a/mobile/android/app/src/main/res/drawable/tor_spinning_onion.xml b/mobile/android/app/src/main/res/drawable/tor_spinning_onion.xml
new file mode 100644
index 000000000000..6bcc52f87f93
--- /dev/null
+++ b/mobile/android/app/src/main/res/drawable/tor_spinning_onion.xml
@@ -0,0 +1,12 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<vector android:height="24dp" android:viewportHeight="289"
+ android:viewportWidth="247" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#FF000000" android:pathData="M100.02,44.97C96.2,31.15 83.92,21.59 69.91,21.51V0c16.52,0 32.07,7.97 41.98,21.51V0h20.99v21.51C142.78,7.97 158.33,0 174.85,0v21.51c-14.01,0.08 -26.29,9.63 -30.12,23.45C203.49,54.97 41.26,54.97 100.02,44.97z"/>
+ <path android:fillColor="#FF000000" android:pathData="M123.92,52.47c-62.22,0 -112.65,50.44 -112.65,112.65c0,62.22 50.44,112.65 112.65,112.65V52.47z"/>
+ <path android:fillColor="#FF000000" android:pathData="M123.92,266.51c56,0 101.39,-45.39 101.39,-101.39c0,-56 -45.39,-101.39 -101.39,-101.39S22.53,109.13 22.53,165.12C22.53,221.12 67.92,266.51 123.92,266.51zM123.92,289.04C55.48,289.04 0,233.56 0,165.12S55.48,41.2 123.92,41.2s123.92,55.48 123.92,123.92S192.36,289.04 123.92,289.04z"/>
+ <path android:fillColor="#FF000000" android:pathData="M124.92,78.78v22.53c34.79,0.54 62.84,28.89 62.84,63.81c0,34.92 -28.04,63.28 -62.84,63.81v22.53c47.24,-0.54 85.37,-38.98 85.37,-86.34C210.29,117.76 172.16,79.32 124.92,78.78z"/>
+ <path android:fillColor="#FF000000" android:pathData="M123.92,116.31v22.53c14.52,0 26.29,11.77 26.29,26.29s-11.77,26.29 -26.29,26.29v22.53c26.96,0 48.82,-21.86 48.82,-48.82C172.74,138.16 150.88,116.31 123.92,116.31z"/>
+</vector>
diff --git a/mobile/android/app/src/main/res/xml/separator.xml b/mobile/android/app/src/main/res/xml/separator.xml
new file mode 100644
index 000000000000..edd95cd90a3a
--- /dev/null
+++ b/mobile/android/app/src/main/res/xml/separator.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- List item separator -->
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:textSize="8sp"
+ android:textStyle="bold"
+ android:background="@drawable/list_section_divider_material"/>
diff --git a/mobile/android/app/src/photon/res/values/colors.xml b/mobile/android/app/src/photon/res/values/colors.xml
index b322a46fe008..2762e364dd79 100644
--- a/mobile/android/app/src/photon/res/values/colors.xml
+++ b/mobile/android/app/src/photon/res/values/colors.xml
@@ -154,6 +154,8 @@
<color name="tor_tab_inactive_text">#484848</color>
<color name="tor_tab_active_text">#7D4698</color>
<color name="tor_description_background_text">#FAFAFA</color>
+
+ <color name="tor_bootstrap_background">#420C5D</color>
<!-- Restricted profiles palette -->
<color name="restricted_profile_background_gold">#ffffcb51</color>
diff --git a/mobile/android/base/locales/en-US/torbrowser_strings.dtd b/mobile/android/base/locales/en-US/torbrowser_strings.dtd
index 5419f4942b3e..44b4a44ff93c 100644
--- a/mobile/android/base/locales/en-US/torbrowser_strings.dtd
+++ b/mobile/android/base/locales/en-US/torbrowser_strings.dtd
@@ -23,3 +23,35 @@
<!ENTITY firstrun_onionservices_title "Onion Services.">
<!ENTITY firstrun_onionservices_message "Onion services are sites that end with a .onion that provide extra protections to publishers and visitors, including added safeguards against censorship. Onion services allow anyone to provide content and services anonymously.">
<!ENTITY firstrun_onionservices_next "Go to explore">
+
+<!ENTITY tor_bootstrap_swipe_for_logs "Swipe to the left to see Tor logs">
+<!ENTITY tor_bootstrap_connect "Connect">
+<!ENTITY tor_bootstrap_starting_status "We are connecting to the Tor network...">
+
+<!ENTITY pref_tor_network_title "Network">
+<!ENTITY pref_tor_select_a_bridge_title "Select a Bridge">
+<!ENTITY pref_tor_provide_a_bridge_title "Provide a Bridge">
+
+<!ENTITY pref_category_tor_network_summary "Tor Browser connects you to the Tor Network run by thousands of volunteers around the world! Can these options help you?">
+<!ENTITY pref_category_tor_bridge_summary "Bridges are unlisted Tor relays that make it more difficult to block connections into the Tor network. Because of how some countries try to block Tor, certain bridges work in some countries but not others.">
+
+<!ENTITY pref_choice_tor_bridges_enabled_title "Internet is censored here">
+<!ENTITY pref_choice_tor_bridges_enabled_summary "Tap to configure a bridge to connect to Tor">
+
+<!ENTITY pref_tor_bridges_provide_manual_button_title "Provide a Bridge I know">
+<!ENTITY pref_tor_bridges_provide_select_text_title "Select a Bridge">
+<!ENTITY pref_tor_bridges_provide_manual_text_title "Enter Bridge">
+<!ENTITY pref_tor_bridges_provide_manual_summary "Enter the bridge information you received from a trusted source">
+<!ENTITY pref_tor_bridges_provide_manual_address_port_placeholder "address:port">
+<!ENTITY pref_tor_hint_type_one_per_line "Type one per line">
+
+<!-- When another PT is recommended, change TorNetworkBridgeSelectPreference::saveCurrentCheckedRadioButton(), too -->
+<!ENTITY pref_bridges_type_obfs4 "obfs4 (recommended)">
+<!ENTITY pref_bridges_type_meek_azure "meek-azure">
+<!ENTITY pref_bridges_type_obfs3 "obfs3">
+<!ENTITY pref_tor_network_bridges_enabled_change_builtin "You\'re using a built-in bridge to connect to Tor. Change">
+<!ENTITY pref_tor_network_bridges_enabled_change_custom "You\'re using a custom bridge to connect to Tor. Change">
+<!ENTITY pref_tor_network_using_multiple_provided_bridges "You\'re using multiple custom bridges.">
+<!ENTITY pref_tor_network_using_a_provided_bridge "You\'re using &formatS; bridge.">
+
+<!ENTITY tor_notify_user_about_error "An error occurred, please swipe for more information.">
diff --git a/mobile/android/base/strings.xml.in b/mobile/android/base/strings.xml.in
index 2beb152ad5bb..eaa902b95060 100644
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -82,6 +82,35 @@
<string name="firstrun_onionservices_message">&firstrun_onionservices_message;</string>
<string name="firstrun_onionservices_next">&firstrun_onionservices_next;</string>
+ <string name="pref_tor_network_title">&pref_tor_network_title;</string>
+ <string name="pref_tor_select_a_bridge_title">&pref_tor_select_a_bridge_title;</string>
+ <string name="pref_tor_provide_a_bridge_title">&pref_tor_provide_a_bridge_title;</string>
+ <string name="pref_category_tor_network_summary">&pref_category_tor_network_summary;</string>
+ <string name="pref_category_tor_bridge_summary">&pref_category_tor_bridge_summary;</string>
+ <string name="tor_notify_user_about_error">&tor_notify_user_about_error;</string>
+
+ <string name="tor_bootstrap_swipe_for_logs">&tor_bootstrap_swipe_for_logs;</string>
+ <string name="tor_bootstrap_connect">&tor_bootstrap_connect;</string>
+ <string name="tor_bootstrap_starting_status">&tor_bootstrap_starting_status;</string>
+
+ <string name="pref_choice_tor_bridges_enabled_title">&pref_choice_tor_bridges_enabled_title;</string>
+ <string name="pref_choice_tor_bridges_enabled_summary">&pref_choice_tor_bridges_enabled_summary;</string>
+ <string name="pref_bridges_type_obfs4">&pref_bridges_type_obfs4;</string>
+ <string name="pref_bridges_type_meek_azure">&pref_bridges_type_meek_azure;</string>
+ <string name="pref_bridges_type_obfs3">&pref_bridges_type_obfs3;</string>
+
+ <string name="pref_tor_bridges_provide_manual_button_title">&pref_tor_bridges_provide_manual_button_title;</string>
+ <string name="pref_tor_bridges_provide_select_text_title">&pref_tor_bridges_provide_select_text_title;</string>
+ <string name="pref_tor_bridges_provide_manual_text_title">&pref_tor_bridges_provide_manual_text_title;</string>
+ <string name="pref_tor_bridges_provide_manual_summary">&pref_tor_bridges_provide_manual_summary;</string>
+
+ <string name="pref_tor_bridges_provide_manual_address_port_placeholder">&pref_tor_bridges_provide_manual_address_port_placeholder;</string>
+ <string name="pref_tor_hint_type_one_per_line">&pref_tor_hint_type_one_per_line;</string>
+ <string name="pref_tor_network_bridges_enabled_change_custom">&pref_tor_network_bridges_enabled_change_custom;</string>
+ <string name="pref_tor_network_bridges_enabled_change_builtin">&pref_tor_network_bridges_enabled_change_builtin;</string>
+ <string name="pref_tor_network_using_multiple_provided_bridges">&pref_tor_network_using_multiple_provided_bridges;</string>
+ <string name="pref_tor_network_using_a_provided_bridge">&pref_tor_network_using_a_provided_bridge;</string>
+
<string name="bookmarks_title">&bookmarks_title;</string>
<string name="history_title">&history_title;</string>
1
0

[tor-browser/tor-browser-60.5.1esr-8.5-1] Bug 28329 - Part 2. Implement checking if the Tor service is running
by gk@torproject.org 15 Mar '19
by gk@torproject.org 15 Mar '19
15 Mar '19
commit a6e87093856101ec93bbeaf93eed22ac77031f73
Author: Matthew Finkel <Matthew.Finkel(a)gmail.com>
Date: Wed Feb 20 01:04:56 2019 +0000
Bug 28329 - Part 2. Implement checking if the Tor service is running
---
.../base/java/org/mozilla/gecko/BrowserApp.java | 130 +++++++++++++++++++++
1 file changed, 130 insertions(+)
diff --git a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
index 9330a6ba9838..5aa0a6a7f3ac 100644
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -51,6 +51,7 @@ import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.NotificationCompat;
+import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.content.res.ResourcesCompat;
import android.support.v4.view.MenuItemCompat;
import android.text.TextUtils;
@@ -182,6 +183,7 @@ import org.mozilla.gecko.widget.SplashScreen;
import org.mozilla.geckoview.GeckoSession;
import org.torproject.android.OrbotMainActivity;
+import org.torproject.android.service.TorService;
import org.torproject.android.service.TorServiceConstants;
import java.io.File;
@@ -270,6 +272,7 @@ public class BrowserApp extends GeckoApp
private TabsPanel mTabsPanel;
private boolean mOrbotNeedsStart = true;
+ private boolean mTorNeedsStart = true;
private boolean showSplashScreen = false;
private SplashScreen splashScreen;
@@ -1079,6 +1082,131 @@ public class BrowserApp extends GeckoApp
}
/**
+ * Send the service a request for the current status.
+ * The response is sent as a broadcast. Capture that in
+ * receiveTorIsStartedAsync().
+ */
+ private void requestTorIsStartedAsync() {
+ Intent torServiceStatus = new Intent(this, TorService.class);
+ torServiceStatus.setAction(TorServiceConstants.ACTION_STATUS);
+ startService(torServiceStatus);
+ }
+
+ private BroadcastReceiver mLocalBroadcastReceiver;
+ private Boolean mTorStatus;
+
+ /**
+ * Setup the status receiver for broadcasts from the service.
+ * The response is sent as a broadcast. Create a background thread
+ * for receiving/handling the broadcast.
+ *
+ * This method is coupled with receiveTorIsStartedAsync(). They should
+ * be used together.
+ */
+ private BroadcastReceiver setupReceiveTorIsStartedAsync() {
+
+ // Create a thread specifically for defining the BroadcastReceiver
+ new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+ mLocalBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
+
+ // We only want ACTION_STATUS messages
+ if (!action.equals(TorServiceConstants.ACTION_STATUS)) {
+ return;
+ }
+
+ // The current status has the EXTRA_STATUS key
+ String currentStatus =
+ intent.getStringExtra(TorServiceConstants.EXTRA_STATUS);
+
+ try {
+ synchronized (mTorStatus) {
+ mTorStatus = (currentStatus == TorServiceConstants.STATUS_ON);
+ mTorStatus.notify();
+ }
+ } catch (IllegalMonitorStateException e) {
+ // |synchronized| should prevent this
+ }
+ }
+ };
+
+ LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(BrowserApp.this);
+ lbm.registerReceiver(mLocalBroadcastReceiver,
+ new IntentFilter(TorServiceConstants.ACTION_STATUS));
+
+ }
+ }).start();
+
+ return mLocalBroadcastReceiver;
+ }
+
+ /**
+ * Receive the current status from the service.
+ * The response is sent as a broadcast. If it is not received within
+ * 1 second, then return false.
+ *
+ * This method is coupled with setupReceiveTorIsStartedAsync(). They
+ * should be used together.
+ */
+ private boolean receiveTorIsStartedAsync(BroadcastReceiver mLocalBroadcastReceiver, Boolean torStatus) {
+ // Wait until we're notified from the above thread, or we're
+ // interrupted by the timeout.
+ try {
+ // One thousand milliseconds = one second
+ final long oneSecTimeout = Math.round(Math.pow(10, 3));
+ synchronized (torStatus) {
+ // We wake from wait() because we reached the one second
+ // timeout, the BroadcastReceiver notified us, or we received
+ // a spurious wakeup. For all three cases, we can accept the
+ // current value of torStatus.
+ torStatus.wait(oneSecTimeout);
+ }
+ } catch (InterruptedException e) {
+ // ignore.
+ } catch (IllegalArgumentException e) {
+ // oneSecTimeout should never be negative
+ } catch (IllegalMonitorStateException e) {
+ // |synchronized| should take care of this
+ }
+
+ // Unregister the receiver
+ LocalBroadcastManager.getInstance(this).unregisterReceiver(mLocalBroadcastReceiver);
+
+ return torStatus;
+ }
+
+ /**
+ * Receive the current Tor status.
+ *
+ * Send a request for the current status and receive the response.
+ * Returns true if Tor is running, false otherwise.
+ *
+ * mTorStatus provides synchronization across threads.
+ */
+ private boolean checkTorIsStarted() {
+ // When tor is started, true. Otherwise, false
+ mTorStatus = false;
+ BroadcastReceiver br = setupReceiveTorIsStartedAsync();
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ requestTorIsStartedAsync();
+ }
+ }).start();
+
+ return receiveTorIsStartedAsync(br, mTorStatus);
+ }
+
+
+ /**
* Code to actually show the first run pager, separated
* for distribution purposes.
*/
@@ -1309,6 +1437,7 @@ public class BrowserApp extends GeckoApp
final SafeIntent intent = new SafeIntent(getIntent());
if (!IntentUtils.getIsInAutomationFromEnvironment(intent)) {
checkStartOrbot();
+ mTorNeedsStart = !checkTorIsStarted();
}
}
@@ -1763,6 +1892,7 @@ public class BrowserApp extends GeckoApp
GeckoNetworkManager.destroy();
mOrbotNeedsStart = true;
+ mTorNeedsStart = true;
super.onDestroy();
}
1
0

[tor-browser/tor-browser-60.5.1esr-8.5-1] Revert "Bug 28051 - Open Orbot when the notification is tapped"
by gk@torproject.org 15 Mar '19
by gk@torproject.org 15 Mar '19
15 Mar '19
commit fbf54c71e26a64859f953e18a34e5c9e8831f071
Author: Matthew Finkel <Matthew.Finkel(a)gmail.com>
Date: Fri Jan 18 13:07:10 2019 +0000
Revert "Bug 28051 - Open Orbot when the notification is tapped"
This reverts commit b5be1210aab76340526aa7828d5d9050b8183e62.
Orbot isn't our bootstrapper anymore
---
.../base/java/org/mozilla/gecko/LauncherActivity.java | 18 ------------------
1 file changed, 18 deletions(-)
diff --git a/mobile/android/base/java/org/mozilla/gecko/LauncherActivity.java b/mobile/android/base/java/org/mozilla/gecko/LauncherActivity.java
index 4cd94ed538c7..e8f8facc24c6 100644
--- a/mobile/android/base/java/org/mozilla/gecko/LauncherActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/LauncherActivity.java
@@ -45,9 +45,6 @@ import static org.mozilla.gecko.deeplink.DeepLinkContract.LINK_FXA_SIGNIN;
import org.mozilla.gecko.deeplink.DeepLinkContract;
-import org.torproject.android.OrbotMainActivity;
-import org.torproject.android.service.TorServiceConstants;
-
/**
* Activity that receives incoming Intents and dispatches them to the appropriate activities (e.g. browser, custom tabs, web app).
*/
@@ -70,9 +67,6 @@ public class LauncherActivity extends Activity {
} else if (isWebAppIntent(safeIntent)) {
dispatchWebAppIntent();
- } else if (TorServiceConstants.TOR_APP_USERNAME.equals(getIntent().getAction())) {
- dispatchOrbotIntent();
-
// If it's not a view intent, it won't be a custom tabs intent either. Just launch!
} else if (!isViewIntentWithURL(safeIntent)) {
dispatchNormalIntent();
@@ -122,18 +116,6 @@ public class LauncherActivity extends Activity {
startActivity(intent);
}
- private void dispatchOrbotIntent() {
- final String orbotStartAction = "android.intent.action.MAIN";
- final Intent intent = new Intent(orbotStartAction, null, this, OrbotMainActivity.class);
-
- //When we launch Orbot, we want a new task.
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
- intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-
- startActivity(intent);
- }
-
private void dispatchUrlIntent(@NonNull String url) {
Intent intent = new Intent(getIntent());
intent.setData(Uri.parse(url));
1
0

[tor-browser/tor-browser-60.5.1esr-8.5-1] Bug 28329 - Part 3. Remove OrbotActivity dependency
by gk@torproject.org 15 Mar '19
by gk@torproject.org 15 Mar '19
15 Mar '19
commit e4d2df5e776b6270b5eb98f4b718ea6eb5ea5094
Author: Matthew Finkel <Matthew.Finkel(a)gmail.com>
Date: Wed Feb 20 01:08:36 2019 +0000
Bug 28329 - Part 3. Remove OrbotActivity dependency
---
mobile/android/app/build.gradle | 5 -----
mobile/android/base/AndroidManifest.xml.in | 8 ++++++++
.../base/java/org/mozilla/gecko/BrowserApp.java | 18 ------------------
.../base/java/org/mozilla/gecko/GeckoApplication.java | 5 -----
4 files changed, 8 insertions(+), 28 deletions(-)
diff --git a/mobile/android/app/build.gradle b/mobile/android/app/build.gradle
index d0060901b6b3..c8380042d8a9 100644
--- a/mobile/android/app/build.gradle
+++ b/mobile/android/app/build.gradle
@@ -225,7 +225,6 @@ dependencies {
implementation "com.android.support:design:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
implementation "com.android.support:customtabs:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
implementation "com.android.support:palette-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
- implementation files('Orbot-16.0.5-RC-1-tor-0.3.4.9-fullperm-release.aar')
implementation files('orbotservice-release.aar')
implementation files('jsocksAndroid-release.aar')
@@ -266,10 +265,6 @@ dependencies {
// Including the Robotium JAR directly can cause issues with dexing.
androidTestImplementation 'com.jayway.android.robotium:robotium-solo:5.5.4'
- // Orbot
- implementation 'com.github.delight-im:Android-Languages:v1.0.1'
- implementation 'pl.bclogic:pulsator4droid:1.0.3'
-
// Orbotservice
implementation 'org.torproject:tor-android-binary:0.3.4.9'
implementation 'com.jrummyapps:android-shell:1.0.1'
diff --git a/mobile/android/base/AndroidManifest.xml.in b/mobile/android/base/AndroidManifest.xml.in
index c7c5ead7f82f..72b1e16925b7 100644
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -493,5 +493,13 @@
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
#endif
+ <!-- Define Orbotservice's TorService -->
+ <service
+ android:name="org.torproject.android.service.TorService"
+ android:enabled="true"
+ android:exported="false"
+ android:stopWithTask="true">
+ </service>
+
</application>
</manifest>
diff --git a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
index 5aa0a6a7f3ac..464b4054c9ff 100644
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -182,7 +182,6 @@ import org.mozilla.gecko.widget.GeckoActionProvider;
import org.mozilla.gecko.widget.SplashScreen;
import org.mozilla.geckoview.GeckoSession;
-import org.torproject.android.OrbotMainActivity;
import org.torproject.android.service.TorService;
import org.torproject.android.service.TorServiceConstants;
@@ -242,7 +241,6 @@ public class BrowserApp extends GeckoApp
public static final int ACTIVITY_REQUEST_TRIPLE_READERVIEW = 4001;
public static final int ACTIVITY_RESULT_TRIPLE_READERVIEW_ADD_BOOKMARK = 4002;
public static final int ACTIVITY_RESULT_TRIPLE_READERVIEW_IGNORE = 4003;
- public static final int ACTIVITY_RESULT_ORBOT_LAUNCH = 5001;
public static final String ACTION_VIEW_MULTIPLE = AppConstants.ANDROID_PACKAGE_NAME + ".action.VIEW_MULTIPLE";
@@ -271,7 +269,6 @@ public class BrowserApp extends GeckoApp
private HomeScreen mHomeScreen;
private TabsPanel mTabsPanel;
- private boolean mOrbotNeedsStart = true;
private boolean mTorNeedsStart = true;
private boolean showSplashScreen = false;
@@ -1410,14 +1407,6 @@ public class BrowserApp extends GeckoApp
}
}
- public void checkStartOrbot() {
- if (mOrbotNeedsStart) {
- final String orbotStartAction = "android.intent.action.MAIN";
- final Intent launchOrbot = new Intent(orbotStartAction, null, this, OrbotMainActivity.class);
- startActivityForResult(launchOrbot, ACTIVITY_RESULT_ORBOT_LAUNCH, null);
- }
- }
-
@Override
public void onResume() {
super.onResume();
@@ -1436,7 +1425,6 @@ public class BrowserApp extends GeckoApp
// need to know if we are in automation.
final SafeIntent intent = new SafeIntent(getIntent());
if (!IntentUtils.getIsInAutomationFromEnvironment(intent)) {
- checkStartOrbot();
mTorNeedsStart = !checkTorIsStarted();
}
}
@@ -1891,7 +1879,6 @@ public class BrowserApp extends GeckoApp
NotificationHelper.destroy();
GeckoNetworkManager.destroy();
- mOrbotNeedsStart = true;
mTorNeedsStart = true;
super.onDestroy();
@@ -3111,11 +3098,6 @@ public class BrowserApp extends GeckoApp
TabQueueHelper.processTabQueuePromptResponse(resultCode, this);
break;
- case ACTIVITY_RESULT_ORBOT_LAUNCH:
- Log.d(LOGTAG, "onActivityResult: ACTIVITY_RESULT_ORBOT_LAUNCH");
- mOrbotNeedsStart = false;
- break;
-
default:
for (final BrowserAppDelegate delegate : delegates) {
delegate.onActivityResult(this, requestCode, resultCode, data);
diff --git a/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java b/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
index b8e4985332a9..b38e7184c798 100644
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
@@ -63,7 +63,6 @@ import java.net.URL;
import java.util.UUID;
import org.torproject.android.service.util.Prefs;
-import org.torproject.android.settings.Languages;
public class GeckoApplication extends Application
implements HapticFeedbackDelegate {
@@ -325,10 +324,6 @@ public class GeckoApplication extends Application
// Give Orbot the base Context
Prefs.setContext(context);
- // Initialize Orbot's Language settings
- Languages.setup(BrowserApp.class, R.string.menu_settings);
- Languages.setLanguage(this, Prefs.getDefaultLocale(), true);
-
super.onCreate();
}
1
0

[tor-browser/tor-browser-60.5.1esr-8.5-1] Bug 28329 - Part 4. Add new Tor Bootstrapping and configuration screens
by gk@torproject.org 15 Mar '19
by gk@torproject.org 15 Mar '19
15 Mar '19
commit 87d6c829225d271ba94fa21672b157085dbf1ad9
Author: Matthew Finkel <Matthew.Finkel(a)gmail.com>
Date: Thu Mar 14 02:03:26 2019 +0000
Bug 28329 - Part 4. Add new Tor Bootstrapping and configuration screens
---
.../android/app/src/main/res/layout/gecko_app.xml | 5 +
.../preference_tor_network_bridge_summary.xml | 25 +
.../preference_tor_network_bridges_enabled.xml | 85 ++
...eference_tor_network_bridges_enabled_switch.xml | 15 +
.../preference_tor_network_provide_bridge.xml | 89 ++
.../preference_tor_network_select_bridge_type.xml | 128 +++
.../app/src/main/res/layout/tor_bootstrap.xml | 86 ++
.../layout/tor_bootstrap_animation_container.xml | 20 +
.../app/src/main/res/layout/tor_bootstrap_log.xml | 37 +
.../main/res/xml/preferences_tor_network_main.xml | 15 +
.../xml/preferences_tor_network_provide_bridge.xml | 27 +
.../preferences_tor_network_select_bridge_type.xml | 17 +
mobile/android/base/AndroidManifest.xml.in | 5 +
.../base/java/org/mozilla/gecko/BrowserApp.java | 44 +-
.../TorBootstrapAnimationContainer.java | 82 ++
.../gecko/torbootstrap/TorBootstrapLogPanel.java | 54 ++
.../gecko/torbootstrap/TorBootstrapLogger.java | 17 +
.../gecko/torbootstrap/TorBootstrapPager.java | 160 ++++
.../torbootstrap/TorBootstrapPagerConfig.java | 87 ++
.../gecko/torbootstrap/TorBootstrapPanel.java | 428 ++++++++++
.../gecko/torbootstrap/TorLogEventListener.java | 128 +++
.../mozilla/gecko/torbootstrap/TorPreferences.java | 950 +++++++++++++++++++++
22 files changed, 2501 insertions(+), 3 deletions(-)
diff --git a/mobile/android/app/src/main/res/layout/gecko_app.xml b/mobile/android/app/src/main/res/layout/gecko_app.xml
index ca25841695a7..9a5d03f79409 100644
--- a/mobile/android/app/src/main/res/layout/gecko_app.xml
+++ b/mobile/android/app/src/main/res/layout/gecko_app.xml
@@ -68,6 +68,11 @@
android:layout_width="match_parent"
android:layout_height="match_parent"/>
+ <ViewStub android:id="@+id/tor_bootstrap_pager_stub"
+ android:layout="@layout/tor_bootstrap_animation_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
</FrameLayout>
<View android:id="@+id/doorhanger_overlay"
diff --git a/mobile/android/app/src/main/res/layout/preference_tor_network_bridge_summary.xml b/mobile/android/app/src/main/res/layout/preference_tor_network_bridge_summary.xml
new file mode 100644
index 000000000000..d99b3c9543b0
--- /dev/null
+++ b/mobile/android/app/src/main/res/layout/preference_tor_network_bridge_summary.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:gravity="center_vertical" >
+ <TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/tor_network_bridge_summary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="30sp"
+ android:paddingBottom="30sp"
+ android:paddingLeft="20sp"
+ android:paddingRight="20sp"
+ android:textSize="16sp"
+ android:fontFamily="Roboto-Regular"
+ android:textColor="#DE000000"
+ android:lineSpacingMultiplier="1.43"
+ android:text="@string/pref_category_tor_bridge_summary" />
+</LinearLayout>
diff --git a/mobile/android/app/src/main/res/layout/preference_tor_network_bridges_enabled.xml b/mobile/android/app/src/main/res/layout/preference_tor_network_bridges_enabled.xml
new file mode 100644
index 000000000000..8d8e4f320ba7
--- /dev/null
+++ b/mobile/android/app/src/main/res/layout/preference_tor_network_bridges_enabled.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 The Android Open Source Project
+ 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.
+-->
+<!-- Layout for a Preference in a PreferenceActivity. The
+ Preference is able to place a specific widget for its particular
+ type in the "widget_frame" layout.
+ This is a modified version of the default Android Preference layout,
+ See: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/pie-…
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:gravity="center_vertical"
+ android:paddingEnd="?android:attr/scrollbarSize"
+ android:orientation="vertical"
+ android:background="?android:attr/selectableItemBackground" >
+ <TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/tor_network_configuration_summary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="30sp"
+ android:paddingBottom="30sp"
+ android:paddingLeft="20sp"
+ android:paddingRight="20sp"
+ android:textSize="16sp"
+ android:fontFamily="Roboto-Regular"
+ android:textColor="#DE000000"
+ android:lineSpacingMultiplier="1.43"
+ android:text="@string/pref_category_tor_network_summary" />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical" >
+ <ImageView
+ android:id="@+android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ />
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="15dp"
+ android:layout_marginEnd="6dp"
+ android:layout_marginTop="6dp"
+ android:layout_marginBottom="12dp"
+ android:layout_weight="1">
+ <TextView android:id="@+android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:fontFamily="Roboto-Regular"
+ android:textSize="20sp"
+ android:textColor="#DE000000"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal" />
+ <TextView android:id="@+android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ android:fontFamily="Roboto-Regular"
+ android:textSize="16sp"
+ android:textColorLink="#8000FF"
+ android:clickable="true"
+ android:focusable="false"
+ android:maxLines="2" />
+ </RelativeLayout>
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout android:id="@+android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/mobile/android/app/src/main/res/layout/preference_tor_network_bridges_enabled_switch.xml b/mobile/android/app/src/main/res/layout/preference_tor_network_bridges_enabled_switch.xml
new file mode 100644
index 000000000000..3ab276f0916c
--- /dev/null
+++ b/mobile/android/app/src/main/res/layout/preference_tor_network_bridges_enabled_switch.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<Switch xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+android:id/switch_widget"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:focusable="false"
+ android:clickable="true"
+ android:thumbTint="@color/tor_bridges_enabled_colors"
+ android:trackTint="@color/tor_bridges_enabled_colors"
+ android:background="@null" />
diff --git a/mobile/android/app/src/main/res/layout/preference_tor_network_provide_bridge.xml b/mobile/android/app/src/main/res/layout/preference_tor_network_provide_bridge.xml
new file mode 100644
index 000000000000..9e72b44ae734
--- /dev/null
+++ b/mobile/android/app/src/main/res/layout/preference_tor_network_provide_bridge.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:orientation="vertical"
+ android:paddingEnd="?android:attr/scrollbarSize"
+ android:gravity="center_vertical"
+ android:background="?android:attr/selectableItemBackground" >
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="16sp"
+ android:paddingRight="16sp"
+ android:orientation="vertical" >
+ <TextView android:id="@+android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="Roboto-Regular"
+ android:textSize="20sp"
+ android:textColor="#DE000000"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal" />
+ <TextView android:id="@+android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingBottom="30sp"
+ android:fontFamily="Roboto-Regular"
+ android:textSize="16sp"
+ android:maxLines="4" />
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:gravity="center_vertical"
+ android:orientation="vertical" >
+ <EditText
+ android:id="@+id/tor_network_provide_bridge1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="10dp"
+ android:inputType="text"
+ android:textSize="20sp"
+ android:fontFamily="Roboto-Regular"
+ android:paddingTop="22sp"
+ android:paddingLeft="16sp"
+ android:paddingBottom="22sp"
+ android:background="#DCDCDC"
+ android:hint="@string/pref_tor_bridges_provide_manual_address_port_placeholder" />
+ <EditText
+ android:id="@+id/tor_network_provide_bridge2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="10dp"
+ android:inputType="text"
+ android:textSize="20sp"
+ android:fontFamily="Roboto-Regular"
+ android:paddingTop="22sp"
+ android:paddingLeft="16sp"
+ android:paddingBottom="22sp"
+ android:background="#DCDCDC"
+ android:hint="@string/pref_tor_bridges_provide_manual_address_port_placeholder" />
+ <EditText
+ android:id="@+id/tor_network_provide_bridge3"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="text"
+ android:textSize="20sp"
+ android:fontFamily="Roboto-Regular"
+ android:paddingTop="22sp"
+ android:paddingLeft="16sp"
+ android:paddingBottom="22sp"
+ android:background="#DCDCDC"
+ android:hint="@string/pref_tor_bridges_provide_manual_address_port_placeholder" />
+ </LinearLayout>
+ </LinearLayout>
+ <LinearLayout android:id="@+android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:orientation="vertical">
+ </LinearLayout>
+</LinearLayout>
diff --git a/mobile/android/app/src/main/res/layout/preference_tor_network_select_bridge_type.xml b/mobile/android/app/src/main/res/layout/preference_tor_network_select_bridge_type.xml
new file mode 100644
index 000000000000..2c1632bb8268
--- /dev/null
+++ b/mobile/android/app/src/main/res/layout/preference_tor_network_select_bridge_type.xml
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:orientation="vertical"
+ android:paddingEnd="?android:attr/scrollbarSize"
+ android:gravity="center_vertical"
+ android:background="?android:attr/selectableItemBackground" >
+
+ <!-- Include the Bridge description -->
+ <include layout="@layout/preference_tor_network_bridge_summary" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="20sp"
+ android:orientation="vertical" >
+ <LinearLayout
+ android:id="@+id/title_and_summary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+ <TextView android:id="@+android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="Roboto-Regular"
+ android:textSize="20sp"
+ android:textColor="#DE000000"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal" />
+ <TextView android:id="@+android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="40dp"
+ android:fontFamily="Roboto-Regular"
+ android:textSize="16sp"
+ android:maxLines="4" />
+ </LinearLayout>
+ <include layout="@xml/separator" />
+ <RadioGroup
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="20sp"
+ android:visibility="gone"
+ android:layoutDirection="rtl"
+ android:id="@+id/pref_radio_group_builtin_bridges_type">
+ <RadioButton android:id="@+id/radio_pref_bridges_obfs4"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10sp"
+ android:layout_marginBottom="10sp"
+ android:buttonTint="@color/tor_bridges_select_builtin"
+ android:fontFamily="Roboto-Regular"
+ android:textColor="#DE000000"
+ android:textSize="16sp"
+ android:text="@string/pref_bridges_type_obfs4"/>
+ <include layout="@xml/separator" />
+ <RadioButton android:id="@+id/radio_pref_bridges_meek_azure"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10sp"
+ android:layout_marginBottom="10sp"
+ android:buttonTint="@color/tor_bridges_select_builtin"
+ android:fontFamily="Roboto-Regular"
+ android:textColor="#DE000000"
+ android:textSize="16sp"
+ android:text="@string/pref_bridges_type_meek_azure"/>
+ <include layout="@xml/separator" />
+ <RadioButton android:id="@+id/radio_pref_bridges_obfs3"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10sp"
+ android:layout_marginBottom="10sp"
+ android:buttonTint="@color/tor_bridges_select_builtin"
+ android:fontFamily="Roboto-Regular"
+ android:textColor="#DE000000"
+ android:textSize="16sp"
+ android:text="@string/pref_bridges_type_obfs3"/>
+ <include layout="@xml/separator" />
+ </RadioGroup>
+ </LinearLayout>
+
+ <LinearLayout android:id="@+android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:orientation="vertical">
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clickable="true"
+ android:paddingTop="20sp"
+ android:paddingLeft="20sp"
+ android:id="@+id/tor_network_provide_a_bridge"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/tor_network_provide_a_bridge_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="Roboto-Regular"
+ android:textSize="20sp"
+ android:textColor="#DE000000"
+ android:text="@string/pref_tor_bridges_provide_manual_button_title"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal" />
+ <TextView
+ android:id="@+id/tor_network_provide_a_bridge_summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="30dp"
+ android:fontFamily="Roboto-Regular"
+ android:textSize="16sp"
+ android:text="@string/pref_tor_bridges_provide_manual_summary"
+ android:maxLines="4" />
+ <include layout="@xml/separator" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/mobile/android/app/src/main/res/layout/tor_bootstrap.xml b/mobile/android/app/src/main/res/layout/tor_bootstrap.xml
new file mode 100644
index 000000000000..ce2b1c910a44
--- /dev/null
+++ b/mobile/android/app/src/main/res/layout/tor_bootstrap.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/tor_bootstrap_background">
+
+ <ImageView android:id="@+id/tor_bootstrap_settings_gear"
+ app:srcCompat="@drawable/ic_settings_24px"
+ android:tint="#ffffffff"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="25dp"
+ android:layout_marginRight="20dp"
+ android:layout_alignParentRight="true" />
+
+ <!-- These three elements are rendered in reverse order -->
+ <TextView android:id="@+id/tor_bootstrap_swipe_log"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:width="301dp"
+ android:height="24dp"
+ android:layout_marginBottom="20dp"
+ android:layout_centerHorizontal="true"
+ android:layout_alignParentBottom="true"
+ android:gravity="center"
+ android:visibility="invisible"
+ android:textSize="14sp"
+ android:fontFamily="Roboto-Regular"
+ android:textColor="#FFFFFFFF"
+ android:lineSpacingMultiplier="1.71"
+ android:text="@string/tor_bootstrap_swipe_for_logs"/>
+
+ <Button android:id="@+id/tor_bootstrap_connect"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="7dp"
+ android:width="144dp"
+ android:height="48dp"
+ android:textSize="14sp"
+ android:layout_above="@id/tor_bootstrap_swipe_log"
+ android:layout_centerHorizontal="true"
+ android:background="@drawable/rounded_corners"
+ android:fontFamily="Roboto-Medium"
+ android:textColor="@color/tor_bootstrap_background"
+ android:lineSpacingMultiplier="1.14"
+ android:text="@string/tor_bootstrap_connect" />
+
+ <TextView android:id="@+id/tor_bootstrap_last_status_message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:width="301dp"
+ android:height="24dp"
+ android:layout_marginBottom="40dp"
+ android:layout_above="@id/tor_bootstrap_connect"
+ android:layout_centerHorizontal="true"
+ android:gravity="center"
+ android:singleLine="true"
+ android:textSize="14sp"
+ android:fontFamily="RobotoMono-Regular"
+ android:textColor="@android:color/white"
+ android:lineSpacingMultiplier="2"
+ android:visibility="invisible" />
+
+ <!-- Keep the src synchronized with TorBootstrapPanel::stopBootstrapping() -->
+ <ImageView android:id="@+id/tor_bootstrap_onion"
+ app:srcCompat="@drawable/tor_spinning_onion"
+ android:scaleType="fitCenter"
+ android:tint="#ffffffff"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_marginTop="130dp"
+ android:layout_marginBottom="37dp"
+ android:layout_marginRight="95dp"
+ android:layout_marginLeft="95dp"
+ android:layout_centerHorizontal="true"
+ android:layout_below="@id/tor_bootstrap_settings_gear"
+ android:layout_above="@id/tor_bootstrap_last_status_message"
+ android:paddingLeft="20dp"
+ android:paddingRight="20dp"/>
+</RelativeLayout>
diff --git a/mobile/android/app/src/main/res/layout/tor_bootstrap_animation_container.xml b/mobile/android/app/src/main/res/layout/tor_bootstrap_animation_container.xml
new file mode 100644
index 000000000000..04dfeb0f3509
--- /dev/null
+++ b/mobile/android/app/src/main/res/layout/tor_bootstrap_animation_container.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<org.mozilla.gecko.torbootstrap.TorBootstrapAnimationContainer xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:gecko="http://schemas.android.com/apk/res-auto"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:background="@color/tor_bootstrap_background">
+
+ <org.mozilla.gecko.torbootstrap.TorBootstrapPager
+ android:id="@+id/tor_bootstrap_pager"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/tor_bootstrap_background">
+
+ </org.mozilla.gecko.torbootstrap.TorBootstrapPager>
+</org.mozilla.gecko.torbootstrap.TorBootstrapAnimationContainer>
diff --git a/mobile/android/app/src/main/res/layout/tor_bootstrap_log.xml b/mobile/android/app/src/main/res/layout/tor_bootstrap_log.xml
new file mode 100644
index 000000000000..c2f02d658d50
--- /dev/null
+++ b/mobile/android/app/src/main/res/layout/tor_bootstrap_log.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/tor_bootstrap_background">
+
+
+ <ImageView android:id="@+id/tor_bootstrap_settings_gear"
+ app:srcCompat="@drawable/ic_settings_24px"
+ android:tint="#ffffffff"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="25dp"
+ android:layout_marginRight="20dp"
+ android:layout_alignParentRight="true" />
+
+ <!-- Encapsulate the TextView within the ScrollView so the view is scrollable -->
+ <ScrollView android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:layout_below="@id/tor_bootstrap_settings_gear" >
+ <TextView android:id="@+id/tor_bootstrap_last_status_message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textColor="@android:color/white"
+ android:fontFamily="RobotoMono-Regular"
+ android:textSize="14sp"
+ android:textIsSelectable="true"
+ android:layout_marginLeft="20dp"
+ android:layout_marginRight="20dp" />
+ </ScrollView>
+</RelativeLayout>
diff --git a/mobile/android/app/src/main/res/xml/preferences_tor_network_main.xml b/mobile/android/app/src/main/res/xml/preferences_tor_network_main.xml
new file mode 100644
index 000000000000..c397bd7c1fc9
--- /dev/null
+++ b/mobile/android/app/src/main/res/xml/preferences_tor_network_main.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:gecko="http://schemas.android.com/apk/res-auto"
+ android:enabled="true">
+ <SwitchPreference android:key="android.not_a_preference.tor.bridges.enabled"
+ android:title="@string/pref_choice_tor_bridges_enabled_title"
+ android:summaryOff="@string/pref_choice_tor_bridges_enabled_summary"
+ android:selectable="false"
+ android:layout="@layout/preference_tor_network_bridges_enabled"
+ android:widgetLayout="@layout/preference_tor_network_bridges_enabled_switch" />
+</PreferenceScreen>
diff --git a/mobile/android/app/src/main/res/xml/preferences_tor_network_provide_bridge.xml b/mobile/android/app/src/main/res/xml/preferences_tor_network_provide_bridge.xml
new file mode 100644
index 000000000000..e8346f4fec63
--- /dev/null
+++ b/mobile/android/app/src/main/res/xml/preferences_tor_network_provide_bridge.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:gecko="http://schemas.android.com/apk/res-auto"
+ android:enabled="true">
+
+
+ <!-- Ideally, this preference would not be needed. We would move the
+ summary into the tor.bridges.provide preference. However, there is
+ a bug in the layout where typing in the text field isn't shown until
+ the user presses the back button. This only occurs when the EditText
+ View is under the first ViewGroup under the ListView. -->
+ <Preference
+ android:layout="@layout/preference_tor_network_bridge_summary"
+ android:selectable="false"
+ android:shouldDisableView="false"
+ android:enabled="false"/>
+
+ <Preference
+ android:key="android.not_a_preference.tor.bridges.provide"
+ android:layout="@layout/preference_tor_network_provide_bridge"
+ android:title="@string/pref_tor_bridges_provide_manual_text_title"
+ android:summary="@string/pref_tor_bridges_provide_manual_summary" />
+</PreferenceScreen>
diff --git a/mobile/android/app/src/main/res/xml/preferences_tor_network_select_bridge_type.xml b/mobile/android/app/src/main/res/xml/preferences_tor_network_select_bridge_type.xml
new file mode 100644
index 000000000000..0bcc18c38997
--- /dev/null
+++ b/mobile/android/app/src/main/res/xml/preferences_tor_network_select_bridge_type.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:gecko="http://schemas.android.com/apk/res-auto"
+ android:enabled="true">
+
+ <Preference
+ android:key="android.not_a_preference.tor.bridges.type"
+ android:layout="@layout/preference_tor_network_select_bridge_type"
+ android:title="@string/pref_tor_bridges_provide_select_text_title"
+ android:summary="@string/pref_choice_tor_bridges_enabled_summary"
+ android:selectable="false"/>
+
+</PreferenceScreen>
diff --git a/mobile/android/base/AndroidManifest.xml.in b/mobile/android/base/AndroidManifest.xml.in
index 72b1e16925b7..01b23d22a19b 100644
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -501,5 +501,10 @@
android:stopWithTask="true">
</service>
+ <activity android:name="org.mozilla.gecko.torbootstrap.TorPreferences"
+ android:theme="@style/Gecko.Preferences"
+ android:configChanges="orientation|screenSize|locale|layoutDirection"
+ android:excludeFromRecents="true"/>
+
</application>
</manifest>
diff --git a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
index 464b4054c9ff..f8af42f09b5f 100644
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -158,6 +158,7 @@ import org.mozilla.gecko.toolbar.AutocompleteHandler;
import org.mozilla.gecko.toolbar.BrowserToolbar;
import org.mozilla.gecko.toolbar.BrowserToolbar.TabEditingState;
import org.mozilla.gecko.toolbar.PwaConfirm;
+import org.mozilla.gecko.torbootstrap.TorBootstrapAnimationContainer;
import org.mozilla.gecko.trackingprotection.TrackingProtectionPrompt;
import org.mozilla.gecko.updater.PostUpdateHandler;
import org.mozilla.gecko.updater.UpdateServiceHelper;
@@ -266,6 +267,7 @@ public class BrowserApp extends GeckoApp
private TabStripInterface mTabStrip;
private AnimatedProgressBar mProgressView;
private FirstrunAnimationContainer mFirstrunAnimationContainer;
+ private TorBootstrapAnimationContainer mTorBootstrapAnimationContainer;
private HomeScreen mHomeScreen;
private TabsPanel mTabsPanel;
@@ -486,7 +488,7 @@ public class BrowserApp extends GeckoApp
mVideoPlayer.stop();
}
- if (Tabs.getInstance().isSelectedTab(tab) && mDynamicToolbar.isEnabled()) {
+ if (Tabs.getInstance().isSelectedTab(tab) && mDynamicToolbar.isEnabled() && !isTorBootstrapVisible()) {
final VisibilityTransition transition = (tab.getShouldShowToolbarWithoutAnimationOnFirstSelection()) ?
VisibilityTransition.IMMEDIATE : VisibilityTransition.ANIMATE;
mDynamicToolbar.setVisible(true, transition);
@@ -496,7 +498,7 @@ public class BrowserApp extends GeckoApp
}
// fall through
case LOCATION_CHANGE:
- if (Tabs.getInstance().isSelectedTab(tab)) {
+ if (Tabs.getInstance().isSelectedTab(tab) && !isTorBootstrapVisible()) {
updateHomePagerForTab(tab);
}
@@ -509,7 +511,7 @@ public class BrowserApp extends GeckoApp
if (Tabs.getInstance().isSelectedTab(tab)) {
invalidateOptionsMenu();
- if (mDynamicToolbar.isEnabled()) {
+ if (mDynamicToolbar.isEnabled() && !isTorBootstrapVisible()) {
mDynamicToolbar.setVisible(true, VisibilityTransition.ANIMATE);
}
}
@@ -1359,6 +1361,10 @@ public class BrowserApp extends GeckoApp
final SafeIntent intent = new SafeIntent(getIntent());
if (!IntentUtils.getIsInAutomationFromEnvironment(intent)) {
+ if (mTorNeedsStart) {
+ showTorBootstrapPager();
+ }
+
// We can't show the first run experience until Gecko has finished initialization (bug 1077583).
checkFirstrun(this, intent);
}
@@ -2756,6 +2762,11 @@ public class BrowserApp extends GeckoApp
&& mHomeScreenContainer != null && mHomeScreenContainer.getVisibility() == View.VISIBLE);
}
+ private boolean isTorBootstrapVisible() {
+ return (mTorBootstrapAnimationContainer != null && mTorBootstrapAnimationContainer.isVisible()
+ && mHomeScreenContainer != null && mHomeScreenContainer.getVisibility() == View.VISIBLE);
+ }
+
/**
* Enters editing mode with the current tab's URL. There might be no
* tabs loaded by the time the user enters editing mode e.g. just after
@@ -3107,6 +3118,33 @@ public class BrowserApp extends GeckoApp
}
}
+ private void showTorBootstrapPager() {
+
+ if (mTorBootstrapAnimationContainer == null) {
+ // We can't use toggleToolbarChrome() because that uses INVISIBLE, but we need GONE
+ mBrowserChrome.setVisibility(View.GONE);
+ final ViewStub torBootstrapPagerStub = (ViewStub) findViewById(R.id.tor_bootstrap_pager_stub);
+ mTorBootstrapAnimationContainer = (TorBootstrapAnimationContainer) torBootstrapPagerStub.inflate();
+ mTorBootstrapAnimationContainer.load(this, getSupportFragmentManager());
+ mTorBootstrapAnimationContainer.registerOnFinishListener(new TorBootstrapAnimationContainer.OnFinishListener() {
+ @Override
+ public void onFinish() {
+ // Show the chrome again
+ toggleToolbarChrome(true);
+ // When the content loaded in the background (such as about:tor),
+ // it was loaded while mBrowserChrome was GONE. We should refresh the
+ // height now so the page is rendered correctly.
+ Tabs.getInstance().getSelectedTab().doReload(false);
+
+ // If we finished, then Tor bootstrapped 100%
+ mTorNeedsStart = false;
+ }
+ });
+ }
+
+ mHomeScreenContainer.setVisibility(View.VISIBLE);
+ }
+
private void showFirstrunPager() {
if (mFirstrunAnimationContainer == null) {
diff --git a/mobile/android/base/java/org/mozilla/gecko/torbootstrap/TorBootstrapAnimationContainer.java b/mobile/android/base/java/org/mozilla/gecko/torbootstrap/TorBootstrapAnimationContainer.java
new file mode 100644
index 000000000000..188e03df0092
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/torbootstrap/TorBootstrapAnimationContainer.java
@@ -0,0 +1,82 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.torbootstrap;
+
+import android.app.Activity;
+import android.content.Context;
+import android.support.v4.app.FragmentManager;
+import android.util.AttributeSet;
+
+import android.view.View;
+import android.widget.LinearLayout;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.firstrun.FirstrunAnimationContainer;
+
+/**
+ * A container for the bootstrapping flow.
+ *
+ * Mostly a modified version of FirstrunAnimationContainer
+ */
+public class TorBootstrapAnimationContainer extends FirstrunAnimationContainer {
+
+ public static interface OnFinishListener {
+ public void onFinish();
+ }
+
+ private TorBootstrapPager pager;
+ private boolean visible;
+
+ // Provides a callback so BrowserApp can execute an action
+ // when the bootstrapping is complete and the bootstrapping
+ // screen closes.
+ private OnFinishListener onFinishListener;
+
+ public TorBootstrapAnimationContainer(Context context) {
+ this(context, null);
+ }
+ public TorBootstrapAnimationContainer(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void load(Activity activity, FragmentManager fm) {
+ visible = true;
+ pager = findViewById(R.id.tor_bootstrap_pager);
+ pager.load(activity, fm, new OnFinishListener() {
+ @Override
+ public void onFinish() {
+ hide();
+ }
+ });
+ }
+
+ public void hide() {
+ visible = false;
+ if (onFinishListener != null) {
+ onFinishListener.onFinish();
+ }
+ animateHide();
+ }
+
+ private void animateHide() {
+ final Animator alphaAnimator = ObjectAnimator.ofFloat(this, "alpha", 0);
+ alphaAnimator.setDuration(150);
+ alphaAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ TorBootstrapAnimationContainer.this.setVisibility(View.GONE);
+ }
+ });
+
+ alphaAnimator.start();
+ }
+
+ public void registerOnFinishListener(OnFinishListener listener) {
+ onFinishListener = listener;
+ }
+}
diff --git a/mobile/android/base/java/org/mozilla/gecko/torbootstrap/TorBootstrapLogPanel.java b/mobile/android/base/java/org/mozilla/gecko/torbootstrap/TorBootstrapLogPanel.java
new file mode 100644
index 000000000000..18d827cec216
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/torbootstrap/TorBootstrapLogPanel.java
@@ -0,0 +1,54 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.torbootstrap;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+import org.mozilla.gecko.R;
+
+/**
+ * Simple subclass of TorBootstrapPanel specifically for showing
+ * Tor and Orbot log entries.
+ */
+public class TorBootstrapLogPanel extends TorBootstrapPanel {
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) {
+ mRoot = (ViewGroup) inflater.inflate(R.layout.tor_bootstrap_log, container, false);
+
+ if (mRoot == null) {
+ Log.w(LOGTAG, "Inflating R.layout.tor_bootstrap returned null");
+ return null;
+ }
+
+ TorLogEventListener.addLogger(this);
+
+ return mRoot;
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstance) {
+ super.onViewCreated(view, savedInstance);
+ // Inherited from the super class
+ configureGearCogClickHandler();
+ }
+
+ // TODO Add a button for Go-to-bottom
+ @Override
+ public void updateStatus(String torServiceMsg, String newTorStatus) {
+ if (torServiceMsg == null) {
+ return;
+ }
+ TextView torLog = (TextView) mRoot.findViewById(R.id.tor_bootstrap_last_status_message);
+ torLog.append("- " + torServiceMsg + "\n");
+ }
+}
diff --git a/mobile/android/base/java/org/mozilla/gecko/torbootstrap/TorBootstrapLogger.java b/mobile/android/base/java/org/mozilla/gecko/torbootstrap/TorBootstrapLogger.java
new file mode 100644
index 000000000000..24c9321beb63
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/torbootstrap/TorBootstrapLogger.java
@@ -0,0 +1,17 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.torbootstrap;
+
+import android.app.Activity;
+
+// Simple interface for a logger.
+//
+// The current implementers are TorBootstrapPanel and
+// TorBootstrapLogPanel.
+public interface TorBootstrapLogger {
+ public void updateStatus(String torServiceMsg, String newTorStatus);
+ public Activity getActivity();
+}
diff --git a/mobile/android/base/java/org/mozilla/gecko/torbootstrap/TorBootstrapPager.java b/mobile/android/base/java/org/mozilla/gecko/torbootstrap/TorBootstrapPager.java
new file mode 100644
index 000000000000..b780810f14ab
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/torbootstrap/TorBootstrapPager.java
@@ -0,0 +1,160 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.torbootstrap;
+
+import android.app.Activity;
+import android.content.Context;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentPagerAdapter;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+
+import org.mozilla.gecko.firstrun.FirstrunPager;
+
+import java.util.List;
+
+/**
+ * ViewPager containing our bootstrapping pages.
+ *
+ * Based on FirstrunPager for simplicity
+ */
+public class TorBootstrapPager extends FirstrunPager {
+
+ private Context context;
+ private Activity mActivity;
+ protected TorBootstrapPanel.PagerNavigation pagerNavigation;
+
+ public TorBootstrapPager(Context context) {
+ this(context, null);
+ }
+
+ public TorBootstrapPager(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ this.context = context;
+ }
+
+ @Override
+ public void addView(View child, int index, ViewGroup.LayoutParams params) {
+ super.addView(child, index, params);
+ }
+
+ // Load the default (hard-coded) panels from TorBootstrapPagerConfig
+ // Mostly copied from super
+ public void load(Activity activity, FragmentManager fm, final TorBootstrapAnimationContainer.OnFinishListener onFinishListener) {
+ mActivity = activity;
+ final List<TorBootstrapPagerConfig.TorBootstrapPanelConfig> panels = TorBootstrapPagerConfig.getDefaultBootstrapPanel();
+
+ setAdapter(new ViewPagerAdapter(fm, panels));
+ this.pagerNavigation = new TorBootstrapPanel.PagerNavigation() {
+ @Override
+ public void next() {
+ // No-op implementation.
+ }
+
+ @Override
+ public void finish() {
+ if (onFinishListener != null) {
+ onFinishListener.onFinish();
+ }
+ }
+ };
+
+ animateLoad();
+ }
+
+ // Copied from super
+ private void animateLoad() {
+ setTranslationY(500);
+ setAlpha(0);
+
+ final Animator translateAnimator = ObjectAnimator.ofFloat(this, "translationY", 0);
+ translateAnimator.setDuration(400);
+
+ final Animator alphaAnimator = ObjectAnimator.ofFloat(this, "alpha", 1);
+ alphaAnimator.setStartDelay(200);
+ alphaAnimator.setDuration(600);
+
+ final AnimatorSet set = new AnimatorSet();
+ set.playTogether(alphaAnimator, translateAnimator);
+ set.setStartDelay(400);
+
+ set.start();
+ }
+
+ // Provide an interface for inter-panel communication allowing
+ // the logging panel to stop the bootstrapping animation on the
+ // main panel.
+ public interface TorBootstrapController {
+ void startBootstrapping();
+ void stopBootstrapping();
+ }
+
+ // Mostly copied from FirstrunPager
+ protected class ViewPagerAdapter extends FragmentPagerAdapter implements TorBootstrapController {
+ private final List<TorBootstrapPagerConfig.TorBootstrapPanelConfig> panels;
+ private final Fragment[] fragments;
+
+ public ViewPagerAdapter(FragmentManager fm, List<TorBootstrapPagerConfig.TorBootstrapPanelConfig> panels) {
+ super(fm);
+ this.panels = panels;
+ this.fragments = new Fragment[panels.size()];
+ }
+
+ @Override
+ public Fragment getItem(int i) {
+ Fragment fragment = fragments[i];
+ if (fragment == null) {
+ TorBootstrapPagerConfig.TorBootstrapPanelConfig panelConfig = panels.get(i);
+ // We know the class is within the "org.mozilla.gecko.torbootstrap" package namespace
+ fragment = Fragment.instantiate(mActivity.getApplicationContext(), panelConfig.getClassname(), panelConfig.getArgs());
+ ((TorBootstrapPanel) fragment).setPagerNavigation(pagerNavigation);
+ ((TorBootstrapPanel) fragment).setContext(mActivity);
+ ((TorBootstrapPanel) fragment).setBootstrapController(this);
+ fragments[i] = fragment;
+ }
+ return fragment;
+ }
+
+ @Override
+ public int getCount() {
+ return panels.size();
+ }
+
+ @Override
+ public CharSequence getPageTitle(int i) {
+ return context.getString(panels.get(i).getTitleRes()).toUpperCase();
+ }
+
+ public void startBootstrapping() {
+ if (fragments.length == 0) {
+ return;
+ }
+
+ TorBootstrapPanel mainPanel = (TorBootstrapPanel) getItem(0);
+ if (mainPanel == null) {
+ return;
+ }
+ mainPanel.startBootstrapping();
+ }
+
+ public void stopBootstrapping() {
+ if (fragments.length == 0) {
+ return;
+ }
+
+ TorBootstrapPanel mainPanel = (TorBootstrapPanel) getItem(0);
+ if (mainPanel == null) {
+ return;
+ }
+ mainPanel.stopBootstrapping();
+ }
+ }
+}
diff --git a/mobile/android/base/java/org/mozilla/gecko/torbootstrap/TorBootstrapPagerConfig.java b/mobile/android/base/java/org/mozilla/gecko/torbootstrap/TorBootstrapPagerConfig.java
new file mode 100644
index 000000000000..7eb5f77fe8ca
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/torbootstrap/TorBootstrapPagerConfig.java
@@ -0,0 +1,87 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.torbootstrap;
+
+import android.os.Bundle;
+import android.util.Log;
+import org.mozilla.gecko.GeckoSharedPrefs;
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
+import org.mozilla.gecko.Experiments;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class TorBootstrapPagerConfig {
+ public static final String LOGTAG = "TorBootstrapPagerConfig";
+
+ public static final String KEY_IMAGE = "imageRes";
+ public static final String KEY_TEXT = "textRes";
+ public static final String KEY_SUBTEXT = "subtextRes";
+ public static final String KEY_CTATEXT = "ctatextRes";
+
+ public static List<TorBootstrapPanelConfig> getDefaultConnectPanel() {
+ final List<TorBootstrapPanelConfig> panels = new LinkedList<>();
+ panels.add(SimplePanelConfigs.connectPanelConfig);
+
+ return panels;
+ }
+
+ public static List<TorBootstrapPanelConfig> getDefaultBootstrapPanel() {
+ final List<TorBootstrapPanelConfig> panels = new LinkedList<>();
+ panels.add(SimplePanelConfigs.bootstrapPanelConfig);
+ panels.add(SimplePanelConfigs.torLogPanelConfig);
+
+ return panels;
+ }
+
+ public static class TorBootstrapPanelConfig {
+
+ private String classname;
+ private int titleRes;
+ private Bundle args;
+
+ public TorBootstrapPanelConfig(String resource, int titleRes) {
+ this(resource, titleRes, -1, -1, -1, true);
+ }
+
+ public TorBootstrapPanelConfig(String classname, int titleRes, int imageRes, int textRes, int subtextRes) {
+ this(classname, titleRes, imageRes, textRes, subtextRes, false);
+ }
+
+ private TorBootstrapPanelConfig(String classname, int titleRes, int imageRes, int textRes, int subtextRes, boolean isCustom) {
+ this.classname = classname;
+ this.titleRes = titleRes;
+
+ if (!isCustom) {
+ this.args = new Bundle();
+ this.args.putInt(KEY_IMAGE, imageRes);
+ this.args.putInt(KEY_TEXT, textRes);
+ this.args.putInt(KEY_SUBTEXT, subtextRes);
+ }
+ }
+
+ public String getClassname() {
+ return this.classname;
+ }
+
+ public int getTitleRes() {
+ return this.titleRes;
+ }
+
+ public Bundle getArgs() {
+ return args;
+ }
+ }
+
+ private static class SimplePanelConfigs {
+ public static final TorBootstrapPanelConfig connectPanelConfig = new TorBootstrapPanelConfig(TorBootstrapPanel.class.getName(), R.string.firstrun_panel_title_welcome);
+ public static final TorBootstrapPanelConfig bootstrapPanelConfig = new TorBootstrapPanelConfig(TorBootstrapPanel.class.getName(), R.string.firstrun_panel_title_welcome);
+ public static final TorBootstrapPanelConfig torLogPanelConfig = new TorBootstrapPanelConfig(TorBootstrapLogPanel.class.getName(), R.string.firstrun_panel_title_privacy);
+
+ }
+}
diff --git a/mobile/android/base/java/org/mozilla/gecko/torbootstrap/TorBootstrapPanel.java b/mobile/android/base/java/org/mozilla/gecko/torbootstrap/TorBootstrapPanel.java
new file mode 100644
index 000000000000..584c0fc3cdde
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/torbootstrap/TorBootstrapPanel.java
@@ -0,0 +1,428 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.torbootstrap;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.drawable.Animatable2;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.content.LocalBroadcastManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.util.Log;
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
+import org.mozilla.gecko.firstrun.FirstrunPanel;
+
+import org.torproject.android.service.OrbotConstants;
+import org.torproject.android.service.TorService;
+import org.torproject.android.service.TorServiceConstants;
+import org.torproject.android.service.util.TorServiceUtils;
+
+
+/**
+ * Tor Bootstrap panel (fragment/screen)
+ *
+ * This is based on the Firstrun Panel for simplicity.
+ */
+public class TorBootstrapPanel extends FirstrunPanel implements TorBootstrapLogger {
+
+ protected static final String LOGTAG = "TorBootstrap";
+
+ protected ViewGroup mRoot;
+ protected Activity mActContext;
+ protected TorBootstrapPager.TorBootstrapController mBootstrapController;
+
+ // These are used by the background AlphaChanging thread for dynamically changing
+ // the alpha value of the Onion during bootstrap.
+ private int mOnionCurrentAlpha = 255;
+ // This is either +1 or -1, depending on the direction of the change.
+ private int mOnionCurrentAlphaDirection = -1;
+ private Object mOnionAlphaChangerLock = new Object();
+ private boolean mOnionAlphaChangerRunning = false;
+
+ // Runnable for changing the alpha of the Onion image every 100 milliseconds.
+ // It gradually increases and then decreases the alpha in the background and
+ // then applies the new alpha on the UI thread.
+ private Thread mChangeOnionAlphaThread = null;
+ final private class ChangeOnionAlphaRunnable implements Runnable {
+ @Override
+ public void run() {
+ while (true) {
+ synchronized(mOnionAlphaChangerLock) {
+ if (!mOnionAlphaChangerRunning) {
+ // Null the reference for this thread when we exit
+ mChangeOnionAlphaThread = null;
+ return;
+ }
+ }
+
+ // Choose the new value here, mOnionCurrentAlpha is set in setOnionAlphaValue()
+ // Increase by 5 if mOnionCurrentAlphaDirection is positive, and decrease by
+ // 5 if mOnionCurrentAlphaDirection is negative.
+ final int newAlpha = mOnionCurrentAlpha + mOnionCurrentAlphaDirection*5;
+ getActivity().runOnUiThread(new Runnable() {
+ public void run() {
+ setOnionAlphaValue(newAlpha);
+ }
+ });
+
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {}
+ }
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) {
+ mRoot = (ViewGroup) inflater.inflate(R.layout.tor_bootstrap, container, false);
+ if (mRoot == null) {
+ Log.w(LOGTAG, "Inflating R.layout.tor_bootstrap returned null");
+ return null;
+ }
+
+ Button connectButton = mRoot.findViewById(R.id.tor_bootstrap_connect);
+ if (connectButton == null) {
+ Log.w(LOGTAG, "Finding the Connect button failed. Did the ID change?");
+ return null;
+ }
+
+ connectButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ startBootstrapping();
+ }
+ });
+
+ // This should be declared in the xml layout, however there is a bug
+ // preventing this (the XML attribute isn't actually defined in the
+ // SDK).
+ // https://issuetracker.google.com/issues/37036728
+ connectButton.setClipToOutline(true);
+
+ configureGearCogClickHandler();
+
+ TorLogEventListener.addLogger(this);
+
+ return mRoot;
+ }
+
+ private void setOnionAlphaValue(int newAlpha) {
+ ImageView onionImg = (ImageView) mRoot.findViewById(R.id.tor_bootstrap_onion);
+ if (onionImg == null) {
+ return;
+ }
+
+ if (newAlpha > 255) {
+ // Cap this at 255 and change direction of animation
+ newAlpha = 255;
+
+ synchronized(mOnionAlphaChangerLock) {
+ mOnionCurrentAlphaDirection = -1;
+ }
+ } else if (newAlpha < 0) {
+ // Lower-bound this at 0 and change direction of animation
+ newAlpha = 0;
+
+ synchronized(mOnionAlphaChangerLock) {
+ mOnionCurrentAlphaDirection = 1;
+ }
+ }
+ onionImg.setImageAlpha(newAlpha);
+ mOnionCurrentAlpha = newAlpha;
+ }
+
+ public void updateStatus(String torServiceMsg, String newTorStatus) {
+ final String noticePrefix = "NOTICE: ";
+
+ if (torServiceMsg == null) {
+ return;
+ }
+
+ TextView torLog = (TextView) mRoot.findViewById(R.id.tor_bootstrap_last_status_message);
+ if (torLog == null) {
+ Log.w(LOGTAG, "updateStatus: torLog is null?");
+ }
+ // Only show Notice-level log messages on this panel
+ if (torServiceMsg.startsWith(noticePrefix)) {
+ // Drop the prefix
+ String msg = torServiceMsg.substring(noticePrefix.length());
+ torLog.setText(msg);
+ } else if (torServiceMsg.toLowerCase().contains("error")) {
+ torLog.setText(R.string.tor_notify_user_about_error);
+
+ // This may be a false-positive, but if we encountered an error within
+ // the OrbotService then there's likely nothing the user can do. This
+ // isn't persistent, so if they restart the app the button will be
+ // visible again.
+ Button connectButton = mRoot.findViewById(R.id.tor_bootstrap_connect);
+ if (connectButton == null) {
+ Log.w(LOGTAG, "updateStatus: Finding the Connect button failed. Did the ID change?");
+ } else {
+ TextView swipeLeftLog = (TextView) mRoot.findViewById(R.id.tor_bootstrap_swipe_log);
+ if (swipeLeftLog == null) {
+ Log.w(LOGTAG, "updateStatus: swipeLeftLog is null?");
+ }
+
+ // Abuse this by showing the log message despite not bootstrapping
+ toggleVisibleElements(true, torLog, connectButton, swipeLeftLog);
+ }
+ }
+
+ // Return to the browser when we reach 100% bootstrapped
+ if (torServiceMsg.contains(TorServiceConstants.TOR_CONTROL_PORT_MSG_BOOTSTRAP_DONE)) {
+ // Inform the background AlphaChanging thread it should terminate
+ synchronized(mOnionAlphaChangerLock) {
+ mOnionAlphaChangerRunning = false;
+ }
+ close();
+ }
+ }
+
+ public void setContext(Activity ctx) {
+ mActContext = ctx;
+ }
+
+ // Save the TorBootstrapController.
+ // This method won't be used by the main TorBootstrapPanel (|this|), but
+ // it will be used by its childen.
+ public void setBootstrapController(TorBootstrapPager.TorBootstrapController bootstrapController) {
+ mBootstrapController = bootstrapController;
+ }
+
+ private void startTorService() {
+ Intent torService = new Intent(getActivity(), TorService.class);
+ torService.setAction(TorServiceConstants.ACTION_START);
+ getActivity().startService(torService);
+ }
+
+ private void stopTorService() {
+ // First, stop the current bootstrapping process (if it's in progress)
+ // TODO Ideally, we'd DisableNetwork here, but that's not available.
+ Intent torService = new Intent(getActivity(), TorService.class);
+ getActivity().stopService(torService);
+ }
+
+ // Setup OnClick handler for the settings gear/cog
+ protected void configureGearCogClickHandler() {
+ if (mRoot == null) {
+ Log.w(LOGTAG, "configureGearCogClickHandler: mRoot is null?");
+ return;
+ }
+
+ final ImageView gearSettingsImage = mRoot.findViewById(R.id.tor_bootstrap_settings_gear);
+ if (gearSettingsImage == null) {
+ Log.w(LOGTAG, "configureGearCogClickHandler: gearSettingsImage is null?");
+ return;
+ }
+
+ gearSettingsImage.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // The existance of the connect button is an indicator of the user
+ // interacting with the main bootstrapping screen or the loggin screen.
+ Button connectButton = mRoot.findViewById(R.id.tor_bootstrap_connect);
+ if (connectButton == null) {
+ Log.w(LOGTAG, "gearSettingsImage onClick: Finding the Connect button failed, proxying request.");
+
+ // If there isn't a connect button on this screen, then proxy the
+ // stopBootstrapping() request via the TorBootstrapController (which
+ // is the underlying PagerAdapter).
+ mBootstrapController.stopBootstrapping();
+ } else {
+ stopBootstrapping();
+ }
+ // Open Tor Network Settings preferences screen
+ Intent intent = new Intent(mActContext, TorPreferences.class);
+ mActContext.startActivity(intent);
+ }
+ });
+ }
+
+ private void toggleVisibleElements(boolean bootstrapping, TextView lastStatus, Button connect, TextView swipeLeft) {
+ final int connectVisible = bootstrapping ? View.INVISIBLE : View.VISIBLE;
+ final int infoTextVisible = bootstrapping ? View.VISIBLE : View.INVISIBLE;
+
+ if (connect != null) {
+ connect.setVisibility(connectVisible);
+ }
+ if (lastStatus != null) {
+ lastStatus.setVisibility(infoTextVisible);
+ }
+ if (swipeLeft != null) {
+ swipeLeft.setVisibility(infoTextVisible);
+ }
+ }
+
+ private void startBackgroundAlphaChangingThread() {
+ // If it is non-null, then this is a bug because the thread should null this reference when
+ // it terminates.
+ if (mChangeOnionAlphaThread != null) {
+ if (mChangeOnionAlphaThread.getState() == Thread.State.TERMINATED) {
+ // The thread likely terminated unexpectedly, null the reference.
+ // The thread should set this itself.
+ Log.i(LOGTAG, "mChangeOnionAlphaThread.getState(): is terminated");
+ mChangeOnionAlphaThread = null;
+ } else {
+ // Don't null the reference in this case because then we'll start another
+ // background thread. We are currently in an unknown state, simply set
+ // the Running flag as false.
+ Log.w(LOGTAG, "We're in an unexpected state. mChangeOnionAlphaThread.getState(): " + mChangeOnionAlphaThread.getState());
+
+ synchronized(mOnionAlphaChangerLock) {
+ mOnionAlphaChangerRunning = false;
+ }
+ }
+ }
+
+ // If the background thread is not currently running, then start it.
+ if (mChangeOnionAlphaThread == null) {
+ mChangeOnionAlphaThread = new Thread(new ChangeOnionAlphaRunnable());
+ if (mChangeOnionAlphaThread == null) {
+ Log.w(LOGTAG, "Instantiating a new ChangeOnionAlphaRunnable Thread failed.");
+ } else if (mChangeOnionAlphaThread.getState() == Thread.State.NEW) {
+ Log.i(LOGTAG, "Starting mChangeOnionAlphaThread");
+
+ // Synchronization across threads should not be necessary because there
+ // shouldn't be any other threads relying on mOnionAlphaChangerRunning.
+ // We do this purely for safety.
+ synchronized(mOnionAlphaChangerLock) {
+ mOnionAlphaChangerRunning = true;
+ }
+
+ mChangeOnionAlphaThread.start();
+ }
+ }
+ }
+
+ public void startBootstrapping() {
+ if (mRoot == null) {
+ Log.w(LOGTAG, "startBootstrapping: mRoot is null?");
+ return;
+ }
+ // We're starting bootstrap, transition into the bootstrapping-tor-panel
+ Button connectButton = mRoot.findViewById(R.id.tor_bootstrap_connect);
+ if (connectButton == null) {
+ Log.w(LOGTAG, "startBootstrapping: connectButton is null?");
+ return;
+ }
+
+ ImageView onionImg = (ImageView) mRoot.findViewById(R.id.tor_bootstrap_onion);
+
+ // Replace the current non-animated image with the animation
+ onionImg.setImageResource(R.drawable.tor_spinning_onion);
+
+ Drawable drawableOnion = onionImg.getDrawable();
+ if (Build.VERSION.SDK_INT >= 23 && drawableOnion instanceof Animatable2) {
+ Animatable2 spinningOnion = (Animatable2) drawableOnion;
+ // Begin spinning
+ spinningOnion.start();
+ } else {
+ Log.i(LOGTAG, "Animatable2 is not supported (or bad inheritance), version: " + Build.VERSION.SDK_INT);
+ }
+
+ mOnionCurrentAlpha = 255;
+ // The onion should have 100% alpha, begin decreasing it.
+ mOnionCurrentAlphaDirection = -1;
+ startBackgroundAlphaChangingThread();
+
+ TextView torStatus = (TextView) mRoot.findViewById(R.id.tor_bootstrap_last_status_message);
+ if (torStatus == null) {
+ Log.w(LOGTAG, "startBootstrapping: torStatus is null?");
+ return;
+ }
+
+ TextView swipeLeftLog = (TextView) mRoot.findViewById(R.id.tor_bootstrap_swipe_log);
+ if (swipeLeftLog == null) {
+ Log.w(LOGTAG, "startBootstrapping: swipeLeftLog is null?");
+ return;
+ }
+
+ torStatus.setText(getString(R.string.tor_bootstrap_starting_status));
+
+ toggleVisibleElements(true, torStatus, connectButton, swipeLeftLog);
+ startTorService();
+ }
+
+ // This is public because this Pager may call this method if another Panel requests it.
+ public void stopBootstrapping() {
+ if (mRoot == null) {
+ Log.w(LOGTAG, "stopBootstrapping: mRoot is null?");
+ return;
+ }
+ // Transition from the animated bootstrapping panel to
+ // the static "Connect" panel
+ Button connectButton = mRoot.findViewById(R.id.tor_bootstrap_connect);
+ if (connectButton == null) {
+ Log.w(LOGTAG, "stopBootstrapping: connectButton is null?");
+ return;
+ }
+
+ ImageView onionImg = (ImageView) mRoot.findViewById(R.id.tor_bootstrap_onion);
+ if (onionImg == null) {
+ Log.w(LOGTAG, "stopBootstrapping: onionImg is null?");
+ return;
+ }
+
+ // Inform the background AlphaChanging thread it should terminate.
+ synchronized(mOnionAlphaChangerLock) {
+ mOnionAlphaChangerRunning = false;
+ }
+
+ Drawable drawableOnion = onionImg.getDrawable();
+
+ // If the connect button wasn't pressed previously, then this object is
+ // not an animation (it is most likely a BitmapDrawable). Only manipulate
+ // it when it is an Animatable2.
+ if (Build.VERSION.SDK_INT >= 23 && drawableOnion instanceof Animatable2) {
+ Animatable2 spinningOnion = (Animatable2) drawableOnion;
+ // spinningOnion is null if we didn't previously call startBootstrapping.
+ // If we reach here and spinningOnion is null, then there is likely a bug
+ // because stopBootstrapping() is called only when the user selects the
+ // gear button and we should only reach this block if the user pressed the
+ // connect button (thus creating and enabling the animation) and then
+ // pressing the gear button. Therefore, if the drawableOnion is an
+ // Animatable2, then spinningOnion should be non-null.
+ if (spinningOnion != null) {
+ spinningOnion.stop();
+
+ onionImg.setImageResource(R.drawable.tor_spinning_onion);
+ }
+ } else {
+ Log.i(LOGTAG, "Animatable2 is not supported (or bad inheritance), version: " + Build.VERSION.SDK_INT);
+ }
+
+ // Reset the onion's alpha value.
+ onionImg.setImageAlpha(255);
+
+ TextView torStatus = (TextView) mRoot.findViewById(R.id.tor_bootstrap_last_status_message);
+ if (torStatus == null) {
+ Log.w(LOGTAG, "stopBootstrapping: torStatus is null?");
+ return;
+ }
+
+ TextView swipeLeftLog = (TextView) mRoot.findViewById(R.id.tor_bootstrap_swipe_log);
+ if (swipeLeftLog == null) {
+ Log.w(LOGTAG, "stopBootstrapping: swipeLeftLog is null?");
+ return;
+ }
+
+ // Reset the displayed message
+ torStatus.setText("");
+
+ toggleVisibleElements(false, torStatus, connectButton, swipeLeftLog);
+ stopTorService();
+ }
+}
diff --git a/mobile/android/base/java/org/mozilla/gecko/torbootstrap/TorLogEventListener.java b/mobile/android/base/java/org/mozilla/gecko/torbootstrap/TorLogEventListener.java
new file mode 100644
index 000000000000..6218763475e5
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/torbootstrap/TorLogEventListener.java
@@ -0,0 +1,128 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.torbootstrap;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.Message;
+import android.support.v4.content.LocalBroadcastManager;
+
+import org.torproject.android.service.OrbotConstants;
+import org.torproject.android.service.TorService;
+import org.torproject.android.service.TorServiceConstants;
+import org.torproject.android.service.util.TorServiceUtils;
+
+import java.util.Vector;
+
+
+/**
+ * This is simply a container for capturing the log events and proxying them
+ * to the TorBootstrapLogger implementers (TorBootstrapPanel and TorBootstrapLogPanel now).
+ *
+ * This should be in BrowserApp, but that class/Activity is already too large,
+ * so this should be easier to reason about.
+ */
+public class TorLogEventListener {
+
+ private static Vector<TorBootstrapLogger> mLoggers;
+
+ private TorLogEventListener instance;
+ private static boolean isInitialized = false;
+
+ public TorLogEventListener getInstance(Context context) {
+ if (instance == null) {
+ instance = new TorLogEventListener();
+ }
+ return instance;
+ }
+
+ private synchronized static void initialize(Context context) {
+ LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
+ lbm.registerReceiver(mLocalBroadcastReceiver,
+ new IntentFilter(TorServiceConstants.ACTION_STATUS));
+ lbm.registerReceiver(mLocalBroadcastReceiver,
+ new IntentFilter(TorServiceConstants.LOCAL_ACTION_LOG));
+
+ isInitialized = true;
+ // There should be at least two Loggers: TorBootstrapPanel
+ // and TorBootstrapLogPanel
+ mLoggers = new Vector<TorBootstrapLogger>(2);
+ }
+
+ public synchronized static void addLogger(TorBootstrapLogger logger) {
+ if (!isInitialized) {
+ // This is an assumption we're making. All Loggers are a subclass
+ // of an Activity.
+ Activity activity = logger.getActivity();
+ initialize(activity);
+ }
+
+ if (mLoggers.contains(logger)) {
+ return;
+ }
+ mLoggers.add(logger);
+ }
+
+ public synchronized static void deleteLogger(TorBootstrapLogger logger) {
+ mLoggers.remove(logger);
+ }
+
+ /**
+ * The state and log info from {@link TorService} are sent to the UI here in
+ * the form of a local broadcast. Regular broadcasts can be sent by any app,
+ * so local ones are used here so other apps cannot interfere with Orbot's
+ * operation.
+ *
+ * Copied from Orbot - OrbotMainActivity.java
+ */
+ private static BroadcastReceiver mLocalBroadcastReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
+
+ // This is only defined for log updates
+ if (!action.equals(TorServiceConstants.LOCAL_ACTION_LOG) &&
+ !action.equals(TorServiceConstants.ACTION_STATUS)) {
+ return;
+ }
+
+ Message msg = mStatusUpdateHandler.obtainMessage();
+
+ if (action.equals(TorServiceConstants.LOCAL_ACTION_LOG)) {
+ msg.obj = intent.getStringExtra(TorServiceConstants.LOCAL_EXTRA_LOG);
+ }
+
+ msg.getData().putString("status",
+ intent.getStringExtra(TorServiceConstants.EXTRA_STATUS));
+ mStatusUpdateHandler.sendMessage(msg);
+ }
+ };
+
+
+ // this is what takes messages or values from the callback threads or other non-mainUI threads
+ // and passes them back into the main UI thread for display to the user
+ private static Handler mStatusUpdateHandler = new Handler() {
+
+ @Override
+ public void handleMessage(final Message msg) {
+ String newTorStatus = msg.getData().getString("status");
+ String log = (String)msg.obj;
+
+ for (TorBootstrapLogger l : mLoggers) {
+ l.updateStatus(log, newTorStatus);
+ }
+ super.handleMessage(msg);
+ }
+ };
+}
diff --git a/mobile/android/base/java/org/mozilla/gecko/torbootstrap/TorPreferences.java b/mobile/android/base/java/org/mozilla/gecko/torbootstrap/TorPreferences.java
new file mode 100644
index 000000000000..32a3bed3e685
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/torbootstrap/TorPreferences.java
@@ -0,0 +1,950 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.torbootstrap;
+
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.graphics.Typeface;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceFragment;
+import android.preference.PreferenceScreen;
+import android.preference.SwitchPreference;
+import android.text.style.ClickableSpan;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.method.LinkMovementMethod;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.view.ViewParent;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.AdapterView;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.Switch;
+import android.widget.TextView;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Xml;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Vector;
+
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.preferences.AppCompatPreferenceActivity;
+
+import org.torproject.android.service.util.Prefs;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import static org.mozilla.gecko.preferences.GeckoPreferences.NON_PREF_PREFIX;
+
+
+/** TorPreferences provides the Tor-related preferences
+ *
+ * We configure bridges using either a set of built-in bridges where the user enables
+ * them based on bridge type (the name of the pluggable transport) or the user provides
+ * their own bridge (obtained from another person or BridgeDB, etc).
+ *
+ * This class (TorPreferences) is divided into multiple Fragments (screens). The first
+ * screen is where the user enables or disables Bridges. The second screen shows the
+ * user a list of built-in bridge types (obfs4, meek, etc) where they may select one of
+ * them. It shows a button they may press for providing their own bridge, as well. The
+ * third screen is where the user may provide (copy/paste) their own bridge.
+ *
+ * On the first screen, if bridges are currently enabled, then the switch/toggle is
+ * shown as enabled. In addition, the user is shown a message saying whether built-in or
+ * provided bridges are being used. There is a link, labeled "Change", where they
+ * transitioned to the appropriate screen for modifying the configuration if it is pressed.
+ *
+ * The second screen shows radio buttons for the built-in bridge types.
+ *
+ * The State of Bridges-Enabled:
+ * There are a few moving parts here, a higher-level description of how we expect this
+ * works, where "Enabled" is "Bridges Enabled", "Type" is "Bridge Type", and "Provided"
+ * is "Bridge Provided":
+ *
+ * We have five preferences:
+ * PREFS_BRIDGES_ENABLED
+ * PREFS_BRIDGES_TYPE
+ * PREFS_BRIDGES_PROVIDE
+ * pref_bridges_enabled (Orbot)
+ * pref_bridges_list (Orbot)
+ *
+ * These may be in following three end states where PREFS_BRIDGES_ENABLED and
+ * pref_bridges_enabled must always match, and pref_bridges_list must either match
+ * PREFS_BRIDGES_PROVIDE or contain a list of bridges of type PREFS_BRIDGES_TYPE.
+ *
+ * PREFS_BRIDGES_ENABLED=false
+ * PREFS_BRIDGES_TYPE=null
+ * PREFS_BRIDGES_PROVIDE=null
+ * pref_bridges_enabled=false
+ * pref_bridges_list=null
+ *
+ * PREFS_BRIDGES_ENABLED=true
+ * PREFS_BRIDGES_TYPE=T1
+ * PREFS_BRIDGES_PROVIDE=null
+ * pref_bridges_enabled=true
+ * pref_bridges_list=X1
+ *
+ * PREFS_BRIDGES_ENABLED=true
+ * PREFS_BRIDGES_TYPE=null
+ * PREFS_BRIDGES_PROVIDE=X2
+ * pref_bridges_enabled=true
+ * pref_bridges_list=X2
+ *
+ * There are transition states where this is not consistent, for example when the
+ * "Bridges Enabled" switch is toggled but "Bridge Type" and "Bridge Provided" are null.
+ */
+
+public class TorPreferences extends AppCompatPreferenceActivity {
+ private static final String LOGTAG = "TorPreferences";
+
+ private static final String PREFS_BRIDGES_ENABLED = NON_PREF_PREFIX + "tor.bridges.enabled";
+ private static final String PREFS_BRIDGES_TYPE = NON_PREF_PREFIX + "tor.bridges.type";
+ private static final String PREFS_BRIDGES_PROVIDE = NON_PREF_PREFIX + "tor.bridges.provide";
+
+ private static final String sClassName = TorPreferences.class.getName();
+ private static final String sTorNetworkBridgesEnabledPreferenceName = sClassName + "$TorNetworkBridgesEnabledPreference";
+ private static final String sTorNetworkBridgeSelectPreferenceName = sClassName + "$TorNetworkBridgeSelectPreference";
+ private static final String sTorNetworkBridgeProvidePreferenceName = sClassName + "$TorNetworkBridgeProvidePreference";
+ private static final String[] sTorPreferenceFragments = {sTorNetworkBridgesEnabledPreferenceName,
+ sTorNetworkBridgeSelectPreferenceName,
+ sTorNetworkBridgeProvidePreferenceName};
+ // Current displayed PreferenceFragment
+ private TorNetworkPreferenceFragment mFrag;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ // Begin with the first (Enable Bridges) fragment
+ getIntent().putExtra(EXTRA_SHOW_FRAGMENT, sTorNetworkBridgesEnabledPreferenceName);
+ super.onCreate(savedInstanceState);
+
+ mFrag = null;
+ }
+
+ // Save the current preference when the app is minimized or swiped away.
+ @Override
+ public void onStop() {
+ mFrag.onSaveState();
+ super.onStop();
+ }
+
+ // This is needed because launching a fragment fails if this
+ // method doesn't return true.
+ @Override
+ protected boolean isValidFragment(String fragmentName) {
+ for (String frag : sTorPreferenceFragments) {
+ if (fragmentName.equals(frag)) {
+ return true;
+ }
+ }
+ Log.i(LOGTAG, "isValidFragment(): Returning false (" + fragmentName + ")");
+ return false;
+ }
+
+ public void setFragment(TorNetworkPreferenceFragment frag) {
+ mFrag = frag;
+ }
+
+ // Save the preference when the user returns to the previous screen using
+ // the back button
+ @Override
+ public void onBackPressed() {
+ mFrag.onSaveState();
+ super.onBackPressed();
+ }
+
+ // Control the behavior when the Up button (back button in top-left
+ // corner) is pressed. Save the current preference and return to the
+ // previous screen.
+ @Override
+ public boolean onNavigateUp() {
+ super.onNavigateUp();
+
+ if (mFrag == null) {
+ Log.w(LOGTAG, "onNavigateUp(): mFrag is null");
+ return false;
+ }
+
+ // Handle the user pressing the Up button in the same way as
+ // we handle them pressing the Back button. Strictly, this
+ // isn't correct, but it will prevent confusion.
+ mFrag.onSaveState();
+
+ if (mFrag.getFragmentManager().getBackStackEntryCount() > 0) {
+ Log.i(LOGTAG, "onNavigateUp(): popping from backstatck");
+ mFrag.getFragmentManager().popBackStack();
+ } else {
+ Log.i(LOGTAG, "onNavigateUp(): finishing activity");
+ finish();
+ }
+ return true;
+ }
+
+ // Overriding this method is necessary because before Oreo the PreferenceActivity didn't
+ // correctly handle the Home button (Up button). This was implemented in Oreo (Android 8+,
+ // API 26+).
+ // https://android.googlesource.com/platform/frameworks/base/+/6af15ebcfec64d0…
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ Log.i(LOGTAG, "onOptionsItemSelected(): Home");
+ onNavigateUp();
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ // Helper abstract Fragment with common methods
+ public static abstract class TorNetworkPreferenceFragment extends PreferenceFragment {
+ protected TorPreferences mTorPrefAct;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // This is only ever a TorPreferences
+ mTorPrefAct = (TorPreferences) getActivity();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mTorPrefAct.setFragment(this);
+ }
+
+ // Implement this callback in child Fragments
+ public void onSaveState() {
+ }
+
+ // Helper method for walking a View hierarchy and printing the children
+ protected void walkViewTree(View view, int depth) {
+ if (view instanceof ViewGroup) {
+ ViewGroup vg = (ViewGroup) view;
+ int childIdx = 0;
+ for (; childIdx < vg.getChildCount(); childIdx++) {
+ walkViewTree(vg.getChildAt(childIdx), depth + 1);
+ }
+ }
+ Log.i(LOGTAG, "walkViewTree: " + depth + ": view: " + view);
+ Log.i(LOGTAG, "walkViewTree: " + depth + ": view id: " + view.getId());
+ Log.i(LOGTAG, "walkViewTree: " + depth + ": view is focused: " + view.isFocused());
+ Log.i(LOGTAG, "walkViewTree: " + depth + ": view is enabled: " + view.isEnabled());
+ Log.i(LOGTAG, "walkViewTree: " + depth + ": view is selected: " + view.isSelected());
+ Log.i(LOGTAG, "walkViewTree: " + depth + ": view is in touch mode: " + view.isInTouchMode());
+ Log.i(LOGTAG, "walkViewTree: " + depth + ": view is activated: " + view.isActivated());
+ Log.i(LOGTAG, "walkViewTree: " + depth + ": view is clickable: " + view.isClickable());
+ Log.i(LOGTAG, "walkViewTree: " + depth + ": view is focusable: " + view.isFocusable());
+ Log.i(LOGTAG, "walkViewTree: " + depth + ": view is FocusableInTouchMode: " + view.isFocusableInTouchMode());
+ }
+
+ // Helper returning the ListView
+ protected ListView getListView(View view) {
+ if (!(view instanceof ViewGroup) || view == null) {
+ return null;
+ }
+
+ View rawListView = view.findViewById(android.R.id.list);
+ if (!(rawListView instanceof ListView) || rawListView == null) {
+ return null;
+ }
+
+ return (ListView) rawListView;
+ }
+
+ // Get Bridges associated with the provided pref key saved in the
+ // provided SharedPreferences. Return null if the SharedPreferences
+ // is null or if there isn't any value associated with the pref.
+ protected String getBridges(SharedPreferences sharedPrefs, String pref) {
+ if (sharedPrefs == null) {
+ Log.w(LOGTAG, "getBridges: sharedPrefs is null");
+ return null;
+ }
+ return sharedPrefs.getString(pref, null);
+ }
+
+ // Save the bridge type and bridge line preferences.
+ //
+ // Save the bridgesType with the PREFS_BRIDGES_TYPE pref as the key
+ // (for future lookup). If bridgesType is null, then save the
+ // bridgesLines with the PREFS_BRIDGES_PROVIDE pref as the key, and
+ // use Orbot's helper method and enable Orbot's bridge pref.
+ protected boolean setBridges(SharedPreferences.Editor editor, String bridgesType, String bridgesLines) {
+ if (editor == null) {
+ Log.w(LOGTAG, "setBridges: editor is null");
+ return false;
+ }
+ Log.i(LOGTAG, "Saving bridge type preference: " + bridgesType);
+ Log.i(LOGTAG, "Saving bridge line preference: " + bridgesLines);
+
+ // If bridgesType is null, then clear the pref and save the bridgesLines
+ // as a provided bridge. If bridgesType is not null, then save the type
+ // but don't save it as a provided bridge.
+ editor.putString(PREFS_BRIDGES_TYPE, bridgesType);
+ if (bridgesType == null) {
+ editor.putString(PREFS_BRIDGES_PROVIDE, bridgesLines);
+ } else {
+ editor.putString(PREFS_BRIDGES_PROVIDE, null);
+ }
+
+ if (!editor.commit()) {
+ return false;
+ }
+
+ // Set Orbot's preference
+ Prefs.setBridgesList(bridgesLines);
+
+ // If either of these are not null, then we're enabling bridges
+ boolean bridgesAreEnabled = (bridgesType != null) || (bridgesLines != null);
+ // Inform Orbot bridges are enabled
+ Prefs.putBridgesEnabled(bridgesAreEnabled);
+ return true;
+ }
+
+ // Disable the bridges.enabled Preference
+ protected void disableBridges(PreferenceFragment frag) {
+ SwitchPreference bridgesEnabled = (SwitchPreference) frag.findPreference(PREFS_BRIDGES_ENABLED);
+ Preference bridgesType = frag.findPreference(PREFS_BRIDGES_TYPE);
+ Preference bridgesProvide = frag.findPreference(PREFS_BRIDGES_PROVIDE);
+ Preference pref = null;
+
+ if (bridgesEnabled != null) {
+ Log.i(LOGTAG, "disableBridges: bridgesEnabled is not null");
+ pref = bridgesEnabled;
+ } else if (bridgesType != null) {
+ Log.i(LOGTAG, "disableBridges: bridgesType is not null");
+ pref = bridgesType;
+ } else if (bridgesProvide != null) {
+ Log.i(LOGTAG, "disableBridges: bridgesProvide is not null");
+ pref = bridgesProvide;
+ } else {
+ Log.w(LOGTAG, "disableBridges: all the expected preferences are is null?");
+ return;
+ }
+
+ // Clear the saved prefs (it's okay we're using a different
+ // SharedPreference.Editor here, they modify the same backend).
+ // In addition, passing null is equivalent to clearing the
+ // preference.
+ setBridges(pref.getEditor(), null, null);
+
+ if (bridgesEnabled != null) {
+ bridgesEnabled.setChecked(false);
+ }
+ }
+
+ // Set the current title
+ protected void setTitle(int resId) {
+ mTorPrefAct.getSupportActionBar().setTitle(resId);
+ }
+ }
+
+ // Fragment implementing the screen for enabling Bridges
+ public static class TorNetworkBridgesEnabledPreference extends TorNetworkPreferenceFragment {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.preferences_tor_network_main);
+ }
+
+ // This class is instantiated within the OnClickListener of the
+ // PreferenceSwitch's Switch widget
+ public class BridgesEnabledSwitchOnClickListener implements View.OnClickListener {
+ @Override
+ public void onClick(View v) {
+ Log.i(LOGTAG, "bridgesEnabledSwitch clicked");
+ if (!(v instanceof Switch)) {
+ Log.w(LOGTAG, "View isn't an instance of Switch?");
+ return;
+ }
+
+ Switch bridgesEnabledSwitch = (Switch) v;
+
+ // The widget was pressed, now find the preference and set it
+ // such that it is synchronized with the widget.
+ final SwitchPreference bridgesEnabled = (SwitchPreference) TorNetworkBridgesEnabledPreference.this.findPreference(PREFS_BRIDGES_ENABLED);
+ if (bridgesEnabled == null) {
+ Log.w(LOGTAG, "onCreate: bridgesEnabled is null?");
+ return;
+ }
+
+ bridgesEnabled.setChecked(bridgesEnabledSwitch.isChecked());
+
+ // Only launch the Fragment if we're enabling bridges.
+ if (bridgesEnabledSwitch.isChecked()) {
+ TorNetworkBridgesEnabledPreference.this.mTorPrefAct.startPreferenceFragment(new TorNetworkBridgeSelectPreference(), true);
+ } else {
+ disableBridges(TorNetworkBridgesEnabledPreference.this);
+ }
+ }
+ }
+
+ // This method must be overridden because, when creating Preferences, the
+ // creation of the View hierarchy occurs asynchronously. Usually
+ // onCreateView() gives us the View hierarchy as it is defined in the XML layout.
+ // However, with Preferences the layout is created across multiple threads and it
+ // usually isn't available at the time onCreateView() or onViewCreated() are
+ // called. As a result, we find the ListView (which is almost guaranteed to exist
+ // at this time) and we add an OnHierarchyChangeListener where we wait until the
+ // children are added into the tree.
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ setTitle(R.string.pref_tor_network_title);
+
+ final SwitchPreference bridgesEnabled = (SwitchPreference) findPreference(PREFS_BRIDGES_ENABLED);
+ if (bridgesEnabled == null) {
+ Log.w(LOGTAG, "onCreate: bridgesEnabled is null?");
+ return;
+ }
+
+ // If we return from either of the "Select Bridge Type" screen
+ // or "Provide Bridge" screen without selecting or inputing
+ // any value, then we could arrive here without any bridge
+ // saved/enabled but this switch is enabled. Disable it.
+ if (!Prefs.bridgesEnabled()) {
+ bridgesEnabled.setChecked(false);
+ }
+
+ // Decide if the configured bridges were provided by the user or
+ // selected from the list of bridge types
+ if (isBridgeProvided(bridgesEnabled)) {
+ String newSummary = getString(R.string.pref_tor_network_bridges_enabled_change_custom);
+ setBridgesEnabledSummaryAndOnClickListener(bridgesEnabled, newSummary, true);
+ } else if (Prefs.bridgesEnabled()) {
+ // If isBridgeProvided() returned false, but Prefs.bridgesEnabled() returns true.
+ // This means we have bridges, but they weren't provided by the user - therefore
+ // they must be built-in bridges.
+ String newSummary = getString(R.string.pref_tor_network_bridges_enabled_change_builtin);
+ setBridgesEnabledSummaryAndOnClickListener(bridgesEnabled, newSummary, false);
+ }
+
+ ListView lv = getListView(view);
+ if (lv == null) {
+ Log.i(LOGTAG, "onViewCreated: ListView not found");
+ return;
+ }
+
+ lv.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
+
+ @Override
+ public void onChildViewAdded(View parent, View child) {
+ Log.i(LOGTAG, "onChildViewAdded: Adding ListView child view");
+
+ // Make sure the Switch widget is synchronized with the preference
+ final Switch bridgesEnabledSwitch =
+ (Switch) parent.findViewById(android.R.id.switch_widget);
+
+ if (bridgesEnabledSwitch != null) {
+ bridgesEnabledSwitch.setChecked(bridgesEnabled.isChecked());
+
+ // When the Switch is pressed by the user, either load the next
+ // fragment (where the user chooses a bridge type), or return to
+ // the main bootstrapping screen.
+ bridgesEnabledSwitch.setOnClickListener(new BridgesEnabledSwitchOnClickListener());
+ }
+
+ final TextView bridgesEnabledSummary =
+ (TextView) parent.findViewById(android.R.id.summary);
+ if (bridgesEnabledSummary == null) {
+ Log.w(LOGTAG, "Bridge Enabled Summary is null, we can't enable the span");
+ return;
+ }
+
+ // Make the ClickableSpan clickable within the TextView.
+ // This is a requirement for using a ClickableSpan in
+ // setBridgesEnabledSummaryAndOnClickListener().
+ bridgesEnabledSummary.setMovementMethod(LinkMovementMethod.getInstance());
+ }
+
+ @Override
+ public void onChildViewRemoved(View parent, View child) {
+ }
+ });
+ }
+
+ // This is a common OnClickListener for when the user clicks on the Change link.
+ // The span won't be clickable until the MovementMethod is set. This happens in
+ // onViewCreated within the OnHierarchyChangeListener we set on the ListView.
+ private void setBridgesEnabledSummaryAndOnClickListener(SwitchPreference bridgesEnabled, final String newSummary, final boolean custom) {
+ Log.i(LOGTAG, "Bridge Summary clicked");
+ if (bridgesEnabled == null) {
+ Log.w(LOGTAG, "Bridge Enabled switch is null");
+ return;
+ }
+
+ // Here we obtain the correct text, based on whether the bridges
+ // were provided (custom) or built-in. Using that text, we create
+ // a spannable string and find the substring "Change" within it.
+ // If it exists, we make that substring clickable.
+ // Note: TODO This breaks with localization.
+ if (newSummary == null) {
+ Log.w(LOGTAG, "R.string.pref_tor_network_bridges_enabled_change_builtin is null");
+ return;
+ }
+ int changeStart = newSummary.indexOf("Change");
+ if (changeStart == -1) {
+ Log.w(LOGTAG, "R.string.pref_tor_network_bridges_enabled_change_builtin doesn't contain 'Change'");
+ return;
+ }
+ SpannableString newSpannableSummary = new SpannableString(newSummary);
+ newSpannableSummary.setSpan(new ClickableSpan() {
+ @Override
+ public void onClick(View v) {
+ // If a custom (provided) bridge is configured, then
+ // open the BridgesProvide preference fragment. Else,
+ // open the built-in/bridge-type fragment.
+ Log.i(LOGTAG, "Span onClick!");
+
+ // Add this Fragment regardless of which Fragment we're showing next. If the Change
+ // link goes to the built-in bridges, then this is what we show the user. If the Change
+ // link goes to the provided bridges, then we consider this a deep-link and we inject the
+ // built-in bridges screen into the backstack so they are shown it when they press Back
+ // from the provided-bridges screen.
+ mTorPrefAct.startPreferenceFragment(new
+ TorNetworkBridgeSelectPreference(), true);
+
+ if (custom) {
+ mTorPrefAct.startPreferenceFragment(new
+ TorNetworkBridgeProvidePreference(), true);
+ }
+ }
+ },
+ // Begin the span
+ changeStart,
+ // End the span
+ newSummary.length(),
+ // Don't include new characters added into the spanned substring
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ bridgesEnabled.setSummaryOn(newSpannableSummary);
+ }
+
+ // We follow this logic:
+ // If the bridgesEnabled switch is off, then false
+ // If Orbot doesn't have bridges enabled, then false
+ // If PREFS_BRIDGES_PROVIDE is not null, then true
+ // Else false
+ private boolean isBridgeProvided(SwitchPreference bridgesEnabled) {
+ if (!bridgesEnabled.isChecked()) {
+ Log.i(LOGTAG, "isBridgeProvided: bridgesEnabled is not checked");
+ return false;
+ }
+
+ if (!Prefs.bridgesEnabled()) {
+ Log.i(LOGTAG, "isBridgeProvided: bridges are not enabled");
+ return false;
+ }
+ SharedPreferences sharedPrefs = bridgesEnabled.getSharedPreferences();
+ boolean hasBridgeProvide =
+ sharedPrefs.getString(PREFS_BRIDGES_PROVIDE, null) != null;
+
+ Log.i(LOGTAG, "isBridgeProvided: We have provided bridges: " + hasBridgeProvide);
+ return hasBridgeProvide;
+ }
+ }
+
+ // Fragment implementing the screen for selecting a built-in Bridge type
+ public static class TorNetworkBridgeSelectPreference extends TorNetworkPreferenceFragment {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.preferences_tor_network_select_bridge_type);
+ }
+
+ // Add OnClickListeners after the View is created
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ setTitle(R.string.pref_tor_select_a_bridge_title);
+
+ ListView lv = getListView(view);
+ if (lv == null) {
+ Log.i(LOGTAG, "onViewCreated: ListView not found");
+ return;
+ }
+
+ // Configure onClick handler for "Provide a Bridge" button
+ lv.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
+
+ @Override
+ public void onChildViewAdded(View parent, View child) {
+ // Set the previously chosen RadioButton as checked
+ final RadioGroup group = getBridgeTypeRadioGroup();
+ if (group == null) {
+ Log.w(LOGTAG, "Radio Group is null");
+ return;
+ }
+
+ final View titleAndSummaryView = parent.findViewById(R.id.title_and_summary);
+ if (titleAndSummaryView == null) {
+ Log.w(LOGTAG, "title and summary view is null");
+ group.setVisibility(View.VISIBLE);
+ return;
+ }
+
+ titleAndSummaryView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ group.setVisibility(View.VISIBLE);
+ }
+ });
+
+ final View provideABridge = parent.findViewById(R.id.tor_network_provide_a_bridge);
+ if (provideABridge == null) {
+ return;
+ }
+
+ provideABridge.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Log.i(LOGTAG, "bridgesProvide clicked");
+ saveCurrentCheckedRadioButton();
+
+ mTorPrefAct.startPreferenceFragment(new TorNetworkBridgeProvidePreference(), true);
+ }
+ });
+
+ final TextView provideABridgeSummary = (TextView) parent.findViewById(R.id.tor_network_provide_a_bridge_summary);
+ if (provideABridgeSummary == null) {
+ Log.i(LOGTAG, "provideABridgeSummary is null");
+ return;
+ }
+
+ Preference bridgesTypePref = findPreference(PREFS_BRIDGES_TYPE);
+ if (bridgesTypePref == null) {
+ return;
+ }
+
+ SharedPreferences sharedPrefs = bridgesTypePref.getSharedPreferences();
+ String provideBridges = sharedPrefs.getString(PREFS_BRIDGES_PROVIDE, null);
+ if (provideBridges != null) {
+ if (provideBridges.indexOf("\n") != -1) {
+ provideABridgeSummary.setText(R.string.pref_tor_network_using_multiple_provided_bridges);
+ } else {
+ String summary = getString(R.string.pref_tor_network_using_a_provided_bridge, provideBridges);
+ provideABridgeSummary.setText(summary);
+ }
+ }
+
+ final String configuredBridgeType = getBridges(bridgesTypePref.getSharedPreferences(), PREFS_BRIDGES_TYPE);
+ if (configuredBridgeType == null) {
+ return;
+ }
+
+ int buttonId = -1;
+ // Note: Keep these synchronized with the layout xml file.
+ switch (configuredBridgeType) {
+ case "obfs4":
+ buttonId = R.id.radio_pref_bridges_obfs4;
+ break;
+ case "meek":
+ buttonId = R.id.radio_pref_bridges_meek_azure;
+ break;
+ case "obfs3":
+ buttonId = R.id.radio_pref_bridges_obfs3;
+ break;
+ }
+
+ if (buttonId != -1) {
+ group.check(buttonId);
+ // If a bridge is selected, then make the list visible
+ group.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ public void onChildViewRemoved(View parent, View child) {
+ }
+ });
+
+ }
+
+ // Save the checked RadioButton in the SharedPreferences
+ private boolean saveCurrentCheckedRadioButton() {
+ ListView lv = getListView(getView());
+ if (lv == null) {
+ Log.w(LOGTAG, "ListView is null");
+ return false;
+ }
+
+ RadioGroup group = getBridgeTypeRadioGroup();
+ if (group == null) {
+ Log.w(LOGTAG, "RadioGroup is null");
+ return false;
+ }
+
+ int checkedId = group.getCheckedRadioButtonId();
+ RadioButton selectedBridgeType = lv.findViewById(checkedId);
+ if (selectedBridgeType == null) {
+ Log.w(LOGTAG, "RadioButton is null");
+ return false;
+ }
+
+ String bridgesType = selectedBridgeType.getText().toString();
+ if (bridgesType == null) {
+ // We don't know with which bridgesType this Id is associated
+ Log.w(LOGTAG, "RadioButton has null text");
+ return false;
+ }
+
+ // Currently obfs4 is the recommended pluggable transport. As a result,
+ // the text contains " (recommended)". This won't be expected elsewhere,
+ // so replace the string with only the pluggable transport name.
+ // This will need updating when another transport is "recommended".
+ //
+ // Similarly, if meek-azure is chosen, substitute it with "meek" (Orbot
+ // only handles these keywords specially if they are less than 5 characters).
+ if (bridgesType.contains("obfs4")) {
+ bridgesType = "obfs4";
+ } else if (bridgesType.contains("meek-azure")) {
+ bridgesType = "meek";
+ }
+
+ Preference bridgesTypePref = findPreference(PREFS_BRIDGES_TYPE);
+ if (bridgesTypePref == null) {
+ Log.w(LOGTAG, PREFS_BRIDGES_TYPE + " preference not found");
+ disableBridges(this);
+ return false;
+ }
+
+ if (!setBridges(bridgesTypePref.getEditor(), bridgesType, bridgesType)) {
+ Log.w(LOGTAG, "Saving Bridge preference failed.");
+ disableBridges(this);
+ return false;
+ }
+
+ return true;
+ }
+
+ // Handle onSaveState when the user presses Back. Save the selected
+ // built-in bridge type.
+ @Override
+ public void onSaveState() {
+ saveCurrentCheckedRadioButton();
+ }
+
+ // Find the RadioGroup within the View hierarchy now.
+ private RadioGroup getBridgeTypeRadioGroup() {
+ ListView lv = getListView(getView());
+ if (lv == null) {
+ Log.w(LOGTAG, "ListView is null");
+ return null;
+ }
+ ViewParent listViewParent = lv.getParent();
+ // If the parent of this ListView isn't a View, then
+ // the RadioGroup doesn't exist
+ if (!(listViewParent instanceof View)) {
+ Log.w(LOGTAG, "ListView's parent isn't a View. Failing");
+ return null;
+ }
+ View lvParent = (View) listViewParent;
+ // Find the RadioGroup with this View hierarchy.
+ return (RadioGroup) lvParent.findViewById(R.id.pref_radio_group_builtin_bridges_type);
+ }
+ }
+
+ // Fragment implementing the screen for providing a Bridge
+ public static class TorNetworkBridgeProvidePreference extends TorNetworkPreferenceFragment {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.preferences_tor_network_provide_bridge);
+ }
+
+ // If there is a provided bridge saved in the preference,
+ // then fill-in the text field with that value.
+ private void setBridgeProvideText(View parent) {
+ final View provideBridge1 = parent.findViewById(R.id.tor_network_provide_bridge1);
+ final View provideBridge2 = parent.findViewById(R.id.tor_network_provide_bridge2);
+ final View provideBridge3 = parent.findViewById(R.id.tor_network_provide_bridge3);
+
+ EditText provideBridge1ET = null;
+ EditText provideBridge2ET = null;
+ EditText provideBridge3ET = null;
+
+ if (provideBridge1 != null) {
+ if (provideBridge1 instanceof EditText) {
+ provideBridge1ET = (EditText) provideBridge1;
+ }
+ }
+
+ if (provideBridge2 != null) {
+ if (provideBridge2 instanceof EditText) {
+ provideBridge2ET = (EditText) provideBridge2;
+ }
+ }
+
+ if (provideBridge3 != null) {
+ if (provideBridge3 instanceof EditText) {
+ provideBridge3ET = (EditText) provideBridge3;
+ }
+ }
+
+ Preference bridgesProvide = findPreference(PREFS_BRIDGES_PROVIDE);
+ if (bridgesProvide != null) {
+ Log.i(LOGTAG, "setBridgeProvideText: bridgesProvide isn't null");
+ String bridgesLines = getBridges(bridgesProvide.getSharedPreferences(), PREFS_BRIDGES_PROVIDE);
+ if (bridgesLines != null) {
+ Log.i(LOGTAG, "setBridgeProvideText: bridgesLines isn't null");
+ if (bridgesLines.contains("\n")) {
+ String[] lines = bridgesLines.split("\n");
+ if (provideBridge1ET != null && lines.length >= 1) {
+ provideBridge1ET.setText(lines[0]);
+ }
+ if (provideBridge2ET != null && lines.length >= 2) {
+ provideBridge2ET.setText(lines[1]);
+ }
+ if (provideBridge3ET != null && lines.length >= 3) {
+ provideBridge3ET.setText(lines[2]);
+ }
+ } else {
+ // Simply set the single line as the text field input if the text field exists.
+ if (provideBridge1ET != null) {
+ provideBridge1ET.setText(bridgesLines);
+ }
+ }
+ }
+ }
+ }
+
+ // See explanation of TorNetworkBridgesEnabledPreference.onViewCreated()
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ setTitle(R.string.pref_tor_provide_a_bridge_title);
+ ListView lv = getListView(view);
+ if (lv == null) {
+ Log.i(LOGTAG, "onViewCreated: ListView not found");
+ return;
+ }
+ // The ListView is given "focus" by default when the EditText
+ // field is selected, this prevents typing anything into the field.
+ // We set FOCUS_AFTER_DESCENDANTS so the ListView's children are
+ // given focus (and, therefore, the EditText) before it is
+ // given focus.
+ lv.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
+
+ // The preferences are adding into the ListView hierarchy asynchronously.
+ // We need the onChildViewAdded callback so we can modify the layout after
+ // the child is added.
+ lv.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
+ @Override
+ public void onChildViewAdded(View parent, View child) {
+ // If we have a bridge line saved for this pref,
+ // then show the user
+ setBridgeProvideText(parent);
+ }
+
+ @Override
+ public void onChildViewRemoved(View parent, View child) {
+ }
+ });
+ }
+
+ private String getBridgeLineFromView(View provideBridge) {
+ if (provideBridge != null) {
+ if (provideBridge instanceof EditText) {
+ Log.i(LOGTAG, "onSaveState: Saving bridge");
+ EditText provideBridgeET = (EditText) provideBridge;
+
+ // Get the bridge line (provided text) from the text
+ // field.
+ String bridgesLine = provideBridgeET.getText().toString();
+ if (bridgesLine != null && !bridgesLine.equals("")) {
+ return bridgesLine;
+ }
+ } else {
+ Log.w(LOGTAG, "onSaveState: provideBridge isn't an EditText");
+ }
+ }
+ return null;
+ }
+
+ // Save EditText field value when the Back button or Up button are pressed.
+ @Override
+ public void onSaveState() {
+ ListView lv = getListView(getView());
+ if (lv == null) {
+ Log.i(LOGTAG, "onSaveState: ListView not found");
+ return;
+ }
+
+ final View provideBridge1 = lv.findViewById(R.id.tor_network_provide_bridge1);
+ final View provideBridge2 = lv.findViewById(R.id.tor_network_provide_bridge2);
+ final View provideBridge3 = lv.findViewById(R.id.tor_network_provide_bridge3);
+
+ String bridgesLines = null;
+ String bridgesLine1 = getBridgeLineFromView(provideBridge1);
+ String bridgesLine2 = getBridgeLineFromView(provideBridge2);
+ String bridgesLine3 = getBridgeLineFromView(provideBridge3);
+
+ if (bridgesLine1 != null) {
+ Log.i(LOGTAG, "bridgesLine1 is not null.");
+ bridgesLines = bridgesLine1;
+ }
+
+ if (bridgesLine2 != null) {
+ // If bridgesLine1 was not null, then append a newline.
+ Log.i(LOGTAG, "bridgesLine2 is not null.");
+ if (bridgesLines != null) {
+ bridgesLines += "\n" + bridgesLine2;
+ } else {
+ bridgesLines = bridgesLine2;
+ }
+ }
+
+ if (bridgesLine3 != null) {
+ // If bridgesLine1 was not null, then append a newline.
+ Log.i(LOGTAG, "bridgesLine3 is not null.");
+ if (bridgesLines != null) {
+ bridgesLines += "\n" + bridgesLine3;
+ } else {
+ bridgesLines = bridgesLine3;
+ }
+ }
+
+ Preference bridgesProvide = findPreference(PREFS_BRIDGES_PROVIDE);
+ if (bridgesProvide == null) {
+ Log.w(LOGTAG, PREFS_BRIDGES_PROVIDE + " preference not found");
+ disableBridges(this);
+ return;
+ }
+
+ if (bridgesLines == null) {
+ Log.i(LOGTAG, "provideBridge is empty. Disabling.");
+ // If provided bridges are null/empty, then only disable all bridges if
+ // the user did not select a built-in bridge
+ String configuredBuiltinBridges = getBridges(bridgesProvide.getSharedPreferences(), PREFS_BRIDGES_TYPE);
+ if (configuredBuiltinBridges == null) {
+ disableBridges(this);
+ }
+ return;
+ }
+
+ // Set the preferences (both our preference and Orbot's preference)
+ Log.w(LOGTAG, "Saving Bridge preference: " + bridgesLines);
+ if (!setBridges(bridgesProvide.getEditor(), null, bridgesLines)) {
+ // TODO inform the user
+ Log.w(LOGTAG, "Saving Bridge preference failed.");
+ disableBridges(this);
+ }
+ }
+ }
+}
1
0