From afe91a8e27d2b38c71dc91a92120a2e9dca9a6f4 Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 9 Jun 2024 14:52:55 +0330 Subject: [PATCH 01/34] Refactor FileManager.java with detailed comments --- .../java/org/bepass/oblivion/FileManager.java | 148 +++++++++++++++++- 1 file changed, 143 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/bepass/oblivion/FileManager.java b/app/src/main/java/org/bepass/oblivion/FileManager.java index 024463af..ca64460c 100644 --- a/app/src/main/java/org/bepass/oblivion/FileManager.java +++ b/app/src/main/java/org/bepass/oblivion/FileManager.java @@ -5,16 +5,30 @@ import java.util.Set; +/** + * This class provides a singleton instance for managing user data within the application. + * It utilizes SharedPreferences to store and retrieve various data types securely. + */ public class FileManager { private static FileManager instance; private final SharedPreferences sharedPreferences; - // Private constructor for singleton pattern + /** + * Private constructor to enforce singleton pattern. + * + * @param context The application context used to access SharedPreferences. + */ private FileManager(Context context) { sharedPreferences = context.getSharedPreferences("UserData", Context.MODE_PRIVATE); } - // Public method to get the singleton instance + /** + * Public method to retrieve the singleton instance of FileManager. + * This ensures only one instance exists throughout the application. + * + * @param context The application context required for SharedPreferences. + * @return The singleton instance of FileManager. + */ public static synchronized FileManager getInstance(Context context) { if (instance == null) { instance = new FileManager(context.getApplicationContext()); @@ -22,77 +36,201 @@ public static synchronized FileManager getInstance(Context context) { return instance; } - // Methods to set various types of data + // =========================================================================== + // Methods for setting various data types + + /** + * Stores a String value associated with a key in SharedPreferences. + * + * @param name The key to identify the data. + * @param value The String value to be stored. + */ public void set(String name, String value) { sharedPreferences.edit().putString(name, value).apply(); } + /** + * Stores a boolean value associated with a key in SharedPreferences. + * + * @param name The key to identify the data. + * @param value The boolean value to be stored. + */ public void set(String name, boolean value) { sharedPreferences.edit().putBoolean(name, value).apply(); } + /** + * Stores a Set of Strings associated with a key in SharedPreferences. + * + * @param name The key to identify the data. + * @param value The Set of Strings to be stored. + */ public void set(String name, Set value) { sharedPreferences.edit().putStringSet(name, value).apply(); } + /** + * Stores an integer value associated with a key in SharedPreferences. + * + * @param name The key to identify the data. + * @param value The integer value to be stored. + */ public void set(String name, int value) { sharedPreferences.edit().putInt(name, value).apply(); } + /** + * Stores a float value associated with a key in SharedPreferences. + * + * @param name The key to identify the data. + * @param value The float value to be stored. + */ public void set(String name, float value) { sharedPreferences.edit().putFloat(name, value).apply(); } + /** + * Stores a long value associated with a key in SharedPreferences. + * + * @param name The key to identify the data. + * @param value The long value to be stored. + */ public void set(String name, long value) { sharedPreferences.edit().putLong(name, value).apply(); } + /** + * Stores a double value associated with a key in SharedPreferences. + * Double values are converted to longs for storage due to limitations. + * + * @param name The key to identify the data. + * @param value The double value to be stored. + */ public void setDouble(String name, double value) { sharedPreferences.edit().putLong(name, Double.doubleToRawLongBits(value)).apply(); } - // Methods to get various types of data + /** + * Retrieves a String value associated with a key from SharedPreferences. + * If the key doesn't exist, an empty string is returned. + * + * @param name The key to identify the data. + * @return The String value associated with the key, + * * @return "" (empty string) if the key doesn't exist. + */ public String getString(String name) { return sharedPreferences.getString(name, ""); } + /** + * Retrieves a String value associated with a key from SharedPreferences. + * If the key doesn't exist, the provided default value is returned. + * + * @param name The key to identify the data. + * @param defaultValue The default value to return if the key doesn't exist. + * @return The String value associated with the key, or the default value if not found. + */ public String getString(String name, String defaultValue) { return sharedPreferences.getString(name, defaultValue); } + /** + * Retrieves a Set of Strings associated with a key from SharedPreferences. + * If the key doesn't exist, the provided default set is returned. + * + * @param name The key to identify the data. + * @param def The default Set of Strings to return if the key doesn't exist. + * @return The Set of Strings associated with the key, or the default set if not found. + */ public Set getStringSet(String name, Set def) { return sharedPreferences.getStringSet(name, def); } + /** + * Retrieves a boolean value associated with a key from SharedPreferences. + * If the key doesn't exist, false is returned. + * + * @param name The key to identify the data. + * @return The boolean value associated with the key, or false if not found. + */ public boolean getBoolean(String name) { return sharedPreferences.getBoolean(name, false); } + /** + * Retrieves a boolean value associated with a key from SharedPreferences. + * If the key doesn't exist, the provided default value is returned. + * + * @param name The key to identify the data. + * @param defaultValue The default boolean value to return if the key doesn't exist. + * @return The boolean value associated with the key, or the default value if not found. + */ public boolean getBoolean(String name, boolean defaultValue) { return sharedPreferences.getBoolean(name, defaultValue); } + /** + * Retrieves an integer value associated with a key from SharedPreferences. + * If the key doesn't exist, 0 is returned. + * + * @param name The key to identify the data. + * @return The integer value associated with the key, or 0 if not found. + */ public int getInt(String name) { return sharedPreferences.getInt(name, 0); } + /** + * Retrieves a float value associated with a key from SharedPreferences. + * If the key doesn't exist, 0.0f is returned. + * + * @param name The key to identify the data. + * @return The float value associated with the key, or 0.0f if not found. + */ public float getFloat(String name) { return sharedPreferences.getFloat(name, 0f); } + /** + * Retrieves a long value associated with a key from SharedPreferences. + * If the key doesn't exist, 0L is returned. + * + * @param name The key to identify the data. + * @return The long value associated with the key, or 0L if not found. + */ public long getLong(String name) { return sharedPreferences.getLong(name, 0L); } + /** + * Retrieves a double value associated with a key from SharedPreferences. + * Since doubles are stored as longs, the retrieved long is converted back to a double. + * If the key doesn't exist, 0.0 is returned. + * + * @param name The key to identify the data. + * @return The double value associated with the key, or 0.0 if not found. + */ public double getDouble(String name) { return Double.longBitsToDouble(sharedPreferences.getLong(name, 0)); } - // Methods for handling logs + // =========================================================================== + // Methods for handling logs (optional, based on your needs) + + /** + * Resets the log stored in SharedPreferences under the key "APP_LOG". + * This removes any existing log message and sets it to an empty string. + */ public void resetLog() { sharedPreferences.edit().putString("APP_LOG", "").apply(); } + /** + * Adds a new log message to SharedPreferences under the key "APP_LOG". + * The existing log message (if any) will be overwritten. + * + * @param log The String message to be stored as the application log. + */ public void addLog(String log) { sharedPreferences.edit().putString("APP_LOG", log).apply(); } From d60a324bfa555a81b402b6a78b1db35a6176933a Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 9 Jun 2024 15:00:00 +0330 Subject: [PATCH 02/34] Implemented ApplicationLoader class for global context access and initialization & added to manifest --- app/src/main/AndroidManifest.xml | 1 + .../oblivion/base/ApplicationLoader.java | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 app/src/main/java/org/bepass/oblivion/base/ApplicationLoader.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ae7b89bd..86d59ffb 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -33,6 +33,7 @@ android:banner="@mipmap/tv_banner" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" + android:name=".base.ApplicationLoader" tools:replace="android:supportsRtl" android:screenOrientation="portrait" android:enableOnBackInvokedCallback="true" diff --git a/app/src/main/java/org/bepass/oblivion/base/ApplicationLoader.java b/app/src/main/java/org/bepass/oblivion/base/ApplicationLoader.java new file mode 100644 index 00000000..bfc6cb0b --- /dev/null +++ b/app/src/main/java/org/bepass/oblivion/base/ApplicationLoader.java @@ -0,0 +1,37 @@ +package org.bepass.oblivion.base; + +import android.annotation.SuppressLint; +import android.app.Application; +import android.content.Context; + +/** + * ApplicationLoader is a custom Application class that extends the Android Application class. + * It is designed to provide a centralized context reference throughout the application. + */ +public class ApplicationLoader extends Application { + + // Tag for logging purposes + private static final String TAG = "ApplicationLoader"; + + // Context reference + @SuppressLint("StaticFieldLeak") + private static Context context; + + /** + * This method is called when the application is starting, before any activity, service, or receiver objects (excluding content providers) have been created. + * @see android.app.Application#onCreate() + */ + @Override + public void onCreate() { + super.onCreate(); + context = getApplicationContext(); + } + + /** + * Returns the application context. + * @return The application context. + */ + public static Context getAppCtx(){ + return context; + } +} From 68f5495247f12bcaf5e5dfda4d19aa694c887825 Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 9 Jun 2024 15:01:43 +0330 Subject: [PATCH 03/34] Implemented default night mode configuration --- .../main/java/org/bepass/oblivion/base/ApplicationLoader.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/org/bepass/oblivion/base/ApplicationLoader.java b/app/src/main/java/org/bepass/oblivion/base/ApplicationLoader.java index bfc6cb0b..ae0fd4c9 100644 --- a/app/src/main/java/org/bepass/oblivion/base/ApplicationLoader.java +++ b/app/src/main/java/org/bepass/oblivion/base/ApplicationLoader.java @@ -4,6 +4,8 @@ import android.app.Application; import android.content.Context; +import androidx.appcompat.app.AppCompatDelegate; + /** * ApplicationLoader is a custom Application class that extends the Android Application class. * It is designed to provide a centralized context reference throughout the application. @@ -25,6 +27,7 @@ public class ApplicationLoader extends Application { public void onCreate() { super.onCreate(); context = getApplicationContext(); + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); } /** From c3cc2b74ef9edb3ff4a5506760546a363bb8bf02 Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 9 Jun 2024 15:08:21 +0330 Subject: [PATCH 04/34] add viewbinding to gradle Implemented BaseActivity for generic data binding setup in AppCompatActivity subclasses --- app/build.gradle | 3 ++ .../bepass/oblivion/base/BaseActivity.java | 42 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 app/src/main/java/org/bepass/oblivion/base/BaseActivity.java diff --git a/app/build.gradle b/app/build.gradle index 86794cd9..317deb49 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,6 +18,9 @@ android { useSupportLibrary true } } + buildFeatures { + dataBinding = true + } buildTypes { release { diff --git a/app/src/main/java/org/bepass/oblivion/base/BaseActivity.java b/app/src/main/java/org/bepass/oblivion/base/BaseActivity.java new file mode 100644 index 00000000..41db36ee --- /dev/null +++ b/app/src/main/java/org/bepass/oblivion/base/BaseActivity.java @@ -0,0 +1,42 @@ +package org.bepass.oblivion.base; + +import android.content.Intent; +import android.os.Bundle; + +import androidx.activity.result.ActivityResult; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.databinding.DataBindingUtil; +import androidx.databinding.ViewDataBinding; + +/** + * BaseActivity is an abstract class serving as a base for activities in an Android application. + * It extends the AppCompatActivity class and utilizes Android's data binding framework. + * @param The type of ViewDataBinding associated with the activity. + */ +public abstract class BaseActivity extends AppCompatActivity { + + // ViewDataBinding instance associated with the activity layout + protected B binding; + + // Tag for logging purposes + protected String TAG = this.getClass().getSimpleName(); + + /** + * Abstract method to be implemented by subclasses to provide the layout resource ID for the activity. + * @return The layout resource ID. + */ + protected abstract int getLayoutResourceId(); + + /** + * Called when the activity is starting. + * @param savedInstanceState If the activity is being re-initialized after previously being shut down then this Bundle contains the data it most recently supplied in onSaveInstanceState(Bundle). + * @see AppCompatActivity#onCreate(Bundle) + */ + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // Inflates the layout and initializes the binding object + binding = DataBindingUtil.setContentView(this, getLayoutResourceId()); + } +} From 3d1455ca25d9de17049d478828a45f9a9efcb488 Mon Sep 17 00:00:00 2001 From: Benny Date: Mon, 10 Jun 2024 11:12:43 +0330 Subject: [PATCH 05/34] refactor: Optimize PublicIPUtils with improved scheduling and error handling - Replaced busy-waiting loop with ScheduledExecutorService for retry mechanism - Added HandlerThread for background operations and thread safety - Enhanced logging for better debugging and traceability - Improved error handling and timeout checks - Ensured more efficient scheduling of retries --- .../org/bepass/oblivion/MainActivity.java | 2 +- .../org/bepass/oblivion/PublicIPUtils.java | 107 ++++++++++++------ 2 files changed, 76 insertions(+), 33 deletions(-) diff --git a/app/src/main/java/org/bepass/oblivion/MainActivity.java b/app/src/main/java/org/bepass/oblivion/MainActivity.java index 37a380f3..f9f05410 100644 --- a/app/src/main/java/org/bepass/oblivion/MainActivity.java +++ b/app/src/main/java/org/bepass/oblivion/MainActivity.java @@ -48,7 +48,7 @@ protected void onCreate(Bundle savedInstanceState) { cleanOrMigrateSettings(); // Get the global PublicIPUtils instance - pIPUtils = PublicIPUtils.getInstance(getApplicationContext()); + pIPUtils = PublicIPUtils.getInstance(); // Initialize the LocaleHandler and set the locale localeHandler = new LocaleHandler(this); diff --git a/app/src/main/java/org/bepass/oblivion/PublicIPUtils.java b/app/src/main/java/org/bepass/oblivion/PublicIPUtils.java index 16036a35..6aebc4ea 100644 --- a/app/src/main/java/org/bepass/oblivion/PublicIPUtils.java +++ b/app/src/main/java/org/bepass/oblivion/PublicIPUtils.java @@ -2,9 +2,11 @@ import android.content.Context; import android.os.Handler; +import android.util.Log; import com.vdurmont.emoji.EmojiManager; +import org.bepass.oblivion.base.ApplicationLoader; import org.json.JSONObject; import java.net.InetSocketAddress; @@ -13,74 +15,115 @@ import java.util.Objects; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; - +/** + * A utility class for fetching public IP details including the country and its flag. + */ public class PublicIPUtils { - + private static final String TAG = "PublicIPUtils"; private static PublicIPUtils instance; private final FileManager fm; - ExecutorService executorService = Executors.newFixedThreadPool(1); + private static final int TIMEOUT_SECONDS = 5; + private static final int RETRY_DELAY_MILLIS = 1000; + private static final int TIMEOUT_MILLIS = 30 * 1000; + private final ScheduledExecutorService scheduler; + private static final String URL_COUNTRY_API = "https://api.country.is/"; - public PublicIPUtils(Context context) { - fm = FileManager.getInstance(context); + /** + * Private constructor to enforce singleton pattern. + * + * @param context The application context. + */ + private PublicIPUtils(Context context) { + this.fm = FileManager.getInstance(context); + this.scheduler = Executors.newSingleThreadScheduledExecutor(); } - // Public method to get the singleton instance - public static synchronized PublicIPUtils getInstance(Context context) { + /** + * Public method to get the singleton instance. + * + * @return The singleton instance of PublicIPUtils. + */ + public static synchronized PublicIPUtils getInstance() { if (instance == null) { - instance = new PublicIPUtils(context.getApplicationContext()); + instance = new PublicIPUtils(ApplicationLoader.getAppCtx()); } return instance; } - + /** + * Fetches public IP details including the country and its flag. This method uses a + * retry mechanism with a specified timeout to ensure reliable fetching of the IP details. + * + * @param callback The callback to handle the IP details once fetched. + */ public void getIPDetails(IPDetailsCallback callback) { Handler handler = new Handler(); + long startTime = System.currentTimeMillis(); IPDetails details = new IPDetails(); - executorService.execute(() -> { - long startTime = System.currentTimeMillis(); - while (System.currentTimeMillis() - startTime < 30 * 1000) { // 30 seconds + + Log.d(TAG, "Starting getIPDetails process"); + + scheduler.schedule(() -> { + while (System.currentTimeMillis() - startTime < TIMEOUT_MILLIS) { // 30 seconds + Log.d(TAG, "Attempting to fetch IP details"); try { int socksPort = Integer.parseInt(fm.getString("USERSETTING_port")); Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("localhost", socksPort)); + OkHttpClient client = new OkHttpClient.Builder() .proxy(proxy) - .connectTimeout(5, TimeUnit.SECONDS) // 5 seconds connection timeout - .readTimeout(5, TimeUnit.SECONDS) // 5 seconds read timeout + .connectTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS) // 5 seconds connection timeout + .readTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS) // 5 seconds read timeout .build(); + Request request = new Request.Builder() - .url("https://api.country.is/") + .url(URL_COUNTRY_API) .build(); - JSONObject jsonData; + try (Response response = client.newCall(request).execute()) { - jsonData = new JSONObject(Objects.requireNonNull(response.body()).string()); + if (response.body() != null) { + JSONObject jsonData = new JSONObject(Objects.requireNonNull(response.body()).string()); + details.ip = jsonData.getString("ip"); + details.country = jsonData.getString("country"); + details.flag = EmojiManager.getForAlias(details.country.toLowerCase(Locale.ROOT)).getUnicode(); + Log.d(TAG, "IP details retrieved successfully"); + } + handler.post(() -> callback.onDetailsReceived(details)); + return; // Exit the loop if details retrieved + } catch (Exception e) { + Log.e(TAG, "Error parsing the response or setting details", e); } - details.ip = jsonData.getString("ip"); - details.country = jsonData.getString("country"); - details.flag = EmojiManager.getForAlias(jsonData.getString("country").toLowerCase(Locale.ROOT)).getUnicode(); - handler.post(() -> callback.onDetailsReceived(details)); - return; } catch (Exception e) { - e.printStackTrace(); - } - - try { - Thread.sleep(1000); // Sleep before retrying - } catch (InterruptedException e) { - e.printStackTrace(); - return; + Log.e(TAG, "Error fetching IP details", e); + } finally { + // Schedule next retry even if an error occurred + Log.d(TAG, "Scheduling next retry after delay"); + scheduler.schedule(() -> getIPDetails(callback), RETRY_DELAY_MILLIS, TimeUnit.MILLISECONDS); } - handler.post(() -> callback.onDetailsReceived(details)); } - }); + Log.d(TAG, "Timeout reached without successful IP details retrieval"); + // Timeout reached, no details retrieved + handler.post(() -> callback.onDetailsReceived(details)); + }, 0, TimeUnit.MILLISECONDS); // Schedule initial attempt immediately } + /** + * Callback interface for receiving IP details. + */ public interface IPDetailsCallback { + + /** + * Called when IP details are received. + * + * @param details The IP details. + */ void onDetailsReceived(IPDetails details); } } From 30c4c78e9fcf5362cfe52c56effaaf68855414ad Mon Sep 17 00:00:00 2001 From: Benny Date: Mon, 10 Jun 2024 11:23:34 +0330 Subject: [PATCH 06/34] feat: Integrate data binding into SplashScreenActivity - Enabled data binding in the project - Updated activity_splash_screen.xml to use data binding - Modified SplashScreenActivity to bind the layout using data binding - Refactored ClickHandler to work with data binding --- .../org/bepass/oblivion/MainActivity.java | 25 +- .../bepass/oblivion/SplashScreenActivity.java | 62 +++- .../res/layout/activity_splash_screen.xml | 302 +++++++++--------- 3 files changed, 218 insertions(+), 171 deletions(-) diff --git a/app/src/main/java/org/bepass/oblivion/MainActivity.java b/app/src/main/java/org/bepass/oblivion/MainActivity.java index f9f05410..c6fc9a6d 100644 --- a/app/src/main/java/org/bepass/oblivion/MainActivity.java +++ b/app/src/main/java/org/bepass/oblivion/MainActivity.java @@ -41,6 +41,12 @@ public class MainActivity extends StateAwareBaseActivity { private LocaleHandler localeHandler; private final Handler handler = new Handler(); + public static void start(Context context) { + Intent starter = new Intent(context, MainActivity.class); + starter.putExtra("origin", context.getClass().getSimpleName()); + context.startActivity(starter); + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -58,7 +64,7 @@ protected void onCreate(Bundle savedInstanceState) { // Handle language change based on floatingActionButton value FloatingActionButton floatingActionButton = findViewById(R.id.floatingActionButton); - floatingActionButton.setOnClickListener(v -> localeHandler.showLanguageSelectionDialog(()-> + floatingActionButton.setOnClickListener(v -> localeHandler.showLanguageSelectionDialog(() -> localeHandler.restartActivity(this))); // Views ImageView infoIcon = findViewById(R.id.info_icon); @@ -108,14 +114,14 @@ protected void onCreate(Bundle savedInstanceState) { } // To check is Internet Connection is available handler.postDelayed(new Runnable() { - @Override - public void run() { - if(!lastKnownConnectionState.isDisconnected()) { - checkInternetConnectionAndDisconnectVPN(); - handler.postDelayed(this, 3000); // Check every 3 seconds - } + @Override + public void run() { + if (!lastKnownConnectionState.isDisconnected()) { + checkInternetConnectionAndDisconnectVPN(); + handler.postDelayed(this, 3000); // Check every 3 seconds } - }, 5000); // Start checking after 5 seconds + } + }, 5000); // Start checking after 5 seconds }); @@ -175,6 +181,7 @@ private void checkInternetConnectionAndDisconnectVPN() { stopVpnService(this); } } + protected void cleanOrMigrateSettings() { // Get the global FileManager instance FileManager fileManager = FileManager.getInstance(getApplicationContext()); @@ -233,7 +240,7 @@ void onConnectionStateChange(ConnectionState state) { ipProgressBar.setVisibility(View.GONE); pIPUtils.getIPDetails((details) -> { if (details.ip != null) { - String ipString = details.ip+ " " + details.flag; + String ipString = details.ip + " " + details.flag; publicIP.setText(ipString); publicIP.setVisibility(View.VISIBLE); } diff --git a/app/src/main/java/org/bepass/oblivion/SplashScreenActivity.java b/app/src/main/java/org/bepass/oblivion/SplashScreenActivity.java index 54212178..625d76b6 100644 --- a/app/src/main/java/org/bepass/oblivion/SplashScreenActivity.java +++ b/app/src/main/java/org/bepass/oblivion/SplashScreenActivity.java @@ -8,30 +8,60 @@ import androidx.appcompat.app.AppCompatActivity; +import org.bepass.oblivion.base.BaseActivity; +import org.bepass.oblivion.databinding.ActivitySplashScreenBinding; + +/** + * A simple splash screen activity that shows a splash screen for a short duration before navigating + * to the main activity. This class extends {@link BaseActivity} and uses data binding. + */ @SuppressLint("CustomSplashScreen") -public class SplashScreenActivity extends AppCompatActivity implements View.OnClickListener { +public class SplashScreenActivity extends BaseActivity { + + /** + * Returns the layout resource ID for the splash screen activity. + * + * @return The layout resource ID. + */ + @Override + protected int getLayoutResourceId() { + return R.layout.activity_splash_screen; + } + /** + * Called when the activity is first created. This method sets up the splash screen and + * schedules the transition to the main activity. + * + * @param savedInstanceState If the activity is being re-initialized after previously being shut down + * then this Bundle contains the data it most recently supplied in onSaveInstanceState(Bundle). + */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - // Initialize the LocaleHandler and set the locale - LocaleHandler localeHandler = new LocaleHandler(this); - setContentView(R.layout.activity_splash_screen); - final int SHORT_SPLASH_DISPLAY_LENGTH = 1000; // 1 second - findViewById(R.id.splashScreen).setOnClickListener(this); + binding.setHandler(new ClickHandler()); + // 1 second + int SHORT_SPLASH_DISPLAY_LENGTH = 1000; new Handler().postDelayed(() -> { - // Create an Intent that will start the Main Activity. - Intent mainIntent = new Intent(SplashScreenActivity.this, MainActivity.class); - SplashScreenActivity.this.startActivity(mainIntent); - SplashScreenActivity.this.finish(); + MainActivity.start(this); + finish(); }, SHORT_SPLASH_DISPLAY_LENGTH); } - @Override - public void onClick(View v) { - // If the user clicks on the splash screen, move to the MainActivity immediately - Intent mainIntent = new Intent(SplashScreenActivity.this, MainActivity.class); - SplashScreenActivity.this.startActivity(mainIntent); - SplashScreenActivity.this.finish(); + /** + * A click handler for handling user interactions with the splash screen. + */ + public class ClickHandler { + + /** + * Called when the root view is pressed. This method immediately navigates to the main activity + * and finishes the splash screen activity. + * + * @param view The view that was clicked. + */ + public void OnRootPressed(View view) { + MainActivity.start(SplashScreenActivity.this); + finish(); + } } + } diff --git a/app/src/main/res/layout/activity_splash_screen.xml b/app/src/main/res/layout/activity_splash_screen.xml index fe0ba5b6..5dd78c7e 100644 --- a/app/src/main/res/layout/activity_splash_screen.xml +++ b/app/src/main/res/layout/activity_splash_screen.xml @@ -1,169 +1,179 @@ - + xmlns:tools="http://schemas.android.com/tools"> + + + + + + android:layout_width="match_parent" + android:onClick="@{handler::OnRootPressed}" + android:layout_height="match_parent" + android:id="@+id/splashScreen" + android:background="@drawable/background_gradient"> - + app:layout_constraintTop_toTopOf="parent"> - + + + + + + + + + app:layout_constraintTop_toBottomOf="@+id/constraintLayout3"> - + + + + + + - + app:layout_constraintTop_toBottomOf="@+id/linearLayout2"> + - - - - - - - - - + - + - - + android:text="@string/slogan" + android:textAlignment="center" + android:textColor="#c4c4c4" + android:textSize="20sp" + android:textStyle="bold" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/linearLayout2" /> - - - + + From 3a422990fcd9666e8399d772ba6c1de4ed6d458c Mon Sep 17 00:00:00 2001 From: Benny Date: Mon, 10 Jun 2024 11:26:36 +0330 Subject: [PATCH 07/34] refactor: Move all activities into ui folder for better project structure - Created a new folder named 'ui' - Moved all activities into the 'ui' folder - Updated package declarations and imports as necessary --- app/src/main/AndroidManifest.xml | 12 ++++++------ .../java/org/bepass/oblivion/OblivionVpnService.java | 2 ++ .../oblivion/{ => base}/StateAwareBaseActivity.java | 5 ++++- .../org/bepass/oblivion/{ => ui}/InfoActivity.java | 4 +++- .../org/bepass/oblivion/{ => ui}/LogActivity.java | 4 +++- .../org/bepass/oblivion/{ => ui}/MainActivity.java | 11 ++++++++++- .../bepass/oblivion/{ => ui}/SettingsActivity.java | 12 +++++++++++- .../oblivion/{ => ui}/SplashScreenActivity.java | 6 ++---- .../oblivion/{ => ui}/SplitTunnelActivity.java | 11 +++++++++-- app/src/main/res/layout/activity_info.xml | 2 +- app/src/main/res/layout/activity_log.xml | 2 +- app/src/main/res/layout/activity_main.xml | 2 +- app/src/main/res/layout/activity_settings.xml | 2 +- app/src/main/res/layout/activity_splash_screen.xml | 2 +- app/src/main/res/layout/activity_split_tunnel.xml | 2 +- 15 files changed, 56 insertions(+), 23 deletions(-) rename app/src/main/java/org/bepass/oblivion/{ => base}/StateAwareBaseActivity.java (95%) rename app/src/main/java/org/bepass/oblivion/{ => ui}/InfoActivity.java (93%) rename app/src/main/java/org/bepass/oblivion/{ => ui}/LogActivity.java (97%) rename app/src/main/java/org/bepass/oblivion/{ => ui}/MainActivity.java (96%) rename app/src/main/java/org/bepass/oblivion/{ => ui}/SettingsActivity.java (95%) rename app/src/main/java/org/bepass/oblivion/{ => ui}/SplashScreenActivity.java (95%) rename app/src/main/java/org/bepass/oblivion/{ => ui}/SplitTunnelActivity.java (87%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 86d59ffb..de9544f5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -70,7 +70,7 @@ - @@ -79,7 +79,7 @@ @@ -89,20 +89,20 @@ diff --git a/app/src/main/java/org/bepass/oblivion/OblivionVpnService.java b/app/src/main/java/org/bepass/oblivion/OblivionVpnService.java index 14809caa..4ddbf400 100644 --- a/app/src/main/java/org/bepass/oblivion/OblivionVpnService.java +++ b/app/src/main/java/org/bepass/oblivion/OblivionVpnService.java @@ -26,6 +26,8 @@ import androidx.core.app.NotificationManagerCompat; import androidx.core.content.ContextCompat; +import org.bepass.oblivion.ui.MainActivity; + import java.io.FileOutputStream; import java.io.IOException; import java.lang.ref.WeakReference; diff --git a/app/src/main/java/org/bepass/oblivion/StateAwareBaseActivity.java b/app/src/main/java/org/bepass/oblivion/base/StateAwareBaseActivity.java similarity index 95% rename from app/src/main/java/org/bepass/oblivion/StateAwareBaseActivity.java rename to app/src/main/java/org/bepass/oblivion/base/StateAwareBaseActivity.java index ebe94bbe..5548cd80 100644 --- a/app/src/main/java/org/bepass/oblivion/StateAwareBaseActivity.java +++ b/app/src/main/java/org/bepass/oblivion/base/StateAwareBaseActivity.java @@ -1,4 +1,4 @@ -package org.bepass.oblivion; +package org.bepass.oblivion.base; import android.content.ComponentName; import android.content.Context; @@ -9,6 +9,9 @@ import androidx.appcompat.app.AppCompatActivity; +import org.bepass.oblivion.ConnectionState; +import org.bepass.oblivion.OblivionVpnService; + /** * Those activities that inherit this class observe connection state by default and have access to lastKnownConnectionState variable. diff --git a/app/src/main/java/org/bepass/oblivion/InfoActivity.java b/app/src/main/java/org/bepass/oblivion/ui/InfoActivity.java similarity index 93% rename from app/src/main/java/org/bepass/oblivion/InfoActivity.java rename to app/src/main/java/org/bepass/oblivion/ui/InfoActivity.java index 3767966d..02017cdf 100644 --- a/app/src/main/java/org/bepass/oblivion/InfoActivity.java +++ b/app/src/main/java/org/bepass/oblivion/ui/InfoActivity.java @@ -1,4 +1,4 @@ -package org.bepass.oblivion; +package org.bepass.oblivion.ui; import android.content.Intent; import android.net.Uri; @@ -8,6 +8,8 @@ import androidx.appcompat.app.AppCompatActivity; +import org.bepass.oblivion.R; + public class InfoActivity extends AppCompatActivity { ImageView back; diff --git a/app/src/main/java/org/bepass/oblivion/LogActivity.java b/app/src/main/java/org/bepass/oblivion/ui/LogActivity.java similarity index 97% rename from app/src/main/java/org/bepass/oblivion/LogActivity.java rename to app/src/main/java/org/bepass/oblivion/ui/LogActivity.java index b1c6aae3..f0d3a74d 100644 --- a/app/src/main/java/org/bepass/oblivion/LogActivity.java +++ b/app/src/main/java/org/bepass/oblivion/ui/LogActivity.java @@ -1,4 +1,4 @@ -package org.bepass.oblivion; +package org.bepass.oblivion.ui; import android.os.Bundle; import android.os.Handler; @@ -9,6 +9,8 @@ import androidx.appcompat.app.AppCompatActivity; +import org.bepass.oblivion.R; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; diff --git a/app/src/main/java/org/bepass/oblivion/MainActivity.java b/app/src/main/java/org/bepass/oblivion/ui/MainActivity.java similarity index 96% rename from app/src/main/java/org/bepass/oblivion/MainActivity.java rename to app/src/main/java/org/bepass/oblivion/ui/MainActivity.java index c6fc9a6d..27591b98 100644 --- a/app/src/main/java/org/bepass/oblivion/MainActivity.java +++ b/app/src/main/java/org/bepass/oblivion/ui/MainActivity.java @@ -1,4 +1,4 @@ -package org.bepass.oblivion; +package org.bepass.oblivion.ui; import static org.bepass.oblivion.OblivionVpnService.startVpnService; import static org.bepass.oblivion.OblivionVpnService.stopVpnService; @@ -28,6 +28,15 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton; +import org.bepass.oblivion.ConnectionState; +import org.bepass.oblivion.FileManager; +import org.bepass.oblivion.LocaleHandler; +import org.bepass.oblivion.OblivionVpnService; +import org.bepass.oblivion.PublicIPUtils; +import org.bepass.oblivion.R; +import org.bepass.oblivion.base.StateAwareBaseActivity; +import org.bepass.oblivion.TouchAwareSwitch; + import java.util.HashSet; import java.util.Set; diff --git a/app/src/main/java/org/bepass/oblivion/SettingsActivity.java b/app/src/main/java/org/bepass/oblivion/ui/SettingsActivity.java similarity index 95% rename from app/src/main/java/org/bepass/oblivion/SettingsActivity.java rename to app/src/main/java/org/bepass/oblivion/ui/SettingsActivity.java index 38de084e..f16377c7 100644 --- a/app/src/main/java/org/bepass/oblivion/SettingsActivity.java +++ b/app/src/main/java/org/bepass/oblivion/ui/SettingsActivity.java @@ -1,4 +1,4 @@ -package org.bepass.oblivion; +package org.bepass.oblivion.ui; import static org.bepass.oblivion.BatteryOptimizationKt.isBatteryOptimizationEnabled; import static org.bepass.oblivion.BatteryOptimizationKt.showBatteryOptimizationDialog; @@ -18,6 +18,16 @@ import androidx.activity.OnBackPressedCallback; +import org.bepass.oblivion.ConnectionState; +import org.bepass.oblivion.CountryUtils; +import org.bepass.oblivion.EditSheet; +import org.bepass.oblivion.FileManager; +import org.bepass.oblivion.LocaleHelper; +import org.bepass.oblivion.OblivionVpnService; +import org.bepass.oblivion.R; +import org.bepass.oblivion.SheetsCallBack; +import org.bepass.oblivion.base.StateAwareBaseActivity; + public class SettingsActivity extends StateAwareBaseActivity { private FileManager fileManager; private LinearLayout countryLayout; diff --git a/app/src/main/java/org/bepass/oblivion/SplashScreenActivity.java b/app/src/main/java/org/bepass/oblivion/ui/SplashScreenActivity.java similarity index 95% rename from app/src/main/java/org/bepass/oblivion/SplashScreenActivity.java rename to app/src/main/java/org/bepass/oblivion/ui/SplashScreenActivity.java index 625d76b6..a51efd68 100644 --- a/app/src/main/java/org/bepass/oblivion/SplashScreenActivity.java +++ b/app/src/main/java/org/bepass/oblivion/ui/SplashScreenActivity.java @@ -1,13 +1,11 @@ -package org.bepass.oblivion; +package org.bepass.oblivion.ui; import android.annotation.SuppressLint; -import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.view.View; -import androidx.appcompat.app.AppCompatActivity; - +import org.bepass.oblivion.R; import org.bepass.oblivion.base.BaseActivity; import org.bepass.oblivion.databinding.ActivitySplashScreenBinding; diff --git a/app/src/main/java/org/bepass/oblivion/SplitTunnelActivity.java b/app/src/main/java/org/bepass/oblivion/ui/SplitTunnelActivity.java similarity index 87% rename from app/src/main/java/org/bepass/oblivion/SplitTunnelActivity.java rename to app/src/main/java/org/bepass/oblivion/ui/SplitTunnelActivity.java index add17f9a..726028f7 100644 --- a/app/src/main/java/org/bepass/oblivion/SplitTunnelActivity.java +++ b/app/src/main/java/org/bepass/oblivion/ui/SplitTunnelActivity.java @@ -1,16 +1,23 @@ -package org.bepass.oblivion; +package org.bepass.oblivion.ui; import android.os.Bundle; import android.view.View; import android.widget.ImageView; -import androidx.activity.OnBackPressedCallback; import androidx.annotation.NonNull; import androidx.recyclerview.widget.ConcatAdapter; import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.progressindicator.CircularProgressIndicator; +import org.bepass.oblivion.BypassListAppsAdapter; +import org.bepass.oblivion.ConnectionState; +import org.bepass.oblivion.FileManager; +import org.bepass.oblivion.R; +import org.bepass.oblivion.SplitTunnelMode; +import org.bepass.oblivion.SplitTunnelOptionsAdapter; +import org.bepass.oblivion.base.StateAwareBaseActivity; + public class SplitTunnelActivity extends StateAwareBaseActivity { private RecyclerView appsRecycler; diff --git a/app/src/main/res/layout/activity_info.xml b/app/src/main/res/layout/activity_info.xml index 3368e06b..92b6584f 100644 --- a/app/src/main/res/layout/activity_info.xml +++ b/app/src/main/res/layout/activity_info.xml @@ -6,7 +6,7 @@ android:layout_height="match_parent" android:background="@drawable/background_gradient" android:orientation="vertical" - tools:context="org.bepass.oblivion.InfoActivity"> + tools:context="org.bepass.oblivion.ui.InfoActivity"> + tools:context="org.bepass.oblivion.ui.LogActivity"> + tools:context="org.bepass.oblivion.ui.MainActivity"> + tools:context="org.bepass.oblivion.ui.SettingsActivity"> + type="org.bepass.oblivion.ui.SplashScreenActivity.ClickHandler" /> + tools:context=".ui.SplitTunnelActivity"> Date: Mon, 10 Jun 2024 11:30:09 +0330 Subject: [PATCH 08/34] make abstract method from StateAwareBaseActivity protected --- .../org/bepass/oblivion/base/StateAwareBaseActivity.java | 4 ++-- app/src/main/java/org/bepass/oblivion/ui/MainActivity.java | 6 +++--- .../main/java/org/bepass/oblivion/ui/SettingsActivity.java | 4 ++-- .../java/org/bepass/oblivion/ui/SplitTunnelActivity.java | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/bepass/oblivion/base/StateAwareBaseActivity.java b/app/src/main/java/org/bepass/oblivion/base/StateAwareBaseActivity.java index 5548cd80..ddc1ba94 100644 --- a/app/src/main/java/org/bepass/oblivion/base/StateAwareBaseActivity.java +++ b/app/src/main/java/org/bepass/oblivion/base/StateAwareBaseActivity.java @@ -45,9 +45,9 @@ public void onServiceDisconnected(ComponentName arg0) { } }; - abstract String getKey(); + protected abstract String getKey(); - abstract void onConnectionStateChange(ConnectionState state); + protected abstract void onConnectionStateChange(ConnectionState state); private void observeConnectionStatus() { if (!isBound) return; diff --git a/app/src/main/java/org/bepass/oblivion/ui/MainActivity.java b/app/src/main/java/org/bepass/oblivion/ui/MainActivity.java index 27591b98..4f823298 100644 --- a/app/src/main/java/org/bepass/oblivion/ui/MainActivity.java +++ b/app/src/main/java/org/bepass/oblivion/ui/MainActivity.java @@ -34,8 +34,8 @@ import org.bepass.oblivion.OblivionVpnService; import org.bepass.oblivion.PublicIPUtils; import org.bepass.oblivion.R; -import org.bepass.oblivion.base.StateAwareBaseActivity; import org.bepass.oblivion.TouchAwareSwitch; +import org.bepass.oblivion.base.StateAwareBaseActivity; import java.util.HashSet; import java.util.Set; @@ -221,12 +221,12 @@ protected void cleanOrMigrateSettings() { @NonNull @Override - String getKey() { + public String getKey() { return "mainActivity"; } @Override - void onConnectionStateChange(ConnectionState state) { + public void onConnectionStateChange(ConnectionState state) { switch (state) { case DISCONNECTED: publicIP.setVisibility(View.GONE); diff --git a/app/src/main/java/org/bepass/oblivion/ui/SettingsActivity.java b/app/src/main/java/org/bepass/oblivion/ui/SettingsActivity.java index f16377c7..76cff87e 100644 --- a/app/src/main/java/org/bepass/oblivion/ui/SettingsActivity.java +++ b/app/src/main/java/org/bepass/oblivion/ui/SettingsActivity.java @@ -195,10 +195,10 @@ private void settingBasicValuesFromSPF() { } @Override - String getKey() { + public String getKey() { return "settingsActivity"; } @Override - void onConnectionStateChange(ConnectionState state) {} + public void onConnectionStateChange(ConnectionState state) {} } diff --git a/app/src/main/java/org/bepass/oblivion/ui/SplitTunnelActivity.java b/app/src/main/java/org/bepass/oblivion/ui/SplitTunnelActivity.java index 726028f7..f4c8f21c 100644 --- a/app/src/main/java/org/bepass/oblivion/ui/SplitTunnelActivity.java +++ b/app/src/main/java/org/bepass/oblivion/ui/SplitTunnelActivity.java @@ -68,9 +68,9 @@ public void shouldShowSystemApps(boolean show) { } @Override - void onConnectionStateChange(ConnectionState state) { } + public void onConnectionStateChange(ConnectionState state) { } @NonNull @Override - String getKey() { return "splitTunnelActivity"; } + public String getKey() { return "splitTunnelActivity"; } } From 5e1fbab8e8c9899b4a4b122073eb9c0f579c37af Mon Sep 17 00:00:00 2001 From: Benny Date: Mon, 10 Jun 2024 11:47:34 +0330 Subject: [PATCH 09/34] refactor: Optimize MainActivity SettingsActivity SplitTunnelActivity class in ui package -add ui binding - Refactored MainActivity class in the ui package to improve readability and maintainability - Introduced method extraction to reduce code duplication and improve modularity - Reorganized imports for better code organization - Added documentation for methods and important code blocks - Improved handling of internet connectivity checks and VPN service toggling - Enhanced error handling and logging for better debugging --- .../oblivion/base/StateAwareBaseActivity.java | 21 +- .../org/bepass/oblivion/ui/MainActivity.java | 181 ++-- .../bepass/oblivion/ui/SettingsActivity.java | 8 +- .../oblivion/ui/SplitTunnelActivity.java | 8 +- app/src/main/res/layout/activity_main.xml | 340 +++---- app/src/main/res/layout/activity_settings.xml | 946 +++++++++--------- .../main/res/layout/activity_split_tunnel.xml | 129 +-- 7 files changed, 846 insertions(+), 787 deletions(-) diff --git a/app/src/main/java/org/bepass/oblivion/base/StateAwareBaseActivity.java b/app/src/main/java/org/bepass/oblivion/base/StateAwareBaseActivity.java index ddc1ba94..027c79fb 100644 --- a/app/src/main/java/org/bepass/oblivion/base/StateAwareBaseActivity.java +++ b/app/src/main/java/org/bepass/oblivion/base/StateAwareBaseActivity.java @@ -4,10 +4,14 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.os.Bundle; import android.os.IBinder; import android.os.Messenger; +import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; +import androidx.databinding.DataBindingUtil; +import androidx.databinding.ViewDataBinding; import org.bepass.oblivion.ConnectionState; import org.bepass.oblivion.OblivionVpnService; @@ -16,12 +20,27 @@ /** * Those activities that inherit this class observe connection state by default and have access to lastKnownConnectionState variable. */ -public abstract class StateAwareBaseActivity extends AppCompatActivity { +public abstract class StateAwareBaseActivity extends AppCompatActivity { protected ConnectionState lastKnownConnectionState = ConnectionState.DISCONNECTED; private static boolean requireRestartVpnService = false; + protected B binding; private Messenger serviceMessenger; private boolean isBound; + protected abstract int getLayoutResourceId(); + + /** + * Called when the activity is starting. + * @param savedInstanceState If the activity is being re-initialized after previously being shut down then this Bundle contains the data it most recently supplied in onSaveInstanceState(Bundle). + * @see AppCompatActivity#onCreate(Bundle) + */ + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // Inflates the layout and initializes the binding object + binding = DataBindingUtil.setContentView(this, getLayoutResourceId()); + } + public static boolean getRequireRestartVpnService() { return requireRestartVpnService; } diff --git a/app/src/main/java/org/bepass/oblivion/ui/MainActivity.java b/app/src/main/java/org/bepass/oblivion/ui/MainActivity.java index 4f823298..3a5ba3c3 100644 --- a/app/src/main/java/org/bepass/oblivion/ui/MainActivity.java +++ b/app/src/main/java/org/bepass/oblivion/ui/MainActivity.java @@ -36,15 +36,12 @@ import org.bepass.oblivion.R; import org.bepass.oblivion.TouchAwareSwitch; import org.bepass.oblivion.base.StateAwareBaseActivity; +import org.bepass.oblivion.databinding.ActivityMainBinding; import java.util.HashSet; import java.util.Set; -public class MainActivity extends StateAwareBaseActivity { - private TouchAwareSwitch switchButton; - private TextView stateText, publicIP; - private ProgressBar ipProgressBar; - private PublicIPUtils pIPUtils; +public class MainActivity extends StateAwareBaseActivity { private long backPressedTime; private Toast backToast; private LocaleHandler localeHandler; @@ -56,51 +53,38 @@ public static void start(Context context) { context.startActivity(starter); } + @Override + protected int getLayoutResourceId() { + return R.layout.activity_main; + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); cleanOrMigrateSettings(); - - // Get the global PublicIPUtils instance - pIPUtils = PublicIPUtils.getInstance(); - + setupUI(); // Initialize the LocaleHandler and set the locale localeHandler = new LocaleHandler(this); - // Set the layout of the main activity - setContentView(R.layout.activity_main); - - // Handle language change based on floatingActionButton value - FloatingActionButton floatingActionButton = findViewById(R.id.floatingActionButton); - floatingActionButton.setOnClickListener(v -> localeHandler.showLanguageSelectionDialog(() -> - localeHandler.restartActivity(this))); - // Views - ImageView infoIcon = findViewById(R.id.info_icon); - ImageView logIcon = findViewById(R.id.bug_icon); - ImageView settingsIcon = findViewById(R.id.setting_icon); + setupVPNConnection(); + // Request permission to create push notifications + requestNotificationPermission(); - FrameLayout switchButtonFrame = findViewById(R.id.switch_button_frame); - switchButton = findViewById(R.id.switch_button); - stateText = findViewById(R.id.state_text); - publicIP = findViewById(R.id.publicIP); - ipProgressBar = findViewById(R.id.ipProgressBar); + // Set the behaviour of the back button + handleBackPress(); + } - // Set listeners - infoIcon.setOnClickListener(v -> startActivity(new Intent(MainActivity.this, InfoActivity.class))); - logIcon.setOnClickListener(v -> startActivity(new Intent(MainActivity.this, LogActivity.class))); - settingsIcon.setOnClickListener(v -> startActivity(new Intent(MainActivity.this, SettingsActivity.class))); - switchButtonFrame.setOnClickListener(v -> switchButton.toggle()); + private void setupVPNConnection() { + ActivityResultLauncher vpnPermissionLauncher = registerForActivityResult( + new ActivityResultContracts.StartActivityForResult(), result -> { + if (result.getResultCode() != RESULT_OK) { + Toast.makeText(this, "Really!?", Toast.LENGTH_LONG).show(); + } + binding.switchButton.setChecked(false); + }); - // Request for VPN creation - ActivityResultLauncher vpnPermissionLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { - if (result.getResultCode() != RESULT_OK) { - Toast.makeText(this, "Really!?", Toast.LENGTH_LONG).show(); - } - switchButton.setChecked(false); - }); - // Listener for toggle switch - switchButton.setOnCheckedChangeListener((view, isChecked) -> { + binding.switchButton.setOnCheckedChangeListener((view, isChecked) -> { if (!isChecked) { if (!lastKnownConnectionState.isDisconnected()) { stopVpnService(this); @@ -112,49 +96,27 @@ protected void onCreate(Bundle savedInstanceState) { vpnPermissionLauncher.launch(vpnIntent); return; } - // Handle the case when the VPN is in the connecting state and the user clicks to abort if (lastKnownConnectionState.isConnecting()) { stopVpnService(this); return; } - // Start the VPN service if it's disconnected if (lastKnownConnectionState.isDisconnected()) { startVpnService(this); } - // To check is Internet Connection is available - handler.postDelayed(new Runnable() { - @Override - public void run() { - if (!lastKnownConnectionState.isDisconnected()) { - checkInternetConnectionAndDisconnectVPN(); - handler.postDelayed(this, 3000); // Check every 3 seconds - } - } - }, 5000); // Start checking after 5 seconds + monitorInternetConnection(); }); + } - - // Request permission to create push notifications - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - ActivityResultLauncher pushNotificationPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> { - if (!isGranted) { - Toast.makeText(this, "Permission denied", Toast.LENGTH_LONG).show(); - } - }); - pushNotificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS); - } - - // Set the behaviour of the back button + private void handleBackPress() { getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) { @Override public void handleOnBackPressed() { - // Custom back pressed logic here if (backPressedTime + 2000 > System.currentTimeMillis()) { if (backToast != null) backToast.cancel(); - finish(); // or super.handleOnBackPressed() if you want to keep default behavior alongside + finish(); } else { if (backToast != null) - backToast.cancel(); // Cancel the existing toast to avoid stacking + backToast.cancel(); backToast = Toast.makeText(MainActivity.this, "برای خروج، دوباره بازگشت را فشار دهید.", Toast.LENGTH_SHORT); backToast.show(); } @@ -163,6 +125,39 @@ public void handleOnBackPressed() { }); } + private void requestNotificationPermission() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + ActivityResultLauncher pushNotificationPermissionLauncher = registerForActivityResult( + new ActivityResultContracts.RequestPermission(), isGranted -> { + if (!isGranted) { + Toast.makeText(this, "Permission denied", Toast.LENGTH_LONG).show(); + } + }); + pushNotificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS); + } + } + + private void setupUI() { + binding.floatingActionButton.setOnClickListener(v -> localeHandler.showLanguageSelectionDialog(() -> + localeHandler.restartActivity(this))); + binding.infoIcon.setOnClickListener(v -> startActivity(new Intent(MainActivity.this, InfoActivity.class))); + binding.bugIcon.setOnClickListener(v -> startActivity(new Intent(MainActivity.this, LogActivity.class))); + binding.settingIcon.setOnClickListener(v -> startActivity(new Intent(MainActivity.this, SettingsActivity.class))); + binding.switchButtonFrame.setOnClickListener(v -> binding.switchButton.toggle()); + } + + private void monitorInternetConnection() { + handler.postDelayed(new Runnable() { + @Override + public void run() { + if (!lastKnownConnectionState.isDisconnected()) { + checkInternetConnectionAndDisconnectVPN(); + handler.postDelayed(this, 3000); // Check every 3 seconds + } + } + }, 5000); // Start checking after 5 seconds + } + // Check internet connectivity private boolean isConnectedToInternet() { ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); @@ -229,32 +224,44 @@ public String getKey() { public void onConnectionStateChange(ConnectionState state) { switch (state) { case DISCONNECTED: - publicIP.setVisibility(View.GONE); - stateText.setText(R.string.notConnected); - ipProgressBar.setVisibility(View.GONE); - switchButton.setEnabled(true); - switchButton.setChecked(false, false); + updateUIForDisconnectedState(); break; case CONNECTING: - stateText.setText(R.string.connecting); - publicIP.setVisibility(View.GONE); - ipProgressBar.setVisibility(View.VISIBLE); - switchButton.setChecked(true, false); - switchButton.setEnabled(true); + updateUIForConnectingState(); break; case CONNECTED: - switchButton.setEnabled(true); - stateText.setText(R.string.connected); - switchButton.setChecked(true, false); - ipProgressBar.setVisibility(View.GONE); - pIPUtils.getIPDetails((details) -> { - if (details.ip != null) { - String ipString = details.ip + " " + details.flag; - publicIP.setText(ipString); - publicIP.setVisibility(View.VISIBLE); - } - }); + updateUIForConnectedState(); break; } } + + private void updateUIForDisconnectedState() { + binding.publicIP.setVisibility(View.GONE); + binding.stateText.setText(R.string.notConnected); + binding.ipProgressBar.setVisibility(View.GONE); + binding.switchButton.setEnabled(true); + binding.switchButton.setChecked(false, false); + } + + private void updateUIForConnectingState() { + binding.stateText.setText(R.string.connecting); + binding.publicIP.setVisibility(View.GONE); + binding.ipProgressBar.setVisibility(View.VISIBLE); + binding.switchButton.setChecked(true, false); + binding.switchButton.setEnabled(true); + } + + private void updateUIForConnectedState() { + binding.switchButton.setEnabled(true); + binding.stateText.setText(R.string.connected); + binding.switchButton.setChecked(true, false); + binding.ipProgressBar.setVisibility(View.GONE); + PublicIPUtils.getInstance().getIPDetails((details) -> { + if (details.ip != null) { + String ipString = details.ip + " " + details.flag; + binding.publicIP.setText(ipString); + binding.publicIP.setVisibility(View.VISIBLE); + } + }); + } } diff --git a/app/src/main/java/org/bepass/oblivion/ui/SettingsActivity.java b/app/src/main/java/org/bepass/oblivion/ui/SettingsActivity.java index 76cff87e..9fae2376 100644 --- a/app/src/main/java/org/bepass/oblivion/ui/SettingsActivity.java +++ b/app/src/main/java/org/bepass/oblivion/ui/SettingsActivity.java @@ -27,8 +27,9 @@ import org.bepass.oblivion.R; import org.bepass.oblivion.SheetsCallBack; import org.bepass.oblivion.base.StateAwareBaseActivity; +import org.bepass.oblivion.databinding.ActivitySettingsBinding; -public class SettingsActivity extends StateAwareBaseActivity { +public class SettingsActivity extends StateAwareBaseActivity { private FileManager fileManager; private LinearLayout countryLayout; private TextView endpoint, port, license; @@ -43,6 +44,11 @@ private void setCheckBoxWithoutTriggeringListener(CheckBox checkBox, boolean isC checkBox.setOnCheckedChangeListener(listener); // Reattach the listener } + @Override + protected int getLayoutResourceId() { + return R.layout.activity_settings; + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); diff --git a/app/src/main/java/org/bepass/oblivion/ui/SplitTunnelActivity.java b/app/src/main/java/org/bepass/oblivion/ui/SplitTunnelActivity.java index f4c8f21c..9a43f844 100644 --- a/app/src/main/java/org/bepass/oblivion/ui/SplitTunnelActivity.java +++ b/app/src/main/java/org/bepass/oblivion/ui/SplitTunnelActivity.java @@ -17,12 +17,18 @@ import org.bepass.oblivion.SplitTunnelMode; import org.bepass.oblivion.SplitTunnelOptionsAdapter; import org.bepass.oblivion.base.StateAwareBaseActivity; +import org.bepass.oblivion.databinding.ActivitySplitTunnelBinding; -public class SplitTunnelActivity extends StateAwareBaseActivity { +public class SplitTunnelActivity extends StateAwareBaseActivity { private RecyclerView appsRecycler; private CircularProgressIndicator progress; + @Override + protected int getLayoutResourceId() { + return R.layout.activity_split_tunnel; + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 26648820..27f82354 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,98 +1,103 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index c9d118b0..08609d43 100644 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -1,578 +1,586 @@ - - - - - - - - - - - - - + + + + + + + android:layout_height="match_parent" + android:background="@drawable/background_gradient" + tools:context="org.bepass.oblivion.ui.SettingsActivity"> + + + + + + + + + + + android:layout_height="80dp" + android:background="?android:selectableItemBackground" + android:gravity="center" + android:paddingHorizontal="16dp" + android:orientation="vertical"> + + + + + + android:text="@string/batteryOpLText" + android:textColor="#9A9A9A" + android:textSize="16sp" /> + - + android:layout_height="10dp" + android:gravity="center" + android:orientation="vertical"> + + + - - - - - + android:id="@+id/endpoint_layout" + android:layout_width="match_parent" + android:paddingHorizontal="16dp" + android:background="?selectableItemBackground" + android:layout_height="80dp" + android:gravity="center" + android:orientation="vertical"> - + + + + + + + - + android:text="@string/endpointTextDesc" + android:textColor="#9A9A9A" + android:textSize="16sp" /> - - - - - - - - + android:layout_height="10dp" + android:gravity="center" + android:orientation="vertical"> + + + + android:id="@+id/port_layout" + android:layout_width="match_parent" + android:layout_height="80dp" + android:gravity="center" + android:paddingHorizontal="16dp" + android:background="?selectableItemBackground" + android:orientation="vertical"> - + + + + + + + + android:text="@string/portTunTextDesc" + android:textColor="#9A9A9A" + android:textSize="16sp" /> - - - - - - - - - + android:layout_height="10dp" + android:gravity="center" + android:orientation="vertical"> + + + + android:layout_height="80dp" + android:background="?android:selectableItemBackground" + android:gravity="center" + android:paddingHorizontal="16dp" + android:orientation="vertical"> + + + + + - - - + android:text="@string/blackListTextDesc" + android:textColor="#9A9A9A" + android:textSize="16sp" /> - + - - - - - + android:layout_height="10dp" + android:gravity="center" + android:orientation="vertical"> + + + - + android:id="@+id/lan_layout" + android:layout_width="match_parent" + android:layout_height="80dp" + android:gravity="center" + android:paddingHorizontal="16dp" + android:background="?selectableItemBackground" + android:orientation="vertical"> - - + android:gravity="end"> + + + + + + + + + android:text="@string/connectFromLanTextDesc" + android:textColor="#9A9A9A" + android:textSize="16sp" /> + - - - - - - - - - + android:layout_height="10dp" + android:gravity="center" + android:orientation="vertical"> + + + + android:id="@+id/psiphon_layout" + android:layout_width="match_parent" + android:layout_height="80dp" + android:gravity="center" + android:paddingHorizontal="16dp" + android:background="?selectableItemBackground" + android:orientation="vertical"> - + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="end"> + + + + + + + + + android:text="@string/psiphonTextDesc" + android:textColor="#9A9A9A" + android:textSize="16sp" /> - - - - - - - - - + android:layout_height="10dp" + android:gravity="center" + android:orientation="vertical"> + + + + android:id="@+id/country_layout" + android:layout_width="match_parent" + android:layout_height="80dp" + android:gravity="center" + android:paddingHorizontal="16dp" + android:background="?selectableItemBackground" + android:orientation="vertical"> - + + + + + + + + android:text="@string/chooseTextDesc" + android:textColor="#9A9A9A" + android:textSize="16sp" /> - - - - - - - - - + android:layout_height="10dp" + android:gravity="center" + android:orientation="vertical"> + + + + android:id="@+id/license_layout" + android:layout_width="match_parent" + android:layout_height="80dp" + android:gravity="center" + android:paddingHorizontal="16dp" + android:background="?selectableItemBackground" + android:orientation="vertical"> - + + + + + + + + android:text="@string/licenseTextDesc" + android:textColor="#9A9A9A" + android:textSize="16sp" /> - - - - - - - - - + android:layout_height="10dp" + android:gravity="center" + android:orientation="vertical"> + + + + android:id="@+id/gool_layout" + android:layout_width="match_parent" + android:layout_height="80dp" + android:gravity="center" + android:paddingHorizontal="16dp" + android:background="?selectableItemBackground" + android:orientation="vertical"> - + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + + + + + + + + android:text="@string/goolTextDesc" + android:textColor="#9A9A9A" + android:textSize="16sp" /> - - - - - + - + + diff --git a/app/src/main/res/layout/activity_split_tunnel.xml b/app/src/main/res/layout/activity_split_tunnel.xml index 11ccf9cb..d26ade66 100644 --- a/app/src/main/res/layout/activity_split_tunnel.xml +++ b/app/src/main/res/layout/activity_split_tunnel.xml @@ -1,73 +1,80 @@ - + xmlns:tools="http://schemas.android.com/tools"> + - + + + android:layout_height="match_parent" + android:background="@drawable/background_gradient" + android:orientation="vertical" + tools:context=".ui.SplitTunnelActivity"> - - + + + - + - + - + + + app:indicatorColor="#E4AB53" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> - \ No newline at end of file + + \ No newline at end of file From 3331ef1a9adf59718308376aa02011e7a5628472 Mon Sep 17 00:00:00 2001 From: Benny Date: Mon, 10 Jun 2024 11:51:15 +0330 Subject: [PATCH 10/34] remove findviewbyids --- .../bepass/oblivion/ui/SettingsActivity.java | 106 +++++++----------- 1 file changed, 41 insertions(+), 65 deletions(-) diff --git a/app/src/main/java/org/bepass/oblivion/ui/SettingsActivity.java b/app/src/main/java/org/bepass/oblivion/ui/SettingsActivity.java index 9fae2376..165a09ad 100644 --- a/app/src/main/java/org/bepass/oblivion/ui/SettingsActivity.java +++ b/app/src/main/java/org/bepass/oblivion/ui/SettingsActivity.java @@ -26,18 +26,14 @@ import org.bepass.oblivion.OblivionVpnService; import org.bepass.oblivion.R; import org.bepass.oblivion.SheetsCallBack; +import org.bepass.oblivion.base.ApplicationLoader; import org.bepass.oblivion.base.StateAwareBaseActivity; import org.bepass.oblivion.databinding.ActivitySettingsBinding; public class SettingsActivity extends StateAwareBaseActivity { private FileManager fileManager; - private LinearLayout countryLayout; - private TextView endpoint, port, license; - private CheckBox psiphon, lan, gool; - private Spinner country; private CheckBox.OnCheckedChangeListener psiphonListener; private CheckBox.OnCheckedChangeListener goolListener; - private Context context; private void setCheckBoxWithoutTriggeringListener(CheckBox checkBox, boolean isChecked, CheckBox.OnCheckedChangeListener listener) { checkBox.setOnCheckedChangeListener(null); // Temporarily detach the listener checkBox.setChecked(isChecked); // Set the checked state @@ -53,39 +49,19 @@ protected int getLayoutResourceId() { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_settings); - context = this; fileManager = FileManager.getInstance(this); - LinearLayout batteryOptLayout = findViewById(R.id.battery_optimization_layout); - LinearLayout batteryOptLine = findViewById(R.id.battery_opt_line); if(isBatteryOptimizationEnabled(this)){ - batteryOptLayout.setOnClickListener(view -> { + binding.batteryOptimizationLayout.setOnClickListener(view -> { showBatteryOptimizationDialog(this); }); }else{ - batteryOptLayout.setVisibility(View.GONE); - batteryOptLine.setVisibility(View.GONE); + binding.batteryOptimizationLayout.setVisibility(View.GONE); + binding.batteryOptLine.setVisibility(View.GONE); } - LinearLayout endpointLayout = findViewById(R.id.endpoint_layout); - LinearLayout portLayout = findViewById(R.id.port_layout); - LinearLayout splitTunnelLayout = findViewById(R.id.split_tunnel_layout); - LinearLayout lanLayout = findViewById(R.id.lan_layout); - LinearLayout psiphonLayout = findViewById(R.id.psiphon_layout); - countryLayout = findViewById(R.id.country_layout); - LinearLayout licenseLayout = findViewById(R.id.license_layout); - LinearLayout goolLayout = findViewById(R.id.gool_layout); - - endpoint = findViewById(R.id.endpoint); - port = findViewById(R.id.port); - country = findViewById(R.id.country); - license = findViewById(R.id.license); - - psiphon = findViewById(R.id.psiphon); - lan = findViewById(R.id.lan); - gool = findViewById(R.id.gool); - - ImageView back = findViewById(R.id.back); - back.setOnClickListener(v -> getOnBackPressedDispatcher().onBackPressed()); + + + binding.back.setOnClickListener(v -> getOnBackPressedDispatcher().onBackPressed()); getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) { @Override @@ -103,19 +79,19 @@ public void handleOnBackPressed() { SheetsCallBack sheetsCallBack = this::settingBasicValuesFromSPF; // Listen to Changes - endpointLayout.setOnClickListener(v -> (new EditSheet(this, getString(R.string.endpointText), "endpoint", sheetsCallBack)).start()); - portLayout.setOnClickListener(v -> (new EditSheet(this, getString(R.string.portTunText), "port", sheetsCallBack)).start()); - licenseLayout.setOnClickListener(v -> (new EditSheet(this, getString(R.string.licenseText), "license", sheetsCallBack)).start()); + binding.endpointLayout.setOnClickListener(v -> (new EditSheet(this, getString(R.string.endpointText), "endpoint", sheetsCallBack)).start()); + binding.portLayout.setOnClickListener(v -> (new EditSheet(this, getString(R.string.portTunText), "port", sheetsCallBack)).start()); + binding.licenseLayout.setOnClickListener(v -> (new EditSheet(this, getString(R.string.licenseText), "license", sheetsCallBack)).start()); ArrayAdapter adapter = ArrayAdapter.createFromResource(this, R.array.countries, R.layout.country_item_layout); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - country.setAdapter(adapter); + binding.country.setAdapter(adapter); - country.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + binding.country.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { String name = parent.getItemAtPosition(position).toString(); - Pair codeAndName = CountryUtils.getCountryCode(context, name); + Pair codeAndName = CountryUtils.getCountryCode(ApplicationLoader.getAppCtx(), name); fileManager.set("USERSETTING_country", codeAndName.first); } @@ -123,40 +99,40 @@ public void onItemSelected(AdapterView parent, View view, int position, long id) public void onNothingSelected(AdapterView parent) {} }); - splitTunnelLayout.setOnClickListener(v -> startActivity(new Intent(this, SplitTunnelActivity.class))); + binding.splitTunnelLayout.setOnClickListener(v -> startActivity(new Intent(this, SplitTunnelActivity.class))); // Set Current Values settingBasicValuesFromSPF(); - goolLayout.setOnClickListener(v -> gool.setChecked(!gool.isChecked())); - lanLayout.setOnClickListener(v -> lan.setChecked(!lan.isChecked())); - psiphonLayout.setOnClickListener(v -> psiphon.setChecked(!psiphon.isChecked())); + binding.goolLayout.setOnClickListener(v -> binding.gool.setChecked(!binding.gool.isChecked())); + binding.lanLayout.setOnClickListener(v -> binding.lan.setChecked(!binding.lan.isChecked())); + binding.psiphonLayout.setOnClickListener(v -> binding.psiphon.setChecked(!binding.psiphon.isChecked())); - lan.setOnCheckedChangeListener((buttonView, isChecked) -> fileManager.set("USERSETTING_lan", isChecked)); + binding.lan.setOnCheckedChangeListener((buttonView, isChecked) -> fileManager.set("USERSETTING_lan", isChecked)); // Initialize the listeners psiphonListener = (buttonView, isChecked) -> { fileManager.set("USERSETTING_psiphon", isChecked); - if (isChecked && gool.isChecked()) { - setCheckBoxWithoutTriggeringListener(gool, false, goolListener); + if (isChecked && binding.gool.isChecked()) { + setCheckBoxWithoutTriggeringListener(binding.gool, false, goolListener); fileManager.set("USERSETTING_gool", false); } - countryLayout.setAlpha(isChecked ? 1f : 0.2f); - country.setEnabled(isChecked); + binding.countryLayout.setAlpha(isChecked ? 1f : 0.2f); + binding.country.setEnabled(isChecked); }; goolListener = (buttonView, isChecked) -> { fileManager.set("USERSETTING_gool", isChecked); - if (isChecked && psiphon.isChecked()) { - setCheckBoxWithoutTriggeringListener(psiphon, false, psiphonListener); + if (isChecked && binding.psiphon.isChecked()) { + setCheckBoxWithoutTriggeringListener(binding.psiphon, false, psiphonListener); fileManager.set("USERSETTING_psiphon", false); - countryLayout.setAlpha(0.2f); - country.setEnabled(false); + binding.countryLayout.setAlpha(0.2f); + binding.country.setEnabled(false); } }; // Set the listeners to the checkboxes - psiphon.setOnCheckedChangeListener(psiphonListener); - gool.setOnCheckedChangeListener(goolListener); + binding.psiphon.setOnCheckedChangeListener(psiphonListener); + binding.gool.setOnCheckedChangeListener(goolListener); } private int getIndexFromName(Spinner spinner, String name) { @@ -172,31 +148,31 @@ private int getIndexFromName(Spinner spinner, String name) { } private void settingBasicValuesFromSPF() { - endpoint.setText(fileManager.getString("USERSETTING_endpoint")); - port.setText(fileManager.getString("USERSETTING_port")); - license.setText(fileManager.getString("USERSETTING_license")); + binding.endpoint.setText(fileManager.getString("USERSETTING_endpoint")); + binding.port.setText(fileManager.getString("USERSETTING_port")); + binding.license.setText(fileManager.getString("USERSETTING_license")); String countryCode = fileManager.getString("USERSETTING_country"); int index = 0; if (!countryCode.isEmpty()) { LocaleHelper.goEn(this); String countryName = CountryUtils.getCountryName(countryCode); - index = getIndexFromName(country, countryName); + index = getIndexFromName(binding.country, countryName); LocaleHelper.restoreLocale(this); } - country.setSelection(index); + binding.country.setSelection(index); - psiphon.setChecked(fileManager.getBoolean("USERSETTING_psiphon")); - lan.setChecked(fileManager.getBoolean("USERSETTING_lan")); - gool.setChecked(fileManager.getBoolean("USERSETTING_gool")); + binding.psiphon.setChecked(fileManager.getBoolean("USERSETTING_psiphon")); + binding.lan.setChecked(fileManager.getBoolean("USERSETTING_lan")); + binding.gool.setChecked(fileManager.getBoolean("USERSETTING_gool")); - if (!psiphon.isChecked()) { - countryLayout.setAlpha(0.2f); - country.setEnabled(false); + if (!binding.psiphon.isChecked()) { + binding.countryLayout.setAlpha(0.2f); + binding.country.setEnabled(false); } else { - countryLayout.setAlpha(1f); - country.setEnabled(true); + binding.countryLayout.setAlpha(1f); + binding.country.setEnabled(true); } } From 89651c67f2ced3c976e4cbf11bc3ddc60584648a Mon Sep 17 00:00:00 2001 From: Benny Date: Mon, 10 Jun 2024 11:51:37 +0330 Subject: [PATCH 11/34] remove setcontentView --- app/src/main/java/org/bepass/oblivion/ui/SettingsActivity.java | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/org/bepass/oblivion/ui/SettingsActivity.java b/app/src/main/java/org/bepass/oblivion/ui/SettingsActivity.java index 165a09ad..149140f0 100644 --- a/app/src/main/java/org/bepass/oblivion/ui/SettingsActivity.java +++ b/app/src/main/java/org/bepass/oblivion/ui/SettingsActivity.java @@ -48,7 +48,6 @@ protected int getLayoutResourceId() { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_settings); fileManager = FileManager.getInstance(this); if(isBatteryOptimizationEnabled(this)){ From 6d4c3ba2e67aace1a72620aada1d3964841e18e3 Mon Sep 17 00:00:00 2001 From: Benny Date: Mon, 10 Jun 2024 11:53:03 +0330 Subject: [PATCH 12/34] add binding and remove findviewbyIds --- .../oblivion/ui/SplitTunnelActivity.java | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/org/bepass/oblivion/ui/SplitTunnelActivity.java b/app/src/main/java/org/bepass/oblivion/ui/SplitTunnelActivity.java index 9a43f844..00c0986d 100644 --- a/app/src/main/java/org/bepass/oblivion/ui/SplitTunnelActivity.java +++ b/app/src/main/java/org/bepass/oblivion/ui/SplitTunnelActivity.java @@ -21,8 +21,6 @@ public class SplitTunnelActivity extends StateAwareBaseActivity { - private RecyclerView appsRecycler; - private CircularProgressIndicator progress; @Override protected int getLayoutResourceId() { @@ -32,23 +30,14 @@ protected int getLayoutResourceId() { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - - // Sets the contents from the layout - setContentView(R.layout.activity_split_tunnel); - - // Find UI elements and assign them to vars - ImageView back = findViewById(R.id.back); - appsRecycler = findViewById(R.id.appsRecycler); - progress = findViewById(R.id.progress); - // Handles the back button behaviour - back.setOnClickListener(v -> getOnBackPressedDispatcher().onBackPressed()); + binding.back.setOnClickListener(v -> getOnBackPressedDispatcher().onBackPressed()); // Set up the app list BypassListAppsAdapter bypassListAppsAdapter = new BypassListAppsAdapter(this, loading -> { - appsRecycler.setVisibility(loading ? View.INVISIBLE : View.VISIBLE); - if (loading) progress.show(); - else progress.hide(); + binding.appsRecycler.setVisibility(loading ? View.INVISIBLE : View.VISIBLE); + if (loading) binding.progress.show(); + else binding.progress.hide(); }); // Signal the need to restart the VPN service on app selection change @@ -70,7 +59,7 @@ public void shouldShowSystemApps(boolean show) { } }); - appsRecycler.setAdapter(new ConcatAdapter(optionsAdapter, bypassListAppsAdapter)); + binding.appsRecycler.setAdapter(new ConcatAdapter(optionsAdapter, bypassListAppsAdapter)); } @Override From f77e37ca343d383baa21010c5a43a4cd65a5d326 Mon Sep 17 00:00:00 2001 From: Benny Date: Mon, 10 Jun 2024 11:54:48 +0330 Subject: [PATCH 13/34] add data binding --- .../org/bepass/oblivion/ui/InfoActivity.java | 18 +- app/src/main/res/layout/activity_info.xml | 201 +++++++++--------- 2 files changed, 113 insertions(+), 106 deletions(-) diff --git a/app/src/main/java/org/bepass/oblivion/ui/InfoActivity.java b/app/src/main/java/org/bepass/oblivion/ui/InfoActivity.java index 02017cdf..9f9f9d3e 100644 --- a/app/src/main/java/org/bepass/oblivion/ui/InfoActivity.java +++ b/app/src/main/java/org/bepass/oblivion/ui/InfoActivity.java @@ -9,26 +9,26 @@ import androidx.appcompat.app.AppCompatActivity; import org.bepass.oblivion.R; +import org.bepass.oblivion.base.BaseActivity; +import org.bepass.oblivion.databinding.ActivityInfoBinding; -public class InfoActivity extends AppCompatActivity { +public class InfoActivity extends BaseActivity { - ImageView back; - RelativeLayout github; + @Override + protected int getLayoutResourceId() { + return R.layout.activity_info; + } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_info); - - back = findViewById(R.id.back); - github = findViewById(R.id.github_layout); - github.setOnClickListener(v -> { + binding.githubLayout.setOnClickListener(v -> { Uri uri = Uri.parse("https://github.com/bepass-org/oblivion"); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent); }); - back.setOnClickListener(v -> getOnBackPressedDispatcher().onBackPressed()); + binding.back.setOnClickListener(v -> getOnBackPressedDispatcher().onBackPressed()); } } diff --git a/app/src/main/res/layout/activity_info.xml b/app/src/main/res/layout/activity_info.xml index 92b6584f..d59dff32 100644 --- a/app/src/main/res/layout/activity_info.xml +++ b/app/src/main/res/layout/activity_info.xml @@ -1,111 +1,118 @@ - - - - - + - - - - - - - - - + + + + + + + + + + + + + + android:textSize="16sp" + tools:ignore="RtlCompat" /> + + + + + + + + + + - - - - - + + From f7e66b3184adc1bd6ed83c24862e44c74851c3b2 Mon Sep 17 00:00:00 2001 From: Benny Date: Mon, 10 Jun 2024 11:56:10 +0330 Subject: [PATCH 14/34] add data binding and reformat --- .../org/bepass/oblivion/ui/LogActivity.java | 30 +++-- app/src/main/res/layout/activity_log.xml | 120 +++++++++--------- 2 files changed, 79 insertions(+), 71 deletions(-) diff --git a/app/src/main/java/org/bepass/oblivion/ui/LogActivity.java b/app/src/main/java/org/bepass/oblivion/ui/LogActivity.java index f0d3a74d..72ca1da9 100644 --- a/app/src/main/java/org/bepass/oblivion/ui/LogActivity.java +++ b/app/src/main/java/org/bepass/oblivion/ui/LogActivity.java @@ -3,6 +3,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; +import android.util.Log; import android.widget.ImageView; import android.widget.ScrollView; import android.widget.TextView; @@ -10,30 +11,31 @@ import androidx.appcompat.app.AppCompatActivity; import org.bepass.oblivion.R; +import org.bepass.oblivion.base.BaseActivity; +import org.bepass.oblivion.databinding.ActivityLogBinding; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; -public class LogActivity extends AppCompatActivity { +public class LogActivity extends BaseActivity { private final Handler handler = new Handler(Looper.getMainLooper()); - private TextView logs; - private ScrollView logScrollView; private boolean isUserScrollingUp = false; private Runnable logUpdater; + @Override + protected int getLayoutResourceId() { + return R.layout.activity_log; + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_log); - ImageView back = findViewById(R.id.back); - logs = findViewById(R.id.logs); - logScrollView = findViewById(R.id.logScrollView); setupScrollListener(); - back.setOnClickListener(v -> getOnBackPressedDispatcher().onBackPressed()); + binding.back.setOnClickListener(v -> getOnBackPressedDispatcher().onBackPressed()); logUpdater = new Runnable() { @Override public void run() { @@ -44,9 +46,9 @@ public void run() { } private void setupScrollListener() { - logScrollView.getViewTreeObserver().addOnScrollChangedListener(() -> { - int scrollY = logScrollView.getScrollY(); - int maxScrollY = logs.getHeight() - logScrollView.getHeight(); + binding.logScrollView.getViewTreeObserver().addOnScrollChangedListener(() -> { + int scrollY = binding.logScrollView.getScrollY(); + int maxScrollY = binding.logs.getHeight() - binding.logScrollView.getHeight(); isUserScrollingUp = scrollY < maxScrollY; }); } @@ -74,13 +76,13 @@ private void readLogsFromFile() { String finalLog = sb.toString(); runOnUiThread(() -> { - logs.setText(finalLog); + binding.logs.setText(finalLog); if (!isUserScrollingUp) { - logScrollView.post(() -> logScrollView.fullScroll(ScrollView.FOCUS_DOWN)); + binding.logScrollView.post(() -> binding.logScrollView.fullScroll(ScrollView.FOCUS_DOWN)); } }); } catch (IOException e) { - e.printStackTrace(); + Log.e(TAG, "readLogsFromFile: ",e ); } } } diff --git a/app/src/main/res/layout/activity_log.xml b/app/src/main/res/layout/activity_log.xml index 7c9d854b..8907e216 100644 --- a/app/src/main/res/layout/activity_log.xml +++ b/app/src/main/res/layout/activity_log.xml @@ -1,66 +1,72 @@ - + - + - + - + - + - + - - + + + + + + + + - + + From d73c22191ebde5b3c15caf75fadb14cc0b5f01e3 Mon Sep 17 00:00:00 2001 From: Benny Date: Mon, 10 Jun 2024 12:06:37 +0330 Subject: [PATCH 15/34] move TouchAwareSwitch into new folder "Component" --- .../bepass/oblivion/{ => component}/TouchAwareSwitch.java | 2 +- app/src/main/java/org/bepass/oblivion/ui/MainActivity.java | 7 ------- app/src/main/res/layout/activity_main.xml | 2 +- 3 files changed, 2 insertions(+), 9 deletions(-) rename app/src/main/java/org/bepass/oblivion/{ => component}/TouchAwareSwitch.java (96%) diff --git a/app/src/main/java/org/bepass/oblivion/TouchAwareSwitch.java b/app/src/main/java/org/bepass/oblivion/component/TouchAwareSwitch.java similarity index 96% rename from app/src/main/java/org/bepass/oblivion/TouchAwareSwitch.java rename to app/src/main/java/org/bepass/oblivion/component/TouchAwareSwitch.java index 82370658..2b17126c 100644 --- a/app/src/main/java/org/bepass/oblivion/TouchAwareSwitch.java +++ b/app/src/main/java/org/bepass/oblivion/component/TouchAwareSwitch.java @@ -1,4 +1,4 @@ -package org.bepass.oblivion; +package org.bepass.oblivion.component; import android.annotation.SuppressLint; import android.content.Context; diff --git a/app/src/main/java/org/bepass/oblivion/ui/MainActivity.java b/app/src/main/java/org/bepass/oblivion/ui/MainActivity.java index 3a5ba3c3..e88961e9 100644 --- a/app/src/main/java/org/bepass/oblivion/ui/MainActivity.java +++ b/app/src/main/java/org/bepass/oblivion/ui/MainActivity.java @@ -15,10 +15,6 @@ import android.os.Bundle; import android.os.Handler; import android.view.View; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.ProgressBar; -import android.widget.TextView; import android.widget.Toast; import androidx.activity.OnBackPressedCallback; @@ -26,15 +22,12 @@ import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.NonNull; -import com.google.android.material.floatingactionbutton.FloatingActionButton; - import org.bepass.oblivion.ConnectionState; import org.bepass.oblivion.FileManager; import org.bepass.oblivion.LocaleHandler; import org.bepass.oblivion.OblivionVpnService; import org.bepass.oblivion.PublicIPUtils; import org.bepass.oblivion.R; -import org.bepass.oblivion.TouchAwareSwitch; import org.bepass.oblivion.base.StateAwareBaseActivity; import org.bepass.oblivion.databinding.ActivityMainBinding; diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 27f82354..96d93424 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -99,7 +99,7 @@ app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.565"> - Date: Sat, 29 Jun 2024 12:37:49 +0330 Subject: [PATCH 16/34] conflict fix --- app/libs/go.mod | 17 +-- app/libs/go.sum | 17 +++ .../org/bepass/oblivion/ui/LogActivity.java | 63 ++++++--- app/src/main/res/drawable/button.xml | 8 ++ app/src/main/res/layout/activity_log.xml | 126 +++++++++--------- app/src/main/res/values-fa/strings.xml | 13 +- app/src/main/res/values-ru/strings.xml | 5 +- app/src/main/res/values-zh/strings.xml | 19 +-- app/src/main/res/values/strings.xml | 5 +- 9 files changed, 169 insertions(+), 104 deletions(-) create mode 100644 app/src/main/res/drawable/button.xml diff --git a/app/libs/go.mod b/app/libs/go.mod index 413b230f..91934257 100644 --- a/app/libs/go.mod +++ b/app/libs/go.mod @@ -6,12 +6,13 @@ toolchain go1.22.2 replace github.com/eycorsican/go-tun2socks => github.com/trojan-gfw/go-tun2socks v1.16.3-0.20210702214000-083d49176e05 + require ( github.com/bepass-org/warp-plus v1.2.4-0.20240603141320-b3f8c9953e10 github.com/eycorsican/go-tun2socks v1.16.11 github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 github.com/xjasonlyu/tun2socks/v2 v2.5.2 - golang.org/x/mobile v0.0.0-20240213143359-d1f7d3436075 + golang.org/x/mobile v0.0.0-20240604190613-2782386b8afd ) require ( @@ -72,15 +73,15 @@ require ( gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/goptlib v1.5.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/mock v0.4.0 // indirect - golang.org/x/crypto v0.22.0 // indirect + golang.org/x/crypto v0.24.0 // indirect golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 // indirect - golang.org/x/mod v0.15.0 // indirect - golang.org/x/net v0.24.0 // indirect - golang.org/x/sync v0.6.0 // indirect - golang.org/x/sys v0.19.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/mod v0.18.0 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.18.0 // indirect + golang.org/x/tools v0.22.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wireguard/windows v0.5.3 // indirect google.golang.org/protobuf v1.32.0 // indirect diff --git a/app/libs/go.sum b/app/libs/go.sum index 9ff453cb..28d434a3 100644 --- a/app/libs/go.sum +++ b/app/libs/go.sum @@ -221,14 +221,20 @@ golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY= golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/mobile v0.0.0-20240213143359-d1f7d3436075 h1:iZzqyDd8gFkJZpsJNzveyScRBcQlsveheh6Q77LzhPY= golang.org/x/mobile v0.0.0-20240213143359-d1f7d3436075/go.mod h1:Y8Bnziw2dX69ZhYuqQB8Ihyjks1Q6fMmbg17j9+ISNA= +golang.org/x/mobile v0.0.0-20240604190613-2782386b8afd h1:ow0zRCrn9LoaazcXsRUYYjFp+cwkdgB/vlW/ZJEzNRw= +golang.org/x/mobile v0.0.0-20240604190613-2782386b8afd/go.mod h1:CKRkjuY6XI4LTd47GM+4lCT8aFnTO+FcLAGMu0ibd0g= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= @@ -238,12 +244,16 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -259,6 +269,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -267,6 +279,7 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -275,6 +288,8 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -283,6 +298,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= diff --git a/app/src/main/java/org/bepass/oblivion/ui/LogActivity.java b/app/src/main/java/org/bepass/oblivion/ui/LogActivity.java index 72ca1da9..6bebf3a3 100644 --- a/app/src/main/java/org/bepass/oblivion/ui/LogActivity.java +++ b/app/src/main/java/org/bepass/oblivion/ui/LogActivity.java @@ -1,41 +1,52 @@ package org.bepass.oblivion.ui; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.util.Log; +import android.widget.Button; import android.widget.ImageView; import android.widget.ScrollView; import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; +import com.google.android.material.button.MaterialButton; + import org.bepass.oblivion.R; -import org.bepass.oblivion.base.BaseActivity; -import org.bepass.oblivion.databinding.ActivityLogBinding; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; +import java.util.ArrayDeque; +import java.util.Arrays; +import java.util.Deque; -public class LogActivity extends BaseActivity { +public class LogActivity extends AppCompatActivity { private final Handler handler = new Handler(Looper.getMainLooper()); + private TextView logs; + private ScrollView logScrollView; private boolean isUserScrollingUp = false; private Runnable logUpdater; - @Override - protected int getLayoutResourceId() { - return R.layout.activity_log; - } - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + setContentView(R.layout.activity_log); + ImageView back = findViewById(R.id.back); + Button copyToClip = findViewById(R.id.copytoclip); + logs = findViewById(R.id.logs); + logScrollView = findViewById(R.id.logScrollView); setupScrollListener(); - binding.back.setOnClickListener(v -> getOnBackPressedDispatcher().onBackPressed()); + back.setOnClickListener(v -> getOnBackPressedDispatcher().onBackPressed()); + + copyToClip.setOnClickListener(v -> copyLast100LinesToClipboard()); + logUpdater = new Runnable() { @Override public void run() { @@ -46,9 +57,9 @@ public void run() { } private void setupScrollListener() { - binding.logScrollView.getViewTreeObserver().addOnScrollChangedListener(() -> { - int scrollY = binding.logScrollView.getScrollY(); - int maxScrollY = binding.logs.getHeight() - binding.logScrollView.getHeight(); + logScrollView.getViewTreeObserver().addOnScrollChangedListener(() -> { + int scrollY = logScrollView.getScrollY(); + int maxScrollY = logs.getHeight() - logScrollView.getHeight(); isUserScrollingUp = scrollY < maxScrollY; }); } @@ -76,13 +87,33 @@ private void readLogsFromFile() { String finalLog = sb.toString(); runOnUiThread(() -> { - binding.logs.setText(finalLog); + logs.setText(finalLog); if (!isUserScrollingUp) { - binding.logScrollView.post(() -> binding.logScrollView.fullScroll(ScrollView.FOCUS_DOWN)); + logScrollView.post(() -> logScrollView.fullScroll(ScrollView.FOCUS_DOWN)); } }); } catch (IOException e) { - Log.e(TAG, "readLogsFromFile: ",e ); + e.printStackTrace(); } } + + private void copyLast100LinesToClipboard() { + String logText = logs.getText().toString(); + String[] logLines = logText.split("\n"); + int totalLines = logLines.length; + + // Use Deque to efficiently get the last 100 lines + Deque last100Lines = new ArrayDeque<>(100); + last100Lines.addAll(Arrays.asList(logLines).subList(Math.max(0, totalLines - 100), totalLines)); + + StringBuilder sb = new StringBuilder(); + for (String line : last100Lines) { + sb.append(line).append("\n"); + } + + String last100Log = sb.toString(); + ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText("Log", last100Log); + clipboard.setPrimaryClip(clip); + } } diff --git a/app/src/main/res/drawable/button.xml b/app/src/main/res/drawable/button.xml new file mode 100644 index 00000000..0adab4ef --- /dev/null +++ b/app/src/main/res/drawable/button.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_log.xml b/app/src/main/res/layout/activity_log.xml index 8907e216..e512ad5e 100644 --- a/app/src/main/res/layout/activity_log.xml +++ b/app/src/main/res/layout/activity_log.xml @@ -1,72 +1,76 @@ - + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@drawable/background_gradient" + tools:context="org.bepass.oblivion.LogActivity"> - - - - - - - + android:layout_height="48dp" + android:layout_marginTop="8dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> - + - + + - + - - - - + android:layout_height="wrap_content" + android:text="Start Logging Here.." + android:textSize="11sp" /> + +