detekt


Source link: https://github.com/arturbosch/detekt

detekt

Meet detekt, a static code analysis tool for the Kotlin programming language. It operates on the abstract syntax tree provided by the Kotlin compiler.

Features

  • code smell analysis for your kotlin projects
  • complexity report based on logical lines of code, McCabe complexity and amount of code smells
  • highly configurable (rule set or rule level)
  • suppress findings with Kotlin's @Suppress and Java's @SuppressWarnings annotations
  • specify code smell thresholds to break your build or print a warning
  • code Smell baseline and ignore lists for legacy projects
  • gradle plugin for code analysis, formatting and import migration
  • gradle tasks to use local intellij distribution for formatting and inspecting kotlin code
  • optionally configure detekt for each sub module by using profiles (gradle-plugin)
  • sonarqube integration
  • NEW extensible by own rule sets and FileProcessListener's

Table of contents

  1. Commandline interface
  2. Gradle plugin
    1. in groovy dsl
    2. in kotlin dsl
    3. in android projects
    4. plugin tasks
    5. detekt-closure
  3. Standalone gradle task
  4. Standalone maven task
  5. Rule sets
  6. Rule set configuration
  7. Suppress rules
  8. Build failure
  9. Extending detekt
    1. RuleSets
    2. Processors
    3. Reports
    4. Rule testing
  10. Black- and Whitelist code smells
  11. Contributors

Build & use the commandline interface

  • git clone https://github.com/arturbosch/detekt
  • cd detekt
  • ./gradlew build shadow
  • java -jar detekt-cli/build/libs/detekt-cli-[version]-all.jar [parameters]*
Parameters for CLI

The following parameters are shown when --help is entered. The --input/ -i option is required:

Usage: detekt [options]
Options:
  --baseline, -b

 If a baseline xml file is passed in, only new code smells not in the

  baseline are printed in the console.
  --config, -c

 Path to the config file (path/to/config.yml).
  --config-resource, -cr

 Path to the config resource on detekt's classpath (path/to/config.yml).
  --create-baseline, -cb

 Treats current analysis findings as a smell baseline for future detekt

 runs.

  Default: false
  --debug

 Debugs given ktFile by printing its elements.

 Default: false
  --disable-default-rulesets, -dd

 Disables default rule sets.

 Default: false
  --filters, -f

 Path filters defined through regex with separator ';' (".*test.*").
  --generate-config, -gc

 Export default config to default-detekt-config.yml.

 Default: false
  --help, -h

 Shows the usage.
* --input, -i

 Input path to analyze (path/to/project).
  --output, -o

 Directory where output reports are stored.
  --output-name, -on

 The base name for output reports is derived from this parameter.
  --parallel

 Enables parallel compilation of source files. Should only be used if the

  analyzing project has more than ~200 kotlin files.

 Default: false
  --plugins, -p

 Extra paths to plugin jars separated by ',' or ';'. 

--input can either be a directory or a single Kotlin file. The currently only supported configuration format is yaml. --config should point to one. Generating a default configuration file is as easy as using the --generate-config parameter. filters can be used for example to exclude all test directories. With rules you can point to additional ruleset.jar's creating by yourself or others. More on this topic see section Custom RuleSets.

Using the detekt-gradle-plugin

Use the groovy or kotlin dsl of gradle and configure the detekt closure as described here.

Configuration when using groovy dsl

For gradle version >= 2.1

buildscript {

  repositories {

jcenter()
  
}
 
}
  plugins {

  id "io.gitlab.arturbosch.detekt" version "1.0.0.[version]" 
}
  detekt {

  version = "1.0.0.[version]"
  profile("main") {

input = "$projectDir/src/main/kotlin"

config = "$projectDir/detekt.yml"

filters = ".*test.*,.*/resources/.*,.*/tmp/.*"
  
}
 
}

For all gradle versions:

buildscript {

repositories {

  jcenter()
  maven {
 url "https://plugins.gradle.org/m2/" 
}

}

dependencies {

  classpath "gradle.plugin.io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.0.0.[version]"

}
 
}
  apply plugin: "io.gitlab.arturbosch.detekt"  detekt {

  version = "1.0.0.[version]"
  profile("main") {

input = "$projectDir/src/main/kotlin"

config = "$projectDir/detekt.yml"

filters = ".*test.*,.*/resources/.*,.*/tmp/.*"
  
}
 
}
Configuration when using kotlin dsl

