From 77fa799c77de7ba7893934b823d763df3231c094 Mon Sep 17 00:00:00 2001 From: cyberarm Date: Fri, 30 Oct 2020 17:24:48 -0500 Subject: [PATCH] Added LaunchActivity to show logo and ask for permissions before reaching MainActivity, started work on saving Actions as presets and drafted Presets menu. --- .idea/compiler.xml | 6 + .idea/gradle.xml | 1 + .idea/misc.xml | 2 +- app/build.gradle | 1 + app/src/main/AndroidManifest.xml | 4 +- .../LauncherActivity.java | 90 +++++++++++ .../MainActivity.java | 44 +---- .../dialogs/PermissionsRequestDialog.java | 3 +- .../dialogs/PresetDialog.java | 153 ++++++++++++++++++ .../ui/editor/ActionsFragment.java | 54 +++++++ .../ui/settings/presets/PresetsFragment.java | 70 ++++++++ app/src/main/res/layout/activity_launcher.xml | 46 ++++++ .../res/layout/dialog_permission_request.xml | 1 + .../main/res/layout/fragment_part_groups.xml | 4 +- .../main/res/layout/fragment_part_presets.xml | 39 +++++ app/src/main/res/layout/fragment_presets.xml | 67 ++++++++ app/src/main/res/menu/action_extras_menu.xml | 10 ++ app/src/main/res/values/strings.xml | 2 + build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 +- 20 files changed, 556 insertions(+), 47 deletions(-) create mode 100644 .idea/compiler.xml create mode 100644 app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/LauncherActivity.java create mode 100644 app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/dialogs/PresetDialog.java create mode 100644 app/src/main/res/layout/activity_launcher.xml create mode 100644 app/src/main/res/layout/fragment_part_presets.xml create mode 100644 app/src/main/res/layout/fragment_presets.xml create mode 100644 app/src/main/res/menu/action_extras_menu.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..61a9130 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index ac6b0ae..23a89bb 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -15,6 +15,7 @@ diff --git a/.idea/misc.xml b/.idea/misc.xml index 6630d50..61f4772 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -39,7 +39,7 @@ - + diff --git a/app/build.gradle b/app/build.gradle index c161885..ba6832c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'com.android.application' +android.defaultConfig.vectorDrawables.useSupportLibrary = true android { compileSdkVersion 29 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7e8fb00..a4cac16 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ @@ -16,7 +17,7 @@ android:supportsRtl="true" android:theme="@style/AppTheme"> @@ -24,6 +25,7 @@ + \ No newline at end of file diff --git a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/LauncherActivity.java b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/LauncherActivity.java new file mode 100644 index 0000000..da57832 --- /dev/null +++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/LauncherActivity.java @@ -0,0 +1,90 @@ +package org.timecrafters.TimeCraftersConfigurationTool; + +import android.Manifest; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.util.Log; +import android.view.View; +import android.widget.Button; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; + +import com.google.android.material.bottomnavigation.BottomNavigationView; + +import org.timecrafters.TimeCraftersConfigurationTool.dialogs.PermissionsRequestDialog; + +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + +public class LauncherActivity extends AppCompatActivity { + private static final int REQUEST_WRITE_PERMISSION = 70; + private static final String TAG = "LauncherActivity"; + private static final long timerDelay = 2_000; + private static final long timerDelayAfterPermissionRequest = 500; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_launcher); + + if (getSupportActionBar() != null) { + getSupportActionBar().hide(); + } + + Button websiteButton = findViewById(R.id.timecrafters_website_button); + websiteButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://timecrafters.org")); + startActivity(browserIntent); + } + }); + + if (havePermissions()) { + startTimer(timerDelay); + } else { + new PermissionsRequestDialog().show(getSupportFragmentManager(), null); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { + switch (requestCode) { + case REQUEST_WRITE_PERMISSION: { + if (grantResults.length > 0 && grantResults[0] == PERMISSION_GRANTED) { + // Permission granted + startTimer(timerDelayAfterPermissionRequest); + } else { + // Permission not given + new PermissionsRequestDialog().show(getSupportFragmentManager(), null); + } + } + } + } + + private void startTimer(long milliseconds) { + Handler handler = new Handler(); + handler.postDelayed(new Runnable() { + @Override + public void run() { + finish(); + Intent intent = new Intent(LauncherActivity.this, MainActivity.class); + startActivity(intent); + } + }, milliseconds); + } + + private boolean havePermissions() { + return ContextCompat.checkSelfPermission(LauncherActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PERMISSION_GRANTED; + } + + public void requestStoragePermissions() { + ActivityCompat.requestPermissions(LauncherActivity.this, + new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, + REQUEST_WRITE_PERMISSION); + } +} \ No newline at end of file diff --git a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/MainActivity.java b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/MainActivity.java index 69be75f..eada882 100644 --- a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/MainActivity.java +++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/MainActivity.java @@ -1,25 +1,18 @@ package org.timecrafters.TimeCraftersConfigurationTool; -import android.Manifest; import android.os.Bundle; import com.google.android.material.bottomnavigation.BottomNavigationView; import androidx.appcompat.app.AppCompatActivity; -import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; import androidx.navigation.NavController; import androidx.navigation.Navigation; import androidx.navigation.ui.AppBarConfiguration; import androidx.navigation.ui.NavigationUI; import org.timecrafters.TimeCraftersConfigurationTool.backend.Backend; -import org.timecrafters.TimeCraftersConfigurationTool.dialogs.PermissionsRequestDialog; - -import static android.content.pm.PackageManager.PERMISSION_GRANTED; public class MainActivity extends AppCompatActivity { - private static final int REQUEST_WRITE_PERMISSION = 70; private static final String TAG = "MainActivity"; private AppBarConfiguration appBarConfiguration; @@ -39,15 +32,11 @@ public class MainActivity extends AppCompatActivity { NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration); NavigationUI.setupWithNavController(navView, navController); - if (!havePermissions()) { - new PermissionsRequestDialog().show(getSupportFragmentManager(), null); - } else { - if (Backend.instance() == null) { - new Backend(); - } - - Backend.instance().applicationContext = getApplicationContext(); + if (Backend.instance() == null) { + new Backend(); } + + Backend.instance().applicationContext = getApplicationContext(); } @Override @@ -56,31 +45,6 @@ public class MainActivity extends AppCompatActivity { return NavigationUI.navigateUp(navController, appBarConfiguration) || super.onSupportNavigateUp(); } - @Override - public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { - switch (requestCode) { - case REQUEST_WRITE_PERMISSION: { - if (grantResults.length > 0 && grantResults[0] == PERMISSION_GRANTED) { - // Permission granted - new Backend(); - } else { - // Permission not given - new PermissionsRequestDialog().show(getSupportFragmentManager(), null); - } - } - } - } - - private boolean havePermissions() { - return ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PERMISSION_GRANTED; - } - - public void requestStoragePermissions() { - ActivityCompat.requestPermissions(MainActivity.this, - new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, - REQUEST_WRITE_PERMISSION); - } - public void close() { finish(); } diff --git a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/dialogs/PermissionsRequestDialog.java b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/dialogs/PermissionsRequestDialog.java index 00cf2b2..760f2c8 100644 --- a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/dialogs/PermissionsRequestDialog.java +++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/dialogs/PermissionsRequestDialog.java @@ -8,6 +8,7 @@ import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; +import org.timecrafters.TimeCraftersConfigurationTool.LauncherActivity; import org.timecrafters.TimeCraftersConfigurationTool.MainActivity; import org.timecrafters.TimeCraftersConfigurationTool.R; import org.timecrafters.TimeCraftersConfigurationTool.backend.TAC; @@ -40,7 +41,7 @@ public class PermissionsRequestDialog extends TimeCraftersDialog { @Override public void onClick(View v) { dismiss(); - ((MainActivity) getActivity()).requestStoragePermissions(); + ((LauncherActivity) getActivity()).requestStoragePermissions(); } }); diff --git a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/dialogs/PresetDialog.java b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/dialogs/PresetDialog.java new file mode 100644 index 0000000..3764148 --- /dev/null +++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/dialogs/PresetDialog.java @@ -0,0 +1,153 @@ +package org.timecrafters.TimeCraftersConfigurationTool.dialogs; + +import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.google.android.material.snackbar.Snackbar; + +import org.timecrafters.TimeCraftersConfigurationTool.R; +import org.timecrafters.TimeCraftersConfigurationTool.backend.Backend; +import org.timecrafters.TimeCraftersConfigurationTool.backend.Config; +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Action; +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Group; +import org.timecrafters.TimeCraftersConfigurationTool.library.TimeCraftersDialog; +import org.timecrafters.TimeCraftersConfigurationTool.ui.editor.ActionsFragment; +import org.timecrafters.TimeCraftersConfigurationTool.ui.editor.GroupsFragment; + +import java.util.ArrayList; + +public class PresetDialog extends TimeCraftersDialog { + final String TAG = "PresetDialog"; + private Group group; + private Action action; + private boolean isNewPreset = false; + private TextView nameError; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + setCancelable(false); + + View root = super.onCreateView(inflater, container, savedInstanceState); + + if (getArguments() != null) { + isNewPreset = getArguments().getBoolean("is_new_preset", false); + + if (isNewPreset) { + this.group = Backend.instance().getConfig().getGroups().get(getArguments().getInt("group_index")); + this.action = group.getActions().get(getArguments().getInt("action_index")); + } else { + this.action = Backend.instance().getConfig().getPresets().getActions().get(getArguments().getInt("action_index")); + } + } + + final TextView title = root.findViewById(R.id.dialog_title); + final LinearLayout view = root.findViewById(R.id.dialog_content); + view.addView(getLayoutInflater().inflate(R.layout.dialog_edit_group, null)); + final EditText name = view.findViewById(R.id.name); + nameError = view.findViewById(R.id.name_error); + + final Button cancel = view.findViewById(R.id.cancel); + final Button mutate = view.findViewById(R.id.mutate); + cancel.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + dismiss(); + } + }); + + if (group != null) { + title.setText("Editing " + group.name); + name.setText(group.name); + mutate.setText(getResources().getString(R.string.dialog_update)); + } else { + title.setText("Add Preset"); + } + + name.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + validated(name.getText().toString().trim()); + } + + @Override + public void afterTextChanged(Editable s) { + + } + }); + + mutate.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + final String presetName = name.getText().toString().trim(); + Action actionClone = deepCopyAction(action); +// if (group != null && group.name.equals(groupName)) { +// dismiss(); +// } + + if (validated(presetName)) { + if (action.name != presetName) { + actionClone.name = presetName; + } + + Backend.instance().getConfig().getPresets().getActions().add(actionClone); + + Backend.instance().configChanged(); + + dismiss(); + } + } + }); + + return root; + } + + private boolean validated(String name) { + String message = ""; + ArrayList actions = Backend.instance().getConfig().getPresets().getActions(); + boolean nameUnique = true; + + for (Action a : actions) { + if (a.name.equals(name)) { + nameUnique = false; + break; + } + } + + // TODO: fix editing preset name impossible + if (!nameUnique) { + message += "Name is not unique!"; + + } else if (name.length() <= 0) { + message += "Name cannot be blank!"; + + } + + if (message.length() > 0) { + nameError.setVisibility(View.VISIBLE); + nameError.setText(message); + return false; + } else { + nameError.setVisibility(View.GONE); + return true; + } + } + + private Action deepCopyAction(Action action) { + String json = Backend.instance().gsonForConfig().toJson(action); + + return Backend.instance().gsonForConfig().fromJson(json, Action.class); + } +} diff --git a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/editor/ActionsFragment.java b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/editor/ActionsFragment.java index 08582c6..643552d 100644 --- a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/editor/ActionsFragment.java +++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/editor/ActionsFragment.java @@ -1,15 +1,21 @@ package org.timecrafters.TimeCraftersConfigurationTool.ui.editor; +import android.content.Context; import android.os.Bundle; +import android.view.ContextMenu; +import android.view.ContextThemeWrapper; import android.view.LayoutInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.CompoundButton; import android.widget.ImageButton; import android.widget.LinearLayout; +import android.widget.PopupMenu; import android.widget.ScrollView; import android.widget.Switch; import android.widget.TextView; +import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -25,6 +31,7 @@ import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Action; import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Group; import org.timecrafters.TimeCraftersConfigurationTool.dialogs.ActionDialog; import org.timecrafters.TimeCraftersConfigurationTool.dialogs.ConfirmationDialog; +import org.timecrafters.TimeCraftersConfigurationTool.dialogs.PresetDialog; import org.timecrafters.TimeCraftersConfigurationTool.library.TimeCraftersDialog; import org.timecrafters.TimeCraftersConfigurationTool.library.TimeCraftersDialogRunnable; import org.timecrafters.TimeCraftersConfigurationTool.library.TimeCraftersFragment; @@ -97,6 +104,21 @@ public class ActionsFragment extends TimeCraftersFragment { styleSwitch(buttonView, isChecked); } }); + final int index = i; + name.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + showContextMenu(name, index); + + return true; + } + }); + name.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() { + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { + getActivity().getMenuInflater().inflate(R.menu.action_extras_menu, menu); + } + }); edit.setOnClickListener(new View.OnClickListener() { @Override @@ -158,4 +180,36 @@ public class ActionsFragment extends TimeCraftersFragment { container.addView(view); } } + + private void showContextMenu(View view, final int action_index) { + Context context = new ContextThemeWrapper(getActivity(), R.style.PopUpMenu); + PopupMenu menu = new PopupMenu(context, view); + menu.getMenuInflater().inflate(R.menu.action_extras_menu, menu.getMenu()); + + menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()) { + case R.id.clone: { + // SHOW CLONE DIALOG + return true; + } + case R.id.save_as_preset: { + PresetDialog dialog = new PresetDialog(); + Bundle bundle = new Bundle(); + bundle.putInt("group_index", getArguments().getInt("group_index")); + bundle.putInt("action_index", action_index); + bundle.putBoolean("is_new_preset", true); + dialog.setArguments(bundle); + dialog.show(getFragmentManager(), "preset_dialog"); + return true; + } + default: + return false; + } + } + }); + + menu.show(); + } } diff --git a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/settings/presets/PresetsFragment.java b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/settings/presets/PresetsFragment.java index 978310d..5d9fa58 100644 --- a/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/settings/presets/PresetsFragment.java +++ b/app/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/ui/settings/presets/PresetsFragment.java @@ -1,6 +1,76 @@ package org.timecrafters.TimeCraftersConfigurationTool.ui.settings.presets; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.ImageButton; +import android.widget.LinearLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.timecrafters.TimeCraftersConfigurationTool.R; +import org.timecrafters.TimeCraftersConfigurationTool.backend.Backend; +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Action; +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Group; import org.timecrafters.TimeCraftersConfigurationTool.library.TimeCraftersFragment; public class PresetsFragment extends TimeCraftersFragment { + private LayoutInflater inflater; + private LinearLayout groupsContainer, actionsContainer; + private View root; + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + this.inflater = inflater; + this.root = inflater.inflate(R.layout.fragment_presets, container, false); + this.groupsContainer = root.findViewById(R.id.groups_container); + this.actionsContainer = root.findViewById(R.id.actions_container); + + populatePresets(); + + return root; + } + + void populatePresets() { + groupsContainer.removeAllViews(); + actionsContainer.removeAllViews(); + + int i = 0; + for (Group group : Backend.instance().getConfig().getPresets().getGroups()) { + View view = inflater.inflate(R.layout.fragment_part_presets, null); + + if (i % 2 == 0) { // even + view.setBackgroundColor(getResources().getColor(R.color.list_even)); + } else { + view.setBackgroundColor(getResources().getColor(R.color.list_odd)); + } + + groupsContainer.addView(view); + i++; + } + + i = 0; + for (Action action : Backend.instance().getConfig().getPresets().getActions()) { + View view = inflater.inflate(R.layout.fragment_part_presets, null); + + if (i % 2 == 0) { // even + view.setBackgroundColor(getResources().getColor(R.color.list_even)); + } else { + view.setBackgroundColor(getResources().getColor(R.color.list_odd)); + } + + Button name = view.findViewById(R.id.name); + ImageButton rename = view.findViewById(R.id.rename); + ImageButton delete = view.findViewById(R.id.delete); + + name.setText(action.name); + + actionsContainer.addView(view); + i++; + } + } } diff --git a/app/src/main/res/layout/activity_launcher.xml b/app/src/main/res/layout/activity_launcher.xml new file mode 100644 index 0000000..c0eb69a --- /dev/null +++ b/app/src/main/res/layout/activity_launcher.xml @@ -0,0 +1,46 @@ + + + + + + + +