Skip to content

Commit

Permalink
Warn user if "Start Core on Boot" needs special perms
Browse files Browse the repository at this point in the history
  • Loading branch information
TuxPaper committed Jan 19, 2024
1 parent 400d34c commit 2dc2974
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,13 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.SystemClock;
import android.util.Log;

import androidx.core.content.ContextCompat;

import com.biglybt.android.client.*;
import com.biglybt.android.client.service.BiglyBTService;
import com.biglybt.android.client.session.RemoteProfile;
import com.biglybt.util.Thunk;

/**
* Simple Broadcast Receiver that launches BiglyBT Core on boot if configured by
Expand All @@ -42,6 +40,11 @@ public class BootCompleteReceiver

@Override
public void onReceive(final Context context, Intent intent) {
final long startedOn = SystemClock.elapsedRealtime();
final IAnalyticsTracker tracker = AnalyticsTracker.getInstance();
tracker.setDeviceName(
AndroidUtils.getDeviceNameForLogger(context.getContentResolver()));

if (AndroidUtils.DEBUG) {
Log.d(TAG, "BroadcastReceiver.onReceive");
}
Expand All @@ -51,9 +54,12 @@ public void onReceive(final Context context, Intent intent) {
if (!intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
return;
}
if (AndroidUtils.DEBUG) {
Log.d(TAG, "BroadcastReceiver.onReceive ACTION_BOOT_COMPLETED");
}
final PendingResult pendingResult = goAsync();

StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
new Thread(() -> {
try {
if (RemoteUtils.getCoreProfile() == null
Expand All @@ -64,21 +70,15 @@ public void onReceive(final Context context, Intent intent) {
if (AndroidUtils.DEBUG) {
Log.d(TAG, "startForegroundService BiglyBTService");
}

ContextCompat.startForegroundService(context,
new Intent(BiglyBTService.INTENT_ACTION_START, null, context,
BiglyBTService.class));
//BiglyCoreUtils.startBiglyBTCoreService() does bindings which BroadcastReceivers shouldn't do

} catch (Throwable t) {
// TODO: We get an IllegalAccessError on certain devices (Skyworth; Android 8.0)
// due to R8/Proguard. Hopefully it will get fixed, but since it's
// only been reported on one device on one OS version, it may not be
// We could warn the user, provide a non-optimized apk for them
// via non-Google Play
IAnalyticsTracker tracker = AnalyticsTracker.getInstance();
tracker.setDeviceName(
AndroidUtils.getDeviceNameForLogger(context.getContentResolver()));
tracker.setLastViewName(
(SystemClock.elapsedRealtime() - startedOn) + "ms");
tracker.logError(t, stackTrace);
} finally {
pendingResult.finish();
Expand Down
105 changes: 80 additions & 25 deletions app/src/main/java/com/biglybt/android/client/RemoteUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,18 @@
package com.biglybt.android.client;

import android.Manifest.permission;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings;
import android.util.Log;

import androidx.annotation.*;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
Expand All @@ -40,7 +45,11 @@
import com.biglybt.android.client.session.RemoteProfileFactory;
import com.biglybt.android.client.session.SessionManager;
import com.biglybt.android.util.JSONUtils;
import com.biglybt.util.RunnableUIThread;
import com.biglybt.util.Thunk;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;

import net.grandcentrix.tray.TrayPreferences;

import java.util.List;
import java.util.Map;
Expand All @@ -54,13 +63,11 @@ public class RemoteUtils
//private static final String TAG = "RemoteUtils";
public static String lastOpenDebug = null;

/**
*
* @return true if opened immediately
*/
public static boolean openRemote(final AppCompatActivityM activity,
public static void openRemote(final AppCompatActivityM activity,
final RemoteProfile remoteProfile, final boolean isMain,
final boolean closeActivityOnSuccess) {
final RunnableUIThread runOnNoOpen) {

// Ensure remote is saved in AppPreferences
OffThread.runOffUIThread(() -> {
AppPreferences appPreferences = BiglyBTApp.getAppPreferences();

Expand All @@ -69,21 +76,73 @@ public static boolean openRemote(final AppCompatActivityM activity,
}
});

// Ensure remote can autostart if option enabled
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M
&& remoteProfile.isLocalHost()) {
AppPreferences appPreferences = BiglyBTApp.getAppPreferences();
TrayPreferences prefs = appPreferences.getPreferences();
if (prefs.getBoolean(CorePrefs.PREF_CORE_AUTOSTART, false)) {

PowerManager pm = (PowerManager) activity.getSystemService(
Context.POWER_SERVICE);
boolean isIgnoringBatteryOptimizations = pm.isIgnoringBatteryOptimizations(
activity.getApplicationContext().getPackageName());

boolean isRestricted = false;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
ActivityManager am = (ActivityManager) activity.getSystemService(
Context.ACTIVITY_SERVICE);
isRestricted = am.isBackgroundRestricted();
}

if (AndroidUtils.DEBUG) {
Log.d("BatteryOpt",
"isIgnoringBatteryOptimizations: "
+ isIgnoringBatteryOptimizations + "; isRestricted: "
+ isRestricted);
}
if (isRestricted || !isIgnoringBatteryOptimizations) {
OffThread.runOnUIThread(activity, false, (a) -> {
AlertDialog.Builder builder = new MaterialAlertDialogBuilder(
activity).setCancelable(true);
builder.setTitle(R.string.core_auto_start_on_boot);
builder.setMessage(R.string.core_auto_start_on_boot_auth);
builder.setPositiveButton(R.string.settings, (dialog, which) -> {
Intent intent = new Intent();
intent.setAction(
Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
activity.startActivity(intent);
if (runOnNoOpen != null) {
runOnNoOpen.run();
}
});
builder.setNegativeButton(R.string.disable_auto_start,
(dialog, which) -> OffThread.runOffUIThread(() -> {
prefs.put(CorePrefs.PREF_CORE_AUTOSTART, false);

OffThread.runOnUIThread(
() -> reallyOpenRemote(activity, remoteProfile, isMain));
}));
if (runOnNoOpen != null) {
builder.setOnCancelListener(dialog -> runOnNoOpen.run());
}
builder.show();
});
return;
}
}
}

// Ensure permissions
List<String> requiredPermissions = remoteProfile.getRequiredPermissions();
if (requiredPermissions.size() > 0) {
return activity.requestPermissions(
requiredPermissions.toArray(new String[0]),
activity.requestPermissions(requiredPermissions.toArray(new String[0]),
new PermissionResultHandler() {
@WorkerThread
@Override
public void onAllGranted() {
OffThread.runOnUIThread(() -> {

if (closeActivityOnSuccess && !isMain) {
activity.finish();
}
reallyOpenRemote(activity, remoteProfile, isMain);
});
OffThread.runOnUIThread(
() -> reallyOpenRemote(activity, remoteProfile, isMain));
}

@WorkerThread
Expand All @@ -96,23 +155,19 @@ public void onSomeDenied(PermissionRequestResults results) {
if (denies.size() > 0) {
AndroidUtilsUI.showDialog(activity, R.string.permission_denied,
R.string.error_client_requires_permissions);
if (runOnNoOpen != null) {
OffThread.runOnUIThread(runOnNoOpen);
}
} else {
OffThread.runOnUIThread(() -> {
if (closeActivityOnSuccess && !isMain) {
activity.finish();
}
reallyOpenRemote(activity, remoteProfile, isMain);
});
OffThread.runOnUIThread(
() -> reallyOpenRemote(activity, remoteProfile, isMain));
}
}
});
return;
}

if (closeActivityOnSuccess && !isMain) {
activity.finish();
}
reallyOpenRemote(activity, remoteProfile, isMain);
return true;
}

@Thunk
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import android.os.Bundle;
import android.util.Log;

import androidx.annotation.AnyThread;
import androidx.annotation.WorkerThread;
import androidx.fragment.app.FragmentManager;

import com.biglybt.android.client.*;
Expand Down Expand Up @@ -50,25 +52,22 @@ protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.activity_splash);
handleIntent(getIntent());
OffThread.runOffUIThread(() -> handleIntent(getIntent()));
}