For gradle version >= 3.5

import io.gitlab.arturbosch.detekt.DetektExtension  buildscript {

  repositories {

jcenter()
  
}
 
}
 plugins {

  id("io.gitlab.arturbosch.detekt").version("1.0.0.[version]") 
}
  configure<DetektExtension> {

  version = "1.0.0.[version]"
  profile("main") {

input = "$projectDir/src/main/kotlin"

config = "$projectDir/detekt.yml"

filters = ".*test.*,.*/resources/.*,.*/tmp/.*"
  
}
 
}
Configuration for Android projects

When using Android make sure to have detekt configured in the project level build.gradle file. The new preferred plugin configuration way is used, the old way is commented out.

buildscript {

  repositories {
 //

  maven {
 url "https://plugins.gradle.org/m2/" 
}


jcenter()
  
}

  dependencies {

classpath 'com.android.tools.build:gradle:2.3.3' //

  classpath "gradle.plugin.io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.0.0.[version]"
  
}
  
}
 plugins {

  id "io.gitlab.arturbosch.detekt" version "1.0.0.[version]" 
}
  //apply plugin: 'io.gitlab.arturbosch.detekt'  allprojects {

  repositories {

jcenter()
  
}
 
}
  task clean(type: Delete) {

  delete rootProject.buildDir 
}
  detekt {

  version = "1.0.0.[version]"
  profile("main") {

input = "$projectDir/your/app"

config = "$projectDir/detekt.yml"

filters = ".*test.*,.*/resources/.*,.*/tmp/.*"
  
}
 
}

Available plugin tasks

  • detektCheck - Runs a detekt analysis and complexity report. Configure the analysis inside the detekt-closure. By default the standard rule set is used without output report or black- and whitelist checks.
  • detektGenerateConfig - Generates a default detekt config file into your projects location.
  • detektBaseline - Like detektCheck, but creates a code smell baseline. Further detekt runs will only feature new smells not in this list.
  • detektMigrate - Experimental feature for now. Just supports migration of specified imports. See migration section.
  • detektIdeaFormat - Uses a local idea installation to format your kotlin (and other) code according to the specified code-style.xml.
  • detektIdeaInspect Uses a local idea installation to run inspections on your kotlin (and other) code according to the specified inspections.xml profile.
Options for detekt configuration closure
detekt {

  version = "1.0.0.[version]"  // When unspecified the latest detekt version found, will be used. Override to stay on the same version.

  // A profile basically abstracts over the argument vector passed to detekt. 

// Different profiles can be specified and used for different sub modules or testing code.
  profile("main") {

input = "$projectDir/src/main/kotlin" // Which part of your project should be analyzed?

config = "$projectDir/detekt.yml" // Use $project.projectDir or to navigate inside your project 

configResource = "/detekt.yml" // Use this parameter instead of config if your detekt yaml file is inside your resources. Is needed for multi project maven tasks.

filters = ".*test.*, .*/resources/.*" // What paths to exclude? Use comma oder semicolon to separate

ruleSets = "other/optional/ruleset.jar" // Custom rule sets can be linked to this, use comma oder semicolon to separate, remove if unused.

disableDefaultRuleSets = false // Disables the default rule set. Just use detekt as the detection engine with your custom rule sets.

output = "$project.projectDir/reports/detekt.xml" // If present, prints all findings into that file.

outputName = "my-module" // This parameter is used to derive the output report name

baseline = "$project.projectDir/reports/baseline.xml" // If present all current findings are saved in a baseline.xml to only consider new code smells for further runs.

parallel = true // Use this flag if your project has more than 200 files. 
 
}

  // Definines a secondary profile `gradle detektCheck -Ddetekt.profile=override` will use this profile. 
 // The main profile gets always loaded but specified profiles override main profiles parameters.
 profile("override") {

  config = "$projectDir/detekt-test-config.yml"
 
}
 
}
Configure a local idea for detekt
  • download the community edition of intellij
  • extract the file to your preferred location eg. ~/.idea
  • let detekt know about idea inside the detekt-closure
  • extract code-style.xml and inpect.xml from idea settings ( Settings>CodeStyle>Scheme and Settings>Inspections>Profile)
  • run detektIdeaFormat or detektIdeaInspect
  • all parameters in the following detekt-closure are mandatory for both tasks
  • make sure that current or default profile have an input path specified!
