├── .gitignore
├── README.md
├── app
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── antonioleiva
│ │ └── googlenowsearch
│ │ └── ApplicationTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── antonioleiva
│ │ │ └── googlenowsearch
│ │ │ ├── Extensions.kt
│ │ │ ├── MainActivity.kt
│ │ │ └── MainActivityJava.java
│ └── res
│ │ ├── drawable-hdpi
│ │ └── ic_search.png
│ │ ├── drawable-mdpi
│ │ └── ic_search.png
│ │ ├── drawable-xhdpi
│ │ └── ic_search.png
│ │ ├── drawable-xxhdpi
│ │ └── ic_search.png
│ │ ├── drawable-xxxhdpi
│ │ └── ic_search.png
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── menu
│ │ └── main.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── antonioleiva
│ └── googlenowsearch
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | # Generated files
2 | bin/
3 | gen/
4 |
5 | # Gradle files
6 | .gradle/
7 | gradlew.bat
8 | gradlew
9 | build/
10 | gradle.properties
11 |
12 | # Local configuration file (sdk path, etc)
13 | local.properties
14 |
15 | # Intellij project files
16 | *.iws
17 | .idea/tasks.xml
18 | .idea
19 |
20 | *.iml
21 |
22 | # OS
23 | .DS_Store
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Voice Search Example
2 |
3 | This example supports the article about Voice Search on Google Now:
4 |
5 | [http://antonioleiva.com/voice_search_google_now/](http://antonioleiva.com/voice_search_google_now/)
6 |
7 | The working implementation is done using Kotlin (`MainActivity.kt`), though the Java implementation
8 | can also be found at `MainActivityJava.java`. The `AndroidManifest` can be easily modified to run
9 | any of these activities.
10 |
11 | ## License
12 |
13 | Copyright 2015 Antonio Leiva
14 |
15 | Licensed under the Apache License, Version 2.0 (the "License");
16 | you may not use this file except in compliance with the License.
17 | You may obtain a copy of the License at
18 |
19 | http://www.apache.org/licenses/LICENSE-2.0
20 |
21 | Unless required by applicable law or agreed to in writing, software
22 | distributed under the License is distributed on an "AS IS" BASIS,
23 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24 | See the License for the specific language governing permissions and
25 | limitations under the License.
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 |
4 | android {
5 | compileSdkVersion 23
6 | buildToolsVersion "23.0.1"
7 |
8 | defaultConfig {
9 | applicationId "com.antonioleiva.googlenowsearch"
10 | minSdkVersion 15
11 | targetSdkVersion 23
12 | versionCode 1
13 | versionName "1.0"
14 | }
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 | sourceSets {
22 | main.java.srcDirs += 'src/main/kotlin'
23 | }
24 | }
25 |
26 | dependencies {
27 | compile fileTree(dir: 'libs', include: ['*.jar'])
28 | testCompile 'junit:junit:4.12'
29 | compile 'com.android.support:appcompat-v7:23.0.1'
30 | compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
31 | compile "org.jetbrains.anko:anko-sdk15:$anko_version"
32 | compile "org.jetbrains.anko:anko-support-v4:$anko_version"
33 | }
34 | buildscript {
35 | ext.kotlin_version = '0.14.451'
36 | ext.anko_version = '0.7.1'
37 | repositories {
38 | mavenCentral()
39 | }
40 | dependencies {
41 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
42 | }
43 | }
44 | repositories {
45 | mavenCentral()
46 | }
47 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/antonio/Documents/Development/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/antonioleiva/googlenowsearch/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.antonioleiva.googlenowsearch;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/antonioleiva/googlenowsearch/Extensions.kt:
--------------------------------------------------------------------------------
1 | package com.antonioleiva.googlenowsearch
2 |
3 | import android.support.v4.view.MenuItemCompat
4 | import android.support.v7.widget.SearchView
5 | import android.view.Menu
6 | import android.view.View
7 |
8 | inline fun Menu.findCompatActionView(actionRes: Int): T {
9 | val searchItem = findItem(actionRes)
10 | return MenuItemCompat.getActionView(searchItem) as T
11 | }
12 |
13 | fun SearchView.onQueryText(submit: (String) -> Boolean = { false }, textChange: (String) -> Boolean = { false }) {
14 |
15 | this.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
16 |
17 | override fun onQueryTextSubmit(query: String): Boolean = submit(query)
18 |
19 | override fun onQueryTextChange(newText: String): Boolean = textChange(newText)
20 |
21 | })
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/java/com/antonioleiva/googlenowsearch/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.antonioleiva.googlenowsearch
2 |
3 | import android.app.SearchManager
4 | import android.content.Intent
5 | import android.os.Bundle
6 | import android.support.v7.app.AppCompatActivity
7 | import android.support.v7.widget.SearchView
8 | import android.view.Menu
9 | import org.jetbrains.anko.longToast
10 | import kotlin.properties.Delegates
11 |
12 | class MainActivity : AppCompatActivity() {
13 |
14 | companion object {
15 | private val ACTION_VOICE_SEARCH = "com.google.android.gms.actions.SEARCH_ACTION"
16 | }
17 |
18 | private var searchView by Delegates.notNull()
19 |
20 | override fun onCreate(savedInstanceState: Bundle?) {
21 | super.onCreate(savedInstanceState)
22 | setContentView(R.layout.activity_main)
23 | }
24 |
25 | override fun onNewIntent(intent: Intent) {
26 | super.onNewIntent(intent)
27 | handleVoiceSearch(intent)
28 | }
29 |
30 | private fun handleVoiceSearch(intent: Intent) {
31 | if (ACTION_VOICE_SEARCH == intent.action) {
32 | val query = intent.getStringExtra(SearchManager.QUERY)
33 | setSearchViewVisible(true)
34 | searchView.setQuery(query, true)
35 | }
36 | }
37 |
38 | override fun onCreateOptionsMenu(menu: Menu): Boolean {
39 | menuInflater.inflate(R.menu.main, menu)
40 |
41 | searchView = menu.findCompatActionView(R.id.action_search)
42 | searchView.setOnSearchClickListener { setSearchViewVisible(true) }
43 |
44 | searchView.onQueryText ({
45 | longToast(it)
46 | searchView.clearFocus()
47 | true
48 | })
49 |
50 | intent?.let { handleVoiceSearch(it) }
51 |
52 | return true
53 | }
54 |
55 | override fun onBackPressed() {
56 | if (!searchView.isIconified) {
57 | setSearchViewVisible(false)
58 | } else {
59 | super.onBackPressed()
60 | }
61 | }
62 |
63 | private fun setSearchViewVisible(visible: Boolean) {
64 | if (searchView.isIconified == visible) {
65 | searchView.isIconified = !visible
66 | }
67 |
68 | supportActionBar?.setDisplayHomeAsUpEnabled(visible)
69 | }
70 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/antonioleiva/googlenowsearch/MainActivityJava.java:
--------------------------------------------------------------------------------
1 | package com.antonioleiva.googlenowsearch;
2 |
3 | import android.app.SearchManager;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.support.v4.view.MenuItemCompat;
7 | import android.support.v7.app.AppCompatActivity;
8 | import android.support.v7.widget.SearchView;
9 | import android.view.Menu;
10 | import android.view.MenuItem;
11 | import android.view.View;
12 | import android.widget.Toast;
13 |
14 | public class MainActivityJava extends AppCompatActivity {
15 |
16 | private static final String ACTION_VOICE_SEARCH = "com.google.android.gms.actions.SEARCH_ACTION";
17 | private SearchView searchView;
18 |
19 | @Override
20 | protected void onCreate(Bundle savedInstanceState) {
21 | super.onCreate(savedInstanceState);
22 | setContentView(R.layout.activity_main);
23 | }
24 |
25 | @Override protected void onNewIntent(Intent intent) {
26 | super.onNewIntent(intent);
27 | handleVoiceSearch(intent);
28 | }
29 |
30 | private void handleVoiceSearch(Intent intent) {
31 | if (intent != null && ACTION_VOICE_SEARCH.equals(intent.getAction())) {
32 | String query = intent.getStringExtra(SearchManager.QUERY);
33 | setSearchViewVisible(true);
34 | searchView.setQuery(query, true);
35 | }
36 | }
37 |
38 | @Override public boolean onCreateOptionsMenu(Menu menu) {
39 | getMenuInflater().inflate(R.menu.main, menu);
40 |
41 | MenuItem searchItem = menu.findItem(R.id.action_search);
42 | searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
43 |
44 | searchView.setOnSearchClickListener(new View.OnClickListener() {
45 | @Override public void onClick(View v) {
46 | setSearchViewVisible(true);
47 | }
48 | });
49 |
50 | searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
51 | @Override public boolean onQueryTextSubmit(String query) {
52 | Toast.makeText(MainActivityJava.this, query, Toast.LENGTH_LONG).show();
53 | searchView.clearFocus();
54 | return true;
55 | }
56 |
57 | @Override public boolean onQueryTextChange(String newText) {
58 | return false;
59 | }
60 | });
61 |
62 | handleVoiceSearch(getIntent());
63 |
64 | return true;
65 | }
66 |
67 | @Override public void onBackPressed() {
68 | if (!searchView.isIconified()) {
69 | setSearchViewVisible(false);
70 | } else {
71 | super.onBackPressed();
72 | }
73 | }
74 |
75 | private void setSearchViewVisible(boolean visible) {
76 |
77 | if (searchView.isIconified() == visible) {
78 | searchView.setIconified(!visible);
79 | }
80 |
81 | if (getSupportActionBar() != null) {
82 | getSupportActionBar().setDisplayHomeAsUpEnabled(visible);
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antoniolg/Voice-Search-Example/f34c792e3dc1c1280061f1afa7a44ff750ba1e25/app/src/main/res/drawable-hdpi/ic_search.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antoniolg/Voice-Search-Example/f34c792e3dc1c1280061f1afa7a44ff750ba1e25/app/src/main/res/drawable-mdpi/ic_search.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antoniolg/Voice-Search-Example/f34c792e3dc1c1280061f1afa7a44ff750ba1e25/app/src/main/res/drawable-xhdpi/ic_search.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antoniolg/Voice-Search-Example/f34c792e3dc1c1280061f1afa7a44ff750ba1e25/app/src/main/res/drawable-xxhdpi/ic_search.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antoniolg/Voice-Search-Example/f34c792e3dc1c1280061f1afa7a44ff750ba1e25/app/src/main/res/drawable-xxxhdpi/ic_search.png
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antoniolg/Voice-Search-Example/f34c792e3dc1c1280061f1afa7a44ff750ba1e25/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antoniolg/Voice-Search-Example/f34c792e3dc1c1280061f1afa7a44ff750ba1e25/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antoniolg/Voice-Search-Example/f34c792e3dc1c1280061f1afa7a44ff750ba1e25/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antoniolg/Voice-Search-Example/f34c792e3dc1c1280061f1afa7a44ff750ba1e25/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antoniolg/Voice-Search-Example/f34c792e3dc1c1280061f1afa7a44ff750ba1e25/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Google Now Search
3 | Search
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/antonioleiva/googlenowsearch/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.antonioleiva.googlenowsearch;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:1.3.0'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/antoniolg/Voice-Search-Example/f34c792e3dc1c1280061f1afa7a44ff750ba1e25/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Oct 14 07:21:40 CEST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip
7 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------