Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug fixes #366

Merged
merged 16 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ android {
applicationId "com.doubleangels.nextdnsmanagement"
minSdkVersion 32
targetSdk 35
versionCode 235
versionName '5.4.1'
versionCode 236
versionName '5.4.2'
resourceConfigurations += ["en", "zh", "nl", "fi", "fr", "de", "in", "it", "ja", "pl", "pt", "es", "sv", "tr"]
}

Expand Down Expand Up @@ -59,7 +59,7 @@ dependencies {
implementation 'com.jakewharton:process-phoenix:3.0.0'
implementation 'com.squareup.retrofit2:converter-gson:2.11.0'
implementation 'de.hdodenhof:circleimageview:3.1.0'
implementation 'io.sentry:sentry-android:7.20.0'
implementation 'io.sentry:sentry-android:7.20.1'
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.14'
}

Expand Down
3 changes: 2 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY"/>

<application
android:hardwareAccelerated="true"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:name=".NextDNSApplication"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="true"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.doubleangels.nextdnsmanagement;


import static android.Manifest.permission.POST_NOTIFICATIONS;

import android.content.ComponentCallbacks2;
import android.annotation.SuppressLint;
import android.app.DownloadManager;
import android.content.Intent;
Expand Down Expand Up @@ -44,16 +46,31 @@
import java.util.Locale;

public class MainActivity extends AppCompatActivity {

// WebView for displaying web content
private WebView webView;
// Boolean flag for dark mode status
private Boolean darkModeEnabled = false;
private Boolean isWebViewInitialized = false;
private Bundle webViewState = null;

@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
if (webView != null) {
Bundle webViewBundle = new Bundle();
webView.saveState(webViewBundle);
outState.putBundle("webViewState", webViewBundle);
}
outState.putBoolean("darkModeEnabled", darkModeEnabled);
}

@SuppressLint("WrongThread")
@RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
webViewState = savedInstanceState.getBundle("webViewState");
darkModeEnabled = savedInstanceState.getBoolean("darkModeEnabled");
}
setContentView(R.layout.activity_main);
if (!ProcessPhoenix.isPhoenixProcess(this)) {
// Initialize SentryManager for error tracking
Expand Down Expand Up @@ -91,8 +108,45 @@ protected void onCreate(Bundle savedInstanceState) {
// Cleanup when activity is destroyed
protected void onDestroy() {
super.onDestroy();
webView.removeAllViews();
webView.destroy();
cleanupWebView();
}

@Override
public void onLowMemory() {
super.onLowMemory();
if (!isWebViewInitialized) {
return;
}
cleanupWebView();
}

@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
if (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
if (!isWebViewInitialized) {
return;
}
cleanupWebView();
}
}

@Override
protected void onPause() {
super.onPause();
if (webView != null) {
webView.onPause();
}
}

@Override
protected void onResume() {
super.onResume();
if (webView != null) {
webView.onResume();
} else if (!isWebViewInitialized) {
setupWebViewForActivity(getString(R.string.main_url));
}
}

// Setup toolbar for the activity
Expand Down Expand Up @@ -180,13 +234,38 @@ private void setupVisualIndicatorForActivity(SentryManager sentryManager, Lifecy
}
}

// Setup WebView for the activity
private void cleanupWebView() {
if (webView != null) {
try {
// Remove all loaded content
webView.loadUrl("about:blank");

// Remove all views
webView.removeAllViews();

// Destroy the WebView
webView.destroy();
} catch (Exception e) {
// Silently handle any exceptions during cleanup
} finally {
webView = null;
isWebViewInitialized = false;
}
}
}

@SuppressLint("SetJavaScriptEnabled")
public void setupWebViewForActivity(String url) {
webView = findViewById(R.id.webView);
if (webViewState != null) {
webView.restoreState(webViewState);
} else {
webView.loadUrl(url);
}
WebSettings webViewSettings = webView.getSettings();
webViewSettings.setJavaScriptEnabled(true);
webViewSettings.setDomStorageEnabled(true);
webViewSettings.setDomStorageEnabled(true);
webViewSettings.setDatabaseEnabled(true);
webViewSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
webViewSettings.setAllowFileAccess(false);
Expand All @@ -209,6 +288,7 @@ public void onPageFinished(WebView webView, String url) {
setupDownloadManagerForActivity();
// Load URL into WebView
webView.loadUrl(url);
isWebViewInitialized = true;
}

// Setup DownloadManager for handling file downloads
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.doubleangels.nextdnsmanagement;

import android.app.Activity;
import android.app.Application;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

public class NextDNSApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// Register activity lifecycle callbacks to handle theme state
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
if (activity instanceof AppCompatActivity appCompatActivity) { // Ensure the activity is AppCompatActivity
if (savedInstanceState != null) {
appCompatActivity.getDelegate().applyDayNight();
}
}
}
@Override
public void onActivityStarted(@NonNull Activity activity) {}

@Override
public void onActivityResumed(@NonNull Activity activity) {}

@Override
public void onActivityPaused(@NonNull Activity activity) {}