String USER_HOME = System.getProperty("user.home")  detekt {

 profile("main") {

input = "$projectDir/src/main/kotlin"

output = "$projectDir/reports/report.xml"

outputFormat = "xml"
  
}

  idea {

path = "$USER_HOME/.idea"

codeStyleScheme = "$USER_HOME/.idea/idea-code-style.xml"

inspectionsProfile = "$USER_HOME/.idea/inspect.xml"

report = "project.projectDir/reports"

mask = "*.kt,"
  
}
 
}

For more information on using idea as a headless formatting/inspection tool see here.

Migration

Migration rules can be configured in your detekt.yml file. For now only migration of imports is supported.

# *experimental feature* # Migration rules can be defined in the same config file or a new one migration:
active: true
imports:
  # your.package.Class: new.package.or.Class
  # for example:
  # io.gitlab.arturbosch.detekt.api.Rule: io.gitlab.arturbosch.detekt.rule.Rule

Using detekt in custom gradle projects

  1. Add following lines to your build.gradle file.
  2. Run gradle detekt
  3. Add check.dependsOn detekt if you want to run detekt on every build
repositories {

  jcenter() 
}
  configurations {
  detekt 
}
  task detekt(type: JavaExec) {
  main = "io.gitlab.arturbosch.detekt.cli.Main"  classpath = configurations.detekt  def input = "$projectDir"  def config = "$projectDir/detekt.yml"  def filters = ".*test.*"  def rulesets = ""  def params = [ '-i', input, '-c', config, '-f', filters, '-r', rulesets]  args(params) 
}
  dependencies {
  detekt 'io.gitlab.arturbosch.detekt:detekt-cli:1.0.0.[version]' 
}

Attention Android Developers! the dependencies section must be at the bottom, after the repository, configurations and task sections!

Using detekt in Maven Projects

  1. Add following lines to your pom.xml.
  2. Run mvn verify (when using the verify phase as I did here)
<build>
  <plugins>

<plugin>

 <groupId>org.apache.maven.plugins</groupId>

 <artifactId>maven-antrun-plugin</artifactId>

 <version>1.8</version>

 <executions>

  <execution>

<!-- This can be run separately with mvn antrun:run@detekt -->

<id>detekt</id>

<phase>verify</phase>

<configuration>

 <target name="detekt">

  <java taskname="detekt" dir="${
basedir
}
" fork="true" failonerror="true"

  classname="io.gitlab.arturbosch.detekt.cli.Main" classpathref="maven.plugin.classpath">

<arg value="-i"/>

<arg value="${
basedir
}
/src"/>

<arg value="-f"/>

<arg value=".*test.*"/>

<arg value="--useTabs"/>

  </java>

 </target>

</configuration>

<goals><goal>run</goal></goals>

  </execution>

 </executions>

 <dependencies>

  <dependency>

<groupId>io.gitlab.arturbosch.detekt</groupId>

<artifactId>detekt-cli</artifactId>

<version>1.0.0.[CURRENT_MILESTONE]</version>

  </dependency>

 </dependencies>

</plugin>
  </plugins> </build>  <pluginRepositories>
<pluginRepository>
  <id>arturbosch-code-analysis</id>
  <name>arturbosch-code-analysis (for detekt)</name>
  <url>https://dl.bintray.com/arturbosch/code-analysis/</url>
  <layout>default</layout>
  <releases>

 <enabled>true</enabled>

 <updatePolicy>never</updatePolicy>
  </releases>
  <snapshots>

 <enabled>false</enabled>

 <updatePolicy>never</updatePolicy>
  </snapshots>
</pluginRepository> </pluginRepositories>

RuleSets

