diff --git a/DAIRemoteApp/app/src/main/java/com/example/dairemote_app/InteractionPage.java b/DAIRemoteApp/app/src/main/java/com/example/dairemote_app/InteractionPage.java index cd3c65e..d6037a3 100644 --- a/DAIRemoteApp/app/src/main/java/com/example/dairemote_app/InteractionPage.java +++ b/DAIRemoteApp/app/src/main/java/com/example/dairemote_app/InteractionPage.java @@ -10,10 +10,13 @@ import android.text.TextWatcher; import android.util.Log; import android.view.GestureDetector; +import android.view.Gravity; import android.view.KeyEvent; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; +import android.view.Window; +import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.FrameLayout; @@ -24,6 +27,7 @@ import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBarDrawerToggle; +import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.core.view.GravityCompat; @@ -49,6 +53,14 @@ public class InteractionPage extends AppCompatActivity implements NavigationView GridLayout keyboardExtraBtnsLayout; TextView moreOpts; private int currentPageIndex = 0; + + // vars for tutorial + private boolean tutorialOn = false; // tracks if tutorial is active + private int currentStep = 0; + private boolean serverNavigationButtonClicked = false; // tracks if main icon button was clicked + private boolean nextStepPending = false; // tracks if "Next" was clicked but action is pending + + private final String[][][] keyboardExtraRows = { { // Page 1 {"F1", "F2", "F3", "F4", "F5", "F6"}, // Row 1 @@ -118,6 +130,14 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_interaction_page); + // checks if tutorial is still ongoing + tutorialOn = getIntent().getBooleanExtra("tutorialOn", false); + currentStep = getIntent().getIntExtra("currentStep", 0); + if (tutorialOn) { + continueTutorial(currentStep); + } + + FrameLayout touchpadFrame = findViewById(R.id.touchpadFrame); Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); @@ -349,6 +369,9 @@ public void onClick(View v) { interactionsHelpText.setVisibility(View.GONE); // Hide the TextView } else { interactionsHelpText.setVisibility(View.VISIBLE); // Show the TextView + tutorialOn = true; + currentStep = 1; + continueTutorial(currentStep); } } }); @@ -617,6 +640,8 @@ public void onBackPressed() { drawerLayout.closeDrawer(GravityCompat.START); } else if (!(editText.getVisibility() == View.VISIBLE)) { Intent intent = new Intent(InteractionPage.this, MainActivity.class); + intent.putExtra("tutorialOn", tutorialOn); + intent.putExtra("currentStep", currentStep); startActivity(intent); } else { super.onBackPressed(); @@ -634,6 +659,16 @@ public boolean onNavigationItemSelected(@NonNull MenuItem item) { startActivity(intent); } else if (itemId == R.id.nav_server) { intent = new Intent(this, ServersPage.class); + + // if tutorial is still active on navigation button clicked + if (tutorialOn) { + serverNavigationButtonClicked = true; + checkIfStepCompleted(); + // passing data of tutorial to interactionPage + intent.putExtra("tutorialOn", tutorialOn); + intent.putExtra("currentStep", currentStep); + } + startActivity(intent); } else if (itemId == R.id.nav_help) { intent = new Intent(this, InstructionsPage.class); @@ -646,4 +681,77 @@ public boolean onNavigationItemSelected(@NonNull MenuItem item) { drawerLayout.closeDrawer(GravityCompat.START); return true; } + + private void continueTutorial(int step) { + // resumes showing tutorial steps + showSteps(step); + } + + private void showSteps(int step) { + switch (step) { + case 1: + showCustomDialog("Remote Page", "If you ever need a refresher, click the help icon above to start the tutorial.", Gravity.TOP | Gravity.RIGHT, 100, 200); + break; + case 2: + showCustomDialog("Lower Panel Buttons", "Display Modes, Audio Cycling, Hotkeys, App Keyboard", Gravity.BOTTOM | Gravity.RIGHT, 100, 200); + break; + case 3: + showCustomDialog("ToolBar", "Click on the ToolBar button to navigate between pages.", Gravity.TOP | Gravity.RIGHT, 100, 200); + break; + default: + break; + } + } + // shows pop up for each step in customized position (depending on location of feature) + private void showCustomDialog(String title, String message, int gravity, int xOffset, int yOffset) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(title); + builder.setMessage(message); + + // PositiveButton representing "Next" for moving to the next step + builder.setPositiveButton("Next", (dialog, which) -> { + Log.d("Tutorial", "Current Step: " + currentStep); + + if (currentStep == 3) { + nextStepPending = true; + checkIfStepCompleted(); + } + else { + currentStep++; + showSteps(currentStep); + } + + }); + + // NegativeButton representing "Exit Tour" to stop the tutorial + builder.setNegativeButton("Exit Tour", (dialog, which) -> { + tutorialOn = false; + dialog.dismiss(); + }); + + AlertDialog dialog = builder.create(); + dialog.show(); + + // sets custom position + Window window = dialog.getWindow(); + if (window != null) { + WindowManager.LayoutParams params = window.getAttributes(); + params.gravity = gravity; + params.x = xOffset; + params.y = yOffset; + window.setAttributes(params); + } + + } + + // checking if specific action was completed for current step + private void checkIfStepCompleted() { + if (serverNavigationButtonClicked) { + nextStepPending = false; + currentStep++; + showSteps(currentStep); + } + } + + } \ No newline at end of file diff --git a/DAIRemoteApp/app/src/main/java/com/example/dairemote_app/MainActivity.java b/DAIRemoteApp/app/src/main/java/com/example/dairemote_app/MainActivity.java index 8dbe0ea..8919382 100644 --- a/DAIRemoteApp/app/src/main/java/com/example/dairemote_app/MainActivity.java +++ b/DAIRemoteApp/app/src/main/java/com/example/dairemote_app/MainActivity.java @@ -5,12 +5,16 @@ import android.net.wifi.WifiManager; import android.os.Bundle; import android.util.Log; +import android.view.Gravity; import android.view.MenuItem; +import android.view.Window; +import android.view.WindowManager; import android.widget.ImageButton; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBarDrawerToggle; +import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.core.view.GravityCompat; @@ -29,6 +33,12 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On ImageButton remotePage; + // vars for tutorial + private boolean tutorialOn = false; // tracks if tutorial is active + private int currentStep = 0; + private boolean mainButtonClicked = false; // tracks if main icon button was clicked + private boolean nextStepPending = false; // tracks if "Next" was clicked but action is pending + public void notifyUser(Context context, String msg) { runOnUiThread(() -> Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()); } @@ -72,6 +82,12 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); + // find the help button by its ID + ImageButton helpButton = findViewById(R.id.helpButton); + helpButton.setOnClickListener(v -> { + startTutorialPopUp("Interactive Tutorial", "Would you like to start the interactive tutorial?", Gravity.CENTER | Gravity.LEFT, 100, -100); + }); + drawerSetup(R.id.nav_home); remotePage = findViewById(R.id.DAIRemoteLogoBtn); @@ -86,6 +102,12 @@ protected void onCreate(Bundle savedInstanceState) { .start(); }) .start(); + + if (tutorialOn) { + mainButtonClicked = true; + checkIfStepCompleted(); + } + // Initialize the connection manager // Establish connection to host if not already established and not declined prior if (!ConnectionManager.connectionEstablished) { @@ -114,6 +136,16 @@ public void onHostFound(List serverIps) { } else { notifyUser(MainActivity.this, "Connection approved"); Intent intent = new Intent(MainActivity.this, InteractionPage.class); + + //checks if main button action was taken + if (tutorialOn) { + mainButtonClicked = true; + checkIfStepCompleted(); + // passing data of tutorial to interactionPage + intent.putExtra("tutorialOn", tutorialOn); + intent.putExtra("currentStep", currentStep); + } + startActivity(intent); } @@ -169,4 +201,102 @@ public boolean onNavigationItemSelected(@NonNull MenuItem item) { drawerLayout.closeDrawer(GravityCompat.START); return true; } -} \ No newline at end of file + + // pop up for giving choice for user to start tutorial or not + private void startTutorialPopUp(String title, String message, int gravity, int xOffset, int yOffset) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(title); + builder.setMessage(message); + + // PositiveButton representing "Start Tutorial" for starting the tutorial + builder.setPositiveButton("Start Tutorial", (dialog, which) -> { + if (!tutorialOn) { + tutorialOn = true; + startTutorial(); // triggers tutorial + } + }); + + // NegativeButton representing "No" to not start the tutorial + builder.setNegativeButton("No", (dialog, which) -> { + tutorialOn = false; + dialog.dismiss(); + }); + + AlertDialog dialog = builder.create(); + dialog.show(); + + // sets custom position + Window window = dialog.getWindow(); + if (window != null) { + WindowManager.LayoutParams params = window.getAttributes(); + params.gravity = gravity; + params.x = xOffset; + params.y = yOffset; + window.setAttributes(params); + } + } + + + + // functions to trigger tutorial + private void startTutorial() { + currentStep = 0; + showSteps(currentStep); + } + + + private void showSteps(int step) { + switch (step) { + case 0: + showCustomDialog("Main Page", "Tap on the center icon to connect to your local host. Ensure the desktop application is open.", Gravity.TOP | Gravity.LEFT, 100, 200); + break; + + default: + break; + } + } + + + // shows pop up for each step in customized position (depending on location of feature) + private void showCustomDialog(String title, String message, int gravity, int xOffset, int yOffset) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(title); + builder.setMessage(message); + + // PositiveButton representing "Next" for moving to the next step + builder.setPositiveButton("Next", (dialog, which) -> { + nextStepPending = true; + checkIfStepCompleted(); + }); + + // NegativeButton representing "Exit Tour" to stop the tutorial + builder.setNegativeButton("Exit Tour", (dialog, which) -> { + tutorialOn = false; + dialog.dismiss(); + }); + + AlertDialog dialog = builder.create(); + dialog.show(); + + // sets custom position + Window window = dialog.getWindow(); + if (window != null) { + WindowManager.LayoutParams params = window.getAttributes(); + params.gravity = gravity; + params.x = xOffset; + params.y = yOffset; + window.setAttributes(params); + } + } + + // checking if specific action was completed for current step + private void checkIfStepCompleted() { + if (currentStep == 0 && mainButtonClicked) { + nextStepPending = false; + currentStep++; + showSteps(currentStep); + } + } + + +} diff --git a/DAIRemoteApp/app/src/main/java/com/example/dairemote_app/ServersPage.java b/DAIRemoteApp/app/src/main/java/com/example/dairemote_app/ServersPage.java index f1a7615..5b5c769 100644 --- a/DAIRemoteApp/app/src/main/java/com/example/dairemote_app/ServersPage.java +++ b/DAIRemoteApp/app/src/main/java/com/example/dairemote_app/ServersPage.java @@ -4,13 +4,18 @@ import android.content.Intent; import android.os.Bundle; import android.util.Log; +import android.view.Gravity; import android.view.MenuItem; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBarDrawerToggle; +import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.core.view.GravityCompat; @@ -34,6 +39,10 @@ public class ServersPage extends AppCompatActivity implements NavigationView.OnN private ArrayAdapter adapter; private String selectedHost = null; + // vars for tutorial + private boolean tutorialOn = false; // tracks if tutorial is active + private int currentStep = 0; + public void drawerSetup(int page) { drawerLayout = findViewById(R.id.drawer_layout); navigationView = findViewById(R.id.nav_view); @@ -66,6 +75,13 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_servers_page); + // checks if tutorial is still ongoing + tutorialOn = getIntent().getBooleanExtra("tutorialOn", false); + currentStep = getIntent().getIntExtra("currentStep", 0); + if (tutorialOn) { + continueTutorial(currentStep); + } + drawerSetup(R.id.nav_server); ListView hostListView = findViewById(R.id.hostList); @@ -171,4 +187,47 @@ public boolean onNavigationItemSelected(@NonNull MenuItem item) { drawerLayout.closeDrawer(GravityCompat.START); return true; } + + private void continueTutorial(int step) { + // resumes showing tutorial steps + showSteps(step); + } + + private void showSteps(int step) { + switch (step) { + case 4: + showCustomDialog("Servers Page", "A list of all available nearby hosts. If not already connected, select a host from the list and you will be redirected to the Remote Page.", Gravity.BOTTOM | Gravity.RIGHT, 100, 200); + break; + default: + break; + } + } + // shows pop up for each step in customized position (depending on location of feature) + private void showCustomDialog(String title, String message, int gravity, int xOffset, int yOffset) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(title); + builder.setMessage(message); + + // PositiveButton representing Finish + builder.setPositiveButton("Finish", (dialog, which) -> { + currentStep++; + showSteps(currentStep); + tutorialOn = false; + dialog.dismiss(); + }); + + AlertDialog dialog = builder.create(); + dialog.show(); + + // sets custom position + Window window = dialog.getWindow(); + if (window != null) { + WindowManager.LayoutParams params = window.getAttributes(); + params.gravity = gravity; + params.x = xOffset; + params.y = yOffset; + window.setAttributes(params); + } + + } } \ No newline at end of file diff --git a/DAIRemoteApp/app/src/main/res/layout/activity_main.xml b/DAIRemoteApp/app/src/main/res/layout/activity_main.xml index 0eb0961..721b755 100644 --- a/DAIRemoteApp/app/src/main/res/layout/activity_main.xml +++ b/DAIRemoteApp/app/src/main/res/layout/activity_main.xml @@ -29,6 +29,21 @@ android:id="@+id/toolbar" layout="@layout/toolbar" /> + + - + \ No newline at end of file