Преглед на файлове

Merge branch 'cover-editor'

Wayne преди 6 години
родител
ревизия
815d5b08e9

+ 5 - 0
src/main/java/com/bomostory/sceneeditmodule/basicdata/Project.kt

@@ -1,5 +1,7 @@
 package com.bomostory.sceneeditmodule.basicdata
 
+import com.bomostory.sceneeditmodule.cover.Category
+import com.bomostory.sceneeditmodule.cover.CoverColor
 import com.example.bomocloud.theme.ThemeAssetIndex
 import java.io.File
 
@@ -13,6 +15,9 @@ data class Project (
         var assetFolder: File,
         var name: String? = null,
         var author: String? = null,
+        var frontCoverColor: CoverColor = CoverColor.Color1,
+        var backCoverColor: CoverColor = CoverColor.Color1,
+        var category: Category = Category.Others,
     // TODO : Remove this
         var scene1: String? = null,
     // TODO : Remove this

+ 38 - 0
src/main/java/com/bomostory/sceneeditmodule/cover/BackCoverEditorView.kt

@@ -0,0 +1,38 @@
+package com.bomostory.sceneeditmodule.cover
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.View
+import com.example.tfat.myapplication.R
+import kotlinx.android.synthetic.main.view_back_cover_editor.view.*
+
+class BackCoverEditorView @JvmOverloads constructor(
+        context: Context,
+        attrs: AttributeSet? = null,
+        defStyleAttr: Int = 0
+) : BaseCoverEditorView(context, attrs, defStyleAttr) {
+
+    var onCancel: View.OnClickListener? = null
+    var onSave: View.OnClickListener? = null
+    var selectedColor: CoverColor
+        get() {
+            return coverColorSelectorView_backCoverEditor.selectedColor
+        }
+        set(value) {
+            coverColorSelectorView_backCoverEditor.selectedColor = value
+        }
+
+
+    init {
+        View.inflate(context, R.layout.view_back_cover_editor, this)
+        coverColorSelectorView_backCoverEditor.apply {
+            onChangeListener = object : CoverColorSelectorView.OnChangeListener {
+                override fun onChange() {
+                    setCoverColor(selectedColor, this@BackCoverEditorView.iv_backCoverEditor_cover)
+                }
+            }
+        }
+        btn_backCoverEditor_save.setOnClickListener { onSave?.onClick(it) }
+        btn_backCoverEditor_cancel.setOnClickListener { onCancel?.onClick(it) }
+    }
+}

+ 20 - 0
src/main/java/com/bomostory/sceneeditmodule/cover/BaseCoverEditorView.kt

@@ -0,0 +1,20 @@
+package com.bomostory.sceneeditmodule.cover
+
+import android.content.Context
+import android.graphics.drawable.ColorDrawable
+import android.support.constraint.ConstraintLayout
+import android.util.AttributeSet
+import android.widget.ImageView
+
+abstract open class BaseCoverEditorView @JvmOverloads constructor(
+        context: Context,
+        attrs: AttributeSet? = null,
+        defStyleAttr: Int = 0
+) : ConstraintLayout(context, attrs, defStyleAttr) {
+
+    protected fun setCoverColor(coverColor: CoverColor, cover: ImageView) {
+        val colorInt = coverColor.getColor(context)
+        val colorDrawable = ColorDrawable(colorInt)
+        cover.setImageDrawable(colorDrawable)
+    }
+}

+ 18 - 0
src/main/java/com/bomostory/sceneeditmodule/cover/Category.kt

@@ -0,0 +1,18 @@
+package com.bomostory.sceneeditmodule.cover
+
+import com.example.tfat.myapplication.R
+
+enum class Category(val id: Int, val stringResId: Int) {
+    Daily(1, R.string.category_item_daily),
+    HumanRelations(2, R.string.category_item_human_relations),
+    Character(3, R.string.category_item_character),
+    Ispirational(4, R.string.category_item_ispirational),
+    Knowledge(5, R.string.category_item_knowledge),
+    Natural(6, R.string.category_item_natural),
+    EnvironmentalProtection(7, R.string.category_item_environmental_protection),
+    Imagine(8, R.string.category_item_imagine),
+    Life(9, R.string.category_item_life),
+    Culture(10, R.string.category_item_culture),
+    Fables(11, R.string.category_item_fables),
+    Others(12, R.string.category_item_others),
+}

+ 21 - 0
src/main/java/com/bomostory/sceneeditmodule/cover/CoverColor.kt

@@ -0,0 +1,21 @@
+package com.bomostory.sceneeditmodule.cover
+
+import android.content.Context
+import com.example.tfat.myapplication.R
+
+enum class CoverColor(val colorResId: Int) {
+    Color1(R.color.coverColor_1),
+    Color2(R.color.coverColor_2),
+    Color3(R.color.coverColor_3),
+    Color4(R.color.coverColor_4),
+    Color5(R.color.coverColor_5),
+    Color6(R.color.coverColor_6),
+    Color7(R.color.coverColor_7),
+    Color8(R.color.coverColor_8),
+    Color9(R.color.coverColor_9),
+    Color10(R.color.coverColor_10);
+
+    fun getColor(context: Context): Int {
+        return context.resources.getColor(colorResId)
+    }
+}

+ 91 - 0
src/main/java/com/bomostory/sceneeditmodule/cover/CoverColorSelectorView.kt

@@ -0,0 +1,91 @@
+package com.bomostory.sceneeditmodule.cover
+
+import android.content.Context
+import android.support.constraint.ConstraintLayout
+import android.util.AttributeSet
+import android.view.View
+import com.example.tfat.myapplication.R
+
+class CoverColorSelectorView @JvmOverloads constructor(
+        context: Context,
+        attrs: AttributeSet? = null,
+        defStyleAttr: Int = 0
+) : ConstraintLayout(context, attrs, defStyleAttr) {
+
+    private val viewToColorMap = HashMap<Int, CoverColor>().apply {
+        put(R.id.iv_coverColorSelector_color1, CoverColor.Color1)
+        put(R.id.iv_coverColorSelector_color2, CoverColor.Color2)
+        put(R.id.iv_coverColorSelector_color3, CoverColor.Color3)
+        put(R.id.iv_coverColorSelector_color4, CoverColor.Color4)
+        put(R.id.iv_coverColorSelector_color5, CoverColor.Color5)
+        put(R.id.iv_coverColorSelector_color6, CoverColor.Color6)
+        put(R.id.iv_coverColorSelector_color7, CoverColor.Color7)
+        put(R.id.iv_coverColorSelector_color8, CoverColor.Color8)
+        put(R.id.iv_coverColorSelector_color9, CoverColor.Color9)
+        put(R.id.iv_coverColorSelector_color10, CoverColor.Color10)
+    }
+
+    private val colorToViewMap = HashMap<CoverColor, Int>().apply {
+        put(CoverColor.Color1, R.id.iv_coverColorSelector_color1)
+        put(CoverColor.Color2, R.id.iv_coverColorSelector_color2)
+        put(CoverColor.Color3, R.id.iv_coverColorSelector_color3)
+        put(CoverColor.Color4, R.id.iv_coverColorSelector_color4)
+        put(CoverColor.Color5, R.id.iv_coverColorSelector_color5)
+        put(CoverColor.Color6, R.id.iv_coverColorSelector_color6)
+        put(CoverColor.Color7, R.id.iv_coverColorSelector_color7)
+        put(CoverColor.Color8, R.id.iv_coverColorSelector_color8)
+        put(CoverColor.Color9, R.id.iv_coverColorSelector_color9)
+        put(CoverColor.Color10, R.id.iv_coverColorSelector_color10)
+    }
+
+    private val colorViewList = ArrayList<View>()
+
+    init {
+        inflate(context, R.layout.view_cover_color_selector, this)
+        viewToColorMap.keys.forEach { ivId ->
+            val v = findViewById<View>(ivId)
+            v.setOnClickListener { it ->
+                viewToColorMap[it.id]?.let { color -> selectedColor = color }
+            }
+            colorViewList.add(v)
+        }
+    }
+
+    var selectedColor: CoverColor = CoverColor.Color1
+        set(value) {
+            field = value
+            select(value)
+            onChangeListener?.onChange()
+        }
+    var onChangeListener: OnChangeListener? = null
+
+    private fun select(coverColor: CoverColor) {
+        colorToViewMap[coverColor]?.let {
+            val v = findViewById<View>(it)
+            colorViewList.forEach {
+                if (it == v) {
+                    it.setToCheckState()
+                } else {
+                    it.setToNonCheckState()
+                }
+            }
+        }
+    }
+
+    private fun View.setToNonCheckState() {
+        setPadding(0)
+    }
+
+    private fun View.setToCheckState() {
+        val padding = resources.getDimension(R.dimen.cover_color_item_padding).toInt()
+        setPadding(padding)
+    }
+
+    private fun View.setPadding(padding: Int) {
+        setPadding(padding, padding, padding, padding)
+    }
+
+    interface OnChangeListener {
+        fun onChange()
+    }
+}

+ 129 - 0
src/main/java/com/bomostory/sceneeditmodule/cover/CoverEditorDialog.kt

@@ -0,0 +1,129 @@
+package com.bomostory.sceneeditmodule.cover
+
+import android.os.Bundle
+import android.support.design.widget.TabLayout
+import android.support.v4.app.DialogFragment
+import android.support.v4.app.FragmentManager
+import android.support.v4.view.PagerAdapter
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.example.tfat.myapplication.R
+import kotlinx.android.synthetic.main.dialog_cover_editor.*
+import kotlinx.android.synthetic.main.dialog_cover_editor.view.*
+
+class CoverEditorDialog : DialogFragment() {
+    companion object {
+        const val PAGE_COUNT = 2
+    }
+
+    private val dialogTag = this::class.java.simpleName
+
+    lateinit var onViewCreated: Runnable
+    var onSave: View.OnClickListener? = null
+        set(value) {
+            field = value
+            frontCoverEditorView_coverEditorDialog.onSave = value
+            backCoverEditorView_coverEditorDialog.onSave = value
+        }
+    var onCancel: View.OnClickListener? = null
+        set(value) {
+            field = value
+            frontCoverEditorView_coverEditorDialog.onCancel = value
+            backCoverEditorView_coverEditorDialog.onCancel = value
+        }
+    var storyName: String
+        get () {
+            return frontCoverEditorView_coverEditorDialog.storyName
+        }
+        set(value) {
+            frontCoverEditorView_coverEditorDialog.storyName = value
+        }
+    var author: String
+        get() {
+            return frontCoverEditorView_coverEditorDialog.author
+        }
+        set(value) {
+            frontCoverEditorView_coverEditorDialog.author = value
+        }
+    var frontCoverColor: CoverColor
+        get() {
+            return frontCoverEditorView_coverEditorDialog.selectedColor
+        }
+        set(value) {
+            frontCoverEditorView_coverEditorDialog.selectedColor = value
+        }
+    var backCoverColor: CoverColor
+        get() {
+            return backCoverEditorView_coverEditorDialog.selectedColor
+        }
+        set(value) {
+            backCoverEditorView_coverEditorDialog.selectedColor = value
+        }
+    var category: Category
+        get() {
+            return frontCoverEditorView_coverEditorDialog.category
+        }
+        set(value) {
+            frontCoverEditorView_coverEditorDialog.category = value
+        }
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+        return inflater.inflate(R.layout.dialog_cover_editor, container, false).apply {
+            val viewPager = viewPager_coverEditorDialog_content
+            val tabLayout = tabLayout_coverEditorDialog_header
+            viewPager.adapter = object : PagerAdapter() {
+                override fun instantiateItem(container: ViewGroup, position: Int): Any {
+                    return container.getChildAt(position)
+                }
+
+                override fun isViewFromObject(p0: View, p1: Any): Boolean {
+                    return p0 == p1
+                }
+
+                override fun getCount(): Int {
+                    return PAGE_COUNT
+                }
+
+                override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
+                    // no super
+                }
+            }
+            viewPager.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabLayout))
+            tabLayout.apply {
+                addTab(newTab().setText(R.string.cover_editor_dialog_tab_front))
+                addTab(newTab().setText(R.string.cover_editor_dialog_tab_back))
+                addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
+                    override fun onTabReselected(tab: TabLayout.Tab?) {}
+                    override fun onTabUnselected(tab: TabLayout.Tab?) {}
+                    override fun onTabSelected(tab: TabLayout.Tab?) {
+                        tab?.let { viewPager.currentItem = it.position }
+                    }
+                })
+            }
+            frontCoverEditorView_coverEditorDialog.apply {
+                this.onSave = this@CoverEditorDialog.onSave
+                this.onCancel = this@CoverEditorDialog.onCancel
+            }
+            backCoverEditorView_coverEditorDialog.apply {
+                this.onSave = this@CoverEditorDialog.onSave
+                this.onCancel = this@CoverEditorDialog.onCancel
+            }
+        }
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        onViewCreated.run()
+    }
+
+    fun show(manager: FragmentManager?) {
+        manager?.apply {
+            val ft = beginTransaction()
+            findFragmentByTag(dialogTag)?.let { ft.remove(it) }
+            ft.addToBackStack(null)
+            ft.commit()
+        }
+        super.show(manager, dialogTag)
+    }
+}

