rework swap startup putting SwapService first

SwapService is the thing that needs to be always running, and the last
thing killed.  So it should start first, and stop last.  So now, the user
clicking the button starts SwapService, which starts SwapWorkflowActivity.
This also eliminatings the "Loading" screen in favor of just showing the
StartSwapView with various inline progress indicators.
This commit is contained in:
Hans-Christoph Steiner 2019-05-16 14:07:00 +02:00
parent 035a89e5f6
commit d91fbe7b0e
6 changed files with 36 additions and 93 deletions

View File

@ -14,6 +14,7 @@ import android.content.SharedPreferences;
import android.net.Uri; import android.net.Uri;
import android.net.wifi.WifiManager; import android.net.wifi.WifiManager;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Build;
import android.os.IBinder; import android.os.IBinder;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
@ -72,10 +73,20 @@ public class SwapService extends Service {
@NonNull @NonNull
private final Set<String> appsToSwap = new HashSet<>(); private final Set<String> appsToSwap = new HashSet<>();
private static LocalBroadcastManager localBroadcastManager;
private static SharedPreferences swapPreferences; private static SharedPreferences swapPreferences;
private static BluetoothAdapter bluetoothAdapter; private static BluetoothAdapter bluetoothAdapter;
private static WifiManager wifiManager; private static WifiManager wifiManager;
public static void start(Context context) {
Intent intent = new Intent(context, SwapService.class);
if (Build.VERSION.SDK_INT < 26) {
context.startService(intent);
} else {
context.startForegroundService(intent);
}
}
public static void stop(Context context) { public static void stop(Context context) {
Intent intent = new Intent(context, SwapService.class); Intent intent = new Intent(context, SwapService.class);
context.stopService(intent); context.stopService(intent);
@ -413,12 +424,8 @@ public class SwapService extends Service {
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
Utils.debugLog(TAG, "Creating swap service.");
startForeground(NOTIFICATION, createNotification()); startForeground(NOTIFICATION, createNotification());
localBroadcastManager = LocalBroadcastManager.getInstance(this);
deleteAllSwapRepos();
swapPreferences = getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE); swapPreferences = getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE);
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
@ -455,9 +462,16 @@ public class SwapService extends Service {
} }
} }
/**
* This is for setting things up for when the {@code SwapService} was
* started by the user clicking on the initial start button. The things
* that must be run always on start-up go in {@link #onCreate()}.
*/
@Override @Override
public int onStartCommand(Intent intent, int flags, int startId) { public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY; deleteAllSwapRepos();
startActivity(new Intent(this, SwapWorkflowActivity.class));
return START_NOT_STICKY;
} }
@Override @Override
@ -525,7 +539,6 @@ public class SwapService extends Service {
} }
private void initTimer() { private void initTimer() {
// TODO replace by Android scheduler
if (timer != null) { if (timer != null) {
Utils.debugLog(TAG, "Cancelling existing timeout timer so timeout can be reset."); Utils.debugLog(TAG, "Cancelling existing timeout timer so timeout can be reset.");
timer.cancel(); timer.cancel();
@ -537,6 +550,8 @@ public class SwapService extends Service {
@Override @Override
public void run() { public void run() {
Utils.debugLog(TAG, "Disabling swap because " + TIMEOUT + "ms passed."); Utils.debugLog(TAG, "Disabling swap because " + TIMEOUT + "ms passed.");
String msg = getString(R.string.swap_toast_closing_nearby_after_timeout);
Utils.showToastFromService(SwapService.this, msg, android.widget.Toast.LENGTH_LONG);
stop(SwapService.this); stop(SwapService.this);
} }
}, TIMEOUT); }, TIMEOUT);

View File

@ -21,7 +21,7 @@ public class SwapView extends RelativeLayout {
public final int toolbarColor; public final int toolbarColor;
public final String toolbarTitle; public final String toolbarTitle;
private int layoutResId; private int layoutResId = -1;
protected String currentFilterString; protected String currentFilterString;

View File

@ -18,8 +18,8 @@ import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import org.fdroid.fdroid.R; import org.fdroid.fdroid.R;
import org.fdroid.fdroid.localrepo.SDCardScannerService; import org.fdroid.fdroid.localrepo.SDCardScannerService;
import org.fdroid.fdroid.localrepo.SwapService;
import org.fdroid.fdroid.localrepo.TreeUriScannerIntentService; import org.fdroid.fdroid.localrepo.TreeUriScannerIntentService;
import org.fdroid.fdroid.views.swap.SwapWorkflowActivity;
import java.io.File; import java.io.File;
@ -75,7 +75,7 @@ class NearbyViewBinder {
ActivityCompat.requestPermissions(activity, new String[]{coarseLocation}, ActivityCompat.requestPermissions(activity, new String[]{coarseLocation},
MainActivity.REQUEST_LOCATION_PERMISSIONS); MainActivity.REQUEST_LOCATION_PERMISSIONS);
} else { } else {
activity.startActivity(new Intent(activity, SwapWorkflowActivity.class)); SwapService.start(activity);
} }
} }
}); });

View File