private void handleIntent(Intent intent) {
OffThread.runOffUIThread(() -> {
boolean handled = handleIntent2(intent);
if (!handled) {
// .commit will send it over to UI thread for us
FragmentManager fm = getSupportFragmentManager();
if (fm.isDestroyed() || fm.findFragmentByTag("PSF") != null) {
return;
}
fm.beginTransaction().add(R.id.fragment_container,
new ProfileSelectorFragment(), "PSF").commit();
}
});
@AnyThread
private void showProfileSelector() {
// .commit will send it over to UI thread for us
FragmentManager fm = getSupportFragmentManager();
if (fm.isDestroyed() || fm.findFragmentByTag("PSF") != null) {
return;
}
fm.beginTransaction().add(R.id.fragment_container,
new ProfileSelectorFragment(), "PSF").commit();
}

private boolean handleIntent2(Intent intent) {
@WorkerThread
private void handleIntent(Intent intent) {
boolean forceProfileListOpen = false;

if (AndroidUtils.DEBUG) {
Expand Down Expand Up @@ -124,17 +123,19 @@ private boolean handleIntent2(Intent intent) {
openAfterEdit = true;
RemoteUtils.editProfile(remoteProfile, getSupportFragmentManager(),
"1".equals(reqPW));
return false;
showProfileSelector();
return;
} else if (ac.length() < 100) {
RemoteProfile remoteProfile = RemoteProfileFactory.create("vuze",
ac);
return RemoteUtils.openRemote(this, remoteProfile, true, true);
RemoteUtils.openRemote(this, remoteProfile, true,
() -> showProfileSelector());
return;
}
}

// check for http[s]://remote.vuze.com/ac=*
if (("remote.vuze.com".equals(host)
|| "remote.biglybt.com".equals(host))
// check for http[s]://remote.biglybt.com/ac=*
if ("remote.biglybt.com".equals(host)
&& data.getQueryParameter("ac") != null) {
String ac = data.getQueryParameter("ac");
if (AndroidUtils.DEBUG) {
Expand All @@ -144,7 +145,9 @@ private boolean handleIntent2(Intent intent) {
if (ac != null && ac.length() < 100) {
RemoteProfile remoteProfile = RemoteProfileFactory.create("vuze",
ac);
return RemoteUtils.openRemote(this, remoteProfile, true, true);
RemoteUtils.openRemote(this, remoteProfile, true,
() -> showProfileSelector());
return;
}
}
} catch (Exception e) {
Expand All @@ -169,7 +172,7 @@ private boolean handleIntent2(Intent intent) {
}
// New User: Send them to Login (Account Creation)
LoginActivity.launch(this);
return true;
return;
}

if (!clearTop && noSavedInstanceState) {
Expand All @@ -178,7 +181,8 @@ private boolean handleIntent2(Intent intent) {
if (AndroidUtils.DEBUG) {
Log.d(TAG, "No last remote");
}
return false;
showProfileSelector();
return;
}

if (intent.getData() == null
Expand All @@ -188,14 +192,17 @@ private boolean handleIntent2(Intent intent) {
+ appPreferences.getRemotes().length);
}
try {
return RemoteUtils.openRemote(this, remoteProfile, true, true);
RemoteUtils.openRemote(this, remoteProfile, true,
() -> showProfileSelector());
return;
} catch (Throwable t) {
AnalyticsTracker.getInstance(this).logError(t);
}
}
}
}
return false;

showProfileSelector();
}

@Override
Expand All @@ -205,14 +212,15 @@ protected void onNewIntent(Intent intent) {
Log.d(TAG, "onNewIntent " + intent);
}
setIntent(intent);
handleIntent(intent);
OffThread.runOffUIThread(() -> handleIntent(intent));
}

@Override
public void profileEditDone(RemoteProfile oldProfile,
RemoteProfile newProfile) {
if (openAfterEdit) {
RemoteUtils.openRemote(this, newProfile, true, true);
RemoteUtils.openRemote(this, newProfile, true,
() -> showProfileSelector());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ private void loginButtonClicked(View v) {
private void openRemote(String ac) {
RemoteProfile remoteProfile = RemoteProfileFactory.create(
RemoteProfile.DEFAULT_USERNAME, ac);
RemoteUtils.openRemote(this, remoteProfile, false, false);
RemoteUtils.openRemote(this, remoteProfile, false, null);
}

@SuppressWarnings("UnusedParameters")
Expand Down Expand Up @@ -443,7 +443,7 @@ private void createCore() {
@Override
public void profileEditDone(RemoteProfile oldProfile,
RemoteProfile newProfile) {
RemoteUtils.openRemote(this, newProfile, false, false);
RemoteUtils.openRemote(this, newProfile, false, null);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) {
if (item instanceof RemoteProfile) {
RemoteProfile remote = (RemoteProfile) item;
boolean isMain = activity.getIntent().getData() != null;
RemoteUtils.openRemote(activity, remote, isMain, isMain);
RemoteUtils.openRemote(activity, remote, isMain, null);
}
});

Expand Down

0 comments on commit 2dc2974

Please sign in to comment.