Currently there are seven rule sets which are used per default when running the cli.

  • complexity - has rules to detect LongMethod, LongParameterList, LargeClass, ComplexMethod ... smells
  • code-smell - other rules which can be classified as code smells but do not fit into the complexity category
  • style - detects wildcard imports and naming violations
  • comments - has rules to detect missing KDoc over public members and unnecessary KDoc over private members
  • exceptions - too general exceptions are used in throw and catch statements like RuntimeException, Error or Throwable
  • empty - finds empty block statements
  • potential-bugs - code is structured in a way it can lead to bugs like 'only equals but not hashcode is implemented' or explicit garbage collection calls
  • performance - finds potential performance issues

RuleSet Configuration

To turn off specific rules/rule sets or change threshold values for certain rules a yaml configuration file can be used. There are two approaches to configuring your rulesets.

Copy defaults and modify

Export the default config with the --generate-config flag or copy and modify the detekt-cli/src/main/resources/default-detekt-config.yml for your needs.

potential-bugs:
active: true
DuplicateCaseInWhenExpression:
  active: true
EqualsAlwaysReturnsTrueOrFalse:
  active: false
EqualsWithHashCodeExist:
  active: true
WrongEqualsTypeParameter:
  active: false
ExplicitGarbageCollectionCall:
  active: true
UnreachableCode:
  active: true
LateinitUsage:
  active: false
UnsafeCallOnNullableType:
  active: false
UnsafeCast:
  active: false
UselessPostfixExpression:
  active: false  performance:
active: true
ForEachOnRange:
  active: true
SpreadOperator:
  active: true
UnnecessaryTemporaryInstantiation:
  active: true  exceptions:
active: true
ExceptionRaisedInUnexpectedLocation:
  active: false
  methodNames: 'toString,hashCode,equals,finalize'
SwallowedException:
  active: false
TooGenericExceptionCaught:
  active: true
  exceptions:

 - ArrayIndexOutOfBoundsException

 - Error

 - Exception

 - IllegalMonitorStateException

 - IndexOutOfBoundsException

 - InterruptedException

 - NullPointerException

 - RuntimeException
TooGenericExceptionThrown:
  active: true
  exceptions:

 - Throwable

 - ThrowError

 - ThrowException

 - ThrowNullPointerException

 - ThrowRuntimeException

 - ThrowThrowable
InstanceOfCheckForException:
  active: false
IteratorNotThrowingNoSuchElementException:
  active: false
PrintExceptionStackTrace:
  active: false
RethrowCaughtException:
  active: false
ReturnFromFinally:
  active: false
ThrowingExceptionFromFinally:
  active: false
ThrowingExceptionInMain:
  active: false
ThrowingNewInstanceOfSameException:
  active: false  empty-blocks:
active: true
EmptyCatchBlock:
  active: true
EmptyClassBlock:
  active: true
EmptyDefaultConstructor:
  active: true
EmptyDoWhileBlock:
  active: true
EmptyElseBlock:
  active: true
EmptyFinallyBlock:
  active: true
EmptyForBlock:
  active: true
EmptyFunctionBlock:
  active: true
EmptyIfBlock:
  active: true
EmptyInitBlock:
  active: true
EmptySecondaryConstructor:
  active: true
EmptyWhenBlock:
  active: true
EmptyWhileBlock:
  active: true  complexity:
active: true
LongMethod:
  threshold: 20
LongParameterList:
  threshold: 5
LargeClass:
  threshold: 150
ComplexMethod:
  threshold: 10
TooManyFunctions:
  threshold: 10
ComplexCondition:
  threshold: 3
LabeledExpression:
  active: false
StringLiteralDuplication:
  active: false
  threshold: 2
  ignoreAnnotation: true
  excludeStringsWithLessThan5Characters: true
  ignoreStringsRegex: '$^'  style:
active: true
ReturnCount:
  active: true
  max: 2
NewLineAtEndOfFile:
  active: true
OptionalAbstractKeyword:
  active: true
OptionalWhenBraces:
  active: false
EqualsNullCall:
  active: false
ForbiddenComment:
  active: true
  values: 'TODO:,FIXME:,STOPSHIP:'
