Implement info screens for offline app sharing

This commit is contained in:
Torsten Grote
2021-05-07 11:57:52 -03:00
parent 54b239f45e
commit 7c8aa5bc21
16 changed files with 675 additions and 0 deletions

View File

@@ -36,7 +36,10 @@ 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.AbstractTabsFragment;
import org.briarproject.briar.android.hotspot.HotspotIntroFragment;
import org.briarproject.briar.android.hotspot.HotspotManualFragment;
import org.briarproject.briar.android.hotspot.HotspotQrFragment;
import org.briarproject.briar.android.logging.CachingLogHandler;
import org.briarproject.briar.android.login.SignInReminderReceiver;
import org.briarproject.briar.android.removabledrive.ChooserFragment;
@@ -219,6 +222,12 @@ public interface AndroidComponent
void inject(HotspotIntroFragment hotspotIntroFragment);
void inject(AbstractTabsFragment abstractTabsFragment);
void inject(HotspotQrFragment hotspotQrFragment);
void inject(HotspotManualFragment hotspotManualFragment);
void inject(ChooserFragment chooserFragment);
void inject(SendFragment sendFragment);

View File

@@ -0,0 +1,109 @@
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.Toast;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
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.CallSuper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;
import static android.widget.Toast.LENGTH_SHORT;
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public abstract class AbstractTabsFragment extends Fragment {
@Inject
ViewModelProvider.Factory viewModelFactory;
protected HotspotViewModel viewModel;
protected Button stopButton;
protected Button connectedButton;
@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) {
return inflater
.inflate(R.layout.fragment_hotspot_tabs, container, false);
}
@Override
@CallSuper
public void onViewCreated(@NonNull View view,
@Nullable Bundle savedInstanceState) {
TabAdapter tabAdapter = new TabAdapter(this);
ViewPager2 viewPager = view.findViewById(R.id.pager);
viewPager.setAdapter(tabAdapter);
TabLayout tabLayout = view.findViewById(R.id.tabLayout);
new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> {
// tabs are set in XML, but just dummies that don't get not added
if (position == 0) {
tab.setText(R.string.hotspot_tab_manual);
tab.setIcon(R.drawable.forum_item_create_white);
} else if (position == 1) {
tab.setText(R.string.qr_code);
tab.setIcon(R.drawable.ic_qr_code);
} else throw new AssertionError();
}).attach();
stopButton = view.findViewById(R.id.stopButton);
stopButton.setOnClickListener(v -> {
Toast.makeText(requireContext(), "Not yet implemented",
LENGTH_SHORT).show();
});
connectedButton = view.findViewById(R.id.connectedButton);
}
protected abstract Fragment getFirstFragment();
protected abstract Fragment getSecondFragment();
private class TabAdapter extends FragmentStateAdapter {
private TabAdapter(Fragment fragment) {
super(fragment);
}
@NonNull
@Override
public Fragment createFragment(int position) {
if (position == 0) return getFirstFragment();
if (position == 1) return getSecondFragment();
throw new AssertionError();
}
@Override
public int getItemCount() {
return 2;
}
}
}

View File

@@ -1,7 +1,10 @@
package org.briarproject.briar.android.hotspot;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.Toast;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
@@ -16,6 +19,8 @@ import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.lifecycle.ViewModelProvider;
import static android.widget.Toast.LENGTH_SHORT;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class HotspotActivity extends BriarActivity {
@@ -42,6 +47,8 @@ public class HotspotActivity extends BriarActivity {
ab.setDisplayHomeAsUpEnabled(true);
}
// TODO observe viewmodel state and show error or HotspotFragment
if (state == null) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragmentContainer, new HotspotIntroFragment(),
@@ -50,11 +57,21 @@ public class HotspotActivity extends BriarActivity {
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.hotspot_help_action, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == android.R.id.home) {
onBackPressed();
return true;
} else if (item.getItemId() == R.id.action_help) {
Toast.makeText(this, "Not yet implemented", LENGTH_SHORT).show();
return true;
}
return super.onOptionsItemSelected(item);
}

View File

@@ -0,0 +1,47 @@
package org.briarproject.briar.android.hotspot;
import android.os.Bundle;
import android.view.View;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class HotspotFragment extends AbstractTabsFragment {
public final static String TAG = HotspotFragment.class.getName();
@Override
public void onViewCreated(@NonNull View view,
@Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// no need to call into the ViewModel here
connectedButton.setOnClickListener(v -> {
getParentFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.step_next_in,
R.anim.step_previous_out, R.anim.step_previous_in,
R.anim.step_next_out)
.replace(R.id.fragmentContainer, new WebsiteFragment(),
WebsiteFragment.TAG)
.addToBackStack(WebsiteFragment.TAG)
.commit();
});
}
@Override
protected Fragment getFirstFragment() {
return HotspotManualFragment.newInstance(true);
}
@Override
protected Fragment getSecondFragment() {
return HotspotQrFragment.newInstance(true);
}
}

