├── .gitignore
├── .travis.yml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ ├── assets
│ │ └── search_results.json
│ └── java
│ │ └── com
│ │ └── btellez
│ │ └── solidandroid
│ │ └── test
│ │ ├── MockApplicationInjectable.java
│ │ ├── SolidInstrumentationTestRunner.java
│ │ ├── model
│ │ └── DeserializeTest.java
│ │ └── module
│ │ └── SingletonTestModule.java
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── btellez
│ │ └── solidandroid
│ │ ├── SimpleAndroidApplication.java
│ │ ├── activity
│ │ ├── SearchResultsActivity.java
│ │ ├── SelectionActivity.java
│ │ └── SettingsActivity.java
│ │ ├── configuration
│ │ ├── Configuration.java
│ │ └── KeyValueStore.java
│ │ ├── model
│ │ ├── ApiKeys.java
│ │ ├── Icon.java
│ │ ├── IconParser.java
│ │ └── Uploader.java
│ │ ├── module
│ │ ├── DependencyInjector.java
│ │ └── SingletonModule.java
│ │ ├── network
│ │ ├── NetworkBitmapClient.java
│ │ ├── NounProjectApi.java
│ │ ├── NounProjectOAuth.java
│ │ └── OkNounProjectApi.java
│ │ ├── utility
│ │ ├── AnalyticsEvents.java
│ │ ├── Strings.java
│ │ └── Tracker.java
│ │ └── view
│ │ ├── EmptyView.java
│ │ ├── FadeAnimator.java
│ │ ├── SearchResultsView.java
│ │ ├── SelectionScreenView.java
│ │ └── SingleIconResultItem.java
│ └── res
│ ├── drawable-hdpi
│ ├── ic_launcher.png
│ └── ic_outbound.png
│ ├── drawable-mdpi
│ ├── ic_launcher.png
│ └── ic_outbound.png
│ ├── drawable-xhdpi
│ ├── ic_launcher.png
│ └── ic_outbound.png
│ ├── drawable-xxhdpi
│ ├── ic_launcher.png
│ └── ic_outbound.png
│ ├── drawable
│ ├── solid_search.png
│ └── solid_upload.png
│ ├── layout
│ ├── activity_main_user.xml
│ ├── activity_search_results.xml
│ ├── activity_selection.xml
│ ├── error_view.xml
│ └── single_icon_result_item.xml
│ ├── menu
│ └── main_user.xml
│ ├── values-w820dp
│ └── dimens.xml
│ ├── values
│ ├── colors.xml
│ ├── config.xml
│ ├── dimens.xml
│ ├── strings.xml
│ ├── strings_activity_settings.xml
│ └── styles.xml
│ └── xml
│ └── pref_general.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | # Android Studio Files + OS Files
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .idea/
7 | .DS_Store
8 | *.iml
9 | # Built application files
10 | *.apk
11 | *.ap_
12 |
13 | # Files for the Dalvik VM
14 | *.dex
15 |
16 | # Java class files
17 | *.class
18 |
19 | # Generated files
20 | bin/
21 | gen/
22 |
23 | # Gradle files
24 | .gradle/
25 | build/
26 |
27 | # Local configuration file (sdk path, etc)
28 | local.properties
29 |
30 | # Proguard folder generated by Eclipse
31 | proguard/
32 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: android
2 |
3 | script: ./gradlew build assembleDebug
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | SOLID: Noun Project Browser (Circa 2015)
2 | ============================
3 |
4 | [](https://travis-ci.org/blad/solid-android)
5 |
6 | This project's goal is to be a demonstration of how [SOLID principles](http://en.wikipedia.org/wiki/SOLID_%28object-oriented_design%29) can
7 | be applied to Android development.
8 |
9 |
10 | 
11 |
12 |
13 |
14 | ## Contributing
15 |
16 | Contributions are welcome to the project. The goal is to adhere to the SOLID principles.
17 |
18 | ### Bug Fixes
19 |
20 | Bug fixes can be contributed via pull requests for this project.
21 |
22 | ### Features
23 |
24 | Features can be contributed via pull-requests for this project.
25 |
26 | ## Tools & Libraries
27 |
28 | - [Android Studio - Android Development Environment](https://developer.android.com/sdk/installing/studio.html)
29 | - [ButterKnife - View Injection Library](http://jakewharton.github.io/butterknife/)
30 | - [Dagger - Dependency Injection Library](http://square.github.io/dagger/)
31 | - [Gogole Gson - A Java library to convert JSON to Java objects](https://code.google.com/p/google-gson/)
32 | - [okHttp - An HTTP & SPDY client for Android and Java applications](http://square.github.io/okhttp/)
33 | - [Picasso - A powerful image downloading and caching library for Android](http://square.github.io/picasso/)
34 | - [Otto - Event Bus Library](http://square.github.io/otto/)
35 | - [Travis CI - Continuos integration platform](https://travis-ci.org/)
36 |
37 | ## Graphics & Icons
38 |
39 | ### The Noun Project
40 |
41 | [The Noun Project](http://www.thenounproject.com) is the source for some the graphics used in this application. The following users' work was used:
42 |
43 | - [Dice](http://thenounproject.com/term/dice/20125/) created by [Derek Palladino](http://thenounproject.com/derekjp/)
44 | - [Cloud-Upload](http://thenounproject.com/term/cloud-upload/9947/) created by [Scott Lewis](http://thenounproject.com/iconify/)
45 | - [Magnifying-Glass](http://thenounproject.com/term/magnifying-glass/89626/) public domain icon
46 |
47 | The noun project is a great resource for finding clip art for use in applications.
48 |
49 | ### Android Asset Studio
50 |
51 | Icon generator that allows you to quickly and easily generate icons from existing source images, clipart, or text. You can generate Launcher icons, Action bar and tab icons, Notification icons and Generic icons. The asset studio allows you to adjust sizing, padding, and tint icons.
52 |
53 | [Android Asset Studio](http://romannurik.github.io/AndroidAssetStudio/)
54 |
55 | ### Material Palette
56 |
57 | Material Pallet is a simple web app that allows you to generate a color
58 | pallet and export the corresponding xml. This allows non-designers to pick a pallet
59 | that makes sense visually, and aligns with the guidelines for Material Design.
60 |
61 | Additionally this helps enforce the correct use of color names in Android themes.
62 |
63 | [www.MaterialPalette.com](http://www.materialpalette.com/)
64 |
65 | ## Running this Project
66 |
67 | From Android Studio simply choose to import and select the `build.gradle` in the
68 | root directory of the repository. Android Studio will set everything else up
69 | automatically.
70 |
71 | ### The Noun Project API Keys
72 |
73 | To obtain api keys for The Noun Project visit the [Getting Started](http://api.thenounproject.com/getting_started.html) page for additional information
74 |
75 | Once you have the API key and secret, replace the placeholder values in: `app/src/main/res/values/nounproject_api_config.xml`.
76 |
77 |
78 |
79 | ## License
80 |
81 | > Copyright 2014 Bladymir Tellez
82 | >
83 | > Licensed under the Apache License, Version 2.0 (the "License");
84 | > you may not use this file except in compliance with the License.
85 | > You may obtain a copy of the License at
86 | >
87 | > http://www.apache.org/licenses/LICENSE-2.0
88 | >
89 | > Unless required by applicable law or agreed to in writing, software
90 | > distributed under the License is distributed on an "AS IS" BASIS,
91 | > WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
92 | > See the License for the specific language governing permissions and
93 | > limitations under the License.
94 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'android-sdk-manager'
2 | apply plugin: 'com.android.application'
3 | apply plugin: 'hugo' // call after applying android plugin
4 |
5 | android {
6 | compileSdkVersion 23
7 | buildToolsVersion "21.1.2"
8 |
9 | defaultConfig {
10 | testApplicationId "com.btellez.solidandroid.test"
11 | testInstrumentationRunner "com.btellez.solidandroid.test.SolidInstrumentationTestRunner"
12 | testHandleProfiling true
13 | testFunctionalTest true
14 |
15 | applicationId "com.btellez.solidandroid"
16 | minSdkVersion 15
17 | targetSdkVersion 23
18 |
19 | versionCode 2
20 | versionName "1.1"
21 | }
22 |
23 | compileOptions {
24 | sourceCompatibility JavaVersion.VERSION_1_7
25 | targetCompatibility JavaVersion.VERSION_1_7
26 | }
27 |
28 | buildTypes {
29 | debug {
30 | testCoverageEnabled true
31 | }
32 | release {
33 | minifyEnabled false
34 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
35 | }
36 | }
37 |
38 | lintOptions {
39 | disable 'InvalidPackage'
40 | abortOnError false
41 | }
42 |
43 | packagingOptions {
44 | exclude 'META-INF/services/javax.annotation.processing.Processor'
45 | }
46 | }
47 |
48 | dependencies {
49 | // Include Any jar dependencies
50 | compile fileTree(dir: 'libs', include: ['*.jar'])
51 |
52 | // Testing Dependencies
53 | androidTestCompile 'com.google.dexmaker:dexmaker:1.1'
54 | androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.1'
55 | androidTestCompile 'com.jayway.android.robotium:robotium-solo:5.1'
56 | androidTestCompile 'org.mockito:mockito-core:1.9.5'
57 | compile 'com.android.support:support-v4:23.0.1'
58 | compile 'com.android.support:support-v13:23.0.1'
59 | compile 'com.android.support:appcompat-v7:23.0.1'
60 | compile 'com.google.code.gson:gson:2.3.1'
61 | compile 'com.jakewharton:butterknife:5.1.1'
62 | compile 'com.squareup.dagger:dagger-compiler:1.2.2'
63 | compile 'com.squareup.dagger:dagger:1.2.2'
64 | compile 'com.squareup.okhttp:okhttp:2.1.0'
65 | compile 'com.squareup.okhttp:okhttp-urlconnection:2.1.0'
66 | compile 'com.squareup:otto:1.3.5'
67 | compile 'com.squareup.picasso:picasso:2.4.0'
68 | compile 'io.replay:replay-android:0.9.6'
69 | }
70 |
--------------------------------------------------------------------------------
/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/tellez/Libraries/Java/android-sdk-macosx/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 |
19 | -keepclassmembers class ** {
20 | @com.squareup.otto.Subscribe public *;
21 | @com.squareup.otto.Produce public *;
22 | }
23 |
24 | -dontwarn butterknife.internal.**
25 | -keep class **$$ViewInjector { *; }
26 | -keepnames class * { @butterknife.InjectView *;}
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/btellez/solidandroid/test/MockApplicationInjectable.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.test;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 |
6 | import dagger.Module;
7 | import dagger.ObjectGraph;
8 | import dagger.Provides;
9 |
10 | import com.btellez.solidandroid.module.DependencyInjector;
11 |
12 | /**
13 | * This class defined standard behaviour that DaggerInjector needs, and provides an
14 | * application context for tests
15 | *
16 | * Define the getModules() method in an anonymous inner-class implementation defined
17 | * within each Test, since not all modules will be required for all tests.
18 | */
19 | public abstract class MockApplicationInjectable extends Application implements DependencyInjector {
20 |
21 | //region Application Context Module
22 | @Module(library = true)
23 | public final class MockApplicationContextModule {
24 | Context context;
25 | public MockApplicationContextModule(Context app) {
26 | context = app;
27 | }
28 |
29 | @Provides
30 | public Context provideContext(){
31 | return context;
32 | }
33 | }
34 | //endregion
35 |
36 | private ObjectGraph mObjectGraph;
37 |
38 | public MockApplicationInjectable(Context context) {
39 | super.attachBaseContext(context);
40 | mObjectGraph = ObjectGraph.create(getModules());
41 | }
42 |
43 | @Override
44 | public void inject(Object o) {
45 | mObjectGraph.inject(o);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/btellez/solidandroid/test/SolidInstrumentationTestRunner.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.test;
2 |
3 | import android.test.InstrumentationTestRunner;
4 | import android.test.InstrumentationTestSuite;
5 |
6 | import com.btellez.solidandroid.test.model.DeserializeTest;
7 |
8 | import junit.framework.TestSuite;
9 |
10 |
11 | /**
12 | * Testing Fundamentals
13 | */
14 | public class SolidInstrumentationTestRunner extends InstrumentationTestRunner {
15 | @Override
16 | public TestSuite getAllTests() {
17 | InstrumentationTestSuite tests = new InstrumentationTestSuite(this);
18 |
19 | // Add Test's To Be Ran Here:
20 | tests.addTestSuite(DeserializeTest.class);
21 |
22 | return tests;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/btellez/solidandroid/test/model/DeserializeTest.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.test.model;
2 |
3 | import android.test.InstrumentationTestCase;
4 |
5 | import com.btellez.solidandroid.model.Icon;
6 | import com.btellez.solidandroid.model.IconParser;
7 |
8 | import java.io.IOException;
9 | import java.io.InputStream;
10 | import java.util.List;
11 |
12 | public class DeserializeTest extends InstrumentationTestCase {
13 |
14 | public void testSearchResultDeserialization() throws Exception {
15 | String jsonString = readFileFromAssets("search_results.json");
16 | IconParser.GsonIconParser parser = new IconParser.GsonIconParser();
17 | List icons = parser.fromJson(jsonString, null);
18 | assertEquals(72, icons.size());
19 | }
20 |
21 | /**
22 | * Helper method for instrumentation test to load asset serialized objects
23 | * from the assets directory.
24 | * *
25 | * @param fileName
26 | * @return
27 | */
28 | private String readFileFromAssets(String fileName) {
29 | try {
30 | InputStream is = getInstrumentation().getContext().getAssets().open(fileName);
31 | int size = is.available();
32 | byte[] buffer = new byte[size];
33 | is.read(buffer);
34 | is.close();
35 | return new String(buffer);
36 | } catch (IOException e) {
37 | throw new RuntimeException(e);
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/btellez/solidandroid/test/module/SingletonTestModule.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.test.module;
2 |
3 | import android.content.Context;
4 |
5 | import com.squareup.otto.Bus;
6 |
7 | import org.mockito.Mockito;
8 |
9 | import javax.inject.Singleton;
10 |
11 | import dagger.Module;
12 | import dagger.Provides;
13 |
14 | import com.btellez.solidandroid.test.MockApplicationInjectable;
15 |
16 | /**
17 | * This class provides mock instances of the singletons so that
18 | * behaviours can be verified using Mockito during tests.
19 | */
20 | @Module(library = true, includes = MockApplicationInjectable.MockApplicationContextModule.class)
21 | public class SingletonTestModule {
22 |
23 | @Provides @Singleton Bus provideEventBus() {
24 | return Mockito.mock(Bus.class);
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/btellez/solidandroid/SimpleAndroidApplication.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 |
6 | import com.btellez.solidandroid.activity.SearchResultsActivity;
7 | import com.btellez.solidandroid.activity.SelectionActivity;
8 | import com.btellez.solidandroid.activity.SettingsActivity;
9 | import com.btellez.solidandroid.module.DependencyInjector;
10 |
11 | import dagger.Module;
12 | import dagger.ObjectGraph;
13 | import dagger.Provides;
14 |
15 | public class SimpleAndroidApplication extends Application implements DependencyInjector {
16 |
17 | //region Application Context Module
18 | @Module(library = true)
19 | public final class ApplicationContextModule {
20 | Context context;
21 | public ApplicationContextModule(Context app) {
22 | context = app;
23 | }
24 |
25 | @Provides public Context provideContext() {
26 | return context;
27 | }
28 | }
29 | //endregion
30 |
31 | private ObjectGraph mObjectGraph;
32 |
33 | @Override
34 | public void onCreate() {
35 | super.onCreate();
36 | mObjectGraph = ObjectGraph.create(getModules());
37 | }
38 |
39 |
40 | @Override
41 | public Object[] getModules() {
42 | return new Object[]{
43 | new ApplicationContextModule(this),
44 | new SearchResultsActivity.SearchResultDepedencyModule(),
45 | new SelectionActivity.SelectionActivityDepedencyModule(),
46 | new SettingsActivity.GeneralPreferenceFragment.GeneralPreferenceDepedencyModule()
47 | };
48 | }
49 |
50 |
51 | @Override
52 | public void inject(Object o) {
53 | mObjectGraph.inject(o);
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/app/src/main/java/com/btellez/solidandroid/activity/SearchResultsActivity.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.activity;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.net.Uri;
6 | import android.os.Bundle;
7 | import android.support.v4.app.FragmentActivity;
8 | import android.view.Menu;
9 | import android.view.MenuInflater;
10 | import android.view.MenuItem;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.widget.BaseAdapter;
14 | import android.widget.ImageView;
15 |
16 | import com.btellez.solidandroid.R;
17 | import com.btellez.solidandroid.configuration.Configuration;
18 | import com.btellez.solidandroid.model.Icon;
19 | import com.btellez.solidandroid.module.DependencyInjector;
20 | import com.btellez.solidandroid.module.SingletonModule;
21 | import com.btellez.solidandroid.network.NetworkBitmapClient;
22 | import com.btellez.solidandroid.network.NounProjectApi;
23 | import com.btellez.solidandroid.utility.Strings;
24 | import com.btellez.solidandroid.utility.Tracker;
25 | import com.btellez.solidandroid.view.EmptyView;
26 | import com.btellez.solidandroid.view.SearchResultsView;
27 | import com.btellez.solidandroid.view.SingleIconResultItem;
28 | import com.google.gson.JsonSyntaxException;
29 |
30 | import java.util.List;
31 |
32 | import javax.inject.Inject;
33 |
34 | import dagger.Module;
35 |
36 | public class SearchResultsActivity extends FragmentActivity {
37 |
38 | private static final String EXTRA_QUERY_STRING = "extra_query_string";
39 | private static final String ACTION_DISPLAY_SEARCH_RESULTS = "action_display_search_results";
40 | private static final String ACTION_DISPLAY_RECENT_UPLOADS = "action_display_recent_uploads";
41 |
42 | @Inject NetworkBitmapClient networkBitmap;
43 | @Inject NounProjectApi nounProjectApi;
44 | @Inject Configuration configuration;
45 | @Inject Tracker tracker;
46 |
47 | protected List data;
48 | protected BaseAdapter adapter;
49 | protected SingleIconResultItem.Listener itemListener;
50 | protected SearchResultsView contentView;
51 |
52 |
53 | @Override
54 | protected void onCreate(Bundle savedInstanceState) {
55 | super.onCreate(savedInstanceState);
56 | ((DependencyInjector) getApplication()).inject(this);
57 | contentView = new SearchResultsView(this);
58 | setContentView(contentView);
59 | itemListener = new ViewListener();
60 | adapter = new IconListAdapter();
61 | contentView.setAdapter(adapter);
62 | contentView.setState(SearchResultsView.State.Loading);
63 | contentView.setEmptyActionListener(getEmptyActionListener());
64 | contentView.setErrorActionListener(getErrorActionListener());
65 | executeIntentAction();
66 | }
67 |
68 | @Override
69 | public boolean onCreateOptionsMenu(Menu menu) {
70 | MenuInflater inflater = getMenuInflater();
71 | inflater.inflate(R.menu.main_user, menu);
72 | return true;
73 | }
74 |
75 | @Override
76 | public boolean onOptionsItemSelected(MenuItem item) {
77 | switch (item.getItemId()) {
78 | case R.id.menu_settings:
79 | startActivity(new Intent(this, SettingsActivity.class));
80 | return true;
81 | }
82 | return super.onOptionsItemSelected(item);
83 | }
84 |
85 | private EmptyView.Listener getEmptyActionListener() {
86 | return new EmptyView.Listener() {
87 | @Override
88 | public void onPrimaryActionClicked() {
89 | tracker.track("Empty Action Clicked", "button", "primary");
90 | finish();
91 | }
92 |
93 | @Override
94 | public void onSecondaryActionClicked() {
95 | tracker.track("Empty Action Clicked", "button", "secondary");
96 | finish();
97 | }
98 | };
99 | }
100 |
101 | private EmptyView.Listener getErrorActionListener() {
102 | return new EmptyView.Listener() {
103 | @Override
104 | public void onPrimaryActionClicked() {
105 | tracker.track("Error Action Clicked", "button", "primary");
106 | executeIntentAction();
107 | contentView.setState(SearchResultsView.State.Loading);
108 | }
109 |
110 | @Override
111 | public void onSecondaryActionClicked() {
112 | tracker.track("Error Action Clicked", "button", "secondary");
113 | finish();
114 | }
115 | };
116 | }
117 |
118 | private void executeIntentAction() {
119 | if (ACTION_DISPLAY_RECENT_UPLOADS.equals(getIntent().getAction())) {
120 | nounProjectApi.recent(new ApiCallback());
121 | setTitle(R.string.recent_uploads);
122 | } else {
123 | String query = getIntent().getStringExtra(EXTRA_QUERY_STRING);
124 | nounProjectApi.search(query, new ApiCallback());
125 | setTitle(getString(R.string.query_pattern, query));
126 | }
127 | }
128 |
129 | public static class Builder {
130 | String query;
131 | Context context;
132 |
133 | public Builder(Context context) {
134 | this.context = context;
135 | }
136 |
137 | public Builder withSearchTerm(String query) {
138 | this.query = query;
139 | return this;
140 | }
141 |
142 | public Intent build() {
143 | Intent intent = new Intent(context, SearchResultsActivity.class);
144 | if (!Strings.isEmpty(query)) {
145 | intent.setAction(ACTION_DISPLAY_SEARCH_RESULTS);
146 | intent.putExtra(EXTRA_QUERY_STRING, query);
147 | } else {
148 | intent.setAction(ACTION_DISPLAY_RECENT_UPLOADS);
149 | }
150 | return intent;
151 | }
152 | }
153 |
154 | private class ViewListener implements SingleIconResultItem.Listener {
155 | @Override
156 | public void onLinkClicked(String path) {
157 | tracker.track("Icon Selected", "icon", path);
158 | String finalUrl = configuration.getNounProjectBaseUrl() + path;
159 | Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(finalUrl));
160 | startActivity(intent);
161 | }
162 |
163 | @Override
164 | public void requestDownloadInto(String url, ImageView imageView) {
165 | networkBitmap.downloadInto(url, imageView);
166 | }
167 | }
168 |
169 | private class IconListAdapter extends BaseAdapter {
170 | @Override
171 | public int getCount() {
172 | if (data == null)
173 | return 0;
174 | return data.size();
175 | }
176 | @Override
177 | public Object getItem(int position) {
178 | return data.get(position);
179 | }
180 | @Override
181 | public long getItemId(int position) {
182 | return position;
183 | }
184 | @Override
185 | public boolean areAllItemsEnabled() {
186 | return false;
187 | }
188 | @Override
189 | public boolean isEnabled(int position) {
190 | return false;
191 | }
192 | @Override
193 | public View getView(int position, View convertView, ViewGroup parent) {
194 | SingleIconResultItem listItem = (SingleIconResultItem) convertView;
195 | if (listItem == null) {
196 | listItem = new SingleIconResultItem(SearchResultsActivity.this);
197 | listItem.setListener(itemListener);
198 | }
199 | listItem.setIconData((Icon) getItem(position));
200 | return listItem;
201 | }
202 | }
203 |
204 | private class ApiCallback implements NounProjectApi.Callback {
205 | @Override
206 | public void onSuccess(List icons) {
207 | data = icons;
208 | adapter.notifyDataSetChanged();
209 | contentView.setState(SearchResultsView.State.Loaded);
210 | tracker.track("Success Load", "count", String.valueOf(icons.size()));
211 | }
212 |
213 | @Override
214 | public void onFailure(Throwable error) {
215 | tracker.track("Failed Load", "error", error.toString());
216 | boolean isSearch = !ACTION_DISPLAY_RECENT_UPLOADS.equals(getIntent().getAction());
217 | boolean isNoResultJson = error instanceof JsonSyntaxException;
218 | if (isSearch && isNoResultJson) {
219 | contentView.setState(SearchResultsView.State.Empty);
220 | } else {
221 | contentView.setState(SearchResultsView.State.Error);
222 | }
223 | adapter.notifyDataSetChanged();
224 | }
225 | }
226 |
227 | @Module(injects = {SearchResultsActivity.class}, includes = SingletonModule.class)
228 | public static class SearchResultDepedencyModule { }
229 | }
230 |
--------------------------------------------------------------------------------
/app/src/main/java/com/btellez/solidandroid/activity/SelectionActivity.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.activity;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.support.v4.app.FragmentActivity;
6 | import android.view.Menu;
7 | import android.view.MenuInflater;
8 | import android.view.MenuItem;
9 |
10 | import com.btellez.solidandroid.R;
11 | import com.btellez.solidandroid.module.DependencyInjector;
12 | import com.btellez.solidandroid.module.SingletonModule;
13 | import com.btellez.solidandroid.utility.Tracker;
14 | import com.btellez.solidandroid.view.SelectionScreenView;
15 |
16 | import javax.inject.Inject;
17 |
18 | import dagger.Module;
19 |
20 | public class SelectionActivity extends FragmentActivity {
21 |
22 | private SelectionScreenView selectionScreenView;
23 | @Inject Tracker tracker;
24 |
25 | @Override
26 | protected void onCreate(Bundle savedInstanceState) {
27 | super.onCreate(savedInstanceState);
28 | ((DependencyInjector) getApplication()).inject(this);
29 | selectionScreenView = new SelectionScreenView(this);
30 | setContentView(selectionScreenView);
31 | selectionScreenView.setListener(new SelectionScreenView.SimpleListener() {
32 | @Override
33 | public void onRecentGroupSelected() {
34 | Intent intent = new SearchResultsActivity
35 | .Builder(SelectionActivity.this)
36 | .build();
37 | startActivity(intent);
38 | tracker.track("Recent Selected");
39 | }
40 |
41 | @Override
42 | public void onSubmitSearchQuery(String query) {
43 | Intent intent = new SearchResultsActivity
44 | .Builder(SelectionActivity.this)
45 | .withSearchTerm(query).build();
46 | startActivity(intent);
47 | tracker.track("Search Submitted", "query", query);
48 | }
49 | });
50 | }
51 |
52 | @Override
53 | public boolean onCreateOptionsMenu(Menu menu) {
54 | MenuInflater inflater = getMenuInflater();
55 | inflater.inflate(R.menu.main_user, menu);
56 | return true;
57 | }
58 |
59 | @Override
60 | public boolean onOptionsItemSelected(MenuItem item) {
61 | switch (item.getItemId()) {
62 | case R.id.menu_settings:
63 | startActivity(new Intent(this, SettingsActivity.class));
64 | return true;
65 | }
66 | return super.onOptionsItemSelected(item);
67 | }
68 |
69 | @Module(injects = {SelectionActivity.class}, includes = SingletonModule.class)
70 | public static class SelectionActivityDepedencyModule { }
71 | }
72 |
--------------------------------------------------------------------------------
/app/src/main/java/com/btellez/solidandroid/activity/SettingsActivity.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.activity;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.preference.Preference;
6 | import android.preference.PreferenceFragment;
7 |
8 | import com.btellez.solidandroid.R;
9 | import com.btellez.solidandroid.module.DependencyInjector;
10 | import com.btellez.solidandroid.module.SingletonModule;
11 | import com.btellez.solidandroid.utility.Tracker;
12 |
13 | import javax.inject.Inject;
14 |
15 | import dagger.Module;
16 |
17 | public class SettingsActivity extends Activity {
18 | @Override
19 | protected void onCreate(Bundle savedInstanceState) {
20 | super.onCreate(savedInstanceState);
21 | getFragmentManager()
22 | .beginTransaction()
23 | .replace(android.R.id.content, new GeneralPreferenceFragment())
24 | .commit();
25 | }
26 |
27 | public static class GeneralPreferenceFragment extends PreferenceFragment {
28 |
29 | @Inject Tracker tracker;
30 |
31 | @Override
32 | public void onCreate(Bundle savedInstanceState) {
33 | super.onCreate(savedInstanceState);
34 | addPreferencesFromResource(R.xml.pref_general);
35 |
36 | findPreference(getString(R.string.pref_key_enable_history))
37 | .setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
38 | @Override
39 | public boolean onPreferenceClick(Preference preference) {
40 | tracker.track("Toggled History", "from", preference.getSummary().toString());
41 | // TODO: Clear History.
42 | return true;
43 | }
44 | });
45 | }
46 |
47 | @Override
48 | public void onAttach(Activity activity) {
49 | super.onAttach(activity);
50 | ((DependencyInjector) activity.getApplication()).inject(this);
51 | }
52 |
53 | @Module(injects = {GeneralPreferenceFragment.class}, includes = SingletonModule.class)
54 | public static class GeneralPreferenceDepedencyModule { }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/app/src/main/java/com/btellez/solidandroid/configuration/Configuration.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.configuration;
2 |
3 | import android.content.Context;
4 |
5 | import com.btellez.solidandroid.R;
6 | import com.btellez.solidandroid.model.ApiKeys;
7 |
8 | public interface Configuration {
9 | ApiKeys getNounProjectApiKeys();
10 | String getNounProjectBaseUrl();
11 | String getNounProjectBaseApiUrl();
12 | String getReplayAPIKey();
13 |
14 | /**
15 | * Shared configuration settings for Release and Development.
16 | */
17 | abstract class BaseConfiguration implements Configuration {
18 | protected Context context;
19 |
20 | protected BaseConfiguration(Context context) {
21 | this.context = context;
22 | }
23 |
24 | @Override
25 | public String getNounProjectBaseApiUrl() {
26 | return context.getString(R.string.noun_project_base_api_url);
27 | }
28 |
29 | @Override
30 | public String getNounProjectBaseUrl() {
31 | return context.getString(R.string.noun_project_base_url);
32 | }
33 |
34 | @Override
35 | public String getReplayAPIKey() {
36 | return context.getString(R.string.replay_io_api_key);
37 | }
38 | }
39 |
40 | /**
41 | * Configuration for the Release Version of our Application.
42 | */
43 | class ReleaseConfiguration extends BaseConfiguration {
44 |
45 | protected ReleaseConfiguration(Context context) {
46 | super(context);
47 | }
48 |
49 | @Override
50 | public ApiKeys getNounProjectApiKeys() {
51 | return new ApiKeys() {
52 | @Override public String getKey() {
53 | return context.getResources().getStringArray(R.array.noun_project_api_key)[1];
54 | }
55 |
56 | @Override public String getSecret() {
57 | return context.getResources().getStringArray(R.array.noun_project_api_secret)[1];
58 | }
59 | };
60 | }
61 | }
62 |
63 |
64 | /**
65 | * Configuration for the Development version of our application
66 | */
67 | class DevelopmentConfiguration extends BaseConfiguration {
68 |
69 | public DevelopmentConfiguration(Context context) {
70 | super(context);
71 | }
72 |
73 | @Override
74 | public ApiKeys getNounProjectApiKeys() {
75 | return new ApiKeys() {
76 | @Override public String getKey() {
77 | return context.getResources().getStringArray(R.array.noun_project_api_key)[0];
78 | }
79 |
80 | @Override public String getSecret() {
81 | return context.getResources().getStringArray(R.array.noun_project_api_secret)[0];
82 | }
83 | };
84 | }
85 | }
86 |
87 | }
88 |
--------------------------------------------------------------------------------
/app/src/main/java/com/btellez/solidandroid/configuration/KeyValueStore.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.configuration;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 |
6 | import java.util.HashMap;
7 |
8 | /**
9 | * KeyValueStore interface defined common methods that
10 | * may be used to store a set of key's and values to
11 | * a more persistant storage.
12 | */
13 | public interface KeyValueStore {
14 |
15 | void putFloat(String key, float value);
16 | float getFloat(String key, float defaultValue);
17 | void putInt(String key, int value);
18 | int getInt(String key, int defaultValue);
19 | String getString(String key, String defaultValue);
20 | String getString(String key);
21 | void putString(String key, String value);
22 | boolean getBool(String key);
23 | void putBool(String key, boolean value);
24 |
25 |
26 | /**
27 | * No-Op implementation of KeyValue Store.
28 | *
29 | * This allows us to selectively override some and not all methods.
30 | * This allows us to use this instead as to satisfy dependencies in
31 | * tests where we are not relying on the behaviour of KeyValueStore.
32 | */
33 | class SimpleKeyValueStore implements KeyValueStore {
34 | @Override public void putFloat(String key, float value) {}
35 | @Override public float getFloat(String key, float defaultValue) {return 0;}
36 | @Override public void putInt(String key, int value) {}
37 | @Override public int getInt(String key, int defaultValue) {return 0;}
38 | @Override public String getString(String key, String defaultValue) {return null;}
39 | @Override public String getString(String key) {return null;}
40 | @Override public void putString(String key, String value) {}
41 | @Override public boolean getBool(String key) {return false;}
42 | @Override public void putBool(String key, boolean value) {}
43 | }
44 |
45 | /**
46 | * Shared Preferences implementation of the key value store.
47 | * The values stored in with this key-value store implementation
48 | * will persist as long as the application is not unisntalled.
49 | */
50 | class SharedPrefs implements KeyValueStore {
51 | private static final String SHARE_PREFS_KEY_VALUE_STORE = "share_prefs_key_value_store";
52 | private SharedPreferences prefs;
53 |
54 | public SharedPrefs(Context context) {
55 | this(context, SHARE_PREFS_KEY_VALUE_STORE);
56 | }
57 |
58 | public SharedPrefs(Context context, String storeIdentifier) {
59 | prefs = context.getSharedPreferences(storeIdentifier, Context.MODE_PRIVATE);
60 | }
61 |
62 | @Override
63 | public void putFloat(String key, float value) {
64 | prefs.edit().putFloat(key, value).apply();
65 | }
66 |
67 | @Override
68 | public float getFloat(String key, float defaultValue) {
69 | return prefs.getFloat(key, defaultValue);
70 | }
71 |
72 | @Override
73 | public void putInt(String key, int value) {
74 | prefs.edit().putInt(key, value).apply();
75 | }
76 |
77 | @Override
78 | public int getInt(String key, int defaultValue) {
79 | return prefs.getInt(key, defaultValue);
80 | }
81 |
82 | @Override
83 | public void putString(String key, String value) {
84 | prefs.edit().putString(key, value).apply();
85 | }
86 |
87 | @Override
88 | public boolean getBool(String key) {
89 | return prefs.getBoolean(key, false);
90 | }
91 |
92 | @Override
93 | public void putBool(String key, boolean value) {
94 | prefs.edit().putBoolean(key, value).apply();
95 | }
96 |
97 | @Override
98 | public String getString(String key) {
99 | return getString(key, null);
100 | }
101 |
102 | @Override
103 | public String getString(String key, String defaultValue) {
104 | return prefs.getString(key, defaultValue);
105 | }
106 | }
107 |
108 | /**
109 | * Session is an in-memory key-value store whose data will
110 | * cease to exist when the application is killed.
111 | */
112 | class Session implements KeyValueStore {
113 | private HashMap map = new HashMap();
114 |
115 | @Override
116 | public void putFloat(String key, float value) {
117 | map.put(key, value);
118 | }
119 |
120 | @Override
121 | public float getFloat(String key, float defaultValue) {
122 | return map.containsKey(key) ? (Float) map.get(key) : defaultValue;
123 | }
124 |
125 | @Override
126 | public void putInt(String key, int value) {
127 | map.put(key, value);
128 | }
129 |
130 | @Override
131 | public int getInt(String key, int defaultValue) {
132 | return map.containsKey(key) ? (Integer) map.get(key) : defaultValue;
133 | }
134 |
135 | @Override
136 | public String getString(String key) {
137 | return getString(key, null);
138 | }
139 |
140 | @Override
141 | public String getString(String key, String defaultValue) {
142 | return map.containsKey(key) ? (String) map.get(key) : defaultValue;
143 | }
144 |
145 | @Override
146 | public void putString(String key, String value) {
147 | map.put(key, value);
148 | }
149 |
150 | @Override
151 | public boolean getBool(String key) {
152 | return map.containsKey(key) && (boolean) map.get(key);
153 | }
154 |
155 | @Override
156 | public void putBool(String key, boolean value) {
157 | map.put(key, value);
158 | }
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/app/src/main/java/com/btellez/solidandroid/model/ApiKeys.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.model;
2 |
3 | public interface ApiKeys {
4 | String getKey();
5 | String getSecret();
6 | }
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/btellez/solidandroid/model/Icon.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.model;
2 |
3 | import java.io.Serializable;
4 |
5 | /**
6 | * Object representing icon information returned
7 | * by the NounProject API.
8 | */
9 | public class Icon implements Serializable {
10 | private String attribution;
11 | private String attribution_icon_url;
12 | private String attribution_preview_url;
13 | private int count_download;
14 | private int count_purchase;
15 | private int count_view;
16 | private String date_uploaded;
17 | private String id;
18 | private String is_active;
19 | private String license_description;
20 | private String permalink;
21 | private String preview_url;
22 | private String preview_url_42;
23 | private String preview_url_84;
24 | private String sponsor_campaign_link;
25 | private String sponsor_id;
26 | private String term;
27 | private String term_id;
28 | private String term_slug;
29 | private Uploader uploader;
30 | private String uploader_id;
31 | private int year;
32 |
33 | public String getAttribution() {
34 | return attribution;
35 | }
36 |
37 | public int getDownloadCount() {
38 | return count_download;
39 | }
40 |
41 | public int getPurchaseCount() {
42 | return count_purchase;
43 | }
44 |
45 | public int getViewCount() {
46 | return count_view;
47 | }
48 |
49 | public String getPermalink() {
50 | return permalink;
51 | }
52 |
53 | public String getTerm() {
54 | return term;
55 | }
56 |
57 | public String getPreviewUrl() {
58 | return preview_url;
59 | }
60 |
61 | public Uploader getUploader() {
62 | return uploader;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/main/java/com/btellez/solidandroid/model/IconParser.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.model;
2 |
3 | import com.btellez.solidandroid.utility.Strings;
4 | import com.google.gson.Gson;
5 | import com.google.gson.GsonBuilder;
6 | import com.google.gson.JsonArray;
7 | import com.google.gson.JsonDeserializationContext;
8 | import com.google.gson.JsonDeserializer;
9 | import com.google.gson.JsonElement;
10 | import com.google.gson.JsonObject;
11 | import com.google.gson.JsonParseException;
12 | import com.google.gson.JsonParser;
13 |
14 | import java.lang.reflect.Type;
15 | import java.util.ArrayList;
16 | import java.util.List;
17 |
18 | public interface IconParser {
19 | List fromJson(String jsonString, String dataKey);
20 |
21 | /**
22 | * Google Gson Implementation of our Icon Parser
23 | */
24 | class GsonIconParser implements IconParser {
25 | Gson gson = new GsonBuilder().registerTypeAdapter(Icon.class, new IconDeserializer()).create();
26 | JsonParser parser = new JsonParser();
27 |
28 | @Override
29 | public List fromJson(String jsonString, String dataKey) {
30 | JsonElement json = parser.parse(jsonString);
31 | JsonArray iconList;
32 | if (Strings.isEmpty(dataKey)) {
33 | iconList = json.getAsJsonArray();
34 | } else {
35 | iconList = json.getAsJsonObject().getAsJsonArray(dataKey);
36 | }
37 |
38 | List icons = new ArrayList<>(iconList.size());
39 | for (JsonElement item : iconList) {
40 | icons.add(gson.fromJson(item, Icon.class));
41 | }
42 | return icons;
43 | }
44 |
45 | private class IconDeserializer implements JsonDeserializer {
46 | Gson defaultGson = new Gson();
47 | @Override
48 | public Icon deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
49 | JsonElement uploader = json.getAsJsonObject().get("uploader");
50 | if (!uploader.isJsonObject()) {
51 | json.getAsJsonObject().remove("uploader");
52 | json.getAsJsonObject().add("uploader", new JsonObject());
53 | }
54 | return defaultGson.fromJson(json, Icon.class);
55 | }
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/app/src/main/java/com/btellez/solidandroid/model/Uploader.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.model;
2 |
3 | import java.io.Serializable;
4 |
5 | /**
6 | * Object Representing user information returned
7 | * by the NounProject API.
8 | */
9 | public class Uploader implements Serializable{
10 | private String location;
11 | private String name;
12 | private String permalink;
13 | private String username;
14 |
15 | public String getLocation() {
16 | return location;
17 | }
18 |
19 | public String getName() {
20 | return name;
21 | }
22 |
23 | public String getPermalink() {
24 | return permalink;
25 | }
26 |
27 | public String getUsername() {
28 | return username;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/com/btellez/solidandroid/module/DependencyInjector.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.module;
2 |
3 | import android.content.Context;
4 |
5 | import dagger.ObjectGraph;
6 |
7 | /**
8 | * This interface defines behaviour that must be implemented by any
9 | * class that is to act as an injector. Ideally it should be a class
10 | * that will be a singleton instance and global in scope. Extensions
11 | * of `Application` are ideal for this.
12 | */
13 | public interface DependencyInjector {
14 | public Object[] getModules();
15 | public void inject(Object o);
16 |
17 |
18 | public static class SolidInjector implements DependencyInjector {
19 | private ObjectGraph mObjectGraph;
20 | private Context context;
21 |
22 | public SolidInjector(Context context) {
23 | this.context = context;
24 | }
25 |
26 | @Override
27 | public Object[] getModules() {
28 | return new Object[]{
29 | // Each Activity and Class Defined their own Injector,
30 | // Shared Modules should go under the ../Module package
31 | };
32 | }
33 |
34 | @Override
35 | public void inject(Object o) {
36 | mObjectGraph.inject(o);
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/java/com/btellez/solidandroid/module/SingletonModule.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.module;
2 |
3 | import android.content.Context;
4 |
5 | import com.btellez.solidandroid.BuildConfig;
6 | import com.btellez.solidandroid.SimpleAndroidApplication;
7 | import com.btellez.solidandroid.configuration.Configuration;
8 | import com.btellez.solidandroid.model.IconParser;
9 | import com.btellez.solidandroid.network.NetworkBitmapClient;
10 | import com.btellez.solidandroid.network.NounProjectApi;
11 | import com.btellez.solidandroid.network.OkNounProjectApi;
12 | import com.btellez.solidandroid.utility.Tracker;
13 | import com.squareup.otto.Bus;
14 |
15 | import javax.inject.Singleton;
16 |
17 | import dagger.Module;
18 | import dagger.Provides;
19 |
20 | /**
21 | * This module is a provider for singletons across the application.
22 | *
23 | * If you find yourself creating a singleton class, you may be able to simply
24 | * use the @Singleton annotation and inject the singleton into your activities
25 | * or fragments.
26 | *
27 | * This module is intended to be extended. Use `includes = SingletonModule.class`
28 | * to include the injections defined here.
29 | *
30 | * including the ApplicationContextModule allows us to resolve the Context dependencies
31 | * automatically. Ensure that a ApplicationContextModule is initialize and added to the
32 | * list of modules being initialized.
33 | */
34 | @Module(library = true, includes = SimpleAndroidApplication.ApplicationContextModule.class)
35 | public class SingletonModule {
36 |
37 | @Provides @Singleton Configuration provideConfiguration(Context context) {
38 | return new Configuration.DevelopmentConfiguration(context);
39 | }
40 |
41 | @Provides @Singleton IconParser provudeIconParser() {
42 | return new IconParser.GsonIconParser();
43 | }
44 |
45 | @Provides @Singleton NounProjectApi provideNounProjectApi(Configuration configuration, IconParser parser) {
46 | return new OkNounProjectApi(configuration, parser);
47 | }
48 |
49 | @Provides @Singleton NetworkBitmapClient provideNetworkBitmapClient() {
50 | return new NetworkBitmapClient.PicassoBitmapClient();
51 | }
52 |
53 | @Provides @Singleton Bus provideEventBus() {
54 | return new Bus();
55 | }
56 |
57 | @Provides @Singleton Tracker provideTracker(Context context, Configuration appConfig) {
58 | if (BuildConfig.DEBUG) {
59 | return new Tracker.SimpleTracker();
60 | } else {
61 | return new Tracker.ReplayTracker(context, appConfig);
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/main/java/com/btellez/solidandroid/network/NetworkBitmapClient.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.network;
2 |
3 | import android.widget.ImageView;
4 |
5 | import com.squareup.picasso.Picasso;
6 |
7 | public interface NetworkBitmapClient {
8 | void downloadInto(String url, ImageView imageView);
9 |
10 | /**
11 | * Picasso Implementation of NetworkBitmapClient
12 | */
13 | class PicassoBitmapClient implements NetworkBitmapClient {
14 | @Override public void downloadInto(String url, ImageView imageView) {
15 | Picasso.with(imageView.getContext()).load(url).into(imageView);
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/btellez/solidandroid/network/NounProjectApi.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.network;
2 |
3 | import com.btellez.solidandroid.configuration.Configuration;
4 | import com.btellez.solidandroid.model.Icon;
5 |
6 | import java.util.List;
7 |
8 | public interface NounProjectApi {
9 | void search(String term, Callback callback);
10 | void recent(Callback callback);
11 |
12 | /**
13 | * Endpoint Helper that builds correctly formatter URLs
14 | * for the NounProjectAPI.
15 | */
16 | class EndpointBuilder {
17 | public String buildSearchUrl(Configuration config, String term) {
18 | return config.getNounProjectBaseUrl() + "/search/json/icon/?q=" + term + "/?page=1&limit=100&offset=0&raw_html=false";
19 | }
20 |
21 | public String buildRecentUrl(Configuration config) {
22 | return config.getNounProjectBaseApiUrl() + "icons/recent_uploads";
23 | }
24 | }
25 |
26 | /**
27 | * Callback that alerts any listeners about success or failure of the api call.
28 | */
29 | interface Callback {
30 | void onSuccess(List icons);
31 | void onFailure(Throwable error);
32 | }
33 |
34 |
35 | /**
36 | * No-Op Implementation of the Callback that allows overriding select methods,
37 | * instead of forcing implementor to override all methods.
38 | */
39 | class SimpleCallback implements Callback {
40 | @Override public void onSuccess(List icons) {/* no-op */}
41 | @Override public void onFailure(Throwable error) {/* no-op */}
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/java/com/btellez/solidandroid/network/NounProjectOAuth.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.network;
2 |
3 | import android.net.Uri;
4 | import android.support.v4.util.Pair;
5 | import android.util.Base64;
6 |
7 | import com.btellez.solidandroid.model.ApiKeys;
8 | import com.btellez.solidandroid.utility.Strings;
9 |
10 | import java.util.ArrayList;
11 | import java.util.Collections;
12 | import java.util.Comparator;
13 | import java.util.List;
14 |
15 | import javax.crypto.Mac;
16 | import javax.crypto.spec.SecretKeySpec;
17 |
18 | /**
19 | * Based on OAuth Implementation on TheNounProject Api Explorer:
20 | * http://api.thenounproject.com/lib/oauth-1.0a.js
21 | */
22 | public class NounProjectOAuth {
23 |
24 | public enum RequestType {GET, POST}
25 |
26 | private static final String AMP = "&";
27 | private static final String EQ = "=";
28 |
29 | private ApiKeys keys;
30 | private String endpoint;
31 | private RequestType requestType;
32 |
33 | public NounProjectOAuth(ApiKeys keys) {
34 | this.keys = keys;
35 | }
36 |
37 | public NounProjectOAuth withEnpoint(String endpoint) {
38 | this.endpoint = endpoint;
39 | return this;
40 | }
41 |
42 | public NounProjectOAuth withRequestType(RequestType requestType) {
43 | this.requestType = requestType;
44 | return this;
45 | }
46 |
47 | public String getOAuthHeader() {
48 | assertReady(endpoint, requestType);
49 | List> oAuthHeaderList = new ArrayList<>();
50 | oAuthHeaderList.add(new Pair<>("oauth_signature_method", "HMAC-SHA1"));
51 | oAuthHeaderList.add(new Pair<>("oauth_version", "1.0"));
52 | oAuthHeaderList.add(new Pair<>("oauth_consumer_key", keys.getKey()));
53 | oAuthHeaderList.add(new Pair<>("oauth_nonce", getNounce(32)));
54 | oAuthHeaderList.add(new Pair<>("oauth_timestamp", String.valueOf(System.currentTimeMillis()/1000)));
55 |
56 | String signature = computeSignature(sortOAuthHeaderParams(oAuthHeaderList));
57 | oAuthHeaderList.add(new Pair<>("oauth_signature", encode(signature)));
58 | return "OAuth "+ getOAuthheaderString(sortOAuthHeaderParams(oAuthHeaderList));
59 | }
60 |
61 | private void assertReady(String endpoint, RequestType requestType) {
62 | if (Strings.isEmpty(endpoint) || requestType == null) {
63 | throw new IllegalArgumentException("Endpoint and Request type are both required fields.");
64 | }
65 | }
66 |
67 | private String getOAuthheaderString(List> headerList) {
68 | StringBuffer sb = new StringBuffer();
69 | for (Pair headers : headerList) {
70 | sb.append(String.format("%s=\"%s\", ", headers.first, headers.second));
71 | }
72 | return sb.subSequence(0, sb.length()-2).toString(); // trim extra space and comma.;
73 | }
74 |
75 | protected String computeSignature(List> headerList) {
76 | String baseString = requestType.name().toUpperCase() + AMP;
77 | baseString += encode(endpoint) + AMP;
78 | baseString += encode(getParamString(headerList));
79 | return calculateHMACSHA1(getSignatureKey(), baseString);
80 | }
81 |
82 | private String getParamString(List> headerList) {
83 | StringBuffer paramsString = new StringBuffer();
84 | for (Pair set : headerList) {
85 | paramsString.append(encode(set.first)).append(EQ)
86 | .append(encode(set.second)).append(AMP);
87 | }
88 | return paramsString.subSequence(0, paramsString.length() - 1).toString(); // trim extra ampersand
89 | }
90 |
91 | // Utility Methods
92 |
93 | private String getSignatureKey() {
94 | return encode(keys.getSecret()) + AMP;
95 | }
96 |
97 | private String getNounce(int length) {
98 | String alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
99 | StringBuilder result = new StringBuilder(32);
100 |
101 | for(int i = 0; i < length; i++) {
102 | result.append(alphabet.charAt((int) Math.floor(Math.random() * alphabet.length())));
103 | }
104 | return result.toString();
105 | }
106 |
107 | private String encode(String s) {
108 | return Uri.encode(s);
109 | }
110 |
111 | private List> sortOAuthHeaderParams(List> headerList) {
112 | Collections.sort(headerList, new Comparator>() {
113 | @Override
114 | public int compare(Pair lhs, Pair rhs) {
115 | return lhs.first.compareTo(rhs.first);
116 | }
117 | });
118 | return headerList;
119 | }
120 |
121 | public String calculateHMACSHA1(String key, String data)
122 | {
123 | try {
124 | Mac mac = Mac.getInstance("HmacSHA1");
125 | SecretKeySpec secret = new SecretKeySpec(key.getBytes("UTF-8"), mac.getAlgorithm());
126 | mac.init(secret);
127 | byte[] digest = mac.doFinal(data.getBytes());
128 | return Base64.encodeToString(digest, Base64.NO_WRAP);
129 | } catch (Exception e) {
130 | return "";
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/app/src/main/java/com/btellez/solidandroid/network/OkNounProjectApi.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.network;
2 |
3 | import android.os.Handler;
4 | import android.os.Looper;
5 |
6 | import com.btellez.solidandroid.configuration.Configuration;
7 | import com.btellez.solidandroid.model.Icon;
8 | import com.btellez.solidandroid.model.IconParser;
9 | import com.google.gson.JsonParseException;
10 | import com.squareup.okhttp.OkHttpClient;
11 | import com.squareup.okhttp.Request;
12 | import com.squareup.okhttp.Response;
13 |
14 | import java.io.IOException;
15 | import java.util.List;
16 |
17 | import javax.inject.Inject;
18 |
19 | /**
20 | * Implementation of the NounProjectApi interface that uses
21 | * the OkHttp Library.
22 | */
23 | public class OkNounProjectApi implements NounProjectApi {
24 | @Inject Configuration configuration;
25 | @Inject IconParser iconParser;
26 |
27 | // Implementation Related Dependencies
28 | private NounProjectOAuth oAuth;
29 | private OkHttpClient client;
30 |
31 | public OkNounProjectApi(Configuration configuration, IconParser iconParser) {
32 | this.configuration = configuration;
33 | this.iconParser = iconParser;
34 |
35 | // Fulfil Implementation Related Dependencies
36 | this.client = new OkHttpClient();
37 | this.oAuth = new NounProjectOAuth(configuration.getNounProjectApiKeys());
38 | }
39 |
40 | @Override
41 | public void search(String term, final Callback callback) {
42 | String endpoint = new EndpointBuilder().buildSearchUrl(configuration, term);
43 | Request request = buildRequestPublic(endpoint);
44 | client.newCall(request).enqueue(new OkHttpCallback(callback, "icons"));
45 | }
46 |
47 | @Override
48 | public void recent(final Callback callback) {
49 | String endpoint = new EndpointBuilder().buildRecentUrl(configuration);
50 | Request request = buildRequestOAuth(endpoint, NounProjectOAuth.RequestType.GET);
51 | client.newCall(request).enqueue(new OkHttpCallback(callback, "recent_uploads"));
52 | }
53 |
54 |
55 | /**
56 | * Builds Requests for public endpoints that do not require authentication.
57 | *
58 | * @param endpoint
59 | * @return Request
60 | */
61 | private Request buildRequestPublic(String endpoint) {
62 | return new Request.Builder().url(endpoint)
63 | .addHeader("X-Requested-With", "XMLHttpRequest")
64 | .build();
65 | }
66 |
67 |
68 | /**
69 | * Builds Request for endpoints that do require authentication.
70 | * @param endpoint
71 | * @param method
72 | * @return Request
73 | */
74 | private Request buildRequestOAuth(String endpoint, NounProjectOAuth.RequestType method) {
75 | String oAuthString = oAuth.withEnpoint(endpoint)
76 | .withRequestType(method)
77 | .getOAuthHeader();
78 |
79 | return new Request.Builder().url(endpoint)
80 | .addHeader("Authorization", oAuthString)
81 | .build();
82 | }
83 |
84 | /**
85 | * OkHttpCallback wrapper the app's defined callback interface.
86 | * This allows the the callback implementation to be decoupled form okHttp.
87 | */
88 | private class OkHttpCallback implements com.squareup.okhttp.Callback {
89 | public Callback callback;
90 | public String dataKey;
91 | private Handler uiHandler = new Handler(Looper.getMainLooper());
92 |
93 | private OkHttpCallback(Callback callback) {
94 | this(callback, null);
95 | }
96 |
97 | private OkHttpCallback(Callback callback, String dataKey) {
98 | this.callback = callback;
99 | this.dataKey = dataKey;
100 | }
101 |
102 | @Override public void onFailure(final Request request,final IOException e) {
103 | // We need to post update to the UI thread.
104 | uiHandler.post(new Runnable() {
105 | @Override
106 | public void run() {
107 | callback.onFailure(e);
108 | }
109 | });
110 | }
111 |
112 | @Override public void onResponse(final Response response) throws IOException {
113 | try {
114 | String jsonString = response.body().string();
115 | final List result = iconParser.fromJson(jsonString, dataKey);
116 | // We need to post update to the UI thread.
117 | uiHandler.post(new Runnable() {
118 | @Override
119 | public void run() {
120 | callback.onSuccess(result);
121 | }
122 | });
123 | } catch (final JsonParseException nonJsonResponse) {
124 | uiHandler.post(new Runnable() {
125 | @Override
126 | public void run() {
127 | callback.onFailure(nonJsonResponse);
128 | }
129 | });
130 | }
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/app/src/main/java/com/btellez/solidandroid/utility/AnalyticsEvents.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.utility;
2 |
3 | public interface AnalyticsEvents {
4 | interface Lifecycle {
5 | String Start = "Application Start";
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/java/com/btellez/solidandroid/utility/Strings.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.utility;
2 |
3 | import android.text.TextUtils;
4 |
5 | public class Strings {
6 | public static boolean isEmpty(String s) {
7 | return TextUtils.isEmpty(s) || TextUtils.getTrimmedLength(s) == 0;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/btellez/solidandroid/utility/Tracker.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.utility;
2 |
3 | import android.content.Context;
4 |
5 | import com.btellez.solidandroid.BuildConfig;
6 | import com.btellez.solidandroid.configuration.Configuration;
7 |
8 | import java.util.Map;
9 |
10 | import hugo.weaving.DebugLog;
11 | import io.replay.framework.Config;
12 | import io.replay.framework.ReplayIO;
13 |
14 | public interface Tracker {
15 | void track(String eventName);
16 | void track(String eventName, String...properties);
17 | void track(String eventName, Map properties);
18 |
19 | /**
20 | * Simple No-op implementation of tracking.
21 | */
22 | class SimpleTracker implements Tracker {
23 | @DebugLog @Override public void track(String eventName) {}
24 | @DebugLog @Override public void track(String eventName, String... properties) {}
25 | @DebugLog @Override public void track(String eventName, Map properties) {}
26 | }
27 |
28 | /**
29 | * Replay.io implementation of tracking
30 | */
31 | class ReplayTracker implements Tracker {
32 |
33 | public ReplayTracker(Context context, Configuration appConfig) {
34 | // Set-Up Replay Settings
35 | String replayApiKey = appConfig.getReplayAPIKey();
36 | Config config = new Config(replayApiKey);
37 | config.setDebug(BuildConfig.DEBUG);
38 | config.setDispatchInterval(60000);
39 | config.setEnabled(true);
40 | config.setFlushAt(20);
41 | ReplayIO.init(context, config);
42 | track(AnalyticsEvents.Lifecycle.Start);
43 | }
44 |
45 | @Override
46 | public void track(String eventName) {
47 | ReplayIO.track(eventName);
48 | }
49 |
50 | @Override
51 | public void track(String eventName, String... properties) {
52 | ReplayIO.track(eventName, (Object[]) properties);
53 | }
54 |
55 | @Override
56 | public void track(String eventName, Map properties) {
57 | ReplayIO.track(eventName, properties);
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/java/com/btellez/solidandroid/view/EmptyView.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.view;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.os.Build;
6 | import android.util.AttributeSet;
7 | import android.widget.Button;
8 | import android.widget.FrameLayout;
9 | import android.widget.TextView;
10 |
11 | import com.btellez.solidandroid.R;
12 |
13 | import butterknife.ButterKnife;
14 | import butterknife.InjectView;
15 | import butterknife.OnClick;
16 |
17 | public class EmptyView extends FrameLayout {
18 |
19 | public interface Listener {
20 | void onPrimaryActionClicked();
21 | void onSecondaryActionClicked();
22 | }
23 |
24 | public static class SimpleListener implements Listener {
25 | @Override public void onPrimaryActionClicked() {}
26 | @Override public void onSecondaryActionClicked() {}
27 | }
28 |
29 | private Listener listener;
30 | @InjectView(R.id.headline) TextView headline;
31 | @InjectView(R.id.subheadline) TextView subheadline;
32 | @InjectView(R.id.primary_action) Button buttonPrimary;
33 | @InjectView(R.id.secondary_action) Button buttonSecondary;
34 |
35 | public EmptyView(Context context) {
36 | super(context);
37 | init(context);
38 | }
39 |
40 | public EmptyView(Context context, AttributeSet attrs) {
41 | super(context, attrs);
42 | init(context);
43 | }
44 |
45 | public EmptyView(Context context, AttributeSet attrs, int defStyleAttr) {
46 | super(context, attrs, defStyleAttr);
47 | init(context);
48 | }
49 |
50 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
51 | public EmptyView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
52 | super(context, attrs, defStyleAttr, defStyleRes);
53 | init(context);
54 | }
55 |
56 | public void setHeadline(int resString) {
57 | setHeadline(getResources().getString(resString));
58 | }
59 |
60 | public void setHeadline(String value) {
61 | headline.setText(value);
62 | }
63 |
64 | public void setSubheadline(int resString) {
65 | setSubheadline(getResources().getString(resString));
66 | }
67 |
68 | public void setSubheadline(String value) {
69 | subheadline.setText(value);
70 | }
71 |
72 | public void setListener(Listener listener) {
73 | if (listener == null)
74 | listener = new SimpleListener();
75 | this.listener = listener;
76 | }
77 |
78 | public void setPrimaryActionName(int resString) {
79 | setPrimaryActionName(getResources().getString(resString));
80 | }
81 |
82 | public void setPrimaryActionName(String value) {
83 | buttonPrimary.setText(value);
84 | }
85 |
86 | public void setSecondaryActionName(int resString) {
87 | setSecondaryActionName(getResources().getString(resString));
88 | }
89 |
90 | public void setSecondaryActionName(String value) {
91 | buttonSecondary.setText(value);
92 | }
93 |
94 | private void init(Context context) {
95 | inflate(context, R.layout.error_view, this);
96 | ButterKnife.inject(this);
97 | listener = new SimpleListener();
98 | }
99 |
100 | @OnClick(R.id.primary_action)
101 | protected void onPrimaryActionClicked() {
102 | listener.onPrimaryActionClicked();
103 | }
104 |
105 | @OnClick(R.id.secondary_action)
106 | protected void onSecondaryActionClicked() {
107 | listener.onSecondaryActionClicked();
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/app/src/main/java/com/btellez/solidandroid/view/FadeAnimator.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.view;
2 |
3 | import android.animation.Animator;
4 | import android.animation.ObjectAnimator;
5 | import android.view.View;
6 | import android.view.animation.AccelerateDecelerateInterpolator;
7 | import android.view.animation.Interpolator;
8 |
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | public class FadeAnimator {
13 | private Interpolator interpolator = new AccelerateDecelerateInterpolator();
14 | private Animator.AnimatorListener hideListener = new SimpleAnimatorListener();
15 | private Animator.AnimatorListener showListener = new SimpleAnimatorListener();
16 | private List showAnimations = new ArrayList<>();
17 | private List hideAnimations = new ArrayList<>();
18 |
19 | public FadeAnimator hide(View view, long duration, long delay) {
20 | if (view != null) {
21 | ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(view, "alpha", 1.0f, 0f);
22 | objectAnimator.setDuration(duration);
23 | objectAnimator.setStartDelay(delay);
24 | objectAnimator.setInterpolator(interpolator);
25 | hideAnimations.add(objectAnimator);
26 | }
27 | return this;
28 | }
29 |
30 | public FadeAnimator show(View view, long duration, long delay) {
31 | if (view != null) {
32 | view.setAlpha(0);
33 | view.setVisibility(View.VISIBLE);
34 | ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(view, "alpha", 0, 1.0f);
35 | objectAnimator.setDuration(duration);
36 | objectAnimator.setStartDelay(delay);
37 | objectAnimator.setInterpolator(interpolator);
38 | showAnimations.add(objectAnimator);
39 | }
40 | return this;
41 | }
42 |
43 | public FadeAnimator withShowListener(Animator.AnimatorListener listener) {
44 | this.hideListener = validate(listener);
45 | return this;
46 | }
47 |
48 | public FadeAnimator withHideListener(Animator.AnimatorListener listener) {
49 | this.hideListener = validate(listener);
50 | return this;
51 | }
52 |
53 | private Animator.AnimatorListener validate(Animator.AnimatorListener listener) {
54 | if (listener == null)
55 | listener = new SimpleAnimatorListener();
56 | return listener;
57 | }
58 |
59 | public void setInterpolator(Interpolator interpolator) {
60 | if (interpolator == null) {
61 | interpolator = new AccelerateDecelerateInterpolator();
62 | }
63 | this.interpolator = interpolator;
64 | }
65 |
66 | public void start() {
67 | startAnimations(hideAnimations, hideListener);
68 | startAnimations(showAnimations, showListener);
69 | }
70 |
71 | private void startAnimations(List animations, Animator.AnimatorListener listener) {
72 | for (ObjectAnimator animator : animations) {
73 | animator.addListener(listener);
74 | animator.start();
75 | }
76 | }
77 |
78 | public static class SimpleAnimatorListener implements Animator.AnimatorListener {
79 | @Override public void onAnimationStart(Animator animation) {}
80 | @Override public void onAnimationEnd(Animator animation) {}
81 | @Override public void onAnimationCancel(Animator animation) {}
82 | @Override public void onAnimationRepeat(Animator animation) {}
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/app/src/main/java/com/btellez/solidandroid/view/SearchResultsView.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.view;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.os.Build;
6 | import android.util.AttributeSet;
7 | import android.view.View;
8 | import android.widget.BaseAdapter;
9 | import android.widget.FrameLayout;
10 | import android.widget.ListView;
11 | import android.widget.ProgressBar;
12 |
13 | import com.btellez.solidandroid.R;
14 |
15 | import butterknife.ButterKnife;
16 | import butterknife.InjectView;
17 |
18 | public class SearchResultsView extends FrameLayout {
19 |
20 | @InjectView(R.id.result_list) ListView listView;
21 | @InjectView(R.id.progress_indicator) ProgressBar progress;
22 |
23 | protected EmptyView emptyView;
24 | private EmptyView.Listener emptyActionListener;
25 | private EmptyView.Listener errorActionListener;
26 |
27 | public enum State {Loading, Error, Empty, Loaded}
28 |
29 | public SearchResultsView(Context context) {
30 | super(context);
31 | init(context);
32 | }
33 |
34 | public SearchResultsView(Context context, AttributeSet attrs) {
35 | super(context, attrs);
36 | init(context);
37 | }
38 |
39 | public SearchResultsView(Context context, AttributeSet attrs, int defStyleAttr) {
40 | super(context, attrs, defStyleAttr);
41 | init(context);
42 | }
43 |
44 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
45 | public SearchResultsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
46 | super(context, attrs, defStyleAttr, defStyleRes);
47 | init(context);
48 | }
49 |
50 | private void init(Context context) {
51 | inflate(context, R.layout.activity_search_results, this);
52 | ButterKnife.inject(this);
53 | emptyView = new EmptyView(context);
54 | addView(emptyView);
55 | listView.setEmptyView(emptyView);
56 | setState(State.Loading);
57 | }
58 |
59 | public void setAdapter(BaseAdapter adapter) {
60 | listView.setAdapter(adapter);
61 | }
62 |
63 | public void setEmptyActionListener(EmptyView.Listener listener) {
64 | if (listener == null)
65 | listener = new EmptyView.SimpleListener();
66 | this.emptyActionListener = listener;
67 | }
68 |
69 | public void setErrorActionListener(EmptyView.Listener listener) {
70 | if (listener == null)
71 | listener = new EmptyView.SimpleListener();
72 | this.errorActionListener = listener;
73 | }
74 |
75 | public void setState(State state) {
76 | switch (state) {
77 | case Loading:
78 | emptyView.setVisibility(View.INVISIBLE);
79 | listView.setVisibility(View.INVISIBLE);
80 | progress.setVisibility(View.VISIBLE);
81 | break;
82 | case Error:
83 | emptyView.setVisibility(View.VISIBLE);
84 | listView.setVisibility(View.INVISIBLE);
85 | progress.setVisibility(View.INVISIBLE);
86 | onErrorState();
87 | break;
88 | case Empty:
89 | emptyView.setVisibility(View.VISIBLE);
90 | listView.setVisibility(View.INVISIBLE);
91 | progress.setVisibility(View.INVISIBLE);
92 | onEmptyState();
93 | break;
94 | case Loaded:
95 | emptyView.setVisibility(View.INVISIBLE);
96 | listView.setVisibility(View.VISIBLE);
97 | progress.setVisibility(View.INVISIBLE);
98 | break;
99 | }
100 | }
101 |
102 | private void onEmptyState() {
103 | emptyView.setHeadline(R.string.no_results);
104 | emptyView.setSubheadline(R.string.no_results_detail);
105 | emptyView.setPrimaryActionName(R.string.change_search);
106 | emptyView.setSecondaryActionName(R.string.go_back);
107 | emptyView.setListener(emptyActionListener);
108 | }
109 |
110 | private void onErrorState() {
111 | emptyView.setHeadline(R.string.unable_to_load);
112 | emptyView.setSubheadline(R.string.unable_to_load_detail);
113 | emptyView.setPrimaryActionName(R.string.try_again);
114 | emptyView.setSecondaryActionName(R.string.go_back);
115 | emptyView.setListener(errorActionListener);
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/app/src/main/java/com/btellez/solidandroid/view/SelectionScreenView.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.view;
2 |
3 | import android.animation.Animator;
4 | import android.annotation.TargetApi;
5 | import android.content.Context;
6 | import android.os.Build;
7 | import android.util.AttributeSet;
8 | import android.view.View;
9 | import android.view.inputmethod.InputMethodManager;
10 | import android.widget.EditText;
11 | import android.widget.FrameLayout;
12 | import android.widget.RelativeLayout;
13 | import android.widget.TextView;
14 |
15 | import com.btellez.solidandroid.R;
16 | import com.btellez.solidandroid.utility.Strings;
17 |
18 | import butterknife.ButterKnife;
19 | import butterknife.InjectView;
20 | import butterknife.OnClick;
21 |
22 | public class SelectionScreenView extends FrameLayout {
23 |
24 | // Static Screen items:
25 | @InjectView(R.id.search) View searchGroup;
26 | @InjectView(R.id.recent) View recentGroup;
27 |
28 | // Views to be animated in:
29 | @InjectView(R.id.overlay) RelativeLayout overlay;
30 | @InjectView(R.id.dark_background) View overlayBackground;
31 | @InjectView(R.id.search_input_group) View searchInputGroup;
32 | @InjectView(R.id.search_input) EditText searchInput;
33 | @InjectView(R.id.error) TextView error;
34 | @InjectView(R.id.submit) View submit;
35 |
36 | private Listener listener = new SimpleListener();
37 |
38 | private enum State {ShowSelection, ShowOverlay}
39 |
40 | /**
41 | * Interface to alert any listeners about events
42 | * in our view. This helps to keep non-view logic out of
43 | * the the view.
44 | */
45 | public interface Listener {
46 | void onSeachGroupSelected();
47 | void onRecentGroupSelected();
48 | void onSubmitSearchQuery(String query);
49 | }
50 |
51 | public static class SimpleListener implements Listener {
52 | @Override public void onSeachGroupSelected() {}
53 | @Override public void onRecentGroupSelected() {}
54 | @Override public void onSubmitSearchQuery(String query) {}
55 | }
56 |
57 | public SelectionScreenView(Context context) {
58 | super(context);
59 | init(context);
60 | }
61 |
62 | public SelectionScreenView(Context context, AttributeSet attrs) {
63 | super(context, attrs);
64 | init(context);
65 | }
66 |
67 | public SelectionScreenView(Context context, AttributeSet attrs, int defStyleAttr) {
68 | super(context, attrs, defStyleAttr);
69 | init(context);
70 | }
71 |
72 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
73 | public SelectionScreenView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
74 | super(context, attrs, defStyleAttr, defStyleRes);
75 | init(context);
76 | }
77 |
78 | public void setListener(Listener listener) {
79 | if (listener == null)
80 | listener = new SimpleListener();
81 | this.listener = listener;
82 | }
83 |
84 | private void init(Context context) {
85 | inflate(context, R.layout.activity_selection, this);
86 | ButterKnife.inject(this);
87 | setState(State.ShowSelection);
88 | }
89 |
90 | @OnClick(R.id.dark_background)
91 | protected void onOverlayBackgroundClicked() {
92 | setState(State.ShowSelection);
93 | }
94 |
95 | @OnClick(R.id.search)
96 | protected void onSearchSelected() {
97 | listener.onSeachGroupSelected();
98 | setState(State.ShowOverlay);
99 | }
100 |
101 | @OnClick(R.id.recent)
102 | protected void onRecentSelected() {
103 | listener.onRecentGroupSelected();
104 | }
105 |
106 | @OnClick(R.id.submit)
107 | protected void onSubmitClicked() {
108 | String query = searchInput.getText().toString();
109 | if (Strings.isEmpty(query)) {
110 | setError(R.string.input_error);
111 | } else {
112 | listener.onSubmitSearchQuery(searchInput.getText().toString());
113 | }
114 | }
115 |
116 | private void setState(State state) {
117 | switch (state) {
118 | case ShowSelection:
119 | setKeyboardVisibility(View.GONE);
120 | hideOverlay();
121 | break;
122 | case ShowOverlay:
123 | showOverlay();
124 | resetError();
125 | setKeyboardVisibility(View.VISIBLE);
126 | break;
127 | }
128 | }
129 |
130 | private void showOverlay() {
131 | if (!isVisible(overlay)) {
132 | new FadeAnimator()
133 | .show(overlay, 0, 0)
134 | .show(overlayBackground, 250, 0)
135 | .show(searchInputGroup, 500, 200)
136 | .start();
137 | }
138 | }
139 |
140 | private void hideOverlay() {
141 | if (isVisible(overlay)) {
142 | new FadeAnimator()
143 | .hide(overlayBackground, 500, 0)
144 | .hide(searchInputGroup, 250, 200)
145 | .withHideListener(new FadeAnimator.SimpleAnimatorListener() {
146 | @Override
147 | public void onAnimationEnd(Animator animation) {
148 | overlay.setVisibility(INVISIBLE);
149 | }
150 | })
151 | .start();
152 | }
153 | }
154 |
155 | private void setKeyboardVisibility(int visiblity) {
156 | InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
157 | if (visiblity == View.VISIBLE) {
158 | searchInput.requestFocus();
159 | imm.toggleSoftInputFromWindow(searchInput.getApplicationWindowToken(), InputMethodManager.SHOW_FORCED, 0);
160 | } else {
161 | searchInput.clearFocus();
162 | imm.hideSoftInputFromWindow(searchInput.getWindowToken(), 0);
163 | }
164 | }
165 |
166 | private boolean isVisible(View view) {
167 | return view.getVisibility() == VISIBLE;
168 | }
169 |
170 | private void resetError() {
171 | new FadeAnimator()
172 | .hide(error, 250, 0)
173 | .start();
174 | }
175 |
176 | private void setError(int resString) {
177 | error.setText(resString);
178 | new FadeAnimator()
179 | .show(error, 250, 0)
180 | .start();
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/app/src/main/java/com/btellez/solidandroid/view/SingleIconResultItem.java:
--------------------------------------------------------------------------------
1 | package com.btellez.solidandroid.view;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.os.Build;
6 | import android.util.AttributeSet;
7 | import android.widget.FrameLayout;
8 | import android.widget.ImageView;
9 | import android.widget.TextView;
10 |
11 | import com.btellez.solidandroid.R;
12 | import com.btellez.solidandroid.model.Icon;
13 | import com.btellez.solidandroid.utility.Strings;
14 |
15 | import butterknife.ButterKnife;
16 | import butterknife.InjectView;
17 | import butterknife.OnClick;
18 |
19 | public class SingleIconResultItem extends FrameLayout {
20 |
21 | @InjectView(R.id.link) protected ImageView link;
22 | @InjectView(R.id.icon) protected ImageView icon;
23 | @InjectView(R.id.term) protected TextView term;
24 | @InjectView(R.id.attribution) protected TextView attribution;
25 | protected Listener listener = new Listener.SimpleListener();
26 |
27 | /**
28 | * Listener is used to keep non-ui logic out of the view.
29 | */
30 | public interface Listener {
31 | void onLinkClicked(String url);
32 | void requestDownloadInto(String url, ImageView imageView);
33 |
34 | class SimpleListener implements Listener {
35 | @Override public void onLinkClicked(String url) {/* no-op */}
36 | @Override public void requestDownloadInto(String url, ImageView imageView) {/* no-op */}
37 | }
38 | }
39 |
40 | public SingleIconResultItem(Context context) {
41 | super(context);
42 | init(context);
43 | }
44 |
45 | public SingleIconResultItem(Context context, AttributeSet attrs) {
46 | super(context, attrs);
47 | init(context);
48 | }
49 |
50 | public SingleIconResultItem(Context context, AttributeSet attrs, int defStyleAttr) {
51 | super(context, attrs, defStyleAttr);
52 | init(context);
53 | }
54 |
55 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
56 | public SingleIconResultItem(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
57 | super(context, attrs, defStyleAttr, defStyleRes);
58 | init(context);
59 | }
60 |
61 | private void init(Context context) {
62 | inflate(context, R.layout.single_icon_result_item, this);
63 | ButterKnife.inject(this);
64 | listener = new Listener.SimpleListener();
65 | }
66 |
67 | public void setListener(Listener listener) {
68 | if (listener == null)
69 | listener = new Listener.SimpleListener();
70 | this.listener = listener;
71 | }
72 |
73 | public void setIconData(Icon data) {
74 | term.setText(data.getTerm().toUpperCase());
75 | attribution.setText(getAttributionString(data));
76 | link.setTag(data.getPermalink());
77 | listener.requestDownloadInto(data.getPreviewUrl(), icon);
78 | }
79 |
80 | protected String getAttributionString(Icon data) {
81 | if (Strings.isEmpty(data.getUploader().getName())) {
82 | return "The Noun Project";
83 | }
84 | return data.getUploader().getName() +" from The Noun Project";
85 | }
86 |
87 | @OnClick(R.id.link)
88 | protected void onLinkClicked() {
89 | listener.onLinkClicked((String) link.getTag());
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blad/solid-android/5895bbf5ef549f8eb150d6ee7046eeaa137604b4/app/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_outbound.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blad/solid-android/5895bbf5ef549f8eb150d6ee7046eeaa137604b4/app/src/main/res/drawable-hdpi/ic_outbound.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blad/solid-android/5895bbf5ef549f8eb150d6ee7046eeaa137604b4/app/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_outbound.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blad/solid-android/5895bbf5ef549f8eb150d6ee7046eeaa137604b4/app/src/main/res/drawable-mdpi/ic_outbound.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blad/solid-android/5895bbf5ef549f8eb150d6ee7046eeaa137604b4/app/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_outbound.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blad/solid-android/5895bbf5ef549f8eb150d6ee7046eeaa137604b4/app/src/main/res/drawable-xhdpi/ic_outbound.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blad/solid-android/5895bbf5ef549f8eb150d6ee7046eeaa137604b4/app/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_outbound.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blad/solid-android/5895bbf5ef549f8eb150d6ee7046eeaa137604b4/app/src/main/res/drawable-xxhdpi/ic_outbound.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/solid_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blad/solid-android/5895bbf5ef549f8eb150d6ee7046eeaa137604b4/app/src/main/res/drawable/solid_search.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/solid_upload.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blad/solid-android/5895bbf5ef549f8eb150d6ee7046eeaa137604b4/app/src/main/res/drawable/solid_upload.png
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main_user.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
15 |
16 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_search_results.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_selection.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
11 |
12 |
16 |
21 |
22 |
29 |
30 |
39 |
40 |
41 |
46 |
47 |
54 |
55 |
65 |
66 |
67 |
68 |
69 |
74 |
75 |
81 |
82 |
87 |
88 |
99 |
100 |
113 |
114 |
126 |
127 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/error_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
19 |
20 |
31 |
32 |
44 |
45 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/single_icon_result_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
17 |
30 |
31 |
40 |
41 |
52 |
53 |
60 |
61 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/main_user.xml:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #607D8B
4 | #455A64
5 | #CFD8DC
6 | #FF5252
7 | #212121
8 | #727272
9 | #FFFFFF
10 | #B6B6B6
11 | #DD000000
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
13 | - fill-in-development-key
14 | - fill-in-production-key
15 |
16 |
17 |
18 |
19 |
20 | - fill-in-development-secret
21 | - fill-in-production-secret
22 |
23 |
24 | http://api.thenounproject.com/
25 | http://www.thenounproject.com
26 |
27 |
28 | fill-in-production-secret
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | SOLID: Noun Project Browser
4 | Simple String…
5 | Settings
6 | Recent Uploads
7 | search…
8 | Keyword Search
9 | Search term must not be empty!
10 | Search: %s
11 | Unable to Load Data
12 | We were unable to successfully fetch
13 | items form The Noun Project. Please check your connection and try again.
14 |
15 | Retry
16 | Go Back
17 | Sorry Nothing Found for Search Term
18 | No results were returned for the
19 | given term. Try a different search term.
20 |
21 | Change Term
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings_activity_settings.xml:
--------------------------------------------------------------------------------
1 |
2 | Settings
3 | pref_key_enable_notifications
4 | Enable notifications
5 | Enables notification for watched search results
6 |
7 | pref_key_enable_auto_complete
8 | Enable auto-complete suggestions
9 | Enables auto-complete suggestions while typing a search term
10 |
11 | pref_key_enable_history
12 | Enables search history
13 | Enables search history for every search constructed
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/pref_general.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
10 |
11 |
16 |
17 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/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.0.0'
9 | classpath 'com.jakewharton.sdkmanager:gradle-plugin:0.12.+' // SDK Manager Plugin
10 | classpath 'com.jakewharton.hugo:hugo-plugin:1.1.0' // Annotation-triggered method call logging for debug builds.
11 | // NOTE: Do not place your application dependencies here; they belong
12 | // in the individual module build.gradle files
13 | }
14 | }
15 |
16 | allprojects {
17 | repositories {
18 | jcenter()
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Settings specified in this file will override any Gradle settings
5 | # configured through the IDE.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blad/solid-android/5895bbf5ef549f8eb150d6ee7046eeaa137604b4/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Dec 28 12:09:47 PST 2014
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.2.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------