kopia lustrzana https://github.com/ryukoposting/Signal-Android
279 wiersze
9.4 KiB
Java
279 wiersze
9.4 KiB
Java
package org.signal.devicetransfer;
|
|
|
|
import android.app.Notification;
|
|
import android.app.PendingIntent;
|
|
import android.app.Service;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.os.IBinder;
|
|
import android.os.Parcel;
|
|
import android.os.Parcelable;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.core.app.NotificationCompat;
|
|
|
|
import org.greenrobot.eventbus.EventBus;
|
|
import org.greenrobot.eventbus.Subscribe;
|
|
import org.greenrobot.eventbus.ThreadMode;
|
|
import org.signal.core.util.ThreadUtil;
|
|
import org.signal.core.util.logging.Log;
|
|
|
|
import java.io.Serializable;
|
|
import java.util.Objects;
|
|
|
|
/**
|
|
* Foreground service to help manage interactions with the {@link DeviceTransferClient} and
|
|
* {@link DeviceTransferServer}.
|
|
*/
|
|
public class DeviceToDeviceTransferService extends Service implements ShutdownCallback {
|
|
|
|
private static final String TAG = Log.tag(DeviceToDeviceTransferService.class);
|
|
|
|
private static final String ACTION_START_SERVER = "start";
|
|
private static final String ACTION_START_CLIENT = "start_client";
|
|
private static final String ACTION_SET_VERIFIED = "set_verified";
|
|
private static final String ACTION_STOP = "stop";
|
|
|
|
private static final String EXTRA_PENDING_INTENT = "extra_pending_intent";
|
|
private static final String EXTRA_TASK = "extra_task";
|
|
private static final String EXTRA_NOTIFICATION = "extra_notification_data";
|
|
private static final String EXTRA_IS_VERIFIED = "is_verified";
|
|
|
|
private TransferNotificationData notificationData;
|
|
private PendingIntent pendingIntent;
|
|
private DeviceTransferServer server;
|
|
private DeviceTransferClient client;
|
|
|
|
public static void startServer(@NonNull Context context,
|
|
@NonNull ServerTask serverTask,
|
|
@NonNull TransferNotificationData transferNotificationData,
|
|
@Nullable PendingIntent pendingIntent)
|
|
{
|
|
Intent intent = new Intent(context, DeviceToDeviceTransferService.class);
|
|
intent.setAction(ACTION_START_SERVER)
|
|
.putExtra(EXTRA_TASK, serverTask)
|
|
.putExtra(EXTRA_NOTIFICATION, transferNotificationData)
|
|
.putExtra(EXTRA_PENDING_INTENT, pendingIntent);
|
|
|
|
context.startService(intent);
|
|
}
|
|
|
|
public static void startClient(@NonNull Context context,
|
|
@NonNull ClientTask clientTask,
|
|
@NonNull TransferNotificationData transferNotificationData,
|
|
@Nullable PendingIntent pendingIntent)
|
|
{
|
|
Intent intent = new Intent(context, DeviceToDeviceTransferService.class);
|
|
intent.setAction(ACTION_START_CLIENT)
|
|
.putExtra(EXTRA_TASK, clientTask)
|
|
.putExtra(EXTRA_NOTIFICATION, transferNotificationData)
|
|
.putExtra(EXTRA_PENDING_INTENT, pendingIntent);
|
|
|
|
context.startService(intent);
|
|
}
|
|
|
|
public static void setAuthenticationCodeVerified(@NonNull Context context, boolean verified) {
|
|
Intent intent = new Intent(context, DeviceToDeviceTransferService.class);
|
|
intent.setAction(ACTION_SET_VERIFIED)
|
|
.putExtra(EXTRA_IS_VERIFIED, verified);
|
|
|
|
context.startService(intent);
|
|
}
|
|
|
|
public static void stop(@NonNull Context context) {
|
|
context.startService(new Intent(context, DeviceToDeviceTransferService.class).setAction(ACTION_STOP));
|
|
}
|
|
|
|
@Override
|
|
public void onCreate() {
|
|
super.onCreate();
|
|
Log.v(TAG, "onCreate");
|
|
|
|
EventBus.getDefault().register(this);
|
|
}
|
|
|
|
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
|
public void onEventMainThread(@NonNull TransferStatus event) {
|
|
updateNotification(event);
|
|
}
|
|
|
|
@Override
|
|
public void onDestroy() {
|
|
Log.v(TAG, "onDestroy");
|
|
|
|
EventBus.getDefault().unregister(this);
|
|
|
|
if (client != null) {
|
|
client.stop();
|
|
client = null;
|
|
}
|
|
|
|
if (server != null) {
|
|
server.stop();
|
|
server = null;
|
|
}
|
|
|
|
super.onDestroy();
|
|
}
|
|
|
|
@Override
|
|
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
|
|
if (intent == null) {
|
|
return START_NOT_STICKY;
|
|
}
|
|
|
|
final String action = intent.getAction();
|
|
if (action == null) {
|
|
return START_NOT_STICKY;
|
|
}
|
|
|
|
final WifiDirect.AvailableStatus availability = WifiDirect.getAvailability(this);
|
|
if (availability != WifiDirect.AvailableStatus.AVAILABLE) {
|
|
shutdown();
|
|
return START_NOT_STICKY;
|
|
}
|
|
|
|
Log.d(TAG, "Action: " + action);
|
|
switch (action) {
|
|
case ACTION_START_SERVER: {
|
|
if (server == null) {
|
|
notificationData = intent.getParcelableExtra(EXTRA_NOTIFICATION);
|
|
pendingIntent = intent.getParcelableExtra(EXTRA_PENDING_INTENT);
|
|
server = new DeviceTransferServer(getApplicationContext(),
|
|
(ServerTask) Objects.requireNonNull(intent.getSerializableExtra(EXTRA_TASK)),
|
|
this);
|
|
server.start();
|
|
} else {
|
|
Log.i(TAG, "Can't start server, already started.");
|
|
}
|
|
break;
|
|
}
|
|
case ACTION_START_CLIENT: {
|
|
if (client == null) {
|
|
notificationData = intent.getParcelableExtra(EXTRA_NOTIFICATION);
|
|
pendingIntent = intent.getParcelableExtra(EXTRA_PENDING_INTENT);
|
|
client = new DeviceTransferClient(getApplicationContext(),
|
|
(ClientTask) Objects.requireNonNull(intent.getSerializableExtra(EXTRA_TASK)),
|
|
this);
|
|
client.start();
|
|
} else {
|
|
Log.i(TAG, "Can't start client, client already started.");
|
|
}
|
|
break;
|
|
}
|
|
case ACTION_SET_VERIFIED:
|
|
boolean isVerified = intent.getBooleanExtra(EXTRA_IS_VERIFIED, false);
|
|
if (server != null) {
|
|
server.setVerified(isVerified);
|
|
} else if (client != null) {
|
|
client.setVerified(isVerified);
|
|
}
|
|
break;
|
|
case ACTION_STOP:
|
|
shutdown();
|
|
break;
|
|
}
|
|
|
|
return START_STICKY;
|
|
}
|
|
|
|
@Override
|
|
public void shutdown() {
|
|
Log.i(TAG, "Shutdown");
|
|
ThreadUtil.runOnMain(() -> {
|
|
stopForeground(true);
|
|
stopSelf();
|
|
});
|
|
}
|
|
|
|
private void updateNotification(@NonNull TransferStatus transferStatus) {
|
|
if (notificationData != null && (client != null || server != null)) {
|
|
startForeground(notificationData.notificationId, createNotification(transferStatus, notificationData));
|
|
}
|
|
}
|
|
|
|
private @NonNull Notification createNotification(@NonNull TransferStatus transferStatus, @NonNull TransferNotificationData notificationData) {
|
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, notificationData.channelId);
|
|
|
|
String contentText = "";
|
|
switch (transferStatus.getTransferMode()) {
|
|
case READY:
|
|
contentText = getString(R.string.DeviceToDeviceTransferService_status_ready);
|
|
break;
|
|
case STARTING_UP:
|
|
contentText = getString(R.string.DeviceToDeviceTransferService_status_starting_up);
|
|
break;
|
|
case DISCOVERY:
|
|
contentText = getString(R.string.DeviceToDeviceTransferService_status_discovery);
|
|
break;
|
|
case NETWORK_CONNECTED:
|
|
contentText = getString(R.string.DeviceToDeviceTransferService_status_network_connected);
|
|
break;
|
|
case VERIFICATION_REQUIRED:
|
|
contentText = getString(R.string.DeviceToDeviceTransferService_status_verification_required);
|
|
break;
|
|
case SERVICE_CONNECTED:
|
|
contentText = getString(R.string.DeviceToDeviceTransferService_status_service_connected);
|
|
break;
|
|
case UNAVAILABLE:
|
|
case FAILED:
|
|
case SERVICE_DISCONNECTED:
|
|
case SHUTDOWN:
|
|
Log.d(TAG, "Intentionally no notification text for: " + transferStatus.getTransferMode());
|
|
break;
|
|
default:
|
|
throw new AssertionError("No notification text for: " + transferStatus.getTransferMode());
|
|
}
|
|
|
|
builder.setSmallIcon(notificationData.icon)
|
|
.setOngoing(true)
|
|
.setContentTitle(getString(R.string.DeviceToDeviceTransferService_content_title))
|
|
.setContentText(contentText)
|
|
.setContentIntent(pendingIntent);
|
|
|
|
return builder.build();
|
|
}
|
|
|
|
@Override
|
|
public @Nullable IBinder onBind(@NonNull Intent intent) {
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
|
|
public static class TransferNotificationData implements Parcelable {
|
|
private final int notificationId;
|
|
private final String channelId;
|
|
private final int icon;
|
|
|
|
public TransferNotificationData(int notificationId, @NonNull String channelId, int icon) {
|
|
this.notificationId = notificationId;
|
|
this.channelId = channelId;
|
|
this.icon = icon;
|
|
}
|
|
|
|
@Override
|
|
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
|
dest.writeInt(notificationId);
|
|
dest.writeString(channelId);
|
|
dest.writeInt(icon);
|
|
}
|
|
|
|
@Override
|
|
public int describeContents() {
|
|
return 0;
|
|
}
|
|
|
|
public static final Creator<TransferNotificationData> CREATOR = new Creator<TransferNotificationData>() {
|
|
@Override
|
|
public @NonNull TransferNotificationData createFromParcel(@NonNull Parcel in) {
|
|
return new TransferNotificationData(in.readInt(), in.readString(), in.readInt());
|
|
}
|
|
|
|
@Override
|
|
public @NonNull TransferNotificationData[] newArray(int size) {
|
|
return new TransferNotificationData[size];
|
|
}
|
|
};
|
|
}
|
|
}
|