mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 18:59:06 +01:00
Compare commits
1 Commits
offline-te
...
removable-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3692b2a97 |
@@ -437,6 +437,15 @@
|
|||||||
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity" />
|
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name="org.briarproject.briar.android.removabledrive.RemovableDriveActivity"
|
||||||
|
android:label="TODO Removable Drive"
|
||||||
|
android:parentActivityName="org.briarproject.briar.android.conversation.ConversationActivity">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
|
android:value="org.briarproject.briar.android.conversation.ConversationActivity" />
|
||||||
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.contact.add.remote.PendingContactListActivity"
|
android:name=".android.contact.add.remote.PendingContactListActivity"
|
||||||
android:label="@string/pending_contact_requests"
|
android:label="@string/pending_contact_requests"
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ import org.briarproject.briar.android.privategroup.memberlist.GroupMemberModule;
|
|||||||
import org.briarproject.briar.android.privategroup.reveal.GroupRevealModule;
|
import org.briarproject.briar.android.privategroup.reveal.GroupRevealModule;
|
||||||
import org.briarproject.briar.android.privategroup.reveal.RevealContactsActivity;
|
import org.briarproject.briar.android.privategroup.reveal.RevealContactsActivity;
|
||||||
import org.briarproject.briar.android.privategroup.reveal.RevealContactsFragment;
|
import org.briarproject.briar.android.privategroup.reveal.RevealContactsFragment;
|
||||||
|
import org.briarproject.briar.android.removabledrive.RemovableDriveActivity;
|
||||||
import org.briarproject.briar.android.reporting.CrashFragment;
|
import org.briarproject.briar.android.reporting.CrashFragment;
|
||||||
import org.briarproject.briar.android.reporting.CrashReportActivity;
|
import org.briarproject.briar.android.reporting.CrashReportActivity;
|
||||||
import org.briarproject.briar.android.reporting.ReportFormFragment;
|
import org.briarproject.briar.android.reporting.ReportFormFragment;
|
||||||
@@ -176,6 +177,8 @@ public interface ActivityComponent {
|
|||||||
|
|
||||||
void inject(CrashReportActivity crashReportActivity);
|
void inject(CrashReportActivity crashReportActivity);
|
||||||
|
|
||||||
|
void inject(RemovableDriveActivity activity);
|
||||||
|
|
||||||
// Fragments
|
// Fragments
|
||||||
|
|
||||||
void inject(SetupFragment fragment);
|
void inject(SetupFragment fragment);
|
||||||
|
|||||||
@@ -14,5 +14,7 @@ public interface RequestCodes {
|
|||||||
int REQUEST_ATTACH_IMAGE = 13;
|
int REQUEST_ATTACH_IMAGE = 13;
|
||||||
int REQUEST_SAVE_ATTACHMENT = 14;
|
int REQUEST_SAVE_ATTACHMENT = 14;
|
||||||
int REQUEST_AVATAR_IMAGE = 15;
|
int REQUEST_AVATAR_IMAGE = 15;
|
||||||
|
int REQUEST_REMOVABLE_DRIVE_WRITE = 16;
|
||||||
|
int REQUEST_REMOVABLE_DRIVE_READ = 17;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ import org.briarproject.bramble.api.event.EventBus;
|
|||||||
import org.briarproject.bramble.api.event.EventListener;
|
import org.briarproject.bramble.api.event.EventListener;
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.BluetoothConstants;
|
|
||||||
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
|
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
|
||||||
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
|
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
|
||||||
import org.briarproject.bramble.api.sync.ClientId;
|
import org.briarproject.bramble.api.sync.ClientId;
|
||||||
@@ -54,6 +53,7 @@ import org.briarproject.briar.android.forum.ForumActivity;
|
|||||||
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
|
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
|
||||||
import org.briarproject.briar.android.introduction.IntroductionActivity;
|
import org.briarproject.briar.android.introduction.IntroductionActivity;
|
||||||
import org.briarproject.briar.android.privategroup.conversation.GroupActivity;
|
import org.briarproject.briar.android.privategroup.conversation.GroupActivity;
|
||||||
|
import org.briarproject.briar.android.removabledrive.RemovableDriveActivity;
|
||||||
import org.briarproject.briar.android.util.BriarSnackbarBuilder;
|
import org.briarproject.briar.android.util.BriarSnackbarBuilder;
|
||||||
import org.briarproject.briar.android.view.BriarRecyclerView;
|
import org.briarproject.briar.android.view.BriarRecyclerView;
|
||||||
import org.briarproject.briar.android.view.ImagePreview;
|
import org.briarproject.briar.android.view.ImagePreview;
|
||||||
@@ -421,6 +421,11 @@ public class ConversationActivity extends BriarActivity
|
|||||||
} else if (itemId == R.id.action_social_remove_person) {
|
} else if (itemId == R.id.action_social_remove_person) {
|
||||||
askToRemoveContact();
|
askToRemoveContact();
|
||||||
return true;
|
return true;
|
||||||
|
} else if (itemId == R.id.action_removable_drive_write) {
|
||||||
|
Intent intent = new Intent(this, RemovableDriveActivity.class);
|
||||||
|
intent.putExtra(CONTACT_ID, contactId.getInt());
|
||||||
|
startActivity(intent);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,174 @@
|
|||||||
|
package org.briarproject.briar.android.removabledrive;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.file.RemovableDriveTask.State;
|
||||||
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
|
import org.briarproject.briar.android.activity.BriarActivity;
|
||||||
|
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
import androidx.lifecycle.LiveData;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
|
import static org.briarproject.briar.android.conversation.ConversationActivity.CONTACT_ID;
|
||||||
|
|
||||||
|
// TODO 19 will be our requirement for sneakernet support, right. The file apis
|
||||||
|
// used require this.
|
||||||
|
@RequiresApi(api = 19)
|
||||||
|
@MethodsNotNullByDefault
|
||||||
|
@ParametersNotNullByDefault
|
||||||
|
public class RemovableDriveActivity extends BriarActivity
|
||||||
|
implements BaseFragmentListener {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ViewModelProvider.Factory viewModelFactory;
|
||||||
|
private RemovableDriveViewModel viewModel;
|
||||||
|
private TextView text;
|
||||||
|
private Button writeButton;
|
||||||
|
private Button readButton;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void injectActivity(ActivityComponent component) {
|
||||||
|
component.inject(this);
|
||||||
|
|
||||||
|
viewModel = new ViewModelProvider(this, viewModelFactory)
|
||||||
|
.get(RemovableDriveViewModel.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_removable_drive);
|
||||||
|
text = findViewById(R.id.sneaker_text);
|
||||||
|
writeButton = findViewById(R.id.sneaker_write);
|
||||||
|
readButton = findViewById(R.id.sneaker_read);
|
||||||
|
|
||||||
|
Intent intent = getIntent();
|
||||||
|
int contactId = intent.getIntExtra(CONTACT_ID, -1);
|
||||||
|
if (contactId == -1) {
|
||||||
|
writeButton.setEnabled(false);
|
||||||
|
readButton.setEnabled(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO we can pass an extra named DocumentsContract.EXTRA_INITIAL_URI
|
||||||
|
// to have the filepicker start on the usb-stick -- if get hold of URI
|
||||||
|
// of the same. USB manager API?
|
||||||
|
// Overall, passing this extra requires extending the ready-made
|
||||||
|
// contracts and overriding createIntent.
|
||||||
|
|
||||||
|
writeButton.setText("Write for contactId " + contactId);
|
||||||
|
ActivityResultLauncher<String> createDocument =
|
||||||
|
registerForActivityResult(
|
||||||
|
new ActivityResultContracts.CreateDocument(),
|
||||||
|
uri -> write(contactId, uri));
|
||||||
|
writeButton.setOnClickListener(
|
||||||
|
v -> createDocument.launch(viewModel.getFileName()));
|
||||||
|
|
||||||
|
readButton.setText("Read for contactId " + contactId);
|
||||||
|
ActivityResultLauncher<String> getContent =
|
||||||
|
registerForActivityResult(
|
||||||
|
new ActivityResultContracts.GetContent(),
|
||||||
|
uri -> read(contactId, uri));
|
||||||
|
readButton.setOnClickListener(
|
||||||
|
v -> getContent.launch("application/octet-stream"));
|
||||||
|
|
||||||
|
LiveData<State> state;
|
||||||
|
state = viewModel.ongoingWrite(new ContactId(contactId));
|
||||||
|
if (state == null) {
|
||||||
|
writeButton.setEnabled(true);
|
||||||
|
} else {
|
||||||
|
say("\nOngoing write:");
|
||||||
|
writeButton.setEnabled(false);
|
||||||
|
state.observe(this, (taskState) -> handleState("write", taskState));
|
||||||
|
}
|
||||||
|
state = viewModel.ongoingRead(new ContactId(contactId));
|
||||||
|
if (state == null) {
|
||||||
|
readButton.setEnabled(true);
|
||||||
|
} else {
|
||||||
|
say("\nOngoing read:");
|
||||||
|
readButton.setEnabled(false);
|
||||||
|
state.observe(this, (taskState) -> handleState("read", taskState));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
|
if (item.getItemId() == android.R.id.home) {
|
||||||
|
onBackPressed();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void write(int contactId, @Nullable Uri uri) {
|
||||||
|
if (contactId == -1) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
if (uri == null) {
|
||||||
|
say("no URI picked for write");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
say("\nWriting to URI: " + uri);
|
||||||
|
writeButton.setEnabled(false);
|
||||||
|
LiveData<State> state = viewModel.write(new ContactId(contactId), uri);
|
||||||
|
state.observe(this, (taskState) -> handleState("write", taskState));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void read(int contactId, @Nullable Uri uri) {
|
||||||
|
if (contactId == -1) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
if (uri == null) {
|
||||||
|
say("no URI picked for read");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
say("\nReading from URI: " + uri);
|
||||||
|
readButton.setEnabled(false);
|
||||||
|
LiveData<State> state = viewModel.read(new ContactId(contactId), uri);
|
||||||
|
state.observe(this, (taskState) -> handleState("read", taskState));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleState(String action, State taskState) {
|
||||||
|
say(String.format(Locale.getDefault(),
|
||||||
|
"%s: bytes done: %d of %d. %s. %s.",
|
||||||
|
action, taskState.getDone(), taskState.getTotal(),
|
||||||
|
taskState.isFinished() ? "Finished" : "Ongoing",
|
||||||
|
taskState.isFinished() ?
|
||||||
|
(taskState.isSuccess() ? "Success" : "Failed") : ".."));
|
||||||
|
if (taskState.isFinished()) {
|
||||||
|
if (action.equals("write")) {
|
||||||
|
writeButton.setEnabled(true);
|
||||||
|
} else if (action.equals("read")) {
|
||||||
|
readButton.setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void say(String txt) {
|
||||||
|
String time = new SimpleDateFormat("HH:mm:ss", Locale.getDefault())
|
||||||
|
.format(new Date());
|
||||||
|
txt = String.format("%s %s\n", time, txt);
|
||||||
|
text.setText(text.getText().toString().concat(txt));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".android.removabledrive.RemovableDriveActivity">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="@dimen/margin_large">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/sneaker_write"
|
||||||
|
style="@style/BriarButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/margin_medium"
|
||||||
|
android:enabled="false"
|
||||||
|
android:text=""
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:enabled="true" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/sneaker_read"
|
||||||
|
style="@style/BriarButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/margin_medium"
|
||||||
|
android:enabled="false"
|
||||||
|
android:text=""
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/sneaker_write"
|
||||||
|
tools:enabled="true" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/sneaker_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="@dimen/margin_large"
|
||||||
|
android:textSize="12sp"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/sneaker_read" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
@@ -40,4 +40,9 @@
|
|||||||
android:title="@string/delete_contact"
|
android:title="@string/delete_contact"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_removable_drive_write"
|
||||||
|
android:title="Transfer via removable drive"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
|
|||||||
Reference in New Issue
Block a user