From 46c8b9efa1ddbdbb8af367383e2e2e973cd4b086 Mon Sep 17 00:00:00 2001 From: Phillip Thelen Date: Mon, 11 Jul 2022 12:48:01 +0200 Subject: [PATCH] Implement input chooser activity --- .../receivers/DeviceCommunicationService.kt | 12 +-- version.properties | 2 +- wearos/src/main/AndroidManifest.xml | 1 + .../wearos/habitica/MainApplication.kt | 4 +- .../wearos/habitica/data/ApiClient.kt | 2 +- .../habitica/ui/activities/BaseActivity.kt | 7 +- .../ui/activities/ContinuePhoneActivity.kt | 18 ++++- .../habitica/ui/activities/InputActivity.kt | 74 +++++++++++++++++++ .../habitica/ui/activities/LoginActivity.kt | 2 +- .../habitica/ui/activities/SplashActivity.kt | 17 +++-- .../ui/activities/TaskFormActivity.kt | 44 +++++------ .../ui/viewmodels/ContinuePhoneViewModel.kt | 13 +++- .../habitica/ui/viewmodels/InputViewModel.kt | 20 +++++ .../src/main/res/drawable/ic_microphone.xml | 5 ++ wearos/src/main/res/layout/activity_input.xml | 53 +++++++++++++ .../main/res/layout/activity_task_form.xml | 1 + 16 files changed, 229 insertions(+), 46 deletions(-) create mode 100644 wearos/src/main/java/com/habitrpg/wearos/habitica/ui/activities/InputActivity.kt create mode 100644 wearos/src/main/java/com/habitrpg/wearos/habitica/ui/viewmodels/InputViewModel.kt create mode 100644 wearos/src/main/res/drawable/ic_microphone.xml create mode 100644 wearos/src/main/res/layout/activity_input.xml diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/DeviceCommunicationService.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/DeviceCommunicationService.kt index a3e2eb3fe..5ced9de91 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/DeviceCommunicationService.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/DeviceCommunicationService.kt @@ -26,17 +26,18 @@ class DeviceCommunicationService : WearableListenerService() { super.onMessageReceived(event) when (event.path) { DeviceCommunication.REQUEST_AUTH -> processAuthRequest(event) - DeviceCommunication.SHOW_REGISTER -> openActivity(LoginActivity::class.java) - DeviceCommunication.SHOW_LOGIN -> openActivity(LoginActivity::class.java) - DeviceCommunication.SHOW_RYA -> openActivity(MainActivity::class.java) + DeviceCommunication.SHOW_REGISTER -> openActivity(event, LoginActivity::class.java) + DeviceCommunication.SHOW_LOGIN -> openActivity(event, LoginActivity::class.java) + DeviceCommunication.SHOW_RYA -> openActivity(event, MainActivity::class.java) DeviceCommunication.SHOW_TASK_EDIT -> openTaskForm(event) } } - private fun openActivity(activityClass: Class<*>) { + private fun openActivity(event: MessageEvent, activityClass: Class<*>) { val intent = Intent(this, activityClass) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK startActivity(intent) + messageClient.sendMessage(event.sourceNodeId, "/action_completed", null) } private fun openTaskForm(event: MessageEvent) { @@ -44,7 +45,8 @@ class DeviceCommunicationService : WearableListenerService() { val startIntent = Intent(this, TaskFormActivity::class.java).apply { putExtra(TaskFormActivity.TASK_ID_KEY, taskID) } - startActivity(startIntent) + startActivity(startIntent) + messageClient.sendMessage(event.sourceNodeId, "/action_completed", null) } private fun processAuthRequest(event: MessageEvent) { diff --git a/version.properties b/version.properties index 3ba3383af..d89172c60 100644 --- a/version.properties +++ b/version.properties @@ -1,2 +1,2 @@ NAME=4.0 -CODE=4190 \ No newline at end of file +CODE=4200 \ No newline at end of file diff --git a/wearos/src/main/AndroidManifest.xml b/wearos/src/main/AndroidManifest.xml index 225c637e6..72cf9cb4a 100644 --- a/wearos/src/main/AndroidManifest.xml +++ b/wearos/src/main/AndroidManifest.xml @@ -52,6 +52,7 @@ + \ No newline at end of file diff --git a/wearos/src/main/java/com/habitrpg/wearos/habitica/MainApplication.kt b/wearos/src/main/java/com/habitrpg/wearos/habitica/MainApplication.kt index 9472780e5..dfd35ac60 100644 --- a/wearos/src/main/java/com/habitrpg/wearos/habitica/MainApplication.kt +++ b/wearos/src/main/java/com/habitrpg/wearos/habitica/MainApplication.kt @@ -58,7 +58,9 @@ class MainApplication : Application() { } private fun logLaunch() { - Firebase.analytics.logEvent("wear_launched", null) + if (!BuildConfig.DEBUG) { + Firebase.analytics.logEvent("wear_launched", null) + } } private fun setupFirebase() { diff --git a/wearos/src/main/java/com/habitrpg/wearos/habitica/data/ApiClient.kt b/wearos/src/main/java/com/habitrpg/wearos/habitica/data/ApiClient.kt index 6c3ae574d..626569909 100644 --- a/wearos/src/main/java/com/habitrpg/wearos/habitica/data/ApiClient.kt +++ b/wearos/src/main/java/com/habitrpg/wearos/habitica/data/ApiClient.kt @@ -123,7 +123,7 @@ class ApiClient @Inject constructor( val responseBuilder = response.newBuilder() responseBuilder.header("was-cached", (response.networkResponse == null).toString()) if (request.method == "GET") { - if (response.code == 504) { + if (response.code == 504 || response.request.header("x-api-user") != hostConfig.userID) { // Cache miss. Network might be down, but retry call without cache to be sure. chain.proceed(request.newBuilder() .header("Cache-Control", "no-cache") diff --git a/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/activities/BaseActivity.kt b/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/activities/BaseActivity.kt index a38df5e3b..62890276a 100644 --- a/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/activities/BaseActivity.kt +++ b/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/activities/BaseActivity.kt @@ -87,9 +87,12 @@ abstract class BaseActivity : ComponentActivi } } - internal fun openRemoteActivity(url: String) { + internal fun openRemoteActivity(url: String, keepActive: Boolean = false) { sendMessage("open_activity", url, null) - startActivity(Intent(this, ContinuePhoneActivity::class.java)) + startActivity(Intent(this, ContinuePhoneActivity::class.java) + .apply { + putExtra("keep_active", keepActive) + }) } internal fun sendMessage( diff --git a/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/activities/ContinuePhoneActivity.kt b/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/activities/ContinuePhoneActivity.kt index 1011af0b6..9334208d5 100644 --- a/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/activities/ContinuePhoneActivity.kt +++ b/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/activities/ContinuePhoneActivity.kt @@ -20,7 +20,7 @@ import kotlin.time.toDuration class ContinuePhoneActivity : BaseActivity() { override val viewModel: ContinuePhoneViewModel by viewModels() - private var secondsToShow = 2 + private var secondsToShow = 5 override fun onCreate(savedInstanceState: Bundle?) { binding = ActivityContinuePhoneBinding.inflate(layoutInflater) @@ -29,16 +29,23 @@ class ContinuePhoneActivity : BaseActivity() { + override val viewModel: InputViewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + binding = ActivityInputBinding.inflate(layoutInflater) + super.onCreate(savedInstanceState) + binding.titleView.text = viewModel.title + + binding.speechInput.setOnClickListener { + showSpeechInput() + } + binding.keyboardInput.setOnClickListener { + showKeyboard() + } + + binding.editText.setOnEditorActionListener { _, actionId, _ -> + if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_DONE) { + if (binding.editText.text?.isNotEmpty() == true) { + returnInput(binding.editText.text.toString()) + } + } + false + } + } + + private fun returnInput(inputString: String?) { + val data = Intent() + data.putExtra("input", inputString) + setResult(Activity.RESULT_OK, data) + finish() + } + + private fun showKeyboard() { + binding.editText.hint = binding.titleView.text + binding.editText.requestFocus() + binding.editText.postDelayed(100) { + val imm: InputMethodManager = + getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + imm.showSoftInput(binding.editText, InputMethodManager.SHOW_FORCED) + } + } + + private val speechInputResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + if (it.resultCode == Activity.RESULT_OK) { + val spokenText: String? = it.data?.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)?.firstOrNull() + returnInput(spokenText) + } + } + + private fun showSpeechInput() { + val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH).apply { + putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM) + putExtra(RecognizerIntent.EXTRA_PROMPT, binding.titleView.text) + } + speechInputResult.launch(intent) + } +} \ No newline at end of file diff --git a/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/activities/LoginActivity.kt b/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/activities/LoginActivity.kt index 7ac08bc65..43fd23a86 100644 --- a/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/activities/LoginActivity.kt +++ b/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/activities/LoginActivity.kt @@ -87,7 +87,7 @@ class LoginActivity: BaseActivity() { } private fun openRegisterOnPhone() { - openRemoteActivity(DeviceCommunication.SHOW_REGISTER) + openRemoteActivity(DeviceCommunication.SHOW_REGISTER, true) } private fun openLoginOnPhone() { diff --git a/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/activities/SplashActivity.kt b/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/activities/SplashActivity.kt index dde4ac344..f9c85abb2 100644 --- a/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/activities/SplashActivity.kt +++ b/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/activities/SplashActivity.kt @@ -30,8 +30,13 @@ class SplashActivity: BaseActivity() { startLoginActivity() } } + } + override fun onStart() { + super.onStart() messageClient.addListener(viewModel) + + if (!viewModel.hasAuthentication) { sendMessage("provide_auth", "/request/auth", null) { if (it) { showAccountLoader(true) @@ -40,6 +45,13 @@ class SplashActivity: BaseActivity() { startLoginActivity() } } + } + } + + + override fun onStop() { + messageClient.removeListener(viewModel) + super.onStop() } private fun startMainActivity() { @@ -64,9 +76,4 @@ class SplashActivity: BaseActivity() { binding.textView.isVisible = show } } - - override fun onPause() { - messageClient.removeListener(viewModel) - super.onPause() - } } \ No newline at end of file diff --git a/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/activities/TaskFormActivity.kt b/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/activities/TaskFormActivity.kt index 6dfcd5ffe..072e683cc 100644 --- a/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/activities/TaskFormActivity.kt +++ b/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/activities/TaskFormActivity.kt @@ -1,17 +1,14 @@ package com.habitrpg.wearos.habitica.ui.activities import android.app.Activity -import android.content.Context import android.content.Intent import android.content.res.ColorStateList import android.os.Bundle -import android.view.inputmethod.EditorInfo -import android.view.inputmethod.InputMethodManager import android.widget.TextView +import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.core.content.ContextCompat import androidx.core.view.isVisible -import androidx.core.view.postDelayed import androidx.lifecycle.lifecycleScope import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.databinding.ActivityTaskFormBinding @@ -45,23 +42,16 @@ class TaskFormActivity : BaseActivity - if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_DONE) { - if (binding.editText.text?.isNotEmpty() == true) { - binding.editTaskWrapper.isVisible = false - binding.taskConfirmationWrapper.isVisible = true - binding.confirmationText.text = binding.editText.text - binding.editText.clearFocus() - } - } - false + binding.editText.setOnClickListener { + it.clearFocus() + requestInput() } + binding.editButton.setOnClickListener { binding.editTaskWrapper.isVisible = true binding.taskConfirmationWrapper.isVisible = false if (intent.extras?.containsKey("task_type") == true) { - binding.editText.requestFocus() - showKeyboard() + requestInput() } } binding.todoButton.setOnClickListener { taskType = TaskType.TODO } @@ -91,26 +81,28 @@ class TaskFormActivity : BaseActivity("keep_active") ?: false + var onActionCompleted: (() -> Unit)? = null + override fun onMessageReceived(event: MessageEvent) { + when (event.path) { + "/action_completed" -> onActionCompleted?.invoke() + } + } } diff --git a/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/viewmodels/InputViewModel.kt b/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/viewmodels/InputViewModel.kt new file mode 100644 index 000000000..4a5661809 --- /dev/null +++ b/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/viewmodels/InputViewModel.kt @@ -0,0 +1,20 @@ +package com.habitrpg.wearos.habitica.ui.viewmodels + +import androidx.lifecycle.SavedStateHandle +import com.habitrpg.wearos.habitica.data.repositories.TaskRepository +import com.habitrpg.wearos.habitica.data.repositories.UserRepository +import com.habitrpg.wearos.habitica.managers.LoadingManager +import com.habitrpg.wearos.habitica.util.ExceptionHandlerBuilder +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class InputViewModel @Inject constructor( + savedStateHandle: SavedStateHandle, + userRepository: UserRepository, + taskRepository: TaskRepository, + exceptionBuilder: ExceptionHandlerBuilder, + loadingManager: LoadingManager +) : BaseViewModel(userRepository, taskRepository, exceptionBuilder, loadingManager) { + val title = savedStateHandle.get("title") ?: "" +} diff --git a/wearos/src/main/res/drawable/ic_microphone.xml b/wearos/src/main/res/drawable/ic_microphone.xml new file mode 100644 index 000000000..05ab75366 --- /dev/null +++ b/wearos/src/main/res/drawable/ic_microphone.xml @@ -0,0 +1,5 @@ + + + diff --git a/wearos/src/main/res/layout/activity_input.xml b/wearos/src/main/res/layout/activity_input.xml new file mode 100644 index 000000000..b7b1c97d3 --- /dev/null +++ b/wearos/src/main/res/layout/activity_input.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/wearos/src/main/res/layout/activity_task_form.xml b/wearos/src/main/res/layout/activity_task_form.xml index 8d7e7a697..1e6158e8b 100644 --- a/wearos/src/main/res/layout/activity_task_form.xml +++ b/wearos/src/main/res/layout/activity_task_form.xml @@ -65,6 +65,7 @@ android:paddingHorizontal="18dp" android:hint="@string/task_title_hint" android:background="@drawable/row_background_outline" + android:focusable="false" android:inputType="textCapSentences" />