From 0974492fb5d67d961024ae61caee8598806e6704 Mon Sep 17 00:00:00 2001 From: Wojciech Piwonski Date: Tue, 7 Aug 2018 11:41:46 +0200 Subject: [PATCH 01/27] New sample app without theme support, shopping items and info --- sample/build.gradle | 10 +- sample/cockpit/cockpit.yml | 56 ++++++--- sample/cockpit/cockpitBlue.yml | 10 +- sample/cockpit/cockpitDebug.yml | 1 - sample/cockpit/cockpitProd.yml | 3 - sample/cockpit/cockpitRed.yml | 11 +- sample/cockpit/cockpitStaging.yml | 3 - sample/cockpit/cockpitStagingRedDebug.yml | 1 - .../sample/sampleparams/SampleContract.kt | 2 - .../sample/sampleparams/SampleFragment.kt | 12 +- .../sample/sampleparams/SamplePresenter.kt | 81 ++++++------ .../layout/include_edit_values_container.xml | 19 --- sample/src/main/AndroidManifest.xml | 8 +- .../polidea/cockpit/sample/SampleActivity.kt | 4 +- .../sample/sampleparams/SampleBaseContract.kt | 10 +- .../sample/sampleparams/SampleBaseFragment.kt | 26 ++-- .../sampleparams/SampleBasePresenter.kt | 7 +- .../res/drawable/blue_theme_background.xml | 8 ++ .../src/main/res/drawable/v_ic_info_icon.xml | 5 + .../src/main/res/layout/activity_sample.xml | 8 +- .../src/main/res/layout/fragment_sample.xml | 58 --------- .../res/layout/fragment_shopping_cart.xml | 115 ++++++++++++++++++ sample/src/main/res/values/colors.xml | 9 ++ sample/src/main/res/values/dimens.xml | 12 +- sample/src/main/res/values/strings.xml | 4 + sample/src/main/res/values/styles.xml | 48 ++++++++ .../layout/include_edit_values_container.xml | 6 - 27 files changed, 311 insertions(+), 226 deletions(-) delete mode 100644 sample/cockpit/cockpitDebug.yml delete mode 100644 sample/cockpit/cockpitProd.yml delete mode 100644 sample/cockpit/cockpitStaging.yml delete mode 100644 sample/cockpit/cockpitStagingRedDebug.yml delete mode 100644 sample/src/debug/res/layout/include_edit_values_container.xml create mode 100644 sample/src/main/res/drawable/blue_theme_background.xml create mode 100644 sample/src/main/res/drawable/v_ic_info_icon.xml delete mode 100644 sample/src/main/res/layout/fragment_sample.xml create mode 100644 sample/src/main/res/layout/fragment_shopping_cart.xml delete mode 100644 sample/src/release/res/layout/include_edit_values_container.xml diff --git a/sample/build.gradle b/sample/build.gradle index cd4f015..0e167dc 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -50,7 +50,7 @@ android { } } - flavorDimensions "api", "theme" + flavorDimensions "theme" productFlavors { red { @@ -63,14 +63,6 @@ android { applicationIdSuffix ".blue" versionNameSuffix "-blue" } - prod { - dimension "api" - applicationIdSuffix ".prod" - } - staging { - dimension "api" - applicationIdSuffix ".staging" - } } } diff --git a/sample/cockpit/cockpit.yml b/sample/cockpit/cockpit.yml index 6ec4a4b..1b342a5 100644 --- a/sample/cockpit/cockpit.yml +++ b/sample/cockpit/cockpit.yml @@ -1,21 +1,39 @@ -color: - type: color - description: "Text color" - value: "#00ff33" -colorDescription: "Calm green" -fontSize: - type: "range" - min: 5 - max: 50 - value: 30 +style: + type: list + description: "Choose style" + values: ["Red", "Blue"] + group: "Theme" + +animationSpeed: + description: "Animation speed" + value: 500 + group: "Checkout Button" + +totalPriceFontSize: + type: range + description: "Total price font size" + min: 20 + max: 60 step: 1 - description: "Font size" -footer: "Created by Polidea" -showFooter: true -version: + value: 36 + group: "Cart" +resetCount: type: action - description: "About application" - buttonText: "Show" -fontList: - type: list - values: [ "sans-serif", "sans-serif-thin", "sans-serif-medium", "sans-serif-condensed" ] + description: "Set item count to \"0\"" + buttonText: "Reset" + group: "Cart" + +headingText: + description: "Heading text" + value: "Shopping cart" + group: "Heading" + +footerFontColor: + type: color + description: "Font color" + value: "#FFFFFF" + group: "Footer" +showFooter: + description: "Show footer" + value: true + group: "Footer" diff --git a/sample/cockpit/cockpitBlue.yml b/sample/cockpit/cockpitBlue.yml index 430323a..cd75dda 100644 --- a/sample/cockpit/cockpitBlue.yml +++ b/sample/cockpit/cockpitBlue.yml @@ -1,6 +1,4 @@ -color: - type: color - description: "Text color" - value: "#2C75FF" -colorDescription: "Electric blue" -footer: "Created with 💙 by Polidea" +footer: + description: "Footer" + value: "Created with 💙 by Polidea" + group: "Footer" diff --git a/sample/cockpit/cockpitDebug.yml b/sample/cockpit/cockpitDebug.yml deleted file mode 100644 index f8b676d..0000000 --- a/sample/cockpit/cockpitDebug.yml +++ /dev/null @@ -1 +0,0 @@ -debugDescription: "Debug" diff --git a/sample/cockpit/cockpitProd.yml b/sample/cockpit/cockpitProd.yml deleted file mode 100644 index 8e98d8c..0000000 --- a/sample/cockpit/cockpitProd.yml +++ /dev/null @@ -1,3 +0,0 @@ -baseUrl: - description: "Base API url" - value: "https://base.prod.url" diff --git a/sample/cockpit/cockpitRed.yml b/sample/cockpit/cockpitRed.yml index 256f8ab..c4ba216 100644 --- a/sample/cockpit/cockpitRed.yml +++ b/sample/cockpit/cockpitRed.yml @@ -1,7 +1,4 @@ -color: - type: color - description: "Text color" - value: "#ff0033" -colorDescription: "Torch red" -footer: "Created with ❤️ by Polidea" -showFooter: true +footer: + description: "Footer" + value: "Created with ❤️ by Polidea" + group: "Footer" diff --git a/sample/cockpit/cockpitStaging.yml b/sample/cockpit/cockpitStaging.yml deleted file mode 100644 index 5191c29..0000000 --- a/sample/cockpit/cockpitStaging.yml +++ /dev/null @@ -1,3 +0,0 @@ -baseUrl: - description: "Base API url" - value: "https://base.staging.url" diff --git a/sample/cockpit/cockpitStagingRedDebug.yml b/sample/cockpit/cockpitStagingRedDebug.yml deleted file mode 100644 index 0f407df..0000000 --- a/sample/cockpit/cockpitStagingRedDebug.yml +++ /dev/null @@ -1 +0,0 @@ -footer: "Created with hot ❤️ by Polidea" diff --git a/sample/src/debug/java/com/polidea/cockpit/sample/sampleparams/SampleContract.kt b/sample/src/debug/java/com/polidea/cockpit/sample/sampleparams/SampleContract.kt index 92d959c..39b0126 100644 --- a/sample/src/debug/java/com/polidea/cockpit/sample/sampleparams/SampleContract.kt +++ b/sample/src/debug/java/com/polidea/cockpit/sample/sampleparams/SampleContract.kt @@ -3,8 +3,6 @@ package com.polidea.cockpit.sample.sampleparams interface SampleContract { interface View : SampleBaseContract.View { - fun setDebugDescription(description: String) - fun showCockpitUi() fun showMessage(message: String) diff --git a/sample/src/debug/java/com/polidea/cockpit/sample/sampleparams/SampleFragment.kt b/sample/src/debug/java/com/polidea/cockpit/sample/sampleparams/SampleFragment.kt index 002e0a2..4ec1538 100644 --- a/sample/src/debug/java/com/polidea/cockpit/sample/sampleparams/SampleFragment.kt +++ b/sample/src/debug/java/com/polidea/cockpit/sample/sampleparams/SampleFragment.kt @@ -4,19 +4,13 @@ import android.content.Context import android.hardware.SensorManager import android.os.Bundle import android.view.View -import android.widget.TextView import android.widget.Toast import com.polidea.cockpit.cockpit.Cockpit -import com.polidea.cockpit.sample.R import com.squareup.seismic.ShakeDetector -import kotlinx.android.synthetic.main.fragment_sample.* class SampleFragment : SampleBaseFragment(), SampleContract.View, ShakeDetector.Listener { - override lateinit var presenter: SampleContract.Presenter - private val debugDescriptionView: TextView by lazy { - variant_include_container.findViewById(R.id.cockpit_debug_textview) - } + override lateinit var presenter: SampleContract.Presenter private val shakeDetector = ShakeDetector(this) @@ -29,10 +23,6 @@ class SampleFragment : SampleBaseFragment(), SampleCon shakeDetector.start(sensorManager) } - override fun setDebugDescription(description: String) { - debugDescriptionView.text = description - } - override fun showCockpitUi() { Cockpit.showCockpit(fragmentManager) } diff --git a/sample/src/debug/java/com/polidea/cockpit/sample/sampleparams/SamplePresenter.kt b/sample/src/debug/java/com/polidea/cockpit/sample/sampleparams/SamplePresenter.kt index eecf004..6a08f58 100644 --- a/sample/src/debug/java/com/polidea/cockpit/sample/sampleparams/SamplePresenter.kt +++ b/sample/src/debug/java/com/polidea/cockpit/sample/sampleparams/SamplePresenter.kt @@ -1,24 +1,22 @@ package com.polidea.cockpit.sample.sampleparams import android.graphics.Color -import android.graphics.Typeface import com.polidea.cockpit.cockpit.Cockpit import com.polidea.cockpit.event.ActionRequestCallback import com.polidea.cockpit.event.PropertyChangeListener import com.polidea.cockpit.event.SelectionChangeListener -import com.polidea.cockpit.sample.BuildConfig class SamplePresenter(override val sampleView: SampleContract.View) : SampleBasePresenter(sampleView), SampleContract.Presenter { - private lateinit var onColorChangeListener: PropertyChangeListener - private lateinit var onFontSizeChangeListener: PropertyChangeListener - private lateinit var onColorDescriptionChangeListener: PropertyChangeListener - private lateinit var onFooterChangeListener: PropertyChangeListener + private lateinit var styleSelectedListener: SelectionChangeListener + private lateinit var onAnimationSpeedChangeListener: PropertyChangeListener + private lateinit var onTotalPriceFontSizeChangeListener: PropertyChangeListener + private lateinit var resetCountCallback: ActionRequestCallback + private lateinit var onHeadingTextChangeListener: PropertyChangeListener + private lateinit var onFooterColorChangeListener: PropertyChangeListener private lateinit var onShowFooterChangeListener: PropertyChangeListener - private lateinit var onDebugDescriptionChangeListener: PropertyChangeListener - private lateinit var versionActionRequestCallback: ActionRequestCallback - private lateinit var fontSelectedListener: SelectionChangeListener + private lateinit var onFooterChangeListener: PropertyChangeListener init { sampleView.presenter = this @@ -32,8 +30,10 @@ class SamplePresenter(override val sampleView: SampleContract.View) } private fun setCallbacks() { - versionActionRequestCallback = ActionRequestCallback { sampleView.showMessage(BuildConfig.VERSION_NAME) } - Cockpit.addVersionActionRequestCallback(versionActionRequestCallback) + resetCountCallback = ActionRequestCallback { + // TODO: implement + } + Cockpit.addResetCountActionRequestCallback(resetCountCallback) } override fun stop() { @@ -43,67 +43,62 @@ class SamplePresenter(override val sampleView: SampleContract.View) } private fun removeCallbacks() { - Cockpit.removeVersionActionRequestCallback(versionActionRequestCallback) + Cockpit.removeResetCountActionRequestCallback(resetCountCallback) } private fun removeOnValueSelectedListeners() { - Cockpit.removeFontListSelectionChangeListener(fontSelectedListener) + Cockpit.removeStyleSelectionChangeListener(styleSelectedListener) } override fun shakeDetected() { sampleView.showCockpitUi() } - override fun initViews() { - super.initViews() - sampleView.setDebugDescription(Cockpit.getDebugDescription()) - } - private fun setOnValueSelectedListener() { - fontSelectedListener = SelectionChangeListener { selectedValue -> - sampleView.setTypeface(Typeface.create(selectedValue, Typeface.NORMAL)) + styleSelectedListener = SelectionChangeListener { selectedValue -> + // TODO: implement } - Cockpit.addFontListSelectionChangeListener(fontSelectedListener) + Cockpit.addStyleSelectionChangeListener(styleSelectedListener) } private fun setOnChangeListeners() { - onColorChangeListener = PropertyChangeListener { _, newColor -> - sampleView.setTextColor(Color.parseColor(newColor)) + onAnimationSpeedChangeListener = PropertyChangeListener { _, newValue -> + // TODO: implement } - Cockpit.addOnColorChangeListener(onColorChangeListener) + Cockpit.addOnAnimationSpeedChangeListener(onAnimationSpeedChangeListener) - onFontSizeChangeListener = PropertyChangeListener { _, newSize -> - sampleView.setFontSize(newSize.toFloat()) + onTotalPriceFontSizeChangeListener = PropertyChangeListener { _, newValue -> + sampleView.setTotalPriceFontSize(newValue.toFloat()) } - Cockpit.addOnFontSizeChangeListener(onFontSizeChangeListener) + Cockpit.addOnTotalPriceFontSizeChangeListener(onTotalPriceFontSizeChangeListener) - onColorDescriptionChangeListener = PropertyChangeListener { _, newColorDescription -> - sampleView.setColorDescription(newColorDescription) + onHeadingTextChangeListener = PropertyChangeListener { _, newValue -> + sampleView.setHeadingText(newValue) } - Cockpit.addOnColorDescriptionChangeListener(onColorDescriptionChangeListener) + Cockpit.addOnHeadingTextChangeListener(onHeadingTextChangeListener) - onFooterChangeListener = PropertyChangeListener { _, newFooter -> - sampleView.setFooterText(newFooter) + onFooterColorChangeListener = PropertyChangeListener { _, newValue -> + sampleView.setFooterTextColor(Color.parseColor(newValue)) } - Cockpit.addOnFooterChangeListener(onFooterChangeListener) + Cockpit.addOnFooterFontColorChangeListener(onFooterColorChangeListener) - onShowFooterChangeListener = PropertyChangeListener { _, isFooterVisible -> - sampleView.showFooter(isFooterVisible) + onShowFooterChangeListener = PropertyChangeListener { _, newValue -> + sampleView.showFooter(newValue) } Cockpit.addOnShowFooterChangeListener(onShowFooterChangeListener) - onDebugDescriptionChangeListener = PropertyChangeListener { _, newDescription -> - sampleView.setDebugDescription(newDescription) + onFooterChangeListener = PropertyChangeListener { _, newValue -> + sampleView.setFooterText(newValue) } - Cockpit.addOnDebugDescriptionChangeListener(onDebugDescriptionChangeListener) + Cockpit.addOnFooterChangeListener(onFooterChangeListener) } private fun removeOnChangeListeners() { - Cockpit.removeOnColorChangeListener(onColorChangeListener) - Cockpit.removeOnFontSizeChangeListener(onFontSizeChangeListener) - Cockpit.removeOnColorDescriptionChangeListener(onColorDescriptionChangeListener) - Cockpit.removeOnFooterChangeListener(onFooterChangeListener) + Cockpit.removeOnAnimationSpeedChangeListener(onAnimationSpeedChangeListener) + Cockpit.removeOnTotalPriceFontSizeChangeListener(onTotalPriceFontSizeChangeListener) + Cockpit.removeOnHeadingTextChangeListener(onHeadingTextChangeListener) + Cockpit.removeOnFooterFontColorChangeListener(onFooterColorChangeListener) Cockpit.removeOnShowFooterChangeListener(onShowFooterChangeListener) - Cockpit.removeOnDebugDescriptionChangeListener(onDebugDescriptionChangeListener) + Cockpit.removeOnFooterChangeListener(onFooterChangeListener) } } \ No newline at end of file diff --git a/sample/src/debug/res/layout/include_edit_values_container.xml b/sample/src/debug/res/layout/include_edit_values_container.xml deleted file mode 100644 index 11c8313..0000000 --- a/sample/src/debug/res/layout/include_edit_values_container.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index 840eec1..b82da64 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -8,12 +8,14 @@ android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> + + android:screenOrientation="portrait" + android:theme="@style/AppTheme.NoActionBar"> - + - + diff --git a/sample/src/main/java/com/polidea/cockpit/sample/SampleActivity.kt b/sample/src/main/java/com/polidea/cockpit/sample/SampleActivity.kt index 105a3fe..13feb85 100644 --- a/sample/src/main/java/com/polidea/cockpit/sample/SampleActivity.kt +++ b/sample/src/main/java/com/polidea/cockpit/sample/SampleActivity.kt @@ -2,16 +2,18 @@ package com.polidea.cockpit.sample import android.os.Bundle import android.support.v7.app.AppCompatActivity +import android.view.Menu +import android.view.MenuItem import com.polidea.cockpit.sample.sampleparams.SampleFragment import com.polidea.cockpit.sample.sampleparams.SamplePresenter import com.polidea.cockpit.sample.util.replaceFragmentInActivity +import kotlinx.android.synthetic.main.activity_sample.* class SampleActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_sample) - val sampleFragment = supportFragmentManager.findFragmentById(R.id.contentFrame) as SampleFragment? ?: SampleFragment.newInstance().also { replaceFragmentInActivity(it, R.id.contentFrame) diff --git a/sample/src/main/java/com/polidea/cockpit/sample/sampleparams/SampleBaseContract.kt b/sample/src/main/java/com/polidea/cockpit/sample/sampleparams/SampleBaseContract.kt index 7cb9c86..6f7d866 100644 --- a/sample/src/main/java/com/polidea/cockpit/sample/sampleparams/SampleBaseContract.kt +++ b/sample/src/main/java/com/polidea/cockpit/sample/sampleparams/SampleBaseContract.kt @@ -1,6 +1,5 @@ package com.polidea.cockpit.sample.sampleparams -import android.graphics.Typeface import android.support.annotation.ColorInt import com.polidea.cockpit.sample.BasePresenter import com.polidea.cockpit.sample.BaseView @@ -12,13 +11,10 @@ interface SampleBaseContract { fun setFooterText(footerText: String) - fun setTextColor(@ColorInt color: Int) + fun setFooterTextColor(@ColorInt color: Int) - fun setFontSize(textSize: Float) - - fun setColorDescription(description: String) - - fun setTypeface(typeface: Typeface) + fun setTotalPriceFontSize(textSize: Float) + fun setHeadingText(headingText: String) } interface Presenter : BasePresenter diff --git a/sample/src/main/java/com/polidea/cockpit/sample/sampleparams/SampleBaseFragment.kt b/sample/src/main/java/com/polidea/cockpit/sample/sampleparams/SampleBaseFragment.kt index 8327282..02985c9 100644 --- a/sample/src/main/java/com/polidea/cockpit/sample/sampleparams/SampleBaseFragment.kt +++ b/sample/src/main/java/com/polidea/cockpit/sample/sampleparams/SampleBaseFragment.kt @@ -1,6 +1,5 @@ package com.polidea.cockpit.sample.sampleparams -import android.graphics.Typeface import android.os.Bundle import android.support.v4.app.Fragment import android.util.TypedValue @@ -8,37 +7,32 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import com.polidea.cockpit.sample.R -import kotlinx.android.synthetic.main.fragment_sample.* +import kotlinx.android.synthetic.main.fragment_shopping_cart.* abstract class SampleBaseFragment : Fragment(), SampleBaseContract.View { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = - inflater.inflate(R.layout.fragment_sample, container, false) + inflater.inflate(R.layout.fragment_shopping_cart, container, false) override fun showFooter(isVisible: Boolean) { footer_container.visibility = if (isVisible) View.VISIBLE else View.INVISIBLE } - override fun setTextColor(color: Int) { - cockpit_textview.setTextColor(color) - cockpit_color_textview.setTextColor(color) - } - - override fun setFontSize(textSize: Float) { - cockpit_textview.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize) + override fun setFooterText(footerText: String) { + footer_text_view.text = footerText } - override fun setColorDescription(description: String) { - cockpit_color_textview.text = description + override fun setFooterTextColor(color: Int) { + footer_text_view.setTextColor(color) } - override fun setFooterText(footerText: String) { - footer_text_view.text = footerText + override fun setTotalPriceFontSize(textSize: Float) { + total_price.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize) } - override fun setTypeface(typeface: Typeface) { - cockpit_textview.typeface = typeface + override fun setHeadingText(headingText: String) { + heading_text_view.text = headingText } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { diff --git a/sample/src/main/java/com/polidea/cockpit/sample/sampleparams/SampleBasePresenter.kt b/sample/src/main/java/com/polidea/cockpit/sample/sampleparams/SampleBasePresenter.kt index de51684..683b224 100644 --- a/sample/src/main/java/com/polidea/cockpit/sample/sampleparams/SampleBasePresenter.kt +++ b/sample/src/main/java/com/polidea/cockpit/sample/sampleparams/SampleBasePresenter.kt @@ -14,11 +14,10 @@ abstract class SampleBasePresenter(open val sampleView: SampleBaseContract.View< } protected open fun initViews() { - sampleView.setColorDescription(Cockpit.getColorDescription()) + sampleView.setTotalPriceFontSize(Cockpit.getTotalPriceFontSize().toFloat()) + sampleView.setHeadingText(Cockpit.getHeadingText()) sampleView.setFooterText(Cockpit.getFooter()) - sampleView.setFontSize(Cockpit.getFontSize().toFloat()) + sampleView.setFooterTextColor(Color.parseColor(Cockpit.getFooterFontColor())) sampleView.showFooter(true) - sampleView.setTypeface(Typeface.create(Cockpit.getFontListSelectedValue(), Typeface.NORMAL)) - sampleView.setTextColor(Color.parseColor(Cockpit.getColor())) } } \ No newline at end of file diff --git a/sample/src/main/res/drawable/blue_theme_background.xml b/sample/src/main/res/drawable/blue_theme_background.xml new file mode 100644 index 0000000..d7e98e3 --- /dev/null +++ b/sample/src/main/res/drawable/blue_theme_background.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/sample/src/main/res/drawable/v_ic_info_icon.xml b/sample/src/main/res/drawable/v_ic_info_icon.xml new file mode 100644 index 0000000..fbf6fe4 --- /dev/null +++ b/sample/src/main/res/drawable/v_ic_info_icon.xml @@ -0,0 +1,5 @@ + + + + diff --git a/sample/src/main/res/layout/activity_sample.xml b/sample/src/main/res/layout/activity_sample.xml index 8040d54..971ca00 100644 --- a/sample/src/main/res/layout/activity_sample.xml +++ b/sample/src/main/res/layout/activity_sample.xml @@ -1,14 +1,14 @@ - + android:layout_height="match_parent"/> - + \ No newline at end of file diff --git a/sample/src/main/res/layout/fragment_sample.xml b/sample/src/main/res/layout/fragment_sample.xml deleted file mode 100644 index 090fdd7..0000000 --- a/sample/src/main/res/layout/fragment_sample.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/sample/src/main/res/layout/fragment_shopping_cart.xml b/sample/src/main/res/layout/fragment_shopping_cart.xml new file mode 100644 index 0000000..bf5a56c --- /dev/null +++ b/sample/src/main/res/layout/fragment_shopping_cart.xml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + +