Skip to content

Commit

Permalink
fix activity resume error
Browse files Browse the repository at this point in the history
It seems that ActivityResultLaunchers must be initialized exactly
once during startup.
  • Loading branch information
ostrya committed Feb 28, 2021
1 parent fb668e1 commit 418769f
Show file tree
Hide file tree
Showing 10 changed files with 76 additions and 52 deletions.
17 changes: 14 additions & 3 deletions app/src/main/java/org/ostrya/presencepublisher/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,22 @@
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;

import androidx.fragment.app.FragmentActivity;
import androidx.preference.PreferenceManager;
import androidx.viewpager2.widget.ViewPager2;

import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
import com.hypertrack.hyperlog.HyperLog;

import org.ostrya.presencepublisher.schedule.Scheduler;
import org.ostrya.presencepublisher.ui.MainPagerAdapter;
import org.ostrya.presencepublisher.ui.initialization.InitializationHandler;

import java.util.Collections;

import static org.ostrya.presencepublisher.ui.initialization.InitializationHandler.HANDLER_CHAIN;
import static org.ostrya.presencepublisher.ui.preference.about.LocationConsentPreference.LOCATION_CONSENT;
import static org.ostrya.presencepublisher.ui.preference.condition.AddBeaconChoicePreferenceDummy.BEACON_LIST;
import static org.ostrya.presencepublisher.ui.preference.condition.AddNetworkChoicePreferenceDummy.SSID_LIST;
Expand Down Expand Up @@ -42,7 +46,7 @@ public class MainActivity extends FragmentActivity {
private static final String TAG = "MainActivity";

private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceListener = this::onSharedPreferenceChanged;
private final InitializationHandler handler = InitializationHandler.getHandler(InitializationHandler.HANDLER_CHAIN);
private InitializationHandler handler;
private boolean locationServiceNeeded;
private SharedPreferences sharedPreferences;

Expand All @@ -63,13 +67,14 @@ public void onCreate(Bundle savedInstanceState) {
|| Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceListener);
handler = InitializationHandler.getHandler(this, HANDLER_CHAIN);
}

@Override
protected void onResume() {
super.onResume();
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceListener);
handler.initialize(this);
handler.initialize();
}

@Override
Expand All @@ -78,6 +83,12 @@ protected void onPause() {
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceListener);
}

@Override
protected void onDestroy() {
super.onDestroy();
handler = null;
}

