├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml └── runConfigurations.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── codevate │ │ └── mvvmreddit │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── codevate │ │ │ └── mvvmreddit │ │ │ ├── MvvmRedditApplication.java │ │ │ ├── client │ │ │ └── RedditClient.java │ │ │ ├── dependencyinjection │ │ │ ├── ApplicationComponent.java │ │ │ └── module │ │ │ │ ├── ApplicationModule.java │ │ │ │ └── ClientModule.java │ │ │ ├── deserializer │ │ │ ├── RedditDateDeserializer.java │ │ │ └── RedditObjectJsonDeserializer.java │ │ │ ├── model │ │ │ ├── RedditComment.java │ │ │ ├── RedditLink.java │ │ │ ├── RedditListing.java │ │ │ └── RedditObject.java │ │ │ ├── view │ │ │ ├── FeedActivity.java │ │ │ ├── PostAdapter.java │ │ │ └── PostViewHolder.java │ │ │ └── viewmodel │ │ │ ├── FeedViewModel.java │ │ │ └── PostViewModel.java │ └── res │ │ ├── layout │ │ ├── action_progress.xml │ │ ├── activity_feed.xml │ │ └── view_item_post.xml │ │ ├── menu │ │ └── feed.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ ├── java │ └── com │ │ └── codevate │ │ └── mvvmreddit │ │ └── viewmodel │ │ └── FeedViewModelTest.java │ └── resources │ ├── top.json │ └── top2.json ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── images ├── screenshot.png └── screenshot_full.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | MVVM Reddit -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 23 | 24 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mvvm-reddit 2 | 3 | Example of MVVM architecture on Android, using RxJava. 4 | 5 | ![App screenshot](https://github.com/Codevate/mvvm-reddit/raw/master/images/screenshot.png) 6 | 7 | ### About Codevate 8 | Codevate is a specialist [UK mobile app development company](https://www.codevate.com/) that builds cloud-connected software. This repository was created for a blog post about [mobile application development](https://www.codevate.com/services/mobile-app-development) and was written by David Bennett. 9 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'com.neenbedankt.android-apt' 3 | apply plugin: 'me.tatarka.retrolambda' 4 | 5 | android { 6 | compileSdkVersion 23 7 | buildToolsVersion "23.0.3" 8 | 9 | defaultConfig { 10 | applicationId "com.codevate.mvvmreddit" 11 | minSdkVersion 16 12 | targetSdkVersion 23 13 | versionCode 1 14 | versionName "1.0" 15 | } 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | compileOptions { 23 | sourceCompatibility JavaVersion.VERSION_1_8 24 | targetCompatibility JavaVersion.VERSION_1_8 25 | } 26 | } 27 | 28 | dependencies { 29 | compile fileTree(dir: 'libs', include: ['*.jar']) 30 | testCompile 'junit:junit:4.12' 31 | testCompile 'org.mockito:mockito-core:1.10.19' 32 | compile 'com.android.support:appcompat-v7:23.4.0' 33 | compile 'com.android.support:support-v4:23.2.1' 34 | compile 'com.android.support:design:23.2.1' 35 | compile 'com.android.support:recyclerview-v7:23.2.1' 36 | compile 'com.android.support:cardview-v7:23.2.1' 37 | compile 'com.google.dagger:dagger:2.4' 38 | compile 'com.squareup.picasso:picasso:2.5.2' 39 | compile 'com.squareup.retrofit2:retrofit:2.1.0' 40 | compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0' 41 | compile 'com.squareup.retrofit2:converter-gson:2.1.0' 42 | compile 'com.squareup.okhttp3:logging-interceptor:3.3.1' 43 | compile 'io.reactivex:rxjava:1.1.8' 44 | compile 'io.reactivex:rxandroid:1.2.1' 45 | compile 'org.ocpsoft.prettytime:prettytime:4.0.1.Final' 46 | compile "org.androidannotations:androidannotations-api:4.0.0" 47 | compile group: 'commons-io', name: 'commons-io', version: '2.5' 48 | compile group: 'commons-validator', name: 'commons-validator', version: '1.5.1' 49 | apt "org.androidannotations:androidannotations:4.0.0" 50 | apt 'com.google.dagger:dagger-compiler:2.4' 51 | apt 'org.projectlombok:lombok:1.16.8' 52 | provided 'org.projectlombok:lombok:1.16.8' 53 | } 54 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Applications/adt-bundle/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/codevate/mvvmreddit/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.codevate.mvvmreddit; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | public class ApplicationTest extends ApplicationTestCase 7 | { 8 | public ApplicationTest() 9 | { 10 | super(Application.class); 11 | } 12 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/codevate/mvvmreddit/MvvmRedditApplication.java: -------------------------------------------------------------------------------- 1 | package com.codevate.mvvmreddit; 2 | 3 | import android.app.Application; 4 | 5 | import com.codevate.mvvmreddit.dependencyinjection.ApplicationComponent; 6 | import com.codevate.mvvmreddit.dependencyinjection.DaggerApplicationComponent; 7 | import com.codevate.mvvmreddit.dependencyinjection.module.ApplicationModule; 8 | 9 | import org.androidannotations.annotations.EApplication; 10 | 11 | @EApplication 12 | public class MvvmRedditApplication extends Application 13 | { 14 | private ApplicationComponent applicationComponent; 15 | 16 | @Override 17 | public void onCreate() 18 | { 19 | super.onCreate(); 20 | 21 | applicationComponent = DaggerApplicationComponent 22 | .builder() 23 | .applicationModule(new ApplicationModule(this)) 24 | .build(); 25 | } 26 | 27 | public ApplicationComponent component() 28 | { 29 | return applicationComponent; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/codevate/mvvmreddit/client/RedditClient.java: -------------------------------------------------------------------------------- 1 | package com.codevate.mvvmreddit.client; 2 | 3 | import com.codevate.mvvmreddit.model.RedditObject; 4 | 5 | import retrofit2.http.GET; 6 | import retrofit2.http.Query; 7 | import rx.Observable; 8 | 9 | public interface RedditClient 10 | { 11 | @GET("/top.json") 12 | Observable getTop(@Query("after") String after, @Query("limit") int limit); 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/codevate/mvvmreddit/dependencyinjection/ApplicationComponent.java: -------------------------------------------------------------------------------- 1 | package com.codevate.mvvmreddit.dependencyinjection; 2 | 3 | import com.codevate.mvvmreddit.dependencyinjection.module.ApplicationModule; 4 | import com.codevate.mvvmreddit.dependencyinjection.module.ClientModule; 5 | import com.codevate.mvvmreddit.view.FeedActivity; 6 | 7 | import javax.inject.Singleton; 8 | 9 | import dagger.Component; 10 | 11 | @Singleton 12 | @Component(modules = {ApplicationModule.class, ClientModule.class}) 13 | public interface ApplicationComponent 14 | { 15 | void inject(FeedActivity activity); 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/codevate/mvvmreddit/dependencyinjection/module/ApplicationModule.java: -------------------------------------------------------------------------------- 1 | package com.codevate.mvvmreddit.dependencyinjection.module; 2 | 3 | import com.codevate.mvvmreddit.MvvmRedditApplication; 4 | 5 | import dagger.Module; 6 | 7 | @Module 8 | public class ApplicationModule 9 | { 10 | private MvvmRedditApplication application; 11 | 12 | public ApplicationModule(MvvmRedditApplication application) 13 | { 14 | this.application = application; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/codevate/mvvmreddit/dependencyinjection/module/ClientModule.java: -------------------------------------------------------------------------------- 1 | package com.codevate.mvvmreddit.dependencyinjection.module; 2 | 3 | import com.codevate.mvvmreddit.BuildConfig; 4 | import com.codevate.mvvmreddit.client.RedditClient; 5 | import com.codevate.mvvmreddit.model.RedditObject; 6 | import com.codevate.mvvmreddit.deserializer.RedditObjectJsonDeserializer; 7 | import com.codevate.mvvmreddit.deserializer.RedditDateDeserializer; 8 | import com.google.gson.FieldNamingPolicy; 9 | import com.google.gson.Gson; 10 | import com.google.gson.GsonBuilder; 11 | 12 | import java.util.Date; 13 | 14 | import dagger.Module; 15 | import dagger.Provides; 16 | import okhttp3.OkHttpClient; 17 | import okhttp3.logging.HttpLoggingInterceptor; 18 | import retrofit2.Retrofit; 19 | import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; 20 | import retrofit2.converter.gson.GsonConverterFactory; 21 | import rx.schedulers.Schedulers; 22 | 23 | @Module 24 | public class ClientModule 25 | { 26 | @Provides 27 | public RedditClient provideRedditClient() 28 | { 29 | Gson gson = new GsonBuilder() 30 | .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) 31 | .registerTypeAdapter(RedditObject.class, new RedditObjectJsonDeserializer()) 32 | .registerTypeAdapter(Date.class, new RedditDateDeserializer()) 33 | .create(); 34 | 35 | RxJavaCallAdapterFactory rxAdapter = RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()); 36 | 37 | HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); 38 | interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); 39 | OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder(); 40 | if (BuildConfig.DEBUG) 41 | { 42 | okHttpClientBuilder.addInterceptor(interceptor); 43 | } 44 | OkHttpClient client = okHttpClientBuilder.build(); 45 | 46 | Retrofit retrofit = new Retrofit.Builder() 47 | .baseUrl("http://reddit.com") 48 | .client(client) 49 | .addConverterFactory(GsonConverterFactory.create(gson)) 50 | .addCallAdapterFactory(rxAdapter) 51 | .build(); 52 | 53 | return retrofit.create(RedditClient.class); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/com/codevate/mvvmreddit/deserializer/RedditDateDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.codevate.mvvmreddit.deserializer; 2 | 3 | import com.google.gson.JsonDeserializationContext; 4 | import com.google.gson.JsonDeserializer; 5 | import com.google.gson.JsonElement; 6 | import com.google.gson.JsonParseException; 7 | 8 | import java.lang.reflect.Type; 9 | import java.util.Date; 10 | 11 | /** 12 | * Reddit uses timestamps for dates. This deserializer transforms them into Java dates; 13 | */ 14 | public class RedditDateDeserializer implements JsonDeserializer 15 | { 16 | @Override 17 | public Object deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException 18 | { 19 | return new Date(json.getAsJsonPrimitive().getAsLong() * 1000); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/codevate/mvvmreddit/deserializer/RedditObjectJsonDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.codevate.mvvmreddit.deserializer; 2 | 3 | import android.util.Log; 4 | 5 | import com.codevate.mvvmreddit.model.RedditComment; 6 | import com.codevate.mvvmreddit.model.RedditLink; 7 | import com.codevate.mvvmreddit.model.RedditListing; 8 | import com.codevate.mvvmreddit.model.RedditObject; 9 | import com.google.gson.JsonDeserializationContext; 10 | import com.google.gson.JsonDeserializer; 11 | import com.google.gson.JsonElement; 12 | import com.google.gson.JsonObject; 13 | import com.google.gson.JsonParseException; 14 | 15 | import java.lang.reflect.Type; 16 | 17 | /** 18 | * Deserialize the Reddit object into a subclass based on its 'kind' field. 19 | */ 20 | public class RedditObjectJsonDeserializer implements JsonDeserializer 21 | { 22 | private static String TAG = "RedditObjectJsonDeserializer"; 23 | 24 | @Override 25 | public RedditObject deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException 26 | { 27 | if (json == null || !json.isJsonObject()) 28 | { 29 | return null; 30 | } 31 | 32 | try 33 | { 34 | JsonObject jsonObject = json.getAsJsonObject(); 35 | String kind = jsonObject.get("kind").getAsString(); 36 | 37 | return context.deserialize(jsonObject.get("data"), getClassForKind(kind)); 38 | } 39 | catch (JsonParseException e) 40 | { 41 | Log.e(TAG, String.format("Could not deserialize Reddit element: %s", json.toString())); 42 | return null; 43 | } 44 | } 45 | 46 | private Class getClassForKind(String kind) 47 | { 48 | switch (kind) 49 | { 50 | case "Listing": 51 | return RedditListing.class; 52 | case "t1": 53 | return RedditComment.class; 54 | case "t3": 55 | return RedditLink.class; 56 | default: 57 | Log.e(TAG, String.format("Unsupported Reddit kind: %s", kind)); 58 | return null; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/codevate/mvvmreddit/model/RedditComment.java: -------------------------------------------------------------------------------- 1 | package com.codevate.mvvmreddit.model; 2 | 3 | import java.util.Date; 4 | 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | 8 | @Data 9 | public class RedditComment implements RedditObject 10 | { 11 | private String id; 12 | private String body; 13 | private String bodyHtml; 14 | private String author; 15 | private String subredditId; 16 | private String linkId; 17 | private String parentId; 18 | private String score; 19 | private String ups; 20 | private String downs; 21 | private Date created; 22 | private Date createdUtc; 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/codevate/mvvmreddit/model/RedditLink.java: -------------------------------------------------------------------------------- 1 | package com.codevate.mvvmreddit.model; 2 | 3 | import java.util.Date; 4 | 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | 8 | @Data 9 | public class RedditLink implements RedditObject 10 | { 11 | private String id; 12 | private String title; 13 | private String domain; 14 | private String subreddit; 15 | private String subredditId; 16 | private String linkFlairText; 17 | private String author; 18 | private String thumbnail; 19 | private String permalink; 20 | private String url; 21 | private int score; 22 | private int ups; 23 | private int downs; 24 | private int numComments; 25 | private boolean over18; 26 | private boolean hideScore; 27 | private Date created; 28 | private Date createdUtc; 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/codevate/mvvmreddit/model/RedditListing.java: -------------------------------------------------------------------------------- 1 | package com.codevate.mvvmreddit.model; 2 | 3 | import java.util.List; 4 | 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | 8 | @Data 9 | public class RedditListing implements RedditObject 10 | { 11 | private List children; 12 | private String before; 13 | private String after; 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/codevate/mvvmreddit/model/RedditObject.java: -------------------------------------------------------------------------------- 1 | package com.codevate.mvvmreddit.model; 2 | 3 | public interface RedditObject 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/codevate/mvvmreddit/view/FeedActivity.java: -------------------------------------------------------------------------------- 1 | package com.codevate.mvvmreddit.view; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.support.v7.widget.LinearLayoutManager; 6 | import android.support.v7.widget.RecyclerView; 7 | import android.view.MenuItem; 8 | 9 | import com.codevate.mvvmreddit.MvvmRedditApplication; 10 | import com.codevate.mvvmreddit.R; 11 | import com.codevate.mvvmreddit.viewmodel.FeedViewModel; 12 | 13 | import org.androidannotations.annotations.AfterViews; 14 | import org.androidannotations.annotations.EActivity; 15 | import org.androidannotations.annotations.OptionsMenu; 16 | import org.androidannotations.annotations.OptionsMenuItem; 17 | import org.androidannotations.annotations.ViewById; 18 | 19 | import javax.inject.Inject; 20 | 21 | import rx.Observable; 22 | import rx.android.schedulers.AndroidSchedulers; 23 | import rx.subscriptions.CompositeSubscription; 24 | 25 | @EActivity(R.layout.activity_feed) 26 | @OptionsMenu(R.menu.feed) 27 | public class FeedActivity extends AppCompatActivity 28 | { 29 | @ViewById(R.id.post_list) 30 | RecyclerView postList; 31 | 32 | @OptionsMenuItem(R.id.progress) 33 | MenuItem loadingMenuItem; 34 | 35 | @Inject 36 | FeedViewModel viewModel; 37 | 38 | private PostAdapter postAdapter; 39 | private LinearLayoutManager postListLayoutManager; 40 | 41 | /** Hold active loading observable subscriptions, so that they can be unsubscribed from when the activity is destroyed */ 42 | private CompositeSubscription subscriptions; 43 | 44 | @Override 45 | protected void onCreate(Bundle savedInstanceState) 46 | { 47 | super.onCreate(savedInstanceState); 48 | 49 | ((MvvmRedditApplication) getApplication()).component().inject(this); 50 | 51 | subscriptions = new CompositeSubscription(); 52 | } 53 | 54 | @Override 55 | protected void onDestroy() 56 | { 57 | super.onDestroy(); 58 | 59 | subscriptions.unsubscribe(); 60 | } 61 | 62 | @AfterViews 63 | void init() 64 | { 65 | initViews(); 66 | initBindings(); 67 | 68 | // Initial page load 69 | loadNextPage(); 70 | } 71 | 72 | private void initViews() 73 | { 74 | postListLayoutManager = new LinearLayoutManager(this); 75 | postList.setLayoutManager(postListLayoutManager); 76 | 77 | postAdapter = new PostAdapter(); 78 | postList.setAdapter(postAdapter); 79 | } 80 | 81 | private void initBindings() 82 | { 83 | // Observable that emits when the RecyclerView is scrolled to the bottom 84 | Observable infiniteScrollObservable = Observable.create(subscriber -> { 85 | postList.addOnScrollListener(new RecyclerView.OnScrollListener() 86 | { 87 | @Override 88 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) 89 | { 90 | int totalItemCount = postListLayoutManager.getItemCount(); 91 | int visibleItemCount = postListLayoutManager.getChildCount(); 92 | int firstVisibleItem = postListLayoutManager.findFirstVisibleItemPosition(); 93 | 94 | if ((visibleItemCount + firstVisibleItem) >= totalItemCount) 95 | { 96 | subscriber.onNext(null); 97 | } 98 | } 99 | }); 100 | }); 101 | 102 | subscriptions.addAll( 103 | // Bind list of posts to the RecyclerView 104 | viewModel.postsObservable().observeOn(AndroidSchedulers.mainThread()).subscribe(postAdapter::setItems), 105 | 106 | // Bind loading status to show/hide loading spinner 107 | viewModel.isLoadingObservable().observeOn(AndroidSchedulers.mainThread()).subscribe(this::setIsLoading), 108 | 109 | // Trigger next page load when RecyclerView is scrolled to the bottom 110 | infiniteScrollObservable.subscribe(x -> loadNextPage()) 111 | ); 112 | } 113 | 114 | private void loadNextPage() 115 | { 116 | subscriptions.add( 117 | viewModel.loadMorePosts().subscribe() 118 | ); 119 | } 120 | 121 | private void setIsLoading(boolean isLoading) 122 | { 123 | if (loadingMenuItem != null) 124 | { 125 | loadingMenuItem.setVisible(isLoading); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /app/src/main/java/com/codevate/mvvmreddit/view/PostAdapter.java: -------------------------------------------------------------------------------- 1 | package com.codevate.mvvmreddit.view; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | 8 | import com.codevate.mvvmreddit.R; 9 | import com.codevate.mvvmreddit.viewmodel.PostViewModel; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | public class PostAdapter extends RecyclerView.Adapter 15 | { 16 | private List items = new ArrayList<>(); 17 | 18 | public PostAdapter() 19 | { 20 | setHasStableIds(true); 21 | } 22 | 23 | @Override 24 | public PostViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 25 | { 26 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_item_post, parent, false); 27 | return new PostViewHolder(view); 28 | } 29 | 30 | @Override 31 | public void onBindViewHolder(PostViewHolder holder, int position) 32 | { 33 | holder.bind(getItem(position)); 34 | } 35 | 36 | @Override 37 | public int getItemCount() 38 | { 39 | return items.size(); 40 | } 41 | 42 | @Override 43 | public long getItemId(int position) 44 | { 45 | return getItem(position).getId().hashCode(); 46 | } 47 | 48 | public PostViewModel getItem(int position) 49 | { 50 | return items.get(position); 51 | } 52 | 53 | public void setItems(List items) 54 | { 55 | if (items == null) 56 | { 57 | return; 58 | } 59 | 60 | this.items = new ArrayList<>(items); 61 | notifyDataSetChanged(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/com/codevate/mvvmreddit/view/PostViewHolder.java: -------------------------------------------------------------------------------- 1 | package com.codevate.mvvmreddit.view; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.view.View; 5 | import android.widget.ImageView; 6 | import android.widget.TextView; 7 | 8 | import com.codevate.mvvmreddit.R; 9 | import com.codevate.mvvmreddit.viewmodel.PostViewModel; 10 | import com.squareup.picasso.Picasso; 11 | 12 | import org.apache.commons.validator.routines.UrlValidator; 13 | 14 | public class PostViewHolder extends RecyclerView.ViewHolder 15 | { 16 | private View view; 17 | private TextView titleTextView; 18 | private TextView subtitleTextView; 19 | private TextView subtitle2TextView; 20 | private ImageView thumbnailImageView; 21 | 22 | public PostViewHolder(View view) 23 | { 24 | super(view); 25 | 26 | this.view = view; 27 | this.titleTextView = (TextView) view.findViewById(R.id.title); 28 | this.subtitleTextView = (TextView) view.findViewById(R.id.subtitle); 29 | this.subtitle2TextView = (TextView) view.findViewById(R.id.subtitle2); 30 | this.thumbnailImageView = (ImageView)view.findViewById(R.id.thumbnail); 31 | } 32 | 33 | public void bind(PostViewModel viewModel) 34 | { 35 | titleTextView.setText(viewModel.getTitle()); 36 | subtitleTextView.setText(String.format("%s - %s - %s (%s)", viewModel.getAuthor(), viewModel.getCreatedOn(), viewModel.getSubreddit(), viewModel.getDomain())); 37 | subtitle2TextView.setText(String.format("%d points - %d comments", viewModel.getScore(), viewModel.getNumComments())); 38 | 39 | UrlValidator urlValidator = new UrlValidator(); 40 | boolean hasThumbnail = viewModel.getThumbnailUrl() != null && urlValidator.isValid(viewModel.getThumbnailUrl()); 41 | 42 | // Show/hide the thumbnail if there is/isn't one 43 | thumbnailImageView.setVisibility(hasThumbnail ? View.VISIBLE : View.GONE); 44 | 45 | // Load the thumbnail if there is one 46 | if (hasThumbnail) 47 | { 48 | Picasso.with(view.getContext()).load(viewModel.getThumbnailUrl()).into(thumbnailImageView); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/codevate/mvvmreddit/viewmodel/FeedViewModel.java: -------------------------------------------------------------------------------- 1 | package com.codevate.mvvmreddit.viewmodel; 2 | 3 | import com.codevate.mvvmreddit.client.RedditClient; 4 | import com.codevate.mvvmreddit.model.RedditLink; 5 | import com.codevate.mvvmreddit.model.RedditListing; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import javax.inject.Inject; 11 | 12 | import rx.Observable; 13 | import rx.subjects.BehaviorSubject; 14 | 15 | public class FeedViewModel 16 | { 17 | private RedditClient redditClient; 18 | 19 | private int pageLimit; 20 | private String afterToken; 21 | private BehaviorSubject> postSubject = BehaviorSubject.create(new ArrayList<>()); 22 | private BehaviorSubject isLoadingSubject = BehaviorSubject.create(false); 23 | 24 | @Inject 25 | public FeedViewModel(RedditClient redditClient) 26 | { 27 | this.redditClient = redditClient; 28 | this.pageLimit = 25; 29 | } 30 | 31 | public Observable> loadMorePosts() 32 | { 33 | // Don't try and load if we're already loading 34 | if (isLoadingSubject.getValue()) 35 | { 36 | return Observable.empty(); 37 | } 38 | 39 | isLoadingSubject.onNext(true); 40 | 41 | return redditClient 42 | .getTop(afterToken, pageLimit) 43 | // Safe to cast to RedditListing, as this is always returned from top posts 44 | .cast(RedditListing.class) 45 | // Store the after token, so we can use it to get the next page of posts is a subsequent load 46 | .doOnNext(listing -> afterToken = listing.getAfter()) 47 | // Flatten into observable of RedditLinks 48 | .map(RedditListing::getChildren) 49 | .flatMapIterable(list -> list) 50 | .filter(object -> object instanceof RedditLink) 51 | // Transform model to viewmodel 52 | .map(link -> new PostViewModel((RedditLink) link)) 53 | // Merge viewmodels into a single list to be emitted 54 | .toList() 55 | // Concatenate the new posts to the current posts list, then emit it via the post subject 56 | .doOnNext(list -> { 57 | List fullList = new ArrayList<>(postSubject.getValue()); 58 | fullList.addAll(list); 59 | postSubject.onNext(fullList); 60 | }) 61 | .doOnTerminate(() -> isLoadingSubject.onNext(false)); 62 | } 63 | 64 | public Observable> postsObservable() 65 | { 66 | return postSubject.asObservable(); 67 | } 68 | 69 | public Observable isLoadingObservable() 70 | { 71 | return isLoadingSubject.asObservable(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/com/codevate/mvvmreddit/viewmodel/PostViewModel.java: -------------------------------------------------------------------------------- 1 | package com.codevate.mvvmreddit.viewmodel; 2 | 3 | import com.codevate.mvvmreddit.model.RedditLink; 4 | 5 | import org.ocpsoft.prettytime.PrettyTime; 6 | 7 | import lombok.Data; 8 | 9 | @Data 10 | public class PostViewModel 11 | { 12 | private String id; 13 | private String title; 14 | private String author; 15 | private String thumbnailUrl; 16 | private String createdOn; 17 | private String subreddit; 18 | private String domain; 19 | private int numComments; 20 | private int score; 21 | 22 | public PostViewModel(RedditLink redditLink) 23 | { 24 | this.id = redditLink.getId(); 25 | this.title = redditLink.getTitle(); 26 | this.author = redditLink.getAuthor(); 27 | this.thumbnailUrl = redditLink.getThumbnail(); 28 | this.subreddit = redditLink.getSubreddit(); 29 | this.domain = redditLink.getDomain(); 30 | this.numComments = redditLink.getNumComments(); 31 | this.score = redditLink.getScore(); 32 | 33 | PrettyTime pt = new PrettyTime(); 34 | this.createdOn = pt.format(redditLink.getCreated()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/res/layout/action_progress.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_feed.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/layout/view_item_post.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 21 | 22 | 26 | 27 | 34 | 35 | 42 | 43 | 48 | 49 | 54 | 55 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /app/src/main/res/menu/feed.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codevate/mvvm-reddit/f6d4d1a525370dbe6bd66eca0ab2c493777e3a68/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codevate/mvvm-reddit/f6d4d1a525370dbe6bd66eca0ab2c493777e3a68/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codevate/mvvm-reddit/f6d4d1a525370dbe6bd66eca0ab2c493777e3a68/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codevate/mvvm-reddit/f6d4d1a525370dbe6bd66eca0ab2c493777e3a68/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codevate/mvvm-reddit/f6d4d1a525370dbe6bd66eca0ab2c493777e3a68/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #31AD98 4 | #057F6B 5 | #82DCCD 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | MVVM Reddit 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/test/java/com/codevate/mvvmreddit/viewmodel/FeedViewModelTest.java: -------------------------------------------------------------------------------- 1 | package com.codevate.mvvmreddit.viewmodel; 2 | 3 | import com.codevate.mvvmreddit.client.RedditClient; 4 | import com.codevate.mvvmreddit.deserializer.RedditDateDeserializer; 5 | import com.codevate.mvvmreddit.deserializer.RedditObjectJsonDeserializer; 6 | import com.codevate.mvvmreddit.model.RedditObject; 7 | import com.google.gson.FieldNamingPolicy; 8 | import com.google.gson.Gson; 9 | import com.google.gson.GsonBuilder; 10 | 11 | import org.apache.commons.io.IOUtils; 12 | import org.junit.Before; 13 | import org.junit.Test; 14 | import org.junit.runner.RunWith; 15 | import org.mockito.Mock; 16 | import org.mockito.runners.MockitoJUnitRunner; 17 | 18 | import java.util.Date; 19 | import java.util.List; 20 | 21 | import rx.Observable; 22 | import rx.observers.TestSubscriber; 23 | 24 | import static org.mockito.Matchers.anyInt; 25 | import static org.mockito.Matchers.anyString; 26 | import static org.mockito.Mockito.times; 27 | import static org.mockito.Mockito.verify; 28 | import static org.mockito.Mockito.when; 29 | 30 | @RunWith(MockitoJUnitRunner.class) 31 | public class FeedViewModelTest 32 | { 33 | @Mock 34 | RedditClient redditClient; 35 | 36 | private Gson gson; 37 | 38 | @Before 39 | public void setup() throws Exception 40 | { 41 | gson = new GsonBuilder() 42 | .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) 43 | .registerTypeAdapter(RedditObject.class, new RedditObjectJsonDeserializer()) 44 | .registerTypeAdapter(Date.class, new RedditDateDeserializer()) 45 | .create(); 46 | 47 | when(redditClient.getTop(null, 25)).thenReturn(Observable.just(topPage1())); 48 | when(redditClient.getTop("t3_4tsqgv", 25)).thenReturn(Observable.just(topPage2())); 49 | } 50 | 51 | @Test 52 | public void loadNextPage_loadsPage1() throws Exception 53 | { 54 | FeedViewModel viewModel = new FeedViewModel(redditClient); 55 | 56 | TestSubscriber> testSubscriber = new TestSubscriber<>(); 57 | viewModel.loadMorePosts().subscribe(testSubscriber); 58 | testSubscriber.assertCompleted(); 59 | testSubscriber.assertValueCount(1); 60 | 61 | verify(redditClient, times(1)).getTop(anyString(), anyInt()); 62 | verify(redditClient).getTop(null, 25); 63 | } 64 | 65 | @Test 66 | public void loadNextPage_loadsPage2() throws Exception 67 | { 68 | FeedViewModel viewModel = new FeedViewModel(redditClient); 69 | 70 | // Load initial page 71 | TestSubscriber> testSubscriber = new TestSubscriber<>(); 72 | viewModel.loadMorePosts().subscribe(testSubscriber); 73 | testSubscriber.assertCompleted(); 74 | testSubscriber.assertValueCount(1); 75 | 76 | verify(redditClient).getTop(null, 25); 77 | verify(redditClient, times(1)).getTop(null, 25); 78 | 79 | // Load next page 80 | testSubscriber = new TestSubscriber<>(); 81 | viewModel.loadMorePosts().subscribe(testSubscriber); 82 | testSubscriber.assertCompleted(); 83 | testSubscriber.assertValueCount(1); 84 | 85 | verify(redditClient, times(2)).getTop(anyString(), anyInt()); 86 | verify(redditClient, times(1)).getTop("t3_4tsqgv", 25); 87 | verify(redditClient).getTop("t3_4tsqgv", 25); 88 | } 89 | 90 | @Test 91 | public void isLoading_toggled() throws Exception 92 | { 93 | FeedViewModel viewModel = new FeedViewModel(redditClient); 94 | 95 | // Initial state - not loading 96 | TestSubscriber testSubscriber = new TestSubscriber<>(); 97 | viewModel.isLoadingObservable().subscribe(testSubscriber); 98 | testSubscriber.assertValue(false); 99 | 100 | // Start loading page 101 | Observable> postsObservable = viewModel.loadMorePosts(); 102 | 103 | testSubscriber = new TestSubscriber<>(); 104 | viewModel.isLoadingObservable().subscribe(testSubscriber); 105 | testSubscriber.assertValue(true); 106 | 107 | // Finish loading page 108 | postsObservable.subscribe(); 109 | 110 | testSubscriber = new TestSubscriber<>(); 111 | viewModel.isLoadingObservable().subscribe(testSubscriber); 112 | testSubscriber.assertValue(false); 113 | } 114 | 115 | private RedditObject topPage1() throws Exception 116 | { 117 | return gson.fromJson(IOUtils.toString(getClass().getResourceAsStream("/top.json")), RedditObject.class); 118 | } 119 | 120 | private RedditObject topPage2() throws Exception 121 | { 122 | return gson.fromJson(IOUtils.toString(getClass().getResourceAsStream("/top.json")), RedditObject.class); 123 | } 124 | } -------------------------------------------------------------------------------- /app/src/test/resources/top2.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "Listing", 3 | "data": { 4 | "modhash": "06u3o2s4axdf025f37115c07d227d61d03e1d0c8cc501d9866", 5 | "children": [ 6 | { 7 | "kind": "t3", 8 | "data": { 9 | "domain": "asiancorrespondent.com", 10 | "banned_by": null, 11 | "media_embed": {}, 12 | "subreddit": "worldnews", 13 | "selftext_html": null, 14 | "selftext": "", 15 | "likes": null, 16 | "suggested_sort": null, 17 | "user_reports": [], 18 | "secure_media": null, 19 | "link_flair_text": null, 20 | "id": "4sj7q2", 21 | "from_kind": null, 22 | "gilded": 0, 23 | "archived": false, 24 | "clicked": false, 25 | "report_reasons": null, 26 | "author": "randomnamegendarme", 27 | "media": null, 28 | "score": 7175, 29 | "approved_by": null, 30 | "over_18": false, 31 | "hidden": false, 32 | "num_comments": 9896, 33 | "thumbnail": "", 34 | "subreddit_id": "t5_2qh13", 35 | "hide_score": false, 36 | "edited": false, 37 | "link_flair_css_class": null, 38 | "author_flair_css_class": null, 39 | "downs": 0, 40 | "secure_media_embed": {}, 41 | "saved": false, 42 | "removal_reason": null, 43 | "stickied": false, 44 | "from": null, 45 | "is_self": false, 46 | "from_id": null, 47 | "permalink": "/r/worldnews/comments/4sj7q2/body_count_rises_as_new_philippines_president/", 48 | "locked": false, 49 | "name": "t3_4sj7q2", 50 | "created": 1468384442, 51 | "url": "https://asiancorrespondent.com/2016/07/philippines-duterte-drug-addicts/", 52 | "author_flair_text": null, 53 | "quarantine": false, 54 | "title": "Body count rises as new Philippines president calls for drug addicts to be killed", 55 | "created_utc": 1468355642, 56 | "distinguished": null, 57 | "mod_reports": [], 58 | "visited": false, 59 | "num_reports": null, 60 | "ups": 7175 61 | } 62 | }, 63 | { 64 | "kind": "t3", 65 | "data": { 66 | "domain": "i.imgur.com", 67 | "banned_by": null, 68 | "media_embed": {}, 69 | "subreddit": "funny", 70 | "selftext_html": null, 71 | "selftext": "", 72 | "likes": null, 73 | "suggested_sort": null, 74 | "user_reports": [], 75 | "secure_media": null, 76 | "link_flair_text": null, 77 | "id": "4vq7rj", 78 | "from_kind": null, 79 | "gilded": 6, 80 | "archived": false, 81 | "clicked": false, 82 | "report_reasons": null, 83 | "author": "Nephelus", 84 | "media": null, 85 | "score": 8096, 86 | "approved_by": null, 87 | "over_18": false, 88 | "hidden": false, 89 | "preview": { 90 | "images": [ 91 | { 92 | "source": { 93 | "url": "https://i.redditmedia.com/ZGLiI_3CZMmqinFSPeW0VG4p-_qKVNxCHKQrTHPWjyE.jpg?s=bfd51aa282f61c64b7552818a52679d3", 94 | "width": 1400, 95 | "height": 790 96 | }, 97 | "resolutions": [ 98 | { 99 | "url": "https://i.redditmedia.com/ZGLiI_3CZMmqinFSPeW0VG4p-_qKVNxCHKQrTHPWjyE.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=108&s=a29739def4885c76a6a0f0ad5b77c9fb", 100 | "width": 108, 101 | "height": 60 102 | }, 103 | { 104 | "url": "https://i.redditmedia.com/ZGLiI_3CZMmqinFSPeW0VG4p-_qKVNxCHKQrTHPWjyE.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=216&s=ce67f0c1b49eb86c5296a60281028f69", 105 | "width": 216, 106 | "height": 121 107 | }, 108 | { 109 | "url": "https://i.redditmedia.com/ZGLiI_3CZMmqinFSPeW0VG4p-_qKVNxCHKQrTHPWjyE.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=320&s=d520caed31665d4f9ef1cd24c31a81f6", 110 | "width": 320, 111 | "height": 180 112 | }, 113 | { 114 | "url": "https://i.redditmedia.com/ZGLiI_3CZMmqinFSPeW0VG4p-_qKVNxCHKQrTHPWjyE.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=640&s=a4627f48e7f9c7610b740e5dfc73ff5e", 115 | "width": 640, 116 | "height": 361 117 | }, 118 | { 119 | "url": "https://i.redditmedia.com/ZGLiI_3CZMmqinFSPeW0VG4p-_qKVNxCHKQrTHPWjyE.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=960&s=122562e287c8d6dd080320e2275fcbeb", 120 | "width": 960, 121 | "height": 541 122 | }, 123 | { 124 | "url": "https://i.redditmedia.com/ZGLiI_3CZMmqinFSPeW0VG4p-_qKVNxCHKQrTHPWjyE.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=1080&s=4764666dff20e081a6073c8fedab7651", 125 | "width": 1080, 126 | "height": 609 127 | } 128 | ], 129 | "variants": {}, 130 | "id": "gIoDTuK3S5JFDCZDtbA0IWW0hJ1vXmr3Opf5mABb4hg" 131 | } 132 | ] 133 | }, 134 | "num_comments": 1596, 135 | "thumbnail": "http://b.thumbs.redditmedia.com/1fX47ot3K3a7RV8VmmQhkrpURUweWgaJ6831eN--XUg.jpg", 136 | "subreddit_id": "t5_2qh33", 137 | "hide_score": false, 138 | "edited": false, 139 | "link_flair_css_class": null, 140 | "author_flair_css_class": null, 141 | "downs": 0, 142 | "secure_media_embed": {}, 143 | "saved": false, 144 | "removal_reason": null, 145 | "post_hint": "image", 146 | "stickied": false, 147 | "from": null, 148 | "is_self": false, 149 | "from_id": null, 150 | "permalink": "/r/funny/comments/4vq7rj/due_to_all_the_health_hazards_surrounding_the_rio/", 151 | "locked": false, 152 | "name": "t3_4vq7rj", 153 | "created": 1470132973, 154 | "url": "http://i.imgur.com/FApqk3D.jpg", 155 | "author_flair_text": null, 156 | "quarantine": false, 157 | "title": "Due to all the health hazards surrounding the Rio Olympics, I figured they could use a new logo. [OC]", 158 | "created_utc": 1470104173, 159 | "distinguished": null, 160 | "mod_reports": [], 161 | "visited": false, 162 | "num_reports": null, 163 | "ups": 8096 164 | } 165 | }, 166 | { 167 | "kind": "t3", 168 | "data": { 169 | "domain": "good.is", 170 | "banned_by": null, 171 | "media_embed": {}, 172 | "subreddit": "worldnews", 173 | "selftext_html": null, 174 | "selftext": "", 175 | "likes": null, 176 | "suggested_sort": null, 177 | "user_reports": [], 178 | "secure_media": null, 179 | "link_flair_text": null, 180 | "id": "4s7cd1", 181 | "from_kind": null, 182 | "gilded": 0, 183 | "archived": false, 184 | "clicked": false, 185 | "report_reasons": null, 186 | "author": "anutensil", 187 | "media": null, 188 | "score": 7127, 189 | "approved_by": null, 190 | "over_18": false, 191 | "hidden": false, 192 | "num_comments": 1868, 193 | "thumbnail": "", 194 | "subreddit_id": "t5_2qh13", 195 | "hide_score": false, 196 | "edited": false, 197 | "link_flair_css_class": null, 198 | "author_flair_css_class": null, 199 | "downs": 0, 200 | "secure_media_embed": {}, 201 | "saved": false, 202 | "removal_reason": null, 203 | "stickied": false, 204 | "from": null, 205 | "is_self": false, 206 | "from_id": null, 207 | "permalink": "/r/worldnews/comments/4s7cd1/eating_endangered_species_is_now_illegal_in_china/", 208 | "locked": false, 209 | "name": "t3_4s7cd1", 210 | "created": 1468213068, 211 | "url": "https://www.good.is/articles/no-more-endangered-species-for-dinner", 212 | "author_flair_text": null, 213 | "quarantine": false, 214 | "title": "Eating Endangered Species Is Now Illegal In China", 215 | "created_utc": 1468184268, 216 | "distinguished": null, 217 | "mod_reports": [], 218 | "visited": false, 219 | "num_reports": null, 220 | "ups": 7127 221 | } 222 | }, 223 | { 224 | "kind": "t3", 225 | "data": { 226 | "domain": "en.wikipedia.org", 227 | "banned_by": null, 228 | "media_embed": {}, 229 | "subreddit": "todayilearned", 230 | "selftext_html": null, 231 | "selftext": "", 232 | "likes": null, 233 | "suggested_sort": null, 234 | "user_reports": [], 235 | "secure_media": null, 236 | "link_flair_text": null, 237 | "id": "4uj9ea", 238 | "from_kind": null, 239 | "gilded": 0, 240 | "archived": false, 241 | "clicked": false, 242 | "report_reasons": null, 243 | "author": "I-am-theEggman", 244 | "media": null, 245 | "score": 7102, 246 | "approved_by": null, 247 | "over_18": false, 248 | "hidden": false, 249 | "num_comments": 1063, 250 | "thumbnail": "default", 251 | "subreddit_id": "t5_2qqjc", 252 | "hide_score": false, 253 | "edited": false, 254 | "link_flair_css_class": null, 255 | "author_flair_css_class": null, 256 | "downs": 0, 257 | "secure_media_embed": {}, 258 | "saved": false, 259 | "removal_reason": null, 260 | "stickied": false, 261 | "from": null, 262 | "is_self": false, 263 | "from_id": null, 264 | "permalink": "/r/todayilearned/comments/4uj9ea/til_in_1898_nikola_tesla_once_tricked_an_entire/", 265 | "locked": false, 266 | "name": "t3_4uj9ea", 267 | "created": 1469490551, 268 | "url": "https://en.wikipedia.org/wiki/Radio_control#cite_note-1", 269 | "author_flair_text": null, 270 | "quarantine": false, 271 | "title": "TIL In 1898 Nikola Tesla once tricked an entire crowd into believing they could control a toy boat by shouting commands - he had in fact invented Radio Control and was piloting the boat himself.", 272 | "created_utc": 1469461751, 273 | "distinguished": null, 274 | "mod_reports": [], 275 | "visited": false, 276 | "num_reports": null, 277 | "ups": 7102 278 | } 279 | }, 280 | { 281 | "kind": "t3", 282 | "data": { 283 | "domain": "imgur.com", 284 | "banned_by": null, 285 | "media_embed": {}, 286 | "subreddit": "funny", 287 | "selftext_html": null, 288 | "selftext": "", 289 | "likes": null, 290 | "suggested_sort": null, 291 | "user_reports": [], 292 | "secure_media": null, 293 | "link_flair_text": null, 294 | "id": "4ue7g8", 295 | "from_kind": null, 296 | "gilded": 0, 297 | "archived": false, 298 | "clicked": false, 299 | "report_reasons": null, 300 | "author": "stankydeerbawls", 301 | "media": null, 302 | "score": 7096, 303 | "approved_by": null, 304 | "over_18": false, 305 | "hidden": false, 306 | "preview": { 307 | "images": [ 308 | { 309 | "source": { 310 | "url": "https://i.redditmedia.com/ZJEo-C2-3xvQJQORuMjzkgruVC2nzv_SreVZTxfi3lI.jpg?s=8e749f7f8c61fcc4bc0b5518152dd5f6", 311 | "width": 900, 312 | "height": 1200 313 | }, 314 | "resolutions": [ 315 | { 316 | "url": "https://i.redditmedia.com/ZJEo-C2-3xvQJQORuMjzkgruVC2nzv_SreVZTxfi3lI.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=108&s=98f1cce2784ad8e1d3ff74943a747691", 317 | "width": 108, 318 | "height": 144 319 | }, 320 | { 321 | "url": "https://i.redditmedia.com/ZJEo-C2-3xvQJQORuMjzkgruVC2nzv_SreVZTxfi3lI.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=216&s=4b460b6bde8d2c3a97ab6ff6e63a6339", 322 | "width": 216, 323 | "height": 288 324 | }, 325 | { 326 | "url": "https://i.redditmedia.com/ZJEo-C2-3xvQJQORuMjzkgruVC2nzv_SreVZTxfi3lI.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=320&s=2386b8af7267d39556c6064aadeb2d5f", 327 | "width": 320, 328 | "height": 426 329 | }, 330 | { 331 | "url": "https://i.redditmedia.com/ZJEo-C2-3xvQJQORuMjzkgruVC2nzv_SreVZTxfi3lI.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=640&s=553846974e289f9387eb07e7ef9658fb", 332 | "width": 640, 333 | "height": 853 334 | } 335 | ], 336 | "variants": {}, 337 | "id": "RhISwyYjVgjki99hAr7c8C58FgzcPPvo9y2vOoWpZms" 338 | } 339 | ] 340 | }, 341 | "num_comments": 234, 342 | "thumbnail": "http://b.thumbs.redditmedia.com/MZaH3i1yy1uOb3RN1UuSWFwiTrXekos-JdSpFlcWGEE.jpg", 343 | "subreddit_id": "t5_2qh33", 344 | "hide_score": false, 345 | "edited": false, 346 | "link_flair_css_class": null, 347 | "author_flair_css_class": null, 348 | "downs": 0, 349 | "secure_media_embed": {}, 350 | "saved": false, 351 | "removal_reason": null, 352 | "post_hint": "link", 353 | "stickied": false, 354 | "from": null, 355 | "is_self": false, 356 | "from_id": null, 357 | "permalink": "/r/funny/comments/4ue7g8/captain_obvious_at_todays_padres_vs_nationals_game/", 358 | "locked": false, 359 | "name": "t3_4ue7g8", 360 | "created": 1469411974, 361 | "url": "http://imgur.com/ndGPjZl", 362 | "author_flair_text": null, 363 | "quarantine": false, 364 | "title": "Captain Obvious at today's Padres vs. Nationals game.", 365 | "created_utc": 1469383174, 366 | "distinguished": null, 367 | "mod_reports": [], 368 | "visited": false, 369 | "num_reports": null, 370 | "ups": 7096 371 | } 372 | }, 373 | { 374 | "kind": "t3", 375 | "data": { 376 | "domain": "youtube.com", 377 | "banned_by": null, 378 | "media_embed": { 379 | "content": "<iframe width=\"600\" height=\"338\" src=\"https://www.youtube.com/embed/VVV4xeWBIxE?feature=oembed\" frameborder=\"0\" allowfullscreen></iframe>", 380 | "width": 600, 381 | "scrolling": false, 382 | "height": 338 383 | }, 384 | "subreddit": "videos", 385 | "selftext_html": null, 386 | "selftext": "", 387 | "likes": null, 388 | "suggested_sort": null, 389 | "user_reports": [], 390 | "secure_media": { 391 | "type": "youtube.com", 392 | "oembed": { 393 | "provider_url": "https://www.youtube.com/", 394 | "version": "1.0", 395 | "title": "Primitive Technology: Forge Blower", 396 | "type": "video", 397 | "thumbnail_width": 480, 398 | "height": 338, 399 | "width": 600, 400 | "html": "<iframe width=\"600\" height=\"338\" src=\"https://www.youtube.com/embed/VVV4xeWBIxE?feature=oembed\" frameborder=\"0\" allowfullscreen></iframe>", 401 | "author_name": "Primitive Technology", 402 | "provider_name": "YouTube", 403 | "thumbnail_url": "https://i.ytimg.com/vi/VVV4xeWBIxE/hqdefault.jpg", 404 | "thumbnail_height": 360, 405 | "author_url": "https://www.youtube.com/channel/UCAL3JXZSzSm8AlZyD3nQdBA" 406 | } 407 | }, 408 | "link_flair_text": null, 409 | "id": "4v8w5n", 410 | "from_kind": null, 411 | "gilded": 0, 412 | "archived": false, 413 | "clicked": false, 414 | "report_reasons": null, 415 | "author": "octaviousprime", 416 | "media": { 417 | "type": "youtube.com", 418 | "oembed": { 419 | "provider_url": "https://www.youtube.com/", 420 | "version": "1.0", 421 | "title": "Primitive Technology: Forge Blower", 422 | "type": "video", 423 | "thumbnail_width": 480, 424 | "height": 338, 425 | "width": 600, 426 | "html": "<iframe width=\"600\" height=\"338\" src=\"https://www.youtube.com/embed/VVV4xeWBIxE?feature=oembed\" frameborder=\"0\" allowfullscreen></iframe>", 427 | "author_name": "Primitive Technology", 428 | "provider_name": "YouTube", 429 | "thumbnail_url": "https://i.ytimg.com/vi/VVV4xeWBIxE/hqdefault.jpg", 430 | "thumbnail_height": 360, 431 | "author_url": "https://www.youtube.com/channel/UCAL3JXZSzSm8AlZyD3nQdBA" 432 | } 433 | }, 434 | "score": 7090, 435 | "approved_by": null, 436 | "over_18": false, 437 | "hidden": false, 438 | "preview": { 439 | "images": [ 440 | { 441 | "source": { 442 | "url": "https://i.redditmedia.com/lmhbmLG9I_br6IFxlRTWbXEffR1mBUOwXP0_8G9XPUg.jpg?s=e31c2039569885902df34292a51eaf62", 443 | "width": 480, 444 | "height": 360 445 | }, 446 | "resolutions": [ 447 | { 448 | "url": "https://i.redditmedia.com/lmhbmLG9I_br6IFxlRTWbXEffR1mBUOwXP0_8G9XPUg.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=108&s=cc0f4c5993b641650dc07e40751f4c89", 449 | "width": 108, 450 | "height": 81 451 | }, 452 | { 453 | "url": "https://i.redditmedia.com/lmhbmLG9I_br6IFxlRTWbXEffR1mBUOwXP0_8G9XPUg.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=216&s=ad6af34e84b75b09f1dc2ff8d7c87077", 454 | "width": 216, 455 | "height": 162 456 | }, 457 | { 458 | "url": "https://i.redditmedia.com/lmhbmLG9I_br6IFxlRTWbXEffR1mBUOwXP0_8G9XPUg.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=320&s=7e1d470d49cfd49978bcfb27c9d12fc8", 459 | "width": 320, 460 | "height": 240 461 | } 462 | ], 463 | "variants": {}, 464 | "id": "ip4qCoRLhBO62DXn_guHrcs8y6h5Q61kdke18FW4kw4" 465 | } 466 | ] 467 | }, 468 | "num_comments": 3168, 469 | "thumbnail": "http://b.thumbs.redditmedia.com/E1R15cPG8TCtC1EMrZvi0rfNwXKpPbeaE-8CqtWWn2E.jpg", 470 | "subreddit_id": "t5_2qh1e", 471 | "hide_score": false, 472 | "edited": false, 473 | "link_flair_css_class": null, 474 | "author_flair_css_class": null, 475 | "downs": 0, 476 | "secure_media_embed": { 477 | "content": "<iframe width=\"600\" height=\"338\" src=\"https://www.youtube.com/embed/VVV4xeWBIxE?feature=oembed\" frameborder=\"0\" allowfullscreen></iframe>", 478 | "width": 600, 479 | "scrolling": false, 480 | "height": 338 481 | }, 482 | "saved": false, 483 | "removal_reason": null, 484 | "post_hint": "rich:video", 485 | "stickied": false, 486 | "from": null, 487 | "is_self": false, 488 | "from_id": null, 489 | "permalink": "/r/videos/comments/4v8w5n/primitive_technology_forge_blower/", 490 | "locked": false, 491 | "name": "t3_4v8w5n", 492 | "created": 1469855801, 493 | "url": "https://www.youtube.com/watch?v=VVV4xeWBIxE", 494 | "author_flair_text": null, 495 | "quarantine": false, 496 | "title": "Primitive Technology: Forge Blower", 497 | "created_utc": 1469827001, 498 | "distinguished": null, 499 | "mod_reports": [], 500 | "visited": false, 501 | "num_reports": null, 502 | "ups": 7090 503 | } 504 | }, 505 | { 506 | "kind": "t3", 507 | "data": { 508 | "domain": "kwqc.com", 509 | "banned_by": null, 510 | "media_embed": {}, 511 | "subreddit": "funny", 512 | "selftext_html": null, 513 | "selftext": "", 514 | "likes": null, 515 | "suggested_sort": null, 516 | "user_reports": [], 517 | "secure_media": null, 518 | "link_flair_text": null, 519 | "id": "4rdi08", 520 | "from_kind": null, 521 | "gilded": 0, 522 | "archived": false, 523 | "clicked": false, 524 | "report_reasons": null, 525 | "author": "relishcuriosity", 526 | "media": null, 527 | "score": 6967, 528 | "approved_by": null, 529 | "over_18": false, 530 | "hidden": false, 531 | "preview": { 532 | "images": [ 533 | { 534 | "source": { 535 | "url": "https://i.redditmedia.com/5agyGlS4n3LfLZQMuukOc2sJkvI11UYE-QWwFKn91eo.jpg?s=5fc1654af1ec02b81e9f85532b0aceac", 536 | "width": 311, 537 | "height": 226 538 | }, 539 | "resolutions": [ 540 | { 541 | "url": "https://i.redditmedia.com/5agyGlS4n3LfLZQMuukOc2sJkvI11UYE-QWwFKn91eo.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=108&s=6c82341e44e5d1697b08edd23d4a744f", 542 | "width": 108, 543 | "height": 78 544 | }, 545 | { 546 | "url": "https://i.redditmedia.com/5agyGlS4n3LfLZQMuukOc2sJkvI11UYE-QWwFKn91eo.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=216&s=5af74d5eac2dca383369ebafec5363cd", 547 | "width": 216, 548 | "height": 156 549 | } 550 | ], 551 | "variants": {}, 552 | "id": "4dyfSZBYTDwZntBXXPlBliYI08-uUBRJKcZAXCA5XI8" 553 | } 554 | ] 555 | }, 556 | "num_comments": 2459, 557 | "thumbnail": "http://b.thumbs.redditmedia.com/qjdnWgHRXq0GpDzR6yX7zV0JlcbrkBrL2WON1sK9qgM.jpg", 558 | "subreddit_id": "t5_2qh33", 559 | "hide_score": false, 560 | "edited": false, 561 | "link_flair_css_class": null, 562 | "author_flair_css_class": null, 563 | "downs": 0, 564 | "secure_media_embed": {}, 565 | "saved": false, 566 | "removal_reason": null, 567 | "post_hint": "link", 568 | "stickied": false, 569 | "from": null, 570 | "is_self": false, 571 | "from_id": null, 572 | "permalink": "/r/funny/comments/4rdi08/iowa_man_jailed_for_shooting_fireworks_at_4_am/", 573 | "locked": false, 574 | "name": "t3_4rdi08", 575 | "created": 1467764617, 576 | "url": "https://kwqc.com/2016/07/05/iowa-man-jailed-for-shooting-fireworks-because-this-is-america/", 577 | "author_flair_text": null, 578 | "quarantine": false, 579 | "title": "Iowa man jailed for shooting fireworks at 4 a.m. and telling police he'd shoot more fireworks “with a blunt in his mouth because this is America.” When asked to take a preliminary breathalyzer test, he responded “that he would take a preliminary go [expletive] yourself test.”", 580 | "created_utc": 1467735817, 581 | "distinguished": null, 582 | "mod_reports": [], 583 | "visited": false, 584 | "num_reports": null, 585 | "ups": 6967 586 | } 587 | }, 588 | { 589 | "kind": "t3", 590 | "data": { 591 | "domain": "i.reddituploads.com", 592 | "banned_by": null, 593 | "media_embed": {}, 594 | "subreddit": "funny", 595 | "selftext_html": null, 596 | "selftext": "", 597 | "likes": null, 598 | "suggested_sort": null, 599 | "user_reports": [], 600 | "secure_media": null, 601 | "link_flair_text": null, 602 | "id": "4tgb6f", 603 | "from_kind": null, 604 | "gilded": 0, 605 | "archived": false, 606 | "clicked": false, 607 | "report_reasons": null, 608 | "author": "wrxryuu", 609 | "media": null, 610 | "score": 6929, 611 | "approved_by": null, 612 | "over_18": false, 613 | "hidden": false, 614 | "preview": { 615 | "images": [ 616 | { 617 | "source": { 618 | "url": "https://i.redditmedia.com/PWk7SgcaJLd9IrHnyOehAD1RNfQaZInWcRaMCZSMTfI.jpg?s=4fa52b54de93c3f7288604d1c19932f9", 619 | "width": 539, 620 | "height": 380 621 | }, 622 | "resolutions": [ 623 | { 624 | "url": "https://i.redditmedia.com/PWk7SgcaJLd9IrHnyOehAD1RNfQaZInWcRaMCZSMTfI.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=108&s=117896e115615da51e7cdeaede007b28", 625 | "width": 108, 626 | "height": 76 627 | }, 628 | { 629 | "url": "https://i.redditmedia.com/PWk7SgcaJLd9IrHnyOehAD1RNfQaZInWcRaMCZSMTfI.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=216&s=75eedc5a8765f0249aab97027efe2d80", 630 | "width": 216, 631 | "height": 152 632 | }, 633 | { 634 | "url": "https://i.redditmedia.com/PWk7SgcaJLd9IrHnyOehAD1RNfQaZInWcRaMCZSMTfI.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=320&s=20e0db085a38c8769f67fd459dff812a", 635 | "width": 320, 636 | "height": 225 637 | } 638 | ], 639 | "variants": {}, 640 | "id": "ZWQVvCfWF6Z7p_LQ8PrA06f230UG6xCrKTx44NBvZso" 641 | } 642 | ] 643 | }, 644 | "num_comments": 410, 645 | "thumbnail": "http://b.thumbs.redditmedia.com/GkI1377VKlfCTuz2DA9003R-ozL09x458HlNPLg_3Os.jpg", 646 | "subreddit_id": "t5_2qh33", 647 | "hide_score": false, 648 | "edited": false, 649 | "link_flair_css_class": null, 650 | "author_flair_css_class": null, 651 | "downs": 0, 652 | "secure_media_embed": {}, 653 | "saved": false, 654 | "removal_reason": null, 655 | "post_hint": "link", 656 | "stickied": false, 657 | "from": null, 658 | "is_self": false, 659 | "from_id": null, 660 | "permalink": "/r/funny/comments/4tgb6f/finally_a_bathroom_for_me_and_my_magnum_dong/", 661 | "locked": false, 662 | "name": "t3_4tgb6f", 663 | "created": 1468891492, 664 | "url": "https://i.reddituploads.com/484175e3fc37431da402f9e036218b72?fit=max&h=1536&w=1536&s=4754721ed1145a0dc8dec8dff2e135e0", 665 | "author_flair_text": null, 666 | "quarantine": false, 667 | "title": "Finally, a bathroom for me and my magnum dong.", 668 | "created_utc": 1468862692, 669 | "distinguished": null, 670 | "mod_reports": [], 671 | "visited": false, 672 | "num_reports": null, 673 | "ups": 6929 674 | } 675 | }, 676 | { 677 | "kind": "t3", 678 | "data": { 679 | "domain": "rt.com", 680 | "banned_by": null, 681 | "media_embed": {}, 682 | "subreddit": "worldnews", 683 | "selftext_html": null, 684 | "selftext": "", 685 | "likes": null, 686 | "suggested_sort": null, 687 | "user_reports": [], 688 | "secure_media": null, 689 | "link_flair_text": "Turkey", 690 | "id": "4tq4sd", 691 | "from_kind": null, 692 | "gilded": 0, 693 | "archived": false, 694 | "clicked": false, 695 | "report_reasons": null, 696 | "author": "monkeyseemonkeydoodo", 697 | "media": null, 698 | "score": 6919, 699 | "approved_by": null, 700 | "over_18": false, 701 | "hidden": false, 702 | "num_comments": 5413, 703 | "thumbnail": "", 704 | "subreddit_id": "t5_2qh13", 705 | "hide_score": false, 706 | "edited": false, 707 | "link_flair_css_class": "turkey", 708 | "author_flair_css_class": null, 709 | "downs": 0, 710 | "secure_media_embed": {}, 711 | "saved": false, 712 | "removal_reason": null, 713 | "stickied": false, 714 | "from": null, 715 | "is_self": false, 716 | "from_id": null, 717 | "permalink": "/r/worldnews/comments/4tq4sd/all_turkish_academics_banned_from_traveling/", 718 | "locked": false, 719 | "name": "t3_4tq4sd", 720 | "created": 1469030800, 721 | "url": "https://www.rt.com/news/352218-turkey-academics-ban-travel/", 722 | "author_flair_text": null, 723 | "quarantine": false, 724 | "title": "All Turkish academics banned from traveling abroad – report", 725 | "created_utc": 1469002000, 726 | "distinguished": null, 727 | "mod_reports": [], 728 | "visited": false, 729 | "num_reports": null, 730 | "ups": 6919 731 | } 732 | }, 733 | { 734 | "kind": "t3", 735 | "data": { 736 | "domain": "washingtonpost.com", 737 | "banned_by": null, 738 | "media_embed": {}, 739 | "subreddit": "politics", 740 | "selftext_html": null, 741 | "selftext": "", 742 | "likes": null, 743 | "suggested_sort": null, 744 | "user_reports": [], 745 | "secure_media": null, 746 | "link_flair_text": null, 747 | "id": "4uj3aq", 748 | "from_kind": null, 749 | "gilded": 0, 750 | "archived": false, 751 | "clicked": false, 752 | "report_reasons": null, 753 | "author": "gAlienLifeform", 754 | "media": null, 755 | "score": 6914, 756 | "approved_by": null, 757 | "over_18": false, 758 | "hidden": false, 759 | "preview": { 760 | "images": [ 761 | { 762 | "source": { 763 | "url": "https://i.redditmedia.com/enJvXCwXr5zH5s_ekGoV_W8piVKOXRXtbjppXKMbMxA.jpg?s=d8ab2e58e95345597d5cd08de7636164", 764 | "width": 1484, 765 | "height": 1182 766 | }, 767 | "resolutions": [ 768 | { 769 | "url": "https://i.redditmedia.com/enJvXCwXr5zH5s_ekGoV_W8piVKOXRXtbjppXKMbMxA.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=108&s=3cfadd88e091c6295258400edac72acd", 770 | "width": 108, 771 | "height": 86 772 | }, 773 | { 774 | "url": "https://i.redditmedia.com/enJvXCwXr5zH5s_ekGoV_W8piVKOXRXtbjppXKMbMxA.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=216&s=fdac1d62756bdae7868045bcf1dd5cfe", 775 | "width": 216, 776 | "height": 172 777 | }, 778 | { 779 | "url": "https://i.redditmedia.com/enJvXCwXr5zH5s_ekGoV_W8piVKOXRXtbjppXKMbMxA.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=320&s=19df38e35128b9dcb7696efc586a8c50", 780 | "width": 320, 781 | "height": 254 782 | }, 783 | { 784 | "url": "https://i.redditmedia.com/enJvXCwXr5zH5s_ekGoV_W8piVKOXRXtbjppXKMbMxA.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=640&s=c2f0021317d0ff1e161fe15987aaece4", 785 | "width": 640, 786 | "height": 509 787 | }, 788 | { 789 | "url": "https://i.redditmedia.com/enJvXCwXr5zH5s_ekGoV_W8piVKOXRXtbjppXKMbMxA.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=960&s=ec79e72351bfc33c90e2908db9757cb6", 790 | "width": 960, 791 | "height": 764 792 | }, 793 | { 794 | "url": "https://i.redditmedia.com/enJvXCwXr5zH5s_ekGoV_W8piVKOXRXtbjppXKMbMxA.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=1080&s=8624c937ddb2736995ddcdaccd1472d9", 795 | "width": 1080, 796 | "height": 860 797 | } 798 | ], 799 | "variants": {}, 800 | "id": "Pz--AT62wr-Jq8h1d12yc1J2NovawDXB-puqPwNPuDE" 801 | } 802 | ] 803 | }, 804 | "num_comments": 2362, 805 | "thumbnail": "http://a.thumbs.redditmedia.com/o20w1KAF4JUnzkqDhDGSNqWZGuOwdjcheW691VZjVF8.jpg", 806 | "subreddit_id": "t5_2cneq", 807 | "hide_score": false, 808 | "edited": false, 809 | "link_flair_css_class": null, 810 | "author_flair_css_class": null, 811 | "downs": 0, 812 | "secure_media_embed": {}, 813 | "saved": false, 814 | "removal_reason": null, 815 | "post_hint": "link", 816 | "stickied": false, 817 | "from": null, 818 | "is_self": false, 819 | "from_id": null, 820 | "permalink": "/r/politics/comments/4uj3aq/it_took_pressure_from_the_white_house_including_a/", 821 | "locked": false, 822 | "name": "t3_4uj3aq", 823 | "created": 1469488511, 824 | "url": "https://www.washingtonpost.com/news/powerpost/paloma/daily-202/2016/07/25/daily-202-with-dws-ouster-on-eve-of-convention-10-questions-for-the-week-ahead-in-philadelphia/57956b5d4acce20505133c10/", 825 | "author_flair_text": null, 826 | "quarantine": false, 827 | "title": "\"It took pressure from the White House – including a phone call with President Obama – to get her to finally see the writing on the wall. Two reliable sources say Wasserman Schultz was trying to make top aides take the fall, rather than take personal responsibility.\"", 828 | "created_utc": 1469459711, 829 | "distinguished": null, 830 | "mod_reports": [], 831 | "visited": false, 832 | "num_reports": null, 833 | "ups": 6914 834 | } 835 | }, 836 | { 837 | "kind": "t3", 838 | "data": { 839 | "domain": "independent.co.uk", 840 | "banned_by": null, 841 | "media_embed": {}, 842 | "subreddit": "worldnews", 843 | "selftext_html": null, 844 | "selftext": "", 845 | "likes": null, 846 | "suggested_sort": null, 847 | "user_reports": [], 848 | "secure_media": null, 849 | "link_flair_text": null, 850 | "id": "4vmei9", 851 | "from_kind": null, 852 | "gilded": 0, 853 | "archived": false, 854 | "clicked": false, 855 | "report_reasons": null, 856 | "author": "tryin2immigrate", 857 | "media": null, 858 | "score": 6896, 859 | "approved_by": null, 860 | "over_18": false, 861 | "hidden": false, 862 | "num_comments": 5607, 863 | "thumbnail": "", 864 | "subreddit_id": "t5_2qh13", 865 | "hide_score": false, 866 | "edited": false, 867 | "link_flair_css_class": null, 868 | "author_flair_css_class": null, 869 | "downs": 0, 870 | "secure_media_embed": {}, 871 | "saved": false, 872 | "removal_reason": null, 873 | "stickied": false, 874 | "from": null, 875 | "is_self": false, 876 | "from_id": null, 877 | "permalink": "/r/worldnews/comments/4vmei9/rio_2016_swimmers_need_to_ingest_only_three/", 878 | "locked": false, 879 | "name": "t3_4vmei9", 880 | "created": 1470085727, 881 | "url": "http://www.independent.co.uk/sport/olympics/rio-2016-water-pollution-virus-risk-danger-swimming-sailing-rowing-chance-of-infection-almost-a7165866.html", 882 | "author_flair_text": null, 883 | "quarantine": false, 884 | "title": "Rio 2016: Swimmers need to ingest only three teaspoons of water to be almost certain of contracting a virus | Olympics | Sport", 885 | "created_utc": 1470056927, 886 | "distinguished": null, 887 | "mod_reports": [], 888 | "visited": false, 889 | "num_reports": null, 890 | "ups": 6896 891 | } 892 | }, 893 | { 894 | "kind": "t3", 895 | "data": { 896 | "domain": "rt.com", 897 | "banned_by": null, 898 | "media_embed": {}, 899 | "subreddit": "worldnews", 900 | "selftext_html": null, 901 | "selftext": "", 902 | "likes": null, 903 | "suggested_sort": null, 904 | "user_reports": [], 905 | "secure_media": null, 906 | "link_flair_text": "Turkey", 907 | "id": "4u7k9i", 908 | "from_kind": null, 909 | "gilded": 0, 910 | "archived": false, 911 | "clicked": false, 912 | "report_reasons": null, 913 | "author": "P_leoAtrox", 914 | "media": null, 915 | "score": 6890, 916 | "approved_by": null, 917 | "over_18": false, 918 | "hidden": false, 919 | "num_comments": 2424, 920 | "thumbnail": "", 921 | "subreddit_id": "t5_2qh13", 922 | "hide_score": false, 923 | "edited": false, 924 | "link_flair_css_class": "turkey", 925 | "author_flair_css_class": null, 926 | "downs": 0, 927 | "secure_media_embed": {}, 928 | "saved": false, 929 | "removal_reason": null, 930 | "stickied": false, 931 | "from": null, 932 | "is_self": false, 933 | "from_id": null, 934 | "permalink": "/r/worldnews/comments/4u7k9i/erdogan_shuts_down_1000_private_schools_1200/", 935 | "locked": false, 936 | "name": "t3_4u7k9i", 937 | "created": 1469294631, 938 | "url": "https://www.rt.com/news/352867-erdogan-closes-schools-emergency/", 939 | "author_flair_text": null, 940 | "quarantine": false, 941 | "title": "Erdogan shuts down 1,000+ private schools, 1,200+ charities, 15 universities", 942 | "created_utc": 1469265831, 943 | "distinguished": null, 944 | "mod_reports": [], 945 | "visited": false, 946 | "num_reports": null, 947 | "ups": 6890 948 | } 949 | }, 950 | { 951 | "kind": "t3", 952 | "data": { 953 | "domain": "streamable.com", 954 | "banned_by": null, 955 | "media_embed": { 956 | "content": "<iframe class=\"embedly-embed\" src=\"//cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fstreamable.com%2Fe%2Fh6bd&url=https%3A%2F%2Fstreamable.com%2Fh6bd&image=https%3A%2F%2Fcdn.streamable.com%2Fimage%2Fh6bd.jpg&key=2aa3c4d5f3de4f5b9120b660ad850dc9&type=text%2Fhtml&schema=streamable\" width=\"600\" height=\"338\" scrolling=\"no\" frameborder=\"0\" allowfullscreen></iframe>", 957 | "width": 600, 958 | "scrolling": false, 959 | "height": 338 960 | }, 961 | "subreddit": "videos", 962 | "selftext_html": null, 963 | "selftext": "", 964 | "likes": null, 965 | "suggested_sort": null, 966 | "user_reports": [], 967 | "secure_media": { 968 | "type": "streamable.com", 969 | "oembed": { 970 | "provider_url": "https://www.streamable.com", 971 | "description": "Check out this video on Streamable using your phone, tablet or desktop.", 972 | "title": "Streamable - simple video sharing", 973 | "thumbnail_width": 1280, 974 | "height": 338, 975 | "width": 600, 976 | "html": "<iframe class=\"embedly-embed\" src=\"https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fstreamable.com%2Fe%2Fh6bd&url=https%3A%2F%2Fstreamable.com%2Fh6bd&image=https%3A%2F%2Fcdn.streamable.com%2Fimage%2Fh6bd.jpg&key=2aa3c4d5f3de4f5b9120b660ad850dc9&type=text%2Fhtml&schema=streamable\" width=\"600\" height=\"338\" scrolling=\"no\" frameborder=\"0\" allowfullscreen></iframe>", 977 | "version": "1.0", 978 | "provider_name": "Streamable", 979 | "thumbnail_url": "https://i.embed.ly/1/image?url=https%3A%2F%2Fcdn.streamable.com%2Fimage%2Fh6bd.jpg&key=b1e305db91cf4aa5a86b732cc9fffceb", 980 | "type": "video", 981 | "thumbnail_height": 720 982 | } 983 | }, 984 | "link_flair_text": null, 985 | "id": "4veyil", 986 | "from_kind": null, 987 | "gilded": 0, 988 | "archived": false, 989 | "clicked": false, 990 | "report_reasons": null, 991 | "author": "GanjaDingo", 992 | "media": { 993 | "type": "streamable.com", 994 | "oembed": { 995 | "provider_url": "https://www.streamable.com", 996 | "description": "Check out this video on Streamable using your phone, tablet or desktop.", 997 | "title": "Streamable - simple video sharing", 998 | "thumbnail_width": 1280, 999 | "height": 338, 1000 | "width": 600, 1001 | "html": "<iframe class=\"embedly-embed\" src=\"//cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fstreamable.com%2Fe%2Fh6bd&url=https%3A%2F%2Fstreamable.com%2Fh6bd&image=https%3A%2F%2Fcdn.streamable.com%2Fimage%2Fh6bd.jpg&key=2aa3c4d5f3de4f5b9120b660ad850dc9&type=text%2Fhtml&schema=streamable\" width=\"600\" height=\"338\" scrolling=\"no\" frameborder=\"0\" allowfullscreen></iframe>", 1002 | "version": "1.0", 1003 | "provider_name": "Streamable", 1004 | "thumbnail_url": "https://cdn.streamable.com/image/h6bd.jpg", 1005 | "type": "video", 1006 | "thumbnail_height": 720 1007 | } 1008 | }, 1009 | "score": 6889, 1010 | "approved_by": null, 1011 | "over_18": false, 1012 | "hidden": false, 1013 | "preview": { 1014 | "images": [ 1015 | { 1016 | "source": { 1017 | "url": "https://i.redditmedia.com/tIxdAj8qw4K7baJGREegQ5_X3ysahlE2Fr847DdV1x0.jpg?s=f0ee16267169be41a928adf3b6f5b887", 1018 | "width": 1280, 1019 | "height": 720 1020 | }, 1021 | "resolutions": [ 1022 | { 1023 | "url": "https://i.redditmedia.com/tIxdAj8qw4K7baJGREegQ5_X3ysahlE2Fr847DdV1x0.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=108&s=37d2d756c4159a44928a85e561eb049f", 1024 | "width": 108, 1025 | "height": 60 1026 | }, 1027 | { 1028 | "url": "https://i.redditmedia.com/tIxdAj8qw4K7baJGREegQ5_X3ysahlE2Fr847DdV1x0.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=216&s=1d1ad3cf4c4ce467fcc36b937650e170", 1029 | "width": 216, 1030 | "height": 121 1031 | }, 1032 | { 1033 | "url": "https://i.redditmedia.com/tIxdAj8qw4K7baJGREegQ5_X3ysahlE2Fr847DdV1x0.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=320&s=46b0c0db5ca9262aaf890d38917045b6", 1034 | "width": 320, 1035 | "height": 180 1036 | }, 1037 | { 1038 | "url": "https://i.redditmedia.com/tIxdAj8qw4K7baJGREegQ5_X3ysahlE2Fr847DdV1x0.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=640&s=7053f206e01e01ae94287ba7044c5a83", 1039 | "width": 640, 1040 | "height": 360 1041 | }, 1042 | { 1043 | "url": "https://i.redditmedia.com/tIxdAj8qw4K7baJGREegQ5_X3ysahlE2Fr847DdV1x0.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=960&s=f516c2f562c21766c1481c4a8da67ceb", 1044 | "width": 960, 1045 | "height": 540 1046 | }, 1047 | { 1048 | "url": "https://i.redditmedia.com/tIxdAj8qw4K7baJGREegQ5_X3ysahlE2Fr847DdV1x0.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=1080&s=937b88c18ef536f61d59e08aa727b78a", 1049 | "width": 1080, 1050 | "height": 607 1051 | } 1052 | ], 1053 | "variants": {}, 1054 | "id": "L7h2NTL4EzhrRlu24U05syKoZCpvb8vSoHeb5HAVTLY" 1055 | } 1056 | ] 1057 | }, 1058 | "num_comments": 6042, 1059 | "thumbnail": "http://a.thumbs.redditmedia.com/YW19m0PrytN6CbUTYnDwlczE6Hr-yfMMK6K-rkgbe04.jpg", 1060 | "subreddit_id": "t5_2qh1e", 1061 | "hide_score": false, 1062 | "edited": false, 1063 | "link_flair_css_class": null, 1064 | "author_flair_css_class": null, 1065 | "downs": 0, 1066 | "secure_media_embed": { 1067 | "content": "<iframe class=\"embedly-embed\" src=\"https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fstreamable.com%2Fe%2Fh6bd&url=https%3A%2F%2Fstreamable.com%2Fh6bd&image=https%3A%2F%2Fcdn.streamable.com%2Fimage%2Fh6bd.jpg&key=2aa3c4d5f3de4f5b9120b660ad850dc9&type=text%2Fhtml&schema=streamable\" width=\"600\" height=\"338\" scrolling=\"no\" frameborder=\"0\" allowfullscreen></iframe>", 1068 | "width": 600, 1069 | "scrolling": false, 1070 | "height": 338 1071 | }, 1072 | "saved": false, 1073 | "removal_reason": null, 1074 | "post_hint": "rich:video", 1075 | "stickied": false, 1076 | "from": null, 1077 | "is_self": false, 1078 | "from_id": null, 1079 | "permalink": "/r/videos/comments/4veyil/a_guy_just_successfully_jumped_from_25000_ft_with/", 1080 | "locked": false, 1081 | "name": "t3_4veyil", 1082 | "created": 1469956077, 1083 | "url": "https://streamable.com/h6bd", 1084 | "author_flair_text": null, 1085 | "quarantine": false, 1086 | "title": "A guy just successfully jumped from 25,000 ft with no parachute, into a net!", 1087 | "created_utc": 1469927277, 1088 | "distinguished": null, 1089 | "mod_reports": [], 1090 | "visited": false, 1091 | "num_reports": null, 1092 | "ups": 6889 1093 | } 1094 | }, 1095 | { 1096 | "kind": "t3", 1097 | "data": { 1098 | "domain": "news.com.au", 1099 | "banned_by": null, 1100 | "media_embed": {}, 1101 | "subreddit": "worldnews", 1102 | "selftext_html": null, 1103 | "selftext": "", 1104 | "likes": null, 1105 | "suggested_sort": null, 1106 | "user_reports": [], 1107 | "secure_media": null, 1108 | "link_flair_text": null, 1109 | "id": "4uyq4d", 1110 | "from_kind": null, 1111 | "gilded": 0, 1112 | "archived": false, 1113 | "clicked": false, 1114 | "report_reasons": null, 1115 | "author": "arbili", 1116 | "media": null, 1117 | "score": 6865, 1118 | "approved_by": null, 1119 | "over_18": false, 1120 | "hidden": false, 1121 | "num_comments": 3302, 1122 | "thumbnail": "", 1123 | "subreddit_id": "t5_2qh13", 1124 | "hide_score": false, 1125 | "edited": false, 1126 | "link_flair_css_class": null, 1127 | "author_flair_css_class": null, 1128 | "downs": 0, 1129 | "secure_media_embed": {}, 1130 | "saved": false, 1131 | "removal_reason": null, 1132 | "stickied": false, 1133 | "from": null, 1134 | "is_self": false, 1135 | "from_id": null, 1136 | "permalink": "/r/worldnews/comments/4uyq4d/rio_2016_olympic_athletes_told_to_keep_your_mouth/", 1137 | "locked": false, 1138 | "name": "t3_4uyq4d", 1139 | "created": 1469706203, 1140 | "url": "http://www.news.com.au/sport/olympics/rio-2016-olympic-athletes-told-to-keep-your-mouth-closed-when-in-contaminated-water/news-story/45e6ae4643f4dbd4001281f669b898c1", 1141 | "author_flair_text": null, 1142 | "quarantine": false, 1143 | "title": "Rio 2016: Olympic athletes told to ‘keep your mouth closed’ when in contaminated water", 1144 | "created_utc": 1469677403, 1145 | "distinguished": null, 1146 | "mod_reports": [], 1147 | "visited": false, 1148 | "num_reports": null, 1149 | "ups": 6865 1150 | } 1151 | }, 1152 | { 1153 | "kind": "t3", 1154 | "data": { 1155 | "domain": "i.imgur.com", 1156 | "banned_by": null, 1157 | "media_embed": {}, 1158 | "subreddit": "WTF", 1159 | "selftext_html": null, 1160 | "selftext": "", 1161 | "likes": null, 1162 | "suggested_sort": null, 1163 | "user_reports": [], 1164 | "secure_media": null, 1165 | "link_flair_text": null, 1166 | "id": "4v2iz0", 1167 | "from_kind": null, 1168 | "gilded": 0, 1169 | "archived": false, 1170 | "clicked": false, 1171 | "report_reasons": null, 1172 | "author": "ppaed", 1173 | "media": null, 1174 | "score": 6852, 1175 | "approved_by": null, 1176 | "over_18": false, 1177 | "hidden": false, 1178 | "num_comments": 1894, 1179 | "thumbnail": "", 1180 | "subreddit_id": "t5_2qh61", 1181 | "hide_score": false, 1182 | "edited": false, 1183 | "link_flair_css_class": null, 1184 | "author_flair_css_class": null, 1185 | "downs": 0, 1186 | "secure_media_embed": {}, 1187 | "saved": false, 1188 | "removal_reason": null, 1189 | "stickied": false, 1190 | "from": null, 1191 | "is_self": false, 1192 | "from_id": null, 1193 | "permalink": "/r/WTF/comments/4v2iz0/man_uses_a_car_wash_hose_to_chase_away_hijackers/", 1194 | "locked": false, 1195 | "name": "t3_4v2iz0", 1196 | "created": 1469763070, 1197 | "url": "http://i.imgur.com/rWmyS6m.gifv", 1198 | "author_flair_text": null, 1199 | "quarantine": false, 1200 | "title": "Man uses a car wash hose to chase away hijackers", 1201 | "created_utc": 1469734270, 1202 | "distinguished": null, 1203 | "mod_reports": [], 1204 | "visited": false, 1205 | "num_reports": null, 1206 | "ups": 6852 1207 | } 1208 | }, 1209 | { 1210 | "kind": "t3", 1211 | "data": { 1212 | "domain": "hurriyetdailynews.com", 1213 | "banned_by": null, 1214 | "media_embed": {}, 1215 | "subreddit": "worldnews", 1216 | "selftext_html": null, 1217 | "selftext": "", 1218 | "likes": null, 1219 | "suggested_sort": null, 1220 | "user_reports": [], 1221 | "secure_media": null, 1222 | "link_flair_text": "Turkey", 1223 | "id": "4twhwz", 1224 | "from_kind": null, 1225 | "gilded": 0, 1226 | "archived": false, 1227 | "clicked": false, 1228 | "report_reasons": null, 1229 | "author": "r721", 1230 | "media": null, 1231 | "score": 6849, 1232 | "approved_by": null, 1233 | "over_18": false, 1234 | "hidden": false, 1235 | "num_comments": 4523, 1236 | "thumbnail": "", 1237 | "subreddit_id": "t5_2qh13", 1238 | "hide_score": false, 1239 | "edited": false, 1240 | "link_flair_css_class": "turkey", 1241 | "author_flair_css_class": null, 1242 | "downs": 0, 1243 | "secure_media_embed": {}, 1244 | "saved": false, 1245 | "removal_reason": null, 1246 | "stickied": false, 1247 | "from": null, 1248 | "is_self": false, 1249 | "from_id": null, 1250 | "permalink": "/r/worldnews/comments/4twhwz/turkey_to_temporarily_suspend_european_convention/", 1251 | "locked": false, 1252 | "name": "t3_4twhwz", 1253 | "created": 1469129013, 1254 | "url": "http://www.hurriyetdailynews.com/turkey-to-temporarily-suspend-european-convention-on-human-rights-after-coup-attempt.aspx?pageID=238&nid=101910&NewsCatID=338", 1255 | "author_flair_text": null, 1256 | "quarantine": false, 1257 | "title": "Turkey to temporarily suspend European Convention on Human Rights after coup attempt", 1258 | "created_utc": 1469100213, 1259 | "distinguished": null, 1260 | "mod_reports": [], 1261 | "visited": false, 1262 | "num_reports": null, 1263 | "ups": 6849 1264 | } 1265 | }, 1266 | { 1267 | "kind": "t3", 1268 | "data": { 1269 | "domain": "imgur.com", 1270 | "banned_by": null, 1271 | "media_embed": {}, 1272 | "subreddit": "funny", 1273 | "selftext_html": null, 1274 | "selftext": "", 1275 | "likes": null, 1276 | "suggested_sort": null, 1277 | "user_reports": [], 1278 | "secure_media": null, 1279 | "link_flair_text": null, 1280 | "id": "4rhvqx", 1281 | "from_kind": null, 1282 | "gilded": 0, 1283 | "archived": false, 1284 | "clicked": false, 1285 | "report_reasons": null, 1286 | "author": "Jorogasm", 1287 | "media": null, 1288 | "score": 6840, 1289 | "approved_by": null, 1290 | "over_18": false, 1291 | "hidden": false, 1292 | "preview": { 1293 | "images": [ 1294 | { 1295 | "source": { 1296 | "url": "https://i.redditmedia.com/3bNZTN3ymXSzasSPSibG7rfkZHvAnB1Kb-5OsaaRatc.jpg?s=50c9c2310e54b2eb89ff20523b1ff196", 1297 | "width": 773, 1298 | "height": 775 1299 | }, 1300 | "resolutions": [ 1301 | { 1302 | "url": "https://i.redditmedia.com/3bNZTN3ymXSzasSPSibG7rfkZHvAnB1Kb-5OsaaRatc.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=108&s=cd168a148cb86911c066cd218a061eeb", 1303 | "width": 108, 1304 | "height": 108 1305 | }, 1306 | { 1307 | "url": "https://i.redditmedia.com/3bNZTN3ymXSzasSPSibG7rfkZHvAnB1Kb-5OsaaRatc.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=216&s=12c1ad51349158dacc5c951e7c4da4e9", 1308 | "width": 216, 1309 | "height": 216 1310 | }, 1311 | { 1312 | "url": "https://i.redditmedia.com/3bNZTN3ymXSzasSPSibG7rfkZHvAnB1Kb-5OsaaRatc.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=320&s=c21e984af3379cc52a1290723dfdbf2e", 1313 | "width": 320, 1314 | "height": 320 1315 | }, 1316 | { 1317 | "url": "https://i.redditmedia.com/3bNZTN3ymXSzasSPSibG7rfkZHvAnB1Kb-5OsaaRatc.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=640&s=c0e343da018c212c333c3fd9373c1d07", 1318 | "width": 640, 1319 | "height": 641 1320 | } 1321 | ], 1322 | "variants": {}, 1323 | "id": "WGAsyHgFyx-CBuRJjOGWZlsO213Lm1KPj1eXH9J1WcU" 1324 | } 1325 | ] 1326 | }, 1327 | "num_comments": 1201, 1328 | "thumbnail": "http://a.thumbs.redditmedia.com/o5L9Gnl8aQ63PL7e5JAwTK5AbVV-MTR3DKJWEGD0MQ0.jpg", 1329 | "subreddit_id": "t5_2qh33", 1330 | "hide_score": false, 1331 | "edited": false, 1332 | "link_flair_css_class": null, 1333 | "author_flair_css_class": null, 1334 | "downs": 0, 1335 | "secure_media_embed": {}, 1336 | "saved": false, 1337 | "removal_reason": null, 1338 | "post_hint": "link", 1339 | "stickied": false, 1340 | "from": null, 1341 | "is_self": false, 1342 | "from_id": null, 1343 | "permalink": "/r/funny/comments/4rhvqx/is_the_person_naming_these_yarns_okay/", 1344 | "locked": false, 1345 | "name": "t3_4rhvqx", 1346 | "created": 1467830016, 1347 | "url": "http://imgur.com/uoZQEbp", 1348 | "author_flair_text": null, 1349 | "quarantine": false, 1350 | "title": "Is the person naming these yarns okay?", 1351 | "created_utc": 1467801216, 1352 | "distinguished": null, 1353 | "mod_reports": [], 1354 | "visited": false, 1355 | "num_reports": null, 1356 | "ups": 6840 1357 | } 1358 | }, 1359 | { 1360 | "kind": "t3", 1361 | "data": { 1362 | "domain": "i.reddituploads.com", 1363 | "banned_by": null, 1364 | "media_embed": {}, 1365 | "subreddit": "funny", 1366 | "selftext_html": null, 1367 | "selftext": "", 1368 | "likes": null, 1369 | "suggested_sort": null, 1370 | "user_reports": [], 1371 | "secure_media": null, 1372 | "link_flair_text": null, 1373 | "id": "4t5orl", 1374 | "from_kind": null, 1375 | "gilded": 0, 1376 | "archived": false, 1377 | "clicked": false, 1378 | "report_reasons": null, 1379 | "author": "switchfootball", 1380 | "media": null, 1381 | "score": 6850, 1382 | "approved_by": null, 1383 | "over_18": false, 1384 | "hidden": false, 1385 | "preview": { 1386 | "images": [ 1387 | { 1388 | "source": { 1389 | "url": "https://i.redditmedia.com/A0ld4z79PvkSENXJcrmxIwauD6kx2prqho0p7ccjvtk.jpg?s=2716faa5671ef452c278a826a43f870e", 1390 | "width": 1118, 1391 | "height": 1536 1392 | }, 1393 | "resolutions": [ 1394 | { 1395 | "url": "https://i.redditmedia.com/A0ld4z79PvkSENXJcrmxIwauD6kx2prqho0p7ccjvtk.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=108&s=cacfad8a437f93d976f0658e6c7b7704", 1396 | "width": 108, 1397 | "height": 148 1398 | }, 1399 | { 1400 | "url": "https://i.redditmedia.com/A0ld4z79PvkSENXJcrmxIwauD6kx2prqho0p7ccjvtk.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=216&s=d4c1fbeb9aca9accbfc37425a612693a", 1401 | "width": 216, 1402 | "height": 296 1403 | }, 1404 | { 1405 | "url": "https://i.redditmedia.com/A0ld4z79PvkSENXJcrmxIwauD6kx2prqho0p7ccjvtk.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=320&s=ab546fb6d2866388cd3b89e5e560685e", 1406 | "width": 320, 1407 | "height": 439 1408 | }, 1409 | { 1410 | "url": "https://i.redditmedia.com/A0ld4z79PvkSENXJcrmxIwauD6kx2prqho0p7ccjvtk.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=640&s=5a71301700fe12c12544bbc464e0dcd4", 1411 | "width": 640, 1412 | "height": 879 1413 | }, 1414 | { 1415 | "url": "https://i.redditmedia.com/A0ld4z79PvkSENXJcrmxIwauD6kx2prqho0p7ccjvtk.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=960&s=a74c1acc20af90c50665eafaeeaae980", 1416 | "width": 960, 1417 | "height": 1318 1418 | }, 1419 | { 1420 | "url": "https://i.redditmedia.com/A0ld4z79PvkSENXJcrmxIwauD6kx2prqho0p7ccjvtk.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=1080&s=8c569d8da5f2bdb0bfa6758228758b4b", 1421 | "width": 1080, 1422 | "height": 1483 1423 | } 1424 | ], 1425 | "variants": {}, 1426 | "id": "CUK3Fcc-7uNHMd-aVUeLuBaY9cAfWulzWoS75Py-8uk" 1427 | } 1428 | ] 1429 | }, 1430 | "num_comments": 2155, 1431 | "thumbnail": "http://b.thumbs.redditmedia.com/3vCLlXJqLbm9-QL-gyeqR2XGAEK8MmPSsmzNoZErOIg.jpg", 1432 | "subreddit_id": "t5_2qh33", 1433 | "hide_score": false, 1434 | "edited": false, 1435 | "link_flair_css_class": null, 1436 | "author_flair_css_class": null, 1437 | "downs": 0, 1438 | "secure_media_embed": {}, 1439 | "saved": false, 1440 | "removal_reason": null, 1441 | "post_hint": "link", 1442 | "stickied": false, 1443 | "from": null, 1444 | "is_self": false, 1445 | "from_id": null, 1446 | "permalink": "/r/funny/comments/4t5orl/year_no_4_at_my_wifes_family_reunion/", 1447 | "locked": false, 1448 | "name": "t3_4t5orl", 1449 | "created": 1468721064, 1450 | "url": "https://i.reddituploads.com/902c882162eb452e91bb3e207de6fef1?fit=max&h=1536&w=1536&s=1199c1148974dd21827a2732a4a15cfd", 1451 | "author_flair_text": null, 1452 | "quarantine": false, 1453 | "title": "Year No. 4 at my wife's family reunion.", 1454 | "created_utc": 1468692264, 1455 | "distinguished": null, 1456 | "mod_reports": [], 1457 | "visited": false, 1458 | "num_reports": null, 1459 | "ups": 6850 1460 | } 1461 | }, 1462 | { 1463 | "kind": "t3", 1464 | "data": { 1465 | "domain": "top40.about.com", 1466 | "banned_by": null, 1467 | "media_embed": {}, 1468 | "subreddit": "todayilearned", 1469 | "selftext_html": null, 1470 | "selftext": "", 1471 | "likes": null, 1472 | "suggested_sort": null, 1473 | "user_reports": [], 1474 | "secure_media": null, 1475 | "link_flair_text": null, 1476 | "id": "4r6uds", 1477 | "from_kind": null, 1478 | "gilded": 0, 1479 | "archived": false, 1480 | "clicked": false, 1481 | "report_reasons": null, 1482 | "author": "BizarroCullen", 1483 | "media": null, 1484 | "score": 6831, 1485 | "approved_by": null, 1486 | "over_18": false, 1487 | "hidden": false, 1488 | "preview": { 1489 | "images": [ 1490 | { 1491 | "source": { 1492 | "url": "https://i.redditmedia.com/sISKTjEGKTGmHZgSGrKCD-mKsX1SwV-g6595wVfKK24.jpg?s=cab24ab18027c3ca83d6b876a903cdc5", 1493 | "width": 640, 1494 | "height": 427 1495 | }, 1496 | "resolutions": [ 1497 | { 1498 | "url": "https://i.redditmedia.com/sISKTjEGKTGmHZgSGrKCD-mKsX1SwV-g6595wVfKK24.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=108&s=6d6976393c247bbc7af4e06d40ecf570", 1499 | "width": 108, 1500 | "height": 72 1501 | }, 1502 | { 1503 | "url": "https://i.redditmedia.com/sISKTjEGKTGmHZgSGrKCD-mKsX1SwV-g6595wVfKK24.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=216&s=e1211ee901266cbfdc3c02aa8a1f77c4", 1504 | "width": 216, 1505 | "height": 144 1506 | }, 1507 | { 1508 | "url": "https://i.redditmedia.com/sISKTjEGKTGmHZgSGrKCD-mKsX1SwV-g6595wVfKK24.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=320&s=62bed020bde0d0e01d425c0b1a393fc3", 1509 | "width": 320, 1510 | "height": 213 1511 | }, 1512 | { 1513 | "url": "https://i.redditmedia.com/sISKTjEGKTGmHZgSGrKCD-mKsX1SwV-g6595wVfKK24.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=640&s=7a0a8e4a74340ed21bf9482fee86f30f", 1514 | "width": 640, 1515 | "height": 427 1516 | } 1517 | ], 1518 | "variants": {}, 1519 | "id": "6O02hpI52C-pZy9RmoaFD200Txy8X_N-KjkL27NwSPw" 1520 | } 1521 | ] 1522 | }, 1523 | "num_comments": 473, 1524 | "thumbnail": "http://a.thumbs.redditmedia.com/HjsEQHBl3ttz3h3kX0PcIXTku37bhzBTobXsfL0yFT8.jpg", 1525 | "subreddit_id": "t5_2qqjc", 1526 | "hide_score": false, 1527 | "edited": false, 1528 | "link_flair_css_class": null, 1529 | "author_flair_css_class": null, 1530 | "downs": 0, 1531 | "secure_media_embed": {}, 1532 | "saved": false, 1533 | "removal_reason": null, 1534 | "post_hint": "link", 1535 | "stickied": false, 1536 | "from": null, 1537 | "is_self": false, 1538 | "from_id": null, 1539 | "permalink": "/r/todayilearned/comments/4r6uds/til_that_the_reason_top_40_has_specifically_forty/", 1540 | "locked": false, 1541 | "name": "t3_4r6uds", 1542 | "created": 1467661057, 1543 | "url": "http://top40.about.com/od/popmusic101/a/top40.htm", 1544 | "author_flair_text": null, 1545 | "quarantine": false, 1546 | "title": "TIL that the reason Top 40 has specifically forty singles is because standard jukebox machines held 40 songs at that time.", 1547 | "created_utc": 1467632257, 1548 | "distinguished": null, 1549 | "mod_reports": [], 1550 | "visited": false, 1551 | "num_reports": null, 1552 | "ups": 6831 1553 | } 1554 | }, 1555 | { 1556 | "kind": "t3", 1557 | "data": { 1558 | "domain": "theintercept.com", 1559 | "banned_by": null, 1560 | "media_embed": {}, 1561 | "subreddit": "worldnews", 1562 | "selftext_html": null, 1563 | "selftext": "", 1564 | "likes": null, 1565 | "suggested_sort": null, 1566 | "user_reports": [], 1567 | "secure_media": null, 1568 | "link_flair_text": null, 1569 | "id": "4tc8x5", 1570 | "from_kind": null, 1571 | "gilded": 0, 1572 | "archived": false, 1573 | "clicked": false, 1574 | "report_reasons": null, 1575 | "author": "bodobobo", 1576 | "media": null, 1577 | "score": 6834, 1578 | "approved_by": null, 1579 | "over_18": false, 1580 | "hidden": false, 1581 | "num_comments": 3894, 1582 | "thumbnail": "", 1583 | "subreddit_id": "t5_2qh13", 1584 | "hide_score": false, 1585 | "edited": false, 1586 | "link_flair_css_class": null, 1587 | "author_flair_css_class": null, 1588 | "downs": 0, 1589 | "secure_media_embed": {}, 1590 | "saved": false, 1591 | "removal_reason": null, 1592 | "stickied": false, 1593 | "from": null, 1594 | "is_self": false, 1595 | "from_id": null, 1596 | "permalink": "/r/worldnews/comments/4tc8x5/saudi_ties_to_911_detailed_in_documents/", 1597 | "locked": false, 1598 | "name": "t3_4tc8x5", 1599 | "created": 1468828189, 1600 | "url": "https://theintercept.com/2016/07/15/saudi-ties-to-911-detailed-in-documents-suppressed-since-2002/", 1601 | "author_flair_text": null, 1602 | "quarantine": false, 1603 | "title": "Saudi Ties to 9/11 Detailed in Documents Suppressed Since 2002", 1604 | "created_utc": 1468799389, 1605 | "distinguished": null, 1606 | "mod_reports": [], 1607 | "visited": false, 1608 | "num_reports": null, 1609 | "ups": 6834 1610 | } 1611 | }, 1612 | { 1613 | "kind": "t3", 1614 | "data": { 1615 | "domain": "i.reddituploads.com", 1616 | "banned_by": null, 1617 | "media_embed": {}, 1618 | "subreddit": "funny", 1619 | "selftext_html": null, 1620 | "selftext": "", 1621 | "likes": null, 1622 | "suggested_sort": null, 1623 | "user_reports": [], 1624 | "secure_media": null, 1625 | "link_flair_text": null, 1626 | "id": "4t3j2f", 1627 | "from_kind": null, 1628 | "gilded": 0, 1629 | "archived": false, 1630 | "clicked": false, 1631 | "report_reasons": null, 1632 | "author": "batgaz", 1633 | "media": null, 1634 | "score": 6792, 1635 | "approved_by": null, 1636 | "over_18": false, 1637 | "hidden": false, 1638 | "preview": { 1639 | "images": [ 1640 | { 1641 | "source": { 1642 | "url": "https://i.redditmedia.com/vIp24QjVtWBP-bn7hFJDDj3SfYjrSy3A1BhX6hmnYaI.jpg?s=0cc33e02308ad0c48b9b24340032557a", 1643 | "width": 720, 1644 | "height": 960 1645 | }, 1646 | "resolutions": [ 1647 | { 1648 | "url": "https://i.redditmedia.com/vIp24QjVtWBP-bn7hFJDDj3SfYjrSy3A1BhX6hmnYaI.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=108&s=b08f33079ac749a4b295bed6398f23e3", 1649 | "width": 108, 1650 | "height": 144 1651 | }, 1652 | { 1653 | "url": "https://i.redditmedia.com/vIp24QjVtWBP-bn7hFJDDj3SfYjrSy3A1BhX6hmnYaI.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=216&s=8a88c8187763ea5416e8a9d54f365f78", 1654 | "width": 216, 1655 | "height": 288 1656 | }, 1657 | { 1658 | "url": "https://i.redditmedia.com/vIp24QjVtWBP-bn7hFJDDj3SfYjrSy3A1BhX6hmnYaI.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=320&s=2da7d2f54d77b92ac23d1442c6c1bd99", 1659 | "width": 320, 1660 | "height": 426 1661 | }, 1662 | { 1663 | "url": "https://i.redditmedia.com/vIp24QjVtWBP-bn7hFJDDj3SfYjrSy3A1BhX6hmnYaI.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=640&s=2a7c853330d894e5a90dae86badb1a22", 1664 | "width": 640, 1665 | "height": 853 1666 | } 1667 | ], 1668 | "variants": {}, 1669 | "id": "fbKclQJBXqsRSnpBZKVs1YDTim_4UQTDWheUcltniqo" 1670 | } 1671 | ] 1672 | }, 1673 | "num_comments": 1245, 1674 | "thumbnail": "http://b.thumbs.redditmedia.com/yyaAik8v8W0rggroagtAhyhECylVkVTdOHIwekZz2jk.jpg", 1675 | "subreddit_id": "t5_2qh33", 1676 | "hide_score": false, 1677 | "edited": false, 1678 | "link_flair_css_class": null, 1679 | "author_flair_css_class": null, 1680 | "downs": 0, 1681 | "secure_media_embed": {}, 1682 | "saved": false, 1683 | "removal_reason": null, 1684 | "post_hint": "link", 1685 | "stickied": false, 1686 | "from": null, 1687 | "is_self": false, 1688 | "from_id": null, 1689 | "permalink": "/r/funny/comments/4t3j2f/my_pregnant_wife_sent_this_to_me_at_212am_this/", 1690 | "locked": false, 1691 | "name": "t3_4t3j2f", 1692 | "created": 1468681404, 1693 | "url": "https://i.reddituploads.com/f60774811e5c42848d394d396d7df083?fit=max&h=1536&w=1536&s=214279a4d106d58c2be80b6f7545239c", 1694 | "author_flair_text": null, 1695 | "quarantine": false, 1696 | "title": "My pregnant wife sent this to me at 2:12am this morning. I'm going to guess that my snoring was quite bad last night.", 1697 | "created_utc": 1468652604, 1698 | "distinguished": null, 1699 | "mod_reports": [], 1700 | "visited": false, 1701 | "num_reports": null, 1702 | "ups": 6792 1703 | } 1704 | }, 1705 | { 1706 | "kind": "t3", 1707 | "data": { 1708 | "domain": "news.com.au", 1709 | "banned_by": null, 1710 | "media_embed": {}, 1711 | "subreddit": "worldnews", 1712 | "selftext_html": null, 1713 | "selftext": "", 1714 | "likes": null, 1715 | "suggested_sort": null, 1716 | "user_reports": [], 1717 | "secure_media": null, 1718 | "link_flair_text": null, 1719 | "id": "4szcpz", 1720 | "from_kind": null, 1721 | "gilded": 0, 1722 | "archived": false, 1723 | "clicked": false, 1724 | "report_reasons": null, 1725 | "author": "itoitoito", 1726 | "media": null, 1727 | "score": 6785, 1728 | "approved_by": null, 1729 | "over_18": false, 1730 | "hidden": false, 1731 | "num_comments": 2787, 1732 | "thumbnail": "", 1733 | "subreddit_id": "t5_2qh13", 1734 | "hide_score": false, 1735 | "edited": false, 1736 | "link_flair_css_class": null, 1737 | "author_flair_css_class": null, 1738 | "downs": 0, 1739 | "secure_media_embed": {}, 1740 | "saved": false, 1741 | "removal_reason": null, 1742 | "stickied": false, 1743 | "from": null, 1744 | "is_self": false, 1745 | "from_id": null, 1746 | "permalink": "/r/worldnews/comments/4szcpz/asbestos_found_in_new_12b_perth_childrens/", 1747 | "locked": false, 1748 | "name": "t3_4szcpz", 1749 | "created": 1468621784, 1750 | "url": "http://www.news.com.au/lifestyle/health/asbestos-found-in-new-12b-perth-childrens-hospital/news-story/2c391ab14fe220ebaeeee42b41cafa0a", 1751 | "author_flair_text": null, 1752 | "quarantine": false, 1753 | "title": "Asbestos found in new $1.2b Perth children’s hospital - roof panels made in China", 1754 | "created_utc": 1468592984, 1755 | "distinguished": null, 1756 | "mod_reports": [], 1757 | "visited": false, 1758 | "num_reports": null, 1759 | "ups": 6785 1760 | } 1761 | }, 1762 | { 1763 | "kind": "t3", 1764 | "data": { 1765 | "domain": "i.imgur.com", 1766 | "banned_by": null, 1767 | "media_embed": {}, 1768 | "subreddit": "WTF", 1769 | "selftext_html": null, 1770 | "selftext": "", 1771 | "likes": null, 1772 | "suggested_sort": null, 1773 | "user_reports": [], 1774 | "secure_media": null, 1775 | "link_flair_text": null, 1776 | "id": "4up25e", 1777 | "from_kind": null, 1778 | "gilded": 0, 1779 | "archived": false, 1780 | "clicked": false, 1781 | "report_reasons": null, 1782 | "author": "Magmafat", 1783 | "media": null, 1784 | "score": 6778, 1785 | "approved_by": null, 1786 | "over_18": false, 1787 | "hidden": false, 1788 | "num_comments": 1909, 1789 | "thumbnail": "", 1790 | "subreddit_id": "t5_2qh61", 1791 | "hide_score": false, 1792 | "edited": false, 1793 | "link_flair_css_class": null, 1794 | "author_flair_css_class": null, 1795 | "downs": 0, 1796 | "secure_media_embed": {}, 1797 | "saved": false, 1798 | "removal_reason": null, 1799 | "stickied": false, 1800 | "from": null, 1801 | "is_self": false, 1802 | "from_id": null, 1803 | "permalink": "/r/WTF/comments/4up25e/rio_cocaine_dealers_now_using_the_olympic_logo/", 1804 | "locked": false, 1805 | "name": "t3_4up25e", 1806 | "created": 1469574204, 1807 | "url": "http://i.imgur.com/2YiwhCe.jpg", 1808 | "author_flair_text": null, 1809 | "quarantine": false, 1810 | "title": "Rio cocaine dealers now using the Olympic logo, plus the warning \"don't use near children\"", 1811 | "created_utc": 1469545404, 1812 | "distinguished": null, 1813 | "mod_reports": [], 1814 | "visited": false, 1815 | "num_reports": null, 1816 | "ups": 6778 1817 | } 1818 | }, 1819 | { 1820 | "kind": "t3", 1821 | "data": { 1822 | "domain": "streamable.com", 1823 | "banned_by": null, 1824 | "media_embed": { 1825 | "content": "<iframe class=\"embedly-embed\" src=\"//cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fstreamable.com%2Fe%2Fsqj4&url=https%3A%2F%2Fstreamable.com%2Fsqj4&image=http%3A%2F%2Fcdn.streamable.com%2Fimage%2Fsqj4.jpg&key=2aa3c4d5f3de4f5b9120b660ad850dc9&type=text%2Fhtml&schema=streamable\" width=\"398\" height=\"224\" scrolling=\"no\" frameborder=\"0\" allowfullscreen></iframe>", 1826 | "width": 398, 1827 | "scrolling": false, 1828 | "height": 224 1829 | }, 1830 | "subreddit": "videos", 1831 | "selftext_html": null, 1832 | "selftext": "", 1833 | "likes": null, 1834 | "suggested_sort": null, 1835 | "user_reports": [], 1836 | "secure_media": { 1837 | "type": "streamable.com", 1838 | "oembed": { 1839 | "provider_url": "https://www.streamable.com", 1840 | "description": "Check out this video on Streamable using your phone, tablet or desktop.", 1841 | "title": "Streamable - simple video sharing", 1842 | "thumbnail_width": 398, 1843 | "height": 224, 1844 | "width": 398, 1845 | "html": "<iframe class=\"embedly-embed\" src=\"https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fstreamable.com%2Fe%2Fsqj4&url=https%3A%2F%2Fstreamable.com%2Fsqj4&image=http%3A%2F%2Fcdn.streamable.com%2Fimage%2Fsqj4.jpg&key=2aa3c4d5f3de4f5b9120b660ad850dc9&type=text%2Fhtml&schema=streamable\" width=\"398\" height=\"224\" scrolling=\"no\" frameborder=\"0\" allowfullscreen></iframe>", 1846 | "version": "1.0", 1847 | "provider_name": "Streamable", 1848 | "thumbnail_url": "https://i.embed.ly/1/image?url=http%3A%2F%2Fcdn.streamable.com%2Fimage%2Fsqj4.jpg&key=b1e305db91cf4aa5a86b732cc9fffceb", 1849 | "type": "video", 1850 | "thumbnail_height": 224 1851 | } 1852 | }, 1853 | "link_flair_text": null, 1854 | "id": "4s8mz8", 1855 | "from_kind": null, 1856 | "gilded": 0, 1857 | "archived": false, 1858 | "clicked": false, 1859 | "report_reasons": null, 1860 | "author": "ItsComingHomeLads", 1861 | "media": { 1862 | "type": "streamable.com", 1863 | "oembed": { 1864 | "provider_url": "https://www.streamable.com", 1865 | "description": "Check out this video on Streamable using your phone, tablet or desktop.", 1866 | "title": "Streamable - simple video sharing", 1867 | "thumbnail_width": 398, 1868 | "height": 224, 1869 | "width": 398, 1870 | "html": "<iframe class=\"embedly-embed\" src=\"//cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fstreamable.com%2Fe%2Fsqj4&url=https%3A%2F%2Fstreamable.com%2Fsqj4&image=http%3A%2F%2Fcdn.streamable.com%2Fimage%2Fsqj4.jpg&key=2aa3c4d5f3de4f5b9120b660ad850dc9&type=text%2Fhtml&schema=streamable\" width=\"398\" height=\"224\" scrolling=\"no\" frameborder=\"0\" allowfullscreen></iframe>", 1871 | "version": "1.0", 1872 | "provider_name": "Streamable", 1873 | "thumbnail_url": "http://cdn.streamable.com/image/sqj4.jpg", 1874 | "type": "video", 1875 | "thumbnail_height": 224 1876 | } 1877 | }, 1878 | "score": 6757, 1879 | "approved_by": null, 1880 | "over_18": false, 1881 | "hidden": false, 1882 | "preview": { 1883 | "images": [ 1884 | { 1885 | "source": { 1886 | "url": "https://i.redditmedia.com/WioQndKL8Xxds0dRzg_Ze10H0D9VSw6ggHc0vN3b8hI.jpg?s=5bd637e6a340bc17dc5e144003ade4a2", 1887 | "width": 398, 1888 | "height": 224 1889 | }, 1890 | "resolutions": [ 1891 | { 1892 | "url": "https://i.redditmedia.com/WioQndKL8Xxds0dRzg_Ze10H0D9VSw6ggHc0vN3b8hI.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=108&s=f2abb8b7143c7dd6ff92dfe20402b52b", 1893 | "width": 108, 1894 | "height": 60 1895 | }, 1896 | { 1897 | "url": "https://i.redditmedia.com/WioQndKL8Xxds0dRzg_Ze10H0D9VSw6ggHc0vN3b8hI.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=216&s=75750143ef67bc21747b32752960cb0a", 1898 | "width": 216, 1899 | "height": 121 1900 | }, 1901 | { 1902 | "url": "https://i.redditmedia.com/WioQndKL8Xxds0dRzg_Ze10H0D9VSw6ggHc0vN3b8hI.jpg?fit=crop&crop=faces%2Centropy&arh=2&w=320&s=8a8c8a00e95853bce6c2920457b3b31d", 1903 | "width": 320, 1904 | "height": 180 1905 | } 1906 | ], 1907 | "variants": {}, 1908 | "id": "_mAC11s9fu8TLYt5EcOq-gFNlW2Dqa2IQnwUQ0mtagI" 1909 | } 1910 | ] 1911 | }, 1912 | "num_comments": 2179, 1913 | "thumbnail": "http://b.thumbs.redditmedia.com/2mgB-d0E-R79-QZWEa83hZfVpbpTS6KwLJxv1xdMeWY.jpg", 1914 | "subreddit_id": "t5_2qh1e", 1915 | "hide_score": false, 1916 | "edited": false, 1917 | "link_flair_css_class": null, 1918 | "author_flair_css_class": null, 1919 | "downs": 0, 1920 | "secure_media_embed": { 1921 | "content": "<iframe class=\"embedly-embed\" src=\"https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fstreamable.com%2Fe%2Fsqj4&url=https%3A%2F%2Fstreamable.com%2Fsqj4&image=http%3A%2F%2Fcdn.streamable.com%2Fimage%2Fsqj4.jpg&key=2aa3c4d5f3de4f5b9120b660ad850dc9&type=text%2Fhtml&schema=streamable\" width=\"398\" height=\"224\" scrolling=\"no\" frameborder=\"0\" allowfullscreen></iframe>", 1922 | "width": 398, 1923 | "scrolling": false, 1924 | "height": 224 1925 | }, 1926 | "saved": false, 1927 | "removal_reason": null, 1928 | "post_hint": "rich:video", 1929 | "stickied": false, 1930 | "from": null, 1931 | "is_self": false, 1932 | "from_id": null, 1933 | "permalink": "/r/videos/comments/4s8mz8/little_portuguese_fan_consoling_a_frenchman_after/", 1934 | "locked": false, 1935 | "name": "t3_4s8mz8", 1936 | "created": 1468231680, 1937 | "url": "https://streamable.com/sqj4", 1938 | "author_flair_text": null, 1939 | "quarantine": false, 1940 | "title": "Little Portuguese fan consoling a Frenchman after their loss", 1941 | "created_utc": 1468202880, 1942 | "distinguished": null, 1943 | "mod_reports": [], 1944 | "visited": false, 1945 | "num_reports": null, 1946 | "ups": 6757 1947 | } 1948 | }, 1949 | { 1950 | "kind": "t3", 1951 | "data": { 1952 | "domain": "abcnews.go.com", 1953 | "banned_by": null, 1954 | "media_embed": {}, 1955 | "subreddit": "politics", 1956 | "selftext_html": null, 1957 | "selftext": "", 1958 | "likes": null, 1959 | "suggested_sort": null, 1960 | "user_reports": [], 1961 | "secure_media": null, 1962 | "link_flair_text": null, 1963 | "id": "4uilgl", 1964 | "from_kind": null, 1965 | "gilded": 0, 1966 | "archived": false, 1967 | "clicked": false, 1968 | "report_reasons": null, 1969 | "author": "Schwa142", 1970 | "media": null, 1971 | "score": 6739, 1972 | "approved_by": null, 1973 | "over_18": false, 1974 | "hidden": false, 1975 | "num_comments": 3462, 1976 | "thumbnail": "default", 1977 | "subreddit_id": "t5_2cneq", 1978 | "hide_score": false, 1979 | "edited": false, 1980 | "link_flair_css_class": null, 1981 | "author_flair_css_class": null, 1982 | "downs": 0, 1983 | "secure_media_embed": {}, 1984 | "saved": false, 1985 | "removal_reason": null, 1986 | "stickied": false, 1987 | "from": null, 1988 | "is_self": false, 1989 | "from_id": null, 1990 | "permalink": "/r/politics/comments/4uilgl/debbie_wasserman_schultz_booed_at_chaotic_florida/", 1991 | "locked": false, 1992 | "name": "t3_4uilgl", 1993 | "created": 1469482207, 1994 | "url": "http://abcnews.go.com/Politics/debbie-wasserman-schultz-booed-chaotic-florida-delegation-breakfast/story?id=40850654", 1995 | "author_flair_text": null, 1996 | "quarantine": false, 1997 | "title": "Debbie Wasserman Schultz Booed at Chaotic Florida Delegation Breakfast", 1998 | "created_utc": 1469453407, 1999 | "distinguished": null, 2000 | "mod_reports": [], 2001 | "visited": false, 2002 | "num_reports": null, 2003 | "ups": 6739 2004 | } 2005 | } 2006 | ], 2007 | "after": "t3_4uilgl", 2008 | "before": null 2009 | } 2010 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.1.2' 9 | classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' 10 | classpath 'me.tatarka:gradle-retrolambda:3.2.5' 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | jcenter() 17 | } 18 | } 19 | 20 | task clean(type: Delete) { 21 | delete rootProject.buildDir 22 | } 23 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codevate/mvvm-reddit/f6d4d1a525370dbe6bd66eca0ab2c493777e3a68/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 28 10:00:20 PST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codevate/mvvm-reddit/f6d4d1a525370dbe6bd66eca0ab2c493777e3a68/images/screenshot.png -------------------------------------------------------------------------------- /images/screenshot_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codevate/mvvm-reddit/f6d4d1a525370dbe6bd66eca0ab2c493777e3a68/images/screenshot_full.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------