├── .gitignore ├── AndroidDemoApp ├── App │ ├── build.gradle │ ├── proguard-rules.txt │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── android │ │ │ └── test │ │ │ ├── AndroidDemoApplication.java │ │ │ ├── activity │ │ │ ├── AbstractActionBarActivity.java │ │ │ ├── DetailActivity.java │ │ │ ├── ResultListActivity.java │ │ │ └── SideBarActivity.java │ │ │ ├── adapter │ │ │ └── VenueRecyclerAdapter.java │ │ │ ├── client │ │ │ ├── FoursquareClient.java │ │ │ └── IFoursquareClient.java │ │ │ ├── dialog │ │ │ ├── DialogFragmentHelper.java │ │ │ └── ProgressDialogFragment.java │ │ │ ├── domain │ │ │ ├── Contact.java │ │ │ ├── Location.java │ │ │ └── Venue.java │ │ │ ├── dto │ │ │ ├── ErrorType.java │ │ │ ├── FoursquareApiErrorDto.java │ │ │ ├── FoursquareDto.java │ │ │ ├── Meta.java │ │ │ ├── Response.java │ │ │ └── VenueDto.java │ │ │ ├── fragment │ │ │ ├── AbstractFragment.java │ │ │ ├── DetailFragment.java │ │ │ ├── MainFragment.java │ │ │ └── ResultListFragment.java │ │ │ ├── location │ │ │ └── GPSTracker.java │ │ │ ├── module │ │ │ └── AndroidDemoModule.java │ │ │ ├── otto │ │ │ ├── OttoBus.java │ │ │ ├── VenueResultEvent.java │ │ │ └── VenueSearchEvent.java │ │ │ ├── qachee │ │ │ └── QacheeData.java │ │ │ ├── session │ │ │ └── SessionManager.java │ │ │ ├── task │ │ │ ├── FoursquareAsyncTask.java │ │ │ ├── SafeAsyncTask.java │ │ │ ├── VenueBackgroundTask.java │ │ │ └── event │ │ │ │ ├── OnApiErrorEvent.java │ │ │ │ ├── OnFinallyEvent.java │ │ │ │ └── OnPreExecuteEvent.java │ │ │ ├── utils │ │ │ └── DataHelper.java │ │ │ └── view │ │ │ ├── ObservableScrollView.java │ │ │ ├── SideBarCallback.java │ │ │ └── VenueItemView.java │ │ └── res │ │ ├── anim │ │ ├── bottom_in.xml │ │ ├── bottom_out.xml │ │ ├── top_in.xml │ │ └── top_out.xml │ │ ├── drawable-hdpi │ │ ├── ic_action_content_save.png │ │ ├── ic_action_navigation_menu.png │ │ ├── ic_action_search.png │ │ └── ic_launcher.png │ │ ├── drawable-mdpi │ │ ├── ic_action_content_save.png │ │ ├── ic_action_navigation_menu.png │ │ ├── ic_action_search.png │ │ └── ic_launcher.png │ │ ├── drawable-xhdpi │ │ ├── ic_action_content_save.png │ │ ├── ic_action_navigation_menu.png │ │ ├── ic_action_search.png │ │ └── ic_launcher.png │ │ ├── drawable-xxhdpi │ │ ├── drawer_shadow.9.png │ │ ├── ic_ab_drawer.png │ │ ├── ic_action_content_save.png │ │ ├── ic_action_navigation_menu.png │ │ ├── ic_action_search.png │ │ └── ic_launcher.png │ │ ├── drawable-xxxhdpi │ │ └── ic_launcher.png │ │ ├── drawable │ │ ├── card_view_background.xml │ │ └── foreground_selector.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_main_overlay.xml │ │ ├── activity_side_bar.xml │ │ ├── fragment_detail.xml │ │ ├── fragment_main.xml │ │ ├── fragment_result_list.xml │ │ ├── toolbar.xml │ │ └── venue_item.xml │ │ ├── menu │ │ └── search_menu.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── README.md ├── android_demo_keystore.jks ├── signing.properties └── web_hi_res_512.png /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | target/ 15 | tmp/ 16 | 17 | # Local configuration file (sdk path, etc) 18 | local.properties 19 | lint.xml 20 | 21 | 22 | # Eclipse project files 23 | .classpath 24 | .project 25 | .metadata/ 26 | .settings/ 27 | 28 | # Android Studio project files 29 | .idea/ 30 | modules.xml 31 | workspace.xml 32 | *.iml 33 | out/ 34 | .gradle 35 | build/ 36 | 37 | # Mac OS file 38 | .DS_Store 39 | 40 | # Windows File 41 | .thumb 42 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | // Load custom properties file signing.properties 4 | Properties props = new Properties() 5 | props.load(new FileInputStream(file(project.property('signing.properties')))) 6 | 7 | repositories { 8 | mavenCentral() 9 | } 10 | 11 | android { 12 | lintOptions { 13 | abortOnError false 14 | } 15 | 16 | compileSdkVersion 22 17 | buildToolsVersion "22.0.1" 18 | 19 | defaultConfig { 20 | minSdkVersion 14 21 | targetSdkVersion 22 22 | versionCode Integer.parseInt(project.VERSION_CODE) 23 | versionName project.VERSION_NAME 24 | } 25 | 26 | signingConfigs { 27 | release { 28 | // If you want to use another file like signing.properties... 29 | storeFile file(props['STORE_FILE']) 30 | storePassword props['STORE_PASSWORD'] 31 | keyAlias props['KEY_ALIAS'] 32 | keyPassword props['KEY_PASSWORD'] 33 | 34 | // If you want to use the default gradle.properties use directly... 35 | // storeFile file(project.STORE_FILE) 36 | // storePassword project.STORE_PASSWORD 37 | // keyAlias project.KEY_ALIAS 38 | // keyPassword project.KEY_PASSWORD 39 | } 40 | } 41 | 42 | buildTypes { 43 | release { 44 | zipAlignEnabled true 45 | debuggable false 46 | signingConfig signingConfigs.release 47 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.txt' 48 | minifyEnabled true 49 | shrinkResources true 50 | } 51 | 52 | // Uncomment to see the results of using Proguard. NOTE: proguard is not needed while develop the app. 53 | // debug { 54 | // proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.txt' 55 | // minifyEnabled true 56 | // shrinkResources true 57 | // } 58 | } 59 | 60 | } 61 | 62 | dependencies { 63 | compile 'com.google.inject:guice:4.0-beta5:no_aop' 64 | 65 | compile 'com.melnykov:floatingactionbutton:1.0.5' 66 | compile 'com.android.support:appcompat-v7:22.1.+' 67 | compile 'com.android.support:recyclerview-v7:22.1.+' 68 | 69 | compile 'com.squareup.picasso:picasso:2.5.2' 70 | compile 'com.squareup.retrofit:retrofit:1.9.0' 71 | compile 'com.squareup.okhttp:okhttp:2.2.0' 72 | compile 'com.squareup.okhttp:okhttp-urlconnection:2.2.0' 73 | compile 'com.squareup.okio:okio:1.1.0' 74 | 75 | compile 'com.google.code.gson:gson:2.3' 76 | compile 'com.squareup:otto:1.3.5' 77 | 78 | compile 'com.github.nicolasjafelle:qachee:1.2' 79 | } 80 | 81 | 82 | android.applicationVariants.all { variant -> 83 | 84 | variant.outputs.each { output -> 85 | def apk = output.outputFile 86 | 87 | def newName 88 | if (variant.buildType.debuggable ) { 89 | newName = 'Android Demo v' + project.VERSION_NAME + '_DEBUG' +'.apk'; 90 | } else { 91 | newName = 'Android Demo v' + project.VERSION_NAME + '_RELEASE' + '.apk'; 92 | } 93 | 94 | //noinspection GroovyAssignabilityCheck 95 | output.outputFile = new File(apk.parentFile, newName) 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/proguard-rules.txt: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/nicolas/android-studio/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the ProGuard 5 | # include property in project.properties. 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 | # This is a configuration file for ProGuard. 19 | # http://proguard.sourceforge.net/index.html#manual/usage.html 20 | 21 | # Optimizations: If you don't want to optimize, use the 22 | # proguard-android.txt configuration file instead of this one, which 23 | # turns off the optimization flags. Adding optimization introduces 24 | # certain risks, since for example not all optimizations performed by 25 | # ProGuard works on all versions of Dalvik. The following flags turn 26 | # off various optimizations known to have issues, but the list may not 27 | # be complete or up to date. (The "arithmetic" optimization can be 28 | # used if you are only targeting Android 2.0 or later.) Make sure you 29 | # test thoroughly if you go this route. 30 | # an example on how to keep an entire package 31 | # -keep class com.google.zxing.** 32 | -ignorewarnings 33 | -renamesourcefileattribute SourceFile 34 | -keepattributes SourceFile,LineNumberTable,*Annotation*,Signature 35 | 36 | 37 | -keep class retrofit.** { *; } 38 | -keep class okhttp.** { *; } 39 | -keep class com.google.gson.** { *; } 40 | -keep class com.squareup.picasso.** { *; } 41 | -keep class android.support.v7.widget.** { *; } 42 | -keep class android.support.v4.** { *; } 43 | -keep class com.melnykov.fab.** { *; } 44 | -keep class javax.inject.** { *; } 45 | -keep class com.android.test.** { *; } 46 | -keep class com.google.inject.** { *; } 47 | -keep class javax.annotation.** { *; } 48 | -keep class * extends com.google.inject.AnnotationDatabase 49 | 50 | -keep interface retrofit.** { *; } 51 | -keep interface okhttp.** { *; } 52 | -keep interface com.google.gson.** { *; } 53 | -keep interface com.squareup.picasso.** { *; } 54 | -keep interface android.support.v7.widget.** { *; } 55 | -keep interface android.support.v4.** { *; } 56 | -keep interface com.melnykov.fab.** { *; } 57 | -keep interface javax.inject.** { *; } 58 | -keep interface com.android.test.** { *; } 59 | -keep interface com.google.inject.** { *; } 60 | -keep interface javax.annotation.** { *; } 61 | 62 | -keepclassmembers class ** { 63 | @com.squareup.otto.Subscribe public *; 64 | @com.squareup.otto.Produce public *; 65 | } 66 | 67 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 13 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 25 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/AndroidDemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.android.test; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | import com.android.test.module.AndroidDemoModule; 7 | import com.google.inject.Guice; 8 | import com.google.inject.Injector; 9 | import com.qachee.ExpirationTime; 10 | import com.qachee.QacheeManager; 11 | 12 | /** 13 | * Created by nicolas on 11/22/14. 14 | */ 15 | public class AndroidDemoApplication extends Application { 16 | 17 | private static AndroidDemoApplication instance; 18 | private static final Injector INJECTOR = Guice.createInjector(new AndroidDemoModule()); 19 | 20 | @Override 21 | public void onCreate() { 22 | super.onCreate(); 23 | instance = this; 24 | 25 | QacheeManager.getInstance().setExpirationTime(ExpirationTime.THIRTY_SECONDS); 26 | } 27 | 28 | public static Context getAppContext() { 29 | return instance; 30 | } 31 | 32 | public static void injectMembers(final Object object) { 33 | INJECTOR.injectMembers(object); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/activity/AbstractActionBarActivity.java: -------------------------------------------------------------------------------- 1 | package com.android.test.activity; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.Fragment; 5 | import android.support.v4.app.FragmentManager; 6 | import android.support.v4.app.FragmentTransaction; 7 | import android.support.v7.app.ActionBarActivity; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.view.View; 10 | import android.widget.FrameLayout; 11 | 12 | 13 | public abstract class AbstractActionBarActivity extends AppCompatActivity { 14 | 15 | private FrameLayout mainLayout; 16 | 17 | @Override 18 | protected void onCreate(Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | // if (savedInstanceState == null) { 21 | setInitialFragment(); 22 | // } 23 | } 24 | 25 | /** 26 | * This method defines which is the initial Fragment. Classes that extends 27 | * this Class should override it and tells the Parent Class which is the 28 | * initial fragment to load. 29 | */ 30 | protected abstract void setInitialFragment(); 31 | 32 | 33 | /** 34 | * This method loads the initial fragment, it should be called inside 35 | * setInitialFragment(). 36 | * 37 | * @param layoutResId - the activity layout 38 | * @param viewId - the Main view id. 39 | * @param fragment - the initial Fragment 40 | */ 41 | protected void setInitialFragment(int layoutResId, int viewId, Fragment fragment) { 42 | setContentView(layoutResId); 43 | mainLayout = (FrameLayout) findViewById(viewId); 44 | setInitialFragment(mainLayout, fragment); 45 | } 46 | 47 | /** 48 | * This method loads the initial fragment, it should be called inside 49 | * setInitialFragment(). 50 | * 51 | * @param viewId - the Main view id. 52 | * @param fragment - the initial Fragment 53 | */ 54 | protected void setInitialFragment(int viewId, Fragment fragment) { 55 | mainLayout = (FrameLayout) findViewById(viewId); 56 | setInitialFragment(mainLayout, fragment); 57 | } 58 | 59 | /** 60 | * This method loads the initial fragment 61 | * 62 | * @param view - the Main View, it is recommended to use a FrameLayout 63 | * @param fragment - the initial fragment 64 | */ 65 | private void setInitialFragment(View view, Fragment fragment) { 66 | if(getCurrentFragment() == null) { 67 | FragmentManager fragmentManager = getSupportFragmentManager(); 68 | FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); 69 | fragmentTransaction.replace(view.getId(), fragment).commit(); 70 | } 71 | } 72 | 73 | /** 74 | * This method replace the existing fragment with a current one. This new 75 | * fragment is added to the back stack. 76 | * 77 | * @param newFragment - The new Fragment that will replace the current visible fragment. 78 | */ 79 | protected void replaceFragment(Fragment newFragment) { 80 | this.replaceFragment(newFragment, true); 81 | } 82 | 83 | /** 84 | * This method replace the existing fragment with a current one. This new 85 | * fragment could be added or not to the back stack. 86 | * 87 | * @param newFragment The new Fragment that will replace the current visible fragment. 88 | * @param addToBackStack True if the new Fragment should be added to the back stack, false otherwise. 89 | */ 90 | protected void replaceFragment(Fragment newFragment, boolean addToBackStack) { 91 | FragmentManager fragmentManager = getSupportFragmentManager(); 92 | FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); 93 | if (addToBackStack) { 94 | fragmentTransaction.addToBackStack(null); 95 | } 96 | fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE); 97 | fragmentTransaction.replace(mainLayout.getId(), newFragment).commit(); 98 | } 99 | 100 | /** 101 | * Returns the current showing fragment or null if no fragment is added. 102 | * @return the fragment previously added or null 103 | */ 104 | protected Fragment getCurrentFragment() { 105 | return getSupportFragmentManager().findFragmentById(mainLayout.getId()); 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/activity/DetailActivity.java: -------------------------------------------------------------------------------- 1 | package com.android.test.activity; 2 | 3 | import android.content.Intent; 4 | import android.graphics.drawable.Drawable; 5 | import android.os.Build; 6 | import android.os.Bundle; 7 | import android.support.annotation.Nullable; 8 | import android.support.v4.app.ActivityCompat; 9 | import android.support.v4.app.ActivityOptionsCompat; 10 | import android.support.v4.app.NavUtils; 11 | import android.support.v4.util.Pair; 12 | import android.support.v7.widget.Toolbar; 13 | import android.view.MenuItem; 14 | import android.view.View; 15 | 16 | import com.android.test.R; 17 | import com.android.test.fragment.DetailFragment; 18 | import com.android.test.view.ObservableScrollView; 19 | import com.android.test.view.VenueItemView; 20 | 21 | 22 | public class DetailActivity extends AbstractActionBarActivity implements DetailFragment.Callback { 23 | 24 | private Toolbar toolbar; 25 | 26 | private Drawable mActionBarBackgroundDrawable; 27 | 28 | @Override 29 | protected void onCreate(Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | 32 | toolbar = (Toolbar) findViewById(R.id.material_toolbar); 33 | 34 | setSupportActionBar(toolbar); 35 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 36 | 37 | mActionBarBackgroundDrawable = getResources().getDrawable(R.color.md_green_500); 38 | mActionBarBackgroundDrawable.setAlpha(0); 39 | 40 | if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) { 41 | mActionBarBackgroundDrawable.setCallback(mDrawableCallback); 42 | } 43 | 44 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { 45 | toolbar.setBackgroundDrawable(mActionBarBackgroundDrawable); 46 | }else { 47 | toolbar.setBackground(mActionBarBackgroundDrawable); 48 | } 49 | 50 | } 51 | 52 | private Drawable.Callback mDrawableCallback = new Drawable.Callback() { 53 | @Override 54 | public void invalidateDrawable(Drawable who) { 55 | toolbar.setBackgroundDrawable(who); 56 | } 57 | 58 | @Override 59 | public void scheduleDrawable(Drawable who, Runnable what, long when) { 60 | } 61 | 62 | @Override 63 | public void unscheduleDrawable(Drawable who, Runnable what) { 64 | } 65 | }; 66 | 67 | @Override 68 | public void onSetupFadingActionBar(ObservableScrollView observableScrollView, final View header) { 69 | observableScrollView.setObservableScrollViewListener(new ObservableScrollView.ObservableScrollViewListener() { 70 | @Override 71 | public void onScrollChanged(int l, int t, int oldl, int oldt) { 72 | final int headerHeight = (header.getHeight() - toolbar.getHeight()); 73 | final float ratio = (float) Math.min(Math.max(t, 0), headerHeight) / headerHeight; 74 | final int newAlpha = (int) (ratio * 255); 75 | mActionBarBackgroundDrawable.setAlpha(newAlpha); 76 | } 77 | }); 78 | } 79 | 80 | 81 | 82 | @Nullable 83 | @Override 84 | public Intent getParentActivityIntent() { 85 | return super.getParentActivityIntent(); 86 | } 87 | 88 | @Override 89 | protected void setInitialFragment() { 90 | 91 | String url = null; 92 | String name = null; 93 | String distance = null; 94 | 95 | if(getIntent().getExtras() != null) { 96 | Bundle bundle = getIntent().getExtras(); 97 | url = bundle.getString(DetailFragment.ANIMATED_IMAGE); 98 | name = bundle.getString(DetailFragment.ANIMATED_NAME); 99 | distance = bundle.getString(DetailFragment.ANIMATED_DISTANCE); 100 | } 101 | 102 | setInitialFragment(R.layout.activity_main_overlay, R.id.container, DetailFragment.newInstance(url, name, distance)); 103 | } 104 | 105 | @Override 106 | public boolean onOptionsItemSelected(MenuItem item) { 107 | 108 | switch (item.getItemId()) { 109 | case android.R.id.home: 110 | NavUtils.navigateUpTo(this, new Intent(this, ResultListActivity.class)); 111 | animate(); 112 | } 113 | 114 | return super.onOptionsItemSelected(item); 115 | } 116 | 117 | @Override 118 | public void onBackPressed() { 119 | super.onBackPressed(); 120 | animate(); 121 | } 122 | 123 | public static void startActivity(AbstractActionBarActivity activity, View transitionView, String url) { 124 | ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, 125 | Pair.create(transitionView, DetailFragment.ANIMATED_IMAGE), 126 | Pair.create(transitionView, DetailFragment.ANIMATED_NAME), 127 | Pair.create(transitionView, DetailFragment.ANIMATED_DISTANCE)); 128 | 129 | Intent intent = new Intent(activity, DetailActivity.class); 130 | intent.putExtra(DetailFragment.ANIMATED_IMAGE, url); 131 | intent.putExtra(DetailFragment.ANIMATED_NAME, ((VenueItemView)transitionView).getNameText() ); 132 | intent.putExtra(DetailFragment.ANIMATED_DISTANCE, ((VenueItemView)transitionView).getDistanceText() ); 133 | 134 | ActivityCompat.startActivity(activity, intent, options.toBundle()); 135 | animate(activity); 136 | } 137 | 138 | private static void animate(AbstractActionBarActivity activity) { 139 | if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { 140 | activity.overridePendingTransition(R.anim.bottom_in, R.anim.top_out); 141 | } 142 | 143 | } 144 | 145 | private void animate() { 146 | if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { 147 | overridePendingTransition(R.anim.top_in, R.anim.bottom_out); 148 | } 149 | 150 | } 151 | 152 | 153 | 154 | } 155 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/activity/ResultListActivity.java: -------------------------------------------------------------------------------- 1 | package com.android.test.activity; 2 | 3 | import android.content.Intent; 4 | import android.location.Location; 5 | import android.os.Bundle; 6 | import android.support.v4.app.NavUtils; 7 | import android.support.v7.widget.Toolbar; 8 | import android.view.MenuItem; 9 | import android.view.View; 10 | import android.view.animation.AccelerateDecelerateInterpolator; 11 | import android.view.animation.Interpolator; 12 | 13 | import com.android.test.R; 14 | import com.android.test.domain.Venue; 15 | import com.android.test.fragment.ResultListFragment; 16 | import com.android.test.utils.DataHelper; 17 | 18 | 19 | public class ResultListActivity extends AbstractActionBarActivity implements ResultListFragment.Callback { 20 | 21 | private final int ANIM_DURATION = 200; 22 | 23 | private Toolbar toolbar; 24 | 25 | private Interpolator mInterpolator; 26 | 27 | @Override 28 | protected void onCreate(Bundle savedInstanceState) { 29 | super.onCreate(savedInstanceState); 30 | 31 | toolbar = (Toolbar) findViewById(R.id.material_toolbar); 32 | mInterpolator = new AccelerateDecelerateInterpolator(); 33 | 34 | setSupportActionBar(toolbar); 35 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 36 | } 37 | 38 | @Override 39 | protected void setInitialFragment() { 40 | 41 | DataHelper dataHelper = null; 42 | String place = null; 43 | Location location = null; 44 | 45 | if(getIntent().getExtras() != null) { 46 | Bundle bundle = getIntent().getExtras(); 47 | dataHelper = (DataHelper) bundle.getSerializable(ResultListFragment.DATA_HELPER); 48 | place = bundle.getString(ResultListFragment.PLACE); 49 | location = bundle.getParcelable(ResultListFragment.LOCATION); 50 | } 51 | 52 | setInitialFragment(R.layout.activity_main_overlay, R.id.container, ResultListFragment.newInstance(dataHelper, place, location)); 53 | } 54 | 55 | @Override 56 | public boolean onOptionsItemSelected(MenuItem item) { 57 | 58 | switch (item.getItemId()) { 59 | case android.R.id.home: 60 | NavUtils.navigateUpTo(this, new Intent(this, SideBarActivity.class)); 61 | } 62 | 63 | return super.onOptionsItemSelected(item); 64 | } 65 | 66 | 67 | @Override 68 | public void onItemClick(Venue venue, View view, String url) { 69 | DetailActivity.startActivity(this, view, url); 70 | } 71 | 72 | @Override 73 | public void onToolbarHide() { 74 | toolbar.animate().setInterpolator(mInterpolator) 75 | .setDuration(ANIM_DURATION) 76 | .translationY(-toolbar.getHeight()); 77 | } 78 | 79 | @Override 80 | public void onToolbarShow() { 81 | toolbar.animate().setInterpolator(mInterpolator) 82 | .setDuration(ANIM_DURATION) 83 | .translationY(0); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/activity/SideBarActivity.java: -------------------------------------------------------------------------------- 1 | package com.android.test.activity; 2 | 3 | import android.content.Intent; 4 | import android.content.res.Configuration; 5 | import android.location.Location; 6 | import android.os.Bundle; 7 | import android.support.v4.widget.DrawerLayout; 8 | import android.support.v7.app.ActionBarDrawerToggle; 9 | import android.support.v7.widget.Toolbar; 10 | import android.view.Gravity; 11 | import android.view.MenuItem; 12 | import android.view.View; 13 | import android.widget.AdapterView; 14 | import android.widget.ArrayAdapter; 15 | import android.widget.ListView; 16 | 17 | import com.android.test.R; 18 | import com.android.test.domain.Venue; 19 | import com.android.test.fragment.MainFragment; 20 | import com.android.test.fragment.ResultListFragment; 21 | import com.android.test.utils.DataHelper; 22 | import com.android.test.view.SideBarCallback; 23 | 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | import java.util.Set; 27 | 28 | 29 | public class SideBarActivity extends AbstractActionBarActivity implements MainFragment.Callback, AdapterView.OnItemClickListener { 30 | 31 | private Toolbar toolbar; 32 | 33 | private DrawerLayout drawerLayout; 34 | 35 | private ListView listView; 36 | 37 | private ActionBarDrawerToggle drawerToggle; 38 | private SideBarCallback sideBarCallback; 39 | 40 | @Override 41 | protected void onCreate(Bundle savedInstanceState) { 42 | super.onCreate(savedInstanceState); 43 | 44 | toolbar = (Toolbar) findViewById(R.id.material_toolbar); 45 | drawerLayout = (DrawerLayout) findViewById(R.id.activity_main_drawer_layout); 46 | listView = (ListView) findViewById(R.id.activity_side_bar_list_view); 47 | 48 | setSupportActionBar(toolbar); 49 | getSupportActionBar().setHomeButtonEnabled(true); 50 | toolbar.setNavigationIcon(R.drawable.ic_action_navigation_menu); 51 | setupSideBar(); 52 | } 53 | 54 | @Override 55 | protected void setInitialFragment() { 56 | setInitialFragment(R.layout.activity_side_bar, R.id.container, MainFragment.newInstance()); 57 | } 58 | 59 | // @Override 60 | // public boolean onCreateOptionsMenu(Menu menu) { 61 | // getMenuInflater().inflate(R.menu.search_menu, menu); 62 | // return true; 63 | // } 64 | 65 | private void setupSideBar() { 66 | if (toolbar != null) { 67 | drawerLayout.setDrawerShadow(R.drawable.drawer_shadow, Gravity.START); 68 | toolbar.setNavigationOnClickListener(new View.OnClickListener() { 69 | @Override 70 | public void onClick(View view) { 71 | if (drawerLayout.isDrawerOpen(Gravity.START)) { 72 | drawerLayout.closeDrawer(Gravity.START); 73 | } else { 74 | drawerLayout.openDrawer(Gravity.START); 75 | } 76 | } 77 | }); 78 | } 79 | 80 | 81 | drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.drawer_open, R.string.drawer_close) { 82 | /** Called when a drawer has settled in a completely closed state. */ 83 | public void onDrawerClosed(View view) { 84 | supportInvalidateOptionsMenu(); 85 | } 86 | 87 | /** Called when a drawer has settled in a completely open state. */ 88 | public void onDrawerOpened(View drawerView) { 89 | supportInvalidateOptionsMenu(); // creates call to onPrepareOptionsMenu() 90 | } 91 | }; 92 | 93 | drawerLayout.setDrawerListener(drawerToggle); 94 | 95 | } 96 | 97 | @Override 98 | public void onBackPressed() { 99 | if(drawerLayout.isDrawerOpen(Gravity.START)) { 100 | drawerLayout.closeDrawer(Gravity.START); 101 | }else { 102 | super.onBackPressed(); 103 | } 104 | } 105 | 106 | @Override 107 | public boolean onOptionsItemSelected(MenuItem item) { 108 | if (drawerToggle.onOptionsItemSelected(item)) { 109 | return true; 110 | } 111 | 112 | return super.onOptionsItemSelected(item); 113 | } 114 | 115 | 116 | @Override 117 | protected void onPostCreate(Bundle savedInstanceState) { 118 | super.onPostCreate(savedInstanceState); 119 | drawerToggle.syncState(); 120 | } 121 | 122 | 123 | @Override 124 | public void onConfigurationChanged(Configuration newConfig) { 125 | super.onConfigurationChanged(newConfig); 126 | drawerToggle.onConfigurationChanged(newConfig); 127 | 128 | } 129 | 130 | @Override 131 | public void onResult(List venues, Location currentLocation, String place) { 132 | // ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(this, transitionView, EXTRA_IMAGE); 133 | // Intent intent = new Intent(this, ResultListActivity.class); 134 | // intent.putExtra(EXTRA_IMAGE, url); 135 | // ActivityCompat.startActivity(activity, intent, options.toBundle()); 136 | 137 | Intent intent = new Intent(this, ResultListActivity.class); 138 | Bundle bundle = new Bundle(); 139 | bundle.putSerializable(ResultListFragment.DATA_HELPER, new DataHelper(venues)); 140 | bundle.putString(ResultListFragment.PLACE, place); 141 | 142 | if(currentLocation != null) { 143 | bundle.putParcelable(ResultListFragment.LOCATION, currentLocation); 144 | } 145 | 146 | intent.putExtras(bundle); 147 | startActivity(intent); 148 | } 149 | 150 | @Override 151 | public void loadSavedPlaces(Set savedPlaces, SideBarCallback sideBarCallback) { 152 | this.sideBarCallback = sideBarCallback; 153 | listView.setOnItemClickListener(this); 154 | List list = new ArrayList(savedPlaces); 155 | ArrayAdapter savedPlacesAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, android.R.id.text1, list); 156 | listView.setAdapter(savedPlacesAdapter); 157 | } 158 | 159 | @Override 160 | public void onItemClick(AdapterView parent, View view, int position, long id) { 161 | String place = (String) parent.getItemAtPosition(position); 162 | this.sideBarCallback.onSideBarItemClick(place); 163 | drawerLayout.closeDrawer(Gravity.START); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/adapter/VenueRecyclerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.android.test.adapter; 2 | 3 | import android.location.Location; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | 8 | import com.android.test.domain.Venue; 9 | import com.android.test.view.VenueItemView; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * Created by Nicolas Jafelle on 10/28/14. 15 | */ 16 | public class VenueRecyclerAdapter extends RecyclerView.Adapter { 17 | 18 | private enum ViewTypes { 19 | FIRST_VIEW, 20 | REST_VIEW; 21 | } 22 | 23 | public interface RecyclerViewListener { 24 | void onItemClickListener(View view , int position); 25 | 26 | } 27 | 28 | private List venues; 29 | private Location currentLocation; 30 | private RecyclerViewListener recyclerViewListener; 31 | 32 | public VenueRecyclerAdapter(List venues, Location currentLocation, RecyclerViewListener recyclerViewListener) { 33 | this.venues = venues; 34 | this.currentLocation = currentLocation; 35 | this.recyclerViewListener = recyclerViewListener; 36 | } 37 | 38 | @Override 39 | public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { 40 | 41 | // create a new view 42 | VenueItemView view = new VenueItemView(viewGroup.getContext()); 43 | // set the view's size, margins, paddings and layout parameters 44 | if(viewType == ViewTypes.FIRST_VIEW.ordinal()) { 45 | view.setFirst(); 46 | } 47 | 48 | return new ViewHolder(view); 49 | } 50 | 51 | @Override 52 | public void onBindViewHolder(ViewHolder viewHolder, int position) { 53 | final Venue rowData = venues.get(position); 54 | viewHolder.venueItemView.fillData(rowData, currentLocation); 55 | } 56 | 57 | @Override 58 | public int getItemCount() { 59 | return venues.size(); 60 | } 61 | 62 | @Override 63 | public int getItemViewType(int position) { 64 | if(position == 0) { 65 | return ViewTypes.FIRST_VIEW.ordinal(); 66 | }else { 67 | return ViewTypes.REST_VIEW.ordinal(); 68 | } 69 | 70 | } 71 | 72 | public Venue getItemAtPosition(int position) { 73 | return venues.get(position); 74 | } 75 | 76 | 77 | public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 78 | 79 | private final VenueItemView venueItemView; 80 | 81 | public ViewHolder(VenueItemView itemView) { 82 | super(itemView); 83 | venueItemView = itemView; 84 | venueItemView.setOnClickListener(this); 85 | } 86 | 87 | @Override 88 | public void onClick(View v) { 89 | recyclerViewListener.onItemClickListener(v, getPosition()); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/client/FoursquareClient.java: -------------------------------------------------------------------------------- 1 | package com.android.test.client; 2 | 3 | import com.android.test.dto.VenueDto; 4 | import com.google.inject.Singleton; 5 | import com.squareup.okhttp.OkHttpClient; 6 | 7 | import retrofit.RestAdapter; 8 | import retrofit.client.OkClient; 9 | 10 | /** 11 | * This class is the responsable to connect to the API. Here lies all the 12 | * connections between the client and the server.
13 | * As Jake Wharton said: 14 | * "RestAdapter and the created instances should be treated as singletons" 15 | * @author nicolasjafelle 16 | */ 17 | @Singleton 18 | public class FoursquareClient { 19 | 20 | private static final String BASE_URL = "https://api.foursquare.com/v2"; 21 | 22 | private RestAdapter restAdapter; 23 | private IFoursquareClient foursquareClient; 24 | 25 | public FoursquareClient() { 26 | if (restAdapter == null || foursquareClient == null) { 27 | 28 | OkHttpClient okHttpClient = new OkHttpClient(); 29 | 30 | restAdapter = new RestAdapter.Builder() 31 | .setEndpoint(BASE_URL) 32 | .setClient(new OkClient(okHttpClient)) 33 | .setLogLevel(RestAdapter.LogLevel.FULL) 34 | .build(); 35 | 36 | foursquareClient = restAdapter.create(IFoursquareClient.class); 37 | } 38 | } 39 | 40 | 41 | public VenueDto searchForVenues(String criteria) { 42 | String token = "HDQVML4JWDJNHHKCSRPN0MPYUFEMJJEXH3C34FTCPYUOR5OP"; 43 | long v = 20130417; 44 | 45 | return foursquareClient.searchForVenues(criteria, token, v); 46 | } 47 | 48 | 49 | } 50 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/client/IFoursquareClient.java: -------------------------------------------------------------------------------- 1 | package com.android.test.client; 2 | 3 | import com.android.test.dto.VenueDto; 4 | 5 | import retrofit.http.GET; 6 | import retrofit.http.Query; 7 | 8 | /** 9 | * Created by nicolas on 12/22/13. 10 | */ 11 | public interface IFoursquareClient { 12 | 13 | @GET("/venues/search") 14 | VenueDto searchForVenues(@Query("near") String place, @Query("oauth_token") String token, @Query("v") long v); 15 | } 16 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/dialog/DialogFragmentHelper.java: -------------------------------------------------------------------------------- 1 | package com.android.test.dialog; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.support.v4.app.DialogFragment; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v4.app.FragmentActivity; 8 | import android.support.v4.app.FragmentManager; 9 | import android.support.v4.app.FragmentTransaction; 10 | 11 | /** 12 | * Dialog fragment helper 13 | */ 14 | public abstract class DialogFragmentHelper extends DialogFragment { 15 | 16 | private static final String TAG = "dialog_fragment_helper"; 17 | 18 | /** 19 | * Show dialog 20 | */ 21 | public static void show(FragmentActivity activity, DialogFragment fragment, Bundle arguments) { 22 | FragmentManager manager = activity.getSupportFragmentManager(); 23 | FragmentTransaction transaction = manager.beginTransaction(); 24 | Fragment current = manager.findFragmentByTag(TAG); 25 | if (current != null) { 26 | transaction.remove(current); 27 | //transaction.addToBackStack(null); 28 | transaction.commit(); 29 | } 30 | if(fragment.getArguments() == null) { 31 | fragment.setArguments(arguments); 32 | }else { 33 | fragment.getArguments().putAll(arguments); 34 | } 35 | 36 | fragment.show(manager, TAG); 37 | } 38 | 39 | /** 40 | * Dismiss current DialogFragment 41 | * @param activity 42 | */ 43 | public static void dismissDialog(FragmentActivity activity){ 44 | Fragment prev = activity.getSupportFragmentManager().findFragmentByTag(TAG); 45 | if (prev != null) { 46 | DialogFragment df = (DialogFragment) prev; 47 | df.dismissAllowingStateLoss(); 48 | } 49 | } 50 | 51 | /** 52 | * Dismiss current DialogFragment 53 | * @param context 54 | */ 55 | public static void dismissDialog(Context context){ 56 | Fragment prev = ((FragmentActivity)context).getSupportFragmentManager().findFragmentByTag(TAG); 57 | if (prev != null) { 58 | DialogFragment df = (DialogFragment) prev; 59 | df.dismissAllowingStateLoss(); 60 | } 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/dialog/ProgressDialogFragment.java: -------------------------------------------------------------------------------- 1 | package com.android.test.dialog; 2 | 3 | import android.app.Dialog; 4 | import android.app.ProgressDialog; 5 | import android.content.DialogInterface; 6 | import android.os.Bundle; 7 | import android.support.v4.app.DialogFragment; 8 | import android.view.View; 9 | 10 | 11 | 12 | /** 13 | * This Dialog is just an indeterminet progress, typically used for Background Tasks. 14 | */ 15 | public class ProgressDialogFragment extends DialogFragment { 16 | 17 | /** 18 | * Callback used when the user cancel or press the back button to cancel the task. 19 | */ 20 | public interface ProgressDialogFragmentListener { 21 | void onCancel(); 22 | } 23 | 24 | public static ProgressDialogFragment newInstance() { 25 | return new ProgressDialogFragment(); 26 | } 27 | 28 | public static final String MESSAGE = "message"; 29 | private ProgressDialogFragmentListener listener; 30 | 31 | public void setProgressDialogFragmentListener(ProgressDialogFragmentListener listener) { 32 | this.listener = listener; 33 | } 34 | 35 | @Override 36 | public void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | setRetainInstance(true); 39 | } 40 | 41 | @Override 42 | public void onViewCreated(View view, Bundle savedInstanceState) { 43 | super.onViewCreated(view, savedInstanceState); 44 | } 45 | 46 | 47 | @Override 48 | public Dialog onCreateDialog(Bundle savedInstanceState) { 49 | final ProgressDialog dialog = new ProgressDialog(getActivity()); 50 | 51 | dialog.setMessage(getArguments().getString(MESSAGE)); 52 | dialog.setIndeterminate(true); 53 | dialog.setCancelable(true); 54 | dialog.setCanceledOnTouchOutside(false); 55 | return dialog; 56 | } 57 | 58 | public boolean isShowing() { 59 | boolean isShowing = false; 60 | if(getDialog() != null) { 61 | isShowing = getDialog().isShowing(); 62 | } 63 | return isShowing; 64 | } 65 | 66 | /** 67 | * Workaround for an open bug in DialogFragment. 68 | * https://code.google.com/p/android/issues/detail?id=17423 69 | */ 70 | @Override 71 | public void onDestroyView() { 72 | if(getDialog() != null && getRetainInstance()) { 73 | getDialog().setDismissMessage(null); 74 | } 75 | super.onDestroyView(); 76 | } 77 | 78 | public void setMessage(int resId) { 79 | ((ProgressDialog)getDialog()).setMessage(getString(resId)); 80 | } 81 | 82 | @Override 83 | public void onCancel(DialogInterface dialog) { 84 | super.onCancel(dialog); 85 | if (listener != null) { 86 | listener.onCancel(); 87 | } 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/domain/Contact.java: -------------------------------------------------------------------------------- 1 | package com.android.test.domain; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Created by nicolas on 12/22/13. 7 | */ 8 | public class Contact implements Serializable { 9 | 10 | private static final long serialVersionUID = 1L; 11 | 12 | private String formattedPhone; 13 | 14 | public String getFormattedPhone() { 15 | return formattedPhone; 16 | } 17 | 18 | public void setFormattedPhone(String formattedPhone) { 19 | this.formattedPhone = formattedPhone; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/domain/Location.java: -------------------------------------------------------------------------------- 1 | package com.android.test.domain; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Created by nicolas on 12/22/13. 7 | */ 8 | public class Location implements Serializable { 9 | 10 | private static final long serialVersionUID = 1L; 11 | 12 | private String address; 13 | private double lat; 14 | private double lng; 15 | private String postalCode; 16 | private String cc; 17 | private String city; 18 | private String state; 19 | private String country; 20 | 21 | public String getAddress() { 22 | return address; 23 | } 24 | 25 | public void setAddress(String address) { 26 | this.address = address; 27 | } 28 | 29 | public double getLat() { 30 | return lat; 31 | } 32 | 33 | public void setLat(double lat) { 34 | this.lat = lat; 35 | } 36 | 37 | public double getLng() { 38 | return lng; 39 | } 40 | 41 | public void setLng(double lng) { 42 | this.lng = lng; 43 | } 44 | 45 | public String getPostalCode() { 46 | return postalCode; 47 | } 48 | 49 | public void setPostalCode(String postalCode) { 50 | this.postalCode = postalCode; 51 | } 52 | 53 | public String getCc() { 54 | return cc; 55 | } 56 | 57 | public void setCc(String cc) { 58 | this.cc = cc; 59 | } 60 | 61 | public String getCity() { 62 | return city; 63 | } 64 | 65 | public void setCity(String city) { 66 | this.city = city; 67 | } 68 | 69 | public String getState() { 70 | return state; 71 | } 72 | 73 | public void setState(String state) { 74 | this.state = state; 75 | } 76 | 77 | public String getCountry() { 78 | return country; 79 | } 80 | 81 | public void setCountry(String country) { 82 | this.country = country; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/domain/Venue.java: -------------------------------------------------------------------------------- 1 | package com.android.test.domain; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Created by nicolas on 12/22/13. 7 | */ 8 | public class Venue implements Serializable { 9 | 10 | private static final long serialVersionUID = 1L; 11 | 12 | private String id; 13 | private String name; 14 | private Location location; 15 | private Contact contact; 16 | // for this test we don't need additional attributes like categories and stats. 17 | 18 | public String getId() { 19 | return id; 20 | } 21 | 22 | public void setId(String id) { 23 | this.id = id; 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | public void setName(String name) { 31 | this.name = name; 32 | } 33 | 34 | public Location getLocation() { 35 | return location; 36 | } 37 | 38 | public void setLocation(Location location) { 39 | this.location = location; 40 | } 41 | 42 | public Contact getContact() { 43 | return contact; 44 | } 45 | 46 | public void setContact(Contact contact) { 47 | this.contact = contact; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/dto/ErrorType.java: -------------------------------------------------------------------------------- 1 | package com.android.test.dto; 2 | 3 | /** 4 | * Created by nicolas on 12/22/13. 5 | */ 6 | public enum ErrorType { 7 | 8 | failed_geocode; 9 | //TODO add more error types 10 | } 11 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/dto/FoursquareApiErrorDto.java: -------------------------------------------------------------------------------- 1 | package com.android.test.dto; 2 | 3 | /** 4 | * Created by nicolas on 12/22/13. 5 | */ 6 | public class FoursquareApiErrorDto { 7 | 8 | private Meta meta; 9 | 10 | public Meta getMeta() { 11 | return meta; 12 | } 13 | 14 | public void setMeta(Meta meta) { 15 | this.meta = meta; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/dto/FoursquareDto.java: -------------------------------------------------------------------------------- 1 | package com.android.test.dto; 2 | 3 | 4 | /** 5 | * FoursquareDto 6 | * Created by nicolas on 12/22/13. 7 | */ 8 | public abstract class FoursquareDto { 9 | 10 | private Meta meta; 11 | private Response response; 12 | // for this test we don't need additional attributes like confident or geocode. 13 | 14 | 15 | public Meta getMeta() { 16 | return meta; 17 | } 18 | 19 | public void setMeta(Meta meta) { 20 | this.meta = meta; 21 | } 22 | 23 | public Response getResponse() { 24 | return response; 25 | } 26 | 27 | public void setResponse(Response response) { 28 | this.response = response; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/dto/Meta.java: -------------------------------------------------------------------------------- 1 | package com.android.test.dto; 2 | 3 | /** 4 | * Created by nicolas on 12/22/13. 5 | */ 6 | public class Meta { 7 | 8 | private int code; 9 | private ErrorType errorType; 10 | 11 | public int getCode() { 12 | return code; 13 | } 14 | 15 | public void setCode(int code) { 16 | this.code = code; 17 | } 18 | 19 | public ErrorType getErrorType() { 20 | return errorType; 21 | } 22 | 23 | public void setErrorType(ErrorType errorType) { 24 | this.errorType = errorType; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/dto/Response.java: -------------------------------------------------------------------------------- 1 | package com.android.test.dto; 2 | 3 | import com.android.test.domain.Venue; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * Created by nicolas on 12/22/13. 9 | */ 10 | public class Response { 11 | 12 | private List venues; 13 | 14 | public List getVenues() { 15 | return venues; 16 | } 17 | 18 | public void setVenues(List venues) { 19 | this.venues = venues; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/dto/VenueDto.java: -------------------------------------------------------------------------------- 1 | package com.android.test.dto; 2 | 3 | /** 4 | * Created by nicolas on 12/22/13. 5 | */ 6 | public class VenueDto extends FoursquareDto { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/fragment/AbstractFragment.java: -------------------------------------------------------------------------------- 1 | package com.android.test.fragment; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.support.v4.app.Fragment; 6 | import android.support.v7.app.ActionBarActivity; 7 | 8 | import com.android.test.AndroidDemoApplication; 9 | 10 | 11 | public abstract class AbstractFragment extends Fragment { 12 | 13 | protected T callbacks; 14 | 15 | @Override 16 | public void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | setHasOptionsMenu(true); 19 | AndroidDemoApplication.injectMembers(this); 20 | // setRetainInstance(true); 21 | } 22 | 23 | @Override 24 | public void onAttach(Activity activity) { 25 | super.onAttach(activity); 26 | try { 27 | callbacks = (T) activity; 28 | } catch (ClassCastException e) { 29 | throw new ClassCastException(activity.toString() + " must implement Callback interface"); 30 | } 31 | } 32 | 33 | 34 | @Override 35 | public void onDetach() { 36 | super.onDetach(); 37 | this.callbacks = null; 38 | } 39 | 40 | 41 | protected ActionBarActivity getActionBarActivity() { 42 | return (ActionBarActivity) getActivity(); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/fragment/DetailFragment.java: -------------------------------------------------------------------------------- 1 | package com.android.test.fragment; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.Fragment; 5 | import android.support.v4.view.ViewCompat; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.ImageView; 10 | import android.widget.TextView; 11 | 12 | import com.android.test.R; 13 | import com.android.test.view.ObservableScrollView; 14 | import com.squareup.picasso.Picasso; 15 | 16 | 17 | public class DetailFragment extends AbstractFragment { 18 | 19 | public static final String ANIMATED_IMAGE = "image"; 20 | public static final String ANIMATED_NAME = "name"; 21 | public static final String ANIMATED_DISTANCE = "distance"; 22 | 23 | public interface Callback { 24 | void onSetupFadingActionBar(ObservableScrollView observableScrollView, final View header); 25 | } 26 | 27 | private ObservableScrollView scrollview; 28 | 29 | private ImageView imageView; 30 | 31 | private TextView nameView; 32 | 33 | private TextView distanceView; 34 | 35 | private String url; 36 | 37 | private String name; 38 | 39 | private String distance; 40 | 41 | 42 | public static Fragment newInstance(String url, String name, String distance) { 43 | Bundle args = new Bundle(); 44 | args.putString(DetailFragment.ANIMATED_IMAGE, url); 45 | args.putString(DetailFragment.ANIMATED_NAME, name); 46 | args.putString(DetailFragment.ANIMATED_DISTANCE, distance); 47 | 48 | Fragment f = new DetailFragment(); 49 | f.setArguments(args); 50 | return f; 51 | } 52 | 53 | @Override 54 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 55 | View view = inflater.inflate(R.layout.fragment_detail, container, false); 56 | 57 | scrollview = (ObservableScrollView) view.findViewById(R.id.fragment_detail_observable_scroll_view); 58 | imageView = (ImageView) view.findViewById(R.id.fragment_detail_image_view); 59 | nameView = (TextView) view.findViewById(R.id.fragment_detail_name); 60 | distanceView = (TextView) view.findViewById(R.id.fragment_detail_distance); 61 | 62 | return view; 63 | } 64 | 65 | @Override 66 | public void onViewCreated(View view, Bundle savedInstanceState) { 67 | super.onViewCreated(view, savedInstanceState); 68 | 69 | if(getArguments() != null) { 70 | url = getArguments().getString(ANIMATED_IMAGE); 71 | name = getArguments().getString(ANIMATED_NAME); 72 | distance = getArguments().getString(ANIMATED_DISTANCE); 73 | } 74 | 75 | 76 | callbacks.onSetupFadingActionBar(scrollview, imageView); 77 | 78 | ViewCompat.setTransitionName(imageView, ANIMATED_IMAGE); 79 | ViewCompat.setTransitionName(nameView, ANIMATED_NAME); 80 | ViewCompat.setTransitionName(distanceView, ANIMATED_DISTANCE); 81 | 82 | Picasso.with(getActivity()) 83 | .load(url) 84 | .into(imageView); 85 | 86 | nameView.setText(name); 87 | distanceView.setText(distance); 88 | 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/fragment/MainFragment.java: -------------------------------------------------------------------------------- 1 | package com.android.test.fragment; 2 | 3 | import android.content.Context; 4 | import android.location.Location; 5 | import android.os.Bundle; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v7.widget.AppCompatButton; 8 | import android.support.v7.widget.AppCompatEditText; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | import android.view.inputmethod.InputMethodManager; 13 | import android.widget.Button; 14 | import android.widget.EditText; 15 | import android.widget.Toast; 16 | 17 | import com.android.test.AndroidDemoApplication; 18 | import com.android.test.R; 19 | import com.android.test.client.FoursquareClient; 20 | import com.android.test.dialog.DialogFragmentHelper; 21 | import com.android.test.dialog.ProgressDialogFragment; 22 | import com.android.test.domain.Venue; 23 | import com.android.test.dto.ErrorType; 24 | import com.android.test.dto.FoursquareApiErrorDto; 25 | import com.android.test.location.GPSTracker; 26 | import com.android.test.otto.OttoBus; 27 | import com.android.test.otto.VenueResultEvent; 28 | import com.android.test.otto.VenueSearchEvent; 29 | import com.android.test.qachee.QacheeData; 30 | import com.android.test.session.SessionManager; 31 | import com.android.test.task.FoursquareAsyncTask; 32 | import com.android.test.task.VenueBackgroundTask; 33 | import com.android.test.task.event.OnApiErrorEvent; 34 | import com.android.test.task.event.OnFinallyEvent; 35 | import com.android.test.task.event.OnPreExecuteEvent; 36 | import com.android.test.view.SideBarCallback; 37 | import com.qachee.QacheeManager; 38 | import com.squareup.otto.Subscribe; 39 | 40 | import java.util.List; 41 | import java.util.Set; 42 | 43 | import javax.inject.Inject; 44 | 45 | /** 46 | * MainFragment 47 | * Created by nicolas on 12/22/13. 48 | */ 49 | public class MainFragment extends AbstractFragment 50 | implements SideBarCallback, ProgressDialogFragment.ProgressDialogFragmentListener { 51 | 52 | 53 | 54 | 55 | public interface Callback { 56 | void onResult(List venues, Location currentLocation, String place); 57 | void loadSavedPlaces(Set savedPlaces, SideBarCallback sideBarCallback); 58 | } 59 | 60 | private AppCompatEditText editText; 61 | 62 | private AppCompatButton searchButton; 63 | 64 | private FoursquareAsyncTask asyncTask; 65 | 66 | @Inject 67 | private SessionManager sessionManager; 68 | 69 | @Inject 70 | private FoursquareClient foursquareClient; 71 | 72 | @Inject 73 | private OttoBus ottoBus; 74 | 75 | private ProgressDialogFragment progressDialog; 76 | 77 | @Inject 78 | private GPSTracker gpsTracker; 79 | 80 | @Override 81 | public void onCreate(Bundle savedInstanceState) { 82 | super.onCreate(savedInstanceState); 83 | } 84 | 85 | public static Fragment newInstance() { 86 | return new MainFragment(); 87 | } 88 | 89 | @Override 90 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 91 | View view = inflater.inflate(R.layout.fragment_main, container, false); 92 | 93 | editText = (AppCompatEditText) view.findViewById(R.id.fragment_main_edittext); 94 | searchButton = (AppCompatButton) view.findViewById(R.id.fragment_main_button); 95 | 96 | return view; 97 | } 98 | 99 | @Override 100 | public void onViewCreated(View view, Bundle savedInstanceState) { 101 | super.onViewCreated(view, savedInstanceState); 102 | 103 | progressDialog = ProgressDialogFragment.newInstance(); 104 | 105 | searchButton.setOnClickListener(onClickListener); 106 | } 107 | 108 | @Override 109 | public void onResume() { 110 | super.onResume(); 111 | loadSavedPlaces(); 112 | } 113 | 114 | private void createProgressDialog(int resId) { 115 | Bundle arguments = new Bundle(); 116 | progressDialog = ProgressDialogFragment.newInstance(); 117 | progressDialog.setProgressDialogFragmentListener(this); 118 | arguments.putString(ProgressDialogFragment.MESSAGE, getString(resId)); 119 | DialogFragmentHelper.show(getActivity(), progressDialog, arguments); 120 | } 121 | 122 | private View.OnClickListener onClickListener = new View.OnClickListener() { 123 | @Override 124 | public void onClick(View v) { 125 | String place = editText.getText().toString().trim(); 126 | 127 | if(place == null || place.length() == 0) { 128 | Toast.makeText(getActivity(), R.string.edit_text_empty, Toast.LENGTH_SHORT).show(); 129 | }else { 130 | // check if GPS enabled 131 | if(gpsTracker.canGetLocation()){ 132 | postVenueSearchEvent(place); 133 | }else{ 134 | gpsTracker.showSettingsAlert(); 135 | } 136 | } 137 | } 138 | }; 139 | 140 | 141 | @Override 142 | public void onSideBarItemClick(String text) { 143 | if(gpsTracker.canGetLocation()){ 144 | postVenueSearchEvent(text); 145 | }else{ 146 | gpsTracker.showSettingsAlert(); 147 | } 148 | } 149 | 150 | private void postVenueSearchEvent(String text) { 151 | ottoBus.post(new VenueSearchEvent(text)); 152 | } 153 | 154 | private void loadSavedPlaces() { 155 | Set savedPlaces = sessionManager.getSavedPlaces(); 156 | 157 | if(!savedPlaces.isEmpty()) { 158 | callbacks.loadSavedPlaces(savedPlaces, this); 159 | } 160 | } 161 | 162 | @Override 163 | public void onStart() { 164 | super.onStart(); 165 | ottoBus.register(this); 166 | } 167 | 168 | @Override 169 | public void onStop() { 170 | super.onPause(); 171 | ottoBus.unregister(this); 172 | } 173 | 174 | @Override 175 | public void onDestroy() { 176 | super.onDestroy(); 177 | gpsTracker.stopUsingGPS(); 178 | } 179 | 180 | 181 | @Override 182 | public void onCancel() { 183 | cancelTask(); 184 | } 185 | 186 | private void cancelTask() { 187 | if(asyncTask != null && !asyncTask.isCancelled()) { 188 | asyncTask.cancel(true); 189 | } 190 | } 191 | 192 | private void closeKeyboard() { 193 | InputMethodManager imm = (InputMethodManager)getActivity().getSystemService( 194 | Context.INPUT_METHOD_SERVICE); 195 | imm.hideSoftInputFromWindow(editText.getWindowToken(), 0); 196 | } 197 | 198 | @Inject 199 | AndroidDemoApplication application; 200 | 201 | /* ********************************************************* */ 202 | /* **************** Otto Subscribers *********************** */ 203 | /* ********************************************************* */ 204 | 205 | @Subscribe 206 | public void searchForVenues(VenueSearchEvent event) { 207 | 208 | QacheeData data = (QacheeData) QacheeManager.getInstance().get(event.place, true); 209 | 210 | if(data == null) { 211 | asyncTask = new VenueBackgroundTask(event.place, gpsTracker.getLocation()); 212 | asyncTask.execute(); 213 | }else { 214 | ottoBus.post(new VenueResultEvent(data.venues, event.place, gpsTracker.getLocation())); 215 | } 216 | } 217 | 218 | @Subscribe 219 | public void resultVenues(VenueResultEvent event) { 220 | if(event.venues == null || event.venues.size() == 0) { 221 | Toast.makeText(getActivity(), R.string.no_results_found, Toast.LENGTH_SHORT).show(); 222 | }else { 223 | QacheeManager.getInstance().add(new QacheeData(event.place, event.venues)); 224 | callbacks.onResult(event.venues, event.location, event.place); 225 | } 226 | } 227 | 228 | @Subscribe 229 | public void onPreExecute(OnPreExecuteEvent event) { 230 | createProgressDialog(R.string.connecting_to_foursquare); 231 | } 232 | 233 | @Subscribe 234 | public void onApiError(OnApiErrorEvent event) { 235 | FoursquareApiErrorDto errorDto = event.errorDto; 236 | 237 | if(errorDto.getMeta().getErrorType() == ErrorType.failed_geocode) { 238 | Toast.makeText(getActivity(), R.string.no_results_found, Toast.LENGTH_SHORT).show(); 239 | }else { 240 | Toast.makeText(getActivity(), R.string.unknown_error, Toast.LENGTH_SHORT).show(); 241 | } 242 | } 243 | 244 | @Subscribe 245 | public void onFinally(OnFinallyEvent event) { 246 | closeKeyboard(); 247 | DialogFragmentHelper.dismissDialog(getActivity()); 248 | } 249 | 250 | 251 | } 252 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/fragment/ResultListFragment.java: -------------------------------------------------------------------------------- 1 | package com.android.test.fragment; 2 | 3 | import android.location.Location; 4 | import android.os.Bundle; 5 | import android.support.v4.app.Fragment; 6 | import android.support.v7.widget.DefaultItemAnimator; 7 | import android.support.v7.widget.LinearLayoutManager; 8 | import android.support.v7.widget.RecyclerView; 9 | import android.util.Log; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | import android.widget.Toast; 14 | 15 | import com.android.test.R; 16 | import com.android.test.adapter.VenueRecyclerAdapter; 17 | import com.android.test.domain.Venue; 18 | import com.android.test.session.SessionManager; 19 | import com.android.test.utils.DataHelper; 20 | import com.melnykov.fab.FloatingActionButton; 21 | 22 | import javax.inject.Inject; 23 | 24 | 25 | public class ResultListFragment extends AbstractFragment 26 | implements VenueRecyclerAdapter.RecyclerViewListener { 27 | 28 | public static final String DATA_HELPER = "data_helper"; 29 | public static final String PLACE = "place"; 30 | public static final String LOCATION = "location"; 31 | 32 | 33 | 34 | public interface Callback { 35 | void onItemClick(Venue venue, View view, String url); 36 | void onToolbarHide(); 37 | void onToolbarShow(); 38 | } 39 | 40 | private DataHelper dataHelper; 41 | 42 | private String place; 43 | 44 | private Location currentLocation; 45 | 46 | private FloatingActionButton fab; 47 | 48 | private RecyclerView recyclerView; 49 | 50 | @Inject 51 | private SessionManager sessionManager; 52 | 53 | 54 | private VenueRecyclerAdapter adapter; 55 | 56 | 57 | public static Fragment newInstance(DataHelper dataHelper, String place, Location currentLocation) { 58 | Bundle bundle = new Bundle(); 59 | bundle.putSerializable(DATA_HELPER, dataHelper); 60 | bundle.putString(PLACE, place); 61 | bundle.putParcelable(LOCATION, currentLocation); 62 | 63 | Fragment f = new ResultListFragment(); 64 | f.setArguments(bundle); 65 | 66 | return f; 67 | } 68 | 69 | @Override 70 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 71 | View view = inflater.inflate(R.layout.fragment_result_list, container, false); 72 | 73 | fab = (FloatingActionButton) view.findViewById(R.id.fragment_result_list_floating_button); 74 | recyclerView = (RecyclerView) view.findViewById(R.id.fragment_result_list_recyclerview); 75 | 76 | return view; 77 | } 78 | 79 | @Override 80 | public void onViewCreated(View view, Bundle savedInstanceState) { 81 | super.onViewCreated(view, savedInstanceState); 82 | 83 | if(getArguments() != null) { 84 | dataHelper = (DataHelper) getArguments().getSerializable(DATA_HELPER); 85 | place = getArguments().getString(PLACE); 86 | currentLocation = getArguments().getParcelable(LOCATION); 87 | } 88 | 89 | setupRecyclerView(); 90 | setupFab(); 91 | } 92 | 93 | private void setupFab() { 94 | fab.setOnClickListener(new View.OnClickListener() { 95 | @Override 96 | public void onClick(View v) { 97 | sessionManager.savePlace(place); 98 | 99 | Toast.makeText(getActivity(), getString(R.string.location_saved_open_the_side_bar_to_see_it, place), 100 | Toast.LENGTH_SHORT).show(); 101 | } 102 | }); 103 | } 104 | 105 | private void setupRecyclerView() { 106 | recyclerView.setHasFixedSize(true); 107 | 108 | // use a linear layout manager 109 | RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this.getActivity()); 110 | 111 | recyclerView.setLayoutManager(layoutManager); 112 | recyclerView.setItemAnimator(new DefaultItemAnimator()); 113 | 114 | adapter = new VenueRecyclerAdapter(dataHelper.getList(), currentLocation, this); 115 | recyclerView.setAdapter(adapter); 116 | 117 | 118 | recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() { 119 | 120 | private static final int HIDE_THRESHOLD = 10; 121 | private int scrolledDistance = 0; 122 | private boolean isShowing = true; 123 | 124 | @Override 125 | public void onScrollStateChanged(RecyclerView recyclerView, int newState) { 126 | super.onScrollStateChanged(recyclerView, newState); 127 | } 128 | 129 | @Override 130 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) { 131 | super.onScrolled(recyclerView, dx, dy); 132 | 133 | int firstVisibleItem = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition(); 134 | 135 | if (firstVisibleItem == 0) { 136 | if (!isShowing) { 137 | callbacks.onToolbarShow(); 138 | fab.show(); 139 | isShowing = true; 140 | } 141 | }else if (scrolledDistance > HIDE_THRESHOLD && isShowing) { 142 | callbacks.onToolbarHide(); 143 | fab.hide(); 144 | isShowing = false; 145 | scrolledDistance = 0; 146 | 147 | } else if (scrolledDistance < -HIDE_THRESHOLD && !isShowing) { 148 | callbacks.onToolbarShow(); 149 | fab.show(); 150 | isShowing = true; 151 | scrolledDistance = 0; 152 | } 153 | 154 | if((isShowing && dy > 0) || (!isShowing && dy < 0)) { 155 | scrolledDistance = scrolledDistance + dy; 156 | Log.i("RECYCLER VIEW", "CURRENT AMOUNT VERTICAL SCROLL = " + scrolledDistance); 157 | } 158 | } 159 | 160 | }); 161 | } 162 | 163 | @Override 164 | public void onItemClickListener(View view, int position) { 165 | Venue venue = adapter.getItemAtPosition(position); 166 | String url = (String) view.getTag(); 167 | 168 | callbacks.onItemClick(venue, view, url); 169 | } 170 | 171 | } 172 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/location/GPSTracker.java: -------------------------------------------------------------------------------- 1 | package com.android.test.location; 2 | 3 | import android.app.AlertDialog; 4 | import android.app.Service; 5 | import android.content.Context; 6 | import android.content.DialogInterface; 7 | import android.content.Intent; 8 | import android.location.Location; 9 | import android.location.LocationListener; 10 | import android.location.LocationManager; 11 | import android.os.Bundle; 12 | import android.os.IBinder; 13 | import android.provider.Settings; 14 | import android.util.Log; 15 | 16 | import javax.inject.Inject; 17 | 18 | 19 | /** 20 | * Taken from http://stackoverflow.com/questions/19722712/get-user-current-location-android 21 | * Created by nicolas on 12/22/13. 22 | */ 23 | public class GPSTracker extends Service implements LocationListener { 24 | 25 | private final Context mContext; 26 | 27 | // flag for GPS status 28 | boolean isGPSEnabled = false; 29 | 30 | // flag for network status 31 | boolean isNetworkEnabled = false; 32 | 33 | // flag for GPS status 34 | boolean canGetLocation = false; 35 | 36 | Location location; // location 37 | double latitude; // latitude 38 | double longitude; // longitude 39 | 40 | // The minimum distance to change Updates in meters 41 | private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10; // 10 meters 42 | 43 | // The minimum time between updates in milliseconds 44 | private static final long MIN_TIME_BW_UPDATES = 1000 * 60 * 1; // 1 minute 45 | 46 | // Declaring a Location Manager 47 | protected LocationManager locationManager; 48 | 49 | @Inject 50 | public GPSTracker(Context context) { 51 | this.mContext = context; 52 | getLocation(); 53 | } 54 | 55 | public Location getLocation() { 56 | try { 57 | locationManager = (LocationManager) mContext 58 | .getSystemService(LOCATION_SERVICE); 59 | 60 | // getting GPS status 61 | isGPSEnabled = locationManager 62 | .isProviderEnabled(LocationManager.GPS_PROVIDER); 63 | 64 | // getting network status 65 | isNetworkEnabled = locationManager 66 | .isProviderEnabled(LocationManager.NETWORK_PROVIDER); 67 | 68 | if (!isGPSEnabled && !isNetworkEnabled) { 69 | // no network provider is enabled 70 | } else { 71 | this.canGetLocation = true; 72 | if (isNetworkEnabled) { 73 | locationManager.requestLocationUpdates( 74 | LocationManager.NETWORK_PROVIDER, 75 | MIN_TIME_BW_UPDATES, 76 | MIN_DISTANCE_CHANGE_FOR_UPDATES, this); 77 | Log.d("Network", "Network"); 78 | if (locationManager != null) { 79 | location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); 80 | if (location != null) { 81 | latitude = location.getLatitude(); 82 | longitude = location.getLongitude(); 83 | } 84 | } 85 | } 86 | // if GPS Enabled get lat/long using GPS Services 87 | if (isGPSEnabled) { 88 | if (location == null) { 89 | locationManager.requestLocationUpdates( 90 | LocationManager.GPS_PROVIDER, 91 | MIN_TIME_BW_UPDATES, 92 | MIN_DISTANCE_CHANGE_FOR_UPDATES, this); 93 | Log.d("GPS Enabled", "GPS Enabled"); 94 | if (locationManager != null) { 95 | location = locationManager 96 | .getLastKnownLocation(LocationManager.GPS_PROVIDER); 97 | if (location != null) { 98 | latitude = location.getLatitude(); 99 | longitude = location.getLongitude(); 100 | } 101 | } 102 | } 103 | } 104 | } 105 | 106 | } catch (Exception e) { 107 | e.printStackTrace(); 108 | } 109 | 110 | return location; 111 | } 112 | 113 | /** 114 | * Stop using GPS listener 115 | * Calling this function will stop using GPS in your app 116 | * */ 117 | public void stopUsingGPS(){ 118 | if(locationManager != null){ 119 | locationManager.removeUpdates(GPSTracker.this); 120 | } 121 | } 122 | 123 | /** 124 | * Function to get latitude 125 | * */ 126 | public double getLatitude(){ 127 | if(location != null){ 128 | latitude = location.getLatitude(); 129 | } 130 | 131 | // return latitude 132 | return latitude; 133 | } 134 | 135 | /** 136 | * Function to get longitude 137 | * */ 138 | public double getLongitude(){ 139 | if(location != null){ 140 | longitude = location.getLongitude(); 141 | } 142 | 143 | // return longitude 144 | return longitude; 145 | } 146 | 147 | /** 148 | * Function to check GPS/wifi enabled 149 | * @return boolean 150 | * */ 151 | public boolean canGetLocation() { 152 | return this.canGetLocation; 153 | } 154 | 155 | /** 156 | * Function to show settings alert dialog 157 | * On pressing Settings button will lauch Settings Options 158 | * */ 159 | public void showSettingsAlert(){ 160 | AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext); 161 | 162 | // Setting Dialog Title 163 | alertDialog.setTitle("GPS is settings"); 164 | 165 | // Setting Dialog Message 166 | alertDialog.setMessage("GPS is not enabled. Do you want to go to settings menu?"); 167 | 168 | // On pressing Settings button 169 | alertDialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() { 170 | public void onClick(DialogInterface dialog,int which) { 171 | Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); 172 | mContext.startActivity(intent); 173 | } 174 | }); 175 | 176 | // on pressing cancel button 177 | alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { 178 | public void onClick(DialogInterface dialog, int which) { 179 | dialog.cancel(); 180 | } 181 | }); 182 | 183 | // Showing Alert Message 184 | alertDialog.show(); 185 | } 186 | 187 | @Override 188 | public void onLocationChanged(Location location) { 189 | } 190 | 191 | @Override 192 | public void onProviderDisabled(String provider) { 193 | } 194 | 195 | @Override 196 | public void onProviderEnabled(String provider) { 197 | } 198 | 199 | @Override 200 | public void onStatusChanged(String provider, int status, Bundle extras) { 201 | } 202 | 203 | @Override 204 | public IBinder onBind(Intent arg0) { 205 | return null; 206 | } 207 | 208 | } 209 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/module/AndroidDemoModule.java: -------------------------------------------------------------------------------- 1 | package com.android.test.module; 2 | 3 | import android.content.Context; 4 | 5 | import com.android.test.AndroidDemoApplication; 6 | import com.google.inject.AbstractModule; 7 | import com.google.inject.Provider; 8 | 9 | /** 10 | * Created by Nicolas Jafelle on 11/26/14. 11 | */ 12 | public class AndroidDemoModule extends AbstractModule { 13 | 14 | @Override 15 | protected void configure() { 16 | 17 | bind(Context.class).toProvider(new Provider() { 18 | @Override 19 | public Context get() { 20 | return AndroidDemoApplication.getAppContext(); 21 | } 22 | }); 23 | // .in(Singleton.class); 24 | 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/otto/OttoBus.java: -------------------------------------------------------------------------------- 1 | package com.android.test.otto; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | 6 | import com.squareup.otto.Bus; 7 | 8 | import javax.inject.Singleton; 9 | 10 | /** 11 | * Injected Class to use Otto with a Singleton instance and to post always on the main thread. 12 | *
13 | * https://github.com/square/otto/issues/38 14 | */ 15 | @Singleton 16 | public class OttoBus extends Bus { 17 | 18 | 19 | 20 | private final Handler mainThread = new Handler(Looper.getMainLooper()); 21 | 22 | @Override 23 | public void post(final Object event) { 24 | if (Looper.myLooper() == Looper.getMainLooper()) { 25 | super.post(event); 26 | } else { 27 | mainThread.post(new Runnable() { 28 | @Override 29 | public void run() { 30 | OttoBus.super.post(event); 31 | } 32 | }); 33 | } 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/otto/VenueResultEvent.java: -------------------------------------------------------------------------------- 1 | package com.android.test.otto; 2 | 3 | import android.location.Location; 4 | 5 | import com.android.test.domain.Venue; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Created by Nicolas Jafelle on 11/13/14. 11 | */ 12 | public class VenueResultEvent { 13 | 14 | public List venues; 15 | 16 | public String place; 17 | 18 | public Location location; 19 | 20 | 21 | public VenueResultEvent(List venues, String place, Location location) { 22 | this.venues = venues; 23 | this.place = place; 24 | this.location = location; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/otto/VenueSearchEvent.java: -------------------------------------------------------------------------------- 1 | package com.android.test.otto; 2 | 3 | 4 | /** 5 | * Created by Nicolas Jafelle on 11/13/14. 6 | */ 7 | public class VenueSearchEvent { 8 | public String place; 9 | 10 | 11 | public VenueSearchEvent(String place) { 12 | this.place = place; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/qachee/QacheeData.java: -------------------------------------------------------------------------------- 1 | package com.android.test.qachee; 2 | 3 | import com.android.test.domain.Venue; 4 | import com.qachee.QacheeableObject; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Created by Nicolas Jafelle on 11/21/14. 10 | */ 11 | public class QacheeData extends QacheeableObject { 12 | 13 | public String search; 14 | 15 | public List venues; 16 | 17 | 18 | public QacheeData(String search, List venues) { 19 | this.search = search; 20 | this.venues = venues; 21 | } 22 | 23 | @Override 24 | public String getKey() { 25 | return search; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/session/SessionManager.java: -------------------------------------------------------------------------------- 1 | package com.android.test.session; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | 6 | import com.google.inject.Inject; 7 | 8 | import java.util.HashSet; 9 | import java.util.Set; 10 | 11 | public class SessionManager { 12 | 13 | private SharedPreferences sharedPref; 14 | 15 | private final String PREF_NAME = "android_demo_prefs"; 16 | private static final String SAVED_PLACES = "saved_places"; 17 | 18 | @Inject 19 | public SessionManager(Context context) { 20 | sharedPref = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); 21 | } 22 | 23 | 24 | public void savePlace(String place) { 25 | Set locations = new HashSet(getSavedPlaces()); 26 | locations.add(place); 27 | saveValues(SAVED_PLACES, locations); 28 | } 29 | 30 | public Set getSavedPlaces() { 31 | return this.sharedPref.getStringSet(SAVED_PLACES, new HashSet()); 32 | } 33 | 34 | public void clear() { 35 | SharedPreferences.Editor editor = this.sharedPref.edit(); 36 | editor.clear(); 37 | editor.commit(); 38 | } 39 | 40 | 41 | // Helpers 42 | protected void saveValue(String key, String value){ 43 | SharedPreferences.Editor editor = this.sharedPref.edit(); 44 | editor.putString(key, value); 45 | editor.commit(); 46 | } 47 | 48 | protected void saveValues(String key, Set values){ 49 | SharedPreferences.Editor editor = this.sharedPref.edit(); 50 | editor.putStringSet(key, values); 51 | editor.apply(); 52 | } 53 | 54 | 55 | 56 | protected void saveValue(String key, long value){ 57 | SharedPreferences.Editor editor = this.sharedPref.edit(); 58 | editor.putLong(key,value); 59 | editor.commit(); 60 | } 61 | 62 | protected void saveValue(String key, int value){ 63 | SharedPreferences.Editor editor = this.sharedPref.edit(); 64 | editor.putInt(key, value); 65 | editor.commit(); 66 | } 67 | 68 | protected void saveValue(String key, boolean value){ 69 | SharedPreferences.Editor editor = this.sharedPref.edit(); 70 | editor.putBoolean(key, value); 71 | editor.commit(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/task/FoursquareAsyncTask.java: -------------------------------------------------------------------------------- 1 | package com.android.test.task; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | import android.widget.Toast; 6 | 7 | import com.android.test.AndroidDemoApplication; 8 | import com.android.test.R; 9 | import com.android.test.dto.FoursquareApiErrorDto; 10 | import com.google.gson.Gson; 11 | 12 | import java.io.BufferedReader; 13 | import java.io.IOException; 14 | import java.io.InputStreamReader; 15 | import java.net.ConnectException; 16 | import java.net.SocketTimeoutException; 17 | 18 | import javax.inject.Inject; 19 | 20 | import retrofit.RetrofitError; 21 | 22 | /** 23 | * Created by nicolas on 12/22/13. 24 | */ 25 | public abstract class FoursquareAsyncTask extends SafeAsyncTask { 26 | 27 | @Inject 28 | private Context context; 29 | 30 | protected FoursquareAsyncTask() { 31 | super(); 32 | AndroidDemoApplication.injectMembers(this); 33 | } 34 | 35 | @Override 36 | protected void onException(Exception e) throws RuntimeException { 37 | try { 38 | if(e instanceof RetrofitError) { 39 | RetrofitError retrofitError = (RetrofitError) e; 40 | if(retrofitError.getResponse() != null) { 41 | if (retrofitError.getResponse().getStatus() > 500 ){ 42 | String msg = "Network error HTTP ("+retrofitError.getResponse().getStatus()+")"; 43 | if (retrofitError.getMessage()!=null && !retrofitError.getMessage().isEmpty()){ 44 | msg += ": "+retrofitError.getMessage(); 45 | } 46 | super.onException(e); 47 | }else if (retrofitError.getBody()==null){ 48 | Toast.makeText(this.context, e.getMessage(), Toast.LENGTH_LONG).show(); 49 | }else if (retrofitError.getCause() instanceof ConnectException){ 50 | Toast.makeText(this.context, R.string.connection_error, Toast.LENGTH_SHORT).show(); 51 | }else if (retrofitError.getCause() instanceof SocketTimeoutException){ 52 | Toast.makeText(this.context, R.string.connection_error, Toast.LENGTH_SHORT).show(); 53 | }else{ 54 | BufferedReader reader = new BufferedReader(new InputStreamReader(((RetrofitError) e).getResponse().getBody().in())); 55 | FoursquareApiErrorDto errorDto = new Gson().fromJson(reader, FoursquareApiErrorDto.class); 56 | onApiError(errorDto); 57 | } 58 | }else if(retrofitError.isNetworkError() && !isCancelled()){ 59 | Toast.makeText(this.context, R.string.connection_error, Toast.LENGTH_SHORT).show(); 60 | } 61 | }else { 62 | super.onException(e); 63 | } 64 | } catch (IOException e1) { 65 | e1.printStackTrace(); 66 | } 67 | } 68 | 69 | public void clear() { 70 | this.context = null; 71 | } 72 | 73 | @Override 74 | protected void onInterrupted(Exception e) { 75 | Log.d("BACKGROUND_TASK", "Interrupting background task " + this); 76 | } 77 | 78 | protected abstract void onApiError(FoursquareApiErrorDto errorDto); 79 | 80 | } 81 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/task/SafeAsyncTask.java: -------------------------------------------------------------------------------- 1 | package com.android.test.task; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | import android.util.Log; 6 | 7 | import java.io.InterruptedIOException; 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | import java.util.concurrent.Callable; 11 | import java.util.concurrent.CountDownLatch; 12 | import java.util.concurrent.Executor; 13 | import java.util.concurrent.Executors; 14 | import java.util.concurrent.FutureTask; 15 | 16 | 17 | /** 18 | * DEPRECATED!! 19 | * A class similar but unrelated to android's {@link android.os.AsyncTask}. 20 | *

21 | * Unlike AsyncTask, this class properly propagates exceptions. 22 | *

23 | * If you're familiar with AsyncTask and are looking for {@link android.os.AsyncTask#doInBackground(Object[])}, 24 | * we've named it {@link #call()} here to conform with java 1.5's {@link java.util.concurrent.Callable} interface. 25 | *

26 | * Current limitations: does not yet handle progress, although it shouldn't be 27 | * hard to add. 28 | *

29 | * If using your own executor, you must call future() to get a runnable you can execute. 30 | * 31 | * @param 32 | * 33 | */ 34 | public abstract class SafeAsyncTask implements Callable { 35 | public static final int DEFAULT_POOL_SIZE = 25; 36 | protected static final Executor DEFAULT_EXECUTOR = Executors.newFixedThreadPool(DEFAULT_POOL_SIZE); 37 | 38 | protected Handler handler; 39 | protected Executor executor; 40 | protected StackTraceElement[] launchLocation; 41 | protected FutureTask future; 42 | 43 | 44 | /** 45 | * Sets executor to Executors.newFixedThreadPool(DEFAULT_POOL_SIZE) and 46 | * Handler to new Handler() 47 | */ 48 | public SafeAsyncTask() { 49 | this.executor = DEFAULT_EXECUTOR; 50 | } 51 | 52 | /** 53 | * Sets executor to Executors.newFixedThreadPool(DEFAULT_POOL_SIZE) 54 | */ 55 | public SafeAsyncTask(Handler handler) { 56 | this.handler = handler; 57 | this.executor = DEFAULT_EXECUTOR; 58 | } 59 | 60 | /** 61 | * Sets Handler to new Handler() 62 | */ 63 | public SafeAsyncTask(Executor executor) { 64 | this.executor = executor; 65 | } 66 | 67 | public SafeAsyncTask(Handler handler, Executor executor) { 68 | this.handler = handler; 69 | this.executor = executor; 70 | } 71 | 72 | 73 | public FutureTask future() { 74 | future = new FutureTask(newTask()); 75 | return future; 76 | } 77 | 78 | public SafeAsyncTask executor(Executor executor) { 79 | this.executor = executor; 80 | return this; 81 | } 82 | 83 | public Executor executor() { 84 | return executor; 85 | } 86 | 87 | public SafeAsyncTask handler(Handler handler) { 88 | this.handler = handler; 89 | return this; 90 | } 91 | 92 | public Handler handler() { 93 | return handler; 94 | } 95 | 96 | public void execute() { 97 | execute(Thread.currentThread().getStackTrace()); 98 | } 99 | 100 | protected void execute(StackTraceElement[] launchLocation) { 101 | this.launchLocation = launchLocation; 102 | executor.execute(future()); 103 | } 104 | 105 | public boolean cancel(boolean mayInterruptIfRunning) { 106 | if (future == null) throw new UnsupportedOperationException("You cannot cancel this task before calling future()"); 107 | 108 | return future.cancel(mayInterruptIfRunning); 109 | } 110 | 111 | 112 | /** 113 | * @throws Exception, captured on passed to onException() if present. 114 | */ 115 | protected void onPreExecute() throws Exception { 116 | } 117 | 118 | /** 119 | * @param t the result of {@link #call()} 120 | * @throws Exception, captured on passed to onException() if present. 121 | */ 122 | @SuppressWarnings({"UnusedDeclaration"}) 123 | protected void onSuccess(ResultT t) throws Exception { 124 | } 125 | 126 | /** 127 | * Called when the thread has been interrupted, likely because 128 | * the task was canceled. 129 | *

130 | * By default, calls {@link #onException(Exception)}, but this method 131 | * may be overridden to handle interruptions differently than other 132 | * exceptions. 133 | * 134 | * @param e an InterruptedException or InterruptedIOException 135 | */ 136 | protected void onInterrupted(Exception e) { 137 | onException(e); 138 | } 139 | 140 | /** 141 | * Logs the exception as an Error by default, but this method may 142 | * be overridden by subclasses. 143 | * 144 | * @param e the exception thrown from {@link #onPreExecute()}, {@link #call()}, or {@link #onSuccess(Object)} 145 | * @throws RuntimeException, ignored 146 | */ 147 | protected void onException(Exception e) throws RuntimeException { 148 | onThrowable(e); 149 | } 150 | 151 | protected void onThrowable(Throwable t) throws RuntimeException { 152 | Log.e("roboguice", "Throwable caught during background processing", t); 153 | } 154 | 155 | /** 156 | * @throws RuntimeException, ignored 157 | */ 158 | protected void onFinally() throws RuntimeException { 159 | } 160 | 161 | public boolean isCancelled(){ 162 | return future == null ? false : future.isCancelled(); 163 | } 164 | 165 | 166 | protected Task newTask() { 167 | return new Task(this); 168 | } 169 | 170 | 171 | public static class Task implements Callable { 172 | protected SafeAsyncTask parent; 173 | protected Handler handler; 174 | 175 | public Task(SafeAsyncTask parent) { 176 | this.parent = parent; 177 | this.handler = parent.handler != null ? parent.handler : new Handler(Looper.getMainLooper()); 178 | } 179 | 180 | public Void call() throws Exception { 181 | try { 182 | doPreExecute(); 183 | doSuccess(doCall()); 184 | 185 | } catch (final Exception e) { 186 | try { 187 | doException(e); 188 | } catch (Exception f) { 189 | // logged but ignored 190 | Log.e("BACKGROUND_TASK", "Exception in", f); 191 | } 192 | 193 | } catch (final Throwable t) { 194 | try { 195 | doThrowable(t); 196 | } catch (Exception f) { 197 | // logged but ignored 198 | Log.e("BACKGROUND_TASK", "Exception in", f); 199 | } 200 | } finally { 201 | doFinally(); 202 | } 203 | 204 | return null; 205 | } 206 | 207 | protected void doPreExecute() throws Exception { 208 | postToUiThreadAndWait(new Callable() { 209 | public Object call() throws Exception { 210 | parent.onPreExecute(); 211 | return null; 212 | } 213 | }); 214 | } 215 | 216 | protected ResultT doCall() throws Exception { 217 | return parent.call(); 218 | } 219 | 220 | protected void doSuccess(final ResultT r) throws Exception { 221 | postToUiThreadAndWait(new Callable() { 222 | public Object call() throws Exception { 223 | parent.onSuccess(r); 224 | return null; 225 | } 226 | }); 227 | } 228 | 229 | protected void doException(final Exception e) throws Exception { 230 | if (parent.launchLocation != null) { 231 | final ArrayList stack = new ArrayList(Arrays.asList(e.getStackTrace())); 232 | stack.addAll(Arrays.asList(parent.launchLocation)); 233 | e.setStackTrace(stack.toArray(new StackTraceElement[stack.size()])); 234 | } 235 | postToUiThreadAndWait(new Callable() { 236 | public Object call() throws Exception { 237 | if (e instanceof InterruptedException || e instanceof InterruptedIOException) parent.onInterrupted(e); 238 | else parent.onException(e); 239 | return null; 240 | } 241 | }); 242 | } 243 | 244 | protected void doThrowable(final Throwable e) throws Exception { 245 | if (parent.launchLocation != null) { 246 | final ArrayList stack = new ArrayList(Arrays.asList(e.getStackTrace())); 247 | stack.addAll(Arrays.asList(parent.launchLocation)); 248 | e.setStackTrace(stack.toArray(new StackTraceElement[stack.size()])); 249 | } 250 | postToUiThreadAndWait(new Callable() { 251 | public Object call() throws Exception { 252 | parent.onThrowable(e); 253 | return null; 254 | } 255 | }); 256 | } 257 | 258 | protected void doFinally() throws Exception { 259 | postToUiThreadAndWait(new Callable() { 260 | public Object call() throws Exception { 261 | parent.onFinally(); 262 | return null; 263 | } 264 | }); 265 | } 266 | 267 | 268 | /** 269 | * Posts the specified runnable to the UI thread using a handler, 270 | * and waits for operation to finish. If there's an exception, 271 | * it captures it and rethrows it. 272 | * 273 | * @param c the callable to post 274 | * @throws Exception on error 275 | */ 276 | protected void postToUiThreadAndWait(final Callable c) throws Exception { 277 | final CountDownLatch latch = new CountDownLatch(1); 278 | final Exception[] exceptions = new Exception[1]; 279 | 280 | // Execute onSuccess in the UI thread, but wait 281 | // for it to complete. 282 | // If it throws an exception, capture that exception 283 | // and rethrow it later. 284 | handler.post(new Runnable() { 285 | public void run() { 286 | try { 287 | c.call(); 288 | } catch (Exception e) { 289 | exceptions[0] = e; 290 | } finally { 291 | latch.countDown(); 292 | } 293 | } 294 | }); 295 | 296 | // Wait for onSuccess to finish 297 | latch.await(); 298 | 299 | if (exceptions[0] != null) throw exceptions[0]; 300 | 301 | } 302 | 303 | } 304 | 305 | } 306 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/task/VenueBackgroundTask.java: -------------------------------------------------------------------------------- 1 | package com.android.test.task; 2 | 3 | import android.location.Location; 4 | 5 | import com.android.test.client.FoursquareClient; 6 | import com.android.test.domain.Venue; 7 | import com.android.test.dto.FoursquareApiErrorDto; 8 | import com.android.test.dto.VenueDto; 9 | import com.android.test.otto.OttoBus; 10 | import com.android.test.otto.VenueResultEvent; 11 | import com.android.test.task.event.OnApiErrorEvent; 12 | import com.android.test.task.event.OnFinallyEvent; 13 | import com.android.test.task.event.OnPreExecuteEvent; 14 | 15 | import java.util.List; 16 | 17 | import javax.inject.Inject; 18 | 19 | /** 20 | * Created by Nicolas Jafelle on 11/28/14. 21 | */ 22 | public class VenueBackgroundTask extends FoursquareAsyncTask { 23 | 24 | @Inject 25 | private OttoBus ottoBus; 26 | 27 | @Inject 28 | private FoursquareClient foursquareClient; 29 | 30 | private String criteria; 31 | private Location currentLocation; 32 | 33 | public VenueBackgroundTask(String criteria, Location currentLocation) { 34 | super(); 35 | this.criteria = criteria; 36 | this.currentLocation = currentLocation; 37 | } 38 | 39 | @Override 40 | protected void onPreExecute() throws Exception { 41 | super.onPreExecute(); 42 | ottoBus.post(new OnPreExecuteEvent()); 43 | } 44 | 45 | @Override 46 | public VenueDto call() throws Exception { 47 | android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND); 48 | return foursquareClient.searchForVenues(this.criteria); 49 | } 50 | 51 | @Override 52 | protected void onSuccess(VenueDto venueDto) throws Exception { 53 | super.onSuccess(venueDto); 54 | 55 | if(!isCancelled()) { 56 | List venues = venueDto.getResponse().getVenues(); 57 | ottoBus.post(new VenueResultEvent(venues, criteria, currentLocation)); 58 | } 59 | } 60 | 61 | @Override 62 | protected void onApiError(FoursquareApiErrorDto errorDto) { 63 | ottoBus.post(new OnApiErrorEvent(errorDto)); 64 | } 65 | 66 | @Override 67 | protected void onFinally() throws RuntimeException { 68 | super.onFinally(); 69 | super.clear(); 70 | ottoBus.post(new OnFinallyEvent()); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/task/event/OnApiErrorEvent.java: -------------------------------------------------------------------------------- 1 | package com.android.test.task.event; 2 | 3 | import com.android.test.dto.FoursquareApiErrorDto; 4 | 5 | /** 6 | * Created by Nicolas Jafelle on 11/28/14. 7 | */ 8 | public class OnApiErrorEvent { 9 | 10 | public FoursquareApiErrorDto errorDto; 11 | 12 | public OnApiErrorEvent(FoursquareApiErrorDto errorDto) { 13 | this.errorDto = errorDto; 14 | 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/task/event/OnFinallyEvent.java: -------------------------------------------------------------------------------- 1 | package com.android.test.task.event; 2 | 3 | /** 4 | * Created by Nicolas Jafelle on 11/28/14. 5 | */ 6 | public class OnFinallyEvent { 7 | } 8 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/task/event/OnPreExecuteEvent.java: -------------------------------------------------------------------------------- 1 | package com.android.test.task.event; 2 | 3 | /** 4 | * Created by Nicolas Jafelle on 11/28/14. 5 | */ 6 | public class OnPreExecuteEvent { 7 | } 8 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/utils/DataHelper.java: -------------------------------------------------------------------------------- 1 | package com.android.test.utils; 2 | 3 | import com.android.test.domain.Venue; 4 | 5 | import java.io.Serializable; 6 | import java.util.List; 7 | 8 | /** 9 | * Created by Nicolas Jafelle on 10/24/14. 10 | */ 11 | public class DataHelper implements Serializable { 12 | 13 | private List floors; 14 | 15 | public DataHelper(List floors) { 16 | this.floors = floors; 17 | } 18 | 19 | public List getList() { 20 | return this.floors; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/view/ObservableScrollView.java: -------------------------------------------------------------------------------- 1 | package com.android.test.view; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.ScrollView; 6 | 7 | /** 8 | * Created by Nicolas Jafelle on 11/7/14. 9 | */ 10 | public class ObservableScrollView extends ScrollView { 11 | 12 | public interface ObservableScrollViewListener { 13 | void onScrollChanged(int l, int t, int oldl, int oldt); 14 | } 15 | 16 | private ObservableScrollViewListener listener; 17 | 18 | 19 | public ObservableScrollView(Context context) { 20 | super(context); 21 | } 22 | 23 | public ObservableScrollView(Context context, AttributeSet attrs) { 24 | super(context, attrs); 25 | } 26 | 27 | public ObservableScrollView(Context context, AttributeSet attrs, int defStyleAttr) { 28 | super(context, attrs, defStyleAttr); 29 | } 30 | 31 | public ObservableScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 32 | super(context, attrs, defStyleAttr, defStyleRes); 33 | } 34 | 35 | public void setObservableScrollViewListener(ObservableScrollViewListener listener) { 36 | this.listener = listener; 37 | } 38 | 39 | @Override 40 | protected void onScrollChanged(int l, int t, int oldl, int oldt) { 41 | super.onScrollChanged(l, t, oldl, oldt); 42 | if(listener != null) { 43 | this.listener.onScrollChanged(l, t, oldl, oldt); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/view/SideBarCallback.java: -------------------------------------------------------------------------------- 1 | package com.android.test.view; 2 | 3 | /** 4 | * Created by Nicolas Jafelle on 10/27/14. 5 | */ 6 | public interface SideBarCallback { 7 | 8 | void onSideBarItemClick(String text); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/java/com/android/test/view/VenueItemView.java: -------------------------------------------------------------------------------- 1 | package com.android.test.view; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.location.Location; 6 | import android.os.Build; 7 | import android.util.AttributeSet; 8 | import android.widget.ImageView; 9 | import android.widget.RelativeLayout; 10 | import android.widget.TextView; 11 | 12 | import com.android.test.R; 13 | import com.android.test.domain.Venue; 14 | import com.squareup.picasso.Picasso; 15 | 16 | import java.util.Random; 17 | 18 | 19 | /** 20 | * VenueItemView 21 | * Created by nicolas on 12/22/13. 22 | */ 23 | public class VenueItemView extends RelativeLayout { 24 | 25 | private String RANDOM_IMAGE_URL = "http://lorempixel.com/600/480/abstract/"; 26 | 27 | private TextView nameView; 28 | 29 | private TextView distanceView; 30 | 31 | private ImageView randomImage; 32 | 33 | public VenueItemView(Context context) { 34 | super(context); 35 | init(); 36 | } 37 | 38 | public VenueItemView(Context context, AttributeSet attrs) { 39 | super(context, attrs); 40 | init(); 41 | } 42 | 43 | public VenueItemView(Context context, AttributeSet attrs, int defStyle) { 44 | super(context, attrs, defStyle); 45 | init(); 46 | } 47 | 48 | private void init() { 49 | inflate(getContext(), R.layout.venue_item, this); 50 | 51 | nameView = (TextView) findViewById(R.id.venue_item_name); 52 | distanceView = (TextView) findViewById(R.id.venue_item_distance); 53 | randomImage = (ImageView) findViewById(R.id.venue_item_abstract_image); 54 | 55 | Random rand = new Random(); 56 | 57 | // nextInt is normally exclusive of the top value, 58 | // so add 1 to make it inclusive 59 | int randomNum = rand.nextInt((10 - 1) + 1) + 1; 60 | RANDOM_IMAGE_URL = RANDOM_IMAGE_URL + randomNum; 61 | } 62 | 63 | public void fillData(Venue venue, Location currentLocation) { 64 | 65 | 66 | setTag(RANDOM_IMAGE_URL); 67 | nameView.setText(venue.getName()); 68 | 69 | Picasso.with(getContext()) 70 | .load(RANDOM_IMAGE_URL) 71 | .into(randomImage); 72 | 73 | if(currentLocation != null) { 74 | float[] results = new float[3]; 75 | Location.distanceBetween(currentLocation.getLatitude(), currentLocation.getLongitude(), 76 | venue.getLocation().getLat(), venue.getLocation().getLng(), results); 77 | 78 | double km = results[0] / 1000; 79 | String distance = String.format("%.2f Km", km); 80 | 81 | distanceView.setText(distance); 82 | } 83 | } 84 | 85 | public String getDistanceText() { 86 | return this.distanceView.getText().toString(); 87 | } 88 | 89 | public String getNameText() { 90 | return this.nameView.getText().toString(); 91 | } 92 | 93 | public void setFirst() { 94 | 95 | final TypedArray styledAttributes = getContext().getTheme().obtainStyledAttributes( 96 | new int[] { android.R.attr.actionBarSize }); 97 | 98 | float height; 99 | if(Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) { 100 | height = styledAttributes.getDimension(0, 0); 101 | }else { 102 | height = styledAttributes.getDimension(0, 0) + getContext().getResources().getDimension(R.dimen.main_separation); 103 | } 104 | 105 | this.setPadding(0, (int)height, 0, 0); 106 | styledAttributes.recycle(); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/anim/bottom_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/anim/bottom_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/anim/top_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/anim/top_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/drawable-hdpi/ic_action_content_save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolasjafelle/AndroidDemo/eeb502f9904f6316b5aa1ac539fd37c27c9d74f9/AndroidDemoApp/App/src/main/res/drawable-hdpi/ic_action_content_save.png -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/drawable-hdpi/ic_action_navigation_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolasjafelle/AndroidDemo/eeb502f9904f6316b5aa1ac539fd37c27c9d74f9/AndroidDemoApp/App/src/main/res/drawable-hdpi/ic_action_navigation_menu.png -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/drawable-hdpi/ic_action_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolasjafelle/AndroidDemo/eeb502f9904f6316b5aa1ac539fd37c27c9d74f9/AndroidDemoApp/App/src/main/res/drawable-hdpi/ic_action_search.png -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolasjafelle/AndroidDemo/eeb502f9904f6316b5aa1ac539fd37c27c9d74f9/AndroidDemoApp/App/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/drawable-mdpi/ic_action_content_save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolasjafelle/AndroidDemo/eeb502f9904f6316b5aa1ac539fd37c27c9d74f9/AndroidDemoApp/App/src/main/res/drawable-mdpi/ic_action_content_save.png -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/drawable-mdpi/ic_action_navigation_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolasjafelle/AndroidDemo/eeb502f9904f6316b5aa1ac539fd37c27c9d74f9/AndroidDemoApp/App/src/main/res/drawable-mdpi/ic_action_navigation_menu.png -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/drawable-mdpi/ic_action_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolasjafelle/AndroidDemo/eeb502f9904f6316b5aa1ac539fd37c27c9d74f9/AndroidDemoApp/App/src/main/res/drawable-mdpi/ic_action_search.png -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolasjafelle/AndroidDemo/eeb502f9904f6316b5aa1ac539fd37c27c9d74f9/AndroidDemoApp/App/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/drawable-xhdpi/ic_action_content_save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolasjafelle/AndroidDemo/eeb502f9904f6316b5aa1ac539fd37c27c9d74f9/AndroidDemoApp/App/src/main/res/drawable-xhdpi/ic_action_content_save.png -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/drawable-xhdpi/ic_action_navigation_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolasjafelle/AndroidDemo/eeb502f9904f6316b5aa1ac539fd37c27c9d74f9/AndroidDemoApp/App/src/main/res/drawable-xhdpi/ic_action_navigation_menu.png -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/drawable-xhdpi/ic_action_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolasjafelle/AndroidDemo/eeb502f9904f6316b5aa1ac539fd37c27c9d74f9/AndroidDemoApp/App/src/main/res/drawable-xhdpi/ic_action_search.png -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolasjafelle/AndroidDemo/eeb502f9904f6316b5aa1ac539fd37c27c9d74f9/AndroidDemoApp/App/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/drawable-xxhdpi/drawer_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolasjafelle/AndroidDemo/eeb502f9904f6316b5aa1ac539fd37c27c9d74f9/AndroidDemoApp/App/src/main/res/drawable-xxhdpi/drawer_shadow.9.png -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/drawable-xxhdpi/ic_ab_drawer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolasjafelle/AndroidDemo/eeb502f9904f6316b5aa1ac539fd37c27c9d74f9/AndroidDemoApp/App/src/main/res/drawable-xxhdpi/ic_ab_drawer.png -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/drawable-xxhdpi/ic_action_content_save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolasjafelle/AndroidDemo/eeb502f9904f6316b5aa1ac539fd37c27c9d74f9/AndroidDemoApp/App/src/main/res/drawable-xxhdpi/ic_action_content_save.png -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/drawable-xxhdpi/ic_action_navigation_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolasjafelle/AndroidDemo/eeb502f9904f6316b5aa1ac539fd37c27c9d74f9/AndroidDemoApp/App/src/main/res/drawable-xxhdpi/ic_action_navigation_menu.png -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/drawable-xxhdpi/ic_action_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolasjafelle/AndroidDemo/eeb502f9904f6316b5aa1ac539fd37c27c9d74f9/AndroidDemoApp/App/src/main/res/drawable-xxhdpi/ic_action_search.png -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolasjafelle/AndroidDemo/eeb502f9904f6316b5aa1ac539fd37c27c9d74f9/AndroidDemoApp/App/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/drawable-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolasjafelle/AndroidDemo/eeb502f9904f6316b5aa1ac539fd37c27c9d74f9/AndroidDemoApp/App/src/main/res/drawable-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/drawable/card_view_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/drawable/foreground_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/layout/activity_main_overlay.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/layout/activity_side_bar.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | 15 | 16 | 23 | 24 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/layout/fragment_detail.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 14 | 20 | 26 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/layout/fragment_main.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 14 | 18 | 25 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/layout/fragment_result_list.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | 16 | 26 | 27 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/layout/toolbar.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/layout/venue_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 15 | 23 | 29 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/menu/search_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 10 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | #333333 19 | #80000000 20 | #464646 21 | 22 | #E8F5E9 23 | #C8E6C9 24 | #A5D6A7 25 | #81C784 26 | #66BB6A 27 | #4CAF50 28 | #43A047 29 | #388E3C 30 | #2E7D32 31 | #1B5E20 32 | #B9F6CA 33 | #69F0AE 34 | #00E676 35 | #00C853 36 | 37 | #8066BB6A 38 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 15dp 6 | 50dp 7 | 10dp 8 | 260dp 9 | 15dp 10 | 200dp 11 | 5dp 12 | 150dp 13 | 14 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Material Demo 5 | Hello world! 6 | Type some place in the world 7 | Place in the world… 8 | Please type a place 9 | Search 10 | Settings 11 | There was a problem while connecting, try again in a few moments… 12 | Connecting to Foursquare… 13 | No results found, write another place! 14 | Unknown error, try again in a few minutes! 15 | Cancel 16 | Drawer Open 17 | Drawer Close 18 | %1$s saved! Open the side bar to see it 19 | 20 | 21 | -------------------------------------------------------------------------------- /AndroidDemoApp/App/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /AndroidDemoApp/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.1.0' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /AndroidDemoApp/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 19 | VERSION_NAME=1.0 20 | VERSION_CODE=1 21 | 22 | # Load propeties file located somewhere 23 | signing.properties=../../signing.properties 24 | 25 | # Use default gradle.properties to store keystore values 26 | STORE_FILE=../../android_demo_keystore.jks 27 | STORE_PASSWORD=androiddemo 28 | KEY_ALIAS=androiddemo 29 | KEY_PASSWORD=androiddemo -------------------------------------------------------------------------------- /AndroidDemoApp/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolasjafelle/AndroidDemo/eeb502f9904f6316b5aa1ac539fd37c27c9d74f9/AndroidDemoApp/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /AndroidDemoApp/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Nov 26 19:50:51 GMT-03:00 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 | -------------------------------------------------------------------------------- /AndroidDemoApp/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 | -------------------------------------------------------------------------------- /AndroidDemoApp/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 | -------------------------------------------------------------------------------- /AndroidDemoApp/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':App' 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | AndroidDemo 2 | =========== 3 | 4 | Demo app to show how to start an Android App. This demo shows: 5 | 6 | 1. How to create a Fragment and associate with Activity. 7 | 2. Make Asynchronous call using SafeAsyncTask. 8 | 3. Decouple SafeAsyncTask with Otto Bus. 9 | 4. Replace Roboguice for Guice 3.0. 10 | 5. Connect to Foursquare's API using Retrofit with OkHttp and parse Json with Gson. 11 | 6. Material Design with latest AppCompat library (version 22.1) and animations like Google+ and Google Play. It also use latest AppCompat Widgets. 12 | 7. Side Bar implementation. 13 | 8. RecyclerView implementation with onClickListener, View Types and OnScrollListener. 14 | 9. Use of GPS. 15 | 10. Persists data with Qachee. 16 | 11. Use of Square Otto to use an event bus. 17 | 12. Use of Proguard to reduce apk size. 18 | 13. Two ways to handle release keystore and it values to create a Release APK (ready for Google Play). 19 | 20 | It also handle HTTP errors in a generic way (see FoursquareAsyncTask.java) and use GPSTracker to handle Geo locations. 21 | 22 | **UPDATED TO WORK WITH MATERIAL DESIGN** 23 | 24 | Instructions 25 | ============ 26 | 27 | 1. Just Clone the git repo 28 | 2. Import the "AndroidDemoApp" project into your Android Studio IDE. 29 | 4. DONE 30 | 31 | 32 | System Requirements 33 | ============ 34 | 35 | 1. Android Studio 0.9.0 36 | 2. Gradle 2.1+ 37 | 3. Android Gradle plugin version 0.14.0 38 | 4. Minimun Android SDK 14 39 | 5. Target Android SDK 21 40 | 41 | 42 | Developed By 43 | ================ 44 | 45 | * Nicolas Jafelle - 46 | 47 | 48 | License 49 | ================ 50 | 51 | Copyright 2013 Nicolas Jafelle 52 | 53 | Licensed under the Apache License, Version 2.0 (the "License"); 54 | you may not use this file except in compliance with the License. 55 | You may obtain a copy of the License at 56 | 57 | http://www.apache.org/licenses/LICENSE-2.0 58 | 59 | Unless required by applicable law or agreed to in writing, software 60 | distributed under the License is distributed on an "AS IS" BASIS, 61 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 62 | See the License for the specific language governing permissions and 63 | limitations under the License. 64 | -------------------------------------------------------------------------------- /android_demo_keystore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolasjafelle/AndroidDemo/eeb502f9904f6316b5aa1ac539fd37c27c9d74f9/android_demo_keystore.jks -------------------------------------------------------------------------------- /signing.properties: -------------------------------------------------------------------------------- 1 | STORE_FILE=../../android_demo_keystore.jks 2 | STORE_PASSWORD=androiddemo 3 | KEY_ALIAS=androiddemo 4 | KEY_PASSWORD=androiddemo -------------------------------------------------------------------------------- /web_hi_res_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolasjafelle/AndroidDemo/eeb502f9904f6316b5aa1ac539fd37c27c9d74f9/web_hi_res_512.png --------------------------------------------------------------------------------