View File

@@ -59,6 +59,18 @@ public class HotspotIntroFragment extends Fragment {
startButton.setVisibility(INVISIBLE);
progressBar.setVisibility(VISIBLE);
progressTextView.setVisibility(VISIBLE);
// TODO remove below, tell viewModel to start hotspot instead
v.postDelayed(() -> {
getParentFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.step_next_in,
R.anim.step_previous_out,
R.anim.step_previous_in,
R.anim.step_next_out)
.replace(R.id.fragmentContainer, new HotspotFragment(),
HotspotFragment.TAG)
.addToBackStack(HotspotFragment.TAG)
.commit();
}, 1500);
});
return v;

View File

@@ -0,0 +1,85 @@
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.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.GONE;
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class HotspotManualFragment extends Fragment {
public final static String TAG = HotspotManualFragment.class.getName();
@Inject
ViewModelProvider.Factory viewModelFactory;
private HotspotViewModel viewModel;
static HotspotManualFragment newInstance(boolean forWifiConnect) {
HotspotManualFragment f = new HotspotManualFragment();
Bundle bundle = new Bundle();
bundle.putBoolean("forWifiConnect", forWifiConnect);
f.setArguments(bundle);
return f;
}
@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) {
return inflater
.inflate(R.layout.fragment_hotspot_manual, container, false);
}
@Override
public void onViewCreated(View v, @Nullable Bundle savedInstanceState) {
super.onViewCreated(v, savedInstanceState);
TextView manualIntroView = v.findViewById(R.id.manualIntroView);
TextView ssidLabelView = v.findViewById(R.id.ssidLabelView);
TextView ssidView = v.findViewById(R.id.ssidView);
TextView passwordView = v.findViewById(R.id.passwordView);
TextView altView = v.findViewById(R.id.altView);
if (requireArguments().getBoolean("forWifiConnect")) {
manualIntroView.setText(R.string.hotspot_manual_wifi);
ssidLabelView.setText(R.string.hotspot_manual_wifi_ssid);
// TODO observe state in ViewModel and get info from there instead
ssidView.setText("DIRECT-42-dfzsgf34ef");
passwordView.setText("sdf78shfd8");
altView.setText(R.string.hotspot_manual_wifi_alt);
} else {
manualIntroView.setText(R.string.hotspot_manual_site);
ssidLabelView.setText(R.string.hotspot_manual_site_address);
// TODO observe state in ViewModel and get info from there instead
ssidView.setText("http://192.168.49.1:9999");
altView.setText(R.string.hotspot_manual_site_alt);
v.findViewById(R.id.passwordLabelView).setVisibility(GONE);
passwordView.setVisibility(GONE);
}
}
}

View File

@@ -0,0 +1,70 @@
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.ImageView;
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 org.briarproject.briar.android.AppModule.getAndroidComponent;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class HotspotQrFragment extends Fragment {
public final static String TAG = HotspotQrFragment.class.getName();
@Inject
ViewModelProvider.Factory viewModelFactory;
private HotspotViewModel viewModel;
static HotspotQrFragment newInstance(boolean forWifiConnect) {
HotspotQrFragment f = new HotspotQrFragment();
Bundle bundle = new Bundle();
bundle.putBoolean("forWifiConnect", forWifiConnect);
f.setArguments(bundle);
return f;
}
@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_qr, container, false);
TextView qrIntroView = v.findViewById(R.id.qrIntroView);
ImageView qrCodeView = v.findViewById(R.id.qrCodeView);
if (requireArguments().getBoolean("forWifiConnect")) {
qrIntroView.setText(R.string.hotspot_qr_wifi);
// TODO observe state in ViewModel and get QR code from there
} else {
qrIntroView.setText(R.string.hotspot_qr_site);
// TODO observe state in ViewModel and get QR code from there
}
return v;
}
}

View File

@@ -25,4 +25,6 @@ class HotspotViewModel extends DbViewModel {
super(application, dbExecutor, lifecycleManager, db, androidExecutor);
}
// TODO copy actual code from Offline Hotspot app
}

View File

@@ -0,0 +1,38 @@
package org.briarproject.briar.android.hotspot;
import android.os.Bundle;
import android.view.View;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import static android.view.View.INVISIBLE;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class WebsiteFragment extends AbstractTabsFragment {
public final static String TAG = WebsiteFragment.class.getName();
@Override
public void onViewCreated(@NonNull View view,
@Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
connectedButton.setVisibility(INVISIBLE);
}
@Override
protected Fragment getFirstFragment() {
return HotspotManualFragment.newInstance(false);
}
@Override
protected Fragment getSecondFragment() {
return HotspotQrFragment.newInstance(false);
}
}