+ 101 - 0
src/main/java/com/bomostory/sceneeditmodule/cover/FrontCoverEditorView.kt

@@ -0,0 +1,101 @@
+package com.bomostory.sceneeditmodule.cover
+
+import android.content.Context
+import android.text.Editable
+import android.text.TextWatcher
+import android.util.AttributeSet
+import android.view.View
+import android.widget.ArrayAdapter
+import com.example.tfat.myapplication.R
+import kotlinx.android.synthetic.main.view_front_cover_editor.view.*
+
+class FrontCoverEditorView @JvmOverloads constructor(
+        context: Context,
+        attrs: AttributeSet? = null,
+        defStyleAttr: Int = 0
+) : BaseCoverEditorView(context, attrs, defStyleAttr) {
+
+    var onCancel: View.OnClickListener? = null
+    var onSave: View.OnClickListener? = null
+    var selectedColor: CoverColor
+        get() {
+            return coverColorSelectorView_frontCoverEditor.selectedColor
+        }
+        set(value) {
+            coverColorSelectorView_frontCoverEditor.selectedColor = value
+        }
+    var storyName: String
+        get() {
+            return et_frontCoverEditor_projectName.text.toString()
+        }
+        set(value) {
+            et_frontCoverEditor_projectName.setText(value)
+        }
+    var author: String
+        get() {
+            return et_frontCoverEditor_author.text.toString()
+        }
+        set(value) {
+            et_frontCoverEditor_author.setText(value)
+        }
+    var category: Category
+        get() {
+            val position = spinner_frontCoverEditor_category.selectedItemPosition
+            return Category.values()[position]
+        }
+        set(value) {
+            val position = value.ordinal
+            spinner_frontCoverEditor_category.setSelection(position)
+        }
+
+    init {
+        View.inflate(context, R.layout.view_front_cover_editor, this)
+        coverColorSelectorView_frontCoverEditor.apply {
+            onChangeListener = object : CoverColorSelectorView.OnChangeListener {
+                override fun onChange() {
+                    setCoverColor(selectedColor, this@FrontCoverEditorView.iv_frontCoverEditor_cover)
+                }
+            }
+        }
+        btn_frontCoverEditor_save.setOnClickListener { onSave?.onClick(it) }
+        btn_frontCoverEditor_cancel.setOnClickListener { onCancel?.onClick(it) }
+
+        // category spinner
+        val itemLayoutResId = android.R.layout.simple_dropdown_item_1line
+        spinner_frontCoverEditor_category.adapter = ArrayAdapter<String>(context, itemLayoutResId).apply {
+            Category.values().forEach {
+                val s = context.getString(it.stringResId)
+                add(s)
+            }
+        }
+
+        et_frontCoverEditor_projectName.addTextChangedListener(object : TextWatcher {
+            override fun afterTextChanged(editable: Editable?) {
+                if (editable == null) return
+                tv_frontCoverEditor_coverTitle.text = editable.toString()
+            }
+
+            override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
+                // do nothing
+            }
+
+            override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
+                // do nothing
+            }
+        })
+        et_frontCoverEditor_author.addTextChangedListener(object : TextWatcher {
+            override fun afterTextChanged(editable: Editable?) {
+                if (editable == null) return
+                tv_frontCoverEditor_coverAuthor.text = editable.toString()
+            }
+
+            override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
+                // do nothing
+            }
+
+            override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
+                // do nothing
+            }
+        })
+    }
+}