@Override
public void onActivityStopped(@NonNull Activity activity) {}

@Override
public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {}

@Override
public void onActivityDestroyed(@NonNull Activity activity) {}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.view.ContextThemeWrapper;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.ImageView;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.lifecycle.LifecycleOwner;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

Expand All @@ -28,112 +28,121 @@
import java.util.List;
import java.util.Locale;

@RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
public class PermissionActivity extends AppCompatActivity {
private static final int REQUEST_POST_NOTIFICATIONS = 100;
private static final String POST_NOTIFICATIONS = android.Manifest.permission.POST_NOTIFICATIONS;

// SentryManager instance for error tracking
public SentryManager sentryManager;
private SentryManager sentryManager;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_permission);
// Initialize SentryManager for error tracking

sentryManager = new SentryManager(this);
try {
// Check if Sentry is enabled and initialize it
if (sentryManager.isEnabled()) {
SentryInitializer.initialize(this);
}
// Setup toolbar
setupToolbarForActivity();
// Setup language/locale
String appLocale = setupLanguageForActivity();
sentryManager.captureMessage("Using locale: " + appLocale);
// Setup visual indicator
setupVisualIndicatorForActivity(sentryManager, this);
setupVisualIndicatorForActivity(sentryManager);

if (needsNotificationPermission()) {
requestNotificationPermission();
}
} catch (Exception e) {
// Catch and log exceptions
sentryManager.captureException(e);
}
// Setup RecyclerView for displaying permissions list

RecyclerView recyclerView = findViewById(R.id.permissionRecyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
// Get list of permissions and set up RecyclerView adapter
List<PermissionInfo> permissions = getPermissionsList(sentryManager);
PermissionsAdapter adapter = new PermissionsAdapter(permissions);
recyclerView.setAdapter(adapter);
recyclerView.setAdapter(new PermissionsAdapter(permissions));
}

private boolean needsNotificationPermission() {
return checkSelfPermission(POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED;
}

private void requestNotificationPermission() {
requestPermissions(new String[]{POST_NOTIFICATIONS}, REQUEST_POST_NOTIFICATIONS);
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_POST_NOTIFICATIONS) {
refreshPermissionsList();
}
}

// Setup toolbar for the activity
private void setupToolbarForActivity() {
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false);
}
// Setup click listener for connection status ImageView

ImageView imageView = findViewById(R.id.connectionStatus);
imageView.setOnClickListener(v -> startActivity(new Intent(this, StatusActivity.class)));
}

// Setup language/locale for the activity
private String setupLanguageForActivity() {
Configuration config = getResources().getConfiguration();
Locale appLocale = config.getLocales().get(0);
Locale appLocale = Locale.getDefault();
Locale.setDefault(appLocale);
Configuration newConfig = new Configuration(config);
newConfig.setLocale(appLocale);
new ContextThemeWrapper(getBaseContext(), R.style.AppTheme).applyOverrideConfiguration(newConfig);
Configuration config = getResources().getConfiguration();
config.setLocale(appLocale);
getResources().updateConfiguration(config, getResources().getDisplayMetrics());
return appLocale.getLanguage();
}

// Setup visual indicator for the activity
private void setupVisualIndicatorForActivity(SentryManager sentryManager, LifecycleOwner lifecycleOwner) {
private void setupVisualIndicatorForActivity(SentryManager sentryManager) {
try {
new VisualIndicator(this).initialize(this, lifecycleOwner, this);
new VisualIndicator(this).initialize(this, this, this);
} catch (Exception e) {
// Catch and log exceptions
sentryManager.captureException(e);
}
}

// Retrieve the list of permissions requested by the app
private void refreshPermissionsList() {
RecyclerView recyclerView = findViewById(R.id.permissionRecyclerView);
if (recyclerView != null) {
List<PermissionInfo> permissions = getPermissionsList(sentryManager);
recyclerView.setAdapter(new PermissionsAdapter(permissions));
}
}

private List<PermissionInfo> getPermissionsList(SentryManager sentryManager) {
List<PermissionInfo> permissions = new ArrayList<>();
try {
// Get package info including requested permissions
PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_PERMISSIONS);
if (packageInfo.requestedPermissions != null) {
// Retrieve PermissionInfo for each requested permission and add to list
for (String permission : packageInfo.requestedPermissions) {
PermissionInfo permissionInfo = getPackageManager().getPermissionInfo(permission, 0);
permissions.add(permissionInfo);
}
}
} catch (PackageManager.NameNotFoundException e) {
// Catch and log exceptions
sentryManager.captureException(e);
}
return permissions;
}

// Inflate menu for the activity
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_back_only, menu);
getMenuInflater().inflate(R.menu.menu_back_only, menu);
return true;
}

// Handle menu item selection
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.back) {
// Navigate back to SettingsActivity
Intent mainIntent = new Intent(this, SettingsActivity.class);
startActivity(mainIntent);
startActivity(new Intent(this, SettingsActivity.class));
return true;
}
return super.onContextItemSelected(item);
return super.onOptionsItemSelected(item);
}
}
Loading
Loading