diff --git a/briar-android/src/main/AndroidManifest.xml b/briar-android/src/main/AndroidManifest.xml
index 1eac6796d..e610211dd 100644
--- a/briar-android/src/main/AndroidManifest.xml
+++ b/briar-android/src/main/AndroidManifest.xml
@@ -441,6 +441,11 @@
android:label="@string/pending_contact_requests"
android:theme="@style/BriarTheme" />
+
+
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java
index aeed5817f..2730c340b 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java
@@ -35,6 +35,7 @@ import org.briarproject.briar.BriarCoreModule;
import org.briarproject.briar.android.attachment.AttachmentModule;
import org.briarproject.briar.android.attachment.media.MediaModule;
import org.briarproject.briar.android.conversation.glide.BriarModelLoader;
+import org.briarproject.briar.android.hotspot.HotspotIntroFragment;
import org.briarproject.briar.android.logging.CachingLogHandler;
import org.briarproject.briar.android.login.SignInReminderReceiver;
import org.briarproject.briar.android.settings.ConnectionsFragment;
@@ -210,4 +211,6 @@ public interface AndroidComponent
void inject(SecurityFragment securityFragment);
void inject(NotificationsFragment notificationsFragment);
+
+ void inject(HotspotIntroFragment hotspotIntroFragment);
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java b/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java
index 1eeded75f..edb555f9c 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java
@@ -35,6 +35,7 @@ import org.briarproject.briar.android.blog.BlogModule;
import org.briarproject.briar.android.contact.ContactListModule;
import org.briarproject.briar.android.contact.add.nearby.AddNearbyContactModule;
import org.briarproject.briar.android.forum.ForumModule;
+import org.briarproject.briar.android.hotspot.HotspotModule;
import org.briarproject.briar.android.introduction.IntroductionModule;
import org.briarproject.briar.android.logging.LoggingModule;
import org.briarproject.briar.android.login.LoginModule;
@@ -92,6 +93,7 @@ import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
GroupListModule.class,
GroupConversationModule.class,
SharingModule.class,
+ HotspotModule.class
})
public class AppModule {
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java
index c03f98b61..974510b4d 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java
@@ -38,6 +38,7 @@ import org.briarproject.briar.android.forum.CreateForumActivity;
import org.briarproject.briar.android.forum.ForumActivity;
import org.briarproject.briar.android.forum.ForumListFragment;
import org.briarproject.briar.android.fragment.ScreenFilterDialogFragment;
+import org.briarproject.briar.android.hotspot.HotspotActivity;
import org.briarproject.briar.android.introduction.ContactChooserFragment;
import org.briarproject.briar.android.introduction.IntroductionActivity;
import org.briarproject.briar.android.introduction.IntroductionMessageFragment;
@@ -176,6 +177,8 @@ public interface ActivityComponent {
void inject(CrashReportActivity crashReportActivity);
+ void inject(HotspotActivity hotspotActivity);
+
// Fragments
void inject(SetupFragment fragment);
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/hotspot/HotspotActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/hotspot/HotspotActivity.java
new file mode 100644
index 000000000..43ce86ccd
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/hotspot/HotspotActivity.java
@@ -0,0 +1,62 @@
+package org.briarproject.briar.android.hotspot;
+
+import android.os.Bundle;
+import android.view.MenuItem;
+
+import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
+import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
+import org.briarproject.briar.R;
+import org.briarproject.briar.android.activity.ActivityComponent;
+import org.briarproject.briar.android.activity.BriarActivity;
+
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.ActionBar;
+import androidx.lifecycle.ViewModelProvider;
+
+@MethodsNotNullByDefault
+@ParametersNotNullByDefault
+public class HotspotActivity extends BriarActivity {
+
+ @Inject
+ ViewModelProvider.Factory viewModelFactory;
+
+ private HotspotViewModel viewModel;
+
+ @Override
+ public void injectActivity(ActivityComponent component) {
+ component.inject(this);
+ viewModel = new ViewModelProvider(this, viewModelFactory)
+ .get(HotspotViewModel.class);
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle state) {
+ super.onCreate(state);
+ setContentView(R.layout.activity_fragment_container);
+
+ ActionBar ab = getSupportActionBar();
+ if (ab != null) {
+ ab.setDisplayHomeAsUpEnabled(true);
+ }
+
+ if (state == null) {
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.fragmentContainer, new HotspotIntroFragment(),
+ HotspotIntroFragment.TAG)
+ .commit();
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/hotspot/HotspotIntroFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/hotspot/HotspotIntroFragment.java
new file mode 100644
index 000000000..ef565050c
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/hotspot/HotspotIntroFragment.java
@@ -0,0 +1,67 @@
+package org.briarproject.briar.android.hotspot;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
+import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
+import org.briarproject.briar.R;
+
+import javax.inject.Inject;
+
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.lifecycle.ViewModelProvider;
+
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
+import static androidx.transition.TransitionManager.beginDelayedTransition;
+import static org.briarproject.briar.android.AppModule.getAndroidComponent;
+
+@MethodsNotNullByDefault
+@ParametersNotNullByDefault
+public class HotspotIntroFragment extends Fragment {
+
+ public final static String TAG = HotspotIntroFragment.class.getName();
+
+ @Inject
+ ViewModelProvider.Factory viewModelFactory;
+
+ private HotspotViewModel viewModel;
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ getAndroidComponent(requireContext()).inject(this);
+ viewModel = new ViewModelProvider(requireActivity(), viewModelFactory)
+ .get(HotspotViewModel.class);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater,
+ @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ View v = inflater
+ .inflate(R.layout.fragment_hotspot_intro, container, false);
+
+ Button startButton = v.findViewById(R.id.startButton);
+ ProgressBar progressBar = v.findViewById(R.id.progressBar);
+ TextView progressTextView = v.findViewById(R.id.progressTextView);
+
+ startButton.setOnClickListener(button -> {
+ beginDelayedTransition((ViewGroup) v);
+ startButton.setVisibility(INVISIBLE);
+ progressBar.setVisibility(VISIBLE);
+ progressTextView.setVisibility(VISIBLE);
+ });
+
+ return v;
+ }
+
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/hotspot/HotspotModule.java b/briar-android/src/main/java/org/briarproject/briar/android/hotspot/HotspotModule.java
new file mode 100644
index 000000000..ea7427b75
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/hotspot/HotspotModule.java
@@ -0,0 +1,18 @@
+package org.briarproject.briar.android.hotspot;
+
+import org.briarproject.briar.android.viewmodel.ViewModelKey;
+
+import androidx.lifecycle.ViewModel;
+import dagger.Binds;
+import dagger.Module;
+import dagger.multibindings.IntoMap;
+
+@Module
+public interface HotspotModule {
+
+ @Binds
+ @IntoMap
+ @ViewModelKey(HotspotViewModel.class)
+ ViewModel bindHotspotViewModel(HotspotViewModel hotspotViewModel);
+
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/hotspot/HotspotViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/hotspot/HotspotViewModel.java
new file mode 100644
index 000000000..5669a19ae
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/hotspot/HotspotViewModel.java
@@ -0,0 +1,28 @@
+package org.briarproject.briar.android.hotspot;
+
+import android.app.Application;
+
+import org.briarproject.bramble.api.db.DatabaseExecutor;
+import org.briarproject.bramble.api.db.TransactionManager;
+import org.briarproject.bramble.api.lifecycle.LifecycleManager;
+import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+import org.briarproject.bramble.api.system.AndroidExecutor;
+import org.briarproject.briar.android.viewmodel.DbViewModel;
+
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
+@NotNullByDefault
+class HotspotViewModel extends DbViewModel {
+
+ @Inject
+ HotspotViewModel(Application application,
+ @DatabaseExecutor Executor dbExecutor,
+ LifecycleManager lifecycleManager,
+ TransactionManager db,
+ AndroidExecutor androidExecutor) {
+ super(application, dbExecutor, lifecycleManager, db, androidExecutor);
+ }
+
+}
diff --git a/briar-android/src/main/res/drawable/ic_wifi_tethering.xml b/briar-android/src/main/res/drawable/ic_wifi_tethering.xml
new file mode 100644
index 000000000..3a3a02798
--- /dev/null
+++ b/briar-android/src/main/res/drawable/ic_wifi_tethering.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/briar-android/src/main/res/layout/fragment_hotspot_intro.xml b/briar-android/src/main/res/layout/fragment_hotspot_intro.xml
new file mode 100644
index 000000000..43dbf1a9a
--- /dev/null
+++ b/briar-android/src/main/res/layout/fragment_hotspot_intro.xml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml
index ab121d58c..a1c95034e 100644
--- a/briar-android/src/main/res/values/strings.xml
+++ b/briar-android/src/main/res/values/strings.xml
@@ -689,6 +689,14 @@
Briar can connect to your contacts via the Internet, Wi-Fi or Bluetooth.\n\nAll Internet connections go through the Tor network for privacy.\n\nIf a contact can be reached by multiple methods, Briar uses them in parallel.
+
+ Share Briar offline
+ Share this app with someone nearby without internet connection
+ by using your phone\'s Wi-Fi.
+ \n\nYour phone will open a local hotspot and provide a small website with a download of this app.
+ Start sharing
+ Setting up hotspot…
+