+ 6 - 0
src/main/java/com/bomostory/sceneeditmodule/utils/FileUtils.kt

@@ -54,6 +54,12 @@ object FileUtils {
         }
     }
 
+    fun changeProjectName(oldName: String, newName: String): Boolean {
+        val oldProjectFolder = File(Config.PROJECTS_FOLDER, oldName)
+        val newProjectFolder = File(Config.PROJECTS_FOLDER, newName)
+        return oldProjectFolder.renameTo(newProjectFolder)
+    }
+
     fun saveProject(context: Context, project: Project, scaleWidth: Int, scaleHeight: Int): Completable {
         return Completable.create {
             try {

+ 38 - 0
src/main/res/layout/dialog_cover_editor.xml

@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="800dp"
+    android:layout_height="544dp"
+    android:background="@drawable/bg_rounded_8dp"
+    android:elevation="8dp">
+
+    <android.support.design.widget.TabLayout
+        android:id="@+id/tabLayout_coverEditorDialog_header"
+        android:layout_width="800dp"
+        android:layout_height="64dp"
+        android:background="@color/pale_peach"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <android.support.v4.view.ViewPager
+        android:id="@+id/viewPager_coverEditorDialog_content"
+        android:layout_width="0dp"
+        android:layout_height="490dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/tabLayout_coverEditorDialog_header">
+
+        <com.bomostory.sceneeditmodule.cover.FrontCoverEditorView
+            android:id="@+id/frontCoverEditorView_coverEditorDialog"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+        <com.bomostory.sceneeditmodule.cover.BackCoverEditorView
+            android:id="@+id/backCoverEditorView_coverEditorDialog"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+    </android.support.v4.view.ViewPager>
+
+</android.support.constraint.ConstraintLayout>

+ 5 - 0
src/main/res/layout/item_category.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" />
+

+ 62 - 0
src/main/res/layout/view_back_cover_editor.xml

@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="800dp"
+    android:layout_height="480dp">
+
+    <ImageView
+        android:id="@+id/iv_backCoverEditor_cover"
+        android:layout_width="360dp"
+        android:layout_height="360dp"
+        android:layout_marginLeft="40dp"
+        android:layout_marginTop="24dp"
+        android:layout_marginBottom="96dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:src="#faa600" />
+
+    <ImageView
+        android:id="@+id/iv_backCoverEditor_bomoLogo"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="24dp"
+        android:src="@drawable/ic_logo_full_white"
+        app:layout_constraintBottom_toBottomOf="@id/iv_backCoverEditor_cover"
+        app:layout_constraintLeft_toLeftOf="@id/iv_backCoverEditor_cover"
+        app:layout_constraintRight_toRightOf="@id/iv_backCoverEditor_cover" />
+
+    <com.bomostory.sceneeditmodule.cover.CoverColorSelectorView
+        android:id="@+id/coverColorSelectorView_backCoverEditor"
+        android:layout_width="320dp"
+        android:layout_height="wrap_content"
+        app:layout_constraintBottom_toTopOf="@id/btn_backCoverEditor_save"
+        app:layout_constraintLeft_toRightOf="@id/iv_backCoverEditor_cover"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <Button
+        android:id="@+id/btn_backCoverEditor_save"
+        style="@style/Widget.AppCompat.Button.Borderless.Colored"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="28dp"
+        android:layout_marginBottom="24dp"
+        android:text="@string/front_cover_editor_dialog_save_btn"
+        android:textColor="@color/cocoa"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintRight_toRightOf="parent" />
+
+    <Button
+        android:id="@+id/btn_backCoverEditor_cancel"
+        style="@style/Widget.AppCompat.Button.Borderless.Colored"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/front_cover_editor_dialog_cancel_btn"
+        android:textColor="@color/cocoa"
+        app:layout_constraintBottom_toBottomOf="@id/btn_backCoverEditor_save"
+        app:layout_constraintRight_toLeftOf="@id/btn_backCoverEditor_save"
+        app:layout_constraintTop_toTopOf="@id/btn_backCoverEditor_save" />
+
+</android.support.constraint.ConstraintLayout>

+ 98 - 0
src/main/res/layout/view_cover_color_selector.xml

@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    tools:parentTag="android.support.constraint.ConstraintLayout">
+
+    <LinearLayout
+        android:id="@+id/viewGroup_coverColorSelector_1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintTop_toTopOf="parent">
+
+
+        <ImageView
+            android:id="@+id/iv_coverColorSelector_color1"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:layout_margin="8dp"
+            android:src="@color/coverColor_1"
+            tools:padding="8dp" />
+
+        <ImageView
+            android:id="@+id/iv_coverColorSelector_color2"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:layout_margin="8dp"
+            android:src="@color/coverColor_2" />
+
+        <ImageView
+            android:id="@+id/iv_coverColorSelector_color3"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:layout_margin="8dp"
+            android:src="@color/coverColor_3" />
+
+        <ImageView
+            android:id="@+id/iv_coverColorSelector_color4"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:layout_margin="8dp"
+            android:src="@color/coverColor_4" />
+
+        <ImageView
+            android:id="@+id/iv_coverColorSelector_color5"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:layout_margin="8dp"
+            android:src="@color/coverColor_5" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/viewGroup_coverColorSelector_2"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        app:layout_constraintLeft_toLeftOf="@id/viewGroup_coverColorSelector_1"
+        app:layout_constraintTop_toBottomOf="@id/viewGroup_coverColorSelector_1">
+
+        <ImageView
+            android:id="@+id/iv_coverColorSelector_color6"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:layout_margin="8dp"
+            android:src="@color/coverColor_6" />
+
+        <ImageView
+            android:id="@+id/iv_coverColorSelector_color7"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:layout_margin="8dp"
+            android:src="@color/coverColor_7" />
+
+        <ImageView
+            android:id="@+id/iv_coverColorSelector_color8"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:layout_margin="8dp"
+            android:src="@color/coverColor_8" />
+
+        <ImageView
+            android:id="@+id/iv_coverColorSelector_color9"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:layout_margin="8dp"
+            android:src="@color/coverColor_9" />
+
+        <ImageView
+            android:id="@+id/iv_coverColorSelector_color10"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:layout_margin="8dp"
+            android:src="@color/coverColor_10" />
+    </LinearLayout>
+</merge>

+ 141 - 0
src/main/res/layout/view_front_cover_editor.xml

@@ -0,0 +1,141 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:parentTag="android.support.constraint.ConstraintLayout"
+    android:layout_width="800dp"
+    android:layout_height="480dp">
+
+    <ImageView
+        android:id="@+id/iv_frontCoverEditor_cover"
+        android:layout_width="360dp"
+        android:layout_height="360dp"
+        android:layout_marginLeft="40dp"
+        android:layout_marginTop="24dp"
+        android:layout_marginBottom="96dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:src="#faa600" />
+
+    <ImageView
+        android:id="@+id/iv_frontCoverEditor_coverMask"
+        android:layout_width="0dp"
+        android:layout_height="112dp"
+        android:layout_marginBottom="16dp"
+        android:src="#66ffffff"
+        app:layout_constraintBottom_toBottomOf="@id/iv_frontCoverEditor_cover"
+        app:layout_constraintLeft_toLeftOf="@id/iv_frontCoverEditor_cover"
+        app:layout_constraintRight_toRightOf="@id/iv_frontCoverEditor_cover" />
+
+    <TextView
+        android:id="@+id/tv_frontCoverEditor_coverTitle"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:ellipsize="middle"
+        android:fontFamily="sans-serif-medium"
+        android:gravity="center"
+        android:singleLine="true"
+        android:textColor="#ffffff"
+        android:textSize="40sp"
+        app:layout_constraintLeft_toLeftOf="@id/iv_frontCoverEditor_coverMask"
+        app:layout_constraintRight_toRightOf="@id/iv_frontCoverEditor_coverMask"
+        app:layout_constraintTop_toTopOf="@id/iv_frontCoverEditor_coverMask"
+        tools:text="Story Name" />
+
+    <TextView
+        android:id="@+id/tv_frontCoverEditor_coverAuthor"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="9dp"
+        android:ellipsize="middle"
+        android:fontFamily="sans-serif-medium"
+        android:gravity="center"
+        android:singleLine="true"
+        android:textColor="#ffffff"
+        android:textSize="20sp"
+        app:layout_constraintLeft_toLeftOf="@id/iv_frontCoverEditor_coverMask"
+        app:layout_constraintRight_toRightOf="@id/iv_frontCoverEditor_coverMask"
+        app:layout_constraintTop_toBottomOf="@id/tv_frontCoverEditor_coverTitle"
+        tools:text="Author" />
+
+    <EditText
+        android:id="@+id/et_frontCoverEditor_projectName"
+        style="@style/BomoEditText"
+        android:layout_width="320dp"
+        android:layout_height="56dp"
+        android:layout_marginLeft="40dp"
+        android:layout_marginTop="32dp"
+        android:layout_marginRight="40dp"
+        android:hint="@string/front_cover_editor_dialog_project_name_hint"
+        app:layout_constraintLeft_toRightOf="@id/iv_frontCoverEditor_cover"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <EditText
+        android:id="@+id/et_frontCoverEditor_author"
+        style="@style/BomoEditText"
+        android:layout_width="0dp"
+        android:layout_height="56dp"
+        android:layout_marginTop="24dp"
+        android:hint="@string/front_cover_editor_dialog_author_hint"
+        app:layout_constraintLeft_toLeftOf="@id/et_frontCoverEditor_projectName"
+        app:layout_constraintRight_toRightOf="@id/et_frontCoverEditor_projectName"
+        app:layout_constraintTop_toBottomOf="@id/et_frontCoverEditor_projectName" />
+
+    <Spinner
+        android:id="@+id/spinner_frontCoverEditor_category"
+        android:layout_width="0dp"
+        android:layout_height="56dp"
+        android:layout_marginTop="24dp"
+        android:foreground="@drawable/bg_bomo_spinner"
+        app:layout_constraintLeft_toLeftOf="@id/et_frontCoverEditor_projectName"
+        app:layout_constraintRight_toRightOf="@id/et_frontCoverEditor_projectName"
+        app:layout_constraintTop_toBottomOf="@id/et_frontCoverEditor_author" />
+
+    <com.bomostory.sceneeditmodule.cover.CoverColorSelectorView
+        android:id="@+id/coverColorSelectorView_frontCoverEditor"
+        app:layout_constraintLeft_toLeftOf="@id/et_frontCoverEditor_projectName"
+        app:layout_constraintRight_toRightOf="@id/et_frontCoverEditor_projectName"
+        android:layout_width="wrap_content"
+        android:layout_marginTop="16dp"
+        android:layout_height="wrap_content"
+        app:layout_constraintTop_toBottomOf="@id/spinner_frontCoverEditor_category" />
+
+    <Button
+        android:id="@+id/btn_frontCoverEditor_save"
+        style="@style/Widget.AppCompat.Button.Borderless.Colored"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="28dp"
+        android:layout_marginBottom="24dp"
+        android:text="@string/front_cover_editor_dialog_save_btn"
+        android:textColor="@color/cocoa"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/coverColorSelectorView_frontCoverEditor" />
+
+    <Button
+        android:id="@+id/btn_frontCoverEditor_chooseScene"
+        style="@style/Widget.AppCompat.Button.Borderless.Colored"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/front_cover_editor_dialog_choose_a_scene_btn"
+        android:textColor="@color/cocoa"
+        app:layout_constraintBottom_toBottomOf="@id/btn_frontCoverEditor_save"
+        app:layout_constraintRight_toLeftOf="@id/btn_frontCoverEditor_save"
+        app:layout_constraintTop_toTopOf="@id/btn_frontCoverEditor_save" />
+
+    <Button
+        android:id="@+id/btn_frontCoverEditor_cancel"
+        style="@style/Widget.AppCompat.Button.Borderless.Colored"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/front_cover_editor_dialog_cancel_btn"
+        android:textColor="@color/cocoa"
+        app:layout_constraintBottom_toBottomOf="@id/btn_frontCoverEditor_chooseScene"
+        app:layout_constraintRight_toLeftOf="@id/btn_frontCoverEditor_chooseScene"
+        app:layout_constraintTop_toTopOf="@id/btn_frontCoverEditor_chooseScene" />
+
+</merge>

+ 13 - 0
src/main/res/values/colors_cover.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="coverColor_1">#f5a623</color>
+    <color name="coverColor_2">#c82185</color>
+    <color name="coverColor_3">#de0007</color>
+    <color name="coverColor_4">#008cbf</color>
+    <color name="coverColor_5">#33691e</color>
+    <color name="coverColor_6">#827717</color>
+    <color name="coverColor_7">#ba5f43</color>
+    <color name="coverColor_8">#4e342e</color>
+    <color name="coverColor_9">#424242</color>
+    <color name="coverColor_10">#37474f</color>
+</resources>

+ 5 - 0
src/main/res/values/dimens.xml

@@ -9,4 +9,9 @@
     <dimen name="dialogue_padding">50dp</dimen>
     <dimen name="popup_view_setting_height">208dp</dimen>
     <dimen name="navigation_bar_height">128dp</dimen>
+
+    <dimen name="cover_color_item_padding">8dp</dimen>
+
+    <dimen name="cover_editor_dialog_width">800dp</dimen>
+    <dimen name="cover_editor_dialog_height">544dp</dimen>
 </resources>

+ 21 - 0
src/main/res/values/strings.xml

@@ -25,4 +25,25 @@
 
     <string name="share_dialog_upload_suc">Upload success!</string>
     <string name="share_dialog_upload_failed">Upload failed</string>
+
+    <string name="cover_editor_dialog_tab_front">Front Cover</string>
+    <string name="cover_editor_dialog_tab_back">Back Cover</string>
+    <string name="front_cover_editor_dialog_project_name_hint">(Project) Story Name</string>
+    <string name="front_cover_editor_dialog_author_hint">Author</string>
+    <string name="front_cover_editor_dialog_save_btn">Save</string>
+    <string name="front_cover_editor_dialog_choose_a_scene_btn">Choose a scene</string>
+    <string name="front_cover_editor_dialog_cancel_btn">Cancel</string>
+
+    <string name="category_item_daily">Daily</string>
+    <string name="category_item_human_relations">Human relations</string>
+    <string name="category_item_character">Character</string>
+    <string name="category_item_ispirational">Ispirational</string>
+    <string name="category_item_knowledge">Knowledge</string>
+    <string name="category_item_natural">Natural</string>
+    <string name="category_item_environmental_protection">Environmental protection</string>
+    <string name="category_item_imagine">Imagine</string>
+    <string name="category_item_life">Life</string>
+    <string name="category_item_culture">Culture</string>
+    <string name="category_item_fables">Fables</string>
+    <string name="category_item_others">Others</string>
 </resources>