ForbiddenImport:
  active: false
  imports: ''
PackageDeclarationStyle:
  active: false
ModifierOrder:
  active: true
MagicNumber:
  active: true
  ignoreNumbers: '-1,0,1,2'
  ignoreHashCodeFunction: false
  ignorePropertyDeclaration: false
  ignoreAnnotation: false
  ignoreNamedArgument: true
WildcardImport:
  active: true
SafeCast:
  active: true
MaxLineLength:
  active: true
  maxLineLength: 120
  excludePackageStatements: false
  excludeImportStatements: false
PackageNaming:
  active: true
  packagePattern: '^[a-z]+(\.[a-z][a-z0-9]*)*$'
ClassNaming:
  active: true
  classPattern: '[A-Z$][a-zA-Z$]*'
EnumNaming:
  active: true
  enumEntryPattern: '^[A-Z$][a-zA-Z_$]*$'
FunctionNaming:
  active: true
  functionPattern: '^[a-z$][a-zA-Z$0-9]*$'
FunctionMaxLength:
  active: false
  maximumFunctionNameLength: 30
FunctionMinLength:
  active: false
  minimumFunctionNameLength: 3
VariableNaming:
  active: true
  variablePattern: '^(_)?[a-z$][a-zA-Z$0-9]*$'
ConstantNaming:
  active: true
  constantPattern: '^([A-Z_]*|serialVersionUID)$'
VariableMaxLength:
  active: false
  maximumVariableNameLength: 30
VariableMinLength:
  active: false
  minimumVariableNameLength: 3
ForbiddenClassName:
  active: false
  forbiddenName: ''
ProtectedMemberInFinalClass:
  active: false
UnnecessaryParentheses:
  active: false
DataClassContainsFunctions:
  active: false
UseDataClass:
  active: false
UnnecessaryAbstractClass:
  active: false  comments:
active: true
CommentOverPrivateMethod:
  active: true
CommentOverPrivateProperty:
  active: true
UndocumentedPublicClass:
  active: false
  searchInNestedClass: true
  searchInInnerClass: true
  searchInInnerObject: true
  searchInInnerInterface: true
UndocumentedPublicFunction:
  active: false

Override defaults ( via failFast option)

Set failFast: true in your detekt.yml configuration file. As a result, every rule will be enabled and warningThreshold and errorThreshold will be set to 0. Weights can then be ignored and left untouched.

To adjust, for example, the maxLineLength value, use this configuration file:

failFast:true autoCorrect: true  style:
MaxLineLength:
  maxLineLength: 100 

All rules are turned on by default and the value of maxLineLength is adjusted to 100. If you don't want to have the CommentOverPrivateMethod turned on, you append:

comments:
CommentOverPrivateMethod:
  active: false 

Suppress code smell rules

detekt supports the Java ( @SuppressWarnings) and Kotlin ( @Suppress) style suppression. If both annotations are present, only Kotlin's annotation is used! To suppress a rule, the id of the rule must be written inside the values field of the annotation e.g. @Suppress("LongMethod", "LongParameterList", ...)

Configure build failure thresholds

detekt now can throw a BuildFailure(Exception) and let the build fail with following config parameters:

build:
warningThreshold: 5 // Five weighted findings 
failThreshold: 10 // Ten weighted smells to fail the build
weights:
  complexity: 2 // Whole complexity rule should add two for each finding.
  LongParameterList: 1 // The specific rule should not add two.
  comments: 0 // Comments should not fail the build at all?!

Every rule and rule set can be attached with an integer value which is the weight of the finding. For example: If you have 5 findings of the category code-smell, then your failThreshold of 10 is reached as 5 x 2 = 10.

The formula for weights: RuleID > RuleSetID > 1. Only integer values are supported.

Extending detekt

Custom RuleSets

detekt uses a ServiceLoader to collect all instances of RuleSetProvider-interfaces. So it is possible to define rules/rule sets and enhance detekt with your own flavor. Attention: You need a resources/META-INF/services/io.gitlab.arturbosch.detekt.api.RuleSetProvider file which has as content the fully qualified name of your RuleSetProvider e.g. io.gitlab.arturbosch.detekt.sampleruleset.SampleProvider.

