mirror of
https://github.com/oxen-io/session-android.git
synced 2025-02-18 23:58:26 +00:00
Device link related views removed. General unused code cleanup.
This commit is contained in:
parent
a94273fdfc
commit
eafa7c7a77
@ -101,7 +101,7 @@
|
|||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:theme="@style/Theme.Session.DayNight.FlatActionBar"/>
|
android:theme="@style/Theme.Session.DayNight.FlatActionBar"/>
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.loki.activities.RestoreActivity"
|
android:name="org.thoughtcrime.securesms.loki.activities.RecoveryPhraseRestoreActivity"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:windowSoftInputMode="adjustResize"
|
android:windowSoftInputMode="adjustResize"
|
||||||
android:theme="@style/Theme.Session.DayNight.FlatActionBar" />
|
android:theme="@style/Theme.Session.DayNight.FlatActionBar" />
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms;
|
|
||||||
|
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.fragment.app.Fragment;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
|
|
||||||
import network.loki.messenger.R;
|
|
||||||
|
|
||||||
public class DeviceLinkFragment extends Fragment implements View.OnClickListener {
|
|
||||||
|
|
||||||
private LinearLayout container;
|
|
||||||
private LinkClickedListener linkClickedListener;
|
|
||||||
private Uri uri;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup viewGroup, Bundle bundle) {
|
|
||||||
this.container = (LinearLayout) inflater.inflate(R.layout.device_link_fragment, container, false);
|
|
||||||
this.container.findViewById(R.id.link_device).setOnClickListener(this);
|
|
||||||
|
|
||||||
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
|
||||||
container.setOrientation(LinearLayout.HORIZONTAL);
|
|
||||||
} else {
|
|
||||||
container.setOrientation(LinearLayout.VERTICAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.container;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onConfigurationChanged(Configuration newConfiguration) {
|
|
||||||
super.onConfigurationChanged(newConfiguration);
|
|
||||||
if (newConfiguration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
|
||||||
container.setOrientation(LinearLayout.HORIZONTAL);
|
|
||||||
} else {
|
|
||||||
container.setOrientation(LinearLayout.VERTICAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLinkClickedListener(Uri uri, LinkClickedListener linkClickedListener) {
|
|
||||||
this.uri = uri;
|
|
||||||
this.linkClickedListener = linkClickedListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
if (linkClickedListener != null) {
|
|
||||||
linkClickedListener.onLink(uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface LinkClickedListener {
|
|
||||||
void onLink(Uri uri);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,249 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.ListView;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
|
||||||
import androidx.fragment.app.ListFragment;
|
|
||||||
import androidx.loader.app.LoaderManager;
|
|
||||||
import androidx.loader.content.Loader;
|
|
||||||
|
|
||||||
import com.melnykov.fab.FloatingActionButton;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
||||||
import org.thoughtcrime.securesms.database.loaders.DeviceListLoader;
|
|
||||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
|
||||||
import org.thoughtcrime.securesms.devicelist.Device;
|
|
||||||
import org.thoughtcrime.securesms.loki.dialogs.DeviceEditingOptionsBottomSheet;
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|
||||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
|
||||||
import org.session.libsignal.libsignal.util.guava.Function;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import kotlin.Pair;
|
|
||||||
import kotlin.Unit;
|
|
||||||
import network.loki.messenger.R;
|
|
||||||
|
|
||||||
import static org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt.toPx;
|
|
||||||
|
|
||||||
public class DeviceListFragment extends ListFragment
|
|
||||||
implements LoaderManager.LoaderCallbacks<List<Device>>,
|
|
||||||
ListView.OnItemClickListener, InjectableType, Button.OnClickListener
|
|
||||||
{
|
|
||||||
|
|
||||||
private static final String TAG = DeviceListFragment.class.getSimpleName();
|
|
||||||
|
|
||||||
private Locale locale;
|
|
||||||
private View empty;
|
|
||||||
private View progressContainer;
|
|
||||||
private FloatingActionButton addDeviceButton;
|
|
||||||
private Button.OnClickListener addDeviceButtonListener;
|
|
||||||
private Function<String, Void> handleDisconnectDevice;
|
|
||||||
private Function<Pair<String, String>, Void> handleDeviceNameChange;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
this.locale = (Locale) getArguments().getSerializable(PassphraseRequiredActionBarActivity.LOCALE_EXTRA);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAttach(Activity activity) {
|
|
||||||
super.onAttach(activity);
|
|
||||||
ApplicationContext.getInstance(activity).injectDependencies(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
|
|
||||||
View view = inflater.inflate(R.layout.device_list_fragment, container, false);
|
|
||||||
|
|
||||||
this.empty = view.findViewById(R.id.emptyStateTextView);
|
|
||||||
this.progressContainer = view.findViewById(R.id.activityIndicator);
|
|
||||||
this.addDeviceButton = ViewUtil.findById(view, R.id.addDeviceButton);
|
|
||||||
this.addDeviceButton.setOnClickListener(this);
|
|
||||||
updateAddDeviceButtonVisibility();
|
|
||||||
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onActivityCreated(Bundle bundle) {
|
|
||||||
super.onActivityCreated(bundle);
|
|
||||||
getLoaderManager().initLoader(0, null, this);
|
|
||||||
getListView().setOnItemClickListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAddDeviceButtonListener(Button.OnClickListener listener) {
|
|
||||||
this.addDeviceButtonListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setHandleDisconnectDevice(Function<String, Void> handler) {
|
|
||||||
this.handleDisconnectDevice = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setHandleDeviceNameChange(Function<Pair<String, String>, Void> handler) {
|
|
||||||
this.handleDeviceNameChange = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull Loader<List<Device>> onCreateLoader(int id, Bundle args) {
|
|
||||||
empty.setVisibility(View.GONE);
|
|
||||||
progressContainer.setVisibility(View.VISIBLE);
|
|
||||||
|
|
||||||
return new DeviceListLoader(getActivity());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoadFinished(@NonNull Loader<List<Device>> loader, List<Device> data) {
|
|
||||||
progressContainer.setVisibility(View.GONE);
|
|
||||||
|
|
||||||
if (data == null) {
|
|
||||||
handleLoaderFailed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setListAdapter(new DeviceListAdapter(getActivity(), R.layout.device_list_item_view, data, locale));
|
|
||||||
|
|
||||||
if (data.isEmpty()) {
|
|
||||||
empty.setVisibility(View.VISIBLE);
|
|
||||||
TextSecurePreferences.setMultiDevice(getActivity(), false);
|
|
||||||
} else {
|
|
||||||
empty.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoaderReset(@NonNull Loader<List<Device>> loader) {
|
|
||||||
setListAdapter(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
|
||||||
final boolean hasDeviceName = ((DeviceListItem)view).hasDeviceName(); // Tells us whether the name is set to shortId or the device name
|
|
||||||
final String deviceName = ((DeviceListItem)view).getDeviceName();
|
|
||||||
final String deviceId = ((DeviceListItem)view).getDeviceId();
|
|
||||||
|
|
||||||
DeviceEditingOptionsBottomSheet bottomSheet = new DeviceEditingOptionsBottomSheet();
|
|
||||||
bottomSheet.setOnEditTapped(() -> {
|
|
||||||
bottomSheet.dismiss();
|
|
||||||
EditText deviceNameEditText = new EditText(getContext());
|
|
||||||
LinearLayout deviceNameEditTextContainer = new LinearLayout(getContext());
|
|
||||||
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
|
|
||||||
layoutParams.setMarginStart(toPx(18, getResources()));
|
|
||||||
layoutParams.setMarginEnd(toPx(18, getResources()));
|
|
||||||
deviceNameEditText.setLayoutParams(layoutParams);
|
|
||||||
deviceNameEditTextContainer.addView(deviceNameEditText);
|
|
||||||
deviceNameEditText.setText(hasDeviceName ? deviceName : "");
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
|
||||||
builder.setTitle(R.string.DeviceListActivity_edit_device_name);
|
|
||||||
builder.setView(deviceNameEditTextContainer);
|
|
||||||
builder.setNegativeButton(android.R.string.cancel, null);
|
|
||||||
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
if (handleDeviceNameChange != null) { handleDeviceNameChange.apply(new Pair<>(deviceId, deviceNameEditText.getText().toString().trim())); }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
builder.show();
|
|
||||||
return Unit.INSTANCE;
|
|
||||||
});
|
|
||||||
bottomSheet.setOnUnlinkTapped(() -> {
|
|
||||||
bottomSheet.dismiss();
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
|
||||||
builder.setTitle(getActivity().getString(R.string.DeviceListActivity_unlink_s, deviceName));
|
|
||||||
builder.setMessage(R.string.DeviceListActivity_by_unlinking_this_device_it_will_no_longer_be_able_to_send_or_receive);
|
|
||||||
builder.setNegativeButton(android.R.string.cancel, null);
|
|
||||||
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
if (handleDisconnectDevice != null) { handleDisconnectDevice.apply(deviceId); }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
builder.show();
|
|
||||||
return Unit.INSTANCE;
|
|
||||||
});
|
|
||||||
bottomSheet.show(getFragmentManager(), bottomSheet.getTag());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void refresh() {
|
|
||||||
updateAddDeviceButtonVisibility();
|
|
||||||
getLoaderManager().restartLoader(0, null, DeviceListFragment.this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateAddDeviceButtonVisibility() {
|
|
||||||
if (addDeviceButton != null) {
|
|
||||||
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(getContext());
|
|
||||||
boolean isDeviceLinkingEnabled = DatabaseFactory.getLokiAPIDatabase(getContext()).getDeviceLinks(userHexEncodedPublicKey).isEmpty();
|
|
||||||
addDeviceButton.setVisibility(isDeviceLinkingEnabled ? View.VISIBLE : View.INVISIBLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleLoaderFailed() {
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
|
||||||
builder.setMessage(R.string.DeviceListActivity_network_connection_failed);
|
|
||||||
builder.setPositiveButton(R.string.DeviceListActivity_try_again,
|
|
||||||
new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
getLoaderManager().restartLoader(0, null, DeviceListFragment.this);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
DeviceListFragment.this.getActivity().onBackPressed();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
|
||||||
@Override
|
|
||||||
public void onCancel(DialogInterface dialog) {
|
|
||||||
DeviceListFragment.this.getActivity().onBackPressed();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
if (addDeviceButtonListener != null) addDeviceButtonListener.onClick(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class DeviceListAdapter extends ArrayAdapter<Device> {
|
|
||||||
|
|
||||||
private final int resource;
|
|
||||||
private final Locale locale;
|
|
||||||
|
|
||||||
public DeviceListAdapter(Context context, int resource, List<Device> objects, Locale locale) {
|
|
||||||
super(context, resource, objects);
|
|
||||||
this.resource = resource;
|
|
||||||
this.locale = locale;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull View getView(int position, View convertView, @NonNull ViewGroup parent) {
|
|
||||||
if (convertView == null) {
|
|
||||||
convertView = ((Activity)getContext()).getLayoutInflater().inflate(resource, parent, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
((DeviceListItem)convertView).set(getItem(position), locale);
|
|
||||||
|
|
||||||
return convertView;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.devicelist.Device;
|
|
||||||
import org.thoughtcrime.securesms.util.DateUtils;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import network.loki.messenger.R;
|
|
||||||
|
|
||||||
public class DeviceListItem extends LinearLayout {
|
|
||||||
|
|
||||||
private String deviceId;
|
|
||||||
private TextView name;
|
|
||||||
private TextView shortId;
|
|
||||||
|
|
||||||
public DeviceListItem(Context context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DeviceListItem(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFinishInflate() {
|
|
||||||
super.onFinishInflate();
|
|
||||||
this.name = (TextView) findViewById(R.id.name);
|
|
||||||
this.shortId = (TextView) findViewById(R.id.shortId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void set(Device deviceInfo, Locale locale) {
|
|
||||||
this.deviceId = deviceInfo.getId();
|
|
||||||
boolean hasName = !TextUtils.isEmpty(deviceInfo.getName());
|
|
||||||
this.name.setText(hasName ? deviceInfo.getName() : deviceInfo.getShortId());
|
|
||||||
this.shortId.setText(deviceInfo.getShortId());
|
|
||||||
this.shortId.setVisibility(hasName ? VISIBLE : GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDeviceId() {
|
|
||||||
return deviceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDeviceName() {
|
|
||||||
return name.getText().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasDeviceName() {
|
|
||||||
return shortId.getVisibility() == VISIBLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.database.loaders;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import com.annimon.stream.Stream;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
||||||
import org.thoughtcrime.securesms.devicelist.Device;
|
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
|
||||||
import org.thoughtcrime.securesms.util.AsyncLoader;
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|
||||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class DeviceListLoader extends AsyncLoader<List<Device>> {
|
|
||||||
|
|
||||||
private static final String TAG = DeviceListLoader.class.getSimpleName();
|
|
||||||
|
|
||||||
public DeviceListLoader(Context context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Device> loadInBackground() {
|
|
||||||
try {
|
|
||||||
String userPublicKey = TextSecurePreferences.getLocalNumber(getContext());
|
|
||||||
Set<String> slaveDevicePublicKeys = MultiDeviceProtocol.shared.getSlaveDevices(userPublicKey);
|
|
||||||
List<Device> devices = Stream.of(slaveDevicePublicKeys).map(this::mapToDevice).toList();
|
|
||||||
Collections.sort(devices, new DeviceComparator());
|
|
||||||
return devices;
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Device mapToDevice(@NonNull String hexEncodedPublicKey) {
|
|
||||||
String shortId = "";
|
|
||||||
String name = DatabaseFactory.getLokiUserDatabase(getContext()).getDisplayName(hexEncodedPublicKey);
|
|
||||||
return new Device(hexEncodedPublicKey, shortId, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class DeviceComparator implements Comparator<Device> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compare(Device lhs, Device rhs) {
|
|
||||||
return lhs.getName().compareTo(rhs.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,7 +12,6 @@ import org.session.libsignal.service.api.util.UptimeSleepTimer;
|
|||||||
import org.session.libsignal.service.api.websocket.ConnectivityListener;
|
import org.session.libsignal.service.api.websocket.ConnectivityListener;
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
import org.thoughtcrime.securesms.ApplicationContext;
|
||||||
import org.thoughtcrime.securesms.CreateProfileActivity;
|
import org.thoughtcrime.securesms.CreateProfileActivity;
|
||||||
import org.thoughtcrime.securesms.DeviceListFragment;
|
|
||||||
import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl;
|
import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
|
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
|
||||||
@ -79,7 +78,6 @@ import network.loki.messenger.BuildConfig;
|
|||||||
MultiDeviceGroupUpdateJob.class,
|
MultiDeviceGroupUpdateJob.class,
|
||||||
MultiDeviceReadUpdateJob.class,
|
MultiDeviceReadUpdateJob.class,
|
||||||
MultiDeviceBlockedUpdateJob.class,
|
MultiDeviceBlockedUpdateJob.class,
|
||||||
DeviceListFragment.class,
|
|
||||||
RefreshAttributesJob.class,
|
RefreshAttributesJob.class,
|
||||||
RequestGroupInfoJob.class,
|
RequestGroupInfoJob.class,
|
||||||
PushGroupUpdateJob.class,
|
PushGroupUpdateJob.class,
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.devicelist;
|
|
||||||
|
|
||||||
public class Device {
|
|
||||||
|
|
||||||
private final String id;
|
|
||||||
private final String shortId;
|
|
||||||
private final String name;
|
|
||||||
|
|
||||||
public Device(String id, String shortId, String name) {
|
|
||||||
this.id = id;
|
|
||||||
this.shortId = shortId;
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
public String getShortId() { return shortId; }
|
|
||||||
public String getName() { return name; }
|
|
||||||
}
|
|
@ -13,7 +13,6 @@ import android.view.MenuItem
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import kotlinx.android.synthetic.main.activity_create_closed_group.*
|
import kotlinx.android.synthetic.main.activity_create_closed_group.*
|
||||||
import kotlinx.android.synthetic.main.activity_linked_devices.recyclerView
|
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import nl.komponents.kovenant.ui.successUi
|
import nl.komponents.kovenant.ui.successUi
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
@ -31,6 +30,7 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences
|
|||||||
import org.session.libsignal.libsignal.util.guava.Optional
|
import org.session.libsignal.libsignal.util.guava.Optional
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
|
//TODO Refactor to avoid using kotlinx.android.synthetic
|
||||||
class CreateClosedGroupActivity : PassphraseRequiredActionBarActivity(), LoaderManager.LoaderCallbacks<List<String>> {
|
class CreateClosedGroupActivity : PassphraseRequiredActionBarActivity(), LoaderManager.LoaderCallbacks<List<String>> {
|
||||||
private var isLoading = false
|
private var isLoading = false
|
||||||
set(newValue) { field = newValue; invalidateOptionsMenu() }
|
set(newValue) { field = newValue; invalidateOptionsMenu() }
|
||||||
@ -121,9 +121,9 @@ class CreateClosedGroupActivity : PassphraseRequiredActionBarActivity(), LoaderM
|
|||||||
}
|
}
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(this)
|
val userPublicKey = TextSecurePreferences.getLocalNumber(this)
|
||||||
isLoading = true
|
isLoading = true
|
||||||
loader.fadeIn()
|
loaderContainer.fadeIn()
|
||||||
ClosedGroupsProtocol.createClosedGroup(this, name.toString(), selectedMembers + setOf( userPublicKey )).successUi { groupID ->
|
ClosedGroupsProtocol.createClosedGroup(this, name.toString(), selectedMembers + setOf( userPublicKey )).successUi { groupID ->
|
||||||
loader.fadeOut()
|
loaderContainer.fadeOut()
|
||||||
isLoading = false
|
isLoading = false
|
||||||
val threadID = DatabaseFactory.getThreadDatabase(this).getOrCreateThreadIdFor(Recipient.from(this, Address.fromSerialized(groupID), false))
|
val threadID = DatabaseFactory.getThreadDatabase(this).getOrCreateThreadIdFor(Recipient.from(this, Address.fromSerialized(groupID), false))
|
||||||
if (!isFinishing) {
|
if (!isFinishing) {
|
||||||
|
@ -3,25 +3,20 @@ package org.thoughtcrime.securesms.loki.activities
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.loader.app.LoaderManager
|
|
||||||
import androidx.loader.content.Loader
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import android.widget.Toast
|
import android.widget.*
|
||||||
import androidx.appcompat.content.res.AppCompatResources
|
import androidx.loader.app.LoaderManager
|
||||||
import kotlinx.android.synthetic.main.activity_create_closed_group.*
|
import androidx.loader.content.Loader
|
||||||
import kotlinx.android.synthetic.main.activity_create_closed_group.emptyStateContainer
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import kotlinx.android.synthetic.main.activity_create_closed_group.mainContentContainer
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import kotlinx.android.synthetic.main.activity_edit_closed_group.*
|
|
||||||
import kotlinx.android.synthetic.main.activity_edit_closed_group.loader
|
|
||||||
import kotlinx.android.synthetic.main.activity_linked_devices.recyclerView
|
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import nl.komponents.kovenant.ui.failUi
|
import nl.komponents.kovenant.ui.failUi
|
||||||
import nl.komponents.kovenant.ui.successUi
|
import nl.komponents.kovenant.ui.successUi
|
||||||
|
import org.session.libsignal.service.loki.utilities.toHexString
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
import org.thoughtcrime.securesms.database.Address
|
import org.thoughtcrime.securesms.database.Address
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
@ -32,10 +27,8 @@ import org.thoughtcrime.securesms.loki.utilities.fadeIn
|
|||||||
import org.thoughtcrime.securesms.loki.utilities.fadeOut
|
import org.thoughtcrime.securesms.loki.utilities.fadeOut
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp
|
import org.thoughtcrime.securesms.mms.GlideApp
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.util.GroupUtil
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||||
import org.thoughtcrime.securesms.util.ThemeUtil
|
import org.thoughtcrime.securesms.util.ThemeUtil
|
||||||
import org.session.libsignal.service.loki.utilities.toHexString
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
||||||
@ -60,6 +53,14 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
EditClosedGroupMembersAdapter(this, GlideApp.with(this), this::onMemberClick)
|
EditClosedGroupMembersAdapter(this, GlideApp.with(this), this::onMemberClick)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private lateinit var mainContentContainer: LinearLayout
|
||||||
|
private lateinit var cntGroupNameEdit: LinearLayout
|
||||||
|
private lateinit var cntGroupNameDisplay: LinearLayout
|
||||||
|
private lateinit var edtGroupName: EditText
|
||||||
|
private lateinit var emptyStateContainer: LinearLayout
|
||||||
|
private lateinit var lblGroupNameDisplay: TextView
|
||||||
|
private lateinit var loaderContainer: View
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic val groupIDKey = "groupIDKey"
|
@JvmStatic val groupIDKey = "groupIDKey"
|
||||||
private val loaderID = 0
|
private val loaderID = 0
|
||||||
@ -79,15 +80,27 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
originalName = DatabaseFactory.getGroupDatabase(this).getGroup(groupID).get().title
|
originalName = DatabaseFactory.getGroupDatabase(this).getGroup(groupID).get().title
|
||||||
name = originalName
|
name = originalName
|
||||||
|
|
||||||
addMembersClosedGroupButton.setOnClickListener { onAddMembersClick() }
|
mainContentContainer = findViewById(R.id.mainContentContainer)
|
||||||
|
cntGroupNameEdit = findViewById(R.id.cntGroupNameEdit)
|
||||||
|
cntGroupNameDisplay = findViewById(R.id.cntGroupNameDisplay)
|
||||||
|
edtGroupName = findViewById(R.id.edtGroupName)
|
||||||
|
emptyStateContainer = findViewById(R.id.emptyStateContainer)
|
||||||
|
lblGroupNameDisplay = findViewById(R.id.lblGroupNameDisplay)
|
||||||
|
loaderContainer = findViewById(R.id.loaderContainer)
|
||||||
|
|
||||||
recyclerView.adapter = memberListAdapter
|
findViewById<View>(R.id.addMembersClosedGroupButton).setOnClickListener {
|
||||||
recyclerView.layoutManager = LinearLayoutManager(this)
|
onAddMembersClick()
|
||||||
|
}
|
||||||
|
|
||||||
|
findViewById<RecyclerView>(R.id.rvUserList).apply {
|
||||||
|
adapter = memberListAdapter
|
||||||
|
layoutManager = LinearLayoutManager(this@EditClosedGroupActivity)
|
||||||
|
}
|
||||||
|
|
||||||
lblGroupNameDisplay.text = originalName
|
lblGroupNameDisplay.text = originalName
|
||||||
cntGroupNameDisplay.setOnClickListener { isEditingName = true }
|
cntGroupNameDisplay.setOnClickListener { isEditingName = true }
|
||||||
btnCancelGroupNameEdit.setOnClickListener { isEditingName = false }
|
findViewById<Button>(R.id.btnCancelGroupNameEdit).setOnClickListener { isEditingName = false }
|
||||||
btnSaveGroupNameEdit.setOnClickListener { saveName() }
|
findViewById<Button>(R.id.btnSaveGroupNameEdit).setOnClickListener { saveName() }
|
||||||
edtGroupName.setImeActionLabel(getString(R.string.save), EditorInfo.IME_ACTION_DONE)
|
edtGroupName.setImeActionLabel(getString(R.string.save), EditorInfo.IME_ACTION_DONE)
|
||||||
edtGroupName.setOnEditorActionListener { _, actionId, _ ->
|
edtGroupName.setOnEditorActionListener { _, actionId, _ ->
|
||||||
when (actionId) {
|
when (actionId) {
|
||||||
@ -247,9 +260,9 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
|
|
||||||
if (isSSKBasedClosedGroup) {
|
if (isSSKBasedClosedGroup) {
|
||||||
isLoading = true
|
isLoading = true
|
||||||
loader.fadeIn()
|
loaderContainer.fadeIn()
|
||||||
ClosedGroupsProtocol.update(this, groupPublicKey!!, members.map { it.address.serialize() }, name).successUi {
|
ClosedGroupsProtocol.update(this, groupPublicKey!!, members.map { it.address.serialize() }, name).successUi {
|
||||||
loader.fadeOut()
|
loaderContainer.fadeOut()
|
||||||
isLoading = false
|
isLoading = false
|
||||||
finish()
|
finish()
|
||||||
}.failUi { exception ->
|
}.failUi { exception ->
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package org.thoughtcrime.securesms.loki.activities
|
package org.thoughtcrime.securesms.loki.activities
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.AsyncTask
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import kotlinx.android.synthetic.main.activity_landing.*
|
import kotlinx.android.synthetic.main.activity_landing.*
|
||||||
@ -9,34 +8,11 @@ import network.loki.messenger.R
|
|||||||
import org.thoughtcrime.securesms.ApplicationContext
|
import org.thoughtcrime.securesms.ApplicationContext
|
||||||
import org.thoughtcrime.securesms.BaseActionBarActivity
|
import org.thoughtcrime.securesms.BaseActionBarActivity
|
||||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
||||||
import org.thoughtcrime.securesms.database.Address
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
|
||||||
import org.thoughtcrime.securesms.database.IdentityDatabase
|
|
||||||
import org.thoughtcrime.securesms.logging.Log
|
|
||||||
import org.thoughtcrime.securesms.loki.dialogs.LinkDeviceSlaveModeDialog
|
|
||||||
import org.thoughtcrime.securesms.loki.dialogs.LinkDeviceSlaveModeDialogDelegate
|
|
||||||
import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation
|
|
||||||
import org.thoughtcrime.securesms.loki.protocol.shelved.MultiDeviceProtocol
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.push
|
import org.thoughtcrime.securesms.loki.utilities.push
|
||||||
import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo
|
import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo
|
||||||
import org.thoughtcrime.securesms.loki.utilities.show
|
|
||||||
import org.thoughtcrime.securesms.util.Base64
|
|
||||||
import org.thoughtcrime.securesms.util.Hex
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||||
import org.whispersystems.curve25519.Curve25519
|
|
||||||
import org.session.libsignal.libsignal.ecc.Curve
|
|
||||||
import org.session.libsignal.libsignal.ecc.ECKeyPair
|
|
||||||
import org.session.libsignal.libsignal.util.KeyHelper
|
|
||||||
import org.session.libsignal.service.loki.protocol.mentions.MentionsManager
|
|
||||||
import org.session.libsignal.service.loki.protocol.meta.SessionMetaProtocol
|
|
||||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink
|
|
||||||
import org.session.libsignal.service.loki.protocol.sessionmanagement.SessionManagementProtocol
|
|
||||||
import org.session.libsignal.service.loki.protocol.shelved.syncmessages.SyncMessagesProtocol
|
|
||||||
import org.session.libsignal.service.loki.utilities.hexEncodedPublicKey
|
|
||||||
import org.session.libsignal.service.loki.utilities.retryIfNeeded
|
|
||||||
import java.lang.UnsupportedOperationException
|
|
||||||
|
|
||||||
class LandingActivity : BaseActionBarActivity(), LinkDeviceSlaveModeDialogDelegate {
|
class LandingActivity : BaseActionBarActivity() {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@ -44,109 +20,26 @@ class LandingActivity : BaseActionBarActivity(), LinkDeviceSlaveModeDialogDelega
|
|||||||
setUpActionBarSessionLogo(true)
|
setUpActionBarSessionLogo(true)
|
||||||
fakeChatView.startAnimating()
|
fakeChatView.startAnimating()
|
||||||
registerButton.setOnClickListener { register() }
|
registerButton.setOnClickListener { register() }
|
||||||
restoreButton.setOnClickListener { restore() }
|
restoreButton.setOnClickListener { restoreFromRecoveryPhrase() }
|
||||||
restoreBackupButton.setOnClickListener {
|
restoreBackupButton.setOnClickListener { restoreFromBackup() }
|
||||||
val intent = Intent(this, BackupRestoreActivity::class.java)
|
|
||||||
push(intent)
|
|
||||||
}
|
|
||||||
// linkButton.setOnClickListener { linkDevice() }
|
|
||||||
if (TextSecurePreferences.getWasUnlinked(this)) {
|
if (TextSecurePreferences.getWasUnlinked(this)) {
|
||||||
Toast.makeText(this, R.string.activity_landing_device_unlinked_dialog_title, Toast.LENGTH_LONG).show()
|
Toast.makeText(this, R.string.activity_landing_device_unlinked_dialog_title, Toast.LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
|
||||||
super.onActivityResult(requestCode, resultCode, intent)
|
|
||||||
if (resultCode != RESULT_OK) { return }
|
|
||||||
val hexEncodedPublicKey = data!!.getStringExtra("hexEncodedPublicKey")
|
|
||||||
requestDeviceLink(hexEncodedPublicKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun register() {
|
private fun register() {
|
||||||
val intent = Intent(this, RegisterActivity::class.java)
|
val intent = Intent(this, RegisterActivity::class.java)
|
||||||
push(intent)
|
push(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun restore() {
|
private fun restoreFromRecoveryPhrase() {
|
||||||
val intent = Intent(this, RestoreActivity::class.java)
|
val intent = Intent(this, RecoveryPhraseRestoreActivity::class.java)
|
||||||
push(intent)
|
push(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun linkDevice() {
|
private fun restoreFromBackup() {
|
||||||
val intent = Intent(this, LinkDeviceActivity::class.java)
|
val intent = Intent(this, BackupRestoreActivity::class.java)
|
||||||
show(intent, true)
|
push(intent)
|
||||||
}
|
|
||||||
|
|
||||||
private fun requestDeviceLink(hexEncodedPublicKey: String) {
|
|
||||||
var seed: ByteArray? = null
|
|
||||||
var keyPair: ECKeyPair? = null
|
|
||||||
|
|
||||||
//FIXME AC: Previously we used the modified version of the Signal's Curve25519 lib to generate the seed and key pair.
|
|
||||||
// If you need to restore this logic you should probably fork and patch the lib to support that method as well.
|
|
||||||
// https://github.com/signalapp/curve25519-java
|
|
||||||
fun generateKeyPair() {
|
|
||||||
throw UnsupportedOperationException("Generating device link key pair is not supported at the moment.")
|
|
||||||
// val seedCandidate = Curve25519.getInstance(Curve25519.BEST).generateSeed(16)
|
|
||||||
// try {
|
|
||||||
// keyPair = Curve.generateKeyPair(seedCandidate + seedCandidate) // Validate the seed
|
|
||||||
// } catch (exception: Exception) {
|
|
||||||
// return generateKeyPair()
|
|
||||||
// }
|
|
||||||
// seed = seedCandidate
|
|
||||||
}
|
|
||||||
generateKeyPair()
|
|
||||||
IdentityKeyUtil.save(this, IdentityKeyUtil.LOKI_SEED, Hex.toStringCondensed(seed))
|
|
||||||
IdentityKeyUtil.save(this, IdentityKeyUtil.IDENTITY_PUBLIC_KEY_PREF, Base64.encodeBytes(keyPair!!.publicKey.serialize()))
|
|
||||||
IdentityKeyUtil.save(this, IdentityKeyUtil.IDENTITY_PRIVATE_KEY_PREF, Base64.encodeBytes(keyPair!!.privateKey.serialize()))
|
|
||||||
val userHexEncodedPublicKey = keyPair!!.hexEncodedPublicKey
|
|
||||||
val registrationID = KeyHelper.generateRegistrationId(false)
|
|
||||||
TextSecurePreferences.setLocalRegistrationId(this, registrationID)
|
|
||||||
DatabaseFactory.getIdentityDatabase(this).saveIdentity(Address.fromSerialized(userHexEncodedPublicKey),
|
|
||||||
IdentityKeyUtil.getIdentityKeyPair(this).publicKey, IdentityDatabase.VerifiedStatus.VERIFIED,
|
|
||||||
true, System.currentTimeMillis(), true)
|
|
||||||
TextSecurePreferences.setLocalNumber(this, userHexEncodedPublicKey)
|
|
||||||
TextSecurePreferences.setHasSeenWelcomeScreen(this, true)
|
|
||||||
TextSecurePreferences.setPromptedPushRegistration(this, true)
|
|
||||||
val deviceLink = DeviceLink(hexEncodedPublicKey, userHexEncodedPublicKey).sign(DeviceLink.Type.REQUEST, keyPair!!.privateKey.serialize())
|
|
||||||
if (deviceLink == null) {
|
|
||||||
Log.d("Loki", "Failed to sign device link request.")
|
|
||||||
reset()
|
|
||||||
return Toast.makeText(application, R.string.device_linking_failed, Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
val application = ApplicationContext.getInstance(this)
|
|
||||||
application.startPollingIfNeeded()
|
|
||||||
val apiDB = DatabaseFactory.getLokiAPIDatabase(this)
|
|
||||||
val threadDB = DatabaseFactory.getLokiThreadDatabase(this)
|
|
||||||
val userDB = DatabaseFactory.getLokiUserDatabase(this)
|
|
||||||
val sskDatabase = DatabaseFactory.getSSKDatabase(this)
|
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(this)
|
|
||||||
val sessionResetImpl = SessionResetImplementation(this)
|
|
||||||
MentionsManager.configureIfNeeded(userPublicKey, threadDB, userDB)
|
|
||||||
SessionMetaProtocol.configureIfNeeded(apiDB, userPublicKey)
|
|
||||||
org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol.configureIfNeeded(apiDB)
|
|
||||||
SessionManagementProtocol.configureIfNeeded(sessionResetImpl, sskDatabase, application)
|
|
||||||
SyncMessagesProtocol.configureIfNeeded(apiDB, userPublicKey)
|
|
||||||
application.setUpP2PAPIIfNeeded()
|
|
||||||
application.setUpStorageAPIIfNeeded()
|
|
||||||
val linkDeviceDialog = LinkDeviceSlaveModeDialog()
|
|
||||||
linkDeviceDialog.delegate = this
|
|
||||||
linkDeviceDialog.show(supportFragmentManager, "Link Device Dialog")
|
|
||||||
AsyncTask.execute {
|
|
||||||
retryIfNeeded(8) {
|
|
||||||
MultiDeviceProtocol.sendDeviceLinkMessage(this@LandingActivity, deviceLink.masterPublicKey, deviceLink)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) {
|
|
||||||
TextSecurePreferences.setMasterHexEncodedPublicKey(this, deviceLink.masterPublicKey)
|
|
||||||
val intent = Intent(this, HomeActivity::class.java)
|
|
||||||
show(intent)
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDeviceLinkCanceled() {
|
|
||||||
reset()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reset() {
|
private fun reset() {
|
||||||
|
@ -1,104 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.loki.activities
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.fragment.app.FragmentPagerAdapter
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.view.inputmethod.InputMethodManager
|
|
||||||
import android.widget.Toast
|
|
||||||
import kotlinx.android.synthetic.main.activity_link_device.*
|
|
||||||
import kotlinx.android.synthetic.main.fragment_enter_session_id.*
|
|
||||||
import network.loki.messenger.R
|
|
||||||
import org.thoughtcrime.securesms.BaseActionBarActivity
|
|
||||||
import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragment
|
|
||||||
import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragmentDelegate
|
|
||||||
import org.session.libsignal.service.loki.utilities.PublicKeyValidation
|
|
||||||
|
|
||||||
class LinkDeviceActivity : BaseActionBarActivity(), ScanQRCodeWrapperFragmentDelegate {
|
|
||||||
private val adapter = LinkDeviceActivityAdapter(this)
|
|
||||||
|
|
||||||
// region Lifecycle
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
// Set content view
|
|
||||||
setContentView(R.layout.activity_link_device)
|
|
||||||
// Set title
|
|
||||||
supportActionBar!!.title = resources.getString(R.string.activity_link_device_title)
|
|
||||||
// Set up view pager
|
|
||||||
viewPager.adapter = adapter
|
|
||||||
tabLayout.setupWithViewPager(viewPager)
|
|
||||||
}
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Interaction
|
|
||||||
override fun handleQRCodeScanned(hexEncodedPublicKey: String) {
|
|
||||||
requestDeviceLinkIfPossible(hexEncodedPublicKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun requestDeviceLinkIfPossible(hexEncodedPublicKey: String) {
|
|
||||||
if (!PublicKeyValidation.isValid(hexEncodedPublicKey)) {
|
|
||||||
Toast.makeText(this, R.string.invalid_session_id, Toast.LENGTH_SHORT).show()
|
|
||||||
} else {
|
|
||||||
val intent = Intent()
|
|
||||||
intent.putExtra("hexEncodedPublicKey", hexEncodedPublicKey)
|
|
||||||
setResult(RESULT_OK, intent)
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// endregion
|
|
||||||
}
|
|
||||||
|
|
||||||
// region Adapter
|
|
||||||
private class LinkDeviceActivityAdapter(val activity: LinkDeviceActivity) : FragmentPagerAdapter(activity.supportFragmentManager) {
|
|
||||||
|
|
||||||
override fun getCount(): Int {
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getItem(index: Int): Fragment {
|
|
||||||
return when (index) {
|
|
||||||
0 -> EnterSessionIDFragment()
|
|
||||||
1 -> {
|
|
||||||
val result = ScanQRCodeWrapperFragment()
|
|
||||||
result.delegate = activity
|
|
||||||
result.message = activity.resources.getString(R.string.activity_link_device_scan_qr_code_explanation)
|
|
||||||
result
|
|
||||||
}
|
|
||||||
else -> throw IllegalStateException()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getPageTitle(index: Int): CharSequence? {
|
|
||||||
return when (index) {
|
|
||||||
0 -> activity.getString(R.string.activity_link_device_enter_session_id_tab_title)
|
|
||||||
1 -> activity.getString(R.string.activity_link_device_scan_qr_code_tab_title)
|
|
||||||
else -> throw IllegalStateException()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Enter Session ID Fragment
|
|
||||||
class EnterSessionIDFragment : Fragment() {
|
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
|
||||||
return inflater.inflate(R.layout.fragment_enter_session_id, container, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
sessionIDEditText.imeOptions = sessionIDEditText.imeOptions or 16777216 // Always use incognito keyboard
|
|
||||||
requestDeviceLinkButton.setOnClickListener { requestDeviceLinkIfPossible() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun requestDeviceLinkIfPossible() {
|
|
||||||
val inputMethodManager = context!!.getSystemService(BaseActionBarActivity.INPUT_METHOD_SERVICE) as InputMethodManager
|
|
||||||
inputMethodManager.hideSoftInputFromWindow(sessionIDEditText.windowToken, 0)
|
|
||||||
val hexEncodedPublicKey = sessionIDEditText.text.trim().toString().toLowerCase()
|
|
||||||
(activity!! as LinkDeviceActivity).requestDeviceLinkIfPossible(hexEncodedPublicKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// endregion
|
|
@ -1,178 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.loki.activities
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.loader.app.LoaderManager
|
|
||||||
import androidx.loader.content.Loader
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import android.view.Menu
|
|
||||||
import android.view.MenuItem
|
|
||||||
import android.view.View
|
|
||||||
import android.widget.Toast
|
|
||||||
import kotlinx.android.synthetic.main.activity_linked_devices.*
|
|
||||||
import network.loki.messenger.R
|
|
||||||
import nl.komponents.kovenant.ui.failUi
|
|
||||||
import nl.komponents.kovenant.ui.successUi
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext
|
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
|
||||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
|
|
||||||
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
|
||||||
import org.thoughtcrime.securesms.devicelist.Device
|
|
||||||
import org.thoughtcrime.securesms.logging.Log
|
|
||||||
import org.thoughtcrime.securesms.loki.dialogs.*
|
|
||||||
import org.thoughtcrime.securesms.loki.protocol.shelved.SyncMessagesProtocol
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.recipient
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceDataMessage
|
|
||||||
import org.session.libsignal.service.api.push.SignalServiceAddress
|
|
||||||
import org.session.libsignal.service.loki.api.fileserver.FileServerAPI
|
|
||||||
import java.util.*
|
|
||||||
import kotlin.concurrent.schedule
|
|
||||||
|
|
||||||
class LinkedDevicesActivity : PassphraseRequiredActionBarActivity, LoaderManager.LoaderCallbacks<List<Device>>, DeviceClickListener, EditDeviceNameDialogDelegate, LinkDeviceMasterModeDialogDelegate {
|
|
||||||
private var devices = listOf<Device>()
|
|
||||||
set(value) { field = value; linkedDevicesAdapter.devices = value }
|
|
||||||
|
|
||||||
private val linkedDevicesAdapter by lazy {
|
|
||||||
val result = LinkedDevicesAdapter(this)
|
|
||||||
result.deviceClickListener = this
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
// region Lifecycle
|
|
||||||
constructor() : super()
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) {
|
|
||||||
super.onCreate(savedInstanceState, isReady)
|
|
||||||
setContentView(R.layout.activity_linked_devices)
|
|
||||||
supportActionBar!!.title = resources.getString(R.string.activity_linked_devices_title)
|
|
||||||
recyclerView.adapter = linkedDevicesAdapter
|
|
||||||
recyclerView.layoutManager = LinearLayoutManager(this)
|
|
||||||
linkDeviceButton.setOnClickListener { linkDevice() }
|
|
||||||
LoaderManager.getInstance(this).initLoader(0, null, this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
|
||||||
menuInflater.inflate(R.menu.menu_linked_devices, menu)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Updating
|
|
||||||
override fun onCreateLoader(id: Int, bundle: Bundle?): Loader<List<Device>> {
|
|
||||||
return LinkedDevicesLoader(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onLoadFinished(loader: Loader<List<Device>>, devices: List<Device>?) {
|
|
||||||
update(devices ?: listOf())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onLoaderReset(loader: Loader<List<Device>>) {
|
|
||||||
update(listOf())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun update(devices: List<Device>) {
|
|
||||||
this.devices = devices
|
|
||||||
emptyStateContainer.visibility = if (devices.isEmpty()) View.VISIBLE else View.GONE
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun handleDeviceNameChanged(device: Device) {
|
|
||||||
LoaderManager.getInstance(this).restartLoader(0, null, this)
|
|
||||||
}
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Interaction
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
|
||||||
val id = item.itemId
|
|
||||||
when(id) {
|
|
||||||
R.id.linkDeviceButton -> linkDevice()
|
|
||||||
else -> { /* Do nothing */ }
|
|
||||||
}
|
|
||||||
return super.onOptionsItemSelected(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun linkDevice() {
|
|
||||||
if (devices.isEmpty()) {
|
|
||||||
val linkDeviceDialog = LinkDeviceMasterModeDialog()
|
|
||||||
linkDeviceDialog.delegate = this
|
|
||||||
linkDeviceDialog.show(supportFragmentManager, "Link Device Dialog")
|
|
||||||
} else {
|
|
||||||
val builder = AlertDialog.Builder(this)
|
|
||||||
builder.setTitle(resources.getString(R.string.activity_linked_devices_multi_device_limit_reached_dialog_title))
|
|
||||||
builder.setMessage(resources.getString(R.string.activity_linked_devices_multi_device_limit_reached_dialog_explanation))
|
|
||||||
builder.setPositiveButton(resources.getString(R.string.ok), { dialog, _ -> dialog.dismiss() })
|
|
||||||
builder.create().show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDeviceClick(device: Device) {
|
|
||||||
val bottomSheet = DeviceEditingOptionsBottomSheet()
|
|
||||||
bottomSheet.onEditTapped = {
|
|
||||||
bottomSheet.dismiss()
|
|
||||||
val editDeviceNameDialog = EditDeviceNameDialog()
|
|
||||||
editDeviceNameDialog.device = device
|
|
||||||
editDeviceNameDialog.delegate = this
|
|
||||||
editDeviceNameDialog.show(supportFragmentManager, "Edit Device Name Dialog")
|
|
||||||
}
|
|
||||||
bottomSheet.onUnlinkTapped = {
|
|
||||||
bottomSheet.dismiss()
|
|
||||||
unlinkDevice(device.id)
|
|
||||||
}
|
|
||||||
bottomSheet.show(supportFragmentManager, bottomSheet.tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun unlinkDevice(slaveDevicePublicKey: String) {
|
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(this)
|
|
||||||
val apiDB = DatabaseFactory.getLokiAPIDatabase(this)
|
|
||||||
val deviceLinks = apiDB.getDeviceLinks(userPublicKey)
|
|
||||||
val deviceLink = deviceLinks.find { it.masterPublicKey == userPublicKey && it.slavePublicKey == slaveDevicePublicKey }
|
|
||||||
if (deviceLink == null) {
|
|
||||||
return Toast.makeText(this, R.string.activity_linked_devices_unlinking_failed_message, Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
FileServerAPI.shared.setDeviceLinks(setOf()).successUi {
|
|
||||||
DatabaseFactory.getLokiAPIDatabase(this).clearDeviceLinks(userPublicKey)
|
|
||||||
deviceLinks.forEach { deviceLink ->
|
|
||||||
// We don't use PushEphemeralMessageJob because want these messages to send before the pre key and
|
|
||||||
// session associated with the slave device have been deleted
|
|
||||||
val unlinkingRequest = SignalServiceDataMessage.newBuilder()
|
|
||||||
.withTimestamp(System.currentTimeMillis())
|
|
||||||
.asDeviceUnlinkingRequest(true)
|
|
||||||
val messageSender = ApplicationContext.getInstance(this@LinkedDevicesActivity).communicationModule.provideSignalMessageSender()
|
|
||||||
val address = SignalServiceAddress(deviceLink.slavePublicKey)
|
|
||||||
try {
|
|
||||||
val udAccess = UnidentifiedAccessUtil.getAccessFor(this@LinkedDevicesActivity, recipient(this@LinkedDevicesActivity, deviceLink.slavePublicKey))
|
|
||||||
messageSender.sendMessage(0, address, udAccess, unlinkingRequest.build()) // The message ID doesn't matter
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.d("Loki", "Failed to send unlinking request due to error: $e.")
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
DatabaseFactory.getLokiPreKeyBundleDatabase(this).removePreKeyBundle(deviceLink.slavePublicKey)
|
|
||||||
val sessionStore = TextSecureSessionStore(this@LinkedDevicesActivity)
|
|
||||||
sessionStore.deleteAllSessions(deviceLink.slavePublicKey)
|
|
||||||
}
|
|
||||||
LoaderManager.getInstance(this).restartLoader(0, null, this)
|
|
||||||
Toast.makeText(this, R.string.activity_linked_devices_unlinking_successful_message, Toast.LENGTH_LONG).show()
|
|
||||||
}.failUi {
|
|
||||||
Toast.makeText(this, R.string.activity_linked_devices_unlinking_failed_message, Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDeviceLinkRequestAuthorized() {
|
|
||||||
SyncMessagesProtocol.syncAllClosedGroups(this)
|
|
||||||
SyncMessagesProtocol.syncAllOpenGroups(this)
|
|
||||||
Timer().schedule(4000) { // Not the best way to do this but the idea is to wait for the closed groups sync to go through first
|
|
||||||
SyncMessagesProtocol.syncAllContacts(this@LinkedDevicesActivity)
|
|
||||||
}
|
|
||||||
LoaderManager.getInstance(this).restartLoader(0, null, this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDeviceLinkAuthorizationFailed() {
|
|
||||||
Toast.makeText(this, R.string.activity_linked_devices_linking_failed_message, Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDeviceLinkCanceled() {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
// endregion
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.loki.activities
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import org.thoughtcrime.securesms.devicelist.Device
|
|
||||||
import org.thoughtcrime.securesms.loki.views.DeviceView
|
|
||||||
|
|
||||||
class LinkedDevicesAdapter(private val context: Context) : RecyclerView.Adapter<LinkedDevicesAdapter.ViewHolder>() {
|
|
||||||
var devices = listOf<Device>()
|
|
||||||
set(value) { field = value; notifyDataSetChanged() }
|
|
||||||
var deviceClickListener: DeviceClickListener? = null
|
|
||||||
|
|
||||||
class ViewHolder(val view: DeviceView) : RecyclerView.ViewHolder(view)
|
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
|
||||||
return devices.size
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
|
||||||
val view = DeviceView(context)
|
|
||||||
return ViewHolder(view)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
|
|
||||||
val device = devices[position]
|
|
||||||
viewHolder.view.setOnClickListener { deviceClickListener?.onDeviceClick(device) }
|
|
||||||
viewHolder.view.bind(device)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DeviceClickListener {
|
|
||||||
|
|
||||||
fun onDeviceClick(device: Device)
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.loki.activities
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
|
||||||
import org.thoughtcrime.securesms.devicelist.Device
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities
|
|
||||||
import org.thoughtcrime.securesms.util.AsyncLoader
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
|
||||||
import org.session.libsignal.service.loki.crypto.MnemonicCodec
|
|
||||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class LinkedDevicesLoader(context: Context) : AsyncLoader<List<Device>>(context) {
|
|
||||||
|
|
||||||
private val mnemonicCodec by lazy {
|
|
||||||
val loadFileContents: (String) -> String = { fileName ->
|
|
||||||
MnemonicUtilities.loadFileContents(context, fileName)
|
|
||||||
}
|
|
||||||
MnemonicCodec(loadFileContents)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun loadInBackground(): List<Device>? {
|
|
||||||
try {
|
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
|
|
||||||
val slaveDevices = MultiDeviceProtocol.shared.getSlaveDevices(userPublicKey)
|
|
||||||
return slaveDevices.map { device ->
|
|
||||||
val shortID = MnemonicUtilities.getFirst3Words(mnemonicCodec, device)
|
|
||||||
val name = DatabaseFactory.getLokiUserDatabase(context).getDisplayName(device)
|
|
||||||
Device(device, shortID, name)
|
|
||||||
}.sortedBy { it.name }
|
|
||||||
} catch (e: Exception) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -11,7 +11,7 @@ import android.text.style.ClickableSpan
|
|||||||
import android.text.style.StyleSpan
|
import android.text.style.StyleSpan
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import kotlinx.android.synthetic.main.activity_restore.*
|
import kotlinx.android.synthetic.main.activity_recovery_phrase_restore.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.thoughtcrime.securesms.BaseActionBarActivity
|
import org.thoughtcrime.securesms.BaseActionBarActivity
|
||||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
||||||
@ -28,13 +28,13 @@ import org.session.libsignal.libsignal.util.KeyHelper
|
|||||||
import org.session.libsignal.service.loki.crypto.MnemonicCodec
|
import org.session.libsignal.service.loki.crypto.MnemonicCodec
|
||||||
import org.session.libsignal.service.loki.utilities.hexEncodedPublicKey
|
import org.session.libsignal.service.loki.utilities.hexEncodedPublicKey
|
||||||
|
|
||||||
class RestoreActivity : BaseActionBarActivity() {
|
class RecoveryPhraseRestoreActivity : BaseActionBarActivity() {
|
||||||
|
|
||||||
// region Lifecycle
|
// region Lifecycle
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setUpActionBarSessionLogo()
|
setUpActionBarSessionLogo()
|
||||||
setContentView(R.layout.activity_restore)
|
setContentView(R.layout.activity_recovery_phrase_restore)
|
||||||
mnemonicEditText.imeOptions = mnemonicEditText.imeOptions or 16777216 // Always use incognito keyboard
|
mnemonicEditText.imeOptions = mnemonicEditText.imeOptions or 16777216 // Always use incognito keyboard
|
||||||
restoreButton.setOnClickListener { restore() }
|
restoreButton.setOnClickListener { restore() }
|
||||||
val termsExplanation = SpannableStringBuilder("By using this service, you agree to our Terms of Service and Privacy Policy")
|
val termsExplanation = SpannableStringBuilder("By using this service, you agree to our Terms of Service and Privacy Policy")
|
@ -9,15 +9,15 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import kotlinx.android.synthetic.main.activity_create_closed_group.*
|
|
||||||
import kotlinx.android.synthetic.main.activity_create_closed_group.emptyStateContainer
|
import kotlinx.android.synthetic.main.activity_create_closed_group.emptyStateContainer
|
||||||
import kotlinx.android.synthetic.main.activity_create_closed_group.mainContentContainer
|
import kotlinx.android.synthetic.main.activity_create_closed_group.mainContentContainer
|
||||||
import kotlinx.android.synthetic.main.activity_linked_devices.recyclerView
|
|
||||||
import kotlinx.android.synthetic.main.activity_select_contacts.*
|
import kotlinx.android.synthetic.main.activity_select_contacts.*
|
||||||
|
import kotlinx.android.synthetic.main.activity_select_contacts.recyclerView
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp
|
import org.thoughtcrime.securesms.mms.GlideApp
|
||||||
|
|
||||||
|
//TODO Refactor to avoid using kotlinx.android.synthetic
|
||||||
class SelectContactsActivity : PassphraseRequiredActionBarActivity(), LoaderManager.LoaderCallbacks<List<String>> {
|
class SelectContactsActivity : PassphraseRequiredActionBarActivity(), LoaderManager.LoaderCallbacks<List<String>> {
|
||||||
private var members = listOf<String>()
|
private var members = listOf<String>()
|
||||||
set(value) { field = value; selectContactsAdapter.members = value }
|
set(value) { field = value; selectContactsAdapter.members = value }
|
||||||
|
@ -288,11 +288,6 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
push(intent)
|
push(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showLinkedDevices() {
|
|
||||||
val intent = Intent(this, LinkedDevicesActivity::class.java)
|
|
||||||
push(intent)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun sendInvitation() {
|
private fun sendInvitation() {
|
||||||
val intent = Intent()
|
val intent = Intent()
|
||||||
intent.action = Intent.ACTION_SEND
|
intent.action = Intent.ACTION_SEND
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.loki.dialogs
|
|
||||||
|
|
||||||
import android.app.Dialog
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.graphics.drawable.ColorDrawable
|
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.fragment.app.DialogFragment
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import kotlinx.android.synthetic.main.dialog_edit_device_name.view.*
|
|
||||||
import network.loki.messenger.R
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
|
||||||
import org.thoughtcrime.securesms.devicelist.Device
|
|
||||||
|
|
||||||
class EditDeviceNameDialog : DialogFragment() {
|
|
||||||
private lateinit var contentView: View
|
|
||||||
var device: Device? = null
|
|
||||||
var delegate: EditDeviceNameDialogDelegate? = null
|
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
|
||||||
val builder = AlertDialog.Builder(context!!)
|
|
||||||
contentView = LayoutInflater.from(context!!).inflate(R.layout.dialog_edit_device_name, null)
|
|
||||||
contentView.cancelButton.setOnClickListener { dismiss() }
|
|
||||||
contentView.okButton.setOnClickListener { updateDeviceName() }
|
|
||||||
builder.setView(contentView)
|
|
||||||
val result = builder.create()
|
|
||||||
result.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateDeviceName() {
|
|
||||||
DatabaseFactory.getLokiUserDatabase(context).setDisplayName(device!!.id, contentView.deviceNameEditText.text.toString())
|
|
||||||
delegate?.handleDeviceNameChanged(device!!)
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface EditDeviceNameDialogDelegate {
|
|
||||||
|
|
||||||
fun handleDeviceNameChanged(device: Device)
|
|
||||||
}
|
|
@ -1,121 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.loki.dialogs
|
|
||||||
|
|
||||||
import android.app.Dialog
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.graphics.drawable.ColorDrawable
|
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.fragment.app.DialogFragment
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.widget.LinearLayout
|
|
||||||
import kotlinx.android.synthetic.main.dialog_link_device_master_mode.view.*
|
|
||||||
import network.loki.messenger.R
|
|
||||||
import nl.komponents.kovenant.functional.bind
|
|
||||||
import nl.komponents.kovenant.ui.failUi
|
|
||||||
import nl.komponents.kovenant.ui.successUi
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
|
||||||
import org.thoughtcrime.securesms.loki.protocol.shelved.MultiDeviceProtocol
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.QRCodeUtilities
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.toPx
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
|
||||||
import org.thoughtcrime.securesms.util.Util
|
|
||||||
import org.session.libsignal.service.loki.api.SnodeAPI
|
|
||||||
import org.session.libsignal.service.loki.api.fileserver.FileServerAPI
|
|
||||||
import org.session.libsignal.service.loki.crypto.MnemonicCodec
|
|
||||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink
|
|
||||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLinkingSession
|
|
||||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLinkingSessionListener
|
|
||||||
|
|
||||||
class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListener {
|
|
||||||
private lateinit var contentView: View
|
|
||||||
private var deviceLink: DeviceLink? = null
|
|
||||||
var delegate: LinkDeviceMasterModeDialogDelegate? = null
|
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
|
||||||
val builder = AlertDialog.Builder(requireContext())
|
|
||||||
contentView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_link_device_master_mode, null)
|
|
||||||
val size = toPx(128, resources)
|
|
||||||
val hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(requireContext())
|
|
||||||
val qrCode = QRCodeUtilities.encode(hexEncodedPublicKey, size, false, false)
|
|
||||||
contentView.qrCodeImageView.setImageBitmap(qrCode)
|
|
||||||
contentView.cancelButton.setOnClickListener { onDeviceLinkCanceled() }
|
|
||||||
contentView.authorizeButton.setOnClickListener { authorizeDeviceLink() }
|
|
||||||
builder.setView(contentView)
|
|
||||||
DeviceLinkingSession.shared.startListeningForLinkingRequests() // FIXME: This flag is named poorly as it's actually also used for authorizations
|
|
||||||
DeviceLinkingSession.shared.addListener(this)
|
|
||||||
val result = builder.create()
|
|
||||||
result.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun requestUserAuthorization(deviceLink: DeviceLink) {
|
|
||||||
if (deviceLink.type != DeviceLink.Type.REQUEST || deviceLink.masterPublicKey != TextSecurePreferences.getLocalNumber(requireContext()) || this.deviceLink != null) { return }
|
|
||||||
Util.runOnMain {
|
|
||||||
this.deviceLink = deviceLink
|
|
||||||
contentView.qrCodeImageView.visibility = View.GONE
|
|
||||||
val titleTextViewLayoutParams = contentView.titleTextView.layoutParams as LinearLayout.LayoutParams
|
|
||||||
titleTextViewLayoutParams.topMargin = toPx(8, resources)
|
|
||||||
contentView.titleTextView.layoutParams = titleTextViewLayoutParams
|
|
||||||
contentView.titleTextView.text = resources.getString(R.string.dialog_link_device_master_mode_title_2)
|
|
||||||
contentView.explanationTextView.text = resources.getString(R.string.dialog_link_device_master_mode_explanation_2)
|
|
||||||
contentView.mnemonicTextView.visibility = View.VISIBLE
|
|
||||||
val loadFileContents: (String) -> String = { fileName ->
|
|
||||||
MnemonicUtilities.loadFileContents(requireContext(), fileName)
|
|
||||||
}
|
|
||||||
contentView.mnemonicTextView.text = MnemonicUtilities.getFirst3Words(MnemonicCodec(loadFileContents), deviceLink.slavePublicKey)
|
|
||||||
contentView.authorizeButton.visibility = View.VISIBLE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun authorizeDeviceLink() {
|
|
||||||
val deviceLink = this.deviceLink ?: return
|
|
||||||
DeviceLinkingSession.shared.stopListeningForLinkingRequests()
|
|
||||||
DeviceLinkingSession.shared.removeListener(this)
|
|
||||||
Util.runOnMain {
|
|
||||||
contentView.qrCodeImageViewContainer.visibility = View.GONE
|
|
||||||
contentView.spinner.visibility = View.VISIBLE
|
|
||||||
val titleTextViewLayoutParams = contentView.titleTextView.layoutParams as LinearLayout.LayoutParams
|
|
||||||
titleTextViewLayoutParams.topMargin = toPx(24, resources)
|
|
||||||
contentView.titleTextView.layoutParams = titleTextViewLayoutParams
|
|
||||||
contentView.titleTextView.text = resources.getString(R.string.dialog_link_device_master_mode_title_3)
|
|
||||||
contentView.explanationTextView.text = resources.getString(R.string.dialog_link_device_master_mode_explanation_3)
|
|
||||||
contentView.mnemonicTextView.visibility = View.GONE
|
|
||||||
contentView.buttonContainer.visibility = View.GONE
|
|
||||||
contentView.cancelButton.visibility = View.GONE
|
|
||||||
contentView.authorizeButton.visibility = View.GONE
|
|
||||||
}
|
|
||||||
FileServerAPI.shared.addDeviceLink(deviceLink).bind(SnodeAPI.sharedContext) {
|
|
||||||
MultiDeviceProtocol.signAndSendDeviceLinkMessage(requireContext(), deviceLink)
|
|
||||||
}.success {
|
|
||||||
TextSecurePreferences.setMultiDevice(requireContext(), true)
|
|
||||||
}.successUi {
|
|
||||||
delegate?.onDeviceLinkRequestAuthorized()
|
|
||||||
dismiss()
|
|
||||||
}.fail {
|
|
||||||
FileServerAPI.shared.removeDeviceLink(deviceLink) // If this fails we have a problem
|
|
||||||
DatabaseFactory.getLokiPreKeyBundleDatabase(requireContext()).removePreKeyBundle(deviceLink.slavePublicKey)
|
|
||||||
}.failUi {
|
|
||||||
delegate?.onDeviceLinkAuthorizationFailed()
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onDeviceLinkCanceled() {
|
|
||||||
DeviceLinkingSession.shared.stopListeningForLinkingRequests()
|
|
||||||
DeviceLinkingSession.shared.removeListener(this)
|
|
||||||
if (deviceLink != null) {
|
|
||||||
DatabaseFactory.getLokiPreKeyBundleDatabase(context).removePreKeyBundle(deviceLink!!.slavePublicKey)
|
|
||||||
}
|
|
||||||
dismiss()
|
|
||||||
delegate?.onDeviceLinkCanceled()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface LinkDeviceMasterModeDialogDelegate {
|
|
||||||
|
|
||||||
fun onDeviceLinkRequestAuthorized()
|
|
||||||
fun onDeviceLinkAuthorizationFailed()
|
|
||||||
fun onDeviceLinkCanceled()
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.loki.dialogs
|
|
||||||
|
|
||||||
import android.app.Dialog
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.graphics.drawable.ColorDrawable
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.os.Handler
|
|
||||||
import androidx.fragment.app.DialogFragment
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.widget.LinearLayout
|
|
||||||
import kotlinx.android.synthetic.main.dialog_link_device_slave_mode.view.*
|
|
||||||
import network.loki.messenger.R
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
|
||||||
import org.thoughtcrime.securesms.util.Util
|
|
||||||
import org.session.libsignal.service.loki.crypto.MnemonicCodec
|
|
||||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink
|
|
||||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLinkingSession
|
|
||||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLinkingSessionListener
|
|
||||||
|
|
||||||
class LinkDeviceSlaveModeDialog : DialogFragment(), DeviceLinkingSessionListener {
|
|
||||||
private lateinit var contentView: View
|
|
||||||
private var deviceLink: DeviceLink? = null
|
|
||||||
var delegate: LinkDeviceSlaveModeDialogDelegate? = null
|
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
|
||||||
val builder = AlertDialog.Builder(requireContext())
|
|
||||||
contentView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_link_device_slave_mode, null)
|
|
||||||
val hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
|
|
||||||
val loadFileContents: (String) -> String = { fileName ->
|
|
||||||
MnemonicUtilities.loadFileContents(requireContext(), fileName)
|
|
||||||
}
|
|
||||||
contentView.mnemonicTextView.text = MnemonicUtilities.getFirst3Words(MnemonicCodec(loadFileContents), hexEncodedPublicKey)
|
|
||||||
contentView.cancelButton.setOnClickListener { onDeviceLinkCanceled() }
|
|
||||||
builder.setView(contentView)
|
|
||||||
DeviceLinkingSession.shared.startListeningForLinkingRequests()
|
|
||||||
DeviceLinkingSession.shared.addListener(this)
|
|
||||||
val result = builder.create()
|
|
||||||
result.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) {
|
|
||||||
if (deviceLink.type != DeviceLink.Type.AUTHORIZATION || deviceLink.slavePublicKey != TextSecurePreferences.getLocalNumber(requireContext()) || this.deviceLink != null) { return }
|
|
||||||
Util.runOnMain {
|
|
||||||
this.deviceLink = deviceLink
|
|
||||||
DeviceLinkingSession.shared.stopListeningForLinkingRequests()
|
|
||||||
DeviceLinkingSession.shared.removeListener(this)
|
|
||||||
contentView.spinner.visibility = View.GONE
|
|
||||||
val titleTextViewLayoutParams = contentView.titleTextView.layoutParams as LinearLayout.LayoutParams
|
|
||||||
titleTextViewLayoutParams.topMargin = 0
|
|
||||||
contentView.titleTextView.layoutParams = titleTextViewLayoutParams
|
|
||||||
contentView.titleTextView.text = resources.getString(R.string.dialog_link_device_slave_mode_title_2)
|
|
||||||
contentView.explanationTextView.text = resources.getString(R.string.dialog_link_device_slave_mode_explanation_2)
|
|
||||||
contentView.mnemonicTextView.visibility = View.GONE
|
|
||||||
contentView.cancelButton.visibility = View.GONE
|
|
||||||
Handler().postDelayed({
|
|
||||||
dismiss()
|
|
||||||
delegate?.onDeviceLinkRequestAuthorized(deviceLink)
|
|
||||||
}, 4000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onDeviceLinkCanceled() {
|
|
||||||
DeviceLinkingSession.shared.stopListeningForLinkingRequests()
|
|
||||||
DeviceLinkingSession.shared.removeListener(this)
|
|
||||||
dismiss()
|
|
||||||
delegate?.onDeviceLinkCanceled()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface LinkDeviceSlaveModeDialogDelegate {
|
|
||||||
|
|
||||||
fun onDeviceLinkRequestAuthorized(authorization: DeviceLink)
|
|
||||||
fun onDeviceLinkCanceled()
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.loki.views
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.util.AttributeSet
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.widget.LinearLayout
|
|
||||||
import kotlinx.android.synthetic.main.view_device.view.*
|
|
||||||
import network.loki.messenger.R
|
|
||||||
import org.thoughtcrime.securesms.devicelist.Device
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.toPx
|
|
||||||
|
|
||||||
class DeviceView : LinearLayout {
|
|
||||||
var device: Device? = null
|
|
||||||
|
|
||||||
// region Lifecycle
|
|
||||||
constructor(context: Context) : super(context) {
|
|
||||||
setUpViewHierarchy()
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
|
|
||||||
setUpViewHierarchy()
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
|
|
||||||
setUpViewHierarchy()
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {
|
|
||||||
setUpViewHierarchy()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setUpViewHierarchy() {
|
|
||||||
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
|
|
||||||
val contentView = inflater.inflate(R.layout.view_device, null)
|
|
||||||
addView(contentView)
|
|
||||||
}
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Updating
|
|
||||||
fun bind(device: Device) {
|
|
||||||
titleTextView.text = if (!device.name.isNullOrBlank()) device.name else "Unnamed Device"
|
|
||||||
// FIXME: Hacky way of getting the view to be screen width
|
|
||||||
val titleTextViewLayoutParams = titleTextView.layoutParams
|
|
||||||
titleTextViewLayoutParams.width = resources.displayMetrics.widthPixels - toPx(32, resources)
|
|
||||||
titleTextView.layoutParams = titleTextViewLayoutParams
|
|
||||||
subtitleTextView.text = device.shortId
|
|
||||||
}
|
|
||||||
// endregion
|
|
||||||
}
|
|
@ -66,7 +66,7 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:id="@+id/loader"
|
android:id="@+id/loaderContainer"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="#A4000000"
|
android:background="#A4000000"
|
||||||
|
@ -129,7 +129,7 @@
|
|||||||
android:background="?android:dividerHorizontal" />
|
android:background="?android:dividerHorizontal" />
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/recyclerView"
|
android:id="@+id/rvUserList"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:listitem="@layout/view_user"/>
|
tools:listitem="@layout/view_user"/>
|
||||||
@ -147,7 +147,7 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:id="@+id/loader"
|
android:id="@+id/loaderContainer"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="#A4000000"
|
android:background="#A4000000"
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.viewpager.widget.ViewPager
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:id="@+id/viewPager"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent" >
|
|
||||||
|
|
||||||
<com.google.android.material.tabs.TabLayout
|
|
||||||
style="@style/Widget.Session.TabLayout"
|
|
||||||
android:id="@+id/tabLayout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="@dimen/tab_bar_height" />
|
|
||||||
|
|
||||||
</androidx.viewpager.widget.ViewPager>
|
|
@ -1,37 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<RelativeLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/recyclerView"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/emptyStateContainer"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center_horizontal"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_centerInParent="true">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textSize="@dimen/medium_font_size"
|
|
||||||
android:textColor="@color/text"
|
|
||||||
android:text="@string/activity_linked_devices_empty_state_message" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
style="@style/Widget.Session.Button.Common.ProminentOutline"
|
|
||||||
android:id="@+id/linkDeviceButton"
|
|
||||||
android:layout_width="196dp"
|
|
||||||
android:layout_height="@dimen/medium_button_height"
|
|
||||||
android:layout_marginTop="@dimen/medium_spacing"
|
|
||||||
android:text="@string/activity_linked_devices_empty_state_button_title" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
@ -1,53 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
|
|
||||||
<FrameLayout android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.camera.CameraView
|
|
||||||
android:id="@+id/scanner"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
app:camera="0"/>
|
|
||||||
|
|
||||||
<LinearLayout android:id="@+id/overlay"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:weightSum="2">
|
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.ShapeScrim
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"/>
|
|
||||||
|
|
||||||
|
|
||||||
<LinearLayout android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:paddingStart="16dp"
|
|
||||||
android:paddingEnd="16dp"
|
|
||||||
android:background="?android:windowBackground"
|
|
||||||
android:gravity="center">
|
|
||||||
|
|
||||||
<ImageView android:id="@+id/devices"
|
|
||||||
android:src="@drawable/ic_devices_white"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:tint="@color/gray27"
|
|
||||||
android:transitionName="devices"
|
|
||||||
android:layout_marginBottom="16dp"/>
|
|
||||||
|
|
||||||
<TextView android:text="@string/device_add_fragment__scan_the_qr_code_displayed_on_the_device_to_link"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textColor="?android:textColorSecondary"/>
|
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
@ -1,88 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:gravity="center">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:src="@drawable/ic_devices_white"
|
|
||||||
android:transitionName="devices"
|
|
||||||
android:tint="@color/gray27"
|
|
||||||
android:layout_marginBottom="25dp"
|
|
||||||
android:contentDescription="@string/device_link_fragment__link_device"/>
|
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="25dp"
|
|
||||||
android:layout_marginEnd="25dp">
|
|
||||||
|
|
||||||
<LinearLayout android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:background="?device_link_item_card_background">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:padding="10dp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:text="@string/DeviceProvisioningActivity_link_this_device"
|
|
||||||
android:textSize="16sp"/>
|
|
||||||
|
|
||||||
<View android:layout_width="match_parent"
|
|
||||||
android:layout_height="0.5dp"
|
|
||||||
android:background="#1E000000"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:padding="10dp"
|
|
||||||
android:text="@string/DeviceProvisioningActivity_content_intro"
|
|
||||||
android:textSize="14sp"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:padding="10dp"
|
|
||||||
android:text="@string/DeviceProvisioningActivity_content_bullets"
|
|
||||||
android:textSize="14sp"/>
|
|
||||||
|
|
||||||
<View android:layout_width="match_parent"
|
|
||||||
android:layout_height="0.5dp"
|
|
||||||
android:background="#1E000000"/>
|
|
||||||
|
|
||||||
<LinearLayout android:id="@+id/link_device"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:padding="10dp"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:clickable="true">
|
|
||||||
|
|
||||||
<ImageView android:id="@+id/check"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:src="@drawable/ic_check_white_24dp"
|
|
||||||
android:tint="@color/blue_400"
|
|
||||||
android:clickable="false"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textSize="14sp"
|
|
||||||
android:textColor="@color/blue_400"
|
|
||||||
android:text="@string/device_link_fragment__link_device"
|
|
||||||
android:clickable="false"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
@ -1,49 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<RelativeLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
xmlns:fab="http://schemas.android.com/apk/res-auto">
|
|
||||||
|
|
||||||
<ProgressBar
|
|
||||||
android:id="@+id/activityIndicator"
|
|
||||||
android:indeterminate="true"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_centerInParent="true"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/emptyStateTextView"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:gravity="center"
|
|
||||||
android:textSize="20sp"
|
|
||||||
android:visibility="gone"
|
|
||||||
android:text="@string/device_list_fragment__no_devices_linked"
|
|
||||||
android:paddingStart="16dip"
|
|
||||||
android:paddingEnd="16dip"
|
|
||||||
tools:visibility="visible"/>
|
|
||||||
|
|
||||||
<ListView
|
|
||||||
android:id="@id/android:list"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:drawSelectorOnTop="false" />
|
|
||||||
|
|
||||||
<com.melnykov.fab.FloatingActionButton
|
|
||||||
android:id="@+id/addDeviceButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentBottom="true"
|
|
||||||
android:layout_alignParentRight="true"
|
|
||||||
android:layout_margin="16dp"
|
|
||||||
android:src="@drawable/ic_add_white_original_24dp"
|
|
||||||
android:focusable="true"
|
|
||||||
android:contentDescription="@string/device_list_fragment__link_new_device"
|
|
||||||
fab:fab_colorNormal="?fab_color"
|
|
||||||
fab:fab_colorPressed="@color/textsecure_primary_dark"
|
|
||||||
fab:fab_colorRipple="@color/textsecure_primary_dark" />
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
@ -1,34 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<org.thoughtcrime.securesms.DeviceListItem
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="64dp"
|
|
||||||
android:paddingStart="16dp"
|
|
||||||
android:paddingEnd="16dp"
|
|
||||||
android:gravity="center_vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/name"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:ellipsize="marquee"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:text="Name"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
|
||||||
android:textColor="?attr/conversation_list_item_contact_color"
|
|
||||||
android:textSize="18sp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/shortId"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="2dp"
|
|
||||||
android:ellipsize="marquee"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:text="shortId"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
|
||||||
android:textColor="#A2A2A2"
|
|
||||||
android:textSize="14sp" />
|
|
||||||
|
|
||||||
</org.thoughtcrime.securesms.DeviceListItem>
|
|
@ -1,59 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="@drawable/default_dialog_background_inset"
|
|
||||||
android:gravity="center_horizontal"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:paddingLeft="32dp"
|
|
||||||
android:paddingTop="@dimen/medium_spacing"
|
|
||||||
android:paddingRight="32dp"
|
|
||||||
android:paddingBottom="@dimen/medium_spacing">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Change Device Name"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:textSize="@dimen/medium_font_size" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
style="@style/SessionEditText"
|
|
||||||
android:id="@+id/deviceNameEditText"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginLeft="@dimen/medium_spacing"
|
|
||||||
android:layout_marginTop="@dimen/large_spacing"
|
|
||||||
android:layout_marginRight="@dimen/medium_spacing"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:paddingTop="12dp"
|
|
||||||
android:paddingBottom="12dp"
|
|
||||||
android:hint="@string/dialog_edit_device_name_edit_text_hint" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/large_spacing"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<Button
|
|
||||||
style="@style/Widget.Session.Button.Dialog.Unimportant"
|
|
||||||
android:id="@+id/cancelButton"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="@dimen/small_button_height"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:text="@string/cancel" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
style="@style/Widget.Session.Button.Dialog.Unimportant"
|
|
||||||
android:id="@+id/okButton"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="@dimen/small_button_height"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:layout_marginLeft="@dimen/medium_spacing"
|
|
||||||
android:text="@string/ok" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
@ -1,96 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:background="@drawable/default_dialog_background_inset"
|
|
||||||
android:gravity="center_horizontal"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:paddingLeft="32dp"
|
|
||||||
android:paddingTop="@dimen/medium_spacing"
|
|
||||||
android:paddingRight="32dp"
|
|
||||||
android:paddingBottom="@dimen/medium_spacing">
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:id="@+id/qrCodeImageViewContainer"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/small_spacing"
|
|
||||||
android:gravity="center">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/qrCodeImageView"
|
|
||||||
android:layout_width="128dp"
|
|
||||||
android:layout_height="128dp" />
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
<com.github.ybq.android.spinkit.SpinKitView
|
|
||||||
style="@style/SpinKitView.DoubleBounce"
|
|
||||||
android:id="@+id/spinner"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:SpinKit_Color="@color/text" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/titleTextView"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/large_spacing"
|
|
||||||
android:text="@string/dialog_link_device_master_mode_title_1"
|
|
||||||
android:textColor="@color/text"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:textSize="@dimen/medium_font_size" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/explanationTextView"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/large_spacing"
|
|
||||||
android:text="@string/dialog_link_device_master_mode_explanation_1"
|
|
||||||
android:textColor="@color/text"
|
|
||||||
android:textSize="@dimen/small_font_size"
|
|
||||||
android:textAlignment="center" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/mnemonicTextView"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="20dp"
|
|
||||||
android:text="puffin circle idled"
|
|
||||||
android:textColor="@color/text"
|
|
||||||
android:textSize="@dimen/small_font_size"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/buttonContainer"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/large_spacing"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<Button
|
|
||||||
style="@style/Widget.Session.Button.Dialog.Unimportant"
|
|
||||||
android:id="@+id/cancelButton"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="@dimen/small_button_height"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:text="@string/cancel" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
style="@style/Widget.Session.Button.Dialog.Prominent"
|
|
||||||
android:id="@+id/authorizeButton"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="@dimen/small_button_height"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:layout_marginStart="@dimen/medium_spacing"
|
|
||||||
android:text="@string/dialog_link_device_master_mode_authorize_button_title"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
@ -1,61 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:background="@drawable/default_dialog_background_inset"
|
|
||||||
android:gravity="center_horizontal"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:paddingLeft="32dp"
|
|
||||||
android:paddingTop="@dimen/medium_spacing"
|
|
||||||
android:paddingRight="32dp"
|
|
||||||
android:paddingBottom="@dimen/medium_spacing">
|
|
||||||
|
|
||||||
<com.github.ybq.android.spinkit.SpinKitView
|
|
||||||
style="@style/SpinKitView.DoubleBounce"
|
|
||||||
android:id="@+id/spinner"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
app:SpinKit_Color="@color/text" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/titleTextView"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/large_spacing"
|
|
||||||
android:text="@string/dialog_link_device_slave_mode_title_1"
|
|
||||||
android:textColor="@color/text"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:textSize="@dimen/medium_font_size" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/explanationTextView"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/large_spacing"
|
|
||||||
android:text="@string/dialog_link_device_slave_mode_explanation_1"
|
|
||||||
android:textColor="@color/text"
|
|
||||||
android:textSize="@dimen/small_font_size"
|
|
||||||
android:textAlignment="center" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/mnemonicTextView"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/large_spacing"
|
|
||||||
android:text="puffin circle idled"
|
|
||||||
android:textColor="@color/text"
|
|
||||||
android:textSize="@dimen/small_font_size"
|
|
||||||
android:textAlignment="center" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
style="@style/Widget.Session.Button.Dialog.Unimportant"
|
|
||||||
android:id="@+id/cancelButton"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="@dimen/small_button_height"
|
|
||||||
android:layout_marginTop="@dimen/large_spacing"
|
|
||||||
android:text="@string/cancel" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
Loading…
x
Reference in New Issue
Block a user