From 7920bd64730344d549df35f13c4701ef4b3b564f Mon Sep 17 00:00:00 2001 From: Ram Parameswaran Date: Thu, 30 Jan 2020 21:03:43 -0800 Subject: [PATCH 1/3] Add sample implementation of settings to UAMP --- automotive/automotive-lib/.gitignore | 1 + automotive/automotive-lib/build.gradle | 41 +++ automotive/automotive-lib/consumer-rules.pro | 0 automotive/automotive-lib/proguard-rules.pro | 21 ++ .../automotive/lib/ExampleInstrumentedTest.kt | 24 ++ .../src/main/AndroidManifest.xml | 2 + .../uamp/automotive/lib/ListPreference.kt | 320 ++++++++++++++++++ .../automotive/lib/ListPreferenceAdapter.kt | 65 ++++ .../automotive/lib/ListPreferenceFragment.kt | 72 ++++ .../lib/MultiSelectListPreference.kt | 265 +++++++++++++++ .../lib/MultiSelectListPreferenceAdapter.kt | 72 ++++ .../lib/MultiSelectListPreferenceFragment.kt | 68 ++++ .../src/main/res/color/primary_text.xml | 23 ++ .../src/main/res/color/secondary_text.xml | 23 ++ .../main/res/drawable/toolbar_back_icon.xml} | 6 +- .../toolbar_back_ripple_background.xml} | 2 +- .../drawable/toolbar_button_background.xml | 25 ++ .../main/res/layout/check_box_list_item.xml | 53 +++ .../src/main/res/layout/list_preference.xml | 29 ++ .../src/main/res/layout/preference.xml | 76 +++++ .../main/res/layout/preference_category.xml | 27 +- .../main/res/layout/preference_edit_text.xml | 43 +++ .../main/res/layout/preference_seekbar.xml | 83 +++++ .../res/layout/radio_button_list_item.xml | 53 +++ .../src/main/res/values/dimens.xml | 42 +++ .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/styles.xml | 92 +++++ .../src/main/res/values/themes.xml} | 28 +- .../uamp/automotive/lib/ExampleUnitTest.kt | 17 + automotive/build.gradle | 6 +- automotive/src/main/AndroidManifest.xml | 2 +- .../uamp/automotive/SettingsActivity.kt | 5 - .../drawable/default_button_background.xml | 2 +- .../main/res/drawable/ic_settings_wifi.xml | 12 + .../src/main/res/layout/activity_settings.xml | 9 +- automotive/src/main/res/values/arrays.xml | 38 +++ automotive/src/main/res/values/dimens.xml | 5 +- automotive/src/main/res/values/strings.xml | 49 +++ automotive/src/main/res/values/styles.xml | 32 +- automotive/src/main/res/xml/preferences.xml | 90 ++++- build.gradle | 7 +- settings.gradle | 2 +- 42 files changed, 1746 insertions(+), 89 deletions(-) create mode 100644 automotive/automotive-lib/.gitignore create mode 100644 automotive/automotive-lib/build.gradle create mode 100644 automotive/automotive-lib/consumer-rules.pro create mode 100644 automotive/automotive-lib/proguard-rules.pro create mode 100644 automotive/automotive-lib/src/androidTest/java/com/example/android/uamp/automotive/lib/ExampleInstrumentedTest.kt create mode 100644 automotive/automotive-lib/src/main/AndroidManifest.xml create mode 100644 automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/ListPreference.kt create mode 100644 automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/ListPreferenceAdapter.kt create mode 100644 automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/ListPreferenceFragment.kt create mode 100644 automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/MultiSelectListPreference.kt create mode 100644 automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/MultiSelectListPreferenceAdapter.kt create mode 100644 automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/MultiSelectListPreferenceFragment.kt create mode 100644 automotive/automotive-lib/src/main/res/color/primary_text.xml create mode 100644 automotive/automotive-lib/src/main/res/color/secondary_text.xml rename automotive/{src/main/res/drawable/sign_in_toolbar_back_icon.xml => automotive-lib/src/main/res/drawable/toolbar_back_icon.xml} (88%) rename automotive/{src/main/res/drawable/sign_in_toolbar_back_ripple_background.xml => automotive-lib/src/main/res/drawable/toolbar_back_ripple_background.xml} (92%) create mode 100644 automotive/automotive-lib/src/main/res/drawable/toolbar_button_background.xml create mode 100644 automotive/automotive-lib/src/main/res/layout/check_box_list_item.xml create mode 100644 automotive/automotive-lib/src/main/res/layout/list_preference.xml create mode 100644 automotive/automotive-lib/src/main/res/layout/preference.xml rename automotive/{ => automotive-lib}/src/main/res/layout/preference_category.xml (50%) create mode 100644 automotive/automotive-lib/src/main/res/layout/preference_edit_text.xml create mode 100644 automotive/automotive-lib/src/main/res/layout/preference_seekbar.xml create mode 100644 automotive/automotive-lib/src/main/res/layout/radio_button_list_item.xml create mode 100644 automotive/automotive-lib/src/main/res/values/dimens.xml create mode 100644 automotive/automotive-lib/src/main/res/values/strings.xml create mode 100644 automotive/automotive-lib/src/main/res/values/styles.xml rename automotive/{src/main/res/layout/preference.xml => automotive-lib/src/main/res/values/themes.xml} (50%) create mode 100644 automotive/automotive-lib/src/test/java/com/example/android/uamp/automotive/lib/ExampleUnitTest.kt create mode 100644 automotive/src/main/res/drawable/ic_settings_wifi.xml create mode 100644 automotive/src/main/res/values/arrays.xml diff --git a/automotive/automotive-lib/.gitignore b/automotive/automotive-lib/.gitignore new file mode 100644 index 000000000..796b96d1c --- /dev/null +++ b/automotive/automotive-lib/.gitignore @@ -0,0 +1 @@ +/build diff --git a/automotive/automotive-lib/build.gradle b/automotive/automotive-lib/build.gradle new file mode 100644 index 000000000..4fc7ecc79 --- /dev/null +++ b/automotive/automotive-lib/build.gradle @@ -0,0 +1,41 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' +android { + compileSdkVersion 29 + buildToolsVersion "29.0.2" + + + defaultConfig { + minSdkVersion 24 + targetSdkVersion 29 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles 'consumer-rules.pro' + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" + + implementation "androidx.constraintlayout:constraintlayout:$constraint_layout_version" + implementation "androidx.preference:preference:$androidx_preference_version" + implementation "com.google.android.material:material:$material_version" + + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.core:core-ktx:1.1.0' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test.ext:junit:1.1.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' +} diff --git a/automotive/automotive-lib/consumer-rules.pro b/automotive/automotive-lib/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/automotive/automotive-lib/proguard-rules.pro b/automotive/automotive-lib/proguard-rules.pro new file mode 100644 index 000000000..f1b424510 --- /dev/null +++ b/automotive/automotive-lib/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/automotive/automotive-lib/src/androidTest/java/com/example/android/uamp/automotive/lib/ExampleInstrumentedTest.kt b/automotive/automotive-lib/src/androidTest/java/com/example/android/uamp/automotive/lib/ExampleInstrumentedTest.kt new file mode 100644 index 000000000..7564abba2 --- /dev/null +++ b/automotive/automotive-lib/src/androidTest/java/com/example/android/uamp/automotive/lib/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.example.android.uamp.automotive.lib + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.example.android.uamp.automotive.lib.test", appContext.packageName) + } +} diff --git a/automotive/automotive-lib/src/main/AndroidManifest.xml b/automotive/automotive-lib/src/main/AndroidManifest.xml new file mode 100644 index 000000000..c6e56648a --- /dev/null +++ b/automotive/automotive-lib/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + diff --git a/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/ListPreference.kt b/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/ListPreference.kt new file mode 100644 index 000000000..4d4ea2834 --- /dev/null +++ b/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/ListPreference.kt @@ -0,0 +1,320 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.uamp.automotive.lib + +import android.content.Context +import android.content.res.TypedArray +import android.os.Parcel +import android.os.Parcelable +import android.text.TextUtils +import android.util.AttributeSet +import android.util.Log +import androidx.annotation.ArrayRes +import androidx.core.content.res.TypedArrayUtils +import androidx.preference.Preference + +/** + * A {@link Preference} that displays a list of entries in a {@link ListPreferenceFragment}. + * + * This class is taken largely as is from {@link androidx.preference.ListPreference} other than + * the modification to display entries in a full-screen fragment rather than a dialog. + * + *

