diff --git a/app/build.gradle b/app/build.gradle
index d7ff43967..be3a73c25 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -200,8 +200,15 @@ dependencies {
implementation "androidx.core:core-splashscreen:1.0.0-beta02"
implementation project(':geeksville-androidlib')
+
+ // App intro
+ implementation 'com.github.AppIntro:AppIntro:6.2.0'
}
kapt {
correctErrorTypes true
}
+
+repositories {
+ maven { url "https://jitpack.io" }
+}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 55317d3eb..67799bf9e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -191,6 +191,9 @@
android:path="com.geeksville.mesh" /> -->
+
+
diff --git a/app/src/main/java/com/geeksville/mesh/AppIntroduction.kt b/app/src/main/java/com/geeksville/mesh/AppIntroduction.kt
new file mode 100644
index 000000000..f0512bbdc
--- /dev/null
+++ b/app/src/main/java/com/geeksville/mesh/AppIntroduction.kt
@@ -0,0 +1,68 @@
+package com.geeksville.mesh
+
+import android.content.Context
+import android.content.Intent
+import android.content.SharedPreferences
+import android.os.Bundle
+import androidx.fragment.app.Fragment
+import com.github.appintro.AppIntro
+import com.github.appintro.AppIntroFragment
+
+class AppIntroduction : AppIntro() {
+
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // Make sure you don't call setContentView!
+
+ // Call addSlide passing your Fragments.
+ // You can use AppIntroFragment to use a pre-built fragment
+ addSlide(
+ AppIntroFragment.createInstance(
+ title = resources.getString(R.string.intro_welcome_title),
+ description = resources.getString(R.string.intro_meshtastic_desc),
+ imageDrawable = R.mipmap.ic_launcher2_round,
+ backgroundColorRes = R.color.colourGrey,
+ descriptionColorRes = R.color.colorOnPrimary
+ ))
+ addSlide(AppIntroFragment.createInstance(
+ title = resources.getString(R.string.intro_get_started),
+ description = resources.getString(R.string.intro_started_text),
+ imageDrawable = R.drawable.icon_meanings,
+ backgroundColorRes = R.color.colourGrey,
+ descriptionColorRes = R.color.colorOnPrimary
+ ))
+ addSlide(AppIntroFragment.createInstance(
+ title = resources.getString(R.string.intro_encryption_title),
+ description = resources.getString(R.string.intro_encryption_text),
+ imageDrawable = R.drawable.channel_name_image,
+ backgroundColorRes = R.color.colourGrey,
+ descriptionColorRes = R.color.colorOnPrimary
+ ))
+ //addSlide(SlideTwoFragment())
+ }
+
+ override fun onSkipPressed(currentFragment: Fragment?) {
+ super.onSkipPressed(currentFragment)
+ // Decide what to do when the user clicks on "Skip"
+ finish()
+ val preferences = getSharedPreferences("PREFERENCES", Context.MODE_PRIVATE)
+ var editor = preferences.edit()
+ editor.putBoolean("app_intro_completed", true)
+ editor.apply()
+
+ startActivity(Intent(this, MainActivity::class.java))
+ }
+
+ override fun onDonePressed(currentFragment: Fragment?) {
+ super.onDonePressed(currentFragment)
+ // Decide what to do when the user clicks on "Done"
+ finish()
+ val preferences = getSharedPreferences("PREFERENCES", Context.MODE_PRIVATE)
+ var editor = preferences.edit()
+ editor.putBoolean("app_intro_completed", true)
+ editor.apply()
+
+ startActivity(Intent(this, MainActivity::class.java))
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt
index b239af13c..e38b7d5d7 100644
--- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt
+++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt
@@ -404,9 +404,16 @@ class MainActivity : BaseActivity(), Logging,
}
override fun onCreate(savedInstanceState: Bundle?) {
+ val preferences = getSharedPreferences("PREFERENCES", Context.MODE_PRIVATE)
+
installSplashScreen()
super.onCreate(savedInstanceState)
+ if (preferences.getBoolean("app_intro_completed", false) == false) {
+ startActivity(Intent(this, AppIntroduction::class.java))
+ }
+
+
binding = ActivityMainBinding.inflate(layoutInflater)
val prefs = UIViewModel.getPreferences(this)
@@ -1070,6 +1077,10 @@ class MainActivity : BaseActivity(), Logging,
chooseMapStyle()
return true
}
+ R.id.show_intro -> {
+ startActivity(Intent(this, AppIntroduction::class.java))
+ return true
+ }
R.id.preferences_quick_chat -> {
val fragmentManager: FragmentManager = supportFragmentManager
val fragmentTransaction: FragmentTransaction = fragmentManager.beginTransaction()
diff --git a/app/src/main/java/com/geeksville/mesh/android/ContextServices.kt b/app/src/main/java/com/geeksville/mesh/android/ContextServices.kt
index 74b5b3329..3a9ddf10f 100644
--- a/app/src/main/java/com/geeksville/mesh/android/ContextServices.kt
+++ b/app/src/main/java/com/geeksville/mesh/android/ContextServices.kt
@@ -21,9 +21,11 @@ val Context.bluetoothManager: BluetoothManager? get() = getSystemService(Context
val Context.deviceManager: CompanionDeviceManager?
@SuppressLint("InlinedApi")
get() {
- val activity: MainActivity? = GeeksvilleApplication.currentActivity as MainActivity?
- return if (hasCompanionDeviceApi()) activity?.getSystemService(Context.COMPANION_DEVICE_SERVICE) as? CompanionDeviceManager?
- else null
+ if (GeeksvilleApplication.currentActivity is MainActivity) {
+ val activity = GeeksvilleApplication.currentActivity
+ if (hasCompanionDeviceApi()) return activity?.getSystemService(Context.COMPANION_DEVICE_SERVICE) as? CompanionDeviceManager?
+ }
+ return null
}
val Context.usbManager: UsbManager get() = requireNotNull(getSystemService(Context.USB_SERVICE) as? UsbManager?) { "USB_SERVICE is not available"}
diff --git a/app/src/main/res/drawable-nodpi/channel_name_image.jpg b/app/src/main/res/drawable-nodpi/channel_name_image.jpg
new file mode 100644
index 000000000..271bc5241
Binary files /dev/null and b/app/src/main/res/drawable-nodpi/channel_name_image.jpg differ
diff --git a/app/src/main/res/drawable-nodpi/icon_meanings.png b/app/src/main/res/drawable-nodpi/icon_meanings.png
new file mode 100644
index 000000000..3635df2e8
Binary files /dev/null and b/app/src/main/res/drawable-nodpi/icon_meanings.png differ
diff --git a/app/src/main/res/layout/activity_app_introduction.xml b/app/src/main/res/layout/activity_app_introduction.xml
new file mode 100644
index 000000000..b25cb7758
--- /dev/null
+++ b/app/src/main/res/layout/activity_app_introduction.xml
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
index 042f6b7b5..12ba21d62 100644
--- a/app/src/main/res/menu/menu_main.xml
+++ b/app/src/main/res/menu/menu_main.xml
@@ -36,6 +36,10 @@
android:id="@+id/preferences_map_style"
android:title="@string/preferences_map_style"
app:showAsAction="withText" />
+
- #67EA94
#212121
#67EA94
+ #535353
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9f76d8b02..88a99668b 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -144,6 +144,14 @@
Resend
Shutdown
Reboot
+ Hello blank fragment
+ Show Introduction
+ Welcome to Meshtastic
+ Meshtastic is an open-source, off-grid, encrypted communication platform. The meshtastic radios form a mesh network and communicate using the LoRa protocol to send text messages
+ ...Let\'s get started!
+ Connect your meshtastic device by using either Bluetooth, Serial or WiFi. \n\nYou can see which devices are compatible at www.meshtastic.org/docs/hardware
+ "Setting up encryption"
+ As standard, a default encryption key is set. To enable your own channel and enhanced encryption, go to the channel tab and change the channel name, this will set a random key for AES256 encryption. \n\nTo communicate with other devices they will need to scan your QR code or follow the shared link to configure the channel settings.
Message
Append to message
Instantly send