@ -102,7 +102,6 @@ public class SwapWorkflowActivity extends AppCompatActivity {
private ViewGroup container; private ViewGroup container;
private static final int CONNECT_TO_SWAP = 1;
private static final int REQUEST_BLUETOOTH_ENABLE_FOR_SWAP = 2; private static final int REQUEST_BLUETOOTH_ENABLE_FOR_SWAP = 2;
private static final int REQUEST_BLUETOOTH_DISCOVERABLE = 3; private static final int REQUEST_BLUETOOTH_DISCOVERABLE = 3;
private static final int REQUEST_BLUETOOTH_ENABLE_FOR_SEND = 4; private static final int REQUEST_BLUETOOTH_ENABLE_FOR_SEND = 4;
@ -134,17 +133,14 @@ public class SwapWorkflowActivity extends AppCompatActivity {
private final ServiceConnection serviceConnection = new ServiceConnection() { private final ServiceConnection serviceConnection = new ServiceConnection() {
@Override @Override
public void onServiceConnected(ComponentName className, IBinder binder) { public void onServiceConnected(ComponentName className, IBinder binder) {
Utils.debugLog(TAG, "Swap service connected. Will hold onto it so we can talk to it regularly.");
service = ((SwapService.Binder) binder).getService(); service = ((SwapService.Binder) binder).getService();
showRelevantView(); showRelevantView();
} }
// TODO: What causes this? Do we need to stop swapping explicitly when this is invoked?
@Override @Override
public void onServiceDisconnected(ComponentName className) { public void onServiceDisconnected(ComponentName className) {
Utils.debugLog(TAG, "Swap service disconnected"); finish();
service = null; service = null;
// TODO: What to do about the UI in this instance?
} }
}; };
@ -153,10 +149,6 @@ public class SwapWorkflowActivity extends AppCompatActivity {
@NonNull @NonNull
public SwapService getService() { public SwapService getService() {
if (service == null) {
// *Slightly* more informative than a null-pointer error that would otherwise happen.
throw new IllegalStateException("Trying to access swap service before it was initialized.");
}
return service; return service;
} }
@ -178,9 +170,6 @@ public class SwapWorkflowActivity extends AppCompatActivity {
case R.layout.swap_connecting: case R.layout.swap_connecting:
nextStep = R.layout.swap_select_apps; nextStep = R.layout.swap_select_apps;
break; break;
case R.layout.swap_initial_loading:
nextStep = R.layout.swap_join_wifi;
break;
case R.layout.swap_join_wifi: case R.layout.swap_join_wifi:
nextStep = STEP_INTRO; nextStep = STEP_INTRO;
break; break;
@ -215,12 +204,12 @@ public class SwapWorkflowActivity extends AppCompatActivity {
((FDroidApp) getApplication()).setSecureWindow(this); ((FDroidApp) getApplication()).setSecureWindow(this);
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
// The server should not be doing anything or occupying any (noticeable) resources currentView = new SwapView(this); // dummy placeholder to avoid NullPointerExceptions;
// until we actually ask it to enable swapping. Therefore, we will start it nice and
// early so we don't have to wait until it is connected later. if (!bindService(new Intent(this, SwapService.class), serviceConnection,
Intent service = new Intent(this, SwapService.class); BIND_ABOVE_CLIENT | BIND_IMPORTANT)) {
if (bindService(service, serviceConnection, Context.BIND_AUTO_CREATE)) { Toast.makeText(this, "ERROR: cannot bind to SwapService!", Toast.LENGTH_LONG).show();
startService(service); finish();
} }
setContentView(R.layout.swap_activity); setContentView(R.layout.swap_activity);
@ -357,7 +346,6 @@ public class SwapWorkflowActivity extends AppCompatActivity {
new IntentFilter(UpdateService.LOCAL_ACTION_STATUS)); new IntentFilter(UpdateService.LOCAL_ACTION_STATUS));
checkIncomingIntent(); checkIncomingIntent();
showRelevantView();
} }
@Override @Override
@ -446,15 +434,6 @@ public class SwapWorkflowActivity extends AppCompatActivity {
} }
private void showRelevantView() { private void showRelevantView() {
showRelevantView(false);
}
private void showRelevantView(boolean forceReload) {
if (service == null) {
inflateSwapView(R.layout.swap_initial_loading);
return;
}
if (confirmSwapConfig != null) { if (confirmSwapConfig != null) {
inflateSwapView(R.layout.swap_confirm_receive); inflateSwapView(R.layout.swap_confirm_receive);
@ -463,12 +442,6 @@ public class SwapWorkflowActivity extends AppCompatActivity {
return; return;
} }
if (!forceReload
&& (container.getVisibility() == View.GONE || currentView != null && currentView.getLayoutResId() == currentSwapViewLayoutRes)) {
// Already showing the correct step, so don't bother changing anything.
return;
}
switch (currentSwapViewLayoutRes) { switch (currentSwapViewLayoutRes) {
case STEP_INTRO: case STEP_INTRO:
showIntro(); showIntro();
@ -491,20 +464,12 @@ public class SwapWorkflowActivity extends AppCompatActivity {
return service; return service;
} }
public SwapView inflateSwapView(@LayoutRes int viewRes) { public void inflateSwapView(@LayoutRes int viewRes) {
container.removeAllViews(); container.removeAllViews();
View view = ((LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE)).inflate(viewRes, container, false); View view = ((LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE)).inflate(viewRes, container, false);
currentView = (SwapView) view; currentView = (SwapView) view;
currentView.setLayoutResId(viewRes); currentView.setLayoutResId(viewRes);
currentSwapViewLayoutRes = viewRes;
// Don't actually set the step to STEP_INITIAL_LOADING, as we are going to use this view
// purely as a placeholder for _whatever view is meant to be shown_.
if (currentView.getLayoutResId() != R.layout.swap_initial_loading) {
if (service == null) {
throw new IllegalStateException("We are not in the STEP_INITIAL_LOADING state, but the service is not ready.");
}
currentSwapViewLayoutRes = currentView.getLayoutResId();
}
toolbar.setBackgroundColor(currentView.getToolbarColour()); toolbar.setBackgroundColor(currentView.getToolbarColour());
toolbar.setTitle(currentView.getToolbarTitle()); toolbar.setTitle(currentView.getToolbarTitle());
@ -534,8 +499,6 @@ public class SwapWorkflowActivity extends AppCompatActivity {
setUpConnectingView(); setUpConnectingView();
break; break;
} }
return currentView;
} }
private void onToolbarCancel() { private void onToolbarCancel() {
@ -734,8 +697,6 @@ public class SwapWorkflowActivity extends AppCompatActivity {
Toast.makeText(this, R.string.swap_qr_isnt_for_swap, Toast.LENGTH_SHORT).show(); Toast.makeText(this, R.string.swap_qr_isnt_for_swap, Toast.LENGTH_SHORT).show();
} }
} }
} else if (requestCode == CONNECT_TO_SWAP && resultCode == Activity.RESULT_OK) {
finish();
} else if (requestCode == REQUEST_WRITE_SETTINGS_PERMISSION) { } else if (requestCode == REQUEST_WRITE_SETTINGS_PERMISSION) {
if (Build.VERSION.SDK_INT >= 23 && Settings.System.canWrite(this)) { if (Build.VERSION.SDK_INT >= 23 && Settings.System.canWrite(this)) {
setupWifiAP(); setupWifiAP();
@ -809,11 +770,6 @@ public class SwapWorkflowActivity extends AppCompatActivity {
intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, discoverableTimeout); intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, discoverableTimeout);
startActivityForResult(intent, REQUEST_BLUETOOTH_DISCOVERABLE); startActivityForResult(intent, REQUEST_BLUETOOTH_DISCOVERABLE);
} }
if (service == null) {
throw new IllegalStateException("Can't start Bluetooth swap because service is null for some strange reason.");
}
service.getBluetoothSwap().startInBackground(); // TODO replace with Intent to SwapService service.getBluetoothSwap().startInBackground(); // TODO replace with Intent to SwapService
} }
@ -897,7 +853,7 @@ public class SwapWorkflowActivity extends AppCompatActivity {
case Installer.ACTION_INSTALL_COMPLETE: case Installer.ACTION_INSTALL_COMPLETE:
localBroadcastManager.unregisterReceiver(this); localBroadcastManager.unregisterReceiver(this);
showRelevantView(true); showRelevantView();
break; break;
case Installer.ACTION_INSTALL_INTERRUPTED: case Installer.ACTION_INSTALL_INTERRUPTED:
localBroadcastManager.unregisterReceiver(this); localBroadcastManager.unregisterReceiver(this);

View File

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<org.fdroid.fdroid.localrepo.SwapView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:swap="http://schemas.android.com/apk/res-auto"
swap:toolbarTitle="@string/swap"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/swap_blue"
android:paddingTop="38.8dp"> <!-- 69px * 96dpi / 160dpi -->
<ProgressBar
android:id="@+id/progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/loading"
android:textSize="18sp"
android:layout_below="@+id/progress"
android:textColor="@android:color/white"
android:layout_centerHorizontal="true"/>
</org.fdroid.fdroid.localrepo.SwapView>

View File

@ -517,6 +517,7 @@ This often occurs with apps installed via Google Play or other sources, if they
<string name="swap_toast_invalid_url">Invalid URL for swapping: %1$s</string> <string name="swap_toast_invalid_url">Invalid URL for swapping: %1$s</string>
<string name="swap_toast_hotspot_enabled">Wi-Fi Hotspot enabled</string> <string name="swap_toast_hotspot_enabled">Wi-Fi Hotspot enabled</string>
<string name="swap_toast_could_not_enable_hotspot">Could not enable Wi-Fi Hotspot!</string> <string name="swap_toast_could_not_enable_hotspot">Could not enable Wi-Fi Hotspot!</string>
<string name="swap_toast_closing_nearby_after_timeout">Nearby closed since it was idle.</string>
<string name="install_confirm">needs access to</string> <string name="install_confirm">needs access to</string>
<string name="install_confirm_update">Do you want to install an update <string name="install_confirm_update">Do you want to install an update