This preference saves a string value. This string will be the value from the + * {@link #setEntryValues(CharSequence[])} array. + * + * @attr name android:entries + * @attr name android:entryValues + */ +class ListPreference : Preference { + + companion object { + internal const val ARG_KEY = "AutomotiveListPreferenceKey" + } + + private val TAG = "ListPreference" + private var mEntries: Array? = null + private var mEntryValues: Array? = null + private var mValue: String? = null + private var mSummary: String? = null + private var mValueSet: Boolean = false + + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) + : super(context, attrs, defStyleAttr, defStyleRes) { + init(context, attrs, defStyleAttr, defStyleRes) + } + + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) + : this(context, attrs, defStyleAttr, R.style.AutomotivePreference_Preference) + + constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, R.attr.preferenceStyle) + + constructor(context: Context) : this(context, null) + + private fun init(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) { + + val a = context.obtainStyledAttributes( + attrs, R.styleable.ListPreference, defStyleAttr, defStyleRes) + + mEntries = TypedArrayUtils.getTextArray(a, R.styleable.ListPreference_entries, + R.styleable.ListPreference_android_entries) + + mEntryValues = TypedArrayUtils.getTextArray(a, R.styleable.ListPreference_entryValues, + R.styleable.ListPreference_android_entryValues) + + a.recycle() + + extras.putString(ARG_KEY, key) + fragment = ListPreferenceFragment::class.qualifiedName + } + + /** + * Sets the human-readable entries to be shown in the list. This will be shown in subsequent + * dialogs. + * + * + * Each entry must have a corresponding index in [.setEntryValues]. + * + * @param entries The entries + * @see .setEntryValues + */ + fun setEntries(entries: Array) { + mEntries = entries + } + + /** + * @param entriesResId The entries array as a resource + * @see .setEntries + */ + fun setEntries(@ArrayRes entriesResId: Int) { + setEntries(context.resources.getTextArray(entriesResId)) + } + + /** + * The list of entries to be shown in the list in subsequent dialogs. + * + * @return The list as an array + */ + fun getEntries(): Array? { + return mEntries + } + + /** + * The array to find the value to save for a preference when an entry from entries is + * selected. If a user clicks on the second item in entries, the second item in this array + * will be saved to the preference. + * + * @param entryValues The array to be used as values to save for the preference + */ + fun setEntryValues(entryValues: Array) { + mEntryValues = entryValues + } + + /** + * @param entryValuesResId The entry values array as a resource + * @see .setEntryValues + */ + fun setEntryValues(@ArrayRes entryValuesResId: Int) { + setEntryValues(context.resources.getTextArray(entryValuesResId)) + } + + /** + * Returns the array of values to be saved for the preference. + * + * @return The array of values + */ + fun getEntryValues(): Array? { + return mEntryValues + } + + override fun setSummary(summary: CharSequence?) { + super.setSummary(summary) + if (summary == null && mSummary != null) { + mSummary = null + } else if (summary != null && summary != mSummary) { + mSummary = summary.toString() + } + } + + override fun getSummary(): CharSequence { + if (summaryProvider != null) { + return summaryProvider!!.provideSummary(this) + } + val entry = getEntry() + val summary = super.getSummary() + if (mSummary == null) { + return summary + } + val formattedString = String.format(mSummary!!, entry ?: "") + if (TextUtils.equals(formattedString, summary)) { + return summary + } + Log.w(TAG, + "Setting a summary with a String formatting marker is no longer supported." + " You should use a SummaryProvider instead.") + return formattedString + } + + /** + * Sets the value of the key. This should be one of the entries in [.getEntryValues]. + * + * @param value The value to set for the key + */ + fun setValue(value: String?) { + // Always persist/notify the first time. + val changed = !TextUtils.equals(mValue, value) + if (changed || !mValueSet) { + mValue = value + mValueSet = true + persistString(value) + if (changed) { + notifyChanged() + } + } + } + + /** + * Returns the value of the key. This should be one of the entries in [.getEntryValues]. + * + * @return The value of the key + */ + fun getValue(): String? { + return mValue + } + + /** + * Returns the entry corresponding to the current value. + * + * @return The entry corresponding to the current value, or `null` + */ + fun getEntry(): CharSequence? { + val index = getValueIndex() + return if (index >= 0 && mEntries != null) mEntries!![index] else null + } + + /** + * Returns the index of the given value (in the entry values array). + * + * @param value The value whose index should be returned + * @return The index of the value, or -1 if not found + */ + fun findIndexOfValue(value: String?): Int { + if (value != null && mEntryValues != null) { + for (i in mEntryValues!!.indices.reversed()) { + if (mEntryValues!![i] == value) { + return i + } + } + } + return -1 + } + + /** + * Sets the value to the given index from the entry values. + * + * @param index The index of the value to set + */ + fun setValueIndex(index: Int) { + if (mEntryValues != null) { + setValue(mEntryValues!![index].toString()) + } + } + + private fun getValueIndex(): Int { + return findIndexOfValue(mValue) + } + + protected override fun onGetDefaultValue(a: TypedArray, index: Int): Any? { + return a.getString(index) + } + + override fun onSetInitialValue(defaultValue: Any?) { + setValue(getPersistedString(defaultValue as String?)) + } + + override fun onSaveInstanceState(): Parcelable { + val superState = super.onSaveInstanceState() + if (isPersistent) { + // No need to save instance state since it's persistent + return superState + } + + val myState = SavedState(superState) + myState.mValue = getValue() + return myState + } + + override fun onRestoreInstanceState(state: Parcelable?) { + if (state == null || state.javaClass != SavedState::class.java) { + // Didn't save state for us in onSaveInstanceState + super.onRestoreInstanceState(state) + return + } + + val myState = state as SavedState? + super.onRestoreInstanceState(myState!!.superState) + setValue(myState.mValue) + } + + private class SavedState : BaseSavedState { + + internal var mValue: String? = null + + internal constructor(source: Parcel) : super(source) { + mValue = source.readString() + } + + internal constructor(superState: Parcelable) : super(superState) + + override fun writeToParcel(dest: Parcel, flags: Int) { + super.writeToParcel(dest, flags) + dest.writeString(mValue) + } + + override fun describeContents(): Int { + return 0 + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): SavedState { + return SavedState(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + } + + /** + * A simple [androidx.preference.Preference.SummaryProvider] implementation for a + * [ListPreference]. If no value has been set, the summary displayed will be 'Not set', + * otherwise the summary displayed will be the entry set for this preference. + */ + class SimpleSummaryProvider : SummaryProvider { + companion object { + val instance = SimpleSummaryProvider() + } + + override fun provideSummary(preference: ListPreference): CharSequence? { + return if (TextUtils.isEmpty(preference.getEntry())) { + preference.context.getString(R.string.not_set) + } else { + preference.getEntry() + } + } + + } +} diff --git a/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/ListPreferenceAdapter.kt b/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/ListPreferenceAdapter.kt new file mode 100644 index 000000000..11448fea0 --- /dev/null +++ b/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/ListPreferenceAdapter.kt @@ -0,0 +1,65 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.uamp.automotive.lib + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.RadioButton +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView + +/** + * Adapter that provides bindings between a {@link ListPreference} and set of views to display and + * allow for selection of the entries of the preference. + */ +internal class ListPreferenceAdapter(preference: ListPreference) : + RecyclerView.Adapter() { + + private var mEntries: Array? = preference.getEntries() + private var mSelectedEntry: Int = preference.findIndexOfValue(preference.getValue()) + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder(LayoutInflater.from(parent.context) + .inflate(R.layout.radio_button_list_item, parent, false)) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.title.text = mEntries?.get(position) + holder.radioButton.isChecked = position == mSelectedEntry + holder.holderView.setOnClickListener { + val previousIndex = mSelectedEntry + mSelectedEntry = position + notifyItemChanged(previousIndex) + notifyItemChanged(position) + } + } + + fun getSelectedEntry(): Int { + return mSelectedEntry + } + + override fun getItemCount(): Int { + mEntries?.let { return mEntries!!.size } + return 0 + } + + class ViewHolder(val holderView: View) : RecyclerView.ViewHolder(holderView) { + val title: TextView = holderView.findViewById(R.id.title) + val radioButton: RadioButton = holderView.findViewById(R.id.radio_button_widget) + } +} \ No newline at end of file diff --git a/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/ListPreferenceFragment.kt b/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/ListPreferenceFragment.kt new file mode 100644 index 000000000..deb681cd8 --- /dev/null +++ b/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/ListPreferenceFragment.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.uamp.automotive.lib + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.preference.PreferenceFragmentCompat +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.example.android.uamp.automotive.lib.ListPreference.Companion.ARG_KEY + +/** + * Fragment that is used to display the multi-selection entries of a + * {@link MultiSelectListPreference}. + */ +class ListPreferenceFragment : Fragment() { + + private lateinit var mPreference: ListPreference + private lateinit var mAdapter: ListPreferenceAdapter + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.list_preference, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val recyclerView = view.findViewById(R.id.list) + mPreference = getPreference() + mAdapter = ListPreferenceAdapter(mPreference) + recyclerView.adapter = mAdapter + recyclerView.layoutManager = LinearLayoutManager(context) + } + + private fun getPreference() : ListPreference { + val key = arguments?.getCharSequence(ARG_KEY) + ?: throw IllegalStateException("Preference arguments cannot be null") + return (targetFragment as PreferenceFragmentCompat).findPreference(key) + ?: throw IllegalStateException("Unable to find ListPreference with key: $key") + } + + override fun onPause() { + super.onPause() + val selectedIndex = mAdapter.getSelectedEntry() + if (selectedIndex < 0) { + return + } + + val entryValue = mPreference.getEntryValues()!![selectedIndex].toString() + if (mPreference.callChangeListener(entryValue)) { + mPreference.setValue(entryValue) + } + } +} \ No newline at end of file diff --git a/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/MultiSelectListPreference.kt b/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/MultiSelectListPreference.kt new file mode 100644 index 000000000..f1fdf5c0d --- /dev/null +++ b/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/MultiSelectListPreference.kt @@ -0,0 +1,265 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.uamp.automotive.lib + +import android.content.Context +import android.content.res.TypedArray +import android.os.Parcel +import android.os.Parcelable +import android.util.AttributeSet +import androidx.annotation.ArrayRes +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.res.TypedArrayUtils +import androidx.preference.Preference +import java.util.Collections +import kotlin.collections.HashSet + +/** + * A {@link Preference} that displays a list of entries in a {@link ListPreferenceFragment}. + * + * This class is taken largely as is from {@link androidx.preference.ListPreference} other than + * the modification to display entries in a full-screen fragment rather than a dialog. + * + *

This preference saves a string value. This string will be the value from the + * {@link #setEntryValues(CharSequence[])} array. + * + * @attr name android:entries + * @attr name android:entryValues + */ +class MultiSelectListPreference : Preference { + + companion object { + internal const val ARG_KEY = "AutomotiveMultiSelectListPreferenceKey" + } + + private var mEntries: Array? = null + private var mEntryValues: Array? = null + private val mValues = HashSet() + + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) + : super(context, attrs, defStyleAttr, defStyleRes) { + init(context, attrs, defStyleAttr, defStyleRes) + } + + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) + : this(context, attrs, defStyleAttr, R.style.AutomotivePreference_Preference) + + constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, R.attr.preferenceStyle) + + constructor(context: Context) : this(context, null) + + + private fun init(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) { + + val a = context.obtainStyledAttributes( + attrs, R.styleable.ListPreference, defStyleAttr, defStyleRes) + + mEntries = TypedArrayUtils.getTextArray(a, R.styleable.ListPreference_entries, + R.styleable.ListPreference_android_entries) + + mEntryValues = TypedArrayUtils.getTextArray(a, R.styleable.ListPreference_entryValues, + R.styleable.ListPreference_android_entryValues) + + a.recycle() + + extras.putString(ARG_KEY, key) + fragment = MultiSelectListPreferenceFragment::class.qualifiedName + } + + /** + * Sets the human-readable entries to be shown in the list. This will be shown in subsequent + * dialogs. + * + * + * Each entry must have a corresponding index in [.setEntryValues]. + * + * @param entries The entries + * @see .setEntryValues + */ + fun setEntries(entries: Array) { + mEntries = entries + } + + /** + * @param entriesResId The entries array as a resource + * @see .setEntries + */ + fun setEntries(@ArrayRes entriesResId: Int) { + setEntries(context.resources.getTextArray(entriesResId)) + } + + /** + * The list of entries to be shown in the list in subsequent dialogs. + * + * @return The list as an array + */ + fun getEntries(): Array? { + return mEntries + } + + /** + * The array to find the value to save for a preference when an entry from entries is selected. If + * a user clicks on the second item in entries, the second item in this array will be saved to the + * preference. + * + * @param entryValues The array to be used as mValues to save for the preference + */ + fun setEntryValues(entryValues: Array) { + mEntryValues = entryValues + } + + /** + * @param entryValuesResId The entry mValues array as a resource + * @see .setEntryValues + */ + fun setEntryValues(@ArrayRes entryValuesResId: Int) { + setEntryValues(context.resources.getTextArray(entryValuesResId)) + } + + /** + * Returns the array of mValues to be saved for the preference. + * + * @return The array of mValues + */ + fun getEntryValues(): Array? { + return mEntryValues + } + + /** + * Sets the values for the key. This should contain entries in [.getEntryValues]. + * + * @param values The mValues to set for the key + */ + fun setValues(values: Set) { + mValues.clear() + mValues.addAll(values) + + persistStringSet(values) + notifyChanged() + } + + /** + * Retrieves the current values of the key. + * + * @return The set of current values + */ + fun getValues(): Set { + return mValues + } + + /** + * Returns the index of the given value (in the entry mValues array). + * + * @param value The value whose index should be returned + * @return The index of the value, or -1 if not found + */ + fun findIndexOfValue(value: String?): Int { + if (value != null && mEntryValues != null) { + for (i in mEntryValues!!.indices.reversed()) { + if (mEntryValues!![i] == value) { + return i + } + } + } + return -1 + } + + protected fun getSelectedItems(): BooleanArray { + val entries = mEntryValues + val entryCount = entries!!.size + val values = mValues + val result = BooleanArray(entryCount) + + for (i in 0 until entryCount) { + result[i] = values.contains(entries[i].toString()) + } + + return result + } + + override fun onGetDefaultValue(a: TypedArray, index: Int): Any { + val defaultValues = a.getTextArray(index) + val result = HashSet() + + for (defaultValue in defaultValues) { + result.add(defaultValue.toString()) + } + + return result + } + + override fun onSetInitialValue(defaultValue: Any?) { + setValues(getPersistedStringSet(defaultValue as Set?)) + } + + override fun onSaveInstanceState(): Parcelable { + val superState = super.onSaveInstanceState() + if (isPersistent()) { + // No need to save instance state + return superState + } + + val myState = SavedState(superState) + myState.mValues = getValues() + return myState + } + + override fun onRestoreInstanceState(state: Parcelable?) { + if (state == null || state.javaClass != SavedState::class.java) { + // Didn't save state for us in onSaveInstanceState + super.onRestoreInstanceState(state) + return + } + + val myState = state as SavedState? + super.onRestoreInstanceState(myState!!.superState) + setValues(myState.mValues) + } + + private class SavedState : BaseSavedState { + + internal lateinit var mValues: Set + + internal constructor(source: Parcel) : super(source) { + val size = source.readInt() + mValues = HashSet() + val strings = arrayOfNulls(size) + source.readStringArray(strings) + + Collections.addAll(mValues as HashSet, *strings) + } + + internal constructor(superState: Parcelable) : super(superState) {} + + override fun writeToParcel(dest: Parcel, flags: Int) { + super.writeToParcel(dest, flags) + dest.writeInt(mValues.size) + dest.writeStringArray(mValues.toTypedArray()) + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): SavedState { + return SavedState(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + } + +} \ No newline at end of file diff --git a/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/MultiSelectListPreferenceAdapter.kt b/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/MultiSelectListPreferenceAdapter.kt new file mode 100644 index 000000000..7f5d2b510 --- /dev/null +++ b/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/MultiSelectListPreferenceAdapter.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.uamp.automotive.lib + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.CheckBox +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import java.util.HashSet + +/** + * Adapter that provides bindings between a {@link ListPreference} and set of views to display and + * allow for selection of the entries of the preference. + */ +class MultiSelectListPreferenceAdapter(preference: MultiSelectListPreference) : + RecyclerView.Adapter() { + + private var mSelectedEntries: MutableSet = HashSet(preference.getValues()) + private var mEntries: Array? = preference.getEntries() + private var mEntryValues: Array? = preference.getEntryValues() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder(LayoutInflater.from(parent.context) + .inflate(R.layout.check_box_list_item, parent, false)) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.title.text = mEntries?.get(position) + val entryValue = mEntryValues?.get(position).toString() + + holder.checkBox.isChecked = mSelectedEntries.contains(entryValue) + holder.holderView.setOnClickListener { + if (mSelectedEntries.contains(entryValue)) { + mSelectedEntries.remove(entryValue) + } else { + mSelectedEntries.add(entryValue) + } + + notifyItemChanged(position) + } + } + + fun getSelectedEntries(): Set { + return mSelectedEntries + } + + override fun getItemCount(): Int { + mEntries?.let { return mEntries!!.size } + return 0 + } + + class ViewHolder(val holderView: View) : RecyclerView.ViewHolder(holderView) { + val title: TextView = itemView.findViewById(R.id.title) + val checkBox: CheckBox = itemView.findViewById(R.id.check_box_widget) + } +} \ No newline at end of file diff --git a/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/MultiSelectListPreferenceFragment.kt b/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/MultiSelectListPreferenceFragment.kt new file mode 100644 index 000000000..868399214 --- /dev/null +++ b/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/MultiSelectListPreferenceFragment.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.uamp.automotive.lib + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.preference.PreferenceFragmentCompat +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.example.android.uamp.automotive.lib.MultiSelectListPreference.Companion.ARG_KEY + +/** + * Fragment that is used to display the multi-selection entries of a + * {@link MultiSelectListPreference}. + */ +class MultiSelectListPreferenceFragment : Fragment() { + + private lateinit var mPreference: MultiSelectListPreference + private lateinit var mAdapter: MultiSelectListPreferenceAdapter + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.list_preference, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val recyclerView = view.findViewById(R.id.list) + mPreference = getPreference() + mAdapter = MultiSelectListPreferenceAdapter(mPreference) + recyclerView.adapter = mAdapter + recyclerView.layoutManager = LinearLayoutManager(context) + } + + private fun getPreference() : MultiSelectListPreference { + val key = arguments?.getCharSequence(ARG_KEY) + ?: throw IllegalStateException("Preference arguments cannot be null") + return (targetFragment as PreferenceFragmentCompat).findPreference(key) + ?: throw IllegalStateException("Unable to find ListPreference with key: $key") + } + + override fun onPause() { + super.onPause() + + val newValues = mAdapter.getSelectedEntries() + if (mPreference.callChangeListener(newValues)) { + mPreference.setValues(newValues) + } + } +} \ No newline at end of file diff --git a/automotive/automotive-lib/src/main/res/color/primary_text.xml b/automotive/automotive-lib/src/main/res/color/primary_text.xml new file mode 100644 index 000000000..8422a588e --- /dev/null +++ b/automotive/automotive-lib/src/main/res/color/primary_text.xml @@ -0,0 +1,23 @@ + + + + + + + diff --git a/automotive/automotive-lib/src/main/res/color/secondary_text.xml b/automotive/automotive-lib/src/main/res/color/secondary_text.xml new file mode 100644 index 000000000..056030c77 --- /dev/null +++ b/automotive/automotive-lib/src/main/res/color/secondary_text.xml @@ -0,0 +1,23 @@ + + + + + + \ No newline at end of file diff --git a/automotive/src/main/res/drawable/sign_in_toolbar_back_icon.xml b/automotive/automotive-lib/src/main/res/drawable/toolbar_back_icon.xml similarity index 88% rename from automotive/src/main/res/drawable/sign_in_toolbar_back_icon.xml rename to automotive/automotive-lib/src/main/res/drawable/toolbar_back_icon.xml index b84f43548..0de79e2a2 100644 --- a/automotive/src/main/res/drawable/sign_in_toolbar_back_icon.xml +++ b/automotive/automotive-lib/src/main/res/drawable/toolbar_back_icon.xml @@ -15,8 +15,8 @@ ~ limitations under the License. --> @@ -25,4 +25,4 @@ - + \ No newline at end of file diff --git a/automotive/src/main/res/drawable/sign_in_toolbar_back_ripple_background.xml b/automotive/automotive-lib/src/main/res/drawable/toolbar_back_ripple_background.xml similarity index 92% rename from automotive/src/main/res/drawable/sign_in_toolbar_back_ripple_background.xml rename to automotive/automotive-lib/src/main/res/drawable/toolbar_back_ripple_background.xml index 2deabd2cf..416621d58 100644 --- a/automotive/src/main/res/drawable/sign_in_toolbar_back_ripple_background.xml +++ b/automotive/automotive-lib/src/main/res/drawable/toolbar_back_ripple_background.xml @@ -16,4 +16,4 @@ --> + android:radius="@dimen/toolbar_nav_button_size" /> diff --git a/automotive/automotive-lib/src/main/res/drawable/toolbar_button_background.xml b/automotive/automotive-lib/src/main/res/drawable/toolbar_button_background.xml new file mode 100644 index 000000000..312d71def --- /dev/null +++ b/automotive/automotive-lib/src/main/res/drawable/toolbar_button_background.xml @@ -0,0 +1,25 @@ + + + + + + + + + + diff --git a/automotive/automotive-lib/src/main/res/layout/check_box_list_item.xml b/automotive/automotive-lib/src/main/res/layout/check_box_list_item.xml new file mode 100644 index 000000000..a252d2e60 --- /dev/null +++ b/automotive/automotive-lib/src/main/res/layout/check_box_list_item.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/automotive/automotive-lib/src/main/res/layout/list_preference.xml b/automotive/automotive-lib/src/main/res/layout/list_preference.xml new file mode 100644 index 000000000..c72b87e65 --- /dev/null +++ b/automotive/automotive-lib/src/main/res/layout/list_preference.xml @@ -0,0 +1,29 @@ + + + + + + + + + diff --git a/automotive/automotive-lib/src/main/res/layout/preference.xml b/automotive/automotive-lib/src/main/res/layout/preference.xml new file mode 100644 index 000000000..3f22293a8 --- /dev/null +++ b/automotive/automotive-lib/src/main/res/layout/preference.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/automotive/src/main/res/layout/preference_category.xml b/automotive/automotive-lib/src/main/res/layout/preference_category.xml similarity index 50% rename from automotive/src/main/res/layout/preference_category.xml rename to automotive/automotive-lib/src/main/res/layout/preference_category.xml index d843352e9..894099ad1 100644 --- a/automotive/src/main/res/layout/preference_category.xml +++ b/automotive/automotive-lib/src/main/res/layout/preference_category.xml @@ -1,6 +1,6 @@ - - + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/preference_start_margin" + android:layout_marginEnd="@dimen/preference_end_margin" + android:layout_marginTop="@dimen/preference_top_margin" + android:layout_marginBottom="@dimen/preference_bottom_margin" + android:gravity="center_vertical" + android:orientation="vertical"> + \ No newline at end of file diff --git a/automotive/automotive-lib/src/main/res/layout/preference_edit_text.xml b/automotive/automotive-lib/src/main/res/layout/preference_edit_text.xml new file mode 100644 index 000000000..339f9de76 --- /dev/null +++ b/automotive/automotive-lib/src/main/res/layout/preference_edit_text.xml @@ -0,0 +1,43 @@ + + + + + + + + + + diff --git a/automotive/automotive-lib/src/main/res/layout/preference_seekbar.xml b/automotive/automotive-lib/src/main/res/layout/preference_seekbar.xml new file mode 100644 index 000000000..4a6a329f7 --- /dev/null +++ b/automotive/automotive-lib/src/main/res/layout/preference_seekbar.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/automotive/automotive-lib/src/main/res/layout/radio_button_list_item.xml b/automotive/automotive-lib/src/main/res/layout/radio_button_list_item.xml new file mode 100644 index 000000000..a1e5e7d8b --- /dev/null +++ b/automotive/automotive-lib/src/main/res/layout/radio_button_list_item.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/automotive/automotive-lib/src/main/res/values/dimens.xml b/automotive/automotive-lib/src/main/res/values/dimens.xml new file mode 100644 index 000000000..9517a7beb --- /dev/null +++ b/automotive/automotive-lib/src/main/res/values/dimens.xml @@ -0,0 +1,42 @@ + + + + 20dp + 24dp + 44dp + 22dp + 96dp + + 116dp + 32dp + + 32dp + 32dp + 20dp + 20dp + 32dp + + + 32dp + 16dp + 16dp + 112dp + 48dp + + 32sp + 26sp + diff --git a/automotive/automotive-lib/src/main/res/values/strings.xml b/automotive/automotive-lib/src/main/res/values/strings.xml new file mode 100644 index 000000000..842a9a94a --- /dev/null +++ b/automotive/automotive-lib/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Automotive Library + diff --git a/automotive/automotive-lib/src/main/res/values/styles.xml b/automotive/automotive-lib/src/main/res/values/styles.xml new file mode 100644 index 000000000..608ba4f43 --- /dev/null +++ b/automotive/automotive-lib/src/main/res/values/styles.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/automotive/src/main/res/layout/preference.xml b/automotive/automotive-lib/src/main/res/values/themes.xml similarity index 50% rename from automotive/src/main/res/layout/preference.xml rename to automotive/automotive-lib/src/main/res/values/themes.xml index 0f30573ba..ca109b034 100644 --- a/automotive/src/main/res/layout/preference.xml +++ b/automotive/automotive-lib/src/main/res/values/themes.xml @@ -1,6 +1,6 @@ - - - - - \ No newline at end of file + --> + + + \ No newline at end of file diff --git a/automotive/automotive-lib/src/test/java/com/example/android/uamp/automotive/lib/ExampleUnitTest.kt b/automotive/automotive-lib/src/test/java/com/example/android/uamp/automotive/lib/ExampleUnitTest.kt new file mode 100644 index 000000000..b8f1c1f65 --- /dev/null +++ b/automotive/automotive-lib/src/test/java/com/example/android/uamp/automotive/lib/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.example.android.uamp.automotive.lib + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} diff --git a/automotive/build.gradle b/automotive/build.gradle index 6f6f18add..52a7265db 100644 --- a/automotive/build.gradle +++ b/automotive/build.gradle @@ -49,15 +49,15 @@ android { dependencies { implementation project(':common') - + implementation project(':automotive:automotive-lib') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "androidx.core:core-ktx:$androidx_core_ktx_version" + implementation "androidx.constraintlayout:constraintlayout:$constraint_layout_version" implementation "androidx.preference:preference:$androidx_preference_version" - implementation "androidx.car:car:$androidx_car_version" implementation "androidx.appcompat:appcompat:$androidx_app_compat_version" implementation "androidx.lifecycle:lifecycle-extensions:$arch_lifecycle_version" - + implementation "com.google.android.material:material:$material_version" implementation "com.google.android.gms:play-services-auth:$play_services_auth_version" testImplementation "junit:junit:$junit_version" diff --git a/automotive/src/main/AndroidManifest.xml b/automotive/src/main/AndroidManifest.xml index 622c0fdf2..94fcf05c8 100644 --- a/automotive/src/main/AndroidManifest.xml +++ b/automotive/src/main/AndroidManifest.xml @@ -51,7 +51,7 @@ diff --git a/automotive/src/main/java/com/example/android/uamp/automotive/SettingsActivity.kt b/automotive/src/main/java/com/example/android/uamp/automotive/SettingsActivity.kt index 6d74507ae..f28d4b492 100644 --- a/automotive/src/main/java/com/example/android/uamp/automotive/SettingsActivity.kt +++ b/automotive/src/main/java/com/example/android/uamp/automotive/SettingsActivity.kt @@ -42,11 +42,6 @@ class SettingsActivity : AppCompatActivity() { .commit() } - override fun onBackPressed() { - super.onBackPressed() - finish() - } - override fun onSupportNavigateUp(): Boolean { onBackPressed() return true diff --git a/automotive/src/main/res/drawable/default_button_background.xml b/automotive/src/main/res/drawable/default_button_background.xml index d576414a5..0535a2469 100644 --- a/automotive/src/main/res/drawable/default_button_background.xml +++ b/automotive/src/main/res/drawable/default_button_background.xml @@ -20,7 +20,7 @@ android:color="?attr/colorControlHighlight"> - + diff --git a/automotive/src/main/res/drawable/ic_settings_wifi.xml b/automotive/src/main/res/drawable/ic_settings_wifi.xml new file mode 100644 index 000000000..65e2dd5c6 --- /dev/null +++ b/automotive/src/main/res/drawable/ic_settings_wifi.xml @@ -0,0 +1,12 @@ + + + \ No newline at end of file diff --git a/automotive/src/main/res/layout/activity_settings.xml b/automotive/src/main/res/layout/activity_settings.xml index 590ea8cda..1c5b1e534 100644 --- a/automotive/src/main/res/layout/activity_settings.xml +++ b/automotive/src/main/res/layout/activity_settings.xml @@ -22,19 +22,18 @@ + android:layout_height="wrap_content" + android:background="@android:color/transparent"> + android:layout_height="?attr/actionBarSize"/> + android:layout_height="match_parent" /> diff --git a/automotive/src/main/res/values/arrays.xml b/automotive/src/main/res/values/arrays.xml new file mode 100644 index 000000000..5475c985b --- /dev/null +++ b/automotive/src/main/res/values/arrays.xml @@ -0,0 +1,38 @@ + + + + + + + + Choose me! + + No, me! + + What about me?! + + + + + + alpha + + beta + + charlie + + \ No newline at end of file diff --git a/automotive/src/main/res/values/dimens.xml b/automotive/src/main/res/values/dimens.xml index c0c547aa8..4dd7307b4 100644 --- a/automotive/src/main/res/values/dimens.xml +++ b/automotive/src/main/res/values/dimens.xml @@ -23,12 +23,11 @@ 26sp 158dp + 4dp 56dp 56dp - 24dp - 48dp - 96dp + @dimen/toolbar_height 4dp 2dp diff --git a/automotive/src/main/res/values/strings.xml b/automotive/src/main/res/values/strings.xml index b5cef2b15..a4298e446 100644 --- a/automotive/src/main/res/values/strings.xml +++ b/automotive/src/main/res/values/strings.xml @@ -44,4 +44,53 @@ https://chart.apis.google.com/chart?cht=qr&chs=392x392&chl=https://www.youtube.com/watch?v=8I8mCFYYfdk YOUR_SERVER_CLIENT_ID + + + Basic attributes + + Preference + + Simple preference with no special attributes + + Very stylish preference + + Define style tags such as <b> in a string resource to customize a preference\'s text + + Icon preference + + Define a drawable to display it at the start of the preference + + Single line title preference - no matter how long the title is it will never wrap to multiple lines + + This title will be ellipsized instead of wrapping to another line + + Single line preference - no summary + + Widgets + + Checkbox preference + + Tap anywhere in this preference to toggle state + + Switch preference + + Tap anywhere in this preference to toggle state + + Seekbar preference + + Dialogs + + EditText preference + + This title can be changed! + + List preference + + Choose one option! + + Multi-select list preference + + Shows a dialog with multiple choice options + + Choose some options! diff --git a/automotive/src/main/res/values/styles.xml b/automotive/src/main/res/values/styles.xml index 166e76e2f..9d2a8404b 100644 --- a/automotive/src/main/res/values/styles.xml +++ b/automotive/src/main/res/values/styles.xml @@ -19,34 +19,16 @@ - - - - - - - + + - + \ No newline at end of file diff --git a/automotive/src/main/res/xml/preferences.xml b/automotive/src/main/res/xml/preferences.xml index 68ec51720..45bf3ebd4 100644 --- a/automotive/src/main/res/xml/preferences.xml +++ b/automotive/src/main/res/xml/preferences.xml @@ -15,17 +15,89 @@ ~ limitations under the License. --> + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 63c75b5c2..54222721a 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,9 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. -buildscript { +buildscript { + ext.kotlin_version = '1.3.50' + ext { // App SDK versions. compileSdkVersion = 28 @@ -28,7 +30,7 @@ buildscript { androidx_car_version = '1.0.0-alpha7' androidx_core_ktx_version = '1.0.2' androidx_media_version = '1.0.1' - androidx_preference_version = '1.0.0' + androidx_preference_version = '1.1.0' androidx_test_runner_version = '1.1.1' arch_lifecycle_version = '2.0.0' constraint_layout_version = '1.1.3' @@ -43,6 +45,7 @@ buildscript { kotlin_coroutines_version = '1.1.0' play_services_auth_version = '17.0.0' recycler_view_version = '1.0.0' + material_version = '1.0.0' robolectric_version = '4.2' test_runner_version = '1.1.0' } diff --git a/settings.gradle b/settings.gradle index 5edff895c..d320a3a01 100644 --- a/settings.gradle +++ b/settings.gradle @@ -39,4 +39,4 @@ if (extraSettings.exists()) { apply from: extraSettings } -include ':app', ':common', ':automotive' +include ':app', ':common', ':automotive', ':automotive:automotive-lib' From 1ee5ff3e6cb8980a4b83a61cfbd656d465a22266 Mon Sep 17 00:00:00 2001 From: Ram Parameswaran Date: Mon, 3 Feb 2020 14:41:45 -0800 Subject: [PATCH 2/3] Address review comments from nic0lette --- .../automotive/lib/ExampleInstrumentedTest.kt | 18 ++++- .../uamp/automotive/lib/ListPreference.kt | 77 +++++++++---------- .../automotive/lib/ListPreferenceAdapter.kt | 20 ++--- .../automotive/lib/ListPreferenceFragment.kt | 25 +++--- .../lib/MultiSelectListPreference.kt | 65 ++++++++-------- .../lib/MultiSelectListPreferenceAdapter.kt | 26 +++---- .../lib/MultiSelectListPreferenceFragment.kt | 23 +++--- .../src/main/res/color/primary_text.xml | 29 ++++--- .../src/main/res/color/secondary_text.xml | 28 +++---- .../main/res/drawable/toolbar_back_icon.xml | 4 +- .../toolbar_back_ripple_background.xml | 4 +- .../drawable/toolbar_button_background.xml | 4 +- .../main/res/layout/check_box_list_item.xml | 4 +- .../src/main/res/layout/list_preference.xml | 4 +- .../src/main/res/layout/preference.xml | 2 +- .../main/res/layout/preference_category.xml | 2 +- .../main/res/layout/preference_edit_text.xml | 28 +++---- .../main/res/layout/preference_seekbar.xml | 16 ++++ .../res/layout/radio_button_list_item.xml | 4 +- .../src/main/res/values/dimens.xml | 4 +- .../src/main/res/values/strings.xml | 16 ++++ automotive/src/main/res/xml/preferences.xml | 14 ++-- 22 files changed, 229 insertions(+), 188 deletions(-) diff --git a/automotive/automotive-lib/src/androidTest/java/com/example/android/uamp/automotive/lib/ExampleInstrumentedTest.kt b/automotive/automotive-lib/src/androidTest/java/com/example/android/uamp/automotive/lib/ExampleInstrumentedTest.kt index 7564abba2..2b6222cb5 100644 --- a/automotive/automotive-lib/src/androidTest/java/com/example/android/uamp/automotive/lib/ExampleInstrumentedTest.kt +++ b/automotive/automotive-lib/src/androidTest/java/com/example/android/uamp/automotive/lib/ExampleInstrumentedTest.kt @@ -1,3 +1,19 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.example.android.uamp.automotive.lib import androidx.test.platform.app.InstrumentationRegistry @@ -6,7 +22,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import org.junit.Test import org.junit.runner.RunWith -import org.junit.Assert.* +import org.junit.Assert.assertEquals /** * Instrumented test, which will execute on an Android device. diff --git a/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/ListPreference.kt b/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/ListPreference.kt index 4d4ea2834..cffcede43 100644 --- a/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/ListPreference.kt +++ b/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/ListPreference.kt @@ -1,11 +1,11 @@ /* - * Copyright 2020 The Android Open Source Project + * Copyright 2020 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -27,6 +27,8 @@ import androidx.annotation.ArrayRes import androidx.core.content.res.TypedArrayUtils import androidx.preference.Preference +const val LIST_PREFERENCE_ARG_KEY = "AutomotiveListPreferenceKey" + /** * A {@link Preference} that displays a list of entries in a {@link ListPreferenceFragment}. * @@ -41,16 +43,12 @@ import androidx.preference.Preference */ class ListPreference : Preference { - companion object { - internal const val ARG_KEY = "AutomotiveListPreferenceKey" - } - private val TAG = "ListPreference" - private var mEntries: Array? = null - private var mEntryValues: Array? = null - private var mValue: String? = null - private var mSummary: String? = null - private var mValueSet: Boolean = false + private var entries: Array? = null + private var entryValues: Array? = null + private var value: String? = null + private var summary: String? = null + private var valueSet: Boolean = false constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) { @@ -66,18 +64,18 @@ class ListPreference : Preference { private fun init(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) { - val a = context.obtainStyledAttributes( + val attributes = context.obtainStyledAttributes( attrs, R.styleable.ListPreference, defStyleAttr, defStyleRes) - mEntries = TypedArrayUtils.getTextArray(a, R.styleable.ListPreference_entries, + entries = TypedArrayUtils.getTextArray(attributes, R.styleable.ListPreference_entries, R.styleable.ListPreference_android_entries) - mEntryValues = TypedArrayUtils.getTextArray(a, R.styleable.ListPreference_entryValues, + entryValues = TypedArrayUtils.getTextArray(attributes, R.styleable.ListPreference_entryValues, R.styleable.ListPreference_android_entryValues) - a.recycle() + attributes.recycle() - extras.putString(ARG_KEY, key) + extras.putString(LIST_PREFERENCE_ARG_KEY, key) fragment = ListPreferenceFragment::class.qualifiedName } @@ -92,7 +90,7 @@ class ListPreference : Preference { * @see .setEntryValues */ fun setEntries(entries: Array) { - mEntries = entries + this.entries = entries } /** @@ -109,7 +107,7 @@ class ListPreference : Preference { * @return The list as an array */ fun getEntries(): Array? { - return mEntries + return entries } /** @@ -120,7 +118,7 @@ class ListPreference : Preference { * @param entryValues The array to be used as values to save for the preference */ fun setEntryValues(entryValues: Array) { - mEntryValues = entryValues + this.entryValues = entryValues } /** @@ -137,15 +135,15 @@ class ListPreference : Preference { * @return The array of values */ fun getEntryValues(): Array? { - return mEntryValues + return entryValues } override fun setSummary(summary: CharSequence?) { super.setSummary(summary) - if (summary == null && mSummary != null) { - mSummary = null - } else if (summary != null && summary != mSummary) { - mSummary = summary.toString() + if (summary == null && this.summary != null) { + this.summary = null + } else if (summary != null && summary != this.summary) { + this.summary = summary.toString() } } @@ -155,15 +153,16 @@ class ListPreference : Preference { } val entry = getEntry() val summary = super.getSummary() - if (mSummary == null) { + if (this.summary == null) { return summary } - val formattedString = String.format(mSummary!!, entry ?: "") + val formattedString = String.format(this.summary!!, entry ?: "") if (TextUtils.equals(formattedString, summary)) { return summary } Log.w(TAG, - "Setting a summary with a String formatting marker is no longer supported." + " You should use a SummaryProvider instead.") + "Setting a summary with a String formatting marker is no longer supported." + + " You should use a SummaryProvider instead.") return formattedString } @@ -174,10 +173,10 @@ class ListPreference : Preference { */ fun setValue(value: String?) { // Always persist/notify the first time. - val changed = !TextUtils.equals(mValue, value) - if (changed || !mValueSet) { - mValue = value - mValueSet = true + val changed = !TextUtils.equals(this.value, value) + if (changed || !valueSet) { + this.value = value + valueSet = true persistString(value) if (changed) { notifyChanged() @@ -191,7 +190,7 @@ class ListPreference : Preference { * @return The value of the key */ fun getValue(): String? { - return mValue + return value } /** @@ -201,7 +200,7 @@ class ListPreference : Preference { */ fun getEntry(): CharSequence? { val index = getValueIndex() - return if (index >= 0 && mEntries != null) mEntries!![index] else null + return if (index >= 0 && entries != null) entries!![index] else null } /** @@ -211,9 +210,9 @@ class ListPreference : Preference { * @return The index of the value, or -1 if not found */ fun findIndexOfValue(value: String?): Int { - if (value != null && mEntryValues != null) { - for (i in mEntryValues!!.indices.reversed()) { - if (mEntryValues!![i] == value) { + if (value != null && entryValues != null) { + for (i in entryValues!!.indices.reversed()) { + if (entryValues!![i] == value) { return i } } @@ -227,13 +226,13 @@ class ListPreference : Preference { * @param index The index of the value to set */ fun setValueIndex(index: Int) { - if (mEntryValues != null) { - setValue(mEntryValues!![index].toString()) + if (entryValues != null) { + setValue(entryValues!![index].toString()) } } private fun getValueIndex(): Int { - return findIndexOfValue(mValue) + return findIndexOfValue(value) } protected override fun onGetDefaultValue(a: TypedArray, index: Int): Any? { diff --git a/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/ListPreferenceAdapter.kt b/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/ListPreferenceAdapter.kt index 11448fea0..b489548e2 100644 --- a/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/ListPreferenceAdapter.kt +++ b/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/ListPreferenceAdapter.kt @@ -1,11 +1,11 @@ /* - * Copyright 2020 The Android Open Source Project + * Copyright 2020 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -30,8 +30,8 @@ import androidx.recyclerview.widget.RecyclerView internal class ListPreferenceAdapter(preference: ListPreference) : RecyclerView.Adapter() { - private var mEntries: Array? = preference.getEntries() - private var mSelectedEntry: Int = preference.findIndexOfValue(preference.getValue()) + private var entries: Array? = preference.getEntries() + private var selectedEntry: Int = preference.findIndexOfValue(preference.getValue()) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { return ViewHolder(LayoutInflater.from(parent.context) @@ -39,22 +39,22 @@ internal class ListPreferenceAdapter(preference: ListPreference) : } override fun onBindViewHolder(holder: ViewHolder, position: Int) { - holder.title.text = mEntries?.get(position) - holder.radioButton.isChecked = position == mSelectedEntry + holder.title.text = entries?.get(position) + holder.radioButton.isChecked = position == selectedEntry holder.holderView.setOnClickListener { - val previousIndex = mSelectedEntry - mSelectedEntry = position + val previousIndex = selectedEntry + selectedEntry = position notifyItemChanged(previousIndex) notifyItemChanged(position) } } fun getSelectedEntry(): Int { - return mSelectedEntry + return selectedEntry } override fun getItemCount(): Int { - mEntries?.let { return mEntries!!.size } + entries?.let { return entries!!.size } return 0 } diff --git a/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/ListPreferenceFragment.kt b/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/ListPreferenceFragment.kt index deb681cd8..d33959f26 100644 --- a/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/ListPreferenceFragment.kt +++ b/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/ListPreferenceFragment.kt @@ -1,11 +1,11 @@ /* - * Copyright 2020 The Android Open Source Project + * Copyright 2020 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -24,7 +24,6 @@ import androidx.fragment.app.Fragment import androidx.preference.PreferenceFragmentCompat import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import com.example.android.uamp.automotive.lib.ListPreference.Companion.ARG_KEY /** * Fragment that is used to display the multi-selection entries of a @@ -32,8 +31,8 @@ import com.example.android.uamp.automotive.lib.ListPreference.Companion.ARG_KEY */ class ListPreferenceFragment : Fragment() { - private lateinit var mPreference: ListPreference - private lateinit var mAdapter: ListPreferenceAdapter + private lateinit var preference: ListPreference + private lateinit var adapter: ListPreferenceAdapter override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -44,14 +43,14 @@ class ListPreferenceFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val recyclerView = view.findViewById(R.id.list) - mPreference = getPreference() - mAdapter = ListPreferenceAdapter(mPreference) - recyclerView.adapter = mAdapter + preference = getPreference() + adapter = ListPreferenceAdapter(preference) + recyclerView.adapter = adapter recyclerView.layoutManager = LinearLayoutManager(context) } private fun getPreference() : ListPreference { - val key = arguments?.getCharSequence(ARG_KEY) + val key = arguments?.getCharSequence(LIST_PREFERENCE_ARG_KEY) ?: throw IllegalStateException("Preference arguments cannot be null") return (targetFragment as PreferenceFragmentCompat).findPreference(key) ?: throw IllegalStateException("Unable to find ListPreference with key: $key") @@ -59,14 +58,14 @@ class ListPreferenceFragment : Fragment() { override fun onPause() { super.onPause() - val selectedIndex = mAdapter.getSelectedEntry() + val selectedIndex = adapter.getSelectedEntry() if (selectedIndex < 0) { return } - val entryValue = mPreference.getEntryValues()!![selectedIndex].toString() - if (mPreference.callChangeListener(entryValue)) { - mPreference.setValue(entryValue) + val entryValue = preference.getEntryValues()!![selectedIndex].toString() + if (preference.callChangeListener(entryValue)) { + preference.setValue(entryValue) } } } \ No newline at end of file diff --git a/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/MultiSelectListPreference.kt b/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/MultiSelectListPreference.kt index f1fdf5c0d..6511e19fa 100644 --- a/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/MultiSelectListPreference.kt +++ b/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/MultiSelectListPreference.kt @@ -1,11 +1,11 @@ /* - * Copyright 2020 The Android Open Source Project + * Copyright 2020 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -22,12 +22,13 @@ import android.os.Parcel import android.os.Parcelable import android.util.AttributeSet import androidx.annotation.ArrayRes -import androidx.appcompat.app.AppCompatActivity import androidx.core.content.res.TypedArrayUtils import androidx.preference.Preference import java.util.Collections import kotlin.collections.HashSet +const val MULTI_SELECT_LIST_PREFERENCE_ARG_KEY = "AutomotiveMultiSelectListPreferenceKey" + /** * A {@link Preference} that displays a list of entries in a {@link ListPreferenceFragment}. * @@ -42,13 +43,9 @@ import kotlin.collections.HashSet */ class MultiSelectListPreference : Preference { - companion object { - internal const val ARG_KEY = "AutomotiveMultiSelectListPreferenceKey" - } - - private var mEntries: Array? = null - private var mEntryValues: Array? = null - private val mValues = HashSet() + private var entries: Array? = null + private var entryValues: Array? = null + private val values = HashSet() constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) { @@ -65,18 +62,18 @@ class MultiSelectListPreference : Preference { private fun init(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) { - val a = context.obtainStyledAttributes( + val attributes = context.obtainStyledAttributes( attrs, R.styleable.ListPreference, defStyleAttr, defStyleRes) - mEntries = TypedArrayUtils.getTextArray(a, R.styleable.ListPreference_entries, + entries = TypedArrayUtils.getTextArray(attributes, R.styleable.ListPreference_entries, R.styleable.ListPreference_android_entries) - mEntryValues = TypedArrayUtils.getTextArray(a, R.styleable.ListPreference_entryValues, + entryValues = TypedArrayUtils.getTextArray(attributes, R.styleable.ListPreference_entryValues, R.styleable.ListPreference_android_entryValues) - a.recycle() + attributes.recycle() - extras.putString(ARG_KEY, key) + extras.putString(MULTI_SELECT_LIST_PREFERENCE_ARG_KEY, key) fragment = MultiSelectListPreferenceFragment::class.qualifiedName } @@ -91,7 +88,7 @@ class MultiSelectListPreference : Preference { * @see .setEntryValues */ fun setEntries(entries: Array) { - mEntries = entries + this.entries = entries } /** @@ -108,7 +105,7 @@ class MultiSelectListPreference : Preference { * @return The list as an array */ fun getEntries(): Array? { - return mEntries + return entries } /** @@ -116,14 +113,14 @@ class MultiSelectListPreference : Preference { * a user clicks on the second item in entries, the second item in this array will be saved to the * preference. * - * @param entryValues The array to be used as mValues to save for the preference + * @param entryValues The array to be used as values to save for the preference */ fun setEntryValues(entryValues: Array) { - mEntryValues = entryValues + this.entryValues = entryValues } /** - * @param entryValuesResId The entry mValues array as a resource + * @param entryValuesResId The entry values array as a resource * @see .setEntryValues */ fun setEntryValues(@ArrayRes entryValuesResId: Int) { @@ -131,22 +128,22 @@ class MultiSelectListPreference : Preference { } /** - * Returns the array of mValues to be saved for the preference. + * Returns the array of values to be saved for the preference. * - * @return The array of mValues + * @return The array of values */ fun getEntryValues(): Array? { - return mEntryValues + return entryValues } /** * Sets the values for the key. This should contain entries in [.getEntryValues]. * - * @param values The mValues to set for the key + * @param values The values to set for the key */ fun setValues(values: Set) { - mValues.clear() - mValues.addAll(values) + this.values.clear() + this.values.addAll(values) persistStringSet(values) notifyChanged() @@ -158,19 +155,19 @@ class MultiSelectListPreference : Preference { * @return The set of current values */ fun getValues(): Set { - return mValues + return values } /** - * Returns the index of the given value (in the entry mValues array). + * Returns the index of the given value (in the entry values array). * * @param value The value whose index should be returned * @return The index of the value, or -1 if not found */ fun findIndexOfValue(value: String?): Int { - if (value != null && mEntryValues != null) { - for (i in mEntryValues!!.indices.reversed()) { - if (mEntryValues!![i] == value) { + if (value != null && entryValues != null) { + for (i in entryValues!!.indices.reversed()) { + if (entryValues!![i] == value) { return i } } @@ -179,9 +176,9 @@ class MultiSelectListPreference : Preference { } protected fun getSelectedItems(): BooleanArray { - val entries = mEntryValues + val entries = entryValues val entryCount = entries!!.size - val values = mValues + val values = this.values val result = BooleanArray(entryCount) for (i in 0 until entryCount) { @@ -243,7 +240,7 @@ class MultiSelectListPreference : Preference { Collections.addAll(mValues as HashSet, *strings) } - internal constructor(superState: Parcelable) : super(superState) {} + internal constructor(superState: Parcelable) : super(superState) override fun writeToParcel(dest: Parcel, flags: Int) { super.writeToParcel(dest, flags) diff --git a/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/MultiSelectListPreferenceAdapter.kt b/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/MultiSelectListPreferenceAdapter.kt index 7f5d2b510..69b779b79 100644 --- a/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/MultiSelectListPreferenceAdapter.kt +++ b/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/MultiSelectListPreferenceAdapter.kt @@ -1,11 +1,11 @@ /* - * Copyright 2020 The Android Open Source Project + * Copyright 2020 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -31,9 +31,9 @@ import java.util.HashSet class MultiSelectListPreferenceAdapter(preference: MultiSelectListPreference) : RecyclerView.Adapter() { - private var mSelectedEntries: MutableSet = HashSet(preference.getValues()) - private var mEntries: Array? = preference.getEntries() - private var mEntryValues: Array? = preference.getEntryValues() + private var selectedEntries: MutableSet = HashSet(preference.getValues()) + private var entries: Array? = preference.getEntries() + private var entryValues: Array? = preference.getEntryValues() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { return ViewHolder(LayoutInflater.from(parent.context) @@ -41,15 +41,15 @@ class MultiSelectListPreferenceAdapter(preference: MultiSelectListPreference) : } override fun onBindViewHolder(holder: ViewHolder, position: Int) { - holder.title.text = mEntries?.get(position) - val entryValue = mEntryValues?.get(position).toString() + holder.title.text = entries?.get(position) + val entryValue = entryValues?.get(position).toString() - holder.checkBox.isChecked = mSelectedEntries.contains(entryValue) + holder.checkBox.isChecked = selectedEntries.contains(entryValue) holder.holderView.setOnClickListener { - if (mSelectedEntries.contains(entryValue)) { - mSelectedEntries.remove(entryValue) + if (selectedEntries.contains(entryValue)) { + selectedEntries.remove(entryValue) } else { - mSelectedEntries.add(entryValue) + selectedEntries.add(entryValue) } notifyItemChanged(position) @@ -57,11 +57,11 @@ class MultiSelectListPreferenceAdapter(preference: MultiSelectListPreference) : } fun getSelectedEntries(): Set { - return mSelectedEntries + return selectedEntries } override fun getItemCount(): Int { - mEntries?.let { return mEntries!!.size } + entries?.let { return entries!!.size } return 0 } diff --git a/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/MultiSelectListPreferenceFragment.kt b/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/MultiSelectListPreferenceFragment.kt index 868399214..f6f610ba0 100644 --- a/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/MultiSelectListPreferenceFragment.kt +++ b/automotive/automotive-lib/src/main/java/com/example/android/uamp/automotive/lib/MultiSelectListPreferenceFragment.kt @@ -1,11 +1,11 @@ /* - * Copyright 2020 The Android Open Source Project + * Copyright 2020 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -24,7 +24,6 @@ import androidx.fragment.app.Fragment import androidx.preference.PreferenceFragmentCompat import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import com.example.android.uamp.automotive.lib.MultiSelectListPreference.Companion.ARG_KEY /** * Fragment that is used to display the multi-selection entries of a @@ -32,8 +31,8 @@ import com.example.android.uamp.automotive.lib.MultiSelectListPreference.Compani */ class MultiSelectListPreferenceFragment : Fragment() { - private lateinit var mPreference: MultiSelectListPreference - private lateinit var mAdapter: MultiSelectListPreferenceAdapter + private lateinit var preference: MultiSelectListPreference + private lateinit var adapter: MultiSelectListPreferenceAdapter override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -44,14 +43,14 @@ class MultiSelectListPreferenceFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val recyclerView = view.findViewById(R.id.list) - mPreference = getPreference() - mAdapter = MultiSelectListPreferenceAdapter(mPreference) - recyclerView.adapter = mAdapter + preference = getPreference() + adapter = MultiSelectListPreferenceAdapter(preference) + recyclerView.adapter = adapter recyclerView.layoutManager = LinearLayoutManager(context) } private fun getPreference() : MultiSelectListPreference { - val key = arguments?.getCharSequence(ARG_KEY) + val key = arguments?.getCharSequence(MULTI_SELECT_LIST_PREFERENCE_ARG_KEY) ?: throw IllegalStateException("Preference arguments cannot be null") return (targetFragment as PreferenceFragmentCompat).findPreference(key) ?: throw IllegalStateException("Unable to find ListPreference with key: $key") @@ -60,9 +59,9 @@ class MultiSelectListPreferenceFragment : Fragment() { override fun onPause() { super.onPause() - val newValues = mAdapter.getSelectedEntries() - if (mPreference.callChangeListener(newValues)) { - mPreference.setValues(newValues) + val newValues = adapter.getSelectedEntries() + if (preference.callChangeListener(newValues)) { + preference.setValues(newValues) } } } \ No newline at end of file diff --git a/automotive/automotive-lib/src/main/res/color/primary_text.xml b/automotive/automotive-lib/src/main/res/color/primary_text.xml index 8422a588e..380207b91 100644 --- a/automotive/automotive-lib/src/main/res/color/primary_text.xml +++ b/automotive/automotive-lib/src/main/res/color/primary_text.xml @@ -1,20 +1,19 @@ - + ~ Copyright 2020 Google LLC + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ https://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + ~ Copyright 2020 Google LLC + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ https://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + --> + --> + ~ Copyright 2020 Google LLC + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ https://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + + Automotive Library diff --git a/automotive/src/main/res/xml/preferences.xml b/automotive/src/main/res/xml/preferences.xml index 45bf3ebd4..b29bf1811 100644 --- a/automotive/src/main/res/xml/preferences.xml +++ b/automotive/src/main/res/xml/preferences.xml @@ -18,6 +18,13 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> + + + + @@ -93,11 +100,4 @@ - - - - \ No newline at end of file From c3fb610ec408d178a030e0732c7681ea5bac6235 Mon Sep 17 00:00:00 2001 From: Ram Parameswaran Date: Tue, 18 Feb 2020 12:26:08 -0800 Subject: [PATCH 3/3] Update seekbar preference styling --- .../main/res/layout/preference_seekbar.xml | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/automotive/automotive-lib/src/main/res/layout/preference_seekbar.xml b/automotive/automotive-lib/src/main/res/layout/preference_seekbar.xml index 72b2d22b7..275e3dfd9 100644 --- a/automotive/automotive-lib/src/main/res/layout/preference_seekbar.xml +++ b/automotive/automotive-lib/src/main/res/layout/preference_seekbar.xml @@ -16,6 +16,7 @@ --> - + android:layout_height="wrap_content"> + +