diff --git a/app/build.gradle b/app/build.gradle
index 2eaa5dc5..b7cca1d2 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -85,4 +85,5 @@ dependencies {
// Third-party
implementation 'cat.ereza:customactivityoncrash:2.3.0'
implementation 'com.github.KaKaVip:Android-Flag-Kit:v0.1'
+ implementation 'com.github.AppIntro:AppIntro:6.2.0'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 81a1f8d3..c230db79 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -54,6 +54,8 @@
android:name=".activities.EditCtrlPermissions"
android:parentActivityName=".activities.SettingsActivity" />
+
{
diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/OnBoardingActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/OnBoardingActivity.java
new file mode 100644
index 00000000..ba1e4513
--- /dev/null
+++ b/app/src/main/java/com/emanuelef/remote_capture/activities/OnBoardingActivity.java
@@ -0,0 +1,164 @@
+/*
+ * This file is part of PCAPdroid.
+ *
+ * PCAPdroid is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PCAPdroid is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with PCAPdroid. If not, see .
+ *
+ * Copyright 2020-22 - Emanuele Faranda
+ */
+
+package com.emanuelef.remote_capture.activities;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.method.LinkMovementMethod;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.core.content.ContextCompat;
+import androidx.fragment.app.Fragment;
+import androidx.preference.PreferenceManager;
+
+import com.emanuelef.remote_capture.R;
+import com.emanuelef.remote_capture.Utils;
+import com.emanuelef.remote_capture.model.Prefs;
+import com.github.appintro.AppIntro;
+import com.github.appintro.AppIntroBaseFragment;
+import com.github.appintro.model.SliderPagerBuilder;
+
+import org.jetbrains.annotations.Nullable;
+
+public class OnBoardingActivity extends AppIntro {
+ private static final String TAG = "OnBoardingActivity";
+
+ public static class OnBoardingFragment extends AppIntroBaseFragment {
+ @Override
+ protected int getLayoutId() {
+ return R.layout.appintro_fragment_intro;
+ }
+
+ public static OnBoardingFragment createInstance(CharSequence title, CharSequence description, int imageRes, int imageTint) {
+ OnBoardingFragment fragment = new OnBoardingFragment();
+ Bundle args = new SliderPagerBuilder()
+ .title(title)
+ //.description(description) see below
+ .imageDrawable(imageRes)
+ .backgroundColorRes(R.color.backgroundColor)
+ .titleColorRes(R.color.colorAccent)
+ .descriptionColorRes(R.color.colorTabText)
+ .build().toBundle();
+
+ args.putCharSequence("pd_descr", description);
+ args.putInt("pd_image_tint", imageTint);
+ fragment.setArguments(args);
+
+ return fragment;
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ View view = super.onCreateView(inflater, container, savedInstanceState);
+ if(view == null)
+ return null;
+
+ Bundle args = getArguments();
+ assert args != null;
+
+ // fixes links from Utils.getText not clickable
+ TextView tv = view.findViewById(R.id.description);
+ tv.setAutoLinkMask(0);
+ tv.setMovementMethod(LinkMovementMethod.getInstance());
+ tv.setText(args.getCharSequence("pd_descr"));
+ tv.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
+
+ // fix drawable tint and size
+ ImageView image = view.findViewById(R.id.image);
+ int tint = args.getInt("pd_image_tint");
+ if(tint > 0)
+ image.setColorFilter(ContextCompat.getColor(view.getContext(), tint));
+ image.setAdjustViewBounds(true);
+ ViewGroup.LayoutParams params = image.getLayoutParams();
+ params.height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 120, getResources().getDisplayMetrics());
+
+ return view;
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ addSlide(OnBoardingFragment.createInstance(getString(R.string.welcome_to_pcapdroid),
+ getText(R.string.app_intro_welcome_msg),
+ R.drawable.ic_logo, R.color.colorAccent));
+
+ addSlide(OnBoardingFragment.createInstance(getString(R.string.privacy_first),
+ Utils.getText(this, R.string.app_intro_privacy_msg, MainActivity.PRIVACY_POLICY_URL,
+ MainActivity.GITHUB_PROJECT_URL),
+ R.drawable.ic_shield, R.color.colorAccent));
+
+ addSlide(OnBoardingFragment.createInstance(getString(R.string.country_and_asn),
+ getText(R.string.app_intro_geolocation_msg),
+ R.drawable.ic_location_dot, R.color.colorAccent));
+
+ addSlide(OnBoardingFragment.createInstance(getString(R.string.additional_features),
+ Utils.getText(this, R.string.app_intro_additional_features_msg, MainActivity.FIREWALL_DOCS_URL,
+ MainActivity.MALWARE_DETECTION_DOCS_URL, MainActivity.TLS_DECRYPTION_DOCS_URL),
+ R.drawable.ic_rocket_launch, R.color.colorAccent));
+
+ showStatusBar(true);
+ setSkipButtonEnabled(false);
+ setIndicatorEnabled(true);
+ setSystemBackButtonLocked(true);
+
+ // Theme
+ int colorAccent = ContextCompat.getColor(this, R.color.colorAccent);
+ setIndicatorColor(colorAccent, ContextCompat.getColor(this, R.color.colorAccentLight));
+ setBackArrowColor(colorAccent);
+ setColorSkipButton(colorAccent);
+ setNextArrowColor(colorAccent);
+ setBackArrowColor(colorAccent);
+ setColorDoneText(colorAccent);
+ }
+
+ @Override
+ protected void onSkipPressed(@Nullable Fragment currentFragment) {
+ Log.d(TAG, "onSkipPressed");
+ super.onSkipPressed(currentFragment);
+ runMainActivity();
+ }
+
+ @Override
+ protected void onDonePressed(@Nullable Fragment currentFragment) {
+ Log.d(TAG, "onDonePressed");
+ super.onDonePressed(currentFragment);
+ runMainActivity();
+ }
+
+ private void runMainActivity() {
+ Prefs.refreshAppVersion(PreferenceManager.getDefaultSharedPreferences(this));
+
+ Intent intent = new Intent(this, MainActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ startActivity(intent);
+ finish();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/emanuelef/remote_capture/model/Prefs.java b/app/src/main/java/com/emanuelef/remote_capture/model/Prefs.java
index 9f46596b..f1d3b4cb 100644
--- a/app/src/main/java/com/emanuelef/remote_capture/model/Prefs.java
+++ b/app/src/main/java/com/emanuelef/remote_capture/model/Prefs.java
@@ -23,6 +23,7 @@ import android.content.Context;
import android.content.SharedPreferences;
import com.emanuelef.remote_capture.Billing;
+import com.emanuelef.remote_capture.BuildConfig;
import com.emanuelef.remote_capture.Utils;
public class Prefs {
@@ -66,6 +67,7 @@ public class Prefs {
public static final String PREF_FULL_PAYLOAD = "full_payload";
public static final String PREF_BLOCK_QUIC = "block_quic";
public static final String PREF_AUTO_BLOCK_PRIVATE_DNS = "auto_block_private_dns";
+ public static final String PREF_APP_VERSION = "appver";
public enum DumpMode {
NONE,
@@ -97,6 +99,14 @@ public class Prefs {
}
}
+ public static int getAppVersion(SharedPreferences p) {
+ return p.getInt(PREF_APP_VERSION, 0);
+ }
+
+ public static void refreshAppVersion(SharedPreferences p) {
+ p.edit().putInt(PREF_APP_VERSION, BuildConfig.VERSION_CODE).apply();
+ }
+
/* Prefs with defaults */
public static String getCollectorIp(SharedPreferences p) { return(p.getString(PREF_COLLECTOR_IP_KEY, "127.0.0.1")); }
public static int getCollectorPort(SharedPreferences p) { return(Integer.parseInt(p.getString(PREF_COLLECTOR_PORT_KEY, "1234"))); }
diff --git a/app/src/main/res/drawable/ic_location_dot.xml b/app/src/main/res/drawable/ic_location_dot.xml
new file mode 100644
index 00000000..c052a55b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_location_dot.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_rocket_launch.xml b/app/src/main/res/drawable/ic_rocket_launch.xml
new file mode 100644
index 00000000..5f6dc715
--- /dev/null
+++ b/app/src/main/res/drawable/ic_rocket_launch.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/menu/unlock_menu.xml b/app/src/main/res/menu/about_menu.xml
similarity index 66%
rename from app/src/main/res/menu/unlock_menu.xml
rename to app/src/main/res/menu/about_menu.xml
index 92280350..215f5019 100644
--- a/app/src/main/res/menu/unlock_menu.xml
+++ b/app/src/main/res/menu/about_menu.xml
@@ -8,4 +8,9 @@
android:title="@string/paid_features"
android:orderInCategory="10"
app:showAsAction="never" />
+
\ No newline at end of file
diff --git a/app/src/main/res/values-night-v8/colors.xml b/app/src/main/res/values-night-v8/colors.xml
index dd5f42ee..77948501 100644
--- a/app/src/main/res/values-night-v8/colors.xml
+++ b/app/src/main/res/values-night-v8/colors.xml
@@ -6,7 +6,8 @@
#6C52AB
#24144D
#121212
- @color/design_default_color_secondary
+ #03DAC6
+ #BCEBE7
#1CFFFFFF
#FF202020
@color/background_material_dark
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index cc7ce38e..4926f14a 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -7,6 +7,7 @@
#4D3294
#512da8
#D81B60
+ #D8ADBD
#2C000000
@color/design_default_color_surface
@color/background_material_light
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 069f5d0c..be611d73 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -22,6 +22,7 @@
- MaxMind DB Reader: Apache-2.0\n\n
- FlagKit: MIT\n\n
- IP Geolocation by DB-IP\n\n
+ - AppIntro: Apache-2.0\n\n
- Font Awesome: Licenses\n\n
- App icon by Freepik from flaticon\n\n
- SourceCodePro font: OFL-1.1\n\n
@@ -346,4 +347,17 @@
Detect and possibly block private DNS to inspect DNS traffic. Disabling this can hinder detection
This wizard will guide you through the installation of the PCAPdroid mitm addon and certification authority, which are needed to perform the TLS decryption
PCAPdroid is now ready to decrypt TLS traffic\n\nCheck out the user guide to know more about the security measures which may prevent decryption and how to bypass them
+ Skip
+ Next
+ Back
+ Done
+ Welcome to PCAPdroid
+ PCAPdroid is a privacy-friendly app which lets you track and analyze the connections made by the apps in your device\n\nMoreover, it allows you to export a PCAP dump of the traffic, extract metadata and much more!
+ Privacy-first
+ The app does not embed any tracking, analytics or calling home\n\nHow can you be sure? Check out its privacy policy and its source code
+ Additional features
+ Monitoring is not enough? PCAPdroid gets you covered!\n\n• Firewall: block apps, domains or IP addresses*\n• Malware detection and blocking*\n• TLS decryption\n\n*paid feature, only available on Google Play
+ Country and ASN
+ PCAPdroid can query a local database to determine the country of a remote server\n\nYou must first download the database from the PCAPdroid settings
+ On-boarding