public boolean isLocationServiceNeeded() {
return locationServiceNeeded;
}
Expand Down Expand Up @@ -131,7 +142,7 @@ private void onChangedConnectionProperty(String key) {
private void handleConsentChange() {
if (sharedPreferences.getBoolean(LOCATION_CONSENT, false)) {
HyperLog.i(TAG, "User consented to location access, initializing.");
handler.initialize(this);
handler.initialize();
} else {
HyperLog.i(TAG, "User revoked location access consent, stopping schedule.");
new Scheduler(this).stopSchedule();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,51 +3,44 @@
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContract;
import androidx.annotation.Nullable;

import com.hypertrack.hyperlog.HyperLog;

import org.ostrya.presencepublisher.MainActivity;
import org.ostrya.presencepublisher.ui.contract.DummyActivityResultLauncher;

import java.util.Queue;
import java.util.concurrent.atomic.AtomicBoolean;

abstract class AbstractChainedHandler<I, O> implements InitializationHandler {
protected static final String TAG = "AbstractChainedHandler";
@Nullable
private final ActivityResultContract<I, O> contract;
@Nullable
private final InitializationHandler nextHandler;
private final AtomicBoolean inProgress = new AtomicBoolean(false);
@Nullable
private MainActivity activity = null;
private ActivityResultLauncher<I> launcher = new DummyActivityResultLauncher<>();
protected final MainActivity activity;
private final ActivityResultLauncher<I> launcher;

protected AbstractChainedHandler(@Nullable ActivityResultContract<I, O> contract, Queue<HandlerFactory> handlerChain) {
this.contract = contract;
protected AbstractChainedHandler(MainActivity activity, @Nullable ActivityResultContract<I, O> contract, Queue<HandlerFactory> handlerChain) {
this.activity = activity;
this.launcher = this.activity.registerForActivityResult(contract, this::handleResult);
HandlerFactory handlerFactory = handlerChain.poll();
if (handlerFactory != null) {
this.nextHandler = handlerFactory.create(handlerChain);
this.nextHandler = handlerFactory.create(activity, handlerChain);
} else {
this.nextHandler = null;
}
}

@Override
public void initialize(MainActivity activity) {
public void initialize() {
if (inProgress.compareAndSet(false, true)) {
if (!activity.equals(this.activity)) {
this.activity = activity;
if (contract != null) {
launcher = this.activity.registerForActivityResult(contract, this::handleResult);
}
}
HyperLog.i(TAG, "Running initialization for " + getName());
doInitialize(activity);
doInitialize();
} else {
HyperLog.d(TAG, "Skipping initialization, already in progress for " + getName());
}
}

protected abstract void doInitialize(MainActivity activity);
protected abstract void doInitialize();

private void handleResult(O result) {
if (inProgress.get()) {
Expand All @@ -61,7 +54,7 @@ private void handleResult(O result) {

protected void finishInitialization() {
if (nextHandler != null) {
nextHandler.initialize(activity);
nextHandler.initialize();
}
inProgress.compareAndSet(true, false);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.ostrya.presencepublisher.ui.initialization;

import com.hypertrack.hyperlog.HyperLog;

import org.ostrya.presencepublisher.MainActivity;
import org.ostrya.presencepublisher.schedule.Scheduler;

Expand All @@ -14,12 +15,12 @@
public class CreateSchedule extends AbstractChainedHandler<Void, Void> {
private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();

protected CreateSchedule(Queue<HandlerFactory> handlerChain) {
super(null, handlerChain);
protected CreateSchedule(MainActivity activity, Queue<HandlerFactory> handlerChain) {
super(activity, null, handlerChain);
}

@Override
protected void doInitialize(MainActivity activity) {
protected void doInitialize() {
HyperLog.i(TAG, "Starting schedule now");
new Scheduler(activity).scheduleNow();
// make sure we don't re-schedule until the first run has happened
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,33 @@
package org.ostrya.presencepublisher.ui.initialization;

import static org.ostrya.presencepublisher.ui.preference.about.LocationConsentPreference.LOCATION_CONSENT;

import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Build;

import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.RequiresApi;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentManager;
import androidx.preference.PreferenceManager;

import com.hypertrack.hyperlog.HyperLog;
import java.util.Queue;

import org.ostrya.presencepublisher.MainActivity;
import org.ostrya.presencepublisher.R;
import org.ostrya.presencepublisher.ui.dialog.ConfirmationDialogFragment;

import java.util.Queue;

import static org.ostrya.presencepublisher.ui.preference.about.LocationConsentPreference.LOCATION_CONSENT;

public class EnsureBackgroundLocationPermission extends AbstractChainedHandler<String, Boolean> {
protected EnsureBackgroundLocationPermission(Queue<HandlerFactory> handlerChain) {
super(new ActivityResultContracts.RequestPermission(), handlerChain);
protected EnsureBackgroundLocationPermission(MainActivity activity, Queue<HandlerFactory> handlerChain) {
super(activity, new ActivityResultContracts.RequestPermission(), handlerChain);
}

@Override
protected void doInitialize(MainActivity activity) {
protected void doInitialize() {
if (activity.isLocationServiceNeeded()
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
&& ContextCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_BACKGROUND_LOCATION)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
import android.net.Uri;
import android.os.Build;
import android.os.PowerManager;

import androidx.annotation.RequiresApi;
import androidx.fragment.app.FragmentManager;

import com.hypertrack.hyperlog.HyperLog;

import org.ostrya.presencepublisher.MainActivity;
import org.ostrya.presencepublisher.R;
import org.ostrya.presencepublisher.ui.contract.IntentActionContract;
Expand All @@ -19,13 +22,13 @@
import static android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS;

public class EnsureBatteryOptimizationDisabled extends AbstractChainedHandler<String, Boolean> {
protected EnsureBatteryOptimizationDisabled(Queue<HandlerFactory> handlerChain) {
super(new IntentActionContract(
protected EnsureBatteryOptimizationDisabled(MainActivity activity, Queue<HandlerFactory> handlerChain) {
super(activity, new IntentActionContract(
context -> Uri.fromParts("package", context.getPackageName(), null)), handlerChain);
}

@Override
protected void doInitialize(MainActivity activity) {
protected void doInitialize() {
PowerManager powerManager = (PowerManager) activity.getSystemService(POWER_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && powerManager != null
&& !powerManager.isIgnoringBatteryOptimizations(activity.getPackageName())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.os.Build;

import androidx.fragment.app.FragmentManager;

import com.hypertrack.hyperlog.HyperLog;

import org.ostrya.presencepublisher.MainActivity;
import org.ostrya.presencepublisher.R;
import org.ostrya.presencepublisher.ui.contract.IntentActionContract;
Expand All @@ -17,12 +20,12 @@
import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_ENABLE;

public class EnsureBluetoothServiceEnabled extends AbstractChainedHandler<String, Boolean> {
protected EnsureBluetoothServiceEnabled(Queue<HandlerFactory> handlerChain) {
super(new IntentActionContract(), handlerChain);
protected EnsureBluetoothServiceEnabled(MainActivity activity, Queue<HandlerFactory> handlerChain) {
super(activity, new IntentActionContract(), handlerChain);
}

@Override
protected void doInitialize(MainActivity activity) {
protected void doInitialize() {
if (activity.isLocationServiceNeeded()
// make linter happy
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,34 @@
package org.ostrya.presencepublisher.ui.initialization;

import static org.ostrya.presencepublisher.ui.preference.about.LocationConsentPreference.LOCATION_CONSENT;

import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;

import androidx.activity.result.contract.ActivityResultContracts;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentManager;
import androidx.preference.PreferenceManager;

import com.hypertrack.hyperlog.HyperLog;
import java.util.Queue;

import org.ostrya.presencepublisher.MainActivity;
import org.ostrya.presencepublisher.R;
import org.ostrya.presencepublisher.ui.dialog.ConfirmationDialogFragment;

import java.util.Queue;

import static org.ostrya.presencepublisher.ui.preference.about.LocationConsentPreference.LOCATION_CONSENT;

public class EnsureLocationPermission extends AbstractChainedHandler<String, Boolean> {
protected EnsureLocationPermission(Queue<HandlerFactory> handlerChain) {
super(new ActivityResultContracts.RequestPermission(), handlerChain);
protected EnsureLocationPermission(MainActivity activity, Queue<HandlerFactory> handlerChain) {
super(activity, new ActivityResultContracts.RequestPermission(), handlerChain);
}

@Override
protected void doInitialize(MainActivity activity) {
protected void doInitialize() {
if (activity.isLocationServiceNeeded()
&& ContextCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
&& ContextCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
HyperLog.i(TAG, "Location permission not yet granted, asking user ...");
FragmentManager fm = activity.getSupportFragmentManager();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

import android.app.Activity;
import android.location.LocationManager;

import androidx.fragment.app.FragmentManager;

import com.hypertrack.hyperlog.HyperLog;

import org.ostrya.presencepublisher.MainActivity;
import org.ostrya.presencepublisher.R;
import org.ostrya.presencepublisher.ui.contract.IntentActionContract;
Expand All @@ -15,12 +18,12 @@
import static android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS;

public class EnsureLocationServiceEnabled extends AbstractChainedHandler<String, Boolean> {
protected EnsureLocationServiceEnabled(Queue<HandlerFactory> handlerChain) {
super(new IntentActionContract(), handlerChain);
protected EnsureLocationServiceEnabled(MainActivity activity, Queue<HandlerFactory> handlerChain) {
super(activity, new IntentActionContract(), handlerChain);
}

@Override
protected void doInitialize(MainActivity activity) {
protected void doInitialize() {
LocationManager locationManager = (LocationManager) activity.getSystemService(LOCATION_SERVICE);
if (activity.isLocationServiceNeeded()
&& (locationManager == null || !(locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package org.ostrya.presencepublisher.ui.initialization;

import org.ostrya.presencepublisher.MainActivity;

import java.util.Queue;

@FunctionalInterface
interface HandlerFactory {
InitializationHandler create(Queue<HandlerFactory> handlerChain);
InitializationHandler create(MainActivity activity, Queue<HandlerFactory> handlerChain);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ public interface InitializationHandler {
CreateSchedule::new
));

static InitializationHandler getHandler(List<HandlerFactory> handlerChain) {
static InitializationHandler getHandler(MainActivity activity, List<HandlerFactory> handlerChain) {
LinkedList<HandlerFactory> handlerChainQueue = new LinkedList<>(handlerChain);
HandlerFactory firstFactory = Objects.requireNonNull(handlerChainQueue.poll());
return firstFactory.create(handlerChainQueue);
return firstFactory.create(activity, handlerChainQueue);
}

void initialize(MainActivity activity);
void initialize();
}

0 comments on commit 418769f

Please sign in to comment.