The easiest way to define an rule set is to clone the provided detekt-sample-ruleset project.

Own rules have to extend the abstract Rule class and override the visitXXX functions from the AST. A RuleSetProvider must be implemented which declares a RuleSet in the instance method. To allow your rule to be configurable, pass it a Config object from within your rule set provider. You can also specify a Severity type for your rule.

Example of a custom rule:

class TooManyFunctions : Rule("TooManyFunctions") {

private var amount: Int = 0
override fun visitFile(file: PsiFile) {

super.visitFile(file)
if (amount > 10) {

 addFindings(CodeSmell(id, Entity.from(file)))

}
  
}

override fun visitNamedFunction(function: KtNamedFunction) {

amount++  
}
  
}

Example of a much preciser rule in terms of more specific CodeSmell constructor and Rule attributes:

class TooManyFunctions2(config: Config) : Rule("TooManyFunctionsTwo", Severity.Maintainability, config) {

private var amount: Int = 0
override fun visitFile(file: PsiFile) {

super.visitFile(file)
if (amount > 10) {

 addFindings(CodeSmell(

id = id, entity = Entity.from(file),

description = "Too many functions can make the maintainability of a file more costly",

metrics = listOf(Metric(type = "SIZE", value = amount, threshold = 10)),

references = listOf())
 )

}
  
}

override fun visitNamedFunction(function: KtNamedFunction) {

amount++  
}
  
}

If you want your rule to be configurable, write down your properties inside the detekt.yml file and use the withConfig function:

MyRuleSet:
MyRule:
  MyMetric: 5
  threshold: 10
OtherRule:
  active: false

By specifying the rule set and rule ids, detekt will use the sub configuration of MyRule:

val threshold = withConfig { valueOrDefault("threshold") { threshold } }

Maven

If your using maven to build rule sets or use detekt as a dependency, you have to run the additional task install

Custom Processors

TODO

Custom Reports

detekt allows you to extend the console output and to create custom output formats.

For example if you do not like the default printing of findings, we can ... TODO

Testing your rules

To test your rules you need a KtFile object and use it's visit method. There are two predefined methods to help obtaining a KtFile:

  • compileContentForTest(content: String): KtFile
  • compileForTest(path: Path): KtFile

New with M3 there is a special detekt-test module, which specifies above two methods but also Rule extension functions that allow allow to skip compilation, ktFile and visit procedures.

  • Rule.lint(StringContent/Path/KtFile) returns just the findings for given content

Code Smell baseline and ignore list

Specify a report output with --output parameter and specify its format with --output-format. Now you can generate a report which holds all findings of current analysis.

With --baseline you generate a baseline.xml where code smells are white- or blacklisted.

<SmellBaseline>
<Blacklist timestamp="1483388204705">
  <ID>CatchRuntimeException:Junk.kt$e: RuntimeException</ID>
</Blacklist>
<Whitelist timestamp="1496432564542">
  <ID>NestedBlockDepth:Indentation.kt$Indentation$override fun procedure(node: ASTNode)</ID>
  <ID>ComplexCondition:SpacingAroundOperator.kt$SpacingAroundOperator$tokenSet.contains(node.elementType) &amp;&amp; node is LeafPsiElement &amp;&amp; !node.isPartOf(KtPrefixExpression::class) &amp;&amp; // not unary !node.isPartOf(KtTypeParameterList::class) &amp;&amp; // fun &lt;T&gt;fn(): T {

}
 !node.isPartOf(KtTypeArgumentList::class) &amp;&amp; // C&lt;T&gt; !node.isPartOf(KtValueArgument::class) &amp;&amp; // fn(*array) !node.isPartOf(KtImportDirective::class) &amp;&amp; // import * !node.isPartOf(KtSuperExpression::class)</ID>
  <ID>TooManyFunctions:LargeClass.kt$io.gitlab.arturbosch.detekt.rules.complexity.LargeClass.kt</ID>
  <ID>ComplexMethod:DetektExtension.kt$DetektExtension$fun convertToArguments(): MutableList&lt;String&gt;</ID>
</Whitelist> </SmellBaseline>

