First kotlin
Convert one class to kotlin and provide a slight amount of guidance. Test: existing tests Change-Id: Ie8659765b674ac7b2d82ed3d343f387195c07d83
This commit is contained in:
@@ -15,3 +15,5 @@ api_lint_hook = ${REPO_ROOT}/frameworks/base/tools/apilint/apilint_sha.sh ${PREU
|
||||
strings_lint_hook = ${REPO_ROOT}/frameworks/base/tools/stringslint/stringslint_sha.sh ${PREUPLOAD_COMMIT}
|
||||
|
||||
hidden_api_txt_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
|
||||
|
||||
ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES}
|
||||
|
||||
@@ -32,6 +32,7 @@ java_library {
|
||||
android_library {
|
||||
name: "SystemUI-core",
|
||||
srcs: [
|
||||
"src/**/*.kt",
|
||||
"src/**/*.java",
|
||||
"src/**/I*.aidl",
|
||||
],
|
||||
@@ -73,6 +74,59 @@ android_library {
|
||||
],
|
||||
}
|
||||
|
||||
android_library {
|
||||
name: "SystemUI-tests",
|
||||
manifest: "tests/AndroidManifest.xml",
|
||||
resource_dirs: [
|
||||
"tests/res",
|
||||
"res-keyguard",
|
||||
"res",
|
||||
],
|
||||
srcs: [
|
||||
"tests/src/**/*.kt",
|
||||
"tests/src/**/*.java",
|
||||
"src/**/*.kt",
|
||||
"src/**/*.java",
|
||||
"src/**/I*.aidl",
|
||||
],
|
||||
static_libs: [
|
||||
"SystemUIPluginLib",
|
||||
"SystemUISharedLib",
|
||||
"SettingsLib",
|
||||
"androidx.car_car",
|
||||
"androidx.legacy_legacy-support-v4",
|
||||
"androidx.recyclerview_recyclerview",
|
||||
"androidx.preference_preference",
|
||||
"androidx.appcompat_appcompat",
|
||||
"androidx.mediarouter_mediarouter",
|
||||
"androidx.palette_palette",
|
||||
"androidx.legacy_legacy-preference-v14",
|
||||
"androidx.leanback_leanback",
|
||||
"androidx.slice_slice-core",
|
||||
"androidx.slice_slice-view",
|
||||
"androidx.slice_slice-builders",
|
||||
"androidx.arch.core_core-runtime",
|
||||
"androidx.lifecycle_lifecycle-extensions",
|
||||
"SystemUI-tags",
|
||||
"SystemUI-proto",
|
||||
"metrics-helper-lib",
|
||||
"android-support-test",
|
||||
"mockito-target-inline-minus-junit4",
|
||||
"testables",
|
||||
"truth-prebuilt",
|
||||
],
|
||||
libs: [
|
||||
"android.test.runner",
|
||||
"telephony-common",
|
||||
"android.car",
|
||||
"android.test.base",
|
||||
],
|
||||
aaptflags: [
|
||||
"--extra-packages",
|
||||
"com.android.keyguard:com.android.systemui",
|
||||
],
|
||||
}
|
||||
|
||||
android_app {
|
||||
name: "SystemUI",
|
||||
static_libs: [
|
||||
|
||||
22
packages/SystemUI/docs/kotlin-in-sysui.md
Normal file
22
packages/SystemUI/docs/kotlin-in-sysui.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Kotlin in SystemUI
|
||||
|
||||
Queue "it's happening" gif.
|
||||
|
||||
Kotlin is probably going to be a bit of a wild west for a while, but please
|
||||
try to follow these guidelines as much as possible.
|
||||
|
||||
- No semi-colons: they are optional, we probably don't want them in the
|
||||
future, so let's just not add them.
|
||||
- No DSLs: sysui is complicated enough as is, let's not add more layers at
|
||||
the moment.
|
||||
- Only use extension functions for keeping complex code locality: Don't use
|
||||
extension functions to add methods to android classes that you always wished
|
||||
were there, instead add them directly to the class and save us the extension.
|
||||
- inline, reified, and de-compisition can all be great things: just make sure
|
||||
you know what they do and why you are using them.
|
||||
|
||||
# Recommended reading
|
||||
|
||||
- [Kotlin](https://kotlinlang.org/)
|
||||
- [AndroidX-KTX](https://www.youtube.com/watch?v=st1XVfkDWqk)
|
||||
- [Performance and Kotlin tricks](https://www.youtube.com/watch?v=6P20npkvcb8)
|
||||
@@ -1,128 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.android.systemui.statusbar.phone;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.om.IOverlayManager;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.LocaleList;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import com.android.systemui.ConfigurationChangedReceiver;
|
||||
import com.android.systemui.statusbar.policy.ConfigurationController;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ConfigurationControllerImpl implements ConfigurationController,
|
||||
ConfigurationChangedReceiver {
|
||||
|
||||
private final ArrayList<ConfigurationListener> mListeners = new ArrayList<>();
|
||||
private final Configuration mLastConfig = new Configuration();
|
||||
private int mDensity;
|
||||
private float mFontScale;
|
||||
private boolean mInCarMode;
|
||||
private int mUiMode;
|
||||
private LocaleList mLocaleList;
|
||||
|
||||
public ConfigurationControllerImpl(Context context) {
|
||||
Configuration currentConfig = context.getResources().getConfiguration();
|
||||
mFontScale = currentConfig.fontScale;
|
||||
mDensity = currentConfig.densityDpi;
|
||||
mInCarMode = (currentConfig.uiMode & Configuration.UI_MODE_TYPE_MASK)
|
||||
== Configuration.UI_MODE_TYPE_CAR;
|
||||
mUiMode = currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
||||
mLocaleList = currentConfig.getLocales();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyThemeChanged() {
|
||||
ArrayList<ConfigurationListener> listeners = new ArrayList<>(mListeners);
|
||||
|
||||
listeners.forEach(l -> {
|
||||
if (mListeners.contains(l)) {
|
||||
l.onThemeChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
// Avoid concurrent modification exception
|
||||
ArrayList<ConfigurationListener> listeners = new ArrayList<>(mListeners);
|
||||
|
||||
listeners.forEach(l -> {
|
||||
if (mListeners.contains(l)) {
|
||||
l.onConfigChanged(newConfig);
|
||||
}
|
||||
});
|
||||
final float fontScale = newConfig.fontScale;
|
||||
final int density = newConfig.densityDpi;
|
||||
int uiMode = newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
||||
boolean uiModeChanged = uiMode != mUiMode;
|
||||
if (density != mDensity || fontScale != mFontScale
|
||||
|| (mInCarMode && uiModeChanged)) {
|
||||
listeners.forEach(l -> {
|
||||
if (mListeners.contains(l)) {
|
||||
l.onDensityOrFontScaleChanged();
|
||||
}
|
||||
});
|
||||
mDensity = density;
|
||||
mFontScale = fontScale;
|
||||
}
|
||||
|
||||
final LocaleList localeList = newConfig.getLocales();
|
||||
if (!localeList.equals(mLocaleList)) {
|
||||
mLocaleList = localeList;
|
||||
listeners.forEach(l -> {
|
||||
if (mListeners.contains(l)) {
|
||||
l.onLocaleListChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (uiModeChanged) {
|
||||
mUiMode = uiMode;
|
||||
listeners.forEach(l -> {
|
||||
if (mListeners.contains(l)) {
|
||||
l.onUiModeChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if ((mLastConfig.updateFrom(newConfig) & ActivityInfo.CONFIG_ASSETS_PATHS) != 0) {
|
||||
listeners.forEach(l -> {
|
||||
if (mListeners.contains(l)) {
|
||||
l.onOverlayChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCallback(ConfigurationListener listener) {
|
||||
mListeners.add(listener);
|
||||
listener.onDensityOrFontScaleChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCallback(ConfigurationListener listener) {
|
||||
mListeners.remove(listener);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.android.systemui.statusbar.phone
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.content.res.Configuration
|
||||
import android.os.LocaleList
|
||||
|
||||
import com.android.systemui.ConfigurationChangedReceiver
|
||||
import com.android.systemui.statusbar.policy.ConfigurationController
|
||||
|
||||
import java.util.ArrayList
|
||||
|
||||
class ConfigurationControllerImpl(context: Context)
|
||||
: ConfigurationController, ConfigurationChangedReceiver {
|
||||
|
||||
private val listeners: MutableList<ConfigurationController.ConfigurationListener> = ArrayList()
|
||||
private val lastConfig = Configuration()
|
||||
private var density: Int = 0
|
||||
private var fontScale: Float = 0.toFloat()
|
||||
private val inCarMode: Boolean
|
||||
private var uiMode: Int = 0
|
||||
private var localeList: LocaleList? = null
|
||||
|
||||
init {
|
||||
val currentConfig = context.resources.configuration
|
||||
fontScale = currentConfig.fontScale
|
||||
density = currentConfig.densityDpi
|
||||
inCarMode = currentConfig.uiMode and Configuration.UI_MODE_TYPE_MASK ==
|
||||
Configuration.UI_MODE_TYPE_CAR
|
||||
uiMode = currentConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK
|
||||
localeList = currentConfig.locales
|
||||
}
|
||||
|
||||
override fun notifyThemeChanged() {
|
||||
val listeners = ArrayList(listeners)
|
||||
|
||||
listeners.filterForEach({ this.listeners.contains(it) }) {
|
||||
it.onThemeChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
// Avoid concurrent modification exception
|
||||
val listeners = ArrayList(listeners)
|
||||
|
||||
listeners.filterForEach({ this.listeners.contains(it) }) {
|
||||
it.onConfigChanged(newConfig)
|
||||
}
|
||||
val fontScale = newConfig.fontScale
|
||||
val density = newConfig.densityDpi
|
||||
val uiMode = newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK
|
||||
val uiModeChanged = uiMode != this.uiMode
|
||||
if (density != this.density || fontScale != this.fontScale ||
|
||||
inCarMode && uiModeChanged) {
|
||||
listeners.filterForEach({ this.listeners.contains(it) }) {
|
||||
it.onDensityOrFontScaleChanged()
|
||||
}
|
||||
this.density = density
|
||||
this.fontScale = fontScale
|
||||
}
|
||||
|
||||
val localeList = newConfig.locales
|
||||
if (localeList != this.localeList) {
|
||||
this.localeList = localeList
|
||||
listeners.filterForEach({ this.listeners.contains(it) }) {
|
||||
it.onLocaleListChanged()
|
||||
}
|
||||
}
|
||||
|
||||
if (uiModeChanged) {
|
||||
this.uiMode = uiMode
|
||||
listeners.filterForEach({ this.listeners.contains(it) }) {
|
||||
it.onUiModeChanged()
|
||||
}
|
||||
}
|
||||
|
||||
if (lastConfig.updateFrom(newConfig) and ActivityInfo.CONFIG_ASSETS_PATHS != 0) {
|
||||
listeners.filterForEach({ this.listeners.contains(it) }) {
|
||||
it.onOverlayChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun addCallback(listener: ConfigurationController.ConfigurationListener) {
|
||||
listeners.add(listener)
|
||||
listener.onDensityOrFontScaleChanged()
|
||||
}
|
||||
|
||||
override fun removeCallback(listener: ConfigurationController.ConfigurationListener) {
|
||||
listeners.remove(listener)
|
||||
}
|
||||
}
|
||||
|
||||
// This could be done with a Collection.filter and Collection.forEach, but Collection.filter
|
||||
// creates a new array to store them in and we really don't need that here, so this provides
|
||||
// a little more optimized inline version.
|
||||
inline fun <T> Collection<T>.filterForEach(f: (T) -> Boolean, execute: (T) -> Unit) {
|
||||
forEach {
|
||||
if (f.invoke(it)) {
|
||||
execute.invoke(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,28 +21,12 @@ LOCAL_MODULE_TAGS := tests
|
||||
LOCAL_JACK_FLAGS := --multi-dex native
|
||||
LOCAL_DX_FLAGS := --multi-dex
|
||||
|
||||
LOCAL_PROTOC_OPTIMIZE_TYPE := nano
|
||||
LOCAL_PROTOC_FLAGS := -I$(LOCAL_PATH)/..
|
||||
LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors
|
||||
|
||||
LOCAL_PACKAGE_NAME := SystemUITests
|
||||
LOCAL_PRIVATE_PLATFORM_APIS := true
|
||||
LOCAL_COMPATIBILITY_SUITE := device-tests
|
||||
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
|
||||
$(call all-Iaidl-files-under, src)
|
||||
|
||||
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
|
||||
|
||||
LOCAL_STATIC_ANDROID_LIBRARIES := \
|
||||
SystemUI-core
|
||||
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := \
|
||||
metrics-helper-lib \
|
||||
android-support-test \
|
||||
mockito-target-inline-minus-junit4 \
|
||||
testables \
|
||||
truth-prebuilt \
|
||||
SystemUI-tests
|
||||
|
||||
LOCAL_MULTILIB := both
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
tools:replace="android:authorities"
|
||||
android:authorities="${applicationId}.lifecycle-tests"
|
||||
android:exported="false"
|
||||
android:enabled="false"
|
||||
android:multiprocess="true" />
|
||||
<provider android:name="com.android.systemui.keyguard.KeyguardSliceProvider"
|
||||
android:authorities="com.android.systemui.test.keyguard.disabled"
|
||||
@@ -83,6 +84,7 @@
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="com.android.systemui.test.fileprovider"
|
||||
android:exported="false"
|
||||
android:enabled="false"
|
||||
tools:replace="android:authorities"
|
||||
android:grantUriPermissions="true" />
|
||||
</application>
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.android.systemui.statusbar.phone
|
||||
|
||||
import android.support.test.filters.SmallTest
|
||||
import android.testing.AndroidTestingRunner
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mockito.doAnswer
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.verify
|
||||
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@SmallTest
|
||||
class ConfigurationControllerImplTest : SysuiTestCase() {
|
||||
|
||||
private val mConfigurationController =
|
||||
com.android.systemui.statusbar.phone.ConfigurationControllerImpl(mContext)
|
||||
|
||||
@Test
|
||||
fun testThemeChange() {
|
||||
val listener = mock(ConfigurationListener::class.java)
|
||||
mConfigurationController.addCallback(listener)
|
||||
|
||||
mConfigurationController.notifyThemeChanged()
|
||||
verify(listener).onThemeChanged()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRemoveListenerDuringCallback() {
|
||||
val listener = mock(ConfigurationListener::class.java)
|
||||
mConfigurationController.addCallback(listener)
|
||||
val listener2 = mock(ConfigurationListener::class.java)
|
||||
mConfigurationController.addCallback(listener2)
|
||||
|
||||
doAnswer {
|
||||
mConfigurationController.removeCallback(listener2)
|
||||
null
|
||||
}.`when`(listener).onThemeChanged()
|
||||
|
||||
mConfigurationController.notifyThemeChanged()
|
||||
verify(listener).onThemeChanged()
|
||||
verify(listener2, never()).onThemeChanged()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user