├── settings.gradle ├── app ├── libs │ ├── argument-1.0.3.jar │ ├── crittercism_v4_4_0_sdkonly.jar │ └── libGoogleAnalyticsServices.jar ├── src │ ├── main │ │ ├── ic_launcher-web.png │ │ ├── assets │ │ │ └── fonts │ │ │ │ ├── Roboto-Bold.ttf │ │ │ │ ├── Roboto-Light.ttf │ │ │ │ ├── Roboto-Thin.ttf │ │ │ │ ├── Roboto-Italic.ttf │ │ │ │ ├── Roboto-Regular.ttf │ │ │ │ └── Roboto-LightItalic.ttf │ │ ├── res │ │ │ ├── drawable-hdpi │ │ │ │ ├── ic_av_pause.png │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── placeholder.9.png │ │ │ │ ├── ic_av_play_active.png │ │ │ │ ├── ic_av_play_normal.png │ │ │ │ └── ic_user_placeholder.png │ │ │ ├── drawable-mdpi │ │ │ │ ├── ic_av_pause.png │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── placeholder.9.png │ │ │ │ ├── ic_av_play_active.png │ │ │ │ ├── ic_av_play_normal.png │ │ │ │ └── ic_user_placeholder.png │ │ │ ├── drawable-xhdpi │ │ │ │ ├── ic_av_pause.png │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── placeholder.9.png │ │ │ │ ├── ic_av_play_active.png │ │ │ │ ├── ic_av_play_normal.png │ │ │ │ └── ic_user_placeholder.png │ │ │ ├── drawable-xxhdpi │ │ │ │ ├── ic_av_pause.png │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_av_play_active.png │ │ │ │ └── ic_av_play_normal.png │ │ │ ├── values │ │ │ │ ├── tokens.xml │ │ │ │ ├── integers.xml │ │ │ │ ├── dimens.xml │ │ │ │ ├── analytics.xml │ │ │ │ ├── strings.xml │ │ │ │ ├── colors.xml │ │ │ │ └── styles.xml │ │ │ ├── values-land │ │ │ │ └── integers.xml │ │ │ ├── values-large │ │ │ │ ├── integers.xml │ │ │ │ ├── dimens.xml │ │ │ │ └── styles.xml │ │ │ ├── values-xlarge │ │ │ │ └── integers.xml │ │ │ ├── values-xlarge-land │ │ │ │ └── integers.xml │ │ │ ├── drawable │ │ │ │ ├── btn_play_inactive_normal.xml │ │ │ │ ├── btn_play_active.xml │ │ │ │ ├── btn_play_inactive.xml │ │ │ │ ├── btn_play_active_normal.xml │ │ │ │ ├── btn_play_active_pressed.xml │ │ │ │ ├── btn_play_inactive_pressed.xml │ │ │ │ └── seekbar_audio.xml │ │ │ └── layout │ │ │ │ ├── login.xml │ │ │ │ ├── newsfeed.xml │ │ │ │ ├── fragment_main.xml │ │ │ │ ├── audio.xml │ │ │ │ └── post.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── stiggpwnz │ │ │ │ └── vibes │ │ │ │ ├── fragments │ │ │ │ ├── NavigationFragment.java │ │ │ │ ├── BaseFragment.java │ │ │ │ ├── MainFragment.java │ │ │ │ ├── LoginFragment.java │ │ │ │ └── FeedFragment.java │ │ │ │ ├── vk │ │ │ │ ├── models │ │ │ │ │ ├── Unit.java │ │ │ │ │ ├── Result.java │ │ │ │ │ ├── Group.java │ │ │ │ │ ├── Error.java │ │ │ │ │ ├── Profile.java │ │ │ │ │ ├── Likes.java │ │ │ │ │ ├── Attachment.java │ │ │ │ │ ├── Feed.java │ │ │ │ │ ├── Audio.java │ │ │ │ │ ├── Photo.java │ │ │ │ │ └── Post.java │ │ │ │ ├── VKApi.java │ │ │ │ ├── VkOnSubscribe.java │ │ │ │ ├── VKontakte.java │ │ │ │ └── VKAuth.java │ │ │ │ ├── Injector.java │ │ │ │ ├── qualifiers │ │ │ │ └── CacheDir.java │ │ │ │ ├── util │ │ │ │ ├── CrashReportingTree.java │ │ │ │ ├── Persistence.java │ │ │ │ ├── Text.java │ │ │ │ ├── DiskUtils.java │ │ │ │ ├── JacksonSerializer.java │ │ │ │ ├── VKLinkedText.java │ │ │ │ └── Images.java │ │ │ │ ├── player │ │ │ │ ├── State.java │ │ │ │ ├── Event.java │ │ │ │ ├── UrlSource.java │ │ │ │ └── PlayerQueue.java │ │ │ │ ├── text │ │ │ │ ├── HashTagSpan.java │ │ │ │ ├── VKLinkSpan.java │ │ │ │ └── ReplaceTextSpan.java │ │ │ │ ├── db │ │ │ │ ├── models │ │ │ │ │ └── AudioUrl.java │ │ │ │ └── DatabaseHelper.java │ │ │ │ ├── adapters │ │ │ │ ├── InflaterAdapter.java │ │ │ │ └── FeedAdapter.java │ │ │ │ ├── widget │ │ │ │ ├── CustomDrawerLayout.java │ │ │ │ ├── AudioView.java │ │ │ │ ├── PhotoView.java │ │ │ │ └── PostView.java │ │ │ │ ├── Dagger.java │ │ │ │ ├── App.java │ │ │ │ ├── AppDaggerModule.java │ │ │ │ ├── MainActivity.java │ │ │ │ └── ActivityDaggerModule.java │ │ └── AndroidManifest.xml │ └── test │ │ └── java │ │ └── com │ │ └── stiggpwnz │ │ └── vibes │ │ ├── dagger │ │ └── ModulesTestModule.java │ │ ├── TestApp.java │ │ ├── AppTest.java │ │ ├── widget │ │ └── PostViewTest.java │ │ └── MainActivityTest.java ├── proguard-rules.txt └── build.gradle ├── .gitignore ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── ActionBarPullToRefresh ├── gradle.properties ├── AndroidManifest.xml ├── src │ └── uk │ │ └── co │ │ └── senab │ │ └── actionbarpulltorefresh │ │ └── library │ │ ├── sdk │ │ ├── CompatV16.java │ │ ├── CompatBase.java │ │ ├── Compat.java │ │ └── CompatV11.java │ │ ├── EnvironmentDelegate.java │ │ ├── viewdelegates │ │ ├── WebViewDelegate.java │ │ ├── ScrollYDelegate.java │ │ ├── ViewDelegate.java │ │ └── AbsListViewDelegate.java │ │ ├── listeners │ │ ├── OnRefreshListener.java │ │ └── HeaderViewListener.java │ │ ├── HeaderTransformer.java │ │ ├── InstanceCreationUtils.java │ │ ├── Options.java │ │ ├── ActionBarPullToRefresh.java │ │ └── PullToRefreshLayout.java ├── build.gradle ├── project.properties └── res │ ├── values │ ├── colors.xml │ ├── dimens.xml │ ├── ids.xml │ ├── styles.xml │ ├── pull_refresh_strings.xml │ └── attrs.xml │ ├── values-ru │ └── pull_refresh_strings.xml │ ├── values-zh │ └── pull_refresh_strings.xml │ ├── values-ar │ └── pull_refresh_strings.xml │ ├── values-fi │ └── pull_refresh_strings.xml │ ├── values-iw │ └── pull_refresh_strings.xml │ ├── values-ja │ └── pull_refresh_strings.xml │ ├── values-ko │ └── pull_refresh_strings.xml │ ├── values-he │ └── pull_refresh_strings.xml │ ├── values-lt │ └── pull_refresh_strings.xml │ ├── values-cs │ └── pull_refresh_strings.xml │ ├── values-it │ └── pull_refresh_strings.xml │ ├── values-nl │ └── pull_refresh_strings.xml │ ├── values-pl │ └── pull_refresh_strings.xml │ ├── values-pt │ └── pull_refresh_strings.xml │ ├── values-de │ └── pull_refresh_strings.xml │ ├── values-fr │ └── pull_refresh_strings.xml │ ├── values-sv │ └── pull_refresh_strings.xml │ ├── values-pt-rBR │ └── pull_refresh_strings.xml │ ├── values-sk │ └── pull_refresh_strings.xml │ ├── values-ro │ └── pull_refresh_strings.xml │ ├── values-es │ └── pull_refresh_strings.xml │ └── layout │ └── default_header.xml ├── README.md ├── .travis.yml ├── gradlew.bat └── gradlew /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | include ':ActionBarPullToRefresh' -------------------------------------------------------------------------------- /app/libs/argument-1.0.3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/libs/argument-1.0.3.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | .DS_Store 5 | .idea/ 6 | *.iml 7 | build/ -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/libs/crittercism_v4_4_0_sdkonly.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/libs/crittercism_v4_4_0_sdkonly.jar -------------------------------------------------------------------------------- /app/libs/libGoogleAnalyticsServices.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/libs/libGoogleAnalyticsServices.jar -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/assets/fonts/Roboto-Bold.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/assets/fonts/Roboto-Light.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/assets/fonts/Roboto-Thin.ttf -------------------------------------------------------------------------------- /ActionBarPullToRefresh/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=ActionBar-PullToRefresh Library 2 | POM_ARTIFACT_ID=library 3 | POM_PACKAGING=aar 4 | -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/assets/fonts/Roboto-Italic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/assets/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_av_pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/res/drawable-hdpi/ic_av_pause.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_av_pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/res/drawable-mdpi/ic_av_pause.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_av_pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/res/drawable-xhdpi/ic_av_pause.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/assets/fonts/Roboto-LightItalic.ttf -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/placeholder.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/res/drawable-hdpi/placeholder.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/placeholder.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/res/drawable-mdpi/placeholder.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/placeholder.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/res/drawable-xhdpi/placeholder.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_av_pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/res/drawable-xxhdpi/ic_av_pause.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_av_play_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/res/drawable-hdpi/ic_av_play_active.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_av_play_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/res/drawable-hdpi/ic_av_play_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_av_play_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/res/drawable-mdpi/ic_av_play_active.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_av_play_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/res/drawable-mdpi/ic_av_play_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_user_placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/res/drawable-hdpi/ic_user_placeholder.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_user_placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/res/drawable-mdpi/ic_user_placeholder.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_av_play_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/res/drawable-xhdpi/ic_av_play_active.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_av_play_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/res/drawable-xhdpi/ic_av_play_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_av_play_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/res/drawable-xxhdpi/ic_av_play_active.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_av_play_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/res/drawable-xxhdpi/ic_av_play_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_user_placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meoyawn/Vibes/HEAD/app/src/main/res/drawable-xhdpi/ic_user_placeholder.png -------------------------------------------------------------------------------- /app/src/main/res/values/tokens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | XXXX 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values-land/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 2 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values-large/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values-xlarge/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 2 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values-xlarge-land/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 3 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/fragments/NavigationFragment.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.fragments; 2 | 3 | public class NavigationFragment extends BaseFragment { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/res/values-large/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16dp 5 | 32dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/btn_play_inactive_normal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/vk/models/Unit.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.vk.models; 2 | 3 | public interface Unit { 4 | public int getId(); 5 | 6 | public String getName(); 7 | 8 | public String getProfilePic(); 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8dp 5 | 16dp 6 | 330dp 7 | 8 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Apr 10 15:27:10 PDT 2013 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=http\://services.gradle.org/distributions/gradle-1.11-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/analytics.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UA-XXXX-Y 5 | 6 | 7 | true 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/Injector.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | 6 | import dagger.ObjectGraph; 7 | 8 | /** 9 | * Created by adel on 16/03/14 10 | */ 11 | public interface Injector { 12 | public ObjectGraph getObjectGraph(); 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/btn_play_active.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/btn_play_inactive.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/src/uk/co/senab/actionbarpulltorefresh/library/sdk/CompatV16.java: -------------------------------------------------------------------------------- 1 | package uk.co.senab.actionbarpulltorefresh.library.sdk; 2 | 3 | import android.view.View; 4 | 5 | class CompatV16 { 6 | 7 | static void postOnAnimation(View view, Runnable runnable) { 8 | view.postOnAnimation(runnable); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/vk/models/Result.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.vk.models; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | 5 | import lombok.Data; 6 | 7 | public @Data class Result { 8 | @Nullable T response; 9 | @Nullable Error error; 10 | 11 | public boolean isError() { return error != null; } 12 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/btn_play_active_normal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/btn_play_active_pressed.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values-large/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/btn_play_inactive_pressed.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/qualifiers/CacheDir.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.qualifiers; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | import javax.inject.Qualifier; 7 | 8 | /** 9 | * Created by adel on 14/03/14 10 | */ 11 | @Qualifier 12 | @Retention(RetentionPolicy.RUNTIME) 13 | public @interface CacheDir { 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Vibes 3 | Email 4 | Password 5 | User profile image 6 | Image in post 7 | Play 8 | Login 9 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/src/uk/co/senab/actionbarpulltorefresh/library/sdk/CompatBase.java: -------------------------------------------------------------------------------- 1 | package uk.co.senab.actionbarpulltorefresh.library.sdk; 2 | 3 | import android.view.View; 4 | 5 | class CompatBase { 6 | 7 | static void setAlpha(View view, float alpha) { 8 | // NO-OP 9 | } 10 | 11 | static void postOnAnimation(View view, Runnable runnable) { 12 | view.postDelayed(runnable, 10l); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VK Vibes for Android 2 | 3 | ## VKontakte (vk.com) music player for Android with last.fm integration 4 | 5 | 6 | 7 | 8 | Get it on Google Play 10 | 11 | 12 | ### Happy Birthday Commit -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/util/CrashReportingTree.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.util; 2 | 3 | import com.crittercism.app.Crittercism; 4 | 5 | import timber.log.Timber; 6 | 7 | /** 8 | * Created by adelnizamutdinov on 03/03/2014 9 | */ 10 | public class CrashReportingTree extends Timber.HollowTree { 11 | @Override public void e(Throwable t, String message, Object... args) { 12 | Crittercism.logHandledException(t); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/player/State.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.player; 2 | 3 | /** 4 | * Created by adelnizamutdinov on 17/03/2014 5 | */ 6 | public enum State { 7 | EMPTY, 8 | GETTING_URL_TO_PREPARE, 9 | GETTING_URL_TO_EMPTY, 10 | PREPARING_TO_PLAY, 11 | PREPARING_TO_PAUSE, 12 | PLAYING, 13 | PAUSED, 14 | SEEKING_TO_PLAY, 15 | SEEKING_TO_PAUSE, 16 | RESETTING_TO_PREPARE, 17 | RESETTING_TO_EMPTY 18 | } 19 | -------------------------------------------------------------------------------- /app/src/test/java/com/stiggpwnz/vibes/dagger/ModulesTestModule.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.dagger; 2 | 3 | import com.squareup.okhttp.OkHttpClient; 4 | import com.squareup.picasso.Picasso; 5 | import com.stiggpwnz.vibes.fragments.MainFragment; 6 | 7 | import dagger.Module; 8 | 9 | /** 10 | * Created by adelnizamutdinov on 03/03/2014 11 | */ 12 | @Module(injects = {OkHttpClient.class, Picasso.class, MainFragment.class}) 13 | public class ModulesTestModule {} 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/text/HashTagSpan.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.text; 2 | 3 | import android.text.style.ClickableSpan; 4 | import android.view.View; 5 | 6 | import lombok.AllArgsConstructor; 7 | 8 | /** 9 | * Created by adel on 1/28/14 10 | */ 11 | @AllArgsConstructor(suppressConstructorProperties = true) 12 | public class HashTagSpan extends ClickableSpan { 13 | String hashTag; 14 | 15 | @Override public void onClick(View widget) { } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/db/models/AudioUrl.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.db.models; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import lombok.experimental.Accessors; 7 | 8 | /** 9 | * Created by adel on 15/03/14 10 | */ 11 | @Data 12 | @NoArgsConstructor 13 | @Accessors(chain = true) 14 | @AllArgsConstructor(suppressConstructorProperties = true) 15 | public class AudioUrl { 16 | Long _id; 17 | String url; 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/login.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/text/VKLinkSpan.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.text; 2 | 3 | import android.text.style.ClickableSpan; 4 | import android.view.View; 5 | 6 | import lombok.AllArgsConstructor; 7 | import timber.log.Timber; 8 | 9 | /** 10 | * Created by adel on 1/28/14 11 | */ 12 | @AllArgsConstructor(suppressConstructorProperties = true) 13 | public class VKLinkSpan extends ClickableSpan { 14 | String path; 15 | 16 | @Override 17 | public void onClick(View widget) { 18 | Timber.d("clicked on %s group", path); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/adapters/InflaterAdapter.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.adapters; 2 | 3 | import android.view.LayoutInflater; 4 | import android.widget.BaseAdapter; 5 | 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import lombok.RequiredArgsConstructor; 9 | 10 | @RequiredArgsConstructor(suppressConstructorProperties = true) 11 | public abstract class InflaterAdapter extends BaseAdapter { 12 | protected @NotNull final LayoutInflater inflater; 13 | 14 | @Override public long getItemId(int position) { return position; } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/test/java/com/stiggpwnz/vibes/TestApp.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes; 2 | 3 | import com.stiggpwnz.vibes.dagger.ModulesTestModule; 4 | 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.util.Arrays; 8 | import java.util.List; 9 | 10 | /** 11 | * Created by adelnizamutdinov on 03/03/2014 12 | */ 13 | public class TestApp extends App { 14 | @NotNull @Override protected List getModules() { 15 | List modules = super.getModules(); 16 | modules.addAll(Arrays.asList(new ModulesTestModule())); 17 | return modules; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #AAAAAA 4 | #EEEEEE 5 | #BBBBBB 6 | #999999 7 | #444444 8 | #444 9 | #fff 10 | #33b5e5 11 | #0099cc 12 | #000 13 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'android-library' 2 | 3 | dependencies { 4 | compile 'com.github.castorflex.smoothprogressbar:library:+' 5 | } 6 | 7 | android { 8 | compileSdkVersion 19 9 | buildToolsVersion '19.0.3' 10 | 11 | defaultConfig { 12 | // This should be 14, but is 7 because extra-abc/extra-abs depend on this library 13 | minSdkVersion 7 14 | targetSdkVersion 19 15 | } 16 | 17 | sourceSets { 18 | main { 19 | manifest.srcFile 'AndroidManifest.xml' 20 | java.srcDirs = ['src'] 21 | res.srcDirs = ['res'] 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/player/Event.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.player; 2 | 3 | /** 4 | * Created by adelnizamutdinov on 17/03/2014 5 | */ 6 | public enum Event { 7 | PLAY_PAUSE, 8 | NEXT, 9 | PREV, 10 | NTH, 11 | GOT_URL, 12 | PREPARED, 13 | COMPLETED, 14 | RESET_COMPLETE, 15 | SEEK_COMPLETE, 16 | ERROR; 17 | 18 | int position; 19 | Exception exception; 20 | String url; 21 | 22 | public int getPosition() { return position; } 23 | 24 | public Event setException(Exception exception) { 25 | this.exception = exception; 26 | return this; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/util/Persistence.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.util; 2 | 3 | import com.stiggpwnz.vibes.player.PlayerQueue; 4 | 5 | import de.devland.esperandro.annotations.SharedPreferences; 6 | 7 | @SharedPreferences 8 | public interface Persistence { 9 | public String accessToken(); 10 | 11 | public void accessToken(String accessToken); 12 | 13 | public long expiresIn(); 14 | 15 | public void expiresIn(long expiresIn); 16 | 17 | public int userId(); 18 | 19 | public void userId(int userId); 20 | 21 | public PlayerQueue.Repeat repeat(); 22 | 23 | public void repeat(PlayerQueue.Repeat repeat); 24 | } -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/vk/models/Group.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.vk.models; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import java.io.Serializable; 6 | 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | 10 | @Data 11 | @EqualsAndHashCode(of = "gid", doNotUseGetters = true) 12 | public class Group implements Unit, Serializable { 13 | int gid; 14 | String name; 15 | @JsonProperty("photo_medium") String photoMedium; 16 | 17 | @Override public int getId() { return -gid; } 18 | 19 | @Override public String getProfilePic() { return photoMedium; } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/util/Text.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.util; 2 | 3 | import android.text.Html; 4 | 5 | import com.fasterxml.jackson.core.JsonParser; 6 | import com.fasterxml.jackson.databind.DeserializationContext; 7 | import com.fasterxml.jackson.databind.JsonDeserializer; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * Created by adel on 11/9/13 13 | */ 14 | public class Text extends JsonDeserializer { 15 | @Override 16 | public String deserialize(JsonParser parser, DeserializationContext context) throws IOException { 17 | return Html.fromHtml(parser.getValueAsString()).toString(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/vk/models/Error.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.vk.models; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import java.io.Serializable; 6 | 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | 10 | @Data 11 | @EqualsAndHashCode(callSuper = false) 12 | public class Error extends Exception implements Serializable { 13 | static final int USER_AUTHORIZATION_FAILED = 5; 14 | 15 | @JsonProperty("error_code") int errorCode; 16 | @JsonProperty("error_msg") String errorMsg; 17 | 18 | public boolean isAuthError() { return errorCode == USER_AUTHORIZATION_FAILED; } 19 | } -------------------------------------------------------------------------------- /ActionBarPullToRefresh/project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-18 15 | android.library=true 16 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 15 | -------------------------------------------------------------------------------- /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 /Applications/Android Studio.app/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 | #} -------------------------------------------------------------------------------- /app/src/main/res/drawable/seekbar_audio.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: oraclejdk7 3 | 4 | before_install: 5 | # Install base Android SDK 6 | - sudo apt-get update -qq 7 | - if [ `uname -m` = x86_64 ]; then sudo apt-get install -qq --force-yes libgd2-xpm ia32-libs ia32-libs-multiarch > /dev/null; fi 8 | - wget http://dl.google.com/android/android-sdk_r22.6-linux.tgz 9 | - tar xzf android-sdk_r22.6-linux.tgz 10 | - export ANDROID_HOME=$PWD/android-sdk-linux 11 | - export PATH=${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools 12 | - export JAVA7_HOME=/usr/lib/jvm/java-7-oracle 13 | - export JAVA8_HOME=/usr/lib/jvm/java-8-oracle 14 | 15 | # Install required components. 16 | - echo yes | android update sdk -a -t platform-tools,build-tools-19.0.3,android-19,extra-android-support,extra-android-m2repository --no-ui --force > /dev/null -------------------------------------------------------------------------------- /app/src/main/res/layout/newsfeed.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | #FF33B5E5 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/vk/models/Profile.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.vk.models; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import java.io.Serializable; 6 | 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | 10 | @Data 11 | @EqualsAndHashCode(of = "uid") 12 | public class Profile implements Unit, Serializable { 13 | int uid; 14 | @JsonProperty("first_name") String firstName; 15 | @JsonProperty("last_name") String lastName; 16 | @JsonProperty("photo_medium_rec") String photoMediumRec; 17 | 18 | @Override public int getId() { return uid; } 19 | 20 | @Override public String getName() { return String.format("%s %s", firstName, lastName); } 21 | 22 | @Override public String getProfilePic() { return photoMediumRec; } 23 | } 24 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 4dp 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/widget/CustomDrawerLayout.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.widget; 2 | 3 | import android.content.Context; 4 | import android.support.v4.widget.DrawerLayout; 5 | import android.util.AttributeSet; 6 | 7 | /** 8 | * Created by adelnizamutdinov on 05/12/2013 9 | */ 10 | public class CustomDrawerLayout extends DrawerLayout { 11 | public CustomDrawerLayout(Context context, AttributeSet attrs) { super(context, attrs); } 12 | 13 | @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 14 | widthMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY); 15 | heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY); 16 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 17 | } 18 | } -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/player/UrlSource.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.player; 2 | 3 | import com.stiggpwnz.vibes.db.DatabaseHelper; 4 | import com.stiggpwnz.vibes.vk.VKontakte; 5 | import com.stiggpwnz.vibes.vk.models.Audio; 6 | 7 | import lombok.RequiredArgsConstructor; 8 | 9 | /** 10 | * Created by adelnizamutdinov on 17/03/2014 11 | */ 12 | @RequiredArgsConstructor(suppressConstructorProperties = true) 13 | public class UrlSource { 14 | final VKontakte vKontakte; 15 | final DatabaseHelper databaseHelper; 16 | 17 | public String getSavedUrl(Audio audio) { 18 | return databaseHelper.getUrl(audio); 19 | } 20 | 21 | public String tryToGetAndSaveUrl(Audio audio) { 22 | String url = getSavedUrl(audio); 23 | if (url == null) { 24 | url = vKontakte.getUrl(audio); 25 | databaseHelper.putUrl(audio); 26 | } 27 | return url; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 22 | 23 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/src/uk/co/senab/actionbarpulltorefresh/library/sdk/Compat.java: -------------------------------------------------------------------------------- 1 | package uk.co.senab.actionbarpulltorefresh.library.sdk; 2 | 3 | import android.os.Build; 4 | import android.view.View; 5 | 6 | public class Compat { 7 | 8 | public static void setAlpha(View view, float alpha) { 9 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 10 | CompatV11.setAlpha(view, alpha); 11 | } else { 12 | CompatBase.setAlpha(view, alpha); 13 | } 14 | } 15 | 16 | public static void postOnAnimation(View view, Runnable runnable) { 17 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 18 | CompatV16.postOnAnimation(view, runnable); 19 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 20 | CompatV11.postOnAnimation(view, runnable); 21 | } else { 22 | CompatBase.postOnAnimation(view, runnable); 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/Dagger.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | 6 | import dagger.ObjectGraph; 7 | 8 | /** 9 | * Created by adel on 16/03/14 10 | */ 11 | public class Dagger { 12 | public static ObjectGraph getObjectGraph(Context context) { 13 | return ((Injector) context).getObjectGraph(); 14 | } 15 | 16 | public static void inject(Context context, Object object) { 17 | getObjectGraph(context).inject(object); 18 | } 19 | 20 | public static void inject(Context context) { inject(context, context); } 21 | 22 | public static void inject(View view) { inject(view.getContext(), view); } 23 | 24 | public static void inject(android.app.Fragment fragment) { 25 | inject(fragment.getActivity(), fragment); 26 | } 27 | 28 | public static void inject(android.support.v4.app.Fragment fragment) { 29 | inject(fragment.getActivity(), fragment); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/values-ru/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | потяните 19 | отпустите 20 | обновление 21 | 22 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/values-zh/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 下拉刷新… 20 | 放开以刷新… 21 | 正在载入… 22 | 23 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/values-ar/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | اسحب للتحديث… 19 | اترك للتحديث… 20 | تحميل… 21 | 22 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/values-fi/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | Päivitä vetämällä alas… 19 | Päivitä vapauttamalla… 20 | Päivitetään… 21 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/values-iw/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | משוך לרענון… 19 | שחרר לרענון… 20 | טוען… 21 | 22 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/values-ja/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 画面を引っ張って… 20 | 指を離して更新… 21 | 読み込み中… 22 | 23 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/values-ko/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 당겨서 새로 고침… 20 | 놓아서 새로 고침… 21 | 로드 중… 22 | 23 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/values/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | Pull to refresh… 20 | Release to refresh… 21 | Loading… 22 | 23 | 24 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/values-he/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | משוך לרענון… 20 | שחרר לרענון… 21 | טוען… 22 | 23 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/values-lt/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | Traukite, kad atnaujinti… 19 | Paleiskite, kad atnaujinti… 20 | Kraunama… 21 | 22 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/values-cs/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | Tažením aktualizujete… 19 | Uvolněním aktualizujete… 20 | Načítání… 21 | 22 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/values-it/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | Tira per aggiornare… 19 | Rilascia per aggionare… 20 | Caricamento… 21 | 22 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/values-nl/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | Sleep om te vernieuwen… 19 | Loslaten om te vernieuwen… 20 | Laden… 21 | 22 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/values-pl/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | Pociągnij, aby odświeżyć… 19 | Puść, aby odświeżyć… 20 | Wczytywanie… 21 | 22 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/values-pt/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | Puxe para atualizar… 19 | Liberação para atualizar… 20 | A carregar… 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/values-de/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | Ziehen zum Aktualisieren… 19 | Loslassen zum Aktualisieren… 20 | Laden… 21 | 22 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/values-fr/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | Tirez pour rafraîchir… 19 | Relâcher pour rafraîchir… 20 | Chargement… 21 | 22 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/values-sv/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | Dra nedåt om du vill uppdatera 20 | Släpp om du vill uppdatera 21 | Uppdaterar… 22 | 23 | 24 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/values-pt-rBR/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | Puxe para atualizar… 20 | Libere para atualizar… 21 | Carregando… 22 | 23 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/values-sk/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | Potiahnite pre načítanie... 19 | Uvoľnite pre načítanie... 20 | Načítavanie... 21 | 22 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/values-ro/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | Trage pentru a reîmprospăta… 19 | Eliberează pentru a reîmprospăta… 20 | Încărcare… 21 | 22 | -------------------------------------------------------------------------------- /app/src/test/java/com/stiggpwnz/vibes/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.robolectric.Robolectric; 6 | import org.robolectric.RobolectricTestRunner; 7 | import org.robolectric.annotation.Config; 8 | 9 | import static junit.framework.Assert.assertEquals; 10 | import static junit.framework.Assert.assertNotNull; 11 | 12 | /** 13 | * Created by adelnizamutdinov on 03/03/2014 14 | */ 15 | @Config(emulateSdk = 18) 16 | @RunWith(RobolectricTestRunner.class) 17 | public class AppTest { 18 | @Test public void testOnCreate() throws Exception { 19 | App app = (App) Robolectric.application; 20 | assertNotNull(app.getObjectGraph()); 21 | } 22 | 23 | @Test public void testGetModules() throws Exception { 24 | App app = (App) Robolectric.application; 25 | assertEquals(2, app.getModules().size()); 26 | } 27 | 28 | @Test public void testAppClass() { 29 | assertEquals("TestApp", Robolectric.application.getClass().getSimpleName()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/values-es/pull_refresh_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | Desliza el dedo hacia abajo para actualizar. 20 | Soltar para actualizar… 21 | Cargando… 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/util/DiskUtils.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.util; 2 | 3 | import android.content.Context; 4 | import android.os.Environment; 5 | 6 | import java.io.File; 7 | 8 | public class DiskUtils { 9 | public static File cacheDirNamed(Context context, String name) { 10 | File target = new File(getCacheDir(context), name); 11 | if (!target.exists()) { 12 | target.mkdirs(); 13 | } 14 | return target; 15 | } 16 | 17 | public static File getCacheDir(Context context) { 18 | return externalIsMounted() ? 19 | context.getExternalCacheDir() : 20 | context.getCacheDir(); 21 | } 22 | 23 | private static boolean externalIsMounted() { 24 | return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); 25 | } 26 | 27 | public static File getFilesDir(Context context) { 28 | if (externalIsMounted()) { 29 | return context.getExternalFilesDir(null); 30 | } 31 | return context.getFilesDir(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/vk/VKApi.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.vk; 2 | 3 | import com.stiggpwnz.vibes.vk.models.Audio; 4 | import com.stiggpwnz.vibes.vk.models.Feed; 5 | import com.stiggpwnz.vibes.vk.models.Result; 6 | 7 | import retrofit.http.GET; 8 | import retrofit.http.Query; 9 | 10 | public interface VKApi { 11 | public static final String SERVER = "https://api.vk.com/method"; 12 | 13 | @GET("/newsfeed.get?filters=post") 14 | public Result getNewsFeed(@Query("offset") int offset); 15 | 16 | @GET("/wall.get?extended=1") 17 | public Result getWall(@Query("owner_id") int ownerId, 18 | @Query("filter") String filter, 19 | @Query("offset") int offset); 20 | 21 | @GET("/audio.get") 22 | public Result getAudios(); 23 | 24 | @GET("/audio.search?sort=2&count=300") 25 | public Result searchAudios(@Query("q") String query); 26 | 27 | @GET("/audio.getById") 28 | public Result getAudioURL(@Query("audios") String query); 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/text/ReplaceTextSpan.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.text; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.text.style.ReplacementSpan; 8 | 9 | import lombok.AllArgsConstructor; 10 | 11 | /** 12 | * Created by adel on 1/28/14 13 | */ 14 | @AllArgsConstructor(suppressConstructorProperties = true) 15 | public class ReplaceTextSpan extends ReplacementSpan { 16 | Context context; 17 | String replacement; 18 | 19 | @Override 20 | public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) { 21 | return Math.round(paint.measureText(replacement)); 22 | } 23 | 24 | @Override 25 | public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) { 26 | // TODO get from context 27 | paint.setColor(Color.parseColor("#33b5e5")); 28 | paint.setUnderlineText(true); 29 | canvas.drawText(replacement, x, y, paint); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/vk/models/Likes.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.vk.models; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public class Likes { 6 | 7 | private int count; 8 | private int userLikes; 9 | private int canLike; 10 | private int can_publish; 11 | 12 | public int getCount() { return count; } 13 | 14 | public void setCount(int count) { this.count = count; } 15 | 16 | @JsonProperty("user_likes") 17 | public int getUserLikes() { return userLikes; } 18 | 19 | @JsonProperty("user_likes") 20 | public void setUserLikes(int userLikes) { this.userLikes = userLikes; } 21 | 22 | @JsonProperty("can_like") 23 | public int getCanLike() { return canLike; } 24 | 25 | @JsonProperty("can_like") 26 | public void setCanLike(int canLike) { this.canLike = canLike; } 27 | 28 | @JsonProperty("can_publish") 29 | public int getCan_publish() { return can_publish; } 30 | 31 | @JsonProperty("can_publish") 32 | public void setCan_publish(int can_publish) { this.can_publish = can_publish; } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/fragments/BaseFragment.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.fragments; 2 | 3 | import android.app.ActionBar; 4 | import android.os.Bundle; 5 | import android.support.v4.app.Fragment; 6 | import android.view.View; 7 | 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | import butterknife.ButterKnife; 11 | 12 | public abstract class BaseFragment extends Fragment { 13 | protected void configure(@NotNull ActionBar actionBar) { } 14 | 15 | @Override public void onViewCreated(View view, Bundle savedInstanceState) { 16 | super.onViewCreated(view, savedInstanceState); 17 | ButterKnife.inject(this, view); 18 | } 19 | 20 | @Override public void onActivityCreated(Bundle savedInstanceState) { 21 | super.onActivityCreated(savedInstanceState); 22 | if (getActivity().getActionBar() != null) { 23 | configure(getActivity().getActionBar()); 24 | } 25 | } 26 | 27 | @Override public void onDestroyView() { 28 | ButterKnife.reset(this); 29 | super.onDestroyView(); 30 | } 31 | 32 | public boolean onBackPressed() { return false; } 33 | } 34 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/src/uk/co/senab/actionbarpulltorefresh/library/sdk/CompatV11.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Chris Banes 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package uk.co.senab.actionbarpulltorefresh.library.sdk; 18 | 19 | import android.animation.ValueAnimator; 20 | import android.view.View; 21 | 22 | class CompatV11 { 23 | 24 | static void setAlpha(View view, float alpha) { 25 | view.setAlpha(alpha); 26 | } 27 | 28 | static void postOnAnimation(View view, Runnable runnable) { 29 | view.postDelayed(runnable, ValueAnimator.getFrameDelay()); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/vk/VkOnSubscribe.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.vk; 2 | 3 | import com.stiggpwnz.vibes.vk.models.Result; 4 | 5 | import lombok.RequiredArgsConstructor; 6 | import rx.Observable; 7 | import rx.Subscriber; 8 | import rx.functions.Func0; 9 | 10 | /** 11 | * Created by adel on 08/03/14 12 | */ 13 | @RequiredArgsConstructor(suppressConstructorProperties = true) 14 | public class VkOnSubscribe implements Observable.OnSubscribe { 15 | final VKAuth vkAuth; 16 | final Func0> func; 17 | 18 | @Override public void call(Subscriber subscriber) { 19 | Result result = func.call(); 20 | if (result.getError() != null) { 21 | if (result.getError().isAuthError()) { 22 | vkAuth.resetAuth(); 23 | if (!subscriber.isUnsubscribed()) { 24 | call(subscriber); 25 | } 26 | } else { 27 | subscriber.onError(result.getError()); 28 | } 29 | } else { 30 | subscriber.onNext(result.getResponse()); 31 | subscriber.onCompleted(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/src/uk/co/senab/actionbarpulltorefresh/library/EnvironmentDelegate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Chris Banes 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package uk.co.senab.actionbarpulltorefresh.library; 18 | 19 | import android.app.Activity; 20 | import android.content.Context; 21 | 22 | /** 23 | * This is used to provide platform and environment specific functionality for the Attacher. 24 | */ 25 | public interface EnvironmentDelegate { 26 | 27 | /** 28 | * @return Context which should be used for inflating the header layout 29 | */ 30 | public Context getContextForInflater(Activity activity); 31 | 32 | } 33 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/src/uk/co/senab/actionbarpulltorefresh/library/viewdelegates/WebViewDelegate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Chris Banes 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package uk.co.senab.actionbarpulltorefresh.library.viewdelegates; 18 | 19 | import android.view.View; 20 | import android.webkit.WebView; 21 | 22 | /** 23 | * FIXME 24 | */ 25 | public class WebViewDelegate implements ViewDelegate { 26 | 27 | public static final Class[] SUPPORTED_VIEW_CLASSES = { WebView.class }; 28 | 29 | @Override 30 | public boolean isReadyForPull(View view, float x, float y) { 31 | return view.getScrollY() <= 0; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/src/uk/co/senab/actionbarpulltorefresh/library/listeners/OnRefreshListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Chris Banes 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package uk.co.senab.actionbarpulltorefresh.library.listeners; 18 | 19 | import android.view.View; 20 | 21 | /** 22 | * Simple Listener to listen for any callbacks to Refresh. 23 | */ 24 | public interface OnRefreshListener { 25 | /** 26 | * Called when the user has initiated a refresh by pulling. 27 | * 28 | * @param view 29 | * - View which the user has started the refresh from. 30 | */ 31 | public void onRefreshStarted(View view); 32 | } 33 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/src/uk/co/senab/actionbarpulltorefresh/library/viewdelegates/ScrollYDelegate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Chris Banes 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package uk.co.senab.actionbarpulltorefresh.library.viewdelegates; 18 | 19 | import android.view.View; 20 | import android.widget.ScrollView; 21 | 22 | /** 23 | * FIXME 24 | */ 25 | public class ScrollYDelegate implements ViewDelegate { 26 | 27 | public static final Class[] SUPPORTED_VIEW_CLASSES = { ScrollView.class }; 28 | 29 | @Override 30 | public boolean isReadyForPull(View view, float x, float y) { 31 | return view.getScrollY() <= 0; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/adapters/FeedAdapter.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.adapters; 2 | 3 | import android.view.LayoutInflater; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | 7 | import com.stiggpwnz.vibes.R; 8 | import com.stiggpwnz.vibes.vk.models.Post; 9 | import com.stiggpwnz.vibes.widget.PostView; 10 | 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | public class FeedAdapter extends InflaterAdapter { 14 | final @NotNull Post[] posts; 15 | 16 | public FeedAdapter(LayoutInflater layoutInflater, @NotNull Post[] posts) { 17 | super(layoutInflater); 18 | this.posts = posts; 19 | } 20 | 21 | @Override public int getCount() { return posts.length; } 22 | 23 | @Override public Post getItem(int position) { return posts[position]; } 24 | 25 | @Override public View getView(int position, View convertView, ViewGroup parent) { 26 | if (convertView == null) { 27 | convertView = inflater.inflate(R.layout.post, parent, false); 28 | } 29 | 30 | @NotNull PostView postView = (PostView) convertView; 31 | postView.draw(getItem(position)); 32 | 33 | return convertView; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/vk/models/Attachment.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.vk.models; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.databind.DeserializationContext; 5 | import com.fasterxml.jackson.databind.JsonDeserializer; 6 | 7 | import java.io.IOException; 8 | import java.io.Serializable; 9 | 10 | import lombok.Data; 11 | 12 | @Data 13 | public class Attachment implements Serializable { 14 | Type type; 15 | Photo photo; 16 | Audio audio; 17 | 18 | public static enum Type {AUDIO, PHOTO, UNKNOWN} 19 | 20 | public static class TypeDeserializer extends JsonDeserializer { 21 | @Override 22 | public Type deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { 23 | switch (jsonParser.getValueAsString()) { 24 | case "audio": 25 | case "AUDIO": 26 | return Type.AUDIO; 27 | 28 | case "photo": 29 | case "PHOTO": 30 | return Type.PHOTO; 31 | 32 | default: 33 | return Type.UNKNOWN; 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/App.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes; 2 | 3 | import android.app.Application; 4 | import android.os.StrictMode; 5 | 6 | import com.stiggpwnz.vibes.util.CrashReportingTree; 7 | 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Arrays; 12 | import java.util.List; 13 | 14 | import dagger.ObjectGraph; 15 | import lombok.Getter; 16 | import timber.log.Timber; 17 | 18 | /** 19 | * Created by adelnizamutdinov on 03/03/2014 20 | */ 21 | public class App extends Application implements Injector { 22 | @Getter ObjectGraph objectGraph; 23 | 24 | @Override public void onCreate() { 25 | super.onCreate(); 26 | StrictMode.enableDefaults(); 27 | // Crittercism.initialize(getApplicationContext(), getString(R.string.crittercism_app_id)); 28 | Timber.plant(BuildConfig.DEBUG ? new Timber.DebugTree() : new CrashReportingTree()); 29 | objectGraph = ObjectGraph.create(getModules().toArray()); 30 | } 31 | 32 | // we create a list containing the MyModule Injector module 33 | // later we can add any other modules to it (for testing) 34 | @NotNull protected List getModules() { 35 | return new ArrayList<>(Arrays.asList(new AppDaggerModule(this))); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /app/src/test/java/com/stiggpwnz/vibes/widget/PostViewTest.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.widget; 2 | 3 | import android.text.SpannableString; 4 | 5 | import com.stiggpwnz.vibes.text.VKLinkSpan; 6 | 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | import org.robolectric.Robolectric; 11 | import org.robolectric.RobolectricTestRunner; 12 | import org.robolectric.annotation.Config; 13 | 14 | /** 15 | * Created by adel on 08/03/14 16 | */ 17 | @Config(emulateSdk = 18) 18 | @RunWith(RobolectricTestRunner.class) 19 | public class PostViewTest { 20 | @Test public void testOnFinishInflate() throws Exception { 21 | 22 | } 23 | 24 | @Test public void testShowUser() throws Exception { 25 | 26 | } 27 | 28 | @Test public void testLinkify() throws Exception { 29 | SpannableString linkify = PostView.linkify(Robolectric.application, "[club48302822|Oomkah Dee] – житель Санкт-Петербурга, коллекционер, ценитель и исследователь музыки, известный своей уникальной серией миксов Collage. В миксе для Full of Nothing собраны “дорогие сердцу современные композиторы, имена которых, к сожалению, довольно редко встречаются в программках”.
Скачать/трэклист: http://fullofnothing.net/mix-by-oomkah-dee/"); 30 | Assert.assertEquals(1, linkify.getSpans(0, linkify.length(), VKLinkSpan.class).length); 31 | } 32 | 33 | @Test public void testSetPost() throws Exception { 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/vk/models/Feed.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.vk.models; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | import java.io.Serializable; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.Set; 12 | 13 | import lombok.Data; 14 | 15 | public @Data class Feed implements Serializable { 16 | @NotNull Post[] items; 17 | @Nullable Set profiles; 18 | @Nullable Set groups; 19 | @JsonProperty("new_offset") int newOffset; 20 | 21 | public void removeFirstItem() { 22 | Post[] src = items; 23 | items = new Post[src.length - 1]; 24 | System.arraycopy(src, 1, items, 0, items.length); 25 | } 26 | 27 | public void filterAudios() { 28 | List posts = new ArrayList<>(items.length); 29 | for (Post post : items) { 30 | if (post.hasAudios()) { 31 | post.setUnitFrom(this); 32 | post.setRepostedUnitFrom(this); 33 | post.calculateMedia(); 34 | posts.add(post); 35 | } 36 | } 37 | items = posts.toArray(new Post[posts.size()]); 38 | } 39 | 40 | public void setWall(Post[] wall) { items = wall; } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/util/JacksonSerializer.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.util; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.core.type.TypeReference; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | 7 | import java.io.Serializable; 8 | 9 | import de.devland.esperandro.serialization.Serializer; 10 | import lombok.RequiredArgsConstructor; 11 | 12 | @RequiredArgsConstructor(suppressConstructorProperties = true) 13 | public class JacksonSerializer implements Serializer { 14 | final ObjectMapper objectMapper; 15 | 16 | public String serialize(Object object) { 17 | try { 18 | return objectMapper.writeValueAsString(object); 19 | } catch (JsonProcessingException e) { 20 | throw new RuntimeException(e); 21 | } 22 | } 23 | 24 | @Override public String serialize(Serializable serializable) { 25 | return serialize((Object) serializable); 26 | } 27 | 28 | @Override public T deserialize(String json, Class clazz) { 29 | try { 30 | return objectMapper.readValue(json, clazz); 31 | } catch (Exception e) { 32 | throw new RuntimeException(e); 33 | } 34 | } 35 | 36 | public T deserialize(String json, TypeReference typeReference) { 37 | try { 38 | return objectMapper.readValue(json, typeReference); 39 | } catch (Exception e) { 40 | throw new RuntimeException(e); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/src/uk/co/senab/actionbarpulltorefresh/library/viewdelegates/ViewDelegate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Chris Banes 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package uk.co.senab.actionbarpulltorefresh.library.viewdelegates; 18 | 19 | import android.view.View; 20 | 21 | /** 22 | * ViewDelegates are what are used to de-couple the Attacher from the different types of 23 | * scrollable views. 24 | */ 25 | public interface ViewDelegate { 26 | 27 | /** 28 | * Allows you to provide support for View which do not have built-in 29 | * support. In this method you should cast view to it's 30 | * native class, and check if it is scrolled to the top. 31 | * 32 | * @param view 33 | * The view which has should be checked against. 34 | * @param x The X co-ordinate of the touch event 35 | * @param y The Y co-ordinate of the touch event 36 | * @return true if view is scrolled to the top. 37 | */ 38 | public boolean isReadyForPull(View view, float x, float y); 39 | 40 | } 41 | -------------------------------------------------------------------------------- /app/src/test/java/com/stiggpwnz/vibes/MainActivityTest.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes; 2 | 3 | 4 | import android.support.v4.app.Fragment; 5 | import android.support.v4.app.FragmentManager; 6 | 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | import org.robolectric.Robolectric; 11 | import org.robolectric.RobolectricTestRunner; 12 | import org.robolectric.annotation.Config; 13 | import org.robolectric.tester.android.view.TestMenuItem; 14 | import org.robolectric.util.ActivityController; 15 | 16 | import static org.junit.Assert.assertTrue; 17 | 18 | /** 19 | * Created by adelnizamutdinov on 03/03/2014 20 | */ 21 | @Config(emulateSdk = 18) 22 | @RunWith(RobolectricTestRunner.class) 23 | public class MainActivityTest { 24 | ActivityController activityController; 25 | 26 | @Before public void setUp() throws Exception { 27 | activityController = Robolectric.buildActivity(MainActivity.class); 28 | } 29 | 30 | @Test public void testOnOptionsItemSelected() throws Exception { 31 | MainActivity mainActivity = activityController.create().get(); 32 | FragmentManager fragmentManager = mainActivity.getSupportFragmentManager(); 33 | fragmentManager.beginTransaction() 34 | .replace(android.R.id.content, new Fragment()) 35 | .addToBackStack(null) 36 | .commit(); 37 | int oldStack = fragmentManager.getBackStackEntryCount(); 38 | 39 | assertTrue(mainActivity.onOptionsItemSelected(new TestMenuItem(android.R.id.home))); 40 | assertTrue(fragmentManager.getBackStackEntryCount() < oldStack); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/res/layout/default_header.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 21 | 22 | 27 | 28 | 34 | 35 | 36 | 37 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/src/uk/co/senab/actionbarpulltorefresh/library/listeners/HeaderViewListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Chris Banes 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package uk.co.senab.actionbarpulltorefresh.library.listeners; 18 | 19 | import android.view.View; 20 | 21 | public interface HeaderViewListener { 22 | /** 23 | * The state when the header view is completely visible. 24 | */ 25 | public static int STATE_VISIBLE = 0; 26 | 27 | /** 28 | * The state when the header view is minimized. By default this means 29 | * that the progress bar is still visible, but the rest of the view is 30 | * hidden, showing the Action Bar behind. 31 | *

32 | * This will not be called in header minimization is disabled. 33 | */ 34 | public static int STATE_MINIMIZED = 1; 35 | 36 | /** 37 | * The state when the header view is completely hidden. 38 | */ 39 | public static int STATE_HIDDEN = 2; 40 | 41 | /** 42 | * Called when the visibility state of the Header View has changed. 43 | * 44 | * @param headerView 45 | * HeaderView who's state has changed. 46 | * @param state 47 | * The new state. One of {@link #STATE_VISIBLE}, 48 | * {@link #STATE_MINIMIZED} and {@link #STATE_HIDDEN} 49 | */ 50 | public void onStateChanged(View headerView, int state); 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/AppDaggerModule.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes; 2 | 3 | import android.content.Context; 4 | import android.os.Environment; 5 | 6 | import com.squareup.okhttp.HttpResponseCache; 7 | import com.squareup.okhttp.OkHttpClient; 8 | import com.squareup.picasso.LruCache; 9 | import com.squareup.picasso.OkHttpDownloader; 10 | import com.squareup.picasso.Picasso; 11 | import com.stiggpwnz.vibes.qualifiers.CacheDir; 12 | 13 | import java.io.File; 14 | import java.io.IOException; 15 | 16 | import javax.inject.Singleton; 17 | 18 | import dagger.Module; 19 | import dagger.Provides; 20 | 21 | /** 22 | * Created by adelnizamutdinov on 03/03/2014 23 | */ 24 | @Module(library = true) 25 | public class AppDaggerModule { 26 | final Context context; 27 | 28 | public AppDaggerModule(Context context) { this.context = context; } 29 | 30 | @Provides Context provideContext() { return context; } 31 | 32 | @Provides @CacheDir File provideCacheDir(Context context) { 33 | boolean mounted = Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); 34 | return mounted ? context.getExternalCacheDir() : context.getCacheDir(); 35 | } 36 | 37 | @Provides @Singleton OkHttpClient provideOkHttpClient(@CacheDir File cacheDir) { 38 | OkHttpClient okHttpClient = new OkHttpClient(); 39 | try { 40 | okHttpClient.setResponseCache(new HttpResponseCache(cacheDir, 20 * 1024 * 1024)); 41 | } catch (IOException ignored) { 42 | } 43 | return okHttpClient; 44 | } 45 | 46 | @Provides @Singleton LruCache provideLruCache(Context context) { return new LruCache(context); } 47 | 48 | @Provides @Singleton 49 | Picasso providePicasso(Context context, OkHttpClient okHttpClient, LruCache lruCache) { 50 | return new Picasso.Builder(context) 51 | .downloader(new OkHttpDownloader(okHttpClient)) 52 | .memoryCache(lruCache) 53 | .build(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/widget/AudioView.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.widget; 2 | 3 | import android.content.Context; 4 | import android.media.MediaPlayer; 5 | import android.util.AttributeSet; 6 | import android.widget.ImageView; 7 | import android.widget.LinearLayout; 8 | import android.widget.SeekBar; 9 | import android.widget.TextView; 10 | 11 | import com.stiggpwnz.vibes.Dagger; 12 | import com.stiggpwnz.vibes.R; 13 | import com.stiggpwnz.vibes.vk.models.Audio; 14 | 15 | import org.jetbrains.annotations.NotNull; 16 | import org.jetbrains.annotations.Nullable; 17 | 18 | import butterknife.ButterKnife; 19 | import butterknife.InjectView; 20 | import butterknife.OnClick; 21 | 22 | public class AudioView extends LinearLayout implements MediaPlayer.OnBufferingUpdateListener { 23 | // @Inject Lazy playerLazy; 24 | 25 | @InjectView(R.id.imageButtonPlay) ImageView play; 26 | @InjectView(R.id.seekBarAudio) SeekBar seekBar; 27 | @InjectView(R.id.artist_audio) TextView artist; 28 | @InjectView(R.id.title_audio) TextView title; 29 | 30 | @Nullable Audio audio; 31 | 32 | public AudioView(Context context, AttributeSet attrs) { super(context, attrs); } 33 | 34 | @Override protected void onFinishInflate() { 35 | if (!isInEditMode()) { 36 | Dagger.inject(this); 37 | ButterKnife.inject(this); 38 | } 39 | } 40 | 41 | public void draw(@NotNull Audio audio) { 42 | this.audio = audio; 43 | 44 | artist.setText(audio.getArtist()); 45 | title.setText(audio.getTitle()); 46 | } 47 | 48 | @OnClick(R.id.imageButtonPlay) void play() { 49 | play.setImageResource(R.drawable.btn_play_active); 50 | 51 | if (getResources() != null) { 52 | artist.setTextColor(getResources().getColor(R.color.text_white)); 53 | title.setTextColor(getResources().getColor(R.color.text_white)); 54 | } 55 | } 56 | 57 | @Override public void onBufferingUpdate(MediaPlayer mp, int percent) { 58 | seekBar.setSecondaryProgress(percent * seekBar.getMax()); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/fragments/MainFragment.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.fragments; 2 | 3 | import android.app.ActionBar; 4 | import android.os.Bundle; 5 | import android.support.v4.widget.DrawerLayout; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | 10 | import com.stiggpwnz.vibes.R; 11 | 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | import butterknife.InjectView; 15 | 16 | /** 17 | * Created by adelnizamutdinov on 03/03/2014 18 | */ 19 | public class MainFragment extends BaseFragment { 20 | @InjectView(R.id.drawer_layout) DrawerLayout drawerLayout; 21 | 22 | @Override public void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | setRetainInstance(true); 25 | setHasOptionsMenu(true); 26 | } 27 | 28 | @Override protected void configure(@NotNull ActionBar actionBar) { 29 | actionBar.setHomeButtonEnabled(true); 30 | actionBar.setDisplayHomeAsUpEnabled(true); 31 | actionBar.setTitle(R.string.app_name); 32 | } 33 | 34 | @Override 35 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 36 | return inflater.inflate(R.layout.fragment_main, container, false); 37 | } 38 | 39 | @Override public void onViewCreated(View view, Bundle savedInstanceState) { 40 | super.onViewCreated(view, savedInstanceState); 41 | if (getChildFragmentManager().findFragmentById(R.id.left_drawer) == null) { 42 | getChildFragmentManager().beginTransaction() 43 | .replace(R.id.left_drawer, new NavigationFragment()) 44 | .replace(R.id.content_frame, FeedFragmentBuilder.newFeedFragment(0)) 45 | .commit(); 46 | } 47 | } 48 | 49 | @Override public boolean onBackPressed() { 50 | if (getChildFragmentManager().getBackStackEntryCount() > 0) { 51 | getChildFragmentManager().popBackStack(); 52 | return true; 53 | } 54 | return super.onBackPressed(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/util/VKLinkedText.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.util; 2 | 3 | import android.content.Context; 4 | import android.content.ContextWrapper; 5 | import android.text.Html; 6 | import android.text.Spannable; 7 | import android.text.Spanned; 8 | 9 | import com.fasterxml.jackson.core.JsonParser; 10 | import com.fasterxml.jackson.databind.DeserializationContext; 11 | import com.fasterxml.jackson.databind.JsonDeserializer; 12 | import com.stiggpwnz.vibes.text.HashTagSpan; 13 | import com.stiggpwnz.vibes.text.ReplaceTextSpan; 14 | import com.stiggpwnz.vibes.text.VKLinkSpan; 15 | 16 | import java.io.IOException; 17 | import java.util.regex.Matcher; 18 | import java.util.regex.Pattern; 19 | 20 | /** 21 | * Created by adel on 15/03/14 22 | */ 23 | public class VKLinkedText extends JsonDeserializer { 24 | static final Pattern HASH_TAG = Pattern.compile("#[a-zA-Z][\\w@-]*"); 25 | static final Pattern VK_UNIT = Pattern.compile("\\[([^\\[\\|]+?)\\|([^\\]]+?)\\]"); 26 | 27 | static Spannable linkify(Context context, Spannable spannable) { 28 | Matcher hasTags = HASH_TAG.matcher(spannable); 29 | while (hasTags.find()) { 30 | int start = hasTags.start(); 31 | int end = hasTags.end(); 32 | spannable.setSpan(new HashTagSpan(hasTags.group()), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 33 | } 34 | 35 | Matcher units = VK_UNIT.matcher(spannable); 36 | while (units.find()) { 37 | int start = units.start(); 38 | int end = units.end(); 39 | spannable.setSpan(new VKLinkSpan(units.group(1)), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 40 | spannable.setSpan(new ReplaceTextSpan(context, units.group(2)), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 41 | } 42 | return spannable; 43 | } 44 | 45 | @Override 46 | public Spannable deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { 47 | Spannable spannable = (Spannable) Html.fromHtml(jp.getValueAsString()); 48 | return linkify(new ContextWrapper(null), spannable); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/vk/VKontakte.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.vk; 2 | 3 | import com.stiggpwnz.vibes.db.DatabaseHelper; 4 | import com.stiggpwnz.vibes.vk.models.Audio; 5 | import com.stiggpwnz.vibes.vk.models.Feed; 6 | import com.stiggpwnz.vibes.vk.models.Result; 7 | 8 | import lombok.RequiredArgsConstructor; 9 | import rx.Observable; 10 | import rx.functions.Func0; 11 | 12 | /** 13 | * Created by adel on 11/8/13 14 | */ 15 | @RequiredArgsConstructor(suppressConstructorProperties = true) 16 | public class VKontakte { 17 | final VKApi vkApi; 18 | final VKAuth vkAuth; 19 | final DatabaseHelper databaseHelper; 20 | 21 | public Observable getAudios() { 22 | return vkObservableFrom(vkApi::getAudios); 23 | } 24 | 25 | Observable

43 | * You should not change the visibility of the header 44 | * view. 45 | */ 46 | public void onReset() {} 47 | 48 | /** 49 | * Called the user has pulled on the scrollable view. 50 | * 51 | * @param percentagePulled value between 0.0f and 1.0f depending on how far the 52 | * user has pulled. 53 | */ 54 | public void onPulled(float percentagePulled) {} 55 | 56 | /** 57 | * Called when a refresh has begun. Theoretically this call is similar 58 | * to that provided from {@link uk.co.senab.actionbarpulltorefresh.library.listeners.OnRefreshListener} but is more suitable 59 | * for header view updates. 60 | */ 61 | public void onRefreshStarted() {} 62 | 63 | /** 64 | * Called when a refresh can be initiated when the user ends the touch 65 | * event. This is only called when {@link Options#refreshOnUp} is set to 66 | * true. 67 | */ 68 | public void onReleaseToRefresh() {} 69 | 70 | /** 71 | * Called when the current refresh has taken longer than the time 72 | * specified in {@link Options#refreshMinimizeDelay}. 73 | */ 74 | public void onRefreshMinimized() {} 75 | 76 | /** 77 | * Called when the Header View should be made visible, usually with an animation. 78 | * 79 | * @return true if the visibility has changed. 80 | */ 81 | public abstract boolean showHeaderView(); 82 | 83 | /** 84 | * Called when the Header View should be made invisible, usually with an animation. 85 | * 86 | * @return true if the visibility has changed. 87 | */ 88 | public abstract boolean hideHeaderView(); 89 | 90 | /** 91 | * Called when the Activity's configuration has changed. 92 | * 93 | * @param activity The {@link android.app.Activity} that the header view is attached to. 94 | * @param newConfig New configuration. 95 | * 96 | * @see android.app.Activity#onConfigurationChanged(android.content.res.Configuration) 97 | */ 98 | public void onConfigurationChanged(Activity activity, Configuration newConfig) {} 99 | } 100 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.support.v4.app.Fragment; 6 | import android.support.v4.app.FragmentActivity; 7 | import android.view.MenuItem; 8 | import android.webkit.CookieManager; 9 | 10 | import com.github.mttkay.memento.Memento; 11 | import com.github.mttkay.memento.MementoCallbacks; 12 | import com.github.mttkay.memento.Retain; 13 | import com.stiggpwnz.vibes.fragments.BaseFragment; 14 | import com.stiggpwnz.vibes.fragments.LoginFragment; 15 | import com.stiggpwnz.vibes.fragments.MainFragment; 16 | 17 | import org.jetbrains.annotations.NotNull; 18 | import org.jetbrains.annotations.Nullable; 19 | 20 | import javax.inject.Inject; 21 | 22 | import dagger.Module; 23 | import dagger.ObjectGraph; 24 | import dagger.Provides; 25 | import de.keyboardsurfer.android.widget.crouton.Crouton; 26 | import lombok.Getter; 27 | import uk.co.chrisjenx.calligraphy.CalligraphyConfig; 28 | import uk.co.chrisjenx.calligraphy.CalligraphyContextWrapper; 29 | 30 | public class MainActivity extends FragmentActivity implements Injector, MementoCallbacks { 31 | static { 32 | CalligraphyConfig.initDefault("fonts/Roboto-Regular.ttf"); 33 | } 34 | 35 | @Inject CookieManager cookieManager; 36 | @Nullable @Getter ObjectGraph objectGraph; 37 | @Retain @NotNull ObjectGraph retainedObjectGraph; 38 | 39 | @Module(addsTo = AppDaggerModule.class, 40 | overrides = true) 41 | static class ContextModule { 42 | final Context context; 43 | 44 | ContextModule(Context context) { this.context = context; } 45 | 46 | @Provides Context provideContext() { return context; } 47 | } 48 | 49 | 50 | @Override protected void onCreate(Bundle savedInstanceState) { 51 | super.onCreate(savedInstanceState); 52 | Memento.retain(this); 53 | objectGraph = retainedObjectGraph.plus(new ContextModule(this)); 54 | Dagger.inject(this); 55 | 56 | if (savedInstanceState == null) { 57 | Fragment fragment = cookieManager.getCookie("vk.com") == null ? 58 | new LoginFragment() : 59 | new MainFragment(); 60 | getSupportFragmentManager().beginTransaction() 61 | .add(android.R.id.content, fragment) 62 | .commit(); 63 | } 64 | } 65 | 66 | 67 | @Override public void onLaunch() { 68 | retainedObjectGraph = Dagger.getObjectGraph(getApplication()).plus(new ActivityDaggerModule()); 69 | } 70 | 71 | @Override public void onBackPressed() { 72 | for (Fragment fragment : getSupportFragmentManager().getFragments()) { 73 | if (fragment instanceof BaseFragment) { 74 | if (((BaseFragment) fragment).onBackPressed()) { 75 | return; 76 | } 77 | } 78 | } 79 | super.onBackPressed(); 80 | } 81 | 82 | @Override public boolean onOptionsItemSelected(MenuItem item) { 83 | switch (item.getItemId()) { 84 | case android.R.id.home: 85 | getSupportFragmentManager().popBackStack(); 86 | return true; 87 | default: 88 | return super.onOptionsItemSelected(item); 89 | } 90 | } 91 | 92 | // @Override public void onStart() { 93 | // super.onStart(); 94 | // EasyTracker.getInstance(this).activityStart(this); 95 | // } 96 | // 97 | // @Override public void onStop() { 98 | // super.onStop(); 99 | // EasyTracker.getInstance(this).activityStop(this); 100 | // } 101 | 102 | @Override 103 | protected void attachBaseContext(Context newBase) { 104 | super.attachBaseContext(new CalligraphyContextWrapper(newBase)); 105 | } 106 | 107 | @Override 108 | protected void onDestroy() { 109 | Crouton.cancelAllCroutons(); 110 | super.onDestroy(); 111 | } 112 | } -------------------------------------------------------------------------------- /ActionBarPullToRefresh/src/uk/co/senab/actionbarpulltorefresh/library/InstanceCreationUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Chris Banes 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package uk.co.senab.actionbarpulltorefresh.library; 18 | 19 | import android.content.Context; 20 | import android.util.Log; 21 | import android.view.View; 22 | 23 | import java.lang.reflect.Constructor; 24 | import java.util.HashMap; 25 | import java.util.Map; 26 | import java.util.Set; 27 | 28 | import uk.co.senab.actionbarpulltorefresh.library.viewdelegates.AbsListViewDelegate; 29 | import uk.co.senab.actionbarpulltorefresh.library.viewdelegates.ScrollYDelegate; 30 | import uk.co.senab.actionbarpulltorefresh.library.viewdelegates.ViewDelegate; 31 | import uk.co.senab.actionbarpulltorefresh.library.viewdelegates.WebViewDelegate; 32 | 33 | class InstanceCreationUtils { 34 | 35 | private static final String LOG_TAG = "InstanceCreationUtils"; 36 | 37 | private static final Class[] VIEW_DELEGATE_CONSTRUCTOR_SIGNATURE = new Class[]{}; 38 | private static final Class[] TRANSFORMER_CONSTRUCTOR_SIGNATURE = new Class[]{}; 39 | 40 | private static final HashMap BUILT_IN_DELEGATES; 41 | static { 42 | BUILT_IN_DELEGATES = new HashMap(); 43 | addBuiltinDelegates(AbsListViewDelegate.SUPPORTED_VIEW_CLASSES, AbsListViewDelegate.class); 44 | addBuiltinDelegates(ScrollYDelegate.SUPPORTED_VIEW_CLASSES, ScrollYDelegate.class); 45 | addBuiltinDelegates(WebViewDelegate.SUPPORTED_VIEW_CLASSES, WebViewDelegate.class); 46 | } 47 | 48 | private static void addBuiltinDelegates(Class[] supportedViews, Class delegateClass) { 49 | for (int i = 0, z = supportedViews.length; i< z ; i++) { 50 | BUILT_IN_DELEGATES.put(supportedViews[i], delegateClass); 51 | } 52 | } 53 | 54 | static ViewDelegate getBuiltInViewDelegate(final View view) { 55 | final Set> entries = BUILT_IN_DELEGATES.entrySet(); 56 | for (final Map.Entry entry : entries) { 57 | if (entry.getKey().isInstance(view)) { 58 | return InstanceCreationUtils.newInstance(view.getContext(), 59 | entry.getValue(), VIEW_DELEGATE_CONSTRUCTOR_SIGNATURE); 60 | } 61 | } 62 | return null; 63 | } 64 | 65 | static T instantiateViewDelegate(Context context, String className) { 66 | try { 67 | Class clazz = context.getClassLoader().loadClass(className); 68 | return newInstance(context, clazz, VIEW_DELEGATE_CONSTRUCTOR_SIGNATURE); 69 | } catch (Exception e) { 70 | Log.w(LOG_TAG, "Cannot instantiate class: " + className, e); 71 | } 72 | return null; 73 | } 74 | 75 | static T instantiateTransformer(Context context, String className) { 76 | try { 77 | Class clazz = context.getClassLoader().loadClass(className); 78 | return newInstance(context, clazz, TRANSFORMER_CONSTRUCTOR_SIGNATURE); 79 | } catch (Exception e) { 80 | Log.w(LOG_TAG, "Cannot instantiate class: " + className, e); 81 | } 82 | return null; 83 | } 84 | 85 | private static T newInstance(Context context, Class clazz, Class[] constructorSig, 86 | Object... arguments) { 87 | try { 88 | Constructor constructor = clazz.getConstructor(constructorSig); 89 | return (T) constructor.newInstance(arguments); 90 | } catch (Exception e) { 91 | Log.w(LOG_TAG, "Cannot instantiate class: " + clazz.getName(), e); 92 | } 93 | return null; 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'android' 2 | apply plugin: 'android-apt' 3 | apply plugin: 'retrolambda' 4 | apply plugin: 'android-test' 5 | 6 | android { 7 | compileSdkVersion 19 8 | buildToolsVersion "19.0.3" 9 | 10 | defaultConfig { 11 | minSdkVersion 14 12 | targetSdkVersion 19 13 | versionCode 1 14 | versionName "3.0.0" 15 | 16 | renderscriptTargetApi 19 17 | renderscriptSupportMode true 18 | renderscriptNdkMode false 19 | } 20 | 21 | compileOptions { 22 | sourceCompatibility JavaVersion.VERSION_1_8 23 | targetCompatibility JavaVersion.VERSION_1_8 24 | } 25 | 26 | packagingOptions { 27 | exclude 'META-INF/LICENSE' 28 | exclude 'META-INF/NOTICE' 29 | exclude 'META-INF/services/javax.annotation.processing.Processor' 30 | } 31 | 32 | lintOptions { 33 | enable 'IconExpectedSize', 'EasterEgg', 'SelectableText', 'StopShip', 'TypographyQuotes', 'UnusedIds' 34 | disable 'InvalidPackage' 35 | } 36 | 37 | // signingConfigs { 38 | // release { 39 | // storeFile file('XXXX') 40 | // storePassword 'XXXX' 41 | // keyAlias 'XXXX' 42 | // keyPassword 'XXXX' 43 | // } 44 | // } 45 | // 46 | // buildTypes { 47 | // release { 48 | // signingConfig signingConfigs.release 49 | // applicationVariants.all { variant -> 50 | // def file = variant.outputFile 51 | // variant.outputFile = new File(file.parent, file.name.replace(".apk", "-" + defaultConfig.versionName + ".apk")) 52 | // } 53 | // } 54 | // } 55 | 56 | sourceSets { 57 | main { 58 | androidTest.setRoot('src/test') 59 | } 60 | } 61 | } 62 | 63 | retrolambda { 64 | compile "net.orfjackal.retrolambda:retrolambda:1.+" 65 | jdk System.getenv("JAVA8_HOME") 66 | oldJdk System.getenv("JAVA7_HOME") 67 | javaVersion JavaVersion.VERSION_1_7 68 | } 69 | 70 | repositories { 71 | // robolectric 2.3-SNAPSHOT 72 | maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } 73 | } 74 | 75 | dependencies { 76 | compile 'com.netflix.rxjava:rxjava-android:0.17.0' // FRP 77 | compile project(':ActionBarPullToRefresh') // pull to refresh 78 | compile 'uk.co.chrisjenx.calligraphy:calligraphy:+' // custom fonts 79 | compile 'com.etsy.android.grid:library:+' // staggered grid 80 | 81 | compile 'com.jakewharton:butterknife:4.+' // view injection 82 | compile 'com.jakewharton.timber:timber:2.+' // logging 83 | 84 | compile 'com.squareup.dagger:dagger:1.+' // DI 85 | apt 'com.squareup.dagger:dagger-compiler:1.+' 86 | compile 'com.squareup.picasso:picasso:2.+' // image loading 87 | compile 'com.squareup.okhttp:okhttp:1.+' // HTTP client 88 | compile('com.squareup.retrofit:converter-jackson:1.+') { //rest client 89 | exclude group: 'com.google.code.gson' 90 | } 91 | compile 'com.intellij:annotations:12.+' // @Nullable @NotNull 92 | 93 | compile 'org.projectlombok:lombok:1.+' // get set generation 94 | 95 | compile 'com.fasterxml.jackson.core:jackson-databind:2.+' // json parsing 96 | compile 'nl.qbusict:cupboard:1.+' // sql boilerplate 97 | 98 | compile files('libs/argument-1.0.3.jar') // fragment arguments boilerplate 99 | compile files('libs/crittercism_v4_4_0_sdkonly.jar') // crash reporting 100 | compile files('libs/libGoogleAnalyticsServices.jar') // analytics 101 | 102 | compile('de.keyboardsurfer.android.widget:crouton:+') { 103 | exclude group: 'com.google.android' 104 | } 105 | 106 | apt 'de.devland.esperandro:esperandro:1.+' 107 | compile 'de.devland.esperandro:esperandro-api:1.+' 108 | 109 | compile 'org.squirrelframework:squirrel-foundation:+' // ??? 110 | compile 'au.com.datasymphony:EasyFlow:1.3.+' 111 | 112 | compile 'com.github.mttkay.memento:memento:0.+' // retaining objects in Activity 113 | apt 'com.github.mttkay.memento:memento-processor:0.+' 114 | 115 | 116 | testCompile 'com.squareup.dagger:dagger-compiler:1.+' 117 | testCompile 'junit:junit:4.+' 118 | testCompile 'org.mockito:mockito-all:1.+' 119 | testCompile 'org.robolectric:robolectric:2.3-SNAPSHOT' 120 | testCompile 'com.squareup:fest-android:1.+' 121 | 122 | androidTestCompile 'junit:junit:4.+' 123 | androidTestCompile 'org.mockito:mockito-all:1.+' 124 | androidTestCompile 'org.robolectric:robolectric:2.3-SNAPSHOT' 125 | androidTestCompile 'com.squareup:fest-android:1.+' 126 | } -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/fragments/LoginFragment.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.fragments; 2 | 3 | import android.app.ActionBar; 4 | import android.graphics.Bitmap; 5 | import android.os.Bundle; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.webkit.CookieManager; 10 | import android.webkit.CookieSyncManager; 11 | import android.webkit.WebView; 12 | import android.webkit.WebViewClient; 13 | 14 | import com.stiggpwnz.vibes.Dagger; 15 | import com.stiggpwnz.vibes.R; 16 | import com.stiggpwnz.vibes.util.Persistence; 17 | import com.stiggpwnz.vibes.vk.VKAuth; 18 | 19 | import org.jetbrains.annotations.NotNull; 20 | import org.jetbrains.annotations.Nullable; 21 | 22 | import javax.inject.Inject; 23 | 24 | import butterknife.InjectView; 25 | import rx.Subscription; 26 | import rx.android.observables.AndroidObservable; 27 | import rx.schedulers.Schedulers; 28 | import rx.subjects.PublishSubject; 29 | import uk.co.senab.actionbarpulltorefresh.library.ActionBarPullToRefresh; 30 | import uk.co.senab.actionbarpulltorefresh.library.PullToRefreshLayout; 31 | 32 | public class LoginFragment extends BaseFragment { 33 | @Inject Persistence persistence; 34 | @Inject CookieManager cookieManager; 35 | @Inject CookieSyncManager cookieSyncManager; 36 | @Inject VKAuth vkAuth; 37 | 38 | @InjectView(R.id.ptr_layout) PullToRefreshLayout pullToRefreshLayout; 39 | @InjectView(R.id.webview_login) WebView webView; 40 | 41 | @NotNull final PublishSubject urls = PublishSubject.create(); 42 | @NotNull Subscription urlSubscription; 43 | 44 | @Override public void onCreate(Bundle savedInstanceState) { 45 | super.onCreate(savedInstanceState); 46 | Dagger.inject(this); 47 | } 48 | 49 | @Override protected void configure(@NotNull ActionBar actionBar) { 50 | actionBar.setHomeButtonEnabled(false); 51 | actionBar.setDisplayHomeAsUpEnabled(false); 52 | actionBar.setTitle(getString(R.string.login)); 53 | } 54 | 55 | @Override 56 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 57 | return inflater.inflate(R.layout.login, container, false); 58 | } 59 | 60 | @Override public void onViewCreated(@NotNull View view, @Nullable Bundle savedInstanceState) { 61 | super.onViewCreated(view, savedInstanceState); 62 | ActionBarPullToRefresh.from(getActivity()) 63 | .allChildrenArePullable() 64 | .setup(pullToRefreshLayout); 65 | 66 | webView.setWebViewClient(new WebViewClient() { 67 | @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { 68 | urls.onNext(url); 69 | if (pullToRefreshLayout != null) { 70 | pullToRefreshLayout.setRefreshing(true); 71 | } 72 | } 73 | 74 | @Override public void onPageFinished(WebView view, String url) { 75 | if (pullToRefreshLayout != null) { 76 | pullToRefreshLayout.setRefreshComplete(); 77 | } 78 | } 79 | }); 80 | 81 | urlSubscription = AndroidObservable.fromFragment(this, urls 82 | .filter(s -> s.startsWith(VKAuth.REDIRECT_URL)) 83 | .flatMap(s -> vkAuth.saveAccessToken(s, System.currentTimeMillis()) 84 | .subscribeOn(Schedulers.io()))) 85 | .subscribe(s -> { 86 | getFragmentManager().beginTransaction() 87 | .replace(android.R.id.content, new MainFragment()) 88 | .commit(); 89 | }, e -> loadInitialUrl()); 90 | 91 | if (savedInstanceState == null) { 92 | loadInitialUrl(); 93 | } else { 94 | webView.restoreState(savedInstanceState); 95 | } 96 | } 97 | 98 | 99 | void loadInitialUrl() { 100 | webView.stopLoading(); 101 | webView.loadUrl(VKAuth.authUrl()); 102 | if (pullToRefreshLayout != null) { 103 | pullToRefreshLayout.setRefreshing(true); 104 | } 105 | } 106 | 107 | @Override public void onSaveInstanceState(Bundle outState) { 108 | super.onSaveInstanceState(outState); 109 | webView.saveState(outState); 110 | } 111 | 112 | @Override public void onDestroy() { 113 | urlSubscription.unsubscribe(); 114 | if (persistence.accessToken() == null) { 115 | cookieManager.removeAllCookie(); 116 | } 117 | cookieSyncManager.sync(); 118 | super.onDestroy(); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/widget/PostView.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.widget; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.widget.ImageView; 8 | import android.widget.LinearLayout; 9 | import android.widget.TextView; 10 | 11 | import com.squareup.picasso.Picasso; 12 | import com.stiggpwnz.vibes.Dagger; 13 | import com.stiggpwnz.vibes.R; 14 | import com.stiggpwnz.vibes.vk.models.Post; 15 | import com.stiggpwnz.vibes.vk.models.Unit; 16 | 17 | import org.jetbrains.annotations.NotNull; 18 | 19 | import javax.inject.Inject; 20 | 21 | import butterknife.ButterKnife; 22 | import butterknife.InjectView; 23 | import butterknife.OnClick; 24 | import dagger.Lazy; 25 | import rx.subjects.PublishSubject; 26 | 27 | /** 28 | * Created by adelnizamutdinov on 28/01/2014 29 | */ 30 | public class PostView extends LinearLayout { 31 | @Inject Lazy picassoLazy; 32 | @Inject PublishSubject unitClicks; 33 | 34 | @InjectView(R.id.original_user_icon) ImageView profilePic; 35 | @InjectView(R.id.original_user_name) TextView user; 36 | @InjectView(R.id.original_time) TextView time; 37 | @InjectView(R.id.original_text) TextView upperText; 38 | @InjectView(R.id.image_item) PhotoView image; 39 | @InjectView(R.id.repost_user_icon) ImageView repostProfilePic; 40 | @InjectView(R.id.repost_user_name) TextView repostUserName; 41 | @InjectView(R.id.repost_time) TextView repostTime; 42 | @InjectView(R.id.repost_text) TextView lowerText; 43 | @InjectView(R.id.copy_user_container) View copyContainer; 44 | 45 | final AudioView[] audioViews = new AudioView[10]; 46 | 47 | @NotNull Post post; 48 | 49 | public PostView(Context context, AttributeSet attrs) { super(context, attrs); } 50 | 51 | @Override protected void onFinishInflate() { 52 | Context context = getContext(); 53 | if (!isInEditMode()) { 54 | Dagger.inject(this); 55 | ButterKnife.inject(this); 56 | } 57 | 58 | if (context != null) { 59 | LayoutInflater inflater = LayoutInflater.from(context); 60 | for (int i = 0; i < audioViews.length; i++) { 61 | audioViews[i] = (AudioView) inflater.inflate(R.layout.audio, this, false); 62 | addView(audioViews[i]); 63 | } 64 | } 65 | } 66 | 67 | @OnClick({R.id.original_user_icon, R.id.original_user_name}) void showUser() { 68 | unitClicks.onNext(post.getOriginalUnit()); 69 | } 70 | 71 | @OnClick({R.id.repost_user_icon, R.id.repost_user_name}) void showRepostUser() { 72 | unitClicks.onNext(post.getRepostedUnit()); 73 | } 74 | 75 | 76 | void drawPostTextOn(@NotNull TextView textView) { 77 | if (post.hasOriginalText()) { 78 | textView.setText(post.getText()); 79 | textView.setVisibility(VISIBLE); 80 | } else { 81 | textView.setVisibility(GONE); 82 | } 83 | } 84 | 85 | public void draw(@NotNull Post post) { 86 | this.post = post; 87 | 88 | picassoLazy.get().load(post.getOriginalUnit().getProfilePic()) 89 | .placeholder(R.drawable.ic_user_placeholder) 90 | .into(profilePic); 91 | 92 | user.setText(post.getOriginalUnit().getName()); 93 | time.setText(post.relativeTimeString()); 94 | 95 | if (post.getRepostedUnit() != null) { 96 | if (post.hasRepostedText()) { 97 | upperText.setText(post.getCopyText()); 98 | upperText.setVisibility(VISIBLE); 99 | } else { 100 | upperText.setVisibility(GONE); 101 | } 102 | 103 | picassoLazy.get().load(post.getRepostedUnit().getProfilePic()) 104 | .placeholder(R.drawable.ic_user_placeholder) 105 | .into(repostProfilePic); 106 | repostUserName.setText(post.getRepostedUnit().getName()); 107 | repostTime.setText(post.repostRelativeTimeString()); 108 | 109 | drawPostTextOn(lowerText); 110 | 111 | copyContainer.setVisibility(VISIBLE); 112 | } else { 113 | copyContainer.setVisibility(GONE); 114 | 115 | drawPostTextOn(upperText); 116 | } 117 | 118 | image.setPhoto(post.getFirstPhoto()); 119 | 120 | for (int i = 0; i < post.getAudios().length; i++) { 121 | audioViews[i].draw(post.getAudios()[i]); 122 | } 123 | for (int i = post.getAudios().length; i < audioViews.length; i++) { 124 | audioViews[i].setVisibility(GONE); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/ActivityDaggerModule.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.webkit.CookieManager; 6 | import android.webkit.CookieSyncManager; 7 | 8 | import com.fasterxml.jackson.databind.DeserializationFeature; 9 | import com.fasterxml.jackson.databind.ObjectMapper; 10 | import com.fasterxml.jackson.databind.module.SimpleModule; 11 | import com.squareup.okhttp.OkHttpClient; 12 | import com.stiggpwnz.vibes.db.DatabaseHelper; 13 | import com.stiggpwnz.vibes.fragments.FeedFragment; 14 | import com.stiggpwnz.vibes.fragments.LoginFragment; 15 | import com.stiggpwnz.vibes.fragments.MainFragment; 16 | import com.stiggpwnz.vibes.fragments.NavigationFragment; 17 | import com.stiggpwnz.vibes.util.JacksonSerializer; 18 | import com.stiggpwnz.vibes.util.Persistence; 19 | import com.stiggpwnz.vibes.vk.VKApi; 20 | import com.stiggpwnz.vibes.vk.VKAuth; 21 | import com.stiggpwnz.vibes.vk.VKontakte; 22 | import com.stiggpwnz.vibes.vk.models.Attachment; 23 | import com.stiggpwnz.vibes.vk.models.Unit; 24 | import com.stiggpwnz.vibes.widget.AudioView; 25 | import com.stiggpwnz.vibes.widget.PhotoView; 26 | import com.stiggpwnz.vibes.widget.PostView; 27 | 28 | import javax.inject.Singleton; 29 | 30 | import dagger.Module; 31 | import dagger.Provides; 32 | import de.devland.esperandro.Esperandro; 33 | import retrofit.RestAdapter; 34 | import retrofit.client.OkClient; 35 | import retrofit.converter.Converter; 36 | import retrofit.converter.JacksonConverter; 37 | import rx.subjects.PublishSubject; 38 | import timber.log.Timber; 39 | 40 | /** 41 | * Created by adelnizamutdinov on 03/03/2014 42 | */ 43 | @Module(addsTo = AppDaggerModule.class, 44 | injects = { 45 | MainActivity.class, 46 | LoginFragment.class, 47 | MainFragment.class, 48 | NavigationFragment.class, 49 | AudioView.class, 50 | 51 | FeedFragment.class, 52 | PostView.class, 53 | PhotoView.class 54 | }) 55 | public class ActivityDaggerModule { 56 | /* 57 | * ACTIVITY 58 | */ 59 | 60 | @Provides @Singleton ObjectMapper provideObjectMapper(Context context) { 61 | ObjectMapper objectMapper = new ObjectMapper(); 62 | objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 63 | 64 | SimpleModule simpleModule = new SimpleModule(""); 65 | simpleModule.addDeserializer(Attachment.Type.class, new Attachment.TypeDeserializer()); 66 | objectMapper.registerModule(simpleModule); 67 | 68 | return objectMapper; 69 | } 70 | 71 | @Provides @Singleton CookieSyncManager provideCookieSyncManager(Context context) { 72 | return CookieSyncManager.createInstance(context); 73 | } 74 | 75 | @Provides 76 | CookieManager provideCookieManager(@SuppressWarnings("UnusedParameters") CookieSyncManager cookieSyncManager) { 77 | return CookieManager.getInstance(); 78 | } 79 | 80 | @Provides @Singleton JacksonSerializer provideJacksonSerializer(ObjectMapper objectMapper) { 81 | return new JacksonSerializer(objectMapper); 82 | } 83 | 84 | @Provides @Singleton 85 | Persistence providePersistence(JacksonSerializer jacksonSerializer, Context context) { 86 | Esperandro.setSerializer(jacksonSerializer); 87 | return Esperandro.getPreferences(Persistence.class, context); 88 | } 89 | 90 | @Provides @Singleton VKAuth provideVkAuth(OkHttpClient okHttpClient, 91 | CookieSyncManager cookieSyncManager, 92 | CookieManager cookieManager, Persistence persistence) { 93 | return new VKAuth(okHttpClient, cookieSyncManager, cookieManager, persistence); 94 | } 95 | 96 | /* 97 | * FEED FRAGMENT 98 | */ 99 | 100 | @Provides @Singleton Converter provideJacksonConverter(ObjectMapper objectMapper) { 101 | return new JacksonConverter(objectMapper); 102 | } 103 | 104 | @Provides @Singleton 105 | VKApi provideVkApi(OkHttpClient okHttpClient, Converter converter, VKAuth vkAuth) { 106 | Timber.d("creating vkapi"); 107 | return new RestAdapter.Builder() 108 | .setEndpoint(VKApi.SERVER) 109 | .setClient(new OkClient(okHttpClient)) 110 | .setConverter(converter) 111 | .setRequestInterceptor(vkAuth) 112 | .setLog(Timber::d) 113 | .setLogLevel(RestAdapter.LogLevel.BASIC) 114 | .build() 115 | .create(VKApi.class); 116 | } 117 | 118 | @Provides @Singleton DatabaseHelper provideDatabaseHelper(Context context) { 119 | return new DatabaseHelper(context); 120 | } 121 | 122 | @Provides @Singleton 123 | VKontakte provideVKontakte(VKApi vkApi, VKAuth vkAuth, DatabaseHelper databaseHelper) { 124 | return new VKontakte(vkApi, vkAuth, databaseHelper); 125 | } 126 | 127 | @Provides @Singleton PublishSubject provideUnitClicks() { 128 | return PublishSubject.create(); 129 | } 130 | 131 | @Provides @Singleton PublishSubject provideLoadedBitmaps() { 132 | return PublishSubject.create(); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/src/uk/co/senab/actionbarpulltorefresh/library/Options.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Chris Banes 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package uk.co.senab.actionbarpulltorefresh.library; 18 | 19 | /** 20 | * Allows you to specify a number of configuration options when setting up a {@link PullToRefreshLayout}. 21 | */ 22 | public final class Options { 23 | 24 | /* Default configuration values */ 25 | private static final int DEFAULT_HEADER_LAYOUT = R.layout.default_header; 26 | private static final float DEFAULT_REFRESH_SCROLL_DISTANCE = 0.5f; 27 | private static final boolean DEFAULT_REFRESH_ON_UP = false; 28 | private static final int DEFAULT_REFRESH_MINIMIZED_DELAY = 1 * 1000; 29 | private static final boolean DEFAULT_REFRESH_MINIMIZE = true; 30 | 31 | public static Builder create() { 32 | return new Builder(); 33 | } 34 | 35 | Options() {} 36 | 37 | EnvironmentDelegate environmentDelegate = null; 38 | int headerLayout = DEFAULT_HEADER_LAYOUT; 39 | HeaderTransformer headerTransformer = null; 40 | float refreshScrollDistance = DEFAULT_REFRESH_SCROLL_DISTANCE; 41 | boolean refreshOnUp = DEFAULT_REFRESH_ON_UP; 42 | int refreshMinimizeDelay = DEFAULT_REFRESH_MINIMIZED_DELAY; 43 | 44 | /** 45 | * Enable or disable the header 'minimization', which by default means that the majority of 46 | * the header is hidden, leaving only the progress bar still showing. 47 | *

48 | * If set to true, the header will be minimized after the delay set in 49 | * {@link #refreshMinimizeDelay}. If set to false then the whole header will be displayed 50 | * until the refresh is finished. 51 | */ 52 | boolean refreshMinimize = DEFAULT_REFRESH_MINIMIZE; 53 | 54 | public static class Builder { 55 | final Options mOptions = new Options(); 56 | 57 | /** 58 | * EnvironmentDelegate instance which will be used. If null, we will 59 | * create an instance of the default class. 60 | */ 61 | public Builder environmentDelegate(EnvironmentDelegate environmentDelegate) { 62 | mOptions.environmentDelegate = environmentDelegate; 63 | return this; 64 | } 65 | 66 | /** 67 | * The layout resource ID which should be inflated to be displayed above 68 | * the Action Bar 69 | */ 70 | public Builder headerLayout(int headerLayoutId) { 71 | mOptions.headerLayout = headerLayoutId; 72 | return this; 73 | } 74 | 75 | /** 76 | * The header transformer to be used to transfer the header view. If 77 | * null, an instance of {@link DefaultHeaderTransformer} will be used. 78 | */ 79 | public Builder headerTransformer(HeaderTransformer headerTransformer) { 80 | mOptions.headerTransformer = headerTransformer; 81 | return this; 82 | } 83 | 84 | /** 85 | * The percentage of the refreshable view that needs to be scrolled 86 | * before a refresh is initiated. 87 | */ 88 | public Builder scrollDistance(float refreshScrollDistance) { 89 | mOptions.refreshScrollDistance = refreshScrollDistance; 90 | return this; 91 | } 92 | 93 | /** 94 | * Whether a refresh should only be initiated when the user has finished 95 | * the touch event. 96 | */ 97 | public Builder refreshOnUp(boolean enabled) { 98 | mOptions.refreshOnUp = enabled; 99 | return this; 100 | } 101 | 102 | /** 103 | * Disable the header 'minimization', which by default means that the majority of 104 | * the header is hidden, leaving only the progress bar still showing. 105 | */ 106 | public Builder noMinimize() { 107 | mOptions.refreshMinimize = false; 108 | return this; 109 | } 110 | 111 | /** 112 | * Enable header 'minimization', which by default means that the majority of 113 | * the header is hidden, leaving only the progress bar still showing. 114 | */ 115 | public Builder minimize() { 116 | return minimize(DEFAULT_REFRESH_MINIMIZED_DELAY); 117 | } 118 | 119 | /** 120 | * Enable header 'minimization' and set the delay. 121 | */ 122 | public Builder minimize(int delay) { 123 | mOptions.refreshMinimizeDelay = delay; 124 | mOptions.refreshMinimize = true; 125 | return this; 126 | } 127 | 128 | /** 129 | * @return the built {@link uk.co.senab.actionbarpulltorefresh.library.Options} instance. 130 | */ 131 | public Options build() { 132 | return mOptions; 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /app/src/main/res/layout/post.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 23 | 24 | 29 | 30 | 37 | 38 | 46 | 47 | 48 | 49 | 60 | 61 | 68 | 69 | 76 | 77 | 82 | 83 | 90 | 91 | 99 | 100 | 101 | 102 | 113 | 114 | 122 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/src/uk/co/senab/actionbarpulltorefresh/library/ActionBarPullToRefresh.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Chris Banes 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package uk.co.senab.actionbarpulltorefresh.library; 18 | 19 | 20 | import android.app.Activity; 21 | import android.view.View; 22 | import android.view.ViewGroup; 23 | 24 | import java.util.HashMap; 25 | import java.util.Map; 26 | import java.util.Set; 27 | 28 | import uk.co.senab.actionbarpulltorefresh.library.listeners.OnRefreshListener; 29 | import uk.co.senab.actionbarpulltorefresh.library.viewdelegates.ViewDelegate; 30 | 31 | public class ActionBarPullToRefresh { 32 | 33 | public static SetupWizard from(Activity activity) { 34 | return new SetupWizard(activity); 35 | } 36 | 37 | public static final class SetupWizard { 38 | private final Activity mActivity; 39 | private Options mOptions; 40 | private int[] refreshableViewIds; 41 | private View[] refreshableViews; 42 | private OnRefreshListener mOnRefreshListener; 43 | private ViewGroup mViewGroupToInsertInto; 44 | private HashMap mViewDelegates; 45 | 46 | private SetupWizard(Activity activity) { 47 | mActivity = activity; 48 | } 49 | 50 | public SetupWizard options(Options options) { 51 | mOptions = options; 52 | return this; 53 | } 54 | 55 | public SetupWizard allChildrenArePullable() { 56 | refreshableViewIds = null; 57 | refreshableViews = null; 58 | return this; 59 | } 60 | 61 | public SetupWizard theseChildrenArePullable(int... viewIds) { 62 | refreshableViewIds = viewIds; 63 | refreshableViews = null; 64 | return this; 65 | } 66 | 67 | public SetupWizard theseChildrenArePullable(View... views) { 68 | refreshableViews = views; 69 | refreshableViewIds = null; 70 | return this; 71 | } 72 | 73 | public SetupWizard useViewDelegate(Class viewClass, ViewDelegate delegate) { 74 | if (mViewDelegates == null) { 75 | mViewDelegates = new HashMap(); 76 | } 77 | mViewDelegates.put(viewClass, delegate); 78 | return this; 79 | } 80 | 81 | public SetupWizard listener(OnRefreshListener listener) { 82 | mOnRefreshListener = listener; 83 | return this; 84 | } 85 | 86 | public SetupWizard insertLayoutInto(ViewGroup viewGroup) { 87 | mViewGroupToInsertInto = viewGroup; 88 | return this; 89 | } 90 | 91 | public void setup(PullToRefreshLayout pullToRefreshLayout) { 92 | PullToRefreshAttacher attacher = pullToRefreshLayout.createPullToRefreshAttacher( 93 | mActivity, mOptions); 94 | attacher.setOnRefreshListener(mOnRefreshListener); 95 | 96 | if (mViewGroupToInsertInto != null) { 97 | insertLayoutIntoViewGroup(mViewGroupToInsertInto, pullToRefreshLayout); 98 | } 99 | 100 | pullToRefreshLayout.setPullToRefreshAttacher(attacher); 101 | 102 | // First add the pullable child views 103 | if (refreshableViewIds != null) { 104 | pullToRefreshLayout.addChildrenAsPullable(refreshableViewIds); 105 | } else if (refreshableViews != null) { 106 | pullToRefreshLayout.addChildrenAsPullable(refreshableViews); 107 | } else { 108 | pullToRefreshLayout.addAllChildrenAsPullable(); 109 | } 110 | 111 | // Now set any custom view delegates 112 | if (mViewDelegates != null) { 113 | final Set> entries = mViewDelegates.entrySet(); 114 | for (final Map.Entry entry : entries) { 115 | attacher.useViewDelegate(entry.getKey(), entry.getValue()); 116 | } 117 | } 118 | } 119 | 120 | private static void insertLayoutIntoViewGroup(ViewGroup viewGroup, 121 | PullToRefreshLayout pullToRefreshLayout) { 122 | // Move all children to PullToRefreshLayout. This code looks a bit silly but the child 123 | // indices change every time we remove a View (so we can't just iterate through) 124 | View child = viewGroup.getChildAt(0); 125 | while (child != null) { 126 | viewGroup.removeViewAt(0); 127 | pullToRefreshLayout.addView(child); 128 | child = viewGroup.getChildAt(0); 129 | } 130 | 131 | viewGroup.addView(pullToRefreshLayout, ViewGroup.LayoutParams.MATCH_PARENT, 132 | ViewGroup.LayoutParams.MATCH_PARENT); 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/vk/VKAuth.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.vk; 2 | 3 | import android.os.Looper; 4 | import android.webkit.CookieManager; 5 | import android.webkit.CookieSyncManager; 6 | 7 | import com.squareup.okhttp.OkHttpClient; 8 | import com.stiggpwnz.vibes.util.Persistence; 9 | 10 | import java.net.HttpURLConnection; 11 | import java.net.URL; 12 | import java.util.Locale; 13 | 14 | import de.devland.esperandro.SharedPreferenceActions; 15 | import lombok.RequiredArgsConstructor; 16 | import retrofit.RequestInterceptor; 17 | import rx.Observable; 18 | import rx.Subscriber; 19 | 20 | @RequiredArgsConstructor(suppressConstructorProperties = true) 21 | public class VKAuth implements RequestInterceptor { 22 | public static final String REDIRECT_URL = "https://oauth.vk.com/blank.html"; 23 | static final String AUTH_URL = "https://oauth.vk.com/authorize"; 24 | 25 | static final String ACCESS_TOKEN = "access_token"; 26 | static final String USER_ID = "user_id"; 27 | static final String EXPIRES_IN = "expires_in"; 28 | 29 | static final int CLIENT_ID = 3027476; 30 | static final int SCOPE = 2 + 8 + 8192; 31 | 32 | final OkHttpClient okHttpClient; 33 | final CookieSyncManager cookieSyncManager; 34 | final CookieManager cookieManager; 35 | final Persistence persistence; 36 | 37 | public static String authUrl() { 38 | return String.format(Locale.US, 39 | "%s?client_id=%d&scope=%s&redirect_uri=%s&display=touch&response_type=token", 40 | AUTH_URL, CLIENT_ID, SCOPE, REDIRECT_URL); 41 | } 42 | 43 | public static void assertBgThread() { 44 | if (Looper.getMainLooper() == Looper.myLooper()) { 45 | throw new RuntimeException("Wrong thread, buddy"); 46 | } 47 | } 48 | 49 | String authTailRecursive(String url) throws Exception { 50 | while (true) { 51 | HttpURLConnection connection = okHttpClient.open(new URL(url)); 52 | connection.setInstanceFollowRedirects(false); 53 | connection.setRequestProperty("Cookie", cookieManager.getCookie(url)); 54 | connection.connect(); 55 | 56 | if (connection.getResponseCode() == 302) { 57 | cookieManager.setCookie(url, connection.getHeaderField("Set-Cookie")); 58 | String redirectUrl = connection.getHeaderField("Location"); 59 | if (redirectUrl.startsWith(REDIRECT_URL)) { 60 | cookieSyncManager.sync(); 61 | return saveAndGetAccessToken(redirectUrl, System.currentTimeMillis()); 62 | } else { 63 | url = redirectUrl; 64 | } 65 | } else { 66 | throw new Exception("auth flow incomplete"); 67 | } 68 | } 69 | } 70 | 71 | public Observable saveAccessToken(String redirectUrl, long now) { 72 | return Observable.create((Subscriber subscriber) -> { 73 | subscriber.onNext(saveAndGetAccessToken(redirectUrl, now)); 74 | subscriber.onCompleted(); 75 | }); 76 | } 77 | 78 | private String saveAndGetAccessToken(String redirectUrl, long now) { 79 | assertBgThread(); 80 | 81 | // not extracted because of the very rare use 82 | @SuppressWarnings("DynamicRegexReplaceableByCompiledPattern") 83 | String[] params = redirectUrl.split("#")[1].split("&"); 84 | 85 | for (String param : params) { 86 | @SuppressWarnings("DynamicRegexReplaceableByCompiledPattern") 87 | String[] pairs = param.split("="); 88 | 89 | String value = pairs[1]; 90 | switch (pairs[0]) { 91 | case ACCESS_TOKEN: 92 | persistence.accessToken(value); 93 | break; 94 | 95 | case EXPIRES_IN: 96 | persistence.expiresIn((1000 * Long.valueOf(value)) + now); 97 | break; 98 | 99 | case USER_ID: 100 | persistence.userId(Integer.valueOf(value)); 101 | break; 102 | 103 | default: 104 | throw new RuntimeException("redirected to a wrong url"); 105 | } 106 | } 107 | return persistence.accessToken(); 108 | } 109 | 110 | public void resetAuth() { 111 | assertBgThread(); 112 | 113 | SharedPreferenceActions actions = (SharedPreferenceActions) persistence; 114 | actions.remove("accessToken"); 115 | actions.remove("expiresIn"); 116 | actions.remove("userId"); 117 | } 118 | 119 | String getAccessToken(long now) throws Exception { 120 | assertBgThread(); 121 | 122 | String accessToken = persistence.accessToken(); 123 | if (accessToken == null || now >= persistence.expiresIn()) { 124 | return authTailRecursive(authUrl()); 125 | } 126 | return accessToken; 127 | } 128 | 129 | @Override 130 | public void intercept(RequestFacade request) { 131 | try { 132 | request.addQueryParam(ACCESS_TOKEN, getAccessToken(System.currentTimeMillis())); 133 | } catch (Exception e) { 134 | throw new RuntimeException("Failed to refresh access token", e); 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/java/com/stiggpwnz/vibes/fragments/FeedFragment.java: -------------------------------------------------------------------------------- 1 | package com.stiggpwnz.vibes.fragments; 2 | 3 | import android.graphics.Bitmap; 4 | import android.os.Bundle; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.AbsListView; 9 | 10 | import com.neenbedankt.bundles.annotation.Argument; 11 | import com.stiggpwnz.vibes.Dagger; 12 | import com.stiggpwnz.vibes.R; 13 | import com.stiggpwnz.vibes.adapters.FeedAdapter; 14 | import com.stiggpwnz.vibes.util.Images; 15 | import com.stiggpwnz.vibes.vk.VKontakte; 16 | import com.stiggpwnz.vibes.vk.models.Feed; 17 | import com.stiggpwnz.vibes.vk.models.Unit; 18 | 19 | import org.jetbrains.annotations.NotNull; 20 | import org.jetbrains.annotations.Nullable; 21 | 22 | import java.util.concurrent.TimeUnit; 23 | 24 | import javax.inject.Inject; 25 | 26 | import butterknife.InjectView; 27 | import dagger.Lazy; 28 | import rx.Observable; 29 | import rx.Subscriber; 30 | import rx.Subscription; 31 | import rx.android.observables.AndroidObservable; 32 | import rx.schedulers.Schedulers; 33 | import rx.subjects.PublishSubject; 34 | import timber.log.Timber; 35 | import uk.co.senab.actionbarpulltorefresh.library.ActionBarPullToRefresh; 36 | import uk.co.senab.actionbarpulltorefresh.library.PullToRefreshLayout; 37 | 38 | public class FeedFragment extends BaseFragment { 39 | @Inject Lazy vkontakte; 40 | @Inject PublishSubject unitClicks; 41 | @Inject PublishSubject loadedBitmaps; 42 | 43 | @Argument int ownerId; 44 | 45 | @InjectView(R.id.ptr_layout) PullToRefreshLayout pullToRefreshLayout; 46 | @InjectView(R.id.grid) AbsListView gridView; 47 | 48 | @Nullable Subscription subscription; 49 | @NotNull Subscription unitClickSubscription; 50 | @NotNull Subscription bitmaps; 51 | 52 | @Nullable Feed lastResult; 53 | 54 | @Override public void onCreate(Bundle savedInstanceState) { 55 | super.onCreate(savedInstanceState); 56 | FeedFragmentBuilder.injectArguments(this); 57 | Dagger.inject(this); 58 | } 59 | 60 | @Override 61 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 62 | return inflater.inflate(R.layout.newsfeed, container, false); 63 | } 64 | 65 | @Override 66 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 67 | super.onViewCreated(view, savedInstanceState); 68 | ActionBarPullToRefresh.from(getActivity()) 69 | .listener(view1 -> makeRequest()) 70 | .setup(pullToRefreshLayout); 71 | 72 | init(savedInstanceState); 73 | 74 | unitClickSubscription = AndroidObservable.fromFragment(this, unitClicks) 75 | .subscribe(unit -> { 76 | getFragmentManager().beginTransaction() 77 | .replace(R.id.content_frame, FeedFragmentBuilder.newFeedFragment(unit.getId())) 78 | .addToBackStack(null) 79 | .commit(); 80 | }); 81 | 82 | bitmaps = AndroidObservable.fromFragment(this, loadedBitmaps 83 | .debounce(500, TimeUnit.MILLISECONDS) 84 | .flatMap(bitmap -> Observable.create((Subscriber subscriber) -> { 85 | subscriber.onNext(Images.blur(getActivity(), bitmap)); 86 | }) 87 | .map(blurred -> Images.transition(gridView.getBackground(), blurred, getResources())) 88 | .subscribeOn(Schedulers.computation()))) 89 | .subscribe(transition -> { 90 | gridView.setBackgroundDrawable(transition); 91 | transition.startTransition(500); 92 | }); 93 | } 94 | 95 | private void init(@Nullable Bundle savedInstanceState) { 96 | if (subscription == null) { 97 | if (lastResult == null && savedInstanceState != null && savedInstanceState.containsKey("feed")) { 98 | lastResult = (Feed) savedInstanceState.get("feed"); 99 | } 100 | if (lastResult == null) { 101 | makeRequest(); 102 | } else { 103 | updateView(); 104 | } 105 | } else { 106 | pullToRefreshLayout.setRefreshing(true); 107 | } 108 | } 109 | 110 | @Override public void onSaveInstanceState(Bundle outState) { 111 | super.onSaveInstanceState(outState); 112 | outState.putSerializable("feed", lastResult); 113 | } 114 | 115 | @Override public void onDestroyView() { 116 | unitClickSubscription.unsubscribe(); 117 | bitmaps.unsubscribe(); 118 | super.onDestroyView(); 119 | } 120 | 121 | void updateView() { 122 | if (getView() != null && lastResult != null && getActivity() != null) { 123 | pullToRefreshLayout.setRefreshComplete(); 124 | gridView.setAdapter(new FeedAdapter(getLayoutInflater(null), lastResult.getItems())); 125 | } 126 | } 127 | 128 | Observable createNewFeedObservable() { 129 | return (ownerId == 0 ? 130 | vkontakte.get().getNewsFeed(0) : 131 | vkontakte.get().getWall(ownerId, null, 0)) 132 | .subscribeOn(Schedulers.io()); 133 | } 134 | 135 | void makeRequest() { 136 | pullToRefreshLayout.setRefreshing(true); 137 | subscription = AndroidObservable.fromFragment(this, createNewFeedObservable()) 138 | .doOnEach(notification -> subscription = null) 139 | .subscribe(feed -> { 140 | lastResult = feed; 141 | updateView(); 142 | }, e -> { 143 | Timber.e(e, "error getting feed"); 144 | pullToRefreshLayout.setRefreshComplete(); 145 | }); 146 | } 147 | 148 | @Override public void onDestroy() { 149 | if (subscription != null) { 150 | subscription.unsubscribe(); 151 | subscription = null; 152 | } 153 | super.onDestroy(); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /ActionBarPullToRefresh/src/uk/co/senab/actionbarpulltorefresh/library/PullToRefreshLayout.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Chris Banes 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package uk.co.senab.actionbarpulltorefresh.library; 18 | 19 | import android.app.Activity; 20 | import android.content.Context; 21 | import android.content.res.Configuration; 22 | import android.content.res.TypedArray; 23 | import android.text.TextUtils; 24 | import android.util.AttributeSet; 25 | import android.util.Log; 26 | import android.view.MotionEvent; 27 | import android.view.View; 28 | import android.view.ViewGroup; 29 | import android.widget.FrameLayout; 30 | 31 | import java.util.HashSet; 32 | 33 | import uk.co.senab.actionbarpulltorefresh.library.listeners.HeaderViewListener; 34 | import uk.co.senab.actionbarpulltorefresh.library.listeners.OnRefreshListener; 35 | import uk.co.senab.actionbarpulltorefresh.library.viewdelegates.ViewDelegate; 36 | 37 | /** 38 | * The main component of the library. You wrap the views you wish to be 'pullable' within this layout. 39 | * This layout is setup by using the {@link ActionBarPullToRefresh} setup-wizard return by 40 | * @link ActionBarPullToRefresh#from(android.app.Activity)}. 41 | */ 42 | public class PullToRefreshLayout extends FrameLayout { 43 | 44 | private static final boolean DEBUG = false; 45 | private static final String LOG_TAG = "PullToRefreshLayout"; 46 | 47 | private PullToRefreshAttacher mPullToRefreshAttacher; 48 | 49 | public PullToRefreshLayout(Context context) { 50 | this(context, null); 51 | } 52 | 53 | public PullToRefreshLayout(Context context, AttributeSet attrs) { 54 | this(context, attrs, 0); 55 | } 56 | 57 | public PullToRefreshLayout(Context context, AttributeSet attrs, int defStyle) { 58 | super(context, attrs, defStyle); 59 | } 60 | 61 | /** 62 | * Manually set this Attacher's refreshing state. The header will be 63 | * displayed or hidden as requested. 64 | * 65 | * @param refreshing 66 | * - Whether the attacher should be in a refreshing state, 67 | */ 68 | public final void setRefreshing(boolean refreshing) { 69 | ensureAttacher(); 70 | mPullToRefreshAttacher.setRefreshing(refreshing); 71 | } 72 | 73 | /** 74 | * @return true if this Attacher is currently in a refreshing state. 75 | */ 76 | public final boolean isRefreshing() { 77 | ensureAttacher(); 78 | return mPullToRefreshAttacher.isRefreshing(); 79 | } 80 | 81 | /** 82 | * Call this when your refresh is complete and this view should reset itself 83 | * (header view will be hidden). 84 | * 85 | * This is the equivalent of calling setRefreshing(false). 86 | */ 87 | public final void setRefreshComplete() { 88 | ensureAttacher(); 89 | mPullToRefreshAttacher.setRefreshComplete(); 90 | } 91 | 92 | /** 93 | * Set a {@link uk.co.senab.actionbarpulltorefresh.library.listeners.HeaderViewListener} which is called when the visibility 94 | * state of the Header View has changed. 95 | * 96 | * @param listener 97 | */ 98 | public final void setHeaderViewListener(HeaderViewListener listener) { 99 | ensureAttacher(); 100 | mPullToRefreshAttacher.setHeaderViewListener(listener); 101 | } 102 | 103 | /** 104 | * @return The Header View which is displayed when the user is pulling, or 105 | * we are refreshing. 106 | */ 107 | public final View getHeaderView() { 108 | ensureAttacher(); 109 | return mPullToRefreshAttacher.getHeaderView(); 110 | } 111 | 112 | /** 113 | * @return The HeaderTransformer currently used by this Attacher. 114 | */ 115 | public HeaderTransformer getHeaderTransformer() { 116 | ensureAttacher(); 117 | return mPullToRefreshAttacher.getHeaderTransformer(); 118 | } 119 | 120 | 121 | @Override 122 | public final boolean onInterceptTouchEvent(MotionEvent event) { 123 | if (DEBUG) { 124 | Log.d(LOG_TAG, "onInterceptTouchEvent. " + event.toString()); 125 | } 126 | if (isEnabled() && mPullToRefreshAttacher != null && getChildCount() > 0) { 127 | return mPullToRefreshAttacher.onInterceptTouchEvent(event); 128 | } 129 | return false; 130 | } 131 | 132 | @Override 133 | public final boolean onTouchEvent(MotionEvent event) { 134 | if (DEBUG) { 135 | Log.d(LOG_TAG, "onTouchEvent. " + event.toString()); 136 | } 137 | if (isEnabled() && mPullToRefreshAttacher != null) { 138 | return mPullToRefreshAttacher.onTouchEvent(event); 139 | } 140 | return super.onTouchEvent(event); 141 | } 142 | 143 | @Override 144 | public FrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) { 145 | return new LayoutParams(getContext(), attrs); 146 | } 147 | 148 | @Override 149 | protected void onDetachedFromWindow() { 150 | // Destroy the PullToRefreshAttacher 151 | if (mPullToRefreshAttacher != null) { 152 | mPullToRefreshAttacher.destroy(); 153 | } 154 | super.onDetachedFromWindow(); 155 | } 156 | 157 | @Override 158 | protected void onConfigurationChanged(Configuration newConfig) { 159 | if (mPullToRefreshAttacher != null) { 160 | mPullToRefreshAttacher.onConfigurationChanged(newConfig); 161 | } 162 | super.onConfigurationChanged(newConfig); 163 | } 164 | 165 | void setPullToRefreshAttacher(PullToRefreshAttacher attacher) { 166 | if (mPullToRefreshAttacher != null) { 167 | mPullToRefreshAttacher.destroy(); 168 | } 169 | mPullToRefreshAttacher = attacher; 170 | } 171 | 172 | void addAllChildrenAsPullable() { 173 | ensureAttacher(); 174 | for (int i = 0, z = getChildCount(); i < z; i++) { 175 | addRefreshableView(getChildAt(i)); 176 | } 177 | } 178 | 179 | void addChildrenAsPullable(int[] viewIds) { 180 | for (int i = 0, z = viewIds.length; i < z; i++) { 181 | View view = findViewById(viewIds[i]); 182 | if (view != null) { 183 | addRefreshableView(findViewById(viewIds[i])); 184 | } 185 | } 186 | } 187 | 188 | void addChildrenAsPullable(View[] views) { 189 | for (int i = 0, z = views.length; i < z; i++) { 190 | if (views[i] != null) { 191 | addRefreshableView(views[i]); 192 | } 193 | } 194 | } 195 | 196 | void addRefreshableView(View view) { 197 | if (mPullToRefreshAttacher != null) { 198 | mPullToRefreshAttacher.addRefreshableView(view, getViewDelegateFromLayoutParams(view)); 199 | } 200 | } 201 | 202 | ViewDelegate getViewDelegateFromLayoutParams(View view) { 203 | if (view != null && view.getLayoutParams() instanceof LayoutParams) { 204 | LayoutParams lp = (LayoutParams) view.getLayoutParams(); 205 | String clazzName = lp.getViewDelegateClassName(); 206 | 207 | if (!TextUtils.isEmpty(clazzName)) { 208 | // Lets convert any relative class names (i.e. .XYZViewDelegate) 209 | final int firstDot = clazzName.indexOf('.'); 210 | if (firstDot == -1) { 211 | clazzName = getContext().getPackageName() + "." + clazzName; 212 | } else if (firstDot == 0) { 213 | clazzName = getContext().getPackageName() + clazzName; 214 | } 215 | return InstanceCreationUtils.instantiateViewDelegate(getContext(), clazzName); 216 | } 217 | } 218 | return null; 219 | } 220 | 221 | protected PullToRefreshAttacher createPullToRefreshAttacher(Activity activity, 222 | Options options) { 223 | return new PullToRefreshAttacher(activity, options != null ? options : new Options()); 224 | } 225 | 226 | private void ensureAttacher() { 227 | if (mPullToRefreshAttacher == null) { 228 | throw new IllegalStateException("You need to setup the PullToRefreshLayout before using it"); 229 | } 230 | } 231 | 232 | static class LayoutParams extends FrameLayout.LayoutParams { 233 | private final String mViewDelegateClassName; 234 | 235 | LayoutParams(Context c, AttributeSet attrs) { 236 | super(c, attrs); 237 | 238 | TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.PullToRefreshView); 239 | mViewDelegateClassName = a 240 | .getString(R.styleable.PullToRefreshView_ptrViewDelegateClass); 241 | a.recycle(); 242 | } 243 | 244 | String getViewDelegateClassName() { 245 | return mViewDelegateClassName; 246 | } 247 | } 248 | } 249 | --------------------------------------------------------------------------------