The intention of a whitelist is that only new code smells are printed on further analysis. The blacklist can be used to write down false positive detections. The ID node must be build of <RuleID>:<Signature>. Both values can be found inside the report file.

Contributors

Credits

Resources

Android library for EditText. Easy way for add phone readability in your project.

KotlinPoet is a Kotlin and Java API for generating .kt source files.

Source file generation can be useful when doing things such as annotation processing or interacting with metadata files (e.g., database schemas, protocol formats). By generating code, you eliminate the need to write boilerplate while also keeping a single source of truth for the metadata.

An awesome and customizable search dialog with built-in search options.

The Fuze project is an example of MVVM design pattern in full functional reactive programming RxJava2 with Dagger2.

Fun

A library for android to animate expanding and collapsing views.

Android Architecture using Google guides.

Topics


2D Engines   3D Engines   9-Patch   Action Bars   Activities   ADB   Advertisements   Analytics   Animations   ANR   AOP   API   APK   APT   Architecture   Audio   Autocomplete   Background Processing   Backward Compatibility   Badges   Bar Codes   Benchmarking   Bitmaps   Bluetooth   Blur Effects   Bread Crumbs   BRMS   Browser Extensions   Build Systems   Bundles   Buttons   Caching   Camera   Canvas   Cards   Carousels   Changelog   Checkboxes   Cloud Storages   Color Analysis   Color Pickers   Colors   Comet/Push   Compass Sensors   Conferences   Content Providers   Continuous Integration   Crash Reports   Credit Cards   Credits   CSV   Curl/Flip   Data Binding   Data Generators   Data Structures   Database   Database Browsers   Date &   Debugging   Decompilers   Deep Links   Dependency Injections   Design   Design Patterns   Dex   Dialogs   Distributed Computing   Distribution Platforms   Download Managers   Drawables   Emoji   Emulators   EPUB   Equalizers &   Event Buses   Exception Handling   Face Recognition   Feedback &   File System   File/Directory   Fingerprint   Floating Action   Fonts   Forms   Fragments   FRP   FSM   Functional Programming   Gamepads   Games   Geocaching   Gestures   GIF   Glow Pad   Gradle Plugins   Graphics   Grid Views   Highlighting   HTML   HTTP Mocking   Icons   IDE   IDE Plugins   Image Croppers   Image Loaders   Image Pickers   Image Processing   Image Views   Instrumentation   Intents   Job Schedulers   JSON   Keyboard   Kotlin   Layouts   Library Demos   List View   List Views   Localization   Location   Lock Patterns   Logcat   Logging   Mails   Maps   Markdown   Mathematics   Maven Plugins   MBaaS   Media   Menus   Messaging   MIME   Mobile Web   Native Image   Navigation   NDK   Networking   NFC   NoSQL   Number Pickers   OAuth   Object Mocking   OCR Engines   OpenGL   ORM   Other Pickers   Parallax List   Parcelables   Particle Systems   Password Inputs   PDF   Permissions   Physics Engines   Platforms   Plugin Frameworks   Preferences   Progress Indicators   ProGuard   Properties   Protocol Buffer   Pull To   Purchases   Push/Pull   QR Codes   Quick Return   Radio Buttons   Range Bars   Ratings   Recycler Views   Resources   REST   Ripple Effects   RSS   Screenshots   Scripting   Scroll Views   SDK   Search Inputs   Security   Sensors   Services   Showcase Views   Signatures   Sliding Panels   Snackbars   SOAP   Social Networks   Spannable   Spinners   Splash Screens   SSH   Static Analysis   Status Bars   Styling   SVG   System   Tags   Task Managers   TDD &   Template Engines   Testing   Testing Tools   Text Formatting   Text Views   Text Watchers   Text-to   Toasts   Toolkits For   Tools   Tooltips   Trainings   TV   Twitter   Updaters   USB   User Stories   Utils   Validation   Video   View Adapters   View Pagers   Views   Watch Face   Wearable Data   Wearables   Weather   Web Tools   Web Views   WebRTC   WebSockets   Wheel Widgets   Wi-Fi   Widgets   Windows   Wizards   XML   XMPP   YAML   ZIP Codes