From a199eaa1710e89c485d781ce3dd30f9783a6b775 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 13 Mar 2019 17:00:30 +0100 Subject: [PATCH] Setup various tools --- .github/PULL_REQUEST_TEMPLATE.md | 10 ++ .gitignore | 2 + .travis.yml | 58 +++++++ AUTHORS.md | 0 CHANGES.md | 49 ++++++ CONTRIBUTING.md | 76 +++++++++ README.md | 2 +- app/build.gradle | 78 ++++++++- app/src/main/res/values/config.xml | 30 ++++ tools/check/check_code_quality.sh | 141 ++++++++++++++++ tools/check/forbidden_strings_in_code.txt | 151 ++++++++++++++++++ .../check/forbidden_strings_in_resources.txt | 84 ++++++++++ tools/debug_alter_scalar_token.sh | 3 + tools/debug_dump_filesystem.sh | 3 + tools/debug_dump_prefs.sh | 3 + tools/tests/test_boot_complete.sh | 3 + tools/tests/test_referrer.sh | 8 + tools/travis/check_pr.sh | 45 ++++++ 18 files changed, 743 insertions(+), 3 deletions(-) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .travis.yml create mode 100644 AUTHORS.md create mode 100644 CHANGES.md create mode 100644 CONTRIBUTING.md create mode 100755 app/src/main/res/values/config.xml create mode 100755 tools/check/check_code_quality.sh create mode 100644 tools/check/forbidden_strings_in_code.txt create mode 100644 tools/check/forbidden_strings_in_resources.txt create mode 100755 tools/debug_alter_scalar_token.sh create mode 100755 tools/debug_dump_filesystem.sh create mode 100755 tools/debug_dump_prefs.sh create mode 100755 tools/tests/test_boot_complete.sh create mode 100755 tools/tests/test_referrer.sh create mode 100755 tools/travis/check_pr.sh diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..01cdae74dd --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,10 @@ +### Pull Request Checklist + + + +- [ ] Changes has been tested on an Android device or Android emulator with API 16 +- [ ] UI change has been tested on both light and dark themes +- [ ] Pull request is based on the develop branch +- [ ] Pull request updates [CHANGES.md](https://github.com/vector-im/riotX-android/blob/develop/CHANGES.md) +- [ ] Pull request includes screenshots or videos if containing UI changes +- [ ] Pull request includes a [sign off](https://github.com/matrix-org/synapse/blob/master/CONTRIBUTING.rst#sign-off) diff --git a/.gitignore b/.gitignore index a668f05331..0722110715 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ /build /captures .externalNativeBuild + +/tmp diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..a41eba004f --- /dev/null +++ b/.travis.yml @@ -0,0 +1,58 @@ +# FTR: Configuration on https://travis-ci.org/vector-im/riot-android/settings +# +# - Build only if .travis.yml is present -> On +# - Limit concurrent jobs -> Off +# - Build pushed branches -> On (build the branch) +# - Build pushed pull request -> On (build the PR after auto-merge) +# +# - Auto cancel branch builds -> On +# - Auto cancel pull request builds -> On + +language: android +jdk: oraclejdk8 +sudo: false + +notifications: + email: false + +android: + components: + # Uncomment the lines below if you want to + # use the latest revision of Android SDK Tools + - tools + - platform-tools + + # The BuildTools version used by your project + - build-tools-27.0.3 + + # The SDK version used to compile your project + - android-27 + +before_cache: + - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock + - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ + +cache: + directories: + - $HOME/.gradle/caches/ + - $HOME/.gradle/wrapper/ + - $HOME/.android/build-cache + +# Build with the development SDK +before_script: + # Not necessary for the moment + # - /bin/sh ./set_debug_env.sh + +# Just build the project for now +script: + # Build app (assembleAppRelease assembleAppfdroidRelease) + # Build Android test (assembleAndroidTest) + # Code quality (lintAppRelease lintAppfdroidRelease) + - ./gradlew clean assembleAppgplayRelease assembleAppfdroidRelease assembleAndroidTest lintAppgplayRelease lintAppfdroidRelease --stacktrace + # Run unitary test (Disable for now, see https://travis-ci.org/vector-im/riot-android/builds/502504370) + # - ./gradlew testAppgplayReleaseUnitTest --stacktrace + # Other code quality check + - ./tools/check/check_code_quality.sh + - ./tools/travis/check_pr.sh + # Check that indonesians file are identical. Due to Android issue, the resource folder must be value-in/, and Weblate export data into value-id/. + - diff ./app/src/main/res/values-id/strings.xml ./app/src/main/res/values-in/strings.xml diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/CHANGES.md b/CHANGES.md new file mode 100644 index 0000000000..ad6294c176 --- /dev/null +++ b/CHANGES.md @@ -0,0 +1,49 @@ +Changes in RiotX 0.XX (2019-XX-XX) +=================================================== + +Features: + - + +Improvements: + - + +Other changes: + - + +Bugfix: + - + +Translations: + - + +Build: + - + + + +======================================================= ++ TEMPLATE WHEN PREPARING A NEW RELEASE + +======================================================= + + +Changes in RiotX 0.XX (2019-XX-XX) +=================================================== + +Features: + - + +Improvements: + - + +Other changes: + - + +Bugfix: + - + +Translations: + - + +Build: + - + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..6aecbdc451 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,76 @@ +# Contributing code to Matrix + +Please read https://github.com/matrix-org/synapse/blob/master/CONTRIBUTING.rst + +Android support can be found in this [![Riot Android Matrix room #riot-android:matrix.org](https://img.shields.io/matrix/riot-android:matrix.org.svg?label=%23riot-android:matrix.org)](https://matrix.to/#/#riot-android:matrix.org) room. + +Dedicated room for RiotX: [![RiotX Android Matrix room #riot-android:matrix.org](https://img.shields.io/matrix/riotx:matrix.org.svg?label=%23RiotX:matrix.org)](https://matrix.to/#/#riotx:matrix.org) + +# Specific rules for Matrix Android projects + +## Android Studio settings + +Please set the "hard wrap" setting of Android Studio to 160 chars, this is the setting we use internally to format the source code (Menu `Settings/Editor/Code Style` then `Hard wrap at`). + +## Compilation + +For now, the Matrix SDK and the RiotX application are in the same project. So there is no specific thing to do, this project should compile without any special action. + +## I want to help translating RiotX + +If you want to fix an issue with an English string, please submit a PR. +If you want to fix an issue in other languages, or add a missing translation, or even add a new language, please use [Weblate](https://translate.riot.im/projects/riot-android/). + +For the moment, Strings from Riot will be used, there is no dedicated project in Weblate for RiotX. + +## I want to submit a PR to fix an issue + +Please check if a corresponding issue exists. If yes, please let us know in a comment that you're working on it. +If an issue does not exist yet, it may be relevant to open a new issue and let us know that you're implementing it. + +### Kotlin + +This project is full Kotlin. Please do not write Java classes. + +### CHANGES.md + +Please add a line to the top of the file `CHANGES.md` describing your change. + +### Code quality + +Make sure the following commands execute without any error: + +> ./tools/check/check_code_quality.sh + +> ./gradlew lintAppgplayRelease + +### Unit tests + +Make sure the following commands execute without any error: + +> ./gradlew testAppgplayReleaseUnitTest + +### Tests + +RiotX is currently supported on Android Jelly Bean (API 16+): please test your change on an Android device (or Android emulator) running with API 16. Many issues can happen (including crashes) on older devices. +Also, if possible, please test your change on a real device. Testing on Android emulator may not be sufficient. + +### Internationalisation + +When adding new string resources, please only add new entries in file `value/strings.xml`. Translations will be added later by the community of translators with a specific tool named [Weblate](https://translate.riot.im/projects/riot-android/). +Do not hesitate to use plurals when appropriate. + +### Layout + +When adding or editing layouts, make sure the layout will render correctly if device uses a RTL (Right To Left) language. +You can check this in the layout editor preview by selecting any RTL language (ex: Arabic). + +Also please check that the colors are ok for all the current themes of RiotX. Please use `?attr` instead of `@color` to reference colors in the layout. You can check this in the layout editor preview by selecting all the main themes (`AppTheme.Status`, `AppTheme.Dark`, etc.). + +### Authors + +Feel free to add an entry in file AUTHORS.md + +## Thanks + +Thanks for contributing to Matrix projects! diff --git a/README.md b/README.md index 69628f7a5d..60c9d30e9b 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,6 @@ RiotX is an Android Matrix Client currently in development. It's based on a new Matrix SDK, written in Kotlin. -Download nighly build here: https://matrix.org/jenkins/job/RiotXAndroidDevelop/ +Download nightly build here: https://matrix.org/jenkins/job/RiotXAndroidDevelop/ Matrix Room: [#riotx:matrix.org](https://matrix.to/#/#riotx:matrix.org) diff --git a/app/build.gradle b/app/build.gradle index 9d718815c2..4665fa25d3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -15,7 +15,7 @@ def versionMajor = 0 def versionMinor = 1 def versionPatch = 0 -def generateVersionCodeFromTimestamp() { +static def generateVersionCodeFromTimestamp() { // It's unix timestamp divided by 10: It's incremented by one every 10 seconds. return (System.currentTimeMillis() / 1_000 / 10).toInteger() } @@ -24,6 +24,21 @@ def generateVersionCodeFromVersionName() { return versionMajor * 10000 + versionMinor * 100 + versionPatch } +static def gitRevision() { + def cmd = "git rev-parse --short HEAD" + return cmd.execute().text.trim() +} + +static def gitRevisionDate() { + def cmd = "git show -s --format=%ci HEAD^{commit}" + return cmd.execute().text.trim() +} + +static def gitBranchName() { + def cmd = "git name-rev --name-only HEAD" + return cmd.execute().text.trim() +} + project.android.buildTypes.all { buildType -> buildType.javaCompileOptions.annotationProcessorOptions.arguments = [ @@ -31,6 +46,8 @@ project.android.buildTypes.all { buildType -> ] } +def buildNumber = System.getenv("BUILD_NUMBER") as Integer ?: 0 + android { compileSdkVersion 28 defaultConfig { @@ -40,8 +57,15 @@ android { multiDexEnabled true versionCode generateVersionCodeFromTimestamp() versionName "${versionMajor}.${versionMinor}.${versionPatch}" + + resValue "string", "git_revision", "\"${gitRevision()}\"" + resValue "string", "git_revision_date", "\"${gitRevisionDate()}\"" + resValue "string", "git_branch_name", "\"${gitBranchName()}\"" + resValue "string", "build_number", "\"${buildNumber}\"" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } + buildTypes { debug { resValue "bool", "debug_mode", "true" @@ -55,11 +79,61 @@ android { } } + flavorDimensions "store" + + productFlavors { + appgplay { + dimension "store" + + buildConfigField "boolean", "ALLOW_FCM_USE", "true" + buildConfigField "String", "SHORT_FLAVOR_DESCRIPTION", "\"G\"" + buildConfigField "String", "FLAVOR_DESCRIPTION", "\"GooglePlay\"" + } + + appfdroid { + dimension "store" + + buildConfigField "boolean", "ALLOW_FCM_USE", "false" + buildConfigField "String", "SHORT_FLAVOR_DESCRIPTION", "\"F\"" + buildConfigField "String", "FLAVOR_DESCRIPTION", "\"FDroid\"" + } + } + + lintOptions { + warning 'MissingTranslation' + + // Treat some warnings as errors + // Resources + error 'TypographyEllipsis' + warning 'ImpliedQuantity' + + // UX + error 'ButtonOrder' + + // Layout + error 'StringFormatCount' + error 'HardcodedText' + error 'SpUsage' + error 'ObsoleteLayoutParam' + error 'InefficientWeight' + error 'DisableBaselineAlignment' + error 'ScrollViewSize' + + // RTL + error 'RtlEnabled' + error 'RtlHardcoded' + error 'RtlSymmetry' + + // Code + error 'SetTextI18n' + error 'ViewConstructor' + error 'UseValueOf' + } + compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } - } dependencies { diff --git a/app/src/main/res/values/config.xml b/app/src/main/res/values/config.xml new file mode 100755 index 0000000000..696a98f46b --- /dev/null +++ b/app/src/main/res/values/config.xml @@ -0,0 +1,30 @@ + + + + Riot.im + + + https://vector.im + https://matrix.org + https://matrix.org + https://vector.im + "https://piwik.riot.im" + + + "https://scalar-staging.riot.im/scalar-web/" + "https://scalar-staging.riot.im/scalar/api" + + + https://scalar-staging.riot.im/scalar/api + https://scalar.vector.im/api + + + + + + + + matrix.org + + + diff --git a/tools/check/check_code_quality.sh b/tools/check/check_code_quality.sh new file mode 100755 index 0000000000..2e7ef32c75 --- /dev/null +++ b/tools/check/check_code_quality.sh @@ -0,0 +1,141 @@ +#!/usr/bin/env bash + +# +# Copyright 2019 New Vector Ltd +# +# 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. +# + +####################################################################################################################### +# Check drawable quantity +####################################################################################################################### + +echo "Check drawable quantity" + +numberOfFiles1=`ls -1U ./app/src/main/res/drawable-hdpi | wc -l | sed "s/ //g"` +numberOfFiles2=`ls -1U ./app/src/main/res/drawable-mdpi | wc -l | sed "s/ //g"` +numberOfFiles3=`ls -1U ./app/src/main/res/drawable-xhdpi | wc -l | sed "s/ //g"` +numberOfFiles4=`ls -1U ./app/src/main/res/drawable-xxhdpi | wc -l | sed "s/ //g"` +numberOfFiles5=`ls -1U ./app/src/main/res/drawable-xxxhdpi | wc -l | sed "s/ //g"` + +if [[ ${numberOfFiles1} -eq ${numberOfFiles5} ]] && [[ ${numberOfFiles2} -eq ${numberOfFiles5} ]] && [[ ${numberOfFiles3} -eq ${numberOfFiles5} ]] && [[ ${numberOfFiles4} -eq ${numberOfFiles5} ]]; then + resultNbOfDrawable=0 + echo "OK" +else + resultNbOfDrawable=1 + echo "ERROR, missing drawable alternative." +fi + +echo + +####################################################################################################################### +# Search forbidden pattern +####################################################################################################################### + +searchForbiddenStringsScript=./tmp/search_forbidden_strings.pl + +if [[ -f ${searchForbiddenStringsScript} ]]; then + echo "${searchForbiddenStringsScript} already there" +else + mkdir tmp + echo "Get the script" + wget https://raw.githubusercontent.com/matrix-org/matrix-dev-tools/develop/bin/search_forbidden_strings.pl -O ${searchForbiddenStringsScript} +fi + +if [[ -x ${searchForbiddenStringsScript} ]]; then + echo "${searchForbiddenStringsScript} is already executable" +else + echo "Make the script executable" + chmod u+x ${searchForbiddenStringsScript} +fi + +echo +echo "Search for forbidden patterns in code..." + +${searchForbiddenStringsScript} ./tools/check/forbidden_strings_in_code.txt \ + ./app/src/main/java + +resultForbiddenStringInCode=$? + +echo +echo "Search for forbidden patterns in resources..." + +${searchForbiddenStringsScript} ./tools/check/forbidden_strings_in_resources.txt \ + ./app/src/main/res/color \ + ./app/src/main/res/layout \ + ./app/src/main/res/menu \ + ./app/src/main/res/values \ + ./app/src/main/res/values-v21 \ + ./app/src/main/res/xml + +resultForbiddenStringInResource=$? + +####################################################################################################################### +# Check files with long lines +####################################################################################################################### + +checkLongFilesScript=./tmp/check_long_files.pl + +if [[ -f ${checkLongFilesScript} ]]; then + echo "${checkLongFilesScript} already there" +else + mkdir tmp + echo "Get the script" + wget https://raw.githubusercontent.com/matrix-org/matrix-dev-tools/develop/bin/check_long_files.pl -O ${checkLongFilesScript} +fi + +if [[ -x ${checkLongFilesScript} ]]; then + echo "${checkLongFilesScript} is already executable" +else + echo "Make the script executable" + chmod u+x ${checkLongFilesScript} +fi + +echo +echo "Search for long files..." + +${checkLongFilesScript} 1000 \ + ./app/src/main/java \ + ./app/src/main/res/layout \ + ./app/src/main/res/values \ + ./app/src/main/res/values-v21 \ + +resultLongFiles=$? + +####################################################################################################################### +# search png in drawable folder +####################################################################################################################### + +echo +echo "Search for png files in /drawable..." + +ls -1U ./app/src/main/res/drawable/*.png +resultTmp=$? + +# Inverse the result, cause no file found is an error for ls but this is what we want! +if [[ ${resultTmp} -eq 0 ]]; then + echo "ERROR, png files detected in /drawable" + resultPngInDrawable=1 +else + echo "OK" + resultPngInDrawable=0 +fi + +echo + +if [[ ${resultNbOfDrawable} -eq 0 ]] && [[ ${resultForbiddenStringInCode} -eq 0 ]] && [[ ${resultForbiddenStringInResource} -eq 0 ]] && [[ ${resultLongFiles} -eq 0 ]] && [[ ${resultPngInDrawable} -eq 0 ]]; then + echo "MAIN OK" +else + echo "❌ MAIN ERROR" + exit 1 +fi diff --git a/tools/check/forbidden_strings_in_code.txt b/tools/check/forbidden_strings_in_code.txt new file mode 100644 index 0000000000..13328c3b22 --- /dev/null +++ b/tools/check/forbidden_strings_in_code.txt @@ -0,0 +1,151 @@ +# +# Copyright 2018 New Vector Ltd +# +# 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. +# + +# This file list String which are not allowed in source code. +# Use Perl regex to write forbidden strings +# Note: line cannot start with a space. Use \s instead. +# It is possible to specify an authorized number of occurrence with === suffix. Default is 0 +# Example: +# AuthorizedStringThreeTimes===3 + +# Extension:java +# Extension:kt + +# Use new SecureLinearLayoutManager +# DISABLED +#new LinearLayoutManager + +### No import static: use full class name +import static + +### Rubbish from merge. Please delete those lines (sometimes in comment) +<<<<<<< +>>>>>>> + +### carry return before "}". Please remove empty lines. +\n\s*\n\s*\} + +### typo detected. +formated +abtract +Succes[^s] +succes[^s] + +### Please insert line break. ex: .flatMap() not at new line +\}\)\.[\w] + +### Use int instead of Integer +protected Integer + +### Use the interface declaration. Example: use type "Map" instead of type "HashMap" to declare variable or parameter +(private|public|protected| ) (static )?(final )?(HashMap|HashSet|ArrayList)< + +### Use int instead of short +Short\.parseShort +\(short\) +private short +final short + +### Line length is limited to 160 chars. Please split long lines +.{161} + +### "DO NOT COMMIT" has been committed +DO NOT COMMIT + +### invalid formatting +\s{8}/\*\n \* +[^\w]if\( +while\( +for\( + +# Add space after // +# DISABLED To re-enable when code will be formatted globally +#^\s*//[^\s] + +# Not usable with unitary test. Use StringUtils +# DISABLED +#TextUtils\.isEmpty\( + +### invalid formatting (too many space char) +^ /\* + +# No ternary operator +# DISABLED +# \? + +### unnecessary parenthesis around numbers, example: " (0)" + \(\d+\) + +### Malformatted comment +^ \* + +### import the package, do not use long class name with package +android\.os\.Build\. + +### Tab char is forbidden. Use only spaces +\t + +# Empty lines and trailing space +# DISABLED To re-enable when code will be formatted globally +#[ ]$ + +### Deprecated, use retrofit2.HttpException +import retrofit2\.adapter\.rxjava\.HttpException + +### This is generally not necessary, no need to reset the padding if there is no drawable +setCompoundDrawablePadding\(0\) + +### Deprecated use class form SDK API 26 +ButterKnife\.findById\( + +# Change thread with Rx +# DISABLED +#runOnUiThread + +### Bad formatting of chain (missing new line) +\w\.flatMap\( +\w\.map\( + +### Bad formatting of Realm query chain. Insert new line +\)\.equalTo +\)\.findAll + +# Use StandardCharsets.UTF_8.name() +# DISABLED (min API to low) +#\"UTF- + +### Directly use getString() in a Fragment +getActivity\(\)\.getString\( + +### In Kotlin, Void has to be null safe, i.e. use 'Void?' instead of 'Void' +\: Void\) + +### Home menu click is managed in parent Activity, with one exception +android\.R\.id\.home===2 + +### Kotlin conversion tools introduce this, but is can be replace by trim() +trim \{ it \<\= \' \' \} + +### Use AlertDialog form v7 compat lib +android\.app\.AlertDialog + +### Put the operator at the beginning of next line +&&$ +\|\|$ + ==$ + +### Use JsonUtils.getBasicGson() +new Gson\(\) diff --git a/tools/check/forbidden_strings_in_resources.txt b/tools/check/forbidden_strings_in_resources.txt new file mode 100644 index 0000000000..26d7725591 --- /dev/null +++ b/tools/check/forbidden_strings_in_resources.txt @@ -0,0 +1,84 @@ +# +# Copyright 2018 New Vector Ltd +# +# 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. +# + +# This file list String which are not allowed in source code. +# Use Perl regex to write forbidden strings +# Note: line cannot start with a space. Use \s instead. +# It is possible to specify an authorized number of occurrence with === suffix. Default is 0 +# Example: +# AuthorizedStringThreeTimes===3 + +# Extension:xml + + +### Rubbish from merge. Please delete those lines (sometimes in comment) +<<<<<<< +>>>>>>> + +### Hardcoded string are forbidden. Please create a string resource +app\:emptyLabelText=\"[^@] +android\:text=\"[^@] +android\:hint=\"[^@] +# (with tolerance for empty string) +android\:title=\"[^@"] +android\:contentDescription=\"[^@] +# (with tolerance for summary="%s") +android\:summary=\"[^@|\%s] +app\:ms_floatingLabelText=\"[^@] +app\:ms_hint=\"[^@] + +### "DO NOT COMMIT" has been committed +DO NOT COMMIT + +### Tab char is forbidden. Use only spaces +\t + +### Remove space in empty lines and trailing space +[ ]$ + +# Use project color +# DISABLED +#@android\:color\/ + +# String in multiline +# DISABLED +#) +\"><\/ + +### Bad comment format in XML resources. Use instead of // +^\s*\/\/ + +### Bad RTL support, use attribute with Start and End +layout_constraintRight_ +layout_constraintLeft_ + +### Use Preference from v7 library (android.support.v7.preference.PreferenceScreen) +