fragment) {
10 | mDialog = fragment;
11 | }
12 |
13 | @Override
14 | protected void onProgressUpdate(Progress... values) {
15 | if (mDialog != null)
16 | mDialog.updateProgress(values);
17 | }
18 |
19 | @Override
20 | protected void onPostExecute(Result result) {
21 | if (mDialog != null)
22 | mDialog.taskFinished(result);
23 | }
24 |
25 | @Override
26 | protected void onCancelled(Result result) {
27 | if (mDialog != null)
28 | mDialog.taskCancelled(result);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/i2p/bote/android/widget/SummaryEditTextPreference.java:
--------------------------------------------------------------------------------
1 | package i2p.bote.android.widget;
2 |
3 | import android.content.Context;
4 | import android.preference.EditTextPreference;
5 | import android.util.AttributeSet;
6 |
7 | public class SummaryEditTextPreference extends EditTextPreference {
8 |
9 | public SummaryEditTextPreference(Context context) {
10 | super(context);
11 | }
12 |
13 | public SummaryEditTextPreference(Context context, AttributeSet attrs) {
14 | super(context, attrs);
15 | }
16 |
17 | public SummaryEditTextPreference(Context context, AttributeSet attrs, int defStyle) {
18 | super(context, attrs, defStyle);
19 | }
20 |
21 | @Override
22 | public CharSequence getSummary() {
23 | String summary = (String) super.getSummary();
24 | if (summary == null)
25 | summary = "%s";
26 | return String.format(summary, getText());
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
15 |
16 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_view_email.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
16 |
20 |
--------------------------------------------------------------------------------
/app/src/main/java/i2p/bote/android/NetworkInfoActivity.java:
--------------------------------------------------------------------------------
1 | package i2p.bote.android;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.widget.Toolbar;
5 |
6 | public class NetworkInfoActivity extends BoteActivityBase {
7 | @Override
8 | protected void onCreate(Bundle savedInstanceState) {
9 | super.onCreate(savedInstanceState);
10 | setContentView(R.layout.activity_toolbar);
11 |
12 | // Set the action bar
13 | Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
14 | setSupportActionBar(toolbar);
15 |
16 | // Enable ActionBar app icon to behave as action to go back
17 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);
18 |
19 | if (savedInstanceState == null) {
20 | NetworkInfoFragment f = new NetworkInfoFragment();
21 | getSupportFragmentManager().beginTransaction()
22 | .add(R.id.container, f).commit();
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
9 |
10 |
15 |
16 |
21 |
22 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_toolbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
17 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/raw-pt/help_start.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Iniciação
5 | First you need an identity. Create one via the "Settings" menu, or import an existing identity. Afterwards, you can add your friends' addresses by copy-paste, or exchange them via QR Codes or NFC.
6 |
7 | On Android lower than 4.4, it is recommended that you install OI File Manager for enhanced file selection. To share via QR Codes install Barcode Scanner . Clicking on the links will open Google Play Store or F-Droid for installation.
8 |
9 | Eu encontrei um erro no Bote!
10 | Por favor, reporte o erro, usando "rastrear erros I2P" .
11 |
12 | Traduções
13 | Help translating Bote! Everybody can participate at I2P on Transifex .
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/listitem_folder.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/raw-uk/help_start.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Розпочати
5 | Спочатку вам потрібен ідентифікатор. Створіть через меню налаштувань або імпортуйте існуючий. Після цього ви зможете додавати адреси друзів, копіюючи й вставляючи їх або обмінюючись ними через QR-коди чи NFC.
6 |
7 | На Android нижче 4.4 рекомендовано встановити OI File Manager для зручнішого вибору файлів. Щоб поділитись через QR-код, встановіть Barcode Scanner . Натискаючи на посилання, ви відкриєте Google Play Store чи F-Droid для встановлення.
8 |
9 | Я знайшов помилку в Bote!
10 | Будь ласка, повідомте про ваду на баг-трекері I2P .
11 |
12 | Переклади
13 | Допоможіть перекласти Bote! Кожен може взяти участь у I2P на Transifex .
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/raw-ru/help_start.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Начало работы
5 | Для начала вам понадобится идентификатор. Создайте его в меню "Настройки" или импортируйте уже существующий. После этого вы сможете добавить адреса друзей, скопировав и вставив их, или обменяться ими с помощью QR-кодов или NFC.
6 |
7 | На Android до версии 4.4 рекомендуется установить OI File Manager для удобства выбора файлов. Для использования QR-кодов установите Barcode Scanner . При нажатии на ссылку откроется Google Play Store или F-Droid.
8 |
9 | Я нашёл ошибку в Bote!
10 | Пожалуйста, сообщите об ошибке на багтрекере I2P .
11 |
12 | Переводы
13 | Помогите перевести Bote! Каждый может принять участие на странице I2P на Transifex .
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/java/i2p/bote/android/util/Person.java:
--------------------------------------------------------------------------------
1 | package i2p.bote.android.util;
2 |
3 | import android.graphics.Bitmap;
4 |
5 | import java.io.Serializable;
6 |
7 | public class Person implements Serializable {
8 | private static final long serialVersionUID = -2874686247798691378L;
9 | private String name;
10 | private String address;
11 | private Bitmap picture;
12 | private boolean isExternal;
13 |
14 | public Person(String n, String a, Bitmap p) { this(n, a, p, false); }
15 | public Person(String n, String a, Bitmap p, boolean e) { name = n; address = a; picture = p; isExternal = e; }
16 |
17 | public String getName() { return name; }
18 | public String getAddress() { return address; }
19 | public Bitmap getPicture() { return picture; }
20 | public boolean isExternal() { return isExternal; }
21 |
22 | @Override
23 | public boolean equals(Object other) {
24 | return other instanceof Person && address.equals(((Person) other).address);
25 | }
26 |
27 | @Override
28 | public String toString() { return name; }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/res/raw-fi/help_start.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Aloitetaan
5 | Tarvitsen ensiksi identiteetin. Luo yksi ”Asetukset”-valikon kautta, tai tuo olemassa oleva identiteetti. Jälkeenpäin voit lisätä ystävien osoitteet copy-paste-menetelmällä, tai vaihtamalla niitä QR-koodeina tai NFC:n avulla.
6 |
7 | Android-versiota 4.4 alemmissa käyttöjärjestelmäversioissa on suositeltavaa asentaa OI-tiedostohallinta laajennettua tiedostovalintaa varten. QR-kautta jakamiseksi asenna Viivakoodilukija . Linkkejä napsauttamalla avautuu joko Google Play-sovellus tai F-Droid asennusta varten.
8 |
9 | Löysin virhee Bote-ohjelmasta!
10 | Ilmoita viasta käyttäen I2P-vikaseuraajaa .
11 |
12 | Kotoistamiset
13 | Auta Bote-ohjelman kotoistamisessa! Kaikki voivat osallistua osoiteessa I2P Transifexissa .
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/java/i2p/bote/android/InitActivities.java:
--------------------------------------------------------------------------------
1 | package i2p.bote.android;
2 |
3 | import android.content.Context;
4 |
5 | import java.security.Security;
6 |
7 | public class InitActivities {
8 | static {
9 | Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);
10 | }
11 |
12 | private final String myDir;
13 |
14 | public InitActivities(Context c) {
15 | this(c.getFilesDir().getAbsolutePath());
16 | }
17 |
18 | public InitActivities(String i2pBaseDir) {
19 | myDir = i2pBaseDir;
20 | }
21 |
22 | public void initialize() {
23 | // Don't initialize twice
24 | if (System.getProperty("i2pbote.initialized", "false").equals("true"))
25 | return;
26 |
27 | // Set up the locations so settings can find them
28 | System.setProperty("i2p.dir.base", myDir);
29 | System.setProperty("i2p.dir.config", myDir);
30 | System.setProperty("wrapper.logfile", myDir + "/wrapper.log");
31 |
32 | System.setProperty("i2pbote.initialized", "true");
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/res/raw-in/help_start.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Mari memulai
5 | Pertama-tama anda memerlukan sebuah identitas. Buatlah satu identitas melalui menu "Setelan", atau impor identitas yang sudah ada. Setelah itu, anda dapat menambahkan alamat teman-teman anda dengan menyalin-tempel, atau bertukar melalui Kode QR atau NFC.
6 |
7 | Pada Android dibawah 4.4, direkomendasikan agar memasang OI File Manager untuk pemilihan file yang lebih baik. Untuk berbagi melalui Kode QR silahkan pasaang Barcode Scanner . Mengklik tautan akan membuka Google Play Store atau F-Droid untuk pemasang.
8 |
9 | Saya menemukan bug di Bote!
10 | Silahkan laporkan bug dengan menggunakan I2P bug tracker .
11 |
12 | Penerjemahan
13 | Bantu menerjemahkan Bote! Semua orang bisa berpartisipasi di I2P di Transifex .
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_password.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
19 |
20 |
21 |
22 |
23 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/raw-fr/help_start.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Débuter
5 | D'abord vous aurez besoin d'une identité. Créez en une via le menu "Paramètres", ou importez une identité existante. Ensuite, vous pourrez ajouter les adresses de vos amis via copier/coller, ou les échanger via des QR Codes ou NFC.
6 |
7 | Sur Android antérieur à 4.4, et il recommandé d'installer OI File Manager pour disposer d'une sélection de fichiers améliorée. Pour partager via QR Codes, installez Barcode Scanner . Cliquez sur les liens ouvrira Google Play Store ou F-Droid afin de permettre l'installation.
8 |
9 | J'ai trouvé un bug dans Bote !
10 | Veuillez signaler le bug (en anglais) en utilisant le I2P bug tracker .
11 |
12 | Traductions
13 | Aidez à traduire Bote ! Tout le monde peut participer via I2P sur Transifex .
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/raw-de/help_start.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Der Start mit Bote
5 | Zuerst benötigen Sie eine Identität. Erstellen Sie eine unter dem Menüpunkt "Einstellungen", oder importieren Sie bereits existierende Identitäten. Anschließend fügen Sie die Adressen Ihrer Freunde mittels Kopie, NFC oder QR-Code hinzu.
6 |
7 | Auf Android älter als Version 4.4 sollten Sie den OI File Manager zur besseren Dateiverwaltung installieren. Zum Austauschen von QR Codes ist der Barcode Scanner gut. Diese Links öffnen diese Pakete zur Installation im Google Play Store oder in F-Droid.
8 |
9 | Ich habe einen Bug in Bote gefunden!
10 | Bitte teilen Sie uns diesen Bug auf unserem I2P bug tracker mit.
11 |
12 | Übersetzungen
13 | Helfen Sie mit, Bote zu übersetzen! Jeder kann sich unter I2P auf Transifex beteiligen.
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/raw-nl/help_start.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Aan de slag
5 | Eerst moet je een identiteit aanmaken. Maak er een aan via het "Instellingen"-menu, of importeer een bestaande identiteit. Vervolgens kan je de adressen van je vrienden toevoegen door kopiëren-plakken, of door ze uit te wisselen via QR-codes of NFC.
6 |
7 | Op Android-versies lager dan 4.4 raden we aan dat je OI File Manager installeert voor een verbeterde bestandsselectie. Om via QR codes te delen raden we aan dat je Barcode Scanner installeert. Wanneer je op deze links klikt zal F-Droid of Google Play Store worden geopend voor installatie.
8 |
9 | Ik heb een bug in Bote gevonden!
10 | Gelieve de bug de melden via de I2P-bug tracker .
11 |
12 | Vertalingen
13 | Help mee Bote te vertalen! Iedereen kan meewerken op I2P op Transifex .
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/raw-es/help_start.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Comenzar
5 | Primero necesita una identidad. Cree una mediante el menú "Configuración", o importe una identidad existente. Después, puede añadir las direcciones de sus amigos mediante copiar-pegar, o intercambiándolas mediante códigos QR o vía NFC.
6 |
7 | En Android inferior a 4.4, se recomienda que instale el administrador de ficheros OI File Manager para una selección de ficheros mejorada. Para compartir mediante códigos QR instale Barcode Scanner . Haciendo clic en los enlaces abrirá la tienda Google Play o el sitio de F-Droid para la instalación.
8 |
9 | ¡Encontré un fallo en Bote!
10 | Por favor informe del fallo utilizando el rastreador de fallos de I2P .
11 |
12 | Traducciones
13 | ¡Ayude a traducir Bote! Todos pueden participar en I2P en Transifex .
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
18 |
19 |
20 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/listitem_attachment_warning.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
18 |
19 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/java/i2p/bote/android/config/SetPasswordActivity.java:
--------------------------------------------------------------------------------
1 | package i2p.bote.android.config;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.widget.Toolbar;
5 | import android.widget.Toast;
6 |
7 | import i2p.bote.android.BoteActivityBase;
8 | import i2p.bote.android.R;
9 |
10 | public class SetPasswordActivity extends BoteActivityBase implements
11 | SetPasswordFragment.Callbacks {
12 | @Override
13 | public void onCreate(Bundle savedInstanceState) {
14 | super.onCreate(savedInstanceState);
15 | setContentView(R.layout.activity_set_password);
16 |
17 | // Set the action bar
18 | Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
19 | setSupportActionBar(toolbar);
20 |
21 | // Enable ActionBar app icon to behave as action to go back
22 | //noinspection ConstantConditions
23 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);
24 | }
25 |
26 | // SetPasswordFragment.Callbacks
27 |
28 | public void onTaskFinished() {
29 | Toast.makeText(this, R.string.password_changed,
30 | Toast.LENGTH_SHORT).show();
31 | setResult(RESULT_OK);
32 | finish();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/res/raw/help_start.html:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 | Getting started
9 | First you need an identity. Create one via the "Settings" menu, or import an existing identity. Afterwards, you can add your friends' addresses by copy-paste, or exchange them via QR Codes or NFC.
10 |
11 | On Android lower than 4.4, it is recommended that you install OI File Manager for enhanced file selection. To share via QR Codes install Barcode Scanner . Clicking on the links will open Google Play Store or F-Droid for installation.
12 |
13 | I found a bug in Bote!
14 | Please report the bug using the I2P bug tracker .
15 |
16 | Translations
17 | Help translating Bote! Everybody can participate at I2P on Transifex .
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/java/i2p/bote/android/config/AppearancePreferenceFragment.java:
--------------------------------------------------------------------------------
1 | package i2p.bote.android.config;
2 |
3 | import android.os.Bundle;
4 | import android.support.v4.preference.PreferenceFragment;
5 |
6 | import i2p.bote.android.R;
7 |
8 | public class AppearancePreferenceFragment extends PreferenceFragment {
9 | @Override
10 | public void onCreate(Bundle paramBundle) {
11 | super.onCreate(paramBundle);
12 | addPreferencesFromResource(R.xml.settings_appearance);
13 | }
14 |
15 | @Override
16 | public void onStart() {
17 | super.onStart();
18 | getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(
19 | (SettingsActivity) getActivity()
20 | );
21 | }
22 |
23 | @Override
24 | public void onResume() {
25 | super.onResume();
26 | ((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_label_appearance);
27 | }
28 |
29 | @Override
30 | public void onStop() {
31 | super.onStop();
32 | getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(
33 | (SettingsActivity) getActivity()
34 | );
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/gradle/signing.gradle:
--------------------------------------------------------------------------------
1 | android {
2 | signingConfigs {
3 | release
4 | }
5 | buildTypes {
6 | release {
7 | signingConfig signingConfigs.release
8 | }
9 | }
10 | }
11 |
12 | def Properties props = new Properties()
13 | def propFile = new File(project.rootDir, 'signing.properties')
14 |
15 | if (propFile.canRead()) {
16 | props.load(new FileInputStream(propFile))
17 |
18 | if (props != null &&
19 | props.containsKey('STORE_FILE') &&
20 | props.containsKey('STORE_PASSWORD') &&
21 | props.containsKey('KEY_ALIAS') &&
22 | props.containsKey('KEY_PASSWORD')) {
23 | android.signingConfigs.release.storeFile = file(props['STORE_FILE'])
24 | android.signingConfigs.release.storePassword = props['STORE_PASSWORD']
25 | android.signingConfigs.release.keyAlias = props['KEY_ALIAS']
26 | android.signingConfigs.release.keyPassword = props['KEY_PASSWORD']
27 | } else {
28 | println 'signing.properties found but some entries are missing'
29 | android.buildTypes.release.signingConfig = null
30 | }
31 | } else {
32 | println 'signing.properties not found'
33 | android.buildTypes.release.signingConfig = null
34 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/i2p/bote/android/intro/IntroActivityTest.java:
--------------------------------------------------------------------------------
1 | package i2p.bote.android.intro;
2 |
3 | import android.support.test.rule.ActivityTestRule;
4 | import android.support.test.runner.AndroidJUnit4;
5 |
6 | import org.junit.Rule;
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import i2p.bote.android.R;
11 |
12 | import static android.support.test.espresso.Espresso.onView;
13 | import static android.support.test.espresso.action.ViewActions.click;
14 | import static android.support.test.espresso.action.ViewActions.swipeLeft;
15 | import static android.support.test.espresso.matcher.ViewMatchers.withId;
16 |
17 | @RunWith(AndroidJUnit4.class)
18 | public class IntroActivityTest {
19 |
20 | @Rule
21 | public ActivityTestRule mRule = new ActivityTestRule<>(IntroActivity.class);
22 |
23 | @Test
24 | public void enterSetup() {
25 | onView(withId(R.id.pager)).perform(swipeLeft(), swipeLeft(), swipeLeft(), swipeLeft(), swipeLeft());
26 | onView(withId(R.id.start_setup_wizard)).perform(click());
27 | // TODO check result
28 | }
29 |
30 | @Test
31 | public void closeIntro() {
32 | onView(withId(R.id.skip_intro)).perform(click());
33 | // TODO check result
34 | }
35 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_intro.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
13 |
14 |
18 |
19 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/contact_token.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/raw-uk/help_identities.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Що таке ідентифікатор?
5 | Ідентифікатор — це щось, схоже на поштовий аккаунт, але без центру, своїм аккаунтом керуєте ви, а не хтось інший, як наприклад Google чи Yahoo.
6 |
7 | Що таке призначення?
8 | Призначення — це адреса, яку ви даєте друзям і на яку вони можуть відправляти вам повідомлення.
9 |
10 | Чому електронні адрси такі довгі?
11 | Кожне повідомлення автоматично шифрується Bote'ом. Кожна адреса містить у собі набір відкритих ключів, якими шифруються надіслані на цю адресу. Довжина адреси залежить від використовуваного типу шифрування.
12 |
13 | Чи є обмеження на кількість ідентифікаторів, які я можу мати?
14 | Ви можете робити скільки завгодно ідентифікаторів і використовувати кожен для різних цілей.
15 |
16 | Коли ідентифікатори закінчуються?
17 | Ніколи. Поки ви маєте секретні ключі, ви можете його використовувати. Це також означає, що не існує такого, як відновлення аккаунту: втрачаєте телефон — втрачаєте й ідентифікатори.
18 | Ви можете зробити резервну копію ідентифікаторів з меню налаштувань. Це створить їх копію в пам’яті телфона. Файл можна буде використати для відновлення ваших ідентифікаторів або переміщення їх на інший пристрій.
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/java/i2p/bote/crypto/ECUtils.java:
--------------------------------------------------------------------------------
1 | package i2p.bote.crypto;
2 |
3 | import org.spongycastle.asn1.nist.NISTNamedCurves;
4 | import org.spongycastle.asn1.x9.X9ECParameters;
5 | import org.spongycastle.jcajce.provider.asymmetric.util.EC5Util;
6 | import org.spongycastle.jce.ECPointUtil;
7 | import org.spongycastle.jce.spec.ECNamedCurveSpec;
8 |
9 | import java.security.spec.ECParameterSpec;
10 | import java.security.spec.ECPoint;
11 | import java.security.spec.EllipticCurve;
12 |
13 | /**
14 | * A wrapper around the SpongyCastle EC classes.
15 | */
16 | public class ECUtils {
17 | public static ECParameterSpec getParameters(String curveName) {
18 | X9ECParameters params = NISTNamedCurves.getByName(curveName);
19 | return new ECNamedCurveSpec(curveName, params.getCurve(), params.getG(), params.getN(), params.getH(), null);
20 | }
21 |
22 | public static byte[] encodePoint(
23 | ECParameterSpec ecSpec,
24 | ECPoint point,
25 | boolean withCompression) {
26 | org.spongycastle.math.ec.ECPoint bcPoint = EC5Util.convertPoint(ecSpec, point, withCompression);
27 | return bcPoint.getEncoded();
28 | }
29 |
30 | public static ECPoint decodePoint(
31 | EllipticCurve curve,
32 | byte[] encoded) {
33 | return ECPointUtil.decodePoint(curve, encoded);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/listitem_folder_with_icon.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
15 |
16 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_help.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
26 |
27 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/java/im/delight/android/identicons/Identicon.java:
--------------------------------------------------------------------------------
1 | package im.delight.android.identicons;
2 |
3 | import android.graphics.Color;
4 |
5 | public class Identicon extends IdenticonBase {
6 | private static final int CENTER_COLUMN_INDEX = 5;
7 |
8 | @Override
9 | protected int getRowCount() {
10 | return 9;
11 | }
12 |
13 | @Override
14 | protected int getColumnCount() {
15 | return 9;
16 | }
17 |
18 | protected int getSymmetricColumnIndex(int col) {
19 | if (col < CENTER_COLUMN_INDEX) {
20 | return col;
21 | } else {
22 | return getColumnCount() - col - 1;
23 | }
24 | }
25 |
26 | @Override
27 | protected boolean isCellVisible(int row, int column) {
28 | return getByte(3 + row * CENTER_COLUMN_INDEX + getSymmetricColumnIndex(column)) >= 0;
29 | }
30 |
31 | @Override
32 | protected int getIconColor() {
33 | return Color.rgb(getByte(0) + 128, getByte(1) + 128, getByte(2) + 128);
34 | }
35 |
36 | @Override
37 | protected int getBackgroundColor() {
38 | float[] hsv = new float[3];
39 | Color.colorToHSV(getIconColor(), hsv);
40 | if (hsv[2] < 0.5)
41 | return Color.parseColor("#ffeeeeee"); // @color/background_material_light
42 | else
43 | return Color.parseColor("#ff303030"); // @color/background_material_dark
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/res/raw-ru/help_identities.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Что такое идентификатор?
5 | Идентификатор похож на адрес электронной почты, только без посредника: вы сами контролируете ваш аккаунт, а не третья сторона, как Google или Yahoo.
6 |
7 | Что такое пункт назначения?
8 | Пункт назначения — это адрес, который вы даете друзьям, чтобы они могли отправлять вам письма.
9 |
10 | Почему адреса такие длинные?
11 | Bote автоматически шифрует каждое письмо. Адрес содержит набор открытых ключей, которые используются для шифрования писем, отправляемых на этот адрес. Длина адреса зависит от используемого вида шифрования.
12 |
13 | Есть ли ограничение на количество идентификаторов?
14 | Вы можете создать столько идентификаторов, сколько хотите, и использовать различные идентификаторы для разных целей.
15 |
16 | Когда заканчивается время действия идентификатора?
17 | Никогда. Пока у вас есть закрытый ключ для идентификатора, вы можете им пользоваться. Это также означает, что возможность восстановить аккаунт отсутствует: если вы потеряете телефон, вы потеряете ключи и идентификаторы.
18 | Вы можете создать резервную копию идентификаторов с помощью функции экспорта в меню "Настройки". В результате вы получите файл в хранилище вашего телефона. Файл можно использовать для восстановления идентифкаторов или переноса на другое устройство.
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/listitem_contact.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
18 |
19 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/java/i2p/bote/android/widget/IntEditTextPreference.java:
--------------------------------------------------------------------------------
1 | package i2p.bote.android.widget;
2 |
3 | import android.content.Context;
4 | import android.preference.EditTextPreference;
5 | import android.text.InputType;
6 | import android.util.AttributeSet;
7 |
8 | public class IntEditTextPreference extends EditTextPreference {
9 |
10 | public IntEditTextPreference(Context context) {
11 | super(context);
12 | getEditText().setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED);
13 | }
14 |
15 | public IntEditTextPreference(Context context, AttributeSet attrs) {
16 | super(context, attrs);
17 | getEditText().setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED);
18 | }
19 |
20 | public IntEditTextPreference(Context context, AttributeSet attrs, int defStyle) {
21 | super(context, attrs, defStyle);
22 | getEditText().setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED);
23 | }
24 |
25 | @Override
26 | public CharSequence getSummary() {
27 | return String.format((String) super.getSummary(), getText());
28 | }
29 |
30 | @Override
31 | protected String getPersistedString(String defaultReturnValue) {
32 | return String.valueOf(getPersistedInt(-1));
33 | }
34 |
35 | @Override
36 | protected boolean persistString(String value) {
37 | return persistInt(Integer.valueOf(value));
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/res/raw-in/help_identities.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Apa sih Identitas?
5 | Identitas adalah seperti sebuah akun email, tetapi tanpa penyedia layanan - anda yang mengontrol akun anda, sebagai pengganti pihak ketiga seperti Google atau Yahoo.
6 |
7 | Apa sih Destinasi?
8 | Destinasi adalah alamat yang anda berikan kepada teman-teman anda agar mereka dapat mengirimi anda email.
9 |
10 | Kenapa alamat emailnya begitu panjang?
11 | Setiap email otomatis dienkripsi oleh Bote. Setiap alamat berisi satu set kunci publik, yang mana digunakan untuk mengenkripsi email terkirim ke alamat tersebut. Panjang alamat tergantung pada tipe enkripsi yang digunakan.
12 |
13 | Apakah ada batasan pada jumlah Identitas yang dapat saya miliki?
14 | Anda dapat membuat Identitas sebanyak yang anda inginkan, dan menggunakan Identitas berbeda untuk kepentingan berbeda.
15 |
16 | Kapan Identitas berakhir?
17 | Tidak akan. Selama anda memiliki kunci pribadi untuk sebuah Identitas, anda tetap dapat menggunakannya. Hal ini juga berarti tidak ada "pemulihan akun" - jika anda kehilangan ponsel anda, maka anda kehilangan Identitas anda.
18 | Anda dapat mencadangkan Identitas dengan mengekspornya dari menu "Setelan". Ini akan menyimpan salinannya kedalam sebuah file pada penyimpanan ponsel anda. File dapat digunakan untuk memulihkan Identitas anda, atau memindahkannya ke perangkat lain.
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/raw-nl/help_identities.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Wat is een Identiteit?
5 | Een Identiteit is zoals een e-mailaccount, maar dan zonder provider - je beheert zelf je account, in plaats van een derde partij zoals Google of Yahoo.
6 |
7 | Wat is een Bestemming?
8 | Een Bestemming is het adres dat je aan je vrienden geeft zodat ze je e-mails kunnen sturen.
9 |
10 | Waarom zijn de e-mailadressen zo lang?
11 | Elke e-mail wordt automatisch versleuteld door Bote. Elk adres bevat een set publieke sleutels, die gebruikt worden om e-mails die naar dat adres verzonden worden te versleutelen. De lengte van het adres hangt af van de gebruikte versleutelingsmethode.
12 |
13 | Is er een limiet op het aantal Identiteiten die ik kan hebben?
14 | Je kan zoveel Identiteiten aanmaken als je wil, en verschillende Identiteiten gebruiken voor verschillende doeleinden.
15 |
16 | Wanneer vervallen Identiteiten?
17 | Nooit. Zo lang je de privésleutels voor een Identiteit hebt, kan je die gebruiken. Dit betekent ook dat er geen "accountherstel" is - als je je telefoon verliest, verlies je je Identiteiten.
18 | Je kan je Identiteiten backuppen door ze te exporteren via het "Instellingen"-menu. Dit zal een kopie ervan opslaan naar een bestand in je telefoongeheugen. Het bestand kan gebruikt worden om je Identiteiten te herstellen, of ze naar een ander toestel te verplaatsen.
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/raw-es/help_identities.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ¿Qué es una identidad?
5 | Una identidad es como una cuenta de correo electrónico, pero sin un proveedor ajeno - puede controlar su cuenta en lugar de que lo haga un tercero como Google o Yahoo.
6 |
7 | ¿Qué es un destino?
8 | Un destino es una dirección que usted proporciona a sus amigos para que puedan enviarle sus correos electrónicos.
9 |
10 | ¿Por qué son tan largas las direcciones de correo electrónico?
11 | Todo el correo electrónico se cifra automáticamente por Bote. Cada dirección contiene un conjunto de claves públicas que se utilizan para cifrar correos enviados a esa dirección. La longitud de la dirección depende del tipo de cifrado usado.
12 |
13 | ¿Hay un límite para el número de identidades que puedo tener?
14 | Puede crear tantas identidades como quiera, y utilizar distintas identidades para diferentes propósitos.
15 |
16 | ¿Cuándo caducan las identidades?
17 | Nunca. Mientras tenga las claves privadas para una identidad, podrá usarla. Esto también significa que no hay "recuperación de cuenta" - si pierde su teléfono, pierde sus identidades.
18 | Puede realizar una copia de seguridad de sus identidades exportándolas desde el menú "Configuración". Esto guardará una copia de ellas en un fichero en el almacenamiento de su teléfono. El fichero puede usarse para recuperar sus identidades, o moverlas a otro dispositivo.
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/listitem_identity.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
19 |
20 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/src/main/java/i2p/bote/android/HelpHtmlFragment.java:
--------------------------------------------------------------------------------
1 | package i2p.bote.android;
2 |
3 | import android.os.Bundle;
4 | import android.support.v4.app.Fragment;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ScrollView;
9 |
10 | import org.sufficientlysecure.htmltextview.HtmlTextView;
11 |
12 | public class HelpHtmlFragment extends Fragment {
13 | public static final String ARG_HTML_FILE = "htmlFile";
14 |
15 | static HelpHtmlFragment newInstance(int htmlFile) {
16 | HelpHtmlFragment f = new HelpHtmlFragment();
17 | Bundle args = new Bundle();
18 | args.putInt(ARG_HTML_FILE, htmlFile);
19 | f.setArguments(args);
20 | return f;
21 | }
22 |
23 | @Override
24 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
25 | ScrollView scroller = new ScrollView(getActivity());
26 | HtmlTextView text = new HtmlTextView(getActivity());
27 | scroller.addView(text);
28 | int padH = getResources().getDimensionPixelOffset(R.dimen.activity_horizontal_margin);
29 | int padV = getResources().getDimensionPixelOffset(R.dimen.activity_vertical_margin);
30 | text.setPadding(padH, padV, padH, padV);
31 | text.setHtmlFromRawResource(getActivity(), getArguments().getInt(ARG_HTML_FILE), true);
32 | text.setTextColor(getResources().getColor(R.color.primary_text_default_material_light));
33 | return scroller;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/res/raw/help_identities.html:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 | What is an Identity?
9 | An Identity is like an email account, but without a provider - you control your account, instead of a third party like Google or Yahoo.
10 |
11 | What is a Destination?
12 | A Destination is the address that you give to your friends so they can send you emails.
13 |
14 | Why are the email addresses so long?
15 | Every email is automatically encrypted by Bote. Each address contains a set of public keys, which are used to encrypt emails sent to that address. The length of the address depends on the type of encryption used.
16 |
17 | Is there a limit on the number of Identities I can have?
18 | You can make as many Identities as you want, and use different Identities for different purposes.
19 |
20 | When do Identities expire?
21 | Never. As long as you have the private keys for an Identity, you can use it. This also means that there is no "account recovery" - if you lose your phone, you lose your Identities.
22 | You can back up your Identities by exporting them from the "Settings" menu. This will save a copy of them to a file in your phone's storage. The file can be used to recover your Identities, or move them to another device.
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/raw-de/help_identities.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Was ist eine Identität?
5 | Eine Idnetität ist wie ein E-Mail-Konto, nur ohne einen Anbieter - Sie kontrollieren Ihr Konto, und nicht jemand anderes wie Google oder Yahoo.
6 |
7 | Was ist eine Destination?
8 | Eine Destination ist eine Adresse, die Sie Ihren Freunden geben können, damit diese Ihnen dann E-Mails schicken können.
9 |
10 | Warum sind die E-Mail-Adressen so lang?
11 | Jede E-Mail wird von Bote automatisch verschlüsselt. Jede Adresse enthält ein Paar öffentlicher Schlüsseln, um die E-Mails, die an diese Adresse geschickt werden, zu verschlüsseln. Die Länge der Adresse variiert mit je nach verwendeter Verschlüsselung.
12 |
13 | Gibt es eine Begrenzung der Anzahl an Indentitäten, die ich haben kann?
14 | Sie können so viele Identitäten erstellen, wie sie möchten und jeweils für verschiedene Zwecke nutzen.
15 |
16 | Wann verfallen die Identitäten?
17 | Niemals. Solange Sie die privaten Schlüssel für eine Identität haben, können Sie diese nutzen. Somit gibt es auch keine "Kontowiederherstellung" - verlieren Sie Ihre privaten Schlüssel (ihr Handy), verlieren Sie auch diese Identitäten.
18 | Sie können Ihre Identitäten mit einem Export aus dem "Einstellungen"-Menü sichern. Dieses legt eine Kopie als Datei im Speicher Ihres Handys an. Diese Datei sollten Sie extern sichern und sie kann zum Wiederherstellen Ihrer Indentitäten auf diesem oder einem anderem Gerät benutzt werden.
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/raw-fr/help_identities.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Qu'est-ce qu'une identité ?
5 | Une identité est comme un compte email, mais sans fournisseur - vous contrôlez vote compte, au lieu que ce soit un tiers tel que Google, Yahoo, ...
6 |
7 | Qu'est-ce qu'une Destination ?
8 | Une Destination est l'adresse que vous indiquez à vos amis afin qu'ils puissent vous envoyer des emails.
9 |
10 | Pourquoi les adresses emails sont-elles si longues ?
11 | Chaque courrier électronique est chiffré automatiquement par Bote. Chaque adresse contient un ensemble de clés publiques, lesquelles sont utilisées pour chiffrer les courriers électroniques envoyés à cette adresse. La longueur de l'adresse dépend du type de chiffrement utilisé.
12 |
13 | Y-a-t-il un nombre limite d'Identités que je peux avoir ?
14 | Vous pouvez faire autant d'Identités que voulez, et utiliser des Identités différentes pour des buts différents.
15 |
16 | Quand l'identité expire-t-elle ?
17 | Jamais. Aussi longtemps que vous aurez les clés privées d'une Identité, vous pourrez l'utiliser. Ceci signifie aussi qu'il n'y a aucun "rétablissement de compte" - si vous perdez votre téléphone, vous perdez vos Identités.
18 | Vous pouvez sauvegarder vos Identités en les exportant depuis le menu "Paramètres". Ceci sauvegardera une copie d'entre eux dans un fichier situé sur le stockage de votre téléphone. Le fichier pourra être utilisé pour récupérer vos Identités, ou pour les déplacer vers un autre dispositif.
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/raw/help_about_libraries.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_identity_ship.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
16 |
19 |
20 |
24 |
25 |
29 |
30 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_edit_identity.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
16 |
19 |
20 |
24 |
25 |
29 |
30 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/java/i2p/bote/android/BoteActivityBase.java:
--------------------------------------------------------------------------------
1 | package i2p.bote.android;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.SharedPreferences;
5 | import android.os.Build;
6 | import android.os.Bundle;
7 | import android.preference.PreferenceManager;
8 | import android.support.v7.app.AppCompatActivity;
9 | import android.view.WindowManager;
10 |
11 | import i2p.bote.android.util.LocaleManager;
12 |
13 | @SuppressLint("Registered")
14 | public class BoteActivityBase extends AppCompatActivity {
15 | private final LocaleManager localeManager = new LocaleManager();
16 |
17 | @Override
18 | protected void onCreate(Bundle savedInstanceState) {
19 | localeManager.onCreate(this);
20 | super.onCreate(savedInstanceState);
21 |
22 | // Initialize I2P settings
23 | InitActivities init = new InitActivities(this);
24 | init.initialize();
25 |
26 | // Initialize screen security
27 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
28 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH &&
29 | prefs.getBoolean("pref_screen_security", true))
30 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
31 | else
32 | getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
33 | }
34 |
35 | @Override
36 | public void onResume() {
37 | super.onResume();
38 | localeManager.onResume(this);
39 | }
40 |
41 | public void notifyLocaleChanged() {
42 | localeManager.onResume(this);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/res/values/arrays.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - 0
5 | - 1
6 | - 2
7 | - 3
8 |
9 |
10 | - internal
11 | - android
12 | - remote
13 |
14 |
15 | - @string/internal
16 | - I2P Android
17 | - @string/remote_i2p_router
18 |
19 |
20 | - zz
21 | - en
22 | - zh
23 | - de
24 | - es
25 | - fr
26 | - in
27 | - nb
28 | - nl
29 | - pl
30 | - pt
31 | - ro
32 | - ru
33 | - sq
34 |
35 |
36 | - @string/settings_default
37 | - English
38 | - Chinese 中国的
39 | - Deutsch
40 | - Español
41 | - Français
42 | - Indonesia
43 | - Norwegian Bokmål
44 | - Nederlands
45 | - Polski
46 | - Português
47 | - Română
48 | - Russian Pусский
49 | - Shqip
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/settings_privacy.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
15 |
16 |
17 |
23 |
24 |
30 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/app/src/main/java/i2p/bote/android/config/AppProtectionPreferenceFragment.java:
--------------------------------------------------------------------------------
1 | package i2p.bote.android.config;
2 |
3 | import android.content.Intent;
4 | import android.os.Build;
5 | import android.os.Bundle;
6 | import android.preference.Preference;
7 | import android.support.v4.preference.PreferenceFragment;
8 |
9 | import i2p.bote.android.R;
10 |
11 | public class AppProtectionPreferenceFragment extends PreferenceFragment {
12 | @Override
13 | public void onCreate(Bundle paramBundle) {
14 | super.onCreate(paramBundle);
15 | addPreferencesFromResource(R.xml.settings_app_protection);
16 | setupAppProtectionSettings();
17 | }
18 |
19 | @Override
20 | public void onResume() {
21 | super.onResume();
22 | //noinspection ConstantConditions
23 | ((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_label_app_protection);
24 |
25 | // Screen security only works from API 14
26 | Preference screenSecurityPreference = findPreference("pref_screen_security");
27 | if (screenSecurityPreference != null &&
28 | Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH)
29 | getPreferenceScreen().removePreference(screenSecurityPreference);
30 | }
31 |
32 | private void setupAppProtectionSettings() {
33 | findPreference("pref_change_password").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
34 | @Override
35 | public boolean onPreferenceClick(Preference preference) {
36 | startActivity(new Intent(getActivity(), SetPasswordActivity.class));
37 | return true;
38 | }
39 | });
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/java/i2p/bote/android/identities/EditIdentityActivity.java:
--------------------------------------------------------------------------------
1 | package i2p.bote.android.identities;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.widget.Toolbar;
5 | import android.widget.Toast;
6 |
7 | import i2p.bote.android.BoteActivityBase;
8 | import i2p.bote.android.R;
9 |
10 | public class EditIdentityActivity extends BoteActivityBase implements
11 | EditIdentityFragment.Callbacks {
12 | @Override
13 | public void onCreate(Bundle savedInstanceState) {
14 | super.onCreate(savedInstanceState);
15 | setContentView(R.layout.activity_edit_identity);
16 |
17 | // Set the action bar
18 | Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
19 | setSupportActionBar(toolbar);
20 |
21 | // Enable ActionBar app icon to behave as action to go back
22 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);
23 |
24 | if (savedInstanceState == null) {
25 | String key = null;
26 | Bundle args = getIntent().getExtras();
27 | if (args != null)
28 | key = args.getString(EditIdentityFragment.IDENTITY_KEY);
29 | if (key != null)
30 | getSupportActionBar().setDisplayShowTitleEnabled(false);
31 | EditIdentityFragment f = EditIdentityFragment.newInstance(key);
32 | getSupportFragmentManager().beginTransaction()
33 | .add(R.id.edit_identity_frag, f).commit();
34 | }
35 | }
36 |
37 | // EditIdentityFragment.Callbacks
38 |
39 | public void onTaskFinished() {
40 | Toast.makeText(this, R.string.identity_saved,
41 | Toast.LENGTH_SHORT).show();
42 | setResult(RESULT_OK);
43 | finish();
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/java/i2p/bote/android/config/NetworkPreferenceFragment.java:
--------------------------------------------------------------------------------
1 | package i2p.bote.android.config;
2 |
3 | import android.content.SharedPreferences;
4 | import android.os.Bundle;
5 | import android.preference.PreferenceManager;
6 | import android.support.v4.preference.PreferenceFragment;
7 |
8 | import java.util.Map;
9 |
10 | import i2p.bote.Configuration;
11 | import i2p.bote.I2PBote;
12 | import i2p.bote.android.R;
13 |
14 | public class NetworkPreferenceFragment extends PreferenceFragment {
15 | @Override
16 | public void onCreate(Bundle paramBundle) {
17 | super.onCreate(paramBundle);
18 | addPreferencesFromResource(R.xml.settings_network);
19 | }
20 |
21 | @Override
22 | public void onResume() {
23 | super.onResume();
24 | //noinspection ConstantConditions
25 | ((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_label_network);
26 | }
27 |
28 | @Override
29 | public void onPause() {
30 | Configuration config = I2PBote.getInstance().getConfiguration();
31 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
32 |
33 | Map all = prefs.getAll();
34 | for (String x : all.keySet()) {
35 | if ("autoMailCheckEnabled".equals(x))
36 | config.setAutoMailCheckEnabled(prefs.getBoolean(x, true));
37 | else if ("mailCheckInterval".equals(x))
38 | config.setMailCheckInterval(prefs.getInt(x, 30));
39 | else if ("deliveryCheckEnabled".equals(x))
40 | config.setDeliveryCheckEnabled(prefs.getBoolean(x, true));
41 | }
42 |
43 | config.save();
44 |
45 | // Store the settings in Android
46 | super.onPause();
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 | 120dp
7 | 64dp
8 |
9 | 320dp
10 | 16dp
11 | 48dp
12 |
13 | 20sp
14 | 16sp
15 | 14sp
16 |
17 | 8dp
18 | 16dp
19 | 40dp
20 | 24dp
21 | 72dp
22 | 8dp
23 |
24 | 48dp
25 | 56dp
26 |
27 | 88dp
28 |
29 | 54dp
30 |
31 | 64dp
32 | 64dp
33 |
34 | 32dp
35 | 8dp
36 | 12dp
37 | 16dp
38 |
39 | 200dp
40 |
41 | 300dp
42 |
43 | 32dp
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/java/i2p/bote/android/identities/IdentityShipActivity.java:
--------------------------------------------------------------------------------
1 | package i2p.bote.android.identities;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.widget.Toolbar;
5 | import android.widget.Toast;
6 |
7 | import i2p.bote.android.BoteActivityBase;
8 | import i2p.bote.android.InitActivities;
9 | import i2p.bote.android.R;
10 |
11 | public class IdentityShipActivity extends BoteActivityBase implements
12 | IdentityShipFragment.Callbacks {
13 | public static final String EXPORTING = "exporting";
14 |
15 | boolean mExporting;
16 |
17 | @Override
18 | public void onCreate(Bundle savedInstanceState) {
19 | super.onCreate(savedInstanceState);
20 | setContentView(R.layout.activity_identity_ship);
21 |
22 | // Set the action bar
23 | Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
24 | setSupportActionBar(toolbar);
25 |
26 | // Enable ActionBar app icon to behave as action to go back
27 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);
28 |
29 | if (savedInstanceState == null) {
30 | Bundle args = getIntent().getExtras();
31 | mExporting = args.getBoolean(EXPORTING);
32 | IdentityShipFragment f = IdentityShipFragment.newInstance(mExporting);
33 | getSupportFragmentManager().beginTransaction()
34 | .add(R.id.identity_ship_frag, f).commit();
35 | }
36 | }
37 |
38 | // IdentityShipFragment.Callbacks
39 |
40 | public void onTaskFinished() {
41 | Toast.makeText(this,
42 | mExporting ?
43 | R.string.identities_exported :
44 | R.string.identities_imported,
45 | Toast.LENGTH_SHORT).show();
46 | setResult(RESULT_OK);
47 | finish();
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_set_password.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
17 |
21 |
22 |
26 |
27 |
33 |
34 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_intro_1.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
18 |
19 |
29 |
30 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_intro_3.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
18 |
19 |
29 |
30 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_intro_4.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
18 |
19 |
29 |
30 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_intro_2.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
18 |
19 |
29 |
30 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/java/i2p/bote/android/widget/IconicsFloatingActionButton.java:
--------------------------------------------------------------------------------
1 | package i2p.bote.android.widget;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.util.AttributeSet;
6 |
7 | import com.mikepenz.iconics.IconicsDrawable;
8 |
9 | import net.i2p.android.ext.floatingactionbutton.FloatingActionButton;
10 |
11 | import i2p.bote.android.R;
12 |
13 | public class IconicsFloatingActionButton extends FloatingActionButton {
14 | public IconicsFloatingActionButton(Context context) {
15 | this(context, null);
16 | }
17 |
18 | public IconicsFloatingActionButton(Context context, AttributeSet attrs) {
19 | super(context, attrs);
20 | init(context, attrs);
21 | }
22 |
23 | public IconicsFloatingActionButton(Context context, AttributeSet attrs, int defStyle) {
24 | super(context, attrs, defStyle);
25 | init(context, attrs);
26 | }
27 |
28 | private void init(Context context, AttributeSet attrs) {
29 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.IconicsFloatingActionButton, 0, 0);
30 | String iconName = a.getString(R.styleable.IconicsFloatingActionButton_ifab_icon);
31 | if (iconName == null)
32 | return;
33 |
34 | IconicsDrawable icon = new IconicsDrawable(context, iconName);
35 | int color = a.getColor(R.styleable.IconicsFloatingActionButton_ifab_color, 0);
36 | if (color != 0)
37 | icon.color(color);
38 | int size = a.getDimensionPixelSize(R.styleable.IconicsFloatingActionButton_ifab_size, 0);
39 | if (size != 0)
40 | icon.sizePx(size);
41 | int padding = a.getDimensionPixelSize(R.styleable.IconicsFloatingActionButton_ifab_padding, 0);
42 | if (padding != 0)
43 | icon.paddingPx(padding);
44 |
45 | a.recycle();
46 | setIconDrawable(icon);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/java/i2p/bote/android/util/IconicsPreference.java:
--------------------------------------------------------------------------------
1 | package i2p.bote.android.util;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.content.res.TypedArray;
6 | import android.os.Build;
7 | import android.preference.Preference;
8 | import android.util.AttributeSet;
9 |
10 | import com.mikepenz.iconics.IconicsDrawable;
11 |
12 | import i2p.bote.android.R;
13 |
14 | public class IconicsPreference extends Preference {
15 | public IconicsPreference(Context context, AttributeSet attrs) {
16 | super(context, attrs);
17 | init(context, attrs);
18 | }
19 |
20 | public IconicsPreference(Context context, AttributeSet attrs, int defStyle) {
21 | super(context, attrs, defStyle);
22 | init(context, attrs);
23 | }
24 |
25 | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
26 | private void init(Context context, AttributeSet attrs) {
27 | // Icons only work on API 11+
28 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB)
29 | return;
30 |
31 | TypedArray a = context.obtainStyledAttributes(attrs,
32 | R.styleable.IconicsPreference, 0, 0);
33 | String iconName = a.getString(R.styleable.IconicsPreference_ip_icon);
34 | if (iconName == null)
35 | return;
36 |
37 | IconicsDrawable icon = new IconicsDrawable(context, iconName);
38 | int color = a.getColor(R.styleable.IconicsPreference_ip_color, 0);
39 | if (color != 0)
40 | icon.color(color);
41 | int size = a.getDimensionPixelSize(R.styleable.IconicsPreference_ip_size, 0);
42 | if (size != 0)
43 | icon.sizePx(size);
44 | int padding = a.getDimensionPixelSize(R.styleable.IconicsPreference_ip_padding, 0);
45 | if (padding != 0)
46 | icon.paddingPx(padding);
47 |
48 | a.recycle();
49 | setIcon(icon);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/listitem_attachment.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
18 |
19 |
26 |
27 |
33 |
34 |
40 |
41 |
42 |
47 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_set_password.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
18 |
19 |
20 |
21 |
22 |
29 |
30 |
37 |
38 |
44 |
45 |
50 |
51 |
--------------------------------------------------------------------------------
/app/src/main/java/org/sufficientlysecure/htmltextview/LocalImageGetter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 Dominik Schürmann
3 | * Copyright (C) 2014 drawk
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package org.sufficientlysecure.htmltextview;
19 |
20 | import android.content.Context;
21 | import android.graphics.drawable.Drawable;
22 | import android.text.Html;
23 | import android.util.Log;
24 |
25 | /**
26 | * Copied from http://stackoverflow.com/a/22298833
27 | */
28 | public class LocalImageGetter implements Html.ImageGetter {
29 | Context c;
30 |
31 | public LocalImageGetter(Context c) {
32 | this.c = c;
33 | }
34 |
35 | public Drawable getDrawable(String source) {
36 | int id = c.getResources().getIdentifier(source, "drawable", c.getPackageName());
37 |
38 | if (id == 0) {
39 | // the drawable resource wasn't found in our package, maybe it is a stock android drawable?
40 | id = c.getResources().getIdentifier(source, "drawable", "android");
41 | }
42 |
43 | if (id == 0) {
44 | // prevent a crash if the resource still can't be found
45 | Log.e(HtmlTextView.TAG, "source could not be found: " + source);
46 | return null;
47 | } else {
48 | Drawable d = c.getResources().getDrawable(id);
49 | d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
50 | return d;
51 | }
52 | }
53 |
54 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_list_identities.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
12 |
17 |
18 |
22 |
23 |
31 |
32 |
33 |
34 |
46 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_import_identities.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
17 |
18 |
24 |
25 |
31 |
32 |
38 |
39 |
45 |
46 |
51 |
--------------------------------------------------------------------------------
/app/src/main/java/i2p/bote/android/identities/IdentityListActivity.java:
--------------------------------------------------------------------------------
1 | package i2p.bote.android.identities;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.support.v7.widget.Toolbar;
7 |
8 | import i2p.bote.android.BoteActivityBase;
9 | import i2p.bote.android.R;
10 | import i2p.bote.email.EmailIdentity;
11 |
12 | public class IdentityListActivity extends BoteActivityBase implements
13 | IdentityListFragment.OnIdentitySelectedListener {
14 | static final int ALTER_IDENTITY_LIST = 1;
15 |
16 | @Override
17 | public void onCreate(Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 | setContentView(R.layout.activity_toolbar);
20 |
21 | // Set the action bar
22 | Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
23 | setSupportActionBar(toolbar);
24 |
25 | // Enable ActionBar app icon to behave as action to go back
26 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);
27 |
28 | if (savedInstanceState == null) {
29 | IdentityListFragment f = new IdentityListFragment();
30 | getSupportFragmentManager().beginTransaction()
31 | .add(R.id.container, f).commit();
32 | }
33 | }
34 |
35 | @Override
36 | public void onIdentitySelected(EmailIdentity identity) {
37 | Intent i = new Intent(this, ViewIdentityActivity.class);
38 | i.putExtra(ViewIdentityFragment.ADDRESS, identity.getKey());
39 | startActivityForResult(i, ALTER_IDENTITY_LIST);
40 | }
41 |
42 | @Override
43 | public void onActivityResult(int requestCode, int resultCode, Intent data) {
44 | if (requestCode == ALTER_IDENTITY_LIST) {
45 | if (resultCode == Activity.RESULT_OK) {
46 | IdentityListFragment f = (IdentityListFragment) getSupportFragmentManager().findFragmentById(R.id.container);
47 | f.updateIdentityList();
48 | }
49 | } else {
50 | super.onActivityResult(requestCode, resultCode, data);
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/CHANGELOG:
--------------------------------------------------------------------------------
1 | 0.6 / 2015-06-21 / 77d67b4f2d465a5c528fb31af5785d176276b94b
2 | * Identity switcher
3 | * Language can be configured
4 | * Block screenshots by default
5 | * Fixed internal router
6 | * Reorganized settings
7 | * UI improvements, bug fixes, translation updates
8 |
9 | 0.5 / 2015-01-13 / f5eaab42b6faa25450b2772315c7849f1eb4a03a
10 | * Attachments!
11 | * Email content can be selected/copied (API 11+)
12 | * Cc: and Bcc: fields
13 | * Improved performance of email and contact lists
14 | * Use ripple effect on API 21+
15 | * Much better support for legacy devices
16 | * Help and About screens
17 | * UI improvements, bug fixes, translation updates
18 |
19 | 0.4 / 2014-12-19 / fdc9c619eff53745d49c987b353ce5b04278dfd6
20 | * New email notifications!
21 | * Fixed crash when clicking "Create new contact" button
22 | * Fixed crash when sending email
23 | * Fixed occasional crashes when Bote connects to / disconnects from the network
24 | * Added "Copy to clipboard" button to identities and contacts
25 | * Added labels to the address book actions menu
26 | * UI improvements, bug fixes, translation updates
27 |
28 | 0.3 / 2014-12-01 / ebba8aac78d50eda9d25f936e8bd348553966649
29 | * Migrated to new Material design from Android Lollipop
30 | * Overhauled password usability
31 | * Identicons for contacts without pictures
32 | * Use new-style swipe for checking emails
33 | * Users can now check emails from menu, or swipe on empty inbox
34 | * "Forward" and "Reply all" actions for emails
35 | * QR codes for sharing identities
36 | * Separated viewing and editing contacts
37 | * Improved network information page
38 | * Bug fixes, translation updates
39 |
40 | 0.3-rc3 / 2014-08-23 / 6e7655836552d13d6fa6b7512e7dcf77cb3d413b
41 | * Test release to Norway on Google Play
42 |
43 | 0.2 / 2014-07-10 / 6c9c580bf6272db575091a1b48f66315535380e0
44 | * Fixed locale hiding in replies
45 | * New notification icons
46 | * New translation
47 | * UI improvements and bug fixes
48 |
49 | 0.1.1 / 2014-06-20 / a9c83d573dc3c5fbeeb288b347f996f440b33621
50 | * Fixed bugs in identity creation and password setting
51 |
52 | 0.1 / 2014-06-19 / 8457f58f367a2f362bdacd470c792b4bcd6d42f6
53 | * Initial release
54 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
11 |
18 |
25 |
32 |
39 |
46 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_list_emails.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
12 |
17 |
18 |
22 |
23 |
31 |
32 |
33 |
34 |
50 |
--------------------------------------------------------------------------------
/app/src/main/java/i2p/bote/android/NewEmailActivity.java:
--------------------------------------------------------------------------------
1 | package i2p.bote.android;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.widget.Toolbar;
5 | import android.widget.Toast;
6 |
7 | public class NewEmailActivity extends BoteActivityBase implements
8 | NewEmailFragment.Callbacks {
9 | @Override
10 | protected void onCreate(Bundle savedInstanceState) {
11 | super.onCreate(savedInstanceState);
12 | setContentView(R.layout.activity_toolbar);
13 |
14 | // Set the action bar
15 | Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
16 | setSupportActionBar(toolbar);
17 |
18 | // Enable ActionBar app icon to behave as action to go back
19 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);
20 |
21 | if (savedInstanceState == null) {
22 | NewEmailFragment f;
23 | String quoteMsgFolder = null;
24 | String quoteMsgId = null;
25 | NewEmailFragment.QuoteMsgType quoteMsgType = null;
26 | Bundle args = getIntent().getExtras();
27 | if (args != null) {
28 | quoteMsgFolder = args.getString(NewEmailFragment.QUOTE_MSG_FOLDER);
29 | quoteMsgId = args.getString(NewEmailFragment.QUOTE_MSG_ID);
30 | quoteMsgType =
31 | (NewEmailFragment.QuoteMsgType) args.getSerializable(NewEmailFragment.QUOTE_MSG_TYPE);
32 | }
33 | f = NewEmailFragment.newInstance(quoteMsgFolder, quoteMsgId, quoteMsgType);
34 | getSupportFragmentManager().beginTransaction()
35 | .add(R.id.container, f).commit();
36 | }
37 | }
38 |
39 | @Override
40 | public void onBackPressed() {
41 | NewEmailFragment f = (NewEmailFragment) getSupportFragmentManager().findFragmentById(R.id.container);
42 | f.onBackPressed();
43 | }
44 |
45 | // NewEmailFragment.Callbacks
46 |
47 | public void onTaskFinished() {
48 | Toast.makeText(this, R.string.email_queued_for_sending,
49 | Toast.LENGTH_SHORT).show();
50 | finish();
51 | }
52 |
53 | @Override
54 | public void onBackPressAllowed() {
55 | super.onBackPressed();
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/src/main/res/raw/help_changelog.html:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 | 0.5
10 |
11 | Attachments!
12 | Email content can be selected/copied (API 11+)
13 | Cc: and Bcc: fields
14 | Improved performance of email and contact lists
15 | Use ripple effect on API 21+
16 | Much better support for legacy devices
17 | Help and About screens
18 | UI improvements, bug fixes, translation updates
19 |
20 |
21 | 0.4
22 |
23 | New email notifications!
24 | Fixed crash when clicking "Create new contact" button
25 | Fixed crash when sending email
26 | Fixed occasional crashes when Bote connects to / disconnects from the network
27 | Added "Copy to clipboard" button to identities and contacts
28 | Added labels to the address book actions menu
29 | UI improvements, bug fixes, translation updates
30 |
31 |
32 | 0.3
33 |
34 | Migrated to new Material design from Android Lollipop
35 | Overhauled password usability
36 | Identicons for contacts without pictures
37 | Use new-style swipe for checking emails
38 | Users can now check emails from menu, or swipe on empty inbox
39 | "Forward" and "Reply all" actions for emails
40 | QR codes for sharing identities
41 | Separated viewing and editing contacts
42 | Improved network information page
43 | Bug fixes, translation updates
44 |
45 |
46 | 0.3-rc3
47 |
48 | Test release to Norway on Google Play
49 |
50 |
51 | 0.2
52 |
53 | Fixed locale hiding in replies
54 | New notification icons
55 | New translation
56 | UI improvements and bug fixes
57 |
58 |
59 | 0.1.1
60 |
61 | Fixed bugs in identity creation and password setting
62 |
63 |
64 | 0.1
65 |
66 | Initial release
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_intro_5.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
18 |
19 |
29 |
30 |
40 |
41 |
49 |
50 |
--------------------------------------------------------------------------------
/app/src/main/java/i2p/bote/android/util/TaskFragment.java:
--------------------------------------------------------------------------------
1 | package i2p.bote.android.util;
2 |
3 | import android.os.Bundle;
4 | import android.support.v4.app.DialogFragment;
5 |
6 | public class TaskFragment extends DialogFragment {
7 | RobustAsyncTask mTask;
8 |
9 | public void setTask(RobustAsyncTask task) {
10 | mTask = task;
11 | mTask.setFragment(this);
12 | }
13 |
14 | @Override
15 | public void onCreate(Bundle savedInstanceState) {
16 | super.onCreate(savedInstanceState);
17 |
18 | // Retain this instance so it isn't destroyed
19 | setRetainInstance(true);
20 |
21 | // Start the task
22 | if (mTask != null)
23 | mTask.execute(getParams());
24 | }
25 |
26 | // This is to work around what is apparently a bug. If you don't have it
27 | // here the dialog will be dismissed on rotation, so tell it not to dismiss.
28 | @Override
29 | public void onDestroyView() {
30 | if (getDialog() != null && getRetainInstance())
31 | getDialog().setDismissMessage(null);
32 | super.onDestroyView();
33 | }
34 |
35 | @Override
36 | public void onResume()
37 | {
38 | super.onResume();
39 | // This is a little hacky, but we will see if the task has finished
40 | // while we weren't in this activity, and then we can dismiss ourselves.
41 | if (mTask == null)
42 | dismiss();
43 | }
44 |
45 | public Params[] getParams() {
46 | return null;
47 | }
48 |
49 | public void updateProgress(Progress... values) {}
50 |
51 | public void taskFinished(Result result) {
52 | finishTask();
53 | }
54 |
55 | public void taskCancelled(Result result) {
56 | finishTask();
57 | }
58 |
59 | private void finishTask() {
60 | // Make sure we check if it is resumed because we will crash if trying
61 | // to dismiss the dialog after the user has switched to another app.
62 | if (isResumed())
63 | dismiss();
64 |
65 | // If we aren't resumed, setting the task to null will allow us to
66 | // dismiss ourselves in onResume().
67 | mTask = null;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/app/src/main/res/layout-land/fragment_intro_3.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
19 |
20 |
26 |
27 |
31 |
32 |
39 |
40 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/layout-land/fragment_intro_2.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
19 |
20 |
26 |
27 |
31 |
32 |
39 |
40 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/layout-land/fragment_intro_4.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
19 |
20 |
26 |
27 |
31 |
32 |
39 |
40 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/layout-land/fragment_intro_1.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
19 |
20 |
26 |
27 |
31 |
32 |
39 |
40 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/raw/router_config:
--------------------------------------------------------------------------------
1 | # initial router.config
2 | # duplicated from I2P Android
3 | # temp directory now set in Init.java
4 | #i2p.dir.temp=/data/data/net.i2p.android.router/files/tmp
5 | #i2p.dir.pid=/data/data/net.i2p.android.router/files/tmp
6 | #
7 | # save memory
8 | # I've done some testing, and the amount 'saved' is not all that much.
9 | # I will, however, leave this alone, for now.
10 | #
11 | prng.buffers=2
12 | prng.bufferSize=32768
13 | router.decayingBloomFilterM=20
14 | stat.full=false
15 | #
16 | # Don't run NTP client, the phone should have a valid time
17 | # Tablets are not phones, turn this on. I've seen the phone not have
18 | # accurate time too (yes, really) and the router had too much skew.
19 | # I've even seen current tablets that have NTP on lose or gain time.
20 | time.disabled=false
21 | #
22 | # no external I2CP (7654)
23 | #
24 | i2cp.disableInterface=true
25 | #
26 | # hosts.txt should be more crash-proof than blockfile
27 | # Todo: implement a NamingService using the android native SQLite
28 | #
29 | i2p.naming.impl=net.i2p.client.naming.HostsTxtNamingService
30 | i2p.hostsfilelist=privatehosts.txt,hosts.txt
31 | #
32 | ##### Tunnels
33 | #
34 | router.inboundPool.backupQuantity=0
35 | router.inboundPool.length=2
36 | router.inboundPool.lengthVariance=0
37 | router.inboundPool.quantity=2
38 | router.outboundPool.backupQuantity=0
39 | router.outboundPool.length=2
40 | router.outboundPool.lengthVariance=0
41 | router.outboundPool.quantity=2
42 | router.maxParticipatingTunnels=0
43 | router.sharePercentage=10
44 | #
45 | ##### Transport
46 | #
47 | i2np.bandwidth.inboundKBytesPerSecond=100
48 | i2np.bandwidth.outboundKBytesPerSecond=50
49 | #
50 | # NTCP
51 | #
52 | i2np.ntcp.enable=true
53 | i2np.ntcp.maxConnections=32
54 | #
55 | # UDP
56 | #
57 | i2np.udp.enable=true
58 | i2np.udp.maxConnections=32
59 | #
60 | #
61 | # not on android
62 | routerconsole.geoip.enable=false
63 | #
64 | # false is default but was true in 0.9.1-0_b1 release so have to set it back
65 | router.reseedSSLDisable=false
66 | #
67 | # Tablets are not phones. It does not hurt to have this on by default.
68 | #
69 | #i2np.upnp.enable=true
70 |
71 | #
72 | # Hidden by default, this turns off sharing, etc.
73 | # bad idea, reduces user experience.
74 | router.hiddenMode=false
75 |
76 | #
77 | # Bad idea, disable it!
78 | #
79 | router.floodfillParticipant=false
80 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_edit_identity.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
26 |
27 |
35 |
36 |
43 |
44 |
51 |
52 |
58 |
59 |
--------------------------------------------------------------------------------
/app/src/main/java/i2p/bote/android/HelpAboutFragment.java:
--------------------------------------------------------------------------------
1 | package i2p.bote.android;
2 |
3 | import android.content.pm.PackageInfo;
4 | import android.content.pm.PackageManager;
5 | import android.content.pm.PackageManager.NameNotFoundException;
6 | import android.os.Bundle;
7 | import android.support.v4.app.Fragment;
8 | import android.util.Log;
9 | import android.view.LayoutInflater;
10 | import android.view.View;
11 | import android.view.ViewGroup;
12 | import android.widget.TextView;
13 |
14 | import org.sufficientlysecure.htmltextview.HtmlTextView;
15 |
16 |
17 | public class HelpAboutFragment extends Fragment {
18 |
19 | @Override
20 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
21 | View view = inflater.inflate(R.layout.fragment_help_about, container, false);
22 |
23 | TextView versionText = (TextView) view.findViewById(R.id.help_about_version);
24 | versionText.setText(getString(R.string.version) + " " + getVersion());
25 |
26 | TextView licenseText = (TextView) view.findViewById(R.id.help_about_license);
27 | licenseText.setText(getString(R.string.license, "GPLv3+"));
28 |
29 | HtmlTextView aboutLibsView = (HtmlTextView) view.findViewById(R.id.help_about_libraries);
30 |
31 | // load html from raw resource (Parsing handled by HtmlTextView library)
32 | aboutLibsView.setHtmlFromRawResource(getActivity(), R.raw.help_about_libraries, true);
33 |
34 | // no flickering when clicking textview for Android < 4
35 | aboutLibsView.setTextColor(getResources().getColor(android.R.color.black));
36 |
37 | return view;
38 | }
39 |
40 | /**
41 | * Get the current package version.
42 | *
43 | * @return The current version.
44 | */
45 | private String getVersion() {
46 | String result = "";
47 | try {
48 | PackageManager manager = getActivity().getPackageManager();
49 | PackageInfo info = manager.getPackageInfo(getActivity().getPackageName(), 0);
50 |
51 | result = String.format("%s (%s)", info.versionName, info.versionCode);
52 | } catch (NameNotFoundException e) {
53 | Log.w(Constants.ANDROID_LOG_TAG, "Unable to get application version: " + e.getMessage());
54 | result = "Unable to get application version.";
55 | }
56 |
57 | return result;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/main/res/layout-land/fragment_intro_5.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
19 |
20 |
25 |
26 |
34 |
35 |
43 |
44 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/app/src/main/java/i2p/bote/android/util/LocaleManager.java:
--------------------------------------------------------------------------------
1 | package i2p.bote.android.util;
2 |
3 | import android.app.Activity;
4 | import android.app.Service;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.res.Configuration;
8 | import android.preference.PreferenceManager;
9 | import android.text.TextUtils;
10 |
11 | import java.util.Locale;
12 |
13 | public class LocaleManager {
14 | private Locale currentLocale;
15 |
16 | public void onCreate(Activity activity) {
17 | currentLocale = getSelectedLocale(activity);
18 | setContextLocale(activity, currentLocale);
19 | }
20 |
21 | public void onResume(Activity activity) {
22 | // If the activity has the incorrect locale, restart it
23 | if (!currentLocale.equals(getSelectedLocale(activity))) {
24 | Intent intent = activity.getIntent();
25 | activity.finish();
26 | activity.overridePendingTransition(0, 0);
27 | activity.startActivity(intent);
28 | activity.overridePendingTransition(0, 0);
29 | }
30 | }
31 |
32 | public void updateServiceLocale(Service service) {
33 | currentLocale = getSelectedLocale(service);
34 | setContextLocale(service, currentLocale);
35 | }
36 |
37 | private static Locale getSelectedLocale(Context context) {
38 | String defaultLanguage = "zz";
39 | String selectedLanguage = PreferenceManager.getDefaultSharedPreferences(context).getString(
40 | "pref_language", defaultLanguage
41 | );
42 | String language[] = TextUtils.split(selectedLanguage, "_");
43 |
44 | if (language[0].equals(defaultLanguage))
45 | return Locale.getDefault();
46 | else if (language.length == 2)
47 | return new Locale(language[0], language[1]);
48 | else
49 | return new Locale(language[0]);
50 | }
51 |
52 | private static void setContextLocale(Context context, Locale selectedLocale) {
53 | Configuration configuration = context.getResources().getConfiguration();
54 | if (!configuration.locale.equals(selectedLocale)) {
55 | configuration.locale = selectedLocale;
56 | context.getResources().updateConfiguration(
57 | configuration,
58 | context.getResources().getDisplayMetrics()
59 | );
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_edit_contact.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
26 |
27 |
34 |
35 |
44 |
45 |
53 |
54 |
60 |
61 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_intro_0.xml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
19 |
20 |
27 |
28 |
35 |
36 |
44 |
45 |
53 |
54 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_list_emails_with_refresh.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
12 |
13 |
16 |
17 |
22 |
23 |
27 |
28 |
36 |
37 |
38 |
39 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/.github/workflows/sync.yaml:
--------------------------------------------------------------------------------
1 | # GitHub Actions workflow file to sync an external repository to this GitHub mirror.
2 | # This file was automatically generated by go-github-sync.
3 | #
4 | # The workflow does the following:
5 | # - Runs on a scheduled basis (and can also be triggered manually)
6 | # - Clones the GitHub mirror repository
7 | # - Fetches changes from the primary external repository
8 | # - Applies those changes to the mirror repository
9 | # - Pushes the updated content back to the GitHub mirror
10 | #
11 | # Authentication is handled by the GITHUB_TOKEN secret provided by GitHub Actions.
12 |
13 | jobs:
14 | sync:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - name: Validate Github Actions Environment
18 | run: if [ "$GITHUB_ACTIONS" != "true" ]; then echo 'This script must be run in a GitHub Actions environment.'; exit 1; fi
19 | - name: Checkout GitHub Mirror
20 | uses: actions/checkout@v3
21 | with:
22 | fetch-depth: 0
23 | - name: Configure Git
24 | run: |-
25 | git config user.name 'GitHub Actions'
26 | git config user.email 'actions@github.com'
27 | - env:
28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
29 | name: Sync Primary Repository
30 | run: |-
31 | # Add the primary repository as a remote
32 | git remote add primary https://i2pgit.org/I2P_Developers/i2p.i2p-bote.android.git
33 |
34 | # Fetch the latest changes from the primary repository
35 | git fetch primary
36 |
37 | # Check if the primary branch exists in the primary repository
38 | if git ls-remote --heads primary master | grep -q master; then
39 | echo "Primary branch master found in primary repository"
40 | else
41 | echo "Error: Primary branch master not found in primary repository"
42 | exit 1
43 | fi
44 |
45 | # Check if we're already on the mirror branch
46 | if git rev-parse --verify --quiet master; then
47 | git checkout master
48 | else
49 | # Create the mirror branch if it doesn't exist
50 | git checkout -b master
51 | fi
52 |
53 |
54 | # Force-apply all changes from primary, overriding any conflicts
55 | echo "Performing force sync from primary/master to master"
56 | git reset --hard primary/master
57 |
58 |
59 | # Push changes back to the mirror repository
60 | git push origin master
61 | name: Sync Primary Repository to GitHub Mirror
62 | "on":
63 | push: {}
64 | schedule:
65 | - cron: 0 * * * *
66 | workflow_dispatch: {}
67 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/app/src/main/java/i2p/bote/android/identities/ViewIdentityActivity.java:
--------------------------------------------------------------------------------
1 | package i2p.bote.android.identities;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.nfc.NdefMessage;
5 | import android.nfc.NfcAdapter;
6 | import android.nfc.NfcEvent;
7 | import android.os.Build;
8 | import android.os.Bundle;
9 |
10 | import i2p.bote.android.BoteActivityBase;
11 |
12 | public class ViewIdentityActivity extends BoteActivityBase {
13 | NfcAdapter mNfcAdapter;
14 |
15 | @SuppressLint("NewApi")
16 | @Override
17 | public void onCreate(Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 |
20 | if (savedInstanceState == null) {
21 | String key = null;
22 | Bundle args = getIntent().getExtras();
23 | if (args != null)
24 | key = args.getString(ViewIdentityFragment.ADDRESS);
25 |
26 | if (key == null) {
27 | setResult(RESULT_CANCELED);
28 | finish();
29 | return;
30 | }
31 |
32 | ViewIdentityFragment f = ViewIdentityFragment.newInstance(key);
33 | getSupportFragmentManager().beginTransaction()
34 | .add(android.R.id.content, f).commit();
35 | }
36 |
37 | // NFC send only works on API 10+
38 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1) {
39 | mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
40 | if (mNfcAdapter != null &&
41 | Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
42 | mNfcAdapter.setNdefPushMessageCallback(new NfcAdapter.CreateNdefMessageCallback() {
43 | @Override
44 | public NdefMessage createNdefMessage(NfcEvent nfcEvent) {
45 | return getNdefMessage();
46 | }
47 | }, this);
48 | }
49 | }
50 |
51 | @SuppressWarnings("deprecation")
52 | @SuppressLint("NewApi")
53 | @Override
54 | public void onResume() {
55 | super.onResume();
56 |
57 | if (mNfcAdapter != null &&
58 | Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
59 | mNfcAdapter.enableForegroundNdefPush(this, getNdefMessage());
60 | }
61 | }
62 |
63 | private NdefMessage getNdefMessage() {
64 | ViewIdentityFragment f = (ViewIdentityFragment) getSupportFragmentManager()
65 | .findFragmentById(android.R.id.content);
66 | return f.createNdefMessage();
67 | }
68 |
69 | @SuppressWarnings("deprecation")
70 | @SuppressLint("NewApi")
71 | @Override
72 | public void onPause() {
73 | super.onPause();
74 |
75 | if (mNfcAdapter != null &&
76 | Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
77 | mNfcAdapter.disableForegroundNdefPush(this);
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/app/src/main/java/i2p/bote/android/config/PrivacyPreferenceFragment.java:
--------------------------------------------------------------------------------
1 | package i2p.bote.android.config;
2 |
3 | import android.content.SharedPreferences;
4 | import android.os.Bundle;
5 | import android.preference.ListPreference;
6 | import android.preference.Preference;
7 | import android.preference.PreferenceManager;
8 | import android.support.v4.preference.PreferenceFragment;
9 |
10 | import java.util.Map;
11 |
12 | import i2p.bote.Configuration;
13 | import i2p.bote.I2PBote;
14 | import i2p.bote.android.R;
15 |
16 | public class PrivacyPreferenceFragment extends PreferenceFragment {
17 | @Override
18 | public void onCreate(Bundle paramBundle) {
19 | super.onCreate(paramBundle);
20 | addPreferencesFromResource(R.xml.settings_privacy);
21 | setupPrivacySettings();
22 | }
23 |
24 | @Override
25 | public void onResume() {
26 | super.onResume();
27 | //noinspection ConstantConditions
28 | ((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.pref_title_privacy);
29 | }
30 |
31 | @Override
32 | public void onPause() {
33 | Configuration config = I2PBote.getInstance().getConfiguration();
34 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
35 |
36 | Map all = prefs.getAll();
37 | for (String x : all.keySet()) {
38 | if ("hideLocale".equals(x))
39 | config.setHideLocale(prefs.getBoolean(x, true));
40 | else if ("includeSentTime".equals(x))
41 | config.setIncludeSentTime(prefs.getBoolean(x, true));
42 | else if ("numSendHops".equals(x))
43 | config.setNumStoreHops(Integer.parseInt(prefs.getString(x, "0")));
44 | else if ("relayMinDelay".equals(x))
45 | config.setRelayMinDelay(prefs.getInt(x, 5));
46 | else if ("relayMaxDelay".equals(x))
47 | config.setRelayMaxDelay(prefs.getInt(x, 40));
48 | }
49 |
50 | config.save();
51 |
52 | // Store the settings in Android
53 | super.onPause();
54 | }
55 |
56 | private void setupPrivacySettings() {
57 | ListPreference numSendHops = (ListPreference) findPreference("numSendHops");
58 | int value = Integer.valueOf(numSendHops.getValue());
59 | numSendHops.setSummary(getResources().getQuantityString(R.plurals.pref_summ_numHops,
60 | value, value));
61 | numSendHops.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
62 | @Override
63 | public boolean onPreferenceChange(Preference preference, Object newValue) {
64 | int value = Integer.valueOf((String) newValue);
65 | preference.setSummary(getResources().getQuantityString(R.plurals.pref_summ_numHops,
66 | value, value));
67 | return true;
68 | }
69 | });
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/app/src/main/java/i2p/bote/android/addressbook/ViewContactActivity.java:
--------------------------------------------------------------------------------
1 | package i2p.bote.android.addressbook;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.nfc.NdefMessage;
5 | import android.nfc.NfcAdapter;
6 | import android.nfc.NfcEvent;
7 | import android.os.Build;
8 | import android.os.Bundle;
9 |
10 | import i2p.bote.android.BoteActivityBase;
11 |
12 | public class ViewContactActivity extends BoteActivityBase {
13 | NfcAdapter mNfcAdapter;
14 |
15 | @SuppressLint("NewApi")
16 | @Override
17 | public void onCreate(Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 |
20 | if (savedInstanceState == null) {
21 | String destination = null;
22 | Bundle args = getIntent().getExtras();
23 | if (args != null)
24 | destination = args.getString(ViewContactFragment.ADDRESS);
25 |
26 | if (destination == null) {
27 | setResult(RESULT_CANCELED);
28 | finish();
29 | return;
30 | }
31 |
32 | ViewContactFragment f = ViewContactFragment.newInstance(destination);
33 | getSupportFragmentManager().beginTransaction()
34 | .add(android.R.id.content, f).commit();
35 | }
36 |
37 | // NFC send only works on API 10+
38 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1) {
39 | mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
40 | if (mNfcAdapter != null &&
41 | Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
42 | mNfcAdapter.setNdefPushMessageCallback(new NfcAdapter.CreateNdefMessageCallback() {
43 | @Override
44 | public NdefMessage createNdefMessage(NfcEvent nfcEvent) {
45 | return getNdefMessage();
46 | }
47 | }, this);
48 | }
49 | }
50 |
51 | @SuppressWarnings("deprecation")
52 | @SuppressLint("NewApi")
53 | @Override
54 | public void onResume() {
55 | super.onResume();
56 |
57 | if (mNfcAdapter != null &&
58 | Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
59 | mNfcAdapter.enableForegroundNdefPush(this, getNdefMessage());
60 | }
61 | }
62 |
63 | private NdefMessage getNdefMessage() {
64 | ViewContactFragment f = (ViewContactFragment) getSupportFragmentManager()
65 | .findFragmentById(android.R.id.content);
66 | return f.createNdefMessage();
67 | }
68 |
69 | @SuppressWarnings("deprecation")
70 | @SuppressLint("NewApi")
71 | @Override
72 | public void onPause() {
73 | super.onPause();
74 |
75 | if (mNfcAdapter != null &&
76 | Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
77 | mNfcAdapter.disableForegroundNdefPush(this);
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_network_error.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
16 |
21 |
22 |
28 |
29 |
35 |
36 |
42 |
43 |
50 |
51 |
52 |
58 |
59 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/app/src/main/java/i2p/bote/android/widget/LoadingRecyclerView.java:
--------------------------------------------------------------------------------
1 | package i2p.bote.android.widget;
2 |
3 | import android.content.Context;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.util.AttributeSet;
6 | import android.view.View;
7 |
8 | import com.pnikosis.materialishprogress.ProgressWheel;
9 |
10 | public class LoadingRecyclerView extends RecyclerView {
11 | private View mLoadingView;
12 | private ProgressWheel mLoadingWheel;
13 | private boolean mLoading;
14 | final private AdapterDataObserver observer = new AdapterDataObserver() {
15 | @Override
16 | public void onChanged() {
17 | mLoading = false;
18 | updateLoading();
19 | }
20 |
21 | @Override
22 | public void onItemRangeInserted(int positionStart, int itemCount) {
23 | mLoading = false;
24 | updateLoading();
25 | }
26 |
27 | @Override
28 | public void onItemRangeRemoved(int positionStart, int itemCount) {
29 | mLoading = false;
30 | updateLoading();
31 | }
32 | };
33 |
34 | public LoadingRecyclerView(Context context) {
35 | super(context);
36 | }
37 |
38 | public LoadingRecyclerView(Context context, AttributeSet attrs) {
39 | super(context, attrs);
40 | }
41 |
42 | public LoadingRecyclerView(Context context, AttributeSet attrs, int defStyle) {
43 | super(context, attrs, defStyle);
44 | }
45 |
46 | private void updateLoading() {
47 | if (mLoadingView != null) {
48 | mLoadingView.setVisibility(mLoading ? VISIBLE : GONE);
49 | setVisibility(mLoading ? GONE : VISIBLE);
50 | if (mLoadingWheel != null) {
51 | if (mLoading && !mLoadingWheel.isSpinning())
52 | mLoadingWheel.spin();
53 | else if (!mLoading && mLoadingWheel.isSpinning())
54 | mLoadingWheel.stopSpinning();
55 | }
56 | }
57 | }
58 |
59 | @Override
60 | public void setAdapter(Adapter adapter) {
61 | final Adapter oldAdapter = getAdapter();
62 | if (oldAdapter != null) {
63 | oldAdapter.unregisterAdapterDataObserver(observer);
64 | }
65 | super.setAdapter(adapter);
66 | if (adapter != null) {
67 | adapter.registerAdapterDataObserver(observer);
68 | }
69 | }
70 |
71 | /**
72 | * Set the views to use for showing state.
73 | *
74 | * This method also sets the state to "loading".
75 | *
76 | * @param loadingView The view to show in place of the RecyclerView while loading.
77 | * @param progressWheel The indeterminate ProgressWheel to spin while loading, if any.
78 | */
79 | public void setLoadingView(View loadingView, ProgressWheel progressWheel) {
80 | mLoadingView = loadingView;
81 | mLoadingWheel = progressWheel;
82 | setLoading(true);
83 | }
84 |
85 | public void setLoading(boolean loading) {
86 | mLoading = loading;
87 | updateLoading();
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # I2P-Bote for Android
2 |
3 | Bote is an Android port of I2P-Bote.
4 |
5 | ## Build process
6 |
7 | ### Dependencies:
8 |
9 | - Java SDK (preferably Oracle/Sun or OpenJDK) 1.6.0 or higher
10 | - Apache Ant 1.8.0 or higher
11 | - [I2P source](https://github.com/i2p/i2p.i2p)
12 | - [I2P-Bote source](https://github.com/i2p/i2p.i2p-bote)
13 | - Android SDK 21
14 | - Android Build Tools 21.1.1
15 | - Android Support Repository
16 | - Gradle 2.2.1
17 |
18 | ### Gradle
19 |
20 | The build system is based on Gradle. There are several methods for setting Gradle up:
21 |
22 | * It can be downloaded from [the Gradle website](http://www.gradle.org/downloads).
23 |
24 | * Most distributions will have Gradle packages. Be careful to check the provided version; Debian and Ubuntu have old versions in their main repositories. There is a [PPA](https://launchpad.net/~cwchien/+archive/gradle) for Ubuntu with the latest version of Gradle.
25 |
26 | * A Gradle wrapper is provided in the codebase. It takes all the same commands as the regular `gradle` command. The first time that any command is run, it will automatically download, cache and use the correct version of Gradle. This is the simplest way to get started with the codebase. To use it, replace `gradle` with `./gradlew` (or `./gradlew.bat` on Windows) in the commands below.
27 |
28 | Gradle will pull dependencies over the clearnet by default. To use Tor, create a `gradle.properties` file in `i2p.android.base` containing:
29 |
30 | ```
31 | systemProp.socksProxyHost=localhost
32 | systemProp.socksProxyPort=9150
33 | ```
34 |
35 | ### Preparation
36 |
37 | 1. Install I2P. You need the installed libraries to build against.
38 |
39 | 2. Download the Android SDK. The simplest method is to download Android Studio.
40 |
41 | 3. Check out the `i2p.i2p-bote` and `i2p.i2p-bote.android` repositories.
42 |
43 | 4. Create a `local.properties` file in `i2p.i2p-bote.android/botejars` containing:
44 |
45 | ```
46 | i2pbase=/path/to/installed/i2p
47 | botesrc=/path/to/i2p.i2p-bote
48 | ```
49 |
50 | 5. If you want to use a local copy of the I2P Android client library, install it in your local Maven repository with:
51 |
52 | ```
53 | cd path/to/i2p.android.base
54 | ./gradlew client:installArchives
55 | ```
56 |
57 | ### Building from the command line
58 |
59 | 1. Create a `local.properties` file in `i2p.i2p-bote.android` containing:
60 |
61 | ```
62 | sdk.dir=/path/to/android-studio/sdk
63 | ```
64 |
65 | 2. `gradle assembleDebug`
66 |
67 | 3. The APK will be placed in `i2p.i2p-bote.android/app/build/apk`.
68 |
69 | ### Building with Android Studio
70 |
71 | 1. Import `i2p.i2p-bote.android` into Android Studio. (This creates the `local.properties` file automatically).
72 |
73 | 2. Build and run the app (`Shift+F10`).
74 |
75 | ### Signing release builds
76 |
77 | 1. Create a `signing.properties` file in `i2p.i2p-bote.android` containing:
78 |
79 | ```
80 | STORE_FILE=/path/to/android.keystore
81 | STORE_PASSWORD=store.password
82 | KEY_ALIAS=key.alias
83 | KEY_PASSWORD=key.password
84 | ```
85 |
86 | 2. `gradle assembleRelease`
87 |
--------------------------------------------------------------------------------
/TODO:
--------------------------------------------------------------------------------
1 | Fixes:
2 | - Auto-comma the To: field when it loses focus
3 | - Fix tick over selected emails
4 | - Delete/read/unread/move actions on view email page that don't break everything
5 | - Prevent router option being changed while Bote is running
6 | - Remove internal router
7 |
8 | Tasks:
9 | - Show logged-in status in persistent notification
10 | - Intro and setup
11 | - More layouts (tune for each screen size)
12 | - Subclass EmailListFragment for each default mailbox
13 | - First-run content of inbox should tell users about pull-to-refresh.
14 | - Cache Identicons for speed
15 | - Refactor code
16 | - Reorganize for clarity
17 | - Optimize use of Android lifecycles
18 |
19 | Silent Store approval checks to confirm/implement:
20 | - Known Vulnerabilities
21 | - Apps will be tested to ensure that they are not susceptible to known
22 | publicly disclosed vulnerabilities. For example:
23 | - Heartbleed
24 | - Poodle
25 | - MasterKey
26 | - Common Path Traversal attacks
27 | - Common SQL Injection attacks
28 | - Network Security Protocols
29 | - All Apps that require transmission of data from the App to a system that
30 | does not exist on the device must use, at a minimum, TLS1.1 standards.
31 | However, Blackphone would prefer the usage of TLS1.2.
32 | - Apps must not use algorithms for cryptographic purposes that are considered
33 | obsolete or outdated i.e. MD5, SHA1, RC4, DES, or any encryption algorithm
34 | that is weaker than AES128.
35 | - Transport Layer Protection
36 | - All network communication should be encrypted
37 | - Not vulnerable to SSl Strip
38 | - Data Leakage
39 | - No storage of sensitive data outside of application sandbox
40 | - Files should not be created with MODE_WORLD_READABLE or MODE_WORLD_WRITABLE
41 | - Copy & Paste will be evaluated on a case by case basis
42 | - App logs should not contain sensitive information
43 | - Authentication and Authorization
44 | - Validate that authentication credentials are not stored on the device
45 | - Must use an approved password-based key derivation function ie. PBKDF2, scrypt
46 | - Data-at-rest Encryption
47 | - Must use at a minimum AES128 with modes CCM or GCM
48 | - Should not store the encryption key on the file system
49 | - Permission Checks
50 | - The App must function with all permissions disabled
51 | - Apps must not hard crash if a permission is disabled
52 | - Apps should ask users to enable permissions that are disabled if needed to
53 | function properly and explain why the permission is necessary
54 | - Privacy Policy
55 | - Apps must have a privacy policy that details how customer data is used,
56 | stored, shared, etc...
57 | - Apps must be configured with the customer opted out by default
58 | - App logs should not contain PII
59 | - Error Handling
60 | - Apps should follow best-practices for error handling and logging
61 |
62 | Features:
63 | - Search
64 | - Fingerprints that users can compare to validate
65 | - Public address book lookup
66 | - "Write email" link in address book
67 | - "Empty trash" option in Trash folder
68 | - Overlay to explain
69 | - Option to run only when connected to an outlet
70 | - Option to run only when connected to wifi
71 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_help_about.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
16 |
17 |
21 |
22 |
28 |
29 |
33 |
34 |
39 |
40 |
46 |
47 |
48 |
49 |
55 |
56 |
62 |
63 |
69 |
70 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/app/src/main/java/i2p/bote/android/util/QrCodeUtils.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2014 str4d
3 | * Copyright (C) 2013-2014 Dominik Schürmann
4 | * Copyright (C) 2011 Andreas Schildbach
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program. If not, see .
18 | */
19 |
20 | package i2p.bote.android.util;
21 |
22 | import android.graphics.Bitmap;
23 | import android.graphics.Color;
24 |
25 | import com.google.zxing.BarcodeFormat;
26 | import com.google.zxing.EncodeHintType;
27 | import com.google.zxing.WriterException;
28 | import com.google.zxing.common.BitMatrix;
29 | import com.google.zxing.qrcode.QRCodeWriter;
30 | import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
31 |
32 | import net.i2p.I2PAppContext;
33 | import net.i2p.util.Log;
34 |
35 | import java.util.Hashtable;
36 |
37 | /**
38 | * Copied from OpenKeychain
39 | * Originally copied from Bitcoin Wallet
40 | */
41 | public class QrCodeUtils {
42 |
43 | /**
44 | * Generate Bitmap with QR Code based on input.
45 | *
46 | * @param input The data to render as a QR code.
47 | * @param size The preferred width and height of the QR code in pixels.
48 | * @return QR Code as Bitmap
49 | */
50 | public static Bitmap getQRCodeBitmap(final String input, final int size) {
51 | try {
52 | final Hashtable hints = new Hashtable();
53 | hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
54 | final BitMatrix result = new QRCodeWriter().encode(input, BarcodeFormat.QR_CODE, size,
55 | size, hints);
56 |
57 | final int width = result.getWidth();
58 | final int height = result.getHeight();
59 | final int[] pixels = new int[width * height];
60 |
61 | for (int y = 0; y < height; y++) {
62 | final int offset = y * width;
63 | for (int x = 0; x < width; x++) {
64 | pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.WHITE;
65 | }
66 | }
67 |
68 | final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
69 | bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
70 | return bitmap;
71 | } catch (final WriterException e) {
72 | Log log = I2PAppContext.getGlobalContext().logManager().getLog(QrCodeUtils.class);
73 | if (log.shouldLog(Log.ERROR))
74 | log.error("QrCodeUtils", e);
75 | return null;
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------