From 2ea5e5d6ef8fa5c574d0a752c33674423e41f163 Mon Sep 17 00:00:00 2001 From: Janne Koschinski <janne@kuschku.de> Date: Wed, 27 Sep 2017 15:17:22 +0200 Subject: [PATCH] Implemented Account Setup UI --- app/build.gradle.kts | 5 + app/src/main/AndroidManifest.xml | 8 + .../ui/setup/AccountSetupActivity.kt | 17 ++ .../quasseldroid_ng/ui/setup/SetupActivity.kt | 172 ++++++++++++++++++ .../slides/AccountSetupConnectionSlide.kt | 64 +++++++ .../ui/setup/slides/AccountSetupNameSlide.kt | 55 ++++++ .../ui/setup/slides/AccountSetupUserSlide.kt | 63 +++++++ .../ui/setup/slides/SlideFragment.kt | 72 ++++++++ .../ui/setup/slides/ValidityChangeCallback.kt | 5 + .../layout-sw600dp-land/activity_setup.xml | 25 +++ .../res/layout-sw600dp-land/setup_slide.xml | 81 +++++++++ .../res/layout-sw600dp/activity_setup.xml | 32 ++++ .../main/res/layout-sw600dp/setup_slide.xml | 46 +++++ app/src/main/res/layout/activity_setup.xml | 25 +++ .../res/layout/setup_account_connection.xml | 53 ++++++ .../main/res/layout/setup_account_name.xml | 40 ++++ .../main/res/layout/setup_account_user.xml | 52 ++++++ app/src/main/res/layout/setup_slide.xml | 44 +++++ app/src/main/res/values-sw600dp/styles.xml | 6 + app/src/main/res/values/colors.xml | 4 +- app/src/main/res/values/defaults.xml | 3 + app/src/main/res/values/strings_setup.xml | 33 ++++ app/src/main/res/values/styles.xml | 14 +- build.gradle.kts | 3 + 24 files changed, 916 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/AccountSetupActivity.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/SetupActivity.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/slides/AccountSetupConnectionSlide.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/slides/AccountSetupNameSlide.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/slides/AccountSetupUserSlide.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/slides/SlideFragment.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/slides/ValidityChangeCallback.kt create mode 100644 app/src/main/res/layout-sw600dp-land/activity_setup.xml create mode 100644 app/src/main/res/layout-sw600dp-land/setup_slide.xml create mode 100644 app/src/main/res/layout-sw600dp/activity_setup.xml create mode 100644 app/src/main/res/layout-sw600dp/setup_slide.xml create mode 100644 app/src/main/res/layout/activity_setup.xml create mode 100644 app/src/main/res/layout/setup_account_connection.xml create mode 100644 app/src/main/res/layout/setup_account_name.xml create mode 100644 app/src/main/res/layout/setup_account_user.xml create mode 100644 app/src/main/res/layout/setup_slide.xml create mode 100644 app/src/main/res/values-sw600dp/styles.xml create mode 100644 app/src/main/res/values/defaults.xml create mode 100644 app/src/main/res/values/strings_setup.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index bb47d3bf5..865e508b7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -51,6 +51,8 @@ android { ) } } + + vectorDrawables.useSupportLibrary = true } buildTypes { @@ -75,6 +77,9 @@ dependencies { implementation(appCompat("customtabs")) implementation(appCompat("cardview-v7")) implementation(appCompat("recyclerview-v7")) + implementation("com.android.support.constraint:constraint-layout:1.0.2") + + implementation("com.github.apl-devs:appintro:v4.2.2") implementation("io.reactivex.rxjava2:rxjava:2.1.3") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1c13407cc..4434e368d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -19,9 +19,17 @@ android:theme="@style/SplashTheme"> <intent-filter> <action android:name="android.intent.action.MAIN" /> + <action android:name="android.intent.action.VIEW" /> + <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> + <activity + android:name=".ui.setup.AccountSetupActivity" + android:exported="true" + android:label="AccountSetup" + android:theme="@style/SplashTheme" /> + <service android:name=".service.QuasselService" android:description="@string/connection_service_description" diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/AccountSetupActivity.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/AccountSetupActivity.kt new file mode 100644 index 000000000..8a537a205 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/AccountSetupActivity.kt @@ -0,0 +1,17 @@ +package de.kuschku.quasseldroid_ng.ui.setup + +import android.os.Bundle +import de.kuschku.quasseldroid_ng.ui.setup.slides.AccountSetupConnectionSlide +import de.kuschku.quasseldroid_ng.ui.setup.slides.AccountSetupNameSlide +import de.kuschku.quasseldroid_ng.ui.setup.slides.AccountSetupUserSlide + +class AccountSetupActivity : SetupActivity() { + override fun onDone(data: Bundle) { + } + + override val fragments = listOf( + AccountSetupConnectionSlide(), + AccountSetupUserSlide(), + AccountSetupNameSlide() + ) +} diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/SetupActivity.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/SetupActivity.kt new file mode 100644 index 000000000..734561b6e --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/SetupActivity.kt @@ -0,0 +1,172 @@ +package de.kuschku.quasseldroid_ng.ui.setup + +import android.arch.lifecycle.MutableLiveData +import android.arch.lifecycle.Observer +import android.os.Bundle +import android.os.Parcelable +import android.support.design.widget.FloatingActionButton +import android.support.v4.app.FragmentManager +import android.support.v4.app.FragmentStatePagerAdapter +import android.support.v4.view.ViewPager +import android.support.v7.app.AppCompatActivity +import android.util.SparseArray +import android.view.ViewGroup +import butterknife.BindView +import butterknife.ButterKnife +import de.kuschku.quasseldroid_ng.R +import de.kuschku.quasseldroid_ng.ui.setup.slides.SlideFragment +import de.kuschku.quasseldroid_ng.util.helper.stickySwitchMapNotNull + +abstract class SetupActivity : AppCompatActivity() { + @BindView(R.id.view_pager) + lateinit var viewPager: ViewPager + + @BindView(R.id.next_button) + lateinit var button: FloatingActionButton + + private lateinit var adapter: SlidePagerAdapter + + protected abstract val fragments: List<SlideFragment> + + private val currentPage = MutableLiveData<SlideFragment?>() + private val isValid = currentPage.stickySwitchMapNotNull(false, SlideFragment::valid) + + private val pageChangeListener = object : ViewPager.OnPageChangeListener { + override fun onPageScrollStateChanged(state: Int) { + when (state) { + ViewPager.SCROLL_STATE_SETTLING -> pageChanged() + } + } + + override fun onPageScrolled(position: Int, positionOffset: Float, + positionOffsetPixels: Int) = Unit + + override fun onPageSelected(position: Int) = Unit + } + + private fun pageChanged() { + currentPage.value = adapter.getItem(viewPager.currentItem) + val drawable = if (viewPager.currentItem == adapter.totalCount - 1) + R.drawable.ic_check + else + R.drawable.ic_arrow_right + button.setImageResource(drawable) + } + + override fun onCreate(savedInstanceState: Bundle?) { + setTheme(R.style.SetupTheme) + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_setup) + ButterKnife.bind(this) + + adapter = SlidePagerAdapter(supportFragmentManager) + fragments.forEach(adapter::addFragment) + viewPager.adapter = adapter + + button.setOnClickListener { + if (viewPager.currentItem == adapter.totalCount - 1) + onDoneInternal() + else + viewPager.setCurrentItem(viewPager.currentItem + 1, true) + } + isValid.observe(this, Observer { + if (it == true) { + button.show() + adapter.lastValidItem = viewPager.currentItem + } else { + button.hide() + adapter.lastValidItem = viewPager.currentItem - 1 + } + }) + viewPager.addOnPageChangeListener(pageChangeListener) + pageChanged() + } + + private fun onDoneInternal() { + onDone(adapter.result) + } + + abstract fun onDone(data: Bundle) + + override fun onSaveInstanceState(outState: Bundle) { + outState.putInt("currentItem", viewPager.currentItem) + outState.putInt("lastValidItem", adapter.lastValidItem) + outState.putBundle("result", adapter.result) + super.onSaveInstanceState(outState) + } + + override fun onRestoreInstanceState(savedInstanceState: Bundle?) { + super.onRestoreInstanceState(savedInstanceState) + if (savedInstanceState != null) { + if (savedInstanceState.containsKey("result")) + adapter.result.putAll(savedInstanceState.getBundle("result")) + if (savedInstanceState.containsKey("lastValidItem")) + adapter.lastValidItem = savedInstanceState.getInt("lastValidItem") + if (savedInstanceState.containsKey("currentItem")) + viewPager.currentItem = savedInstanceState.getInt("currentItem") + currentPage.value = adapter.getItem(viewPager.currentItem) + } + pageChanged() + } + + companion object { + private class SlidePagerAdapter(private val fragmentManager: FragmentManager) : + FragmentStatePagerAdapter(fragmentManager) { + private val retainedFragments = SparseArray<SlideFragment>() + + val result = Bundle() + get() { + (0 until retainedFragments.size()).map(retainedFragments::valueAt).forEach { + it.getData(field) + } + return field + } + + var lastValidItem = -1 + set(value) { + field = value + notifyDataSetChanged() + } + private val list = mutableListOf<SlideFragment>() + + override fun getItem(position: Int): SlideFragment { + return retainedFragments.get(position) ?: list[position] + } + + override fun getCount() = Math.min(list.size, lastValidItem + 2) + val totalCount get() = list.size + fun addFragment(fragment: SlideFragment) { + list.add(fragment) + } + + override fun instantiateItem(container: ViewGroup?, position: Int): Any { + val fragment = super.instantiateItem(container, position) + retainedFragments.put(position, fragment as SlideFragment) + return fragment + } + + override fun destroyItem(container: ViewGroup?, position: Int, `object`: Any?) { + retainedFragments.get(position)?.getData(result) + retainedFragments.remove(position) + super.destroyItem(container, position, `object`) + } + + override fun restoreState(state: Parcelable?, loader: ClassLoader?) { + super.restoreState(state, loader) + if (state != null) { + val bundle = state as Bundle + val keys = bundle.keySet() + for (key in keys) { + if (key.startsWith("f")) { + val index = Integer.parseInt(key.substring(1)) + val f = fragmentManager.getFragment(bundle, key) + if (f != null && f is SlideFragment) { + retainedFragments.put(index, f) + } + } + } + } + } + } + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/slides/AccountSetupConnectionSlide.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/slides/AccountSetupConnectionSlide.kt new file mode 100644 index 000000000..7e6cc8352 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/slides/AccountSetupConnectionSlide.kt @@ -0,0 +1,64 @@ +package de.kuschku.quasseldroid_ng.ui.setup.slides + +import android.os.Bundle +import android.support.design.widget.TextInputEditText +import android.text.Editable +import android.text.TextWatcher +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import butterknife.BindView +import butterknife.ButterKnife +import de.kuschku.quasseldroid_ng.R + +class AccountSetupConnectionSlide : SlideFragment() { + @BindView(R.id.host) + lateinit var hostField: TextInputEditText + + @BindView(R.id.port) + lateinit var portField: TextInputEditText + + private val textWatcher = object : TextWatcher { + override fun afterTextChanged(p0: Editable?) = updateValidity() + override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) = Unit + override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) = Unit + } + + override fun isValid(): Boolean { + return validHost() && validPort() + } + + override val title = R.string.slideAccountConnectionTitle + override val descripion = R.string.slideAccountConnectionDescription + + override fun setData(data: Bundle) { + if (data.containsKey("host")) + hostField.setText(data.getString("host")) + if (data.containsKey("port")) + portField.setText(data.getInt("port").toString()) + updateValidity() + } + + override fun getData(data: Bundle) { + data.putString("host", hostField.text.toString()) + data.putInt("port", portField.text.toString().toIntOrNull() ?: -1) + } + + override fun onCreateContent(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View { + val view = inflater.inflate(R.layout.setup_account_connection, container, false) + ButterKnife.bind(this, view) + hostField.addTextChangedListener(textWatcher) + portField.addTextChangedListener(textWatcher) + return view + } + + override fun onDestroyView() { + hostField.removeTextChangedListener(textWatcher) + portField.removeTextChangedListener(textWatcher) + super.onDestroyView() + } + + private fun validHost() = hostField.text.isNotEmpty() + private fun validPort() = (0 until 65536).contains(portField.text.toString().toIntOrNull()) +} diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/slides/AccountSetupNameSlide.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/slides/AccountSetupNameSlide.kt new file mode 100644 index 000000000..d3be3155c --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/slides/AccountSetupNameSlide.kt @@ -0,0 +1,55 @@ +package de.kuschku.quasseldroid_ng.ui.setup.slides + +import android.os.Bundle +import android.support.design.widget.TextInputEditText +import android.text.Editable +import android.text.TextWatcher +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import butterknife.BindView +import butterknife.ButterKnife +import de.kuschku.quasseldroid_ng.R + +class AccountSetupNameSlide : SlideFragment() { + @BindView(R.id.name) + lateinit var nameField: TextInputEditText + + private val textWatcher = object : TextWatcher { + override fun afterTextChanged(p0: Editable?) = updateValidity() + override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) = Unit + override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) = Unit + } + + override fun isValid(): Boolean { + return validName() + } + + override val title = R.string.slideAccountNameTitle + override val descripion = R.string.slideAccountNameDescription + + override fun setData(data: Bundle) { + if (data.containsKey("name")) + nameField.setText(data.getString("name")) + updateValidity() + } + + override fun getData(data: Bundle) { + data.putString("name", nameField.text.toString()) + } + + override fun onCreateContent(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View { + val view = inflater.inflate(R.layout.setup_account_name, container, false) + ButterKnife.bind(this, view) + nameField.addTextChangedListener(textWatcher) + return view + } + + override fun onDestroyView() { + nameField.removeTextChangedListener(textWatcher) + super.onDestroyView() + } + + private fun validName() = nameField.text.isNotEmpty() +} diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/slides/AccountSetupUserSlide.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/slides/AccountSetupUserSlide.kt new file mode 100644 index 000000000..9efc94cc7 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/slides/AccountSetupUserSlide.kt @@ -0,0 +1,63 @@ +package de.kuschku.quasseldroid_ng.ui.setup.slides + +import android.os.Bundle +import android.support.design.widget.TextInputEditText +import android.text.Editable +import android.text.TextWatcher +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import butterknife.BindView +import butterknife.ButterKnife +import de.kuschku.quasseldroid_ng.R + +class AccountSetupUserSlide : SlideFragment() { + @BindView(R.id.user) + lateinit var userField: TextInputEditText + + @BindView(R.id.pass) + lateinit var passField: TextInputEditText + + private val textWatcher = object : TextWatcher { + override fun afterTextChanged(p0: Editable?) = updateValidity() + override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) = Unit + override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) = Unit + } + + override fun isValid(): Boolean { + return validUser() && validPass() + } + + override val title = R.string.slideAccountUserTitle + override val descripion = R.string.slideAccountUserDescription + + override fun setData(data: Bundle) { + if (data.containsKey("user")) + userField.setText(data.getString("user")) + if (data.containsKey("pass")) + passField.setText(data.getString("pass")) + } + + override fun getData(data: Bundle) { + data.putString("user", userField.text.toString()) + data.putString("pass", passField.text.toString()) + } + + override fun onCreateContent(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View { + val view = inflater.inflate(R.layout.setup_account_user, container, false) + ButterKnife.bind(this, view) + userField.addTextChangedListener(textWatcher) + passField.addTextChangedListener(textWatcher) + return view + } + + override fun onDestroyView() { + userField.removeTextChangedListener(textWatcher) + passField.removeTextChangedListener(textWatcher) + super.onDestroyView() + } + + private fun validUser() = userField.text.isNotEmpty() + private fun validPass() = passField.text.isNotEmpty() +} diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/slides/SlideFragment.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/slides/SlideFragment.kt new file mode 100644 index 000000000..d6e1a76fd --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/slides/SlideFragment.kt @@ -0,0 +1,72 @@ +package de.kuschku.quasseldroid_ng.ui.setup.slides + +import android.arch.lifecycle.LifecycleOwner +import android.arch.lifecycle.MutableLiveData +import android.arch.lifecycle.Observer +import android.os.Bundle +import android.support.annotation.StringRes +import android.support.v4.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import de.kuschku.quasseldroid_ng.R + +abstract class SlideFragment : Fragment() { + @get:StringRes + protected abstract val title: Int + @get:StringRes + protected abstract val descripion: Int + + protected abstract fun isValid(): Boolean + + val valid = object : MutableLiveData<Boolean>() { + override fun observe(owner: LifecycleOwner?, observer: Observer<Boolean>?) { + super.observe(owner, observer) + observer?.onChanged(value) + } + + override fun observeForever(observer: Observer<Boolean>?) { + super.observeForever(observer) + observer?.onChanged(value) + } + } + + protected fun updateValidity() { + val valid1 = isValid() + println("Updating validity: ${this::class.java.simpleName}@${hashCode()} $valid1") + valid.value = valid1 + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View { + val view = inflater.inflate(R.layout.setup_slide, container, false) + val viewGroup = view.findViewById<View>(R.id.content_host) as ViewGroup + viewGroup.addView(onCreateContent(inflater, viewGroup, savedInstanceState)) + + view.findViewById<TextView>(R.id.title).setText(title) + view.findViewById<TextView>(R.id.description).setText(descripion) + + if (savedInstanceState != null) + setData(savedInstanceState) + updateValidity() + + return view + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + getData(outState) + } + + override fun onViewStateRestored(savedInstanceState: Bundle?) { + super.onViewStateRestored(savedInstanceState) + updateValidity() + } + + abstract fun setData(data: Bundle) + abstract fun getData(data: Bundle) + + protected abstract fun onCreateContent(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View +} diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/slides/ValidityChangeCallback.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/slides/ValidityChangeCallback.kt new file mode 100644 index 000000000..2fa49ef4b --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/setup/slides/ValidityChangeCallback.kt @@ -0,0 +1,5 @@ +package de.kuschku.quasseldroid_ng.ui.setup.slides + +interface ValidityChangeCallback { + fun invoke(isValid: Boolean) +} diff --git a/app/src/main/res/layout-sw600dp-land/activity_setup.xml b/app/src/main/res/layout-sw600dp-land/activity_setup.xml new file mode 100644 index 000000000..c97d7bc4e --- /dev/null +++ b/app/src/main/res/layout-sw600dp-land/activity_setup.xml @@ -0,0 +1,25 @@ +<merge xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <android.support.v4.view.ViewPager + android:id="@+id/view_pager" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + + <android.support.design.widget.FloatingActionButton + android:id="@+id/next_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="end|bottom" + android:layout_marginBottom="16dp" + android:layout_marginEnd="80dp" + android:layout_marginRight="80dp" + android:tint="#ffffff" + app:backgroundTint="#8A000000" + app:elevation="0dip" + app:fabSize="normal" + app:pressedTranslationZ="0dip" /> + +</merge> diff --git a/app/src/main/res/layout-sw600dp-land/setup_slide.xml b/app/src/main/res/layout-sw600dp-land/setup_slide.xml new file mode 100644 index 000000000..ed8ebc7b2 --- /dev/null +++ b/app/src/main/res/layout-sw600dp-land/setup_slide.xml @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ QuasselDroid - Quassel client for Android + ~ Copyright (C) 2016 Janne Koschinski + ~ Copyright (C) 2016 Ken Børge Viktil + ~ Copyright (C) 2016 Magnus Fjell + ~ Copyright (C) 2016 Martin Sandsmark <martin.sandsmark@kde.org> + ~ + ~ This program is free software: you can redistribute it and/or modify it + ~ under the terms of the GNU General Public License as published by the Free + ~ Software Foundation, either version 3 of the License, or (at your option) + ~ any later version. + ~ + ~ This program is distributed in the hope that it will be useful, + ~ but WITHOUT ANY WARRANTY; without even the implied warranty of + ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ~ GNU General Public License for more details. + ~ + ~ You should have received a copy of the GNU General Public License along + ~ with this program. If not, see <http://www.gnu.org/licenses/>. + --> + +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingLeft="64dp" + android:paddingRight="64dp"> + + <TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_above="@+id/view" + android:layout_toLeftOf="@+id/scrollView" + android:layout_toStartOf="@+id/scrollView" + android:gravity="end" + android:paddingEnd="64dp" + android:paddingRight="64dp" + android:textSize="28sp" + android:theme="@style/TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse" + tools:text="Connection" /> + + <android.support.v4.widget.Space + android:id="@+id/view" + android:layout_width="match_parent" + android:layout_height="8dp" + android:layout_centerVertical="true" + android:layout_toLeftOf="@+id/scrollView" + android:layout_toStartOf="@+id/scrollView" /> + + <TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/description" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@+id/view" + android:layout_toLeftOf="@+id/scrollView" + android:layout_toStartOf="@+id/scrollView" + android:gravity="end" + android:paddingEnd="64dp" + android:paddingRight="64dp" + android:textSize="16sp" + android:theme="@style/TextAppearance.AppCompat.Widget.ActionBar.Subtitle.Inverse" + tools:text="First, please choose which server your core is hosted on." /> + + <ScrollView + android:id="@+id/scrollView" + android:layout_width="400dp" + android:layout_height="wrap_content" + android:layout_alignParentBottom="true" + android:layout_alignParentEnd="true" + android:layout_alignParentRight="true"> + + <android.support.v7.widget.CardView + android:id="@+id/content_host" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="400dp" /> + + </ScrollView> + +</RelativeLayout> diff --git a/app/src/main/res/layout-sw600dp/activity_setup.xml b/app/src/main/res/layout-sw600dp/activity_setup.xml new file mode 100644 index 000000000..dd0d8803e --- /dev/null +++ b/app/src/main/res/layout-sw600dp/activity_setup.xml @@ -0,0 +1,32 @@ +<merge xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <android.support.v4.view.ViewPager + android:id="@+id/view_pager" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + + <FrameLayout + android:layout_width="400dp" + android:layout_height="wrap_content" + android:layout_gravity="bottom|center"> + + <android.support.design.widget.FloatingActionButton + android:id="@+id/next_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="end|bottom" + android:layout_marginBottom="16dp" + android:layout_marginEnd="16dp" + android:layout_marginRight="16dp" + android:tint="#ffffff" + app:backgroundTint="#8A000000" + app:elevation="0dip" + app:fabSize="normal" + app:pressedTranslationZ="0dip" /> + + </FrameLayout> + +</merge> diff --git a/app/src/main/res/layout-sw600dp/setup_slide.xml b/app/src/main/res/layout-sw600dp/setup_slide.xml new file mode 100644 index 000000000..7f40113c1 --- /dev/null +++ b/app/src/main/res/layout-sw600dp/setup_slide.xml @@ -0,0 +1,46 @@ +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <LinearLayout + android:layout_width="400dp" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal|bottom" + android:orientation="vertical"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="64dp" + android:orientation="vertical" + android:padding="32dp"> + + <TextView + android:id="@+id/title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="8dp" + android:text="Connection" + android:textSize="28sp" + android:theme="@style/TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse" /> + + <TextView + android:id="@+id/description" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="16sp" + android:theme="@style/TextAppearance.AppCompat.Widget.ActionBar.Subtitle.Inverse" + tools:text="First, please choose which server your core is hosted on." /> + + </LinearLayout> + + <android.support.v7.widget.CardView + android:id="@+id/content_host" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="400dp" /> + + </LinearLayout> + +</ScrollView> diff --git a/app/src/main/res/layout/activity_setup.xml b/app/src/main/res/layout/activity_setup.xml new file mode 100644 index 000000000..6778c9ebc --- /dev/null +++ b/app/src/main/res/layout/activity_setup.xml @@ -0,0 +1,25 @@ +<merge xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <android.support.v4.view.ViewPager + android:id="@+id/view_pager" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + + <android.support.design.widget.FloatingActionButton + android:id="@+id/next_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="end|bottom" + android:layout_marginBottom="16dp" + android:layout_marginEnd="16dp" + android:layout_marginRight="16dp" + android:tint="#ffffff" + app:backgroundTint="#8A808080" + app:elevation="0dip" + app:fabSize="normal" + app:pressedTranslationZ="0dip" /> + +</merge> diff --git a/app/src/main/res/layout/setup_account_connection.xml b/app/src/main/res/layout/setup_account_connection.xml new file mode 100644 index 000000000..71cab8049 --- /dev/null +++ b/app/src/main/res/layout/setup_account_connection.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ QuasselDroid - Quassel client for Android + ~ Copyright (C) 2016 Janne Koschinski + ~ Copyright (C) 2016 Ken Børge Viktil + ~ Copyright (C) 2016 Magnus Fjell + ~ Copyright (C) 2016 Martin Sandsmark <martin.sandsmark@kde.org> + ~ + ~ This program is free software: you can redistribute it and/or modify it + ~ under the terms of the GNU General Public License as published by the Free + ~ Software Foundation, either version 3 of the License, or (at your option) + ~ any later version. + ~ + ~ This program is distributed in the hope that it will be useful, + ~ but WITHOUT ANY WARRANTY; without even the implied warranty of + ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ~ GNU General Public License for more details. + ~ + ~ You should have received a copy of the GNU General Public License along + ~ with this program. If not, see <http://www.gnu.org/licenses/>. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:padding="32dp"> + + <android.support.design.widget.TextInputLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:hint="@string/labelConnectionHostname"> + + <android.support.design.widget.TextInputEditText + android:id="@+id/host" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:inputType="textUri" /> + </android.support.design.widget.TextInputLayout> + + <android.support.design.widget.TextInputLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:hint="@string/labelConnectionPort"> + + <android.support.design.widget.TextInputEditText + android:id="@+id/port" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:inputType="number" + android:text="@string/defaultConnectionPort" /> + </android.support.design.widget.TextInputLayout> + +</LinearLayout> diff --git a/app/src/main/res/layout/setup_account_name.xml b/app/src/main/res/layout/setup_account_name.xml new file mode 100644 index 000000000..86c8f1026 --- /dev/null +++ b/app/src/main/res/layout/setup_account_name.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ QuasselDroid - Quassel client for Android + ~ Copyright (C) 2016 Janne Koschinski + ~ Copyright (C) 2016 Ken Børge Viktil + ~ Copyright (C) 2016 Magnus Fjell + ~ Copyright (C) 2016 Martin Sandsmark <martin.sandsmark@kde.org> + ~ + ~ This program is free software: you can redistribute it and/or modify it + ~ under the terms of the GNU General Public License as published by the Free + ~ Software Foundation, either version 3 of the License, or (at your option) + ~ any later version. + ~ + ~ This program is distributed in the hope that it will be useful, + ~ but WITHOUT ANY WARRANTY; without even the implied warranty of + ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ~ GNU General Public License for more details. + ~ + ~ You should have received a copy of the GNU General Public License along + ~ with this program. If not, see <http://www.gnu.org/licenses/>. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:padding="32dp"> + + <android.support.design.widget.TextInputLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:hint="@string/labelAccountName"> + + <android.support.design.widget.TextInputEditText + android:id="@+id/name" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:inputType="text" /> + </android.support.design.widget.TextInputLayout> + +</LinearLayout> diff --git a/app/src/main/res/layout/setup_account_user.xml b/app/src/main/res/layout/setup_account_user.xml new file mode 100644 index 000000000..dd2394f82 --- /dev/null +++ b/app/src/main/res/layout/setup_account_user.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ QuasselDroid - Quassel client for Android + ~ Copyright (C) 2016 Janne Koschinski + ~ Copyright (C) 2016 Ken Børge Viktil + ~ Copyright (C) 2016 Magnus Fjell + ~ Copyright (C) 2016 Martin Sandsmark <martin.sandsmark@kde.org> + ~ + ~ This program is free software: you can redistribute it and/or modify it + ~ under the terms of the GNU General Public License as published by the Free + ~ Software Foundation, either version 3 of the License, or (at your option) + ~ any later version. + ~ + ~ This program is distributed in the hope that it will be useful, + ~ but WITHOUT ANY WARRANTY; without even the implied warranty of + ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ~ GNU General Public License for more details. + ~ + ~ You should have received a copy of the GNU General Public License along + ~ with this program. If not, see <http://www.gnu.org/licenses/>. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:padding="32dp"> + + <android.support.design.widget.TextInputLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:hint="@string/labelAccountUsername"> + + <android.support.design.widget.TextInputEditText + android:id="@+id/user" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:inputType="textVisiblePassword|textNoSuggestions" /> + </android.support.design.widget.TextInputLayout> + + <android.support.design.widget.TextInputLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:hint="@string/labelAccountPassword"> + + <android.support.design.widget.TextInputEditText + android:id="@+id/pass" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:inputType="textPassword" /> + </android.support.design.widget.TextInputLayout> + +</LinearLayout> diff --git a/app/src/main/res/layout/setup_slide.xml b/app/src/main/res/layout/setup_slide.xml new file mode 100644 index 000000000..3cc29475b --- /dev/null +++ b/app/src/main/res/layout/setup_slide.xml @@ -0,0 +1,44 @@ +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?attr/colorPrimary" + android:orientation="vertical" + android:padding="32dp"> + + <TextView + android:id="@+id/title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="32dp" + android:textSize="28sp" + android:theme="@style/TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse" + tools:text="Connection" /> + + <TextView + android:id="@+id/description" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="16sp" + android:theme="@style/TextAppearance.AppCompat.Widget.ActionBar.Subtitle.Inverse" + tools:text="First, please choose which server your core is hosted on." /> + + </LinearLayout> + + <FrameLayout + android:id="@+id/content_host" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + + </LinearLayout> + +</ScrollView> diff --git a/app/src/main/res/values-sw600dp/styles.xml b/app/src/main/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..62a617628 --- /dev/null +++ b/app/src/main/res/values-sw600dp/styles.xml @@ -0,0 +1,6 @@ +<resources> + + <style name="SetupTheme" parent="AppTheme.NoActionBar"> + <item name="android:windowBackground">?attr/colorPrimary</item> + </style> +</resources> diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 3d2127148..04b6d213e 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,6 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <color name="colorPrimary">#0a70c0</color> + <color name="colorPrimary">#ca241f</color> <color name="colorPrimaryDark">#105a94</color> <color name="colorAccent">#ffaf3b</color> + + <attr name="backgroundSetup">?attr/color</attr> </resources> diff --git a/app/src/main/res/values/defaults.xml b/app/src/main/res/values/defaults.xml new file mode 100644 index 000000000..1a915421c --- /dev/null +++ b/app/src/main/res/values/defaults.xml @@ -0,0 +1,3 @@ +<resources> + <string name="defaultConnectionPort" translatable="false">4242</string> +</resources> diff --git a/app/src/main/res/values/strings_setup.xml b/app/src/main/res/values/strings_setup.xml new file mode 100644 index 000000000..4cd5b5f9b --- /dev/null +++ b/app/src/main/res/values/strings_setup.xml @@ -0,0 +1,33 @@ +<resources> + <!-- Account Connection --> + <string name="slideAccountConnectionTitle">Connection</string> + <string name="slideAccountConnectionDescription">First, please choose which server your core is hosted on.</string> + + <string name="labelConnectionHostname">Host</string> + <string name="labelConnectionPort">Port</string> + + <!-- Account User --> + <string name="slideAccountUserTitle">Your Account</string> + <string name="slideAccountUserDescription">Now, please enter the username and password for your account on the core. If you just created that core, we’ll set up this account for you</string> + + <string name="labelAccountUsername">Username</string> + <string name="labelAccountPassword">Password</string> + + <!-- Account Name --> + <string name="slideAccountNameTitle">Customize Account</string> + <string name="slideAccountNameDescription">Give this account a name and icon</string> + + <string name="labelAccountName">Account name</string> + + <!-- Core Authenticator Select --> + <string name="slideCoreAuthenticatorSelectTitle">Select Authentication Backend</string> + <string name="slideCoreAuthenticatorSelectDescription">Please select an authentication backend for the Quassel Core to use for authenticating users.</string> + + <!-- Core Backend Select --> + <string name="slideCoreBackendSelectTitle">Select Storage Backend</string> + <string name="slideCoreBackendSelectDescription">Please select a database backend for the Quassel Core storage to store the backlog and other data in.</string> + + <!-- Core Backend Config --> + <string name="slideCoreBackendSetupTitle">Configure Storage Backend</string> + <string name="slideCoreBackendSetupDescription">Please configure the selected database backend.</string> +</resources> diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 3c8869669..965fd7437 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -6,6 +6,14 @@ <item name="colorAccent">@color/colorAccent</item> </style> + <style name="AppTheme.NoActionBar" parent="Theme.AppCompat.Light.NoActionBar"> + <item name="colorPrimary">@color/colorPrimary</item> + <item name="colorPrimaryDark">@color/colorPrimaryDark</item> + <item name="colorAccent">@color/colorAccent</item> + </style> + + <style name="SetupTheme" parent="AppTheme.NoActionBar" /> + <style name="RaisedButton" parent="AppTheme"> <item name="colorButtonNormal">?attr/background</item> </style> @@ -14,11 +22,7 @@ <item name="colorButtonNormal">@color/colorAccent</item> </style> - <style name="SplashTheme" parent="Theme.AppCompat.NoActionBar"> - <item name="colorPrimary">@color/colorPrimaryDark</item> - <item name="colorPrimaryDark">@color/colorPrimaryDark</item> - <item name="colorAccent">@color/colorAccent</item> - + <style name="SplashTheme" parent="AppTheme.NoActionBar"> <item name="android:windowBackground">@drawable/bg_splash</item> </style> </resources> diff --git a/build.gradle.kts b/build.gradle.kts index 6228f732b..ad56ba870 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,5 +13,8 @@ allprojects { repositories { google() jcenter() + maven { + url = uri("https://jitpack.io") + } } } -- GitLab