diff --git a/.gitignore b/.gitignore
index 60d2c045..06ca2609 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,6 @@
/captures
*debug*
*release*
+*.swp
+*.swo
+*~
diff --git a/app/build.gradle b/app/build.gradle
index 9b72296b..f733a860 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -25,11 +25,26 @@ android {
}
buildTypes {
release {
+ versionNameSuffix ".release"
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ buildConfigField "String", "ROCC_URL_PREFIX", ROCC_URL_PREFIX
+ buildConfigField "String", "ROCC_WEBSOCKETS_PREFIX", ROCC_WEBSOCKETS_PREFIX
+ buildConfigField "Boolean", "ENABLE_IN_APP_ROCC_URL_SETTING", "false"
+ resValue "bool", "enable_in_app_rocc_url_setting", "false"
}
debug {
versionNameSuffix ".debug"
+ buildConfigField "String", "ROCC_URL_PREFIX", ROCC_URL_PREFIX
+ buildConfigField "String", "ROCC_WEBSOCKETS_PREFIX", ROCC_WEBSOCKETS_PREFIX
+ buildConfigField "Boolean", "ENABLE_IN_APP_ROCC_URL_SETTING", "true"
+ resValue "bool", "enable_in_app_rocc_url_setting", "true"
+ }
+ clientDebug {
+ initWith debug
+ buildConfigField "Boolean", "ENABLE_IN_APP_ROCC_URL_SETTING", "false"
+ resValue "bool", "enable_in_app_rocc_url_setting", "false"
+
}
}
defaultConfig {
@@ -80,12 +95,12 @@ dependencies {
implementation 'com.github.codekidX:storage-chooser:2.0.3'
implementation 'com.squareup.moshi:moshi:1.8.0'
implementation 'com.opencsv:opencsv:4.3.2'
- implementation 'org.jsoup:jsoup:1.11.3'
implementation 'com.android.support:support-v4:28.0.0'
implementation 'android.arch.lifecycle:extensions:1.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'
implementation 'com.google.firebase:firebase-core:16.0.9'
+ implementation 'org.java-websocket:Java-WebSocket:1.4.1'
kapt 'com.squareup.moshi:moshi-kotlin-codegen:1.6.0'
testImplementation 'junit:junit:4.12'
testImplementation 'androidx.test:core:1.2.0'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index b030777d..53ccd32c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -19,6 +19,7 @@
android:theme="@style/AppTheme"
android:largeHeap="true"
android:fullBackupContent="false"
+ android:usesCleartextTraffic="true"
tools:replace="android:icon,android:roundIcon"
>
@@ -32,12 +33,6 @@
android:resource="@xml/file_paths"/>
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
diff --git a/app/src/main/java/org/sil/storyproducer/controller/MainActivity.kt b/app/src/main/java/org/sil/storyproducer/controller/MainActivity.kt
index 7815dad1..e7fea7cd 100644
--- a/app/src/main/java/org/sil/storyproducer/controller/MainActivity.kt
+++ b/app/src/main/java/org/sil/storyproducer/controller/MainActivity.kt
@@ -1,5 +1,6 @@
package org.sil.storyproducer.controller
+import android.app.Activity
import android.app.AlertDialog
import android.content.BroadcastReceiver
import android.content.Context
@@ -7,7 +8,7 @@ import android.content.Intent
import android.content.IntentFilter
import android.net.ConnectivityManager
import android.os.Bundle
-import android.os.SystemClock
+import android.preference.PreferenceManager
import android.support.design.widget.NavigationView
import android.support.v4.view.GravityCompat
import android.support.v4.widget.DrawerLayout
@@ -19,21 +20,91 @@ import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.webkit.WebView
+import android.widget.EditText
+import android.widget.LinearLayout
import android.widget.ProgressBar
import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import org.sil.storyproducer.R
-import org.sil.storyproducer.model.Phase
-import org.sil.storyproducer.model.PhaseType
-import org.sil.storyproducer.model.Story
import org.sil.storyproducer.model.Workspace
import org.sil.storyproducer.tools.Network.ConnectivityStatus
import org.sil.storyproducer.tools.Network.VolleySingleton
import java.io.Serializable
+fun handleDrawerItemSelection(activity: Activity, menuItem: MenuItem, drawerLayout: DrawerLayout, currentItemId: Int?): Boolean {
+ menuItem.isChecked = true
+ drawerLayout.closeDrawers()
+
+ if (currentItemId == menuItem.itemId) {
+ return true
+ }
+
+ when (menuItem.itemId) {
+ R.id.nav_workspace -> {
+ val intent = Intent(activity, WorkspaceUpdateActivity::class.java)
+ activity.startActivity(intent)
+ activity.finish()
+ }
+ R.id.nav_stories -> {
+ val intent = Intent(activity, MainActivity::class.java)
+ activity.startActivity(intent)
+ activity.finish()
+ }
+ R.id.nav_registration -> {
+ val intent = Intent(activity, RegistrationActivity::class.java)
+ activity.startActivity(intent)
+ activity.finish()
+ }
+ R.id.nav_license -> {
+ val dialog = AlertDialog.Builder(activity)
+ .setTitle(activity.getString(R.string.license_title))
+ .setMessage(activity.getString(R.string.license_body))
+ .setPositiveButton(activity.getString(R.string.ok)) { _, _ -> }.create()
+ dialog.show()
+ }
+ R.id.nav_set_rocc_url_prefix -> {
+ val container = LinearLayout(activity)
+ container.orientation = LinearLayout.VERTICAL
+ container.layoutParams = LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.MATCH_PARENT)
+ val roccPrefixInput = EditText(activity)
+ roccPrefixInput.setText(PreferenceManager.getDefaultSharedPreferences(activity).getString("ROCC_URL_PREFIX", "")
+ ?: "")
+ container.addView(roccPrefixInput,
+ LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT))
+ val webSocketsInput = EditText(activity)
+ container.addView(webSocketsInput,
+ LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT))
+ webSocketsInput.setText(PreferenceManager.getDefaultSharedPreferences(activity).getString("WEBSOCKETS_URL", "")
+ ?: "")
+ val dialog = AlertDialog.Builder(activity)
+ .setTitle("ROCC URL Prefix")
+ .setMessage("Enter a string to prefix all requests to the remote consultant site")
+ .setView(container)
+ .setPositiveButton(activity.getString(R.string.ok)) { _, _ ->
+ PreferenceManager.getDefaultSharedPreferences(activity).edit()
+ .putString("ROCC_URL_PREFIX", roccPrefixInput.text.toString())
+ .putString("WEBSOCKETS_URL", webSocketsInput.text.toString()).apply()
+ }.create()
+ dialog.show()
+ }
+ R.id.nav_clear_all_messages -> {
+ }
+ R.id.nav_forget_remote_story_id -> {
+ Workspace.activeStory.remoteId = null
+ }
+ }
+
+ return true
+}
+
class MainActivity : AppCompatActivity(), Serializable {
- private var mDrawerLayout: DrawerLayout? = null
+ private lateinit var mDrawerLayout: DrawerLayout
private val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
@@ -60,7 +131,7 @@ class MainActivity : AppCompatActivity(), Serializable {
pb.visibility = View.VISIBLE
GlobalScope.async {
- if(!Workspace.isInitialized) Workspace.initializeWorskpace(this@MainActivity.applicationContext)
+ if (!Workspace.isInitialized) Workspace.initializeWorkspace(this@MainActivity.applicationContext)
runOnUiThread {
pb.visibility = View.GONE
supportFragmentManager.beginTransaction().add(R.id.fragment_container, StoryListFrag()).commit()
@@ -74,19 +145,10 @@ class MainActivity : AppCompatActivity(), Serializable {
return true
}
- /**
- * move to the chosen story
- */
- fun switchToStory(story: Story) {
- Workspace.activeStory = story
- val intent = Intent(this.applicationContext, Workspace.activePhase.getTheClass())
- startActivity(intent)
- }
-
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
android.R.id.home -> {
- mDrawerLayout!!.openDrawer(GravityCompat.START)
+ mDrawerLayout.openDrawer(GravityCompat.START)
true
}
R.id.helpButton -> {
@@ -94,11 +156,12 @@ class MainActivity : AppCompatActivity(), Serializable {
alert.setTitle("Story List Help")
val wv = WebView(this)
- val iStream = assets.open(Phase.getHelpName(PhaseType.STORY_LIST))
+ val iStream = assets.open("story_list.html")
val text = iStream.reader().use {
- it.readText() }
+ it.readText()
+ }
- wv.loadData(text,"text/html",null)
+ wv.loadData(text, "text/html", null)
alert.setView(wv)
alert.setNegativeButton("Close") { dialog, _ ->
dialog!!.dismiss()
@@ -116,52 +179,18 @@ class MainActivity : AppCompatActivity(), Serializable {
private fun setupDrawer() {
val toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
- val actionbar: ActionBar? = supportActionBar
- actionbar?.apply {
+ val actionbar: ActionBar = supportActionBar!!
+ actionbar.apply {
setDisplayHomeAsUpEnabled(true)
setHomeAsUpIndicator(R.drawable.ic_menu_white_24dp)
}
- supportActionBar!!.setDisplayHomeAsUpEnabled(true)
- supportActionBar!!.setHomeButtonEnabled(true)
-
mDrawerLayout = findViewById(R.id.drawer_layout)
- //Lock from opening with left swipe
- mDrawerLayout!!.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
+ mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
val navigationView: NavigationView = findViewById(R.id.nav_view)
+ val drawerLayout = mDrawerLayout
navigationView.setNavigationItemSelectedListener { menuItem ->
- // set item as selected to persist highlight
- menuItem.isChecked = true
- // close drawer when item is tapped
- mDrawerLayout!!.closeDrawers()
-
- // Add code here to update the UI based on the item selected
- // For example, swap UI fragments here
- val intent: Intent
- when (menuItem.itemId) {
- R.id.nav_workspace -> {
- intent = Intent(this, WorkspaceUpdateActivity::class.java)
- this.startActivity(intent)
- this.finish()
- }
- R.id.nav_stories -> {
- // Current fragment
- }
- R.id.nav_registration -> {
- intent = Intent(this, RegistrationActivity::class.java)
- this.startActivity(intent)
- this.finish()
- }
- R.id.nav_license -> {
- val dialog = AlertDialog.Builder(this)
- .setTitle(this.getString(R.string.license_title))
- .setMessage(this.getString(R.string.license_body))
- .setPositiveButton(this.getString(R.string.ok)) { _, _ -> }.create()
- dialog.show()
- }
- }
-
- true
+ handleDrawerItemSelection(this, menuItem, drawerLayout, R.id.nav_stories)
}
}
diff --git a/app/src/main/java/org/sil/storyproducer/controller/MultiRecordFrag.kt b/app/src/main/java/org/sil/storyproducer/controller/MultiRecordFrag.kt
index 4531ac1a..9888794c 100644
--- a/app/src/main/java/org/sil/storyproducer/controller/MultiRecordFrag.kt
+++ b/app/src/main/java/org/sil/storyproducer/controller/MultiRecordFrag.kt
@@ -1,182 +1,177 @@
-package org.sil.storyproducer.controller
-
-import android.app.Activity
-import android.app.AlertDialog
-import android.content.Intent
-import android.os.Bundle
-import android.os.Environment
-import android.provider.MediaStore
-import android.support.v4.content.FileProvider
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.EditText
-import android.widget.ImageView
-import android.widget.Toast
-import com.crashlytics.android.Crashlytics
-import org.sil.storyproducer.BuildConfig
-import org.sil.storyproducer.R
-import org.sil.storyproducer.model.PROJECT_DIR
-import org.sil.storyproducer.model.SLIDE_NUM
-import org.sil.storyproducer.model.SlideType
-import org.sil.storyproducer.model.Workspace
-import org.sil.storyproducer.tools.file.copyToWorkspacePath
-import org.sil.storyproducer.tools.toolbar.MultiRecordRecordingToolbar
-import org.sil.storyproducer.tools.toolbar.PlayBackRecordingToolbar
-import org.sil.storyproducer.tools.toolbar.RecordingToolbar
-import java.io.File
-
-/**
- * The fragment for the Draft view. This is where a user can draft out the story slide by slide
- */
-abstract class MultiRecordFrag : SlidePhaseFrag(), PlayBackRecordingToolbar.ToolbarMediaListener {
- protected open var recordingToolbar: RecordingToolbar = MultiRecordRecordingToolbar()
-
- private var tempPicFile: File? = null
-
-
- override fun onCreateView(inflater: LayoutInflater,
- container: ViewGroup?, savedInstanceState: Bundle?): View? {
- super.onCreateView(inflater, container, savedInstanceState)
- if (Workspace.activeStory.slides[slideNum].slideType != SlideType.LOCALCREDITS) {
- setToolbar()
- }
-
- setupCameraAndEditButton()
-
- return rootView
- }
-
- /**
- * Setup camera button for updating background image
- * and edit button for renaming text and local credits
- */
- fun setupCameraAndEditButton() {
- // display the image selection button, if on the title slide
- if(Workspace.activeStory.slides[slideNum].slideType in
- arrayOf(SlideType.FRONTCOVER,SlideType.LOCALSONG))
- {
- val imageFab: ImageView = rootView!!.findViewById(R.id.insert_image_view) as ImageView
- imageFab.visibility = View.VISIBLE
- imageFab.setOnClickListener {
- val chooser = Intent(Intent.ACTION_CHOOSER)
- chooser.putExtra(Intent.EXTRA_TITLE, "Select From:")
-
- val galleryIntent = Intent(Intent.ACTION_GET_CONTENT)
- galleryIntent.type = "image/*"
- chooser.putExtra(Intent.EXTRA_INTENT, galleryIntent)
-
- val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
- cameraIntent.resolveActivity(activity!!.packageManager).also {
- tempPicFile = File.createTempFile("temp", ".jpg", activity?.getExternalFilesDir(Environment.DIRECTORY_PICTURES))
- cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, FileProvider.getUriForFile(activity!!, "${BuildConfig.APPLICATION_ID}.fileprovider", tempPicFile!!))
- }
- chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf(cameraIntent))
-
- startActivityForResult(chooser, ACTIVITY_SELECT_IMAGE)
- }
- }
-
- // display the image selection button, if on the title slide
- if(Workspace.activeStory.slides[slideNum].slideType in
- arrayOf(SlideType.FRONTCOVER,SlideType.LOCALCREDITS))
- {
- //for these, use the edit text button instead of the text in the lower half.
- //In the phases that these are not there, do nothing.
- val editBox = rootView?.findViewById(R.id.fragment_dramatization_edit_text) as EditText?
- editBox?.visibility = View.INVISIBLE
-
- val editFab = rootView!!.findViewById(R.id.edit_text_view) as ImageView?
- editFab?.visibility = View.VISIBLE
- editFab?.setOnClickListener {
- val editText = EditText(context)
- editText.id = R.id.edit_text_input
-
- // Programmatically set layout properties for edit text field
- val params = ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT)
- // Apply layout properties
- editText.layoutParams = params
- editText.minLines = 5
- editText.text.insert(0,Workspace.activeSlide!!.translatedContent)
-
- val dialog = AlertDialog.Builder(context)
- .setTitle(getString(R.string.enter_text))
- .setView(editText)
- .setNegativeButton(getString(R.string.cancel), null)
- .setPositiveButton(getString(R.string.save)) { _, _ ->
- Workspace.activeSlide!!.translatedContent = editText.text.toString()
- setPic(rootView!!.findViewById(R.id.fragment_image_view) as ImageView)
- }.create()
-
- dialog.show()
- }
- }
- }
-
-
- /**
- * Change the picture behind the screen.
- */
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- try {
- if (resultCode == Activity.RESULT_OK && requestCode == ACTIVITY_SELECT_IMAGE) {
- //copy image into workspace
- var uri = data?.data
- if (uri == null) uri = FileProvider.getUriForFile(context!!, "${BuildConfig.APPLICATION_ID}.fileprovider", tempPicFile!!) //it was a camera intent
- Workspace.activeStory.slides[slideNum].imageFile = "$PROJECT_DIR/${slideNum}_Local.png"
- copyToWorkspacePath(context!!, uri!!,
- "${Workspace.activeStory.title}/${Workspace.activeStory.slides[slideNum].imageFile}")
- tempPicFile?.delete()
- setPic(rootView!!.findViewById(R.id.fragment_image_view) as ImageView)
- }
- }catch (e:Exception){
- Toast.makeText(context,"Error",Toast.LENGTH_SHORT).show()
- Crashlytics.logException(e)
- }
- }
-
- /**
- * This function serves to handle page changes and stops the audio streams from
- * continuing.
- *
- * @param isVisibleToUser whether fragment is currently visible to user
- */
- override fun setUserVisibleHint(isVisibleToUser: Boolean) {
- super.setUserVisibleHint(isVisibleToUser)
-
- // Make sure that we are currently visible
- if (this.isVisible) {
- // If we are becoming invisible, then...
- if (!isVisibleToUser) {
- recordingToolbar.stopToolbarMedia()
- }
- }
- }
-
- protected open fun setToolbar() {
- val bundle = Bundle()
- bundle.putInt(SLIDE_NUM, slideNum)
- recordingToolbar.arguments = bundle
- childFragmentManager.beginTransaction().replace(R.id.toolbar_for_recording_toolbar, recordingToolbar).commit()
-
- recordingToolbar.keepToolbarVisible()
- }
-
- override fun onStartedToolbarMedia() {
- super.onStartedToolbarMedia()
-
- stopSlidePlayBack()
- }
-
- override fun onStartedSlidePlayBack() {
- super.onStartedSlidePlayBack()
-
- recordingToolbar.stopToolbarMedia()
- }
-
- companion object {
- private const val ACTIVITY_SELECT_IMAGE = 53
- }
-}
+package org.sil.storyproducer.controller
+
+import android.app.Activity
+import android.app.AlertDialog
+import android.content.Intent
+import android.os.Bundle
+import android.os.Environment
+import android.provider.MediaStore
+import android.support.v4.content.FileProvider
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.EditText
+import android.widget.ImageView
+import android.widget.Toast
+import com.crashlytics.android.Crashlytics
+import org.sil.storyproducer.BuildConfig
+import org.sil.storyproducer.R
+import org.sil.storyproducer.model.PROJECT_DIR
+import org.sil.storyproducer.model.SLIDE_NUM
+import org.sil.storyproducer.model.PHASE_TYPE
+import org.sil.storyproducer.model.PhaseType
+import org.sil.storyproducer.model.SlideType
+import org.sil.storyproducer.model.Workspace
+import org.sil.storyproducer.tools.file.copyToWorkspacePath
+import org.sil.storyproducer.tools.toolbar.MultiRecordRecordingToolbar
+import org.sil.storyproducer.tools.toolbar.PlayBackRecordingToolbar
+import org.sil.storyproducer.tools.toolbar.RecordingToolbar
+import java.io.File
+
+/**
+ * The fragment for the Draft view. This is where a user can draft out the story slide by slide
+ */
+abstract class MultiRecordFrag : SlidePhaseFrag(), PlayBackRecordingToolbar.ToolbarMediaListener {
+ protected open var recordingToolbar: RecordingToolbar = MultiRecordRecordingToolbar()
+
+ private var tempPicFile: File? = null
+
+ override fun initializeViews() {
+ super.initializeViews()
+ if (Workspace.activeStory.slides[slideNumber].slideType != SlideType.LOCALCREDITS) {
+ setToolbar()
+ }
+ setupCameraAndEditButton()
+ }
+
+ /**
+ * Setup camera button for updating background image
+ * and edit button for renaming text and local credits
+ */
+ fun setupCameraAndEditButton() {
+ // display the image selection button, if on the title slide
+ if(Workspace.activeStory.slides[slideNumber].slideType in arrayOf(SlideType.FRONTCOVER,SlideType.LOCALSONG)
+ && phaseType != PhaseType.BACKT)
+ {
+ val imageFab: ImageView = rootView.findViewById(R.id.insert_image_view) as ImageView
+ imageFab.visibility = View.VISIBLE
+ imageFab.setOnClickListener {
+ val chooser = Intent(Intent.ACTION_CHOOSER)
+ chooser.putExtra(Intent.EXTRA_TITLE, "Select From:")
+
+ val galleryIntent = Intent(Intent.ACTION_GET_CONTENT)
+ galleryIntent.type = "image/*"
+ chooser.putExtra(Intent.EXTRA_INTENT, galleryIntent)
+
+ val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
+ cameraIntent.resolveActivity(activity!!.packageManager).also {
+ tempPicFile = File.createTempFile("temp", ".jpg", activity?.getExternalFilesDir(Environment.DIRECTORY_PICTURES))
+ cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, FileProvider.getUriForFile(activity!!, "${BuildConfig.APPLICATION_ID}.fileprovider", tempPicFile!!))
+ }
+ chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf(cameraIntent))
+
+ startActivityForResult(chooser, ACTIVITY_SELECT_IMAGE)
+ }
+
+ //for these, use the edit text button instead of the text in the lower half.
+ //In the phases that these are not there, do nothing.
+ val editBox = rootView.findViewById(R.id.fragment_dramatization_edit_text) as EditText?
+ editBox?.visibility = View.INVISIBLE
+
+ val editFab = rootView.findViewById(R.id.edit_text_view) as ImageView?
+ editFab?.visibility = View.VISIBLE
+ editFab?.setOnClickListener {
+ val editText = EditText(context)
+ editText.id = R.id.edit_text_input
+
+ // Programmatically set layout properties for edit text field
+ val params = ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT)
+ // Apply layout properties
+ editText.layoutParams = params
+ editText.minLines = 5
+ editText.text.insert(0,Workspace.activeSlide!!.translatedContent)
+
+ val dialog = AlertDialog.Builder(context)
+ .setTitle(getString(R.string.enter_text))
+ .setView(editText)
+ .setNegativeButton(getString(R.string.cancel), null)
+ .setPositiveButton(getString(R.string.save)) { _, _ ->
+ Workspace.activeSlide!!.translatedContent = editText.text.toString()
+ setPic()
+ }.create()
+
+ dialog.show()
+ }
+ }
+ }
+
+
+ /**
+ * Change the picture behind the screen.
+ */
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ try {
+ if (resultCode == Activity.RESULT_OK && requestCode == ACTIVITY_SELECT_IMAGE) {
+ //copy image into workspace
+ var uri = data?.data
+ if (uri == null) uri = FileProvider.getUriForFile(context!!, "${BuildConfig.APPLICATION_ID}.fileprovider", tempPicFile!!) //it was a camera intent
+ Workspace.activeStory.slides[slideNumber].imageFile = "$PROJECT_DIR/${slideNumber}_Local.png"
+ copyToWorkspacePath(context!!, uri!!,
+ "${Workspace.activeStory.title}/${Workspace.activeStory.slides[slideNumber].imageFile}")
+ tempPicFile?.delete()
+ setPic()
+ }
+ }catch (e:Exception){
+ Toast.makeText(context,"Error",Toast.LENGTH_SHORT).show()
+ Crashlytics.logException(e)
+ }
+ }
+
+ /**
+ * This function serves to handle page changes and stops the audio streams from
+ * continuing.
+ *
+ * @param isVisibleToUser whether fragment is currently visible to user
+ */
+ override fun setUserVisibleHint(isVisibleToUser: Boolean) {
+ super.setUserVisibleHint(isVisibleToUser)
+
+ // Make sure that we are currently visible
+ if (this.isVisible) {
+ // If we are becoming invisible, then...
+ if (!isVisibleToUser) {
+ if (Workspace.activeStory.slides[slideNumber].slideType != SlideType.LOCALCREDITS) {
+ recordingToolbar.stopToolbarMedia()
+ }
+ }
+ }
+ }
+
+ protected open fun setToolbar() {
+ val bundle = Bundle()
+ bundle.putInt(SLIDE_NUM, slideNumber)
+ bundle.putInt(PHASE_TYPE, phaseType.ordinal)
+ recordingToolbar.arguments = bundle
+ childFragmentManager.beginTransaction().replace(R.id.toolbar_for_recording_toolbar, recordingToolbar).commit()
+
+ recordingToolbar.keepToolbarVisible()
+ }
+
+ override fun onStartedToolbarMedia() {
+ super.onStartedToolbarMedia()
+
+ stopSlidePlayBack()
+ }
+
+ override fun onStartedSlidePlayBack() {
+ super.onStartedSlidePlayBack()
+
+ recordingToolbar.stopToolbarMedia()
+ }
+
+ companion object {
+ private const val ACTIVITY_SELECT_IMAGE = 53
+ }
+}
diff --git a/app/src/main/java/org/sil/storyproducer/controller/RegistrationActivity.kt b/app/src/main/java/org/sil/storyproducer/controller/RegistrationActivity.kt
index b03bf3ff..0284f5fe 100644
--- a/app/src/main/java/org/sil/storyproducer/controller/RegistrationActivity.kt
+++ b/app/src/main/java/org/sil/storyproducer/controller/RegistrationActivity.kt
@@ -3,33 +3,31 @@ package org.sil.storyproducer.controller
import android.Manifest
import android.app.Activity
import android.app.AlertDialog
-import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
-import android.provider.Settings.Secure
+import android.provider.Settings
import android.support.design.widget.TextInputEditText
-import android.support.design.widget.TextInputLayout
import android.support.v4.app.ActivityCompat
import android.support.v4.content.res.ResourcesCompat
import android.support.v7.app.AppCompatActivity
import android.text.Html
import android.util.Log
import android.view.View
-import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
-import android.widget.*
-import com.android.volley.Request
+import android.widget.Button
+import android.widget.EditText
+import android.widget.Spinner
+import android.widget.Toast
import com.android.volley.Response
import com.android.volley.toolbox.StringRequest
import com.crashlytics.android.Crashlytics
import org.sil.storyproducer.R
import org.sil.storyproducer.model.Workspace
import org.sil.storyproducer.tools.Network.VolleySingleton
-import java.util.*
/**
* The purpose of this class is to create the Registration activity.
@@ -42,16 +40,9 @@ import java.util.*
*
* * setAccordionListener() is called which adds click listeners to the header sections of the accordion.
*
- * 1. onPostCreate() is called and calls the following:
+ * 2. onPostCreate() is called and calls the following:
*
- * 1. setupInputFields() is called which takes a root ScrollView.
- *
- * * getInputFields() is called and takes the root ScrollView and does an in-order
- * traversal of the nodes in the registration xml to find the TextInputEditText
- * and Spinner inputs. Each TextInputEditText and Spinner inputs are added to the
- * sectionViews[] for parsing and saving.
- *
- * 1. addSubmitButtonSave() is called which only parses the TextInpuEditText(not the Spinner input) to check for valid inputs.
+ * 3. addSubmitButtonSave() is called which only parses the TextInpuEditText(not the Spinner input) to check for valid inputs.
*
* * textFieldParsed() is called. This checks to see if all fields were entered
* * A confirmation dialog is launched to ask if the user wants to submit the info
@@ -66,91 +57,266 @@ import java.util.*
* * [android.support.design.widget.TextInputEditText] for inputting text for registration fields.
*
*/
-const val FIRST_ACTIVITY_KEY = "first"
-
open class RegistrationActivity : AppCompatActivity() {
- private val sectionIds = intArrayOf(R.id.language_section, R.id.translator_section, R.id.consultant_section, R.id.trainer_section, R.id.archive_section)
- private val headerIds = intArrayOf(R.id.language_header, R.id.translator_header, R.id.consultant_header, R.id.trainer_header, R.id.archive_header)
- private val sectionViews = arrayOfNulls(sectionIds.size)
- private val headerViews = arrayOfNulls(headerIds.size)
+ private lateinit var projectLanguageEditText: TextInputEditText
+ private lateinit var projectEthnoCodeEditText: TextInputEditText
+ private lateinit var projectCountryEditText: TextInputEditText
+ private lateinit var projectRegionEditText: TextInputEditText
+ private lateinit var projectCityEditText: TextInputEditText
+ private lateinit var projectMajorityLanguageEditText: TextInputEditText
+ private lateinit var projectOrthographySpinner: Spinner
+
+ private lateinit var translatorNameEditText: TextInputEditText
+ private lateinit var translatorEducationEditText: TextInputEditText
+ private lateinit var translatorLanguagesEditText: TextInputEditText
+ private lateinit var translatorPhoneEditText: TextInputEditText
+ private lateinit var translatorEmailEditText: TextInputEditText
+ private lateinit var translatorCommunicationPreferenceSpinner: Spinner
+ private lateinit var translatorLocationEditText: TextInputEditText
+
+ private lateinit var consultantNameEditText: TextInputEditText
+ private lateinit var consultantLanguagesEditText: TextInputEditText
+ private lateinit var consultantPhoneEditText: TextInputEditText
+ private lateinit var consultantEmailEditText: TextInputEditText
+ private lateinit var consultantCommunicationPreferenceSpinner: Spinner
+ private lateinit var consultantLocationEditText: TextInputEditText
+ private lateinit var consultantLocationTypeSpinner: Spinner
+
+ private lateinit var trainerNameEditText: TextInputEditText
+ private lateinit var trainerLanguagesEditText: TextInputEditText
+ private lateinit var trainerPhoneEditText: TextInputEditText
+ private lateinit var trainerEmailEditText: TextInputEditText
+ private lateinit var trainerCommunicationPreferenceSpinner: Spinner
+ private lateinit var trainerLocationEditText: TextInputEditText
+
+ private lateinit var archiveEmail1EditText: TextInputEditText
+ private lateinit var archiveEmail2EditText: TextInputEditText
+ private lateinit var archiveEmail3EditText: TextInputEditText
+
+ private lateinit var sectionViews: Array
+ private lateinit var headerViews: Array
var resp: String? = null
var testErr = ""
- var js: MutableMap = hashMapOf()
- private var inputFields: List? = null
+ private lateinit var inputFields: Array
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- //first get permissions
- val PERMISSION_ALL = 1
- val PERMISSIONS = arrayOf(Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE)
-
- if (!hasPermissions(this, *PERMISSIONS)) {
- ActivityCompat.requestPermissions(this, PERMISSIONS, PERMISSION_ALL)
+ val permissions = arrayOf(Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE)
+ if (!hasPermissions(this, *permissions)) {
+ val permissionsAll = 1
+ ActivityCompat.requestPermissions(this, permissions, permissionsAll)
}
setContentView(R.layout.activity_registration)
- //Initialize sectionViews[] with the integer id's of the various LinearLayouts
- //Add the listeners to the LinearLayouts's header section.
- for (i in sectionIds.indices) {
- sectionViews[i] = findViewById(sectionIds[i])
- headerViews[i] = findViewById(headerIds[i])
- setAccordionListener(findViewById(headerIds[i]), sectionViews[i]!!)
+ // TODO @pwhite: Some of the ugliness could be removed while
+ // simultaneously improving user experience if some field for the
+ // translator, consultant, and trainer were put in a `Person`
+ // abstraction. The UI could reflect this be reusing the same fragment
+ // for each of the forms. Or perhaps the ROCC can serve some
+ // pre-populated people which the user can select from; this would mean
+ // that the user would not have to fill out each of the forms and
+ // potentially make a mistake with an email, for example. Of course,
+ // this ought not to be required since the application might not be
+ // online.
+
+ // @pwhite: This is a lot of boilerplate to load the current values of
+ // the registration.json file. I'm not the most pleased, but I find
+ // this far better than doing a walk of the UI tree to find all the
+ // text and spinner controls and setting them from a JSON object based
+ // on some string manipulation of the android resource IDs. This was a
+ // previous solution which has been replaced by this ugliness; although
+ // this solution is rather ugly, it is extremely clear what is
+ // happening, and quite easy to modify. In addition, this is likely
+ // more efficient than the previous solution since we know exactly what
+ // controls exist and thus don't need to walk the UI tree to find them.
+
+ // NOW @pwhite: Add phone make, model and manufacturer as before.
+ val orthographySpinnerOptions = resources.getStringArray(R.array.orthography_list)
+ val communicationSpinnerOptions = resources.getStringArray(R.array.communication_list)
+ val locationTypeSpinnerOptions = resources.getStringArray(R.array.location_type_list)
+
+ projectLanguageEditText = findViewById(R.id.input_language)
+ projectLanguageEditText.setText(Workspace.registration.projectLanguage)
+ projectEthnoCodeEditText = findViewById(R.id.input_ethnologue)
+ projectEthnoCodeEditText.setText(Workspace.registration.projectEthnoCode)
+ projectCountryEditText = findViewById(R.id.input_country)
+ projectCountryEditText.setText(Workspace.registration.projectCountry)
+ projectRegionEditText = findViewById(R.id.input_location)
+ projectRegionEditText.setText(Workspace.registration.projectRegion)
+ projectCityEditText = findViewById(R.id.input_town)
+ projectCityEditText.setText(Workspace.registration.projectCity)
+ projectMajorityLanguageEditText = findViewById(R.id.input_lwc)
+ projectMajorityLanguageEditText.setText(Workspace.registration.projectMajorityLanguage)
+ projectOrthographySpinner = findViewById(R.id.input_orthography)
+ projectOrthographySpinner.setSelection(
+ indexOfOrZero(orthographySpinnerOptions, Workspace.registration.projectOrthography))
+
+ translatorNameEditText = findViewById(R.id.input_translator_name)
+ translatorNameEditText.setText(Workspace.registration.translatorName)
+ translatorEducationEditText = findViewById(R.id.input_translator_education)
+ translatorEducationEditText.setText(Workspace.registration.translatorEducation)
+ translatorLanguagesEditText = findViewById(R.id.input_translator_languages)
+ translatorLanguagesEditText.setText(Workspace.registration.translatorLanguages)
+ translatorPhoneEditText = findViewById(R.id.input_translator_phone)
+ translatorPhoneEditText.setText(Workspace.registration.translatorPhone)
+ translatorEmailEditText = findViewById(R.id.input_translator_email)
+ translatorEmailEditText.setText(Workspace.registration.translatorEmail)
+ translatorCommunicationPreferenceSpinner = findViewById(R.id.input_translator_communication_preference)
+ translatorCommunicationPreferenceSpinner.setSelection(
+ indexOfOrZero(communicationSpinnerOptions, Workspace.registration.translatorCommunicationPreference))
+ translatorLocationEditText = findViewById(R.id.input_translator_location)
+ translatorLocationEditText.setText(Workspace.registration.translatorLocation)
+
+ consultantNameEditText = findViewById(R.id.input_consultant_name)
+ consultantNameEditText.setText(Workspace.registration.consultantName)
+ consultantLanguagesEditText = findViewById(R.id.input_consultant_languages)
+ consultantLanguagesEditText.setText(Workspace.registration.consultantLanguages)
+ consultantPhoneEditText = findViewById(R.id.input_consultant_phone)
+ consultantPhoneEditText.setText(Workspace.registration.consultantPhone)
+ consultantEmailEditText = findViewById(R.id.input_consultant_email)
+ consultantEmailEditText.setText(Workspace.registration.consultantEmail)
+ consultantCommunicationPreferenceSpinner = findViewById(R.id.input_consultant_communication_preference)
+ consultantCommunicationPreferenceSpinner.setSelection(
+ indexOfOrZero(communicationSpinnerOptions, Workspace.registration.consultantCommunicationPreference))
+ consultantLocationEditText = findViewById(R.id.input_consultant_location)
+ consultantLocationEditText.setText(Workspace.registration.consultantLocation)
+ consultantLocationTypeSpinner = findViewById(R.id.input_consultant_location_type)
+ consultantLocationTypeSpinner.setSelection(
+ indexOfOrZero(locationTypeSpinnerOptions, Workspace.registration.consultantLocationType))
+
+ trainerNameEditText = findViewById(R.id.input_trainer_name)
+ trainerNameEditText.setText(Workspace.registration.trainerName)
+ trainerLanguagesEditText = findViewById(R.id.input_trainer_languages)
+ trainerLanguagesEditText.setText(Workspace.registration.trainerLanguages)
+ trainerPhoneEditText = findViewById(R.id.input_trainer_phone)
+ trainerPhoneEditText.setText(Workspace.registration.trainerPhone)
+ trainerEmailEditText = findViewById(R.id.input_trainer_email)
+ trainerEmailEditText.setText(Workspace.registration.trainerEmail)
+ trainerCommunicationPreferenceSpinner = findViewById(R.id.input_trainer_communication_preference)
+ trainerCommunicationPreferenceSpinner.setSelection(
+ indexOfOrZero(communicationSpinnerOptions, Workspace.registration.trainerCommunicationPreference))
+ trainerLocationEditText = findViewById(R.id.input_trainer_location)
+ trainerLocationEditText.setText(Workspace.registration.trainerLocation)
+
+ archiveEmail1EditText = findViewById(R.id.input_database_email_1)
+ archiveEmail1EditText.setText(Workspace.registration.archiveEmail1)
+ archiveEmail2EditText = findViewById(R.id.input_database_email_2)
+ archiveEmail2EditText.setText(Workspace.registration.archiveEmail2)
+ archiveEmail3EditText = findViewById(R.id.input_database_email_3)
+ archiveEmail3EditText.setText(Workspace.registration.archiveEmail3)
+
+ inputFields = arrayOf(
+ projectLanguageEditText,
+ projectEthnoCodeEditText,
+ projectCountryEditText,
+ projectRegionEditText,
+ projectCityEditText,
+ projectMajorityLanguageEditText,
+ projectOrthographySpinner,
+ translatorNameEditText,
+ translatorEducationEditText,
+ translatorLanguagesEditText,
+ translatorPhoneEditText,
+ translatorEmailEditText,
+ translatorCommunicationPreferenceSpinner,
+ translatorLocationEditText,
+ consultantNameEditText,
+ consultantLanguagesEditText,
+ consultantPhoneEditText,
+ consultantEmailEditText,
+ consultantCommunicationPreferenceSpinner,
+ consultantLocationEditText,
+ consultantLocationTypeSpinner,
+ trainerNameEditText,
+ trainerLanguagesEditText,
+ trainerPhoneEditText,
+ trainerEmailEditText,
+ trainerCommunicationPreferenceSpinner,
+ trainerLocationEditText,
+ archiveEmail1EditText,
+ archiveEmail2EditText,
+ archiveEmail3EditText)
+
+ // Initialize sectionViews[] with the integer id's of the various LinearLayouts
+ // Add the listeners to the LinearLayouts's header section.
+
+ sectionViews = arrayOf(
+ findViewById(R.id.language_section),
+ findViewById(R.id.translator_section),
+ findViewById(R.id.consultant_section),
+ findViewById(R.id.trainer_section),
+ findViewById(R.id.archive_section))
+ headerViews = arrayOf(
+ findViewById(R.id.language_header),
+ findViewById(R.id.translator_header),
+ findViewById(R.id.consultant_header),
+ findViewById(R.id.trainer_header),
+ findViewById(R.id.archive_header))
+
+ for (i in sectionViews.indices) {
+ setAccordionListener(headerViews[i], sectionViews[i])
}
}
- override fun onPause(){
+ fun indexOfOrZero(arr: Array, string: String): Int {
+ return maxOf(0, arr.indexOf(string))
+ }
+
+ override fun onPause() {
super.onPause()
storeRegistrationInfo()
}
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
- setupInputFields()
addSubmitButtonSave()
addRegistrationSkip()
addEthnologueQuestion()
}
- /**
- * Initializes the inputFields to the inputs of this activity.
- */
- private fun setupInputFields() {
- val view = findViewById(R.id.registration_scroll_view)
- inputFields = getInputFields(view)
- }
-
/**
* Sets the on click listener for the submit button.
*/
private fun addSubmitButtonSave() {
val submitButton = findViewById