Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ To build the app, you will need a Freesound API client id and secret stored in t
```
freesound.api.clientId=yourapiclientidvaluegoeshere
freesound.api.clientSecret=yourapiclientsecretvaluegoeshere
google.maps.api.key=yourgooglemapsapikeygoeshere
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll look up how to include this secret so that Travis builds.

```

If you don't have a key and you're not in the Futurice organization, then you will have to [generate your own from the Freesound website.](https://www.freesound.org/docs/api/overview.html)
Expand Down
5 changes: 5 additions & 0 deletions app/app-config.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ ext {
// Freesound API
freesound_api_client_id = getFreesoundApiClientId()
freesound_api_client_secret = getFreesoundApiClientSecret()
google_maps_api_key= getGoogleMapsApiKey()
freesound_api_url = "\"https://www.freesound.org/apiv2/\""
}

Expand All @@ -29,6 +30,10 @@ String getFreesoundApiClientSecret() {
return getProperty("freesound.api.clientSecret", "FREESOUND_API_CLIENT_SECRET")
}

String getGoogleMapsApiKey() {
return getProperty("google.maps.api.key", "GOOGLE_MAPS_API_KEY")
}

String getProperty(String property, String envVariable) {
String value = isCiBuild() ? fromEnv(envVariable) : fromFile(property);
if (value == null || value.isEmpty()) {
Expand Down
17 changes: 11 additions & 6 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def STRING = "String"

android {
compileSdkVersion 26
buildToolsVersion '27.0.0'
buildToolsVersion '27.0.3'

defaultConfig {
applicationId "com.futurice.freesound"
Expand All @@ -41,6 +41,7 @@ android {
buildConfigField(STRING, "FREESOUND_API_URL", freesound_api_url)
buildConfigField(STRING, "FREESOUND_API_CLIENT_ID", freesound_api_client_id)
buildConfigField(STRING, "FREESOUND_API_CLIENT_SECRET", freesound_api_client_secret)
resValue("string", "GOOGLE_MAPS_API_KEY", google_maps_api_key)

// AutoValue and AutoFactory require this workaround because they haven't separated their
// annotation processor from their compile time dependencies.
Expand Down Expand Up @@ -121,11 +122,12 @@ ext {
dependencyVersions = [
kotlin : kotlin_version,
androidSupport : '26.1.0',
androidSupportConstraint: '1.0.2',
androidSupportConstraint: '1.1.0',
androidSupportTest : '1.0.0',
googleMaps : '15.0.0',
dagger2 : '2.4',
rxJava2 : '2.1.0',
rxAndroid : '2.0.1',
rxAndroid : '2.0.2',
okHttp : '3.6.0',
picasso : '2.5.2',
retrofit2 : '2.2.0',
Expand All @@ -140,10 +142,10 @@ ext {
options : '1.2.4',
exoplayer : 'r2.1.1',
butterknife : '8.7.0',
firebase : '9.4.0',
stetho : '1.4.2',
firebase : '15.0.0',
stetho : '1.5.0',
chuck : '1.0.4',
timber : '4.5.1',
timber : '4.6.1',
junit : '4.12',
mockito : '2.2.2',
assertJ : '1.7.1',
Expand All @@ -165,6 +167,9 @@ dependencies {
implementation "com.android.support:design:$dependencyVersions.androidSupport"
implementation "com.android.support.constraint:constraint-layout:$dependencyVersions.androidSupportConstraint"

// Google maps
implementation "com.google.android.gms:play-services-maps:$dependencyVersions.googleMaps"

// Dagger 2
kapt "com.google.dagger:dagger-compiler:$dependencyVersions.dagger2"
implementation "com.google.dagger:dagger:$dependencyVersions.dagger2"
Expand Down
6 changes: 6 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@
android:value="com.futurice.freesound.feature.home.HomeActivity"/>
</activity>
<activity android:name=".feature.details.DetailsActivity"/>
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="@string/GOOGLE_MAPS_API_KEY" />
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.futurice.freesound.feature.search

import android.os.Bundle
import com.futurice.freesound.inject.fragment.BaseFragmentModule
import com.futurice.freesound.map.BindingBaseMapViewFragment
import com.futurice.freesound.viewmodel.DataBinder
import com.futurice.freesound.viewmodel.SimpleDataBinder
import com.futurice.freesound.viewmodel.ViewModel
import javax.inject.Inject

class MapFragment : BindingBaseMapViewFragment<MapFragmentComponent>() {

@Inject
internal lateinit var simpleMapViewViewModel: MapViewModel
Copy link
Contributor

@peter-tackage peter-tackage Apr 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

simpleMapViewViewModel name will need to be changed.


private val dataBinder = SimpleDataBinder()

override fun inject() {
component().inject(this)
}

override fun createComponent(): MapFragmentComponent =
(activity as SearchActivity).component()
.plusMapFragmentComponent(BaseFragmentModule(this))

override fun viewModel(): ViewModel = simpleMapViewViewModel

override fun dataBinder(): DataBinder = dataBinder

override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
this.getMapAsync(simpleMapViewViewModel)
}

companion object {
internal fun create(): MapFragment = MapFragment()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2016 Futurice GmbH
*
* 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.futurice.freesound.feature.search;

import com.futurice.freesound.inject.fragment.BaseFragmentComponent;
import com.futurice.freesound.inject.fragment.FragmentScope;

import dagger.Subcomponent;

@FragmentScope
@Subcomponent(modules = MapFragmentModule.class)
public interface MapFragmentComponent extends BaseFragmentComponent {

void inject(final MapFragment mapFragment);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2016 Futurice GmbH
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also update your template for the copyright - 2018.

*
* 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.futurice.freesound.feature.search;

import com.futurice.freesound.feature.common.scheduling.SchedulerProvider;
import com.futurice.freesound.inject.fragment.BaseFragmentModule;
import com.futurice.freesound.inject.fragment.FragmentScope;

import dagger.Module;
import dagger.Provides;

@Module(includes = BaseFragmentModule.class)
public class MapFragmentModule {

@Provides
@FragmentScope
static MapViewModel provideMapFragmentViewModel(TabController tabController,
SearchDataModel searchDataModel,
SchedulerProvider schedulerProvider) {
return new MapViewModel(tabController, searchDataModel, schedulerProvider);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.futurice.freesound.feature.search

import com.futurice.freesound.common.rx.plusAssign
import com.futurice.freesound.feature.common.scheduling.SchedulerProvider
import com.futurice.freesound.map.SimpleMapViewViewModel
import com.futurice.freesound.network.api.model.Sound
import io.reactivex.disposables.CompositeDisposable
import polanski.option.Option
import timber.log.Timber

internal class MapViewModel(private val tabController: TabController,
Copy link
Contributor

@peter-tackage peter-tackage Apr 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expose Observables with map properties in this ViewModel.

Also move Map related classes such as this to a subpackage: com.futurice.freesound.feature.search.map.

private val searchDataModel: SearchDataModel,
private val schedulerProvider: SchedulerProvider) : SimpleMapViewViewModel() {

override fun bind(d: CompositeDisposable) {
d += tabController.tabRequestStream
.observeOn(schedulerProvider.ui())
.subscribe(
{ soundItem ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make this action a named method.

soundItem.sound.ifSome { sound ->
sound.geotag?.let { zoomToMarker(it) }
}
},
Timber::e)

d += searchDataModel.searchStateOnceAndStream
.map(SearchState::results)
.observeOn(schedulerProvider.ui())
.subscribe(::displayMarkers,
Timber::e)
}

override fun unbind() {
//nothing to do
}

private fun displayMarkers(optionalList: Option<List<Sound>>) {
wipeMarkers()
optionalList.ifSome { list ->
list.forEach { sound ->
sound.geotag?.let { addMarker(it, sound.name) }
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ package com.futurice.freesound.feature.search
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentManager
import android.support.v4.app.FragmentPagerAdapter
import android.support.v7.widget.SearchView
import android.support.v7.widget.SearchView.OnQueryTextListener
import android.view.View
import android.widget.Button
import com.futurice.freesound.R
import com.futurice.freesound.app.FreesoundApplication
import com.futurice.freesound.common.rx.plusAssign
Expand Down Expand Up @@ -53,6 +55,11 @@ class SearchActivity : BindingBaseActivity<SearchActivityComponent>() {
@Inject
internal lateinit var schedulerProvider: SchedulerProvider

@Inject
internal lateinit var tabController: TabController

private lateinit var searchPagerAdapter: SearchPagerAdapter

private val dataBinder = object : SimpleDataBinder() {

private fun SearchView.getTextChangeStream(uiScheduler: Scheduler): Observable<String> =
Expand All @@ -74,6 +81,12 @@ class SearchActivity : BindingBaseActivity<SearchActivityComponent>() {
.observeOn(schedulerProvider.ui())
.subscribe({ handleErrorState(it) })
{ e(it, "Error receiving Errors") }

d += tabController.tabRequestStream
.observeOn(schedulerProvider.ui())
.subscribe({ switchTab(it) })
{ e(it, "Error receiving Errors") }

}

private fun SearchView.subscribeToSearchView(emitter: ObservableEmitter<String>) {
Expand All @@ -88,11 +101,14 @@ class SearchActivity : BindingBaseActivity<SearchActivityComponent>() {
}
}

private fun switchTab(soundInfo: SoundInfo) {
container.currentItem = if (soundInfo.tabType == TabType.RESULTS) 0 else 1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There ought to be a better way to couple/define the TabType and its corresponding position.

}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_search)
savedInstanceState.ifNull { addSearchFragment() }

toolbar_search.apply { setSupportActionBar(this) }
supportActionBar?.setDisplayHomeAsUpEnabled(true)

Expand Down Expand Up @@ -123,9 +139,8 @@ class SearchActivity : BindingBaseActivity<SearchActivityComponent>() {
}

private fun addSearchFragment() {
supportFragmentManager.beginTransaction()
.add(R.id.container, SearchFragment.create())
.commit()
searchPagerAdapter = SearchPagerAdapter(supportFragmentManager)
container.adapter = searchPagerAdapter
}

private fun handleErrorState(searchState: SearchState) {
Expand All @@ -135,7 +150,7 @@ class SearchActivity : BindingBaseActivity<SearchActivityComponent>() {
}

private fun setClearSearchVisible(isClearButtonVisible: Boolean) {
val closeButton : View = search_view.findViewById(R.id.search_close_btn)
val closeButton: View = search_view.findViewById(R.id.search_close_btn)
closeButton.visibility = if (isClearButtonVisible) View.VISIBLE else View.GONE
}

Expand All @@ -149,9 +164,24 @@ class SearchActivity : BindingBaseActivity<SearchActivityComponent>() {

companion object {

@JvmStatic fun open(context: Context) {
@JvmStatic
fun open(context: Context) {
Intent(context, SearchActivity::class.java)
.apply { context.startActivity(this) }
}
}

private inner class SearchPagerAdapter(fm: FragmentManager?) : FragmentPagerAdapter(fm) {
override fun getItem(position: Int): Fragment = if (position == 0) SearchFragment.create() else MapFragment.create()

override fun getCount(): Int = 2

override fun getPageTitle(position: Int): CharSequence =
when (position) {
0 -> getString(R.string.search_tab_results)
1 -> getString(R.string.search_tab_map)
else -> ""
Copy link
Contributor

@peter-tackage peter-tackage Apr 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A position other than 0 or 1 is a error condition, correct? If so then throw an IllegalArgumentException with an explanation from here.

}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,6 @@ public interface SearchActivityComponent extends BaseActivityComponent {
void inject(final SearchActivity activity);

SearchFragmentComponent plusSearchFragmentComponent(BaseFragmentModule baseFragmentModule);

MapFragmentComponent plusMapFragmentComponent(BaseFragmentModule baseFragmentModule);
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ static SearchActivityViewModel provideSearchViewModel(SearchDataModel searchData
Analytics analytics,
SchedulerProvider schedulerProvider) {
return new SearchActivityViewModel(searchDataModel,
audioPlayer,
analytics,
schedulerProvider);
audioPlayer,
analytics,
schedulerProvider);
}

@Provides
Expand All @@ -60,4 +60,9 @@ private SearchActivityModule() {
throw new InstantiationForbiddenError();
}

@Provides
@ActivityScope
static TabController provideTabController() {
return new TabController();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,12 @@ ItemComparator provideComparator() {
@Provides
ViewHolderFactory provideSoundViewHolderFactory(@ForActivity Context context,
Picasso picasso,
SchedulerProvider schedulerProvider) {
SchedulerProvider schedulerProvider,
TabController tabController) {
return new SoundItemViewHolder.SoundItemViewHolderFactory(context,
picasso,
schedulerProvider);
schedulerProvider,
tabController);
}

@IntoMap
Expand Down
Loading