First commit

This commit is contained in:
Alex Ning 2018-07-26 23:04:44 +08:00
commit 1f2137dd36
148 changed files with 5912 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.externalNativeBuild

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="WizardSettings">
<option name="children">
<map>
<entry key="imageWizard">
<value>
<PersistentState />
</value>
</entry>
<entry key="vectorWizard">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="vectorAssetStep">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="clipartAsset">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="url" value="jar:file:/home/alex/Android%20Studio/plugins/android/lib/android.jar!/images/material_design_icons/file/ic_file_download_black_24dp.xml" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
<option name="values">
<map>
<entry key="color" value="ffffff" />
<entry key="outputName" value="ic_file_download_white_24dp" />
<entry key="sourceFile" value="$USER_HOME$" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</component>
</project>

Binary file not shown.

View File

@ -0,0 +1,29 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<Objective-C-extensions>
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
</file>
<class>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
</class>
<extensions>
<pair source="cpp" header="h" fileNamingConvention="NONE" />
<pair source="c" header="h" fileNamingConvention="NONE" />
</extensions>
</Objective-C-extensions>
</code_scheme>
</component>

18
.idea/gradle.xml Normal file
View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>

34
.idea/misc.xml Normal file
View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
<option name="myNullables">
<value>
<list size="5">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
<item index="3" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
<item index="4" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
<list size="4">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
</list>
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

9
.idea/modules.xml Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/Infinity.iml" filepath="$PROJECT_DIR$/Infinity.iml" />
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
</modules>
</component>
</project>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

1
app/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

48
app/build.gradle Normal file
View File

@ -0,0 +1,48 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
applicationId "ml.docilealligator.infinityforreddit"
minSdkVersion 21
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
repositories {
maven {
url "http://dl.bintray.com/rilixtech/maven"
}
mavenCentral()
google()
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:28.0.0-alpha3'
implementation 'com.android.support:design:28.0.0-alpha3'
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
implementation 'com.android.support:support-v4:28.0.0-alpha3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.android.volley:volley:1.1.0'
implementation 'de.hdodenhof:circleimageview:2.2.0'
implementation 'com.google.android.exoplayer:exoplayer:2.7.0'
implementation 'com.google.android.exoplayer:exoplayer-dash:2.7.0'
implementation 'com.android.support:customtabs:28.0.0-alpha3'
implementation 'com.alexvasilkov:gesture-views:2.5.2'
implementation 'com.android.support:cardview-v7:28.0.0-alpha3'
implementation 'com.github.bumptech.glide:glide:4.6.1'
implementation 'com.github.pwittchen:swipe-rx2:0.3.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.6.1'
}

21
app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -0,0 +1,26 @@
package ml.docilealligator.infinityforreddit;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("ml.docilealligator.infinityforreddit", appContext.getPackageName());
}
}

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ml.docilealligator.infinityforreddit">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission-sdk-23 android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="22" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".LoginActivity"
android:label="@string/login_activity_label"
android:parentActivityName=".MainActivity" />
<activity
android:name=".ViewImageActivity"
android:parentActivityName=".MainActivity"
android:theme="@style/Theme.AppCompat.Transparent" />
<activity
android:name=".ViewVideoActivity"
android:parentActivityName=".MainActivity"
android:theme="@style/Theme.AppCompat.Transparent" />
<activity
android:name=".ViewPostDetailActivity"
android:parentActivityName=".MainActivity" />
</application>
</manifest>

View File

@ -0,0 +1,86 @@
package ml.docilealligator.infinityforreddit;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import android.widget.Toast;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
/**
* Created by alex on 3/13/18.
*/
class AcquireAccessToken {
interface AcquireAccessTokenListener {
void onAcquireAccessTokenSuccess();
void onAcquireAccessTokenFail();
}
private Context mContext;
private AcquireAccessTokenListener mAcquireAccessTokenListener;
AcquireAccessToken(Context context) {
mContext = context;
}
void refreshAccessToken(RequestQueue refreshQueue, AcquireAccessTokenListener acquireAccessTokenListener) {
if(mContext != null) {
mAcquireAccessTokenListener = acquireAccessTokenListener;
final String refreshToken = mContext.getSharedPreferences(SharedPreferencesUtils.AUTH_CODE_FILE_KEY, Context.MODE_PRIVATE).getString(SharedPreferencesUtils.REFRESH_TOKEN_KEY, "");
StringRequest newTokenRequest = new StringRequest(Request.Method.POST, RedditUtils.ACQUIRE_ACCESS_TOKEN_URL, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
try {
JSONObject jsonObject = new JSONObject(response);
String newAccessToken = jsonObject.getString(RedditUtils.ACCESS_TOKEN_KEY);
SharedPreferences.Editor editor = mContext.getSharedPreferences(SharedPreferencesUtils.AUTH_CODE_FILE_KEY, Context.MODE_PRIVATE).edit();
editor.putString(SharedPreferencesUtils.ACCESS_TOKEN_KEY, newAccessToken);
editor.apply();
mAcquireAccessTokenListener.onAcquireAccessTokenSuccess();
} catch (JSONException e) {
e.printStackTrace();
mAcquireAccessTokenListener.onAcquireAccessTokenFail();
Toast.makeText(mContext, "Error parsing JSON object when getting the access token", Toast.LENGTH_SHORT).show();
Log.i("main activity", "Error parsing JSON object when getting the access token");
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(mContext, "Error getting the new access token", Toast.LENGTH_SHORT).show();
mAcquireAccessTokenListener.onAcquireAccessTokenFail();
Log.i("error get access token", error.getMessage());
}
}) {
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put(RedditUtils.GRANT_TYPE_KEY, RedditUtils.GRANT_TYPE_REFRESH_TOKEN);
params.put(RedditUtils.REFRESH_TOKEN_KEY, refreshToken);
return params;
}
@Override
public Map<String, String> getHeaders() {
return RedditUtils.getHttpBasicAuthHeader();
}
};
newTokenRequest.setTag(AcquireAccessToken.class);
refreshQueue.add(newTokenRequest);
}
}
}

View File

@ -0,0 +1,232 @@
package ml.docilealligator.infinityforreddit;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by alex on 3/1/18.
*/
class BestPostData implements Parcelable {
static final int TEXT_TYPE = 0;
static final int IMAGE_TYPE = 1;
static final int LINK_TYPE = 2;
static final int VIDEO_TYPE = 3;
static final int GIF_VIDEO_TYPE = 4;
static final int NO_PREVIEW_LINK_TYPE = 5;
private String id;
private String fullName;
private String subredditName;
private String postTime;
private String title;
private String selfText;
private String previewUrl;
private String url;
private String videoUrl;
private String gifOrVideoDownloadUrl;
private String permalink;
private int score;
private int postType;
private int voteType;
private boolean nsfw;
private boolean isDashVideo;
private boolean isDownloadableGifOrVideo;
BestPostData(String id, String fullName, String subredditName, String postTime, String title, String previewUrl, String permalink, int score, int postType, int voteType, boolean nsfw, boolean isDashVideo) {
this.id = id;
this.fullName = fullName;
this.subredditName = subredditName;
this.postTime = postTime;
this.title = title;
this.previewUrl = previewUrl;
this.permalink = RedditUtils.API_BASE_URI + permalink;
this.score = score;
this.postType = postType;
this.voteType = voteType;
this.nsfw = nsfw;
this.isDashVideo = isDashVideo;
}
BestPostData(String id, String fullName, String subredditName, String postTime, String title, String previewUrl, String url, String permalink, int score, int postType, int voteType, boolean nsfw) {
this.id = id;
this.fullName = fullName;
this.subredditName = subredditName;
this.postTime = postTime;
this.title = title;
this.previewUrl = previewUrl;
this.url = url;
this.permalink = RedditUtils.API_BASE_URI + permalink;
this.score = score;
this.postType = postType;
this.voteType = voteType;
this.nsfw = nsfw;
}
BestPostData(String id, String fullName, String subredditName, String postTime, String title, String permalink, int score, int postType, int voteType, boolean nsfw) {
this.id = id;
this.fullName = fullName;
this.subredditName = subredditName;
this.postTime = postTime;
this.title = title;
this.permalink = RedditUtils.API_BASE_URI + permalink;
this.score = score;
this.postType = postType;
this.voteType = voteType;
this.nsfw = nsfw;
}
protected BestPostData(Parcel in) {
id = in.readString();
fullName = in.readString();
subredditName = in.readString();
postTime = in.readString();
title = in.readString();
selfText = in.readString();
previewUrl = in.readString();
url = in.readString();
videoUrl = in.readString();
gifOrVideoDownloadUrl = in.readString();
permalink = in.readString();
score = in.readInt();
postType = in.readInt();
voteType = in.readInt();
nsfw = in.readByte() != 0;
isDashVideo = in.readByte() != 0;
isDownloadableGifOrVideo = in.readByte() != 0;
}
public static final Creator<BestPostData> CREATOR = new Creator<BestPostData>() {
@Override
public BestPostData createFromParcel(Parcel in) {
return new BestPostData(in);
}
@Override
public BestPostData[] newArray(int size) {
return new BestPostData[size];
}
};
public String getId() {
return id;
}
public String getFullName() {
return fullName;
}
public String getSubredditName() {
return subredditName;
}
public String getPostTime() {
return postTime;
}
public void setTitle(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
public void setSelfText(String selfText) {
this.selfText = selfText;
}
public String getSelfText() {
return selfText;
}
public String getPreviewUrl() {
return previewUrl;
}
public String getUrl() {
return url;
}
public void setVideoUrl(String videoUrl) {
this.videoUrl = videoUrl;
}
public String getVideoUrl() {
return videoUrl;
}
public String getGifOrVideoDownloadUrl() {
return gifOrVideoDownloadUrl;
}
public void setGifOrVideoDownloadUrl(String gifOrVideoDownloadUrl) {
this.gifOrVideoDownloadUrl = gifOrVideoDownloadUrl;
}
public String getPermalink() {
return permalink;
}
public void setScore(int score) {
this.score = score;
}
public int getScore() {
return score;
}
public int getPostType() {
return postType;
}
public void setVoteType(int voteType) {
this.voteType = voteType;
}
public int getVoteType() {
return voteType;
}
public boolean getNSFW() {
return nsfw;
}
@Override
public int describeContents() {
return 0;
}
public boolean isDashVideo() {
return isDashVideo;
}
public void setDownloadableGifOrVideo(boolean isDownloadableGifOrVideo) {
this.isDownloadableGifOrVideo = isDownloadableGifOrVideo;
}
public boolean isDownloadableGifOrVideo() {
return isDownloadableGifOrVideo;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(id);
parcel.writeString(fullName);
parcel.writeString(subredditName);
parcel.writeString(postTime);
parcel.writeString(title);
parcel.writeString(selfText);
parcel.writeString(previewUrl);
parcel.writeString(url);
parcel.writeString(videoUrl);
parcel.writeString(gifOrVideoDownloadUrl);
parcel.writeString(permalink);
parcel.writeInt(score);
parcel.writeInt(postType);
parcel.writeInt(voteType);
parcel.writeByte((byte) (nsfw ? 1 : 0));
parcel.writeByte((byte) (isDashVideo ? 1 : 0));
parcel.writeByte((byte) (isDownloadableGifOrVideo ? 1 : 0));
}
}

View File

@ -0,0 +1,245 @@
package ml.docilealligator.infinityforreddit;
import android.app.Fragment;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.Toast;
import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import java.util.ArrayList;
import java.util.Map;
/**
* A simple {@link Fragment} subclass.
*/
public class BestPostFragment extends Fragment {
private CoordinatorLayout mCoordinatorLayout;
private RecyclerView mBestPostRecyclerView;
private LinearLayoutManager mLinearLayoutManager;
private ProgressBar mProgressBar;
private ArrayList<BestPostData> mBestPostData;
private String mLastItem;
private PaginationSynchronizer mPaginationSynchronizer;
private BestPostRecyclerViewAdapter mAdapter;
private String bestPostDataParcelableState = "BPDPS";
private String lastItemState = "LIS";
private String paginationSynchronizerState = "PSS";
private RequestQueue mRequestQueue;
private RequestQueue mPaginationRequestQueue;
private RequestQueue mAcquireAccessTokenRequestQueue;
private RequestQueue mVoteThingRequestQueue;
public BestPostFragment() {
// Required empty public constructor
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if(savedInstanceState != null) {
if(savedInstanceState.containsKey(bestPostDataParcelableState)) {
mBestPostData = savedInstanceState.getParcelableArrayList(bestPostDataParcelableState);
mLastItem = savedInstanceState.getString(lastItemState);
mAdapter = new BestPostRecyclerViewAdapter(getActivity(), mBestPostData, mPaginationSynchronizer, mVoteThingRequestQueue, mAcquireAccessTokenRequestQueue);
mBestPostRecyclerView.setAdapter(mAdapter);
mBestPostRecyclerView.addOnScrollListener(new BestPostPaginationScrollListener(getActivity(), mLinearLayoutManager, mAdapter, mLastItem, mBestPostData, mPaginationSynchronizer,
mAcquireAccessTokenRequestQueue, mPaginationSynchronizer.isLoading(), mPaginationSynchronizer.isLoadSuccess()));
mProgressBar.setVisibility(View.GONE);
} else {
queryBestPost(1);
}
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if(mRequestQueue != null) {
mRequestQueue.cancelAll(this);
}
if(mAcquireAccessTokenRequestQueue != null) {
mAcquireAccessTokenRequestQueue.cancelAll(AcquireAccessToken.class);
}
if(mVoteThingRequestQueue != null) {
mVoteThingRequestQueue.cancelAll(VoteThing.class);
}
if(mPaginationRequestQueue != null) {
mPaginationRequestQueue.cancelAll(BestPostPaginationScrollListener.class);
}
if(mBestPostData != null) {
outState.putParcelableArrayList(bestPostDataParcelableState, mBestPostData);
outState.putString(lastItemState, mLastItem);
outState.putParcelable(paginationSynchronizerState, mPaginationSynchronizer);
}
}
@Override
public void onResume() {
super.onResume();
if(mAdapter != null) {
mAdapter.setCanStartActivity(true);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_best_post, container, false);
mCoordinatorLayout = rootView.findViewById(R.id.coordinator_layout_best_post_fragment);
mBestPostRecyclerView = rootView.findViewById(R.id.recycler_view_best_post_fragment);
mLinearLayoutManager = new LinearLayoutManager(getActivity());
mBestPostRecyclerView.setLayoutManager(mLinearLayoutManager);
mProgressBar = rootView.findViewById(R.id.progress_bar_best_post_fragment);
FloatingActionButton fab = rootView.findViewById(R.id.fab_best_post_fragment);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
mRequestQueue = Volley.newRequestQueue(getActivity());
mAcquireAccessTokenRequestQueue = Volley.newRequestQueue(getActivity());
mVoteThingRequestQueue = Volley.newRequestQueue(getActivity());
if(savedInstanceState != null && savedInstanceState.getParcelable(paginationSynchronizerState) != null) {
mPaginationSynchronizer = savedInstanceState.getParcelable(paginationSynchronizerState);
} else {
mPaginationSynchronizer = new PaginationSynchronizer();
queryBestPost(1);
}
LastItemSynchronizer lastItemSynchronizer = new LastItemSynchronizer() {
@Override
public void lastItemChanged(String lastItem) {
mLastItem = lastItem;
}
};
mPaginationSynchronizer.setLastItemSynchronizer(lastItemSynchronizer);
PaginationRequestQueueSynchronizer paginationRequestQueueSynchronizer = new PaginationRequestQueueSynchronizer() {
@Override
public void passQueue(RequestQueue q) {
mPaginationRequestQueue = q;
}
};
mPaginationSynchronizer.setPaginationRequestQueueSynchronizer(paginationRequestQueueSynchronizer);
return rootView;
}
private void queryBestPost(final int refreshTime) {
if(refreshTime < 0) {
showErrorSnackbar();
return;
}
mProgressBar.setVisibility(View.VISIBLE);
StringRequest bestPostRequest = new StringRequest(Request.Method.GET, RedditUtils.OAUTH_API_BASE_URI + RedditUtils.BEST_POST_SUFFIX, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
if(getActivity() != null) {
ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("response", response);
clipboard.setPrimaryClip(clip);
//new ParseBestPostDataAsyncTask(response, accessToken).execute();
new ParseBestPost(getActivity(), new ParseBestPost.ParseBestPostListener() {
@Override
public void onParseBestPostSuccess(ArrayList<BestPostData> bestPostData, String lastItem) {
mBestPostData = bestPostData;
mLastItem = lastItem;
mAdapter = new BestPostRecyclerViewAdapter(getActivity(), bestPostData, mPaginationSynchronizer, mVoteThingRequestQueue, mAcquireAccessTokenRequestQueue);
mBestPostRecyclerView.setAdapter(mAdapter);
mBestPostRecyclerView.addOnScrollListener(new BestPostPaginationScrollListener(getActivity(), mLinearLayoutManager, mAdapter, lastItem, bestPostData, mPaginationSynchronizer,
mAcquireAccessTokenRequestQueue, mPaginationSynchronizer.isLoading(), mPaginationSynchronizer.isLoadSuccess()));
mProgressBar.setVisibility(View.GONE);
}
@Override
public void onParseBestPostFail() {
Toast.makeText(getActivity(), "Error parsing data", Toast.LENGTH_SHORT).show();
Log.i("Best post fetch error", "Error parsing data");
mProgressBar.setVisibility(View.GONE);
}
}).parseBestPost(response, null);
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
if (error instanceof AuthFailureError) {
// Error indicating that there was an Authentication Failure while performing the request
// Access token expired
new AcquireAccessToken(getActivity()).refreshAccessToken(mAcquireAccessTokenRequestQueue,
new AcquireAccessToken.AcquireAccessTokenListener() {
@Override
public void onAcquireAccessTokenSuccess() {
queryBestPost(refreshTime - 1);
}
@Override
public void onAcquireAccessTokenFail() {}
});
} else {
Log.i("best post fetch error", error.toString());
showErrorSnackbar();
}
}
}) {
@Override
public Map<String, String> getHeaders() {
String accessToken = getActivity().getSharedPreferences(SharedPreferencesUtils.AUTH_CODE_FILE_KEY, Context.MODE_PRIVATE).getString(SharedPreferencesUtils.ACCESS_TOKEN_KEY, "");
return RedditUtils.getOAuthHeader(accessToken);
}
};
bestPostRequest.setTag(BestPostFragment.class);
mRequestQueue.add(bestPostRequest);
}
private void showErrorSnackbar() {
mProgressBar.setVisibility(View.GONE);
Snackbar snackbar = Snackbar.make(mCoordinatorLayout, "Error getting best post", Snackbar.LENGTH_INDEFINITE);
snackbar.setAction(R.string.retry, new View.OnClickListener() {
@Override
public void onClick(View view) {
queryBestPost(1);
}
});
snackbar.show();
}
}

View File

@ -0,0 +1,161 @@
package ml.docilealligator.infinityforreddit;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.widget.Toast;
import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import java.util.ArrayList;
import java.util.Map;
/**
* Created by alex on 3/12/18.
*/
class BestPostPaginationScrollListener extends RecyclerView.OnScrollListener {
private Context mContext;
private LinearLayoutManager mLayoutManager;
private BestPostRecyclerViewAdapter mAdapter;
private ArrayList<BestPostData> mBestPostData;
private PaginationSynchronizer mPaginationSynchronizer;
private PaginationRetryNotifier mPaginationRetryNotifier;
private LastItemSynchronizer mLastItemSynchronizer;
private PaginationRequestQueueSynchronizer mPaginationRequestQueueSynchronizer;
private boolean isLoading;
private boolean loadSuccess;
private String mLastItem;
private RequestQueue mRequestQueue;
private RequestQueue mAcquireAccessTokenRequestQueue;
BestPostPaginationScrollListener(Context context, LinearLayoutManager layoutManager, BestPostRecyclerViewAdapter adapter, String lastItem, ArrayList<BestPostData> bestPostData, PaginationSynchronizer paginationSynchronizer,
RequestQueue acquireAccessTokenRequestQueue, boolean isLoading, boolean loadSuccess) {
if(context != null) {
this.mContext = context;
this.mLayoutManager = layoutManager;
this.mAdapter = adapter;
this.mLastItem = lastItem;
this.mBestPostData = bestPostData;
this.mPaginationSynchronizer = paginationSynchronizer;
this.mAcquireAccessTokenRequestQueue = acquireAccessTokenRequestQueue;
this.isLoading = isLoading;
this.loadSuccess = loadSuccess;
mRequestQueue = Volley.newRequestQueue(mContext);
mAcquireAccessTokenRequestQueue = Volley.newRequestQueue(mContext);
mPaginationRetryNotifier = new PaginationRetryNotifier() {
@Override
public void retry() {
fetchBestPost(1);
}
};
mPaginationSynchronizer.setPaginationRetryNotifier(mPaginationRetryNotifier);
mLastItemSynchronizer = mPaginationSynchronizer.getLastItemSynchronizer();
mPaginationRequestQueueSynchronizer = mPaginationSynchronizer.getPaginationRequestQueueSynchronizer();
mPaginationRequestQueueSynchronizer.passQueue(mRequestQueue);
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if(!isLoading && loadSuccess) {
int visibleItemCount = mLayoutManager.getChildCount();
int totalItemCount = mLayoutManager.getItemCount();
int firstVisibleItemPosition = mLayoutManager.findFirstVisibleItemPosition();
if((visibleItemCount + firstVisibleItemPosition >= totalItemCount) && firstVisibleItemPosition >= 0) {
fetchBestPost(1);
}
}
}
private void fetchBestPost(final int refreshTime) {
if(refreshTime < 0) {
loadFailed();
return;
}
isLoading = true;
loadSuccess = false;
mPaginationSynchronizer.setLoading(true);
StringRequest bestPostRequest = new StringRequest(Request.Method.GET, RedditUtils.OAUTH_API_BASE_URI + RedditUtils.BEST_POST_SUFFIX + "&" + RedditUtils.AFTER_KEY + "=" + mLastItem, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("response", response);
clipboard.setPrimaryClip(clip);
new ParseBestPost(mContext, new ParseBestPost.ParseBestPostListener() {
@Override
public void onParseBestPostSuccess(ArrayList<BestPostData> bestPostData, String lastItem) {
mAdapter.notifyDataSetChanged();
mLastItem = lastItem;
mLastItemSynchronizer.lastItemChanged(mLastItem);
isLoading = false;
loadSuccess = true;
mPaginationSynchronizer.setLoading(false);
mPaginationSynchronizer.setLoadingState(true);
}
@Override
public void onParseBestPostFail() {
Toast.makeText(mContext, "Error parsing data", Toast.LENGTH_SHORT).show();
Log.i("Best post", "Error parsing data");
loadFailed();
}
}).parseBestPost(response, mBestPostData);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
if (error instanceof AuthFailureError) {
//Access token expired
new AcquireAccessToken(mContext).refreshAccessToken(mAcquireAccessTokenRequestQueue,
new AcquireAccessToken.AcquireAccessTokenListener() {
@Override
public void onAcquireAccessTokenSuccess() {
fetchBestPost(refreshTime - 1);
}
@Override
public void onAcquireAccessTokenFail() {
}
});
} else {
Toast.makeText(mContext, "Error getting best post", Toast.LENGTH_SHORT).show();
Log.i("best post", error.toString());
loadFailed();
}
}
}) {
@Override
public Map<String, String> getHeaders() {
String accessToken = mContext.getSharedPreferences(SharedPreferencesUtils.AUTH_CODE_FILE_KEY, Context.MODE_PRIVATE).getString(SharedPreferencesUtils.ACCESS_TOKEN_KEY, "");
return RedditUtils.getOAuthHeader(accessToken);
}
};
bestPostRequest.setTag(BestPostPaginationScrollListener.class);
mRequestQueue.add(bestPostRequest);
}
private void loadFailed() {
isLoading = false;
loadSuccess = false;
mPaginationSynchronizer.setLoading(false);
mPaginationSynchronizer.setLoadingState(false);
}
}

View File

@ -0,0 +1,527 @@
package ml.docilealligator.infinityforreddit;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.customtabs.CustomTabsIntent;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.android.volley.RequestQueue;
import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestManager;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;
import java.util.ArrayList;
import de.hdodenhof.circleimageview.CircleImageView;
/**
* Created by alex on 2/25/18.
*/
class BestPostRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private ArrayList<BestPostData> bestPostData;
private Context mContext;
private PaginationSynchronizer mPaginationSynchronizer;
private RequestQueue mVoteThingRequestQueue;
private RequestQueue mAcquireAccessTokenRequestQueue;
private RequestManager glide;
private boolean isLoadingMorePostSuccess;
private boolean canStartActivity;
private static final int VIEW_TYPE_DATA = 0;
private static final int VIEW_TYPE_LOADING = 1;
BestPostRecyclerViewAdapter(Context context, ArrayList<BestPostData> bestPostData, PaginationSynchronizer paginationSynchronizer,
RequestQueue voteThingRequestQueue, RequestQueue acquireAccessTokenRequestQueue) {
if(context != null) {
mContext = context;
this.bestPostData = bestPostData;
mPaginationSynchronizer = paginationSynchronizer;
mVoteThingRequestQueue = voteThingRequestQueue;
mAcquireAccessTokenRequestQueue = acquireAccessTokenRequestQueue;
isLoadingMorePostSuccess = true;
canStartActivity = true;
glide = Glide.with(mContext);
}
}
void setCanStartActivity(boolean canStartActivity) {
this.canStartActivity = canStartActivity;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if(viewType == VIEW_TYPE_DATA) {
CardView cardView = (CardView) LayoutInflater.from(parent.getContext()).inflate(R.layout.item_best_post, parent, false);
return new DataViewHolder(cardView);
} else {
LinearLayout linearLayout = (LinearLayout) LayoutInflater.from(parent.getContext()).inflate(R.layout.item_footer_progress_bar, parent, false);
return new LoadingViewHolder(linearLayout);
}
}
@Override
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {
if(holder instanceof DataViewHolder) {
if(bestPostData.get(position) == null) {
Log.i("is null", Integer.toString(position));
} else {
final String id = bestPostData.get(position).getFullName();
final String subredditName = bestPostData.get(position).getSubredditName();
final String postTime = bestPostData.get(position).getPostTime();
final String title = bestPostData.get(position).getTitle();
final String permalink = bestPostData.get(position).getPermalink();
int voteType = bestPostData.get(position).getVoteType();
boolean nsfw = bestPostData.get(position).getNSFW();
((DataViewHolder) holder).cardView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(canStartActivity) {
canStartActivity = false;
Intent intent = new Intent(mContext, ViewPostDetailActivity.class);
intent.putExtra(ViewPostDetailActivity.EXTRA_TITLE, title);
intent.putExtra(ViewPostDetailActivity.EXTRA_POST_DATA, bestPostData.get(position));
mContext.startActivity(intent);
}
}
});
((DataViewHolder) holder).subredditNameTextView.setText(subredditName);
((DataViewHolder) holder).postTimeTextView.setText(postTime);
((DataViewHolder) holder).titleTextView.setText(title);
((DataViewHolder) holder).scoreTextView.setText(Integer.toString(bestPostData.get(position).getScore()));
if(nsfw) {
((DataViewHolder) holder).nsfwTextView.setVisibility(View.VISIBLE);
}
switch (voteType) {
case 1:
//Upvote
((DataViewHolder) holder).plusButton.setColorFilter(ContextCompat.getColor(mContext, R.color.colorPrimary), android.graphics.PorterDuff.Mode.SRC_IN);
break;
case -1:
//Downvote
((DataViewHolder) holder).minusButton.setColorFilter(ContextCompat.getColor(mContext, R.color.minusButtonColor), android.graphics.PorterDuff.Mode.SRC_IN);
break;
}
if(bestPostData.get(position).getPostType() != BestPostData.TEXT_TYPE && bestPostData.get(position).getPostType() != BestPostData.NO_PREVIEW_LINK_TYPE) {
((DataViewHolder) holder).relativeLayout.setVisibility(View.VISIBLE);
((DataViewHolder) holder).progressBar.setVisibility(View.VISIBLE);
((DataViewHolder) holder).imageView.setVisibility(View.VISIBLE);
}
switch (bestPostData.get(position).getPostType()) {
case BestPostData.IMAGE_TYPE:
((DataViewHolder) holder).typeTextView.setText("IMAGE");
final String previewImageUrl = bestPostData.get(position).getPreviewUrl();
final String imageUrl = bestPostData.get(position).getUrl();
glide.load(previewImageUrl).listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
((DataViewHolder) holder).progressBar.setVisibility(View.GONE);
return false;
}
}).into(((DataViewHolder) holder).imageView);
((DataViewHolder) holder).imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(mContext, ViewImageActivity.class);
intent.putExtra(ViewImageActivity.IMAGE_URL_KEY, imageUrl);
intent.putExtra(ViewImageActivity.TITLE_KEY, title);
intent.putExtra(ViewImageActivity.SUBREDDIT_KEY, subredditName);
intent.putExtra(ViewImageActivity.ID_KEY, id);
mContext.startActivity(intent);
}
});
break;
case BestPostData.LINK_TYPE:
((DataViewHolder) holder).typeTextView.setText("LINK");
String linkPreviewUrl = bestPostData.get(position).getPreviewUrl();
glide.load(linkPreviewUrl).listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
((DataViewHolder) holder).progressBar.setVisibility(View.GONE);
return false;
}
}).into(((DataViewHolder) holder).imageView);
final String linkUrl = bestPostData.get(position).getUrl();
((DataViewHolder) holder).imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
// add share action to menu list
builder.addDefaultShareMenuItem();
builder.setToolbarColor(mContext.getResources().getColor(R.color.colorPrimary));
CustomTabsIntent customTabsIntent = builder.build();
customTabsIntent.launchUrl(mContext, Uri.parse(linkUrl));
}
});
break;
case BestPostData.GIF_VIDEO_TYPE:
((DataViewHolder) holder).typeTextView.setText("GIF");
String gifVideoPreviewUrl = bestPostData.get(position).getPreviewUrl();
glide.load(gifVideoPreviewUrl).listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
((DataViewHolder) holder).progressBar.setVisibility(View.GONE);
return false;
}
}).into(((DataViewHolder) holder).imageView);
String gifVideoUrl = bestPostData.get(position).getVideoUrl();
final Uri gifVideoUri = Uri.parse(gifVideoUrl);
((DataViewHolder) holder).imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(mContext, ViewVideoActivity.class);
intent.setData(gifVideoUri);
intent.putExtra(ViewVideoActivity.TITLE_KEY, title);
intent.putExtra(ViewVideoActivity.IS_DASH_VIDEO_KEY, bestPostData.get(position).isDashVideo());
intent.putExtra(ViewVideoActivity.IS_DOWNLOADABLE_KEY, bestPostData.get(position).isDownloadableGifOrVideo());
if(bestPostData.get(position).isDownloadableGifOrVideo()) {
intent.putExtra(ViewVideoActivity.DOWNLOAD_URL_KEY, bestPostData.get(position).getGifOrVideoDownloadUrl());
intent.putExtra(ViewVideoActivity.SUBREDDIT_KEY, subredditName);
intent.putExtra(ViewVideoActivity.ID_KEY, id);
}
mContext.startActivity(intent);
}
});
break;
case BestPostData.VIDEO_TYPE:
((DataViewHolder) holder).typeTextView.setText("VIDEO");
String videoPreviewUrl = bestPostData.get(position).getPreviewUrl();
glide.load(videoPreviewUrl).listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
((DataViewHolder) holder).progressBar.setVisibility(View.GONE);
return false;
}
}).into(((DataViewHolder) holder).imageView);
String videoUrl = bestPostData.get(position).getVideoUrl();
final Uri videoUri = Uri.parse(videoUrl);
((DataViewHolder) holder).imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(mContext, ViewVideoActivity.class);
intent.setData(videoUri);
intent.putExtra(ViewVideoActivity.TITLE_KEY, title);
intent.putExtra(ViewVideoActivity.IS_DASH_VIDEO_KEY, bestPostData.get(position).isDashVideo());
intent.putExtra(ViewVideoActivity.IS_DOWNLOADABLE_KEY, bestPostData.get(position).isDownloadableGifOrVideo());
if(bestPostData.get(position).isDownloadableGifOrVideo()) {
intent.putExtra(ViewVideoActivity.DOWNLOAD_URL_KEY, bestPostData.get(position).getGifOrVideoDownloadUrl());
intent.putExtra(ViewVideoActivity.SUBREDDIT_KEY, subredditName);
intent.putExtra(ViewVideoActivity.ID_KEY, id);
}
mContext.startActivity(intent);
}
});
break;
case BestPostData.NO_PREVIEW_LINK_TYPE:
((DataViewHolder) holder).typeTextView.setText("LINK");
final String noPreviewLinkUrl = bestPostData.get(position).getUrl();
((DataViewHolder) holder).noPreviewLinkImageView.setVisibility(View.VISIBLE);
((DataViewHolder) holder).noPreviewLinkImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
// add share action to menu list
builder.addDefaultShareMenuItem();
builder.setToolbarColor(mContext.getResources().getColor(R.color.colorPrimary));
CustomTabsIntent customTabsIntent = builder.build();
customTabsIntent.launchUrl(mContext, Uri.parse(noPreviewLinkUrl));
}
});
break;
default:
((DataViewHolder) holder).typeTextView.setText("TEXT");
}
((DataViewHolder) holder).plusButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
final boolean isDownvotedBefore = ((DataViewHolder) holder).minusButton.getColorFilter() != null;
((DataViewHolder) holder).minusButton.clearColorFilter();
if (((DataViewHolder) holder).plusButton.getColorFilter() == null) {
((DataViewHolder) holder).plusButton.setColorFilter(ContextCompat.getColor(mContext, R.color.colorPrimary), android.graphics.PorterDuff.Mode.SRC_IN);
if(isDownvotedBefore) {
((DataViewHolder) holder).scoreTextView.setText(Integer.toString(bestPostData.get(position).getScore() + 2));
} else {
((DataViewHolder) holder).scoreTextView.setText(Integer.toString(bestPostData.get(position).getScore() + 1));
}
new VoteThing(mContext, mVoteThingRequestQueue, mAcquireAccessTokenRequestQueue).votePost(new VoteThing.VoteThingListener() {
@Override
public void onVoteThingSuccess(int position) {
bestPostData.get(position).setVoteType(1);
if(isDownvotedBefore) {
bestPostData.get(position).setScore(bestPostData.get(position).getScore() + 2);
} else {
bestPostData.get(position).setScore(bestPostData.get(position).getScore() + 1);
}
}
@Override
public void onVoteThingFail(int position) {
Toast.makeText(mContext, "Cannot upvote this post", Toast.LENGTH_SHORT).show();
((DataViewHolder) holder).plusButton.clearColorFilter();
((DataViewHolder) holder).scoreTextView.setText(Integer.toString(bestPostData.get(position).getScore()));
((DataViewHolder) holder).minusButton.setColorFilter(ContextCompat.getColor(mContext, R.color.minusButtonColor), android.graphics.PorterDuff.Mode.SRC_IN);
}
}, id, RedditUtils.DIR_UPVOTE, ((DataViewHolder) holder).getAdapterPosition(), 1);
} else {
//Upvoted before
((DataViewHolder) holder).plusButton.clearColorFilter();
((DataViewHolder) holder).scoreTextView.setText(Integer.toString(bestPostData.get(position).getScore() - 1));
new VoteThing(mContext, mVoteThingRequestQueue, mAcquireAccessTokenRequestQueue).votePost(new VoteThing.VoteThingListener() {
@Override
public void onVoteThingSuccess(int position) {
bestPostData.get(position).setVoteType(0);
bestPostData.get(position).setScore(bestPostData.get(position).getScore() - 1);
}
@Override
public void onVoteThingFail(int position) {
Toast.makeText(mContext, "Cannot unvote this post", Toast.LENGTH_SHORT).show();
((DataViewHolder) holder).scoreTextView.setText(Integer.toString(bestPostData.get(position).getScore() + 1));
((DataViewHolder) holder).plusButton.setColorFilter(ContextCompat.getColor(mContext, R.color.colorPrimary), android.graphics.PorterDuff.Mode.SRC_IN);
bestPostData.get(position).setScore(bestPostData.get(position).getScore() + 1);
}
}, id, RedditUtils.DIR_UNVOTE, ((DataViewHolder) holder).getAdapterPosition(), 1);
}
}
});
((DataViewHolder) holder).minusButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
final boolean isUpvotedBefore = ((DataViewHolder) holder).plusButton.getColorFilter() != null;
((DataViewHolder) holder).plusButton.clearColorFilter();
if (((DataViewHolder) holder).minusButton.getColorFilter() == null) {
((DataViewHolder) holder).minusButton.setColorFilter(ContextCompat.getColor(mContext, R.color.minusButtonColor), android.graphics.PorterDuff.Mode.SRC_IN);
if (isUpvotedBefore) {
((DataViewHolder) holder).scoreTextView.setText(Integer.toString(bestPostData.get(position).getScore() - 2));
} else {
((DataViewHolder) holder).scoreTextView.setText(Integer.toString(bestPostData.get(position).getScore() - 1));
}
new VoteThing(mContext, mVoteThingRequestQueue, mAcquireAccessTokenRequestQueue).votePost(new VoteThing.VoteThingListener() {
@Override
public void onVoteThingSuccess(int position) {
bestPostData.get(position).setVoteType(-1);
if(isUpvotedBefore) {
bestPostData.get(position).setScore(bestPostData.get(position).getScore() - 2);
} else {
bestPostData.get(position).setScore(bestPostData.get(position).getScore() - 1);
}
}
@Override
public void onVoteThingFail(int position) {
Toast.makeText(mContext, "Cannot downvote this post", Toast.LENGTH_SHORT).show();
((DataViewHolder) holder).minusButton.clearColorFilter();
((DataViewHolder) holder).scoreTextView.setText(Integer.toString(bestPostData.get(position).getScore()));
((DataViewHolder) holder).plusButton.setColorFilter(ContextCompat.getColor(mContext, R.color.colorPrimary), android.graphics.PorterDuff.Mode.SRC_IN);
}
}, id, RedditUtils.DIR_DOWNVOTE, holder.getAdapterPosition(), 1);
} else {
//Down voted before
((DataViewHolder) holder).minusButton.clearColorFilter();
((DataViewHolder) holder).scoreTextView.setText(Integer.toString(bestPostData.get(position).getScore() + 1));
new VoteThing(mContext, mVoteThingRequestQueue, mAcquireAccessTokenRequestQueue).votePost(new VoteThing.VoteThingListener() {
@Override
public void onVoteThingSuccess(int position) {
bestPostData.get(position).setVoteType(0);
bestPostData.get(position).setScore(bestPostData.get(position).getScore());
}
@Override
public void onVoteThingFail(int position) {
Toast.makeText(mContext, "Cannot unvote this post", Toast.LENGTH_SHORT).show();
((DataViewHolder) holder).minusButton.setColorFilter(ContextCompat.getColor(mContext, R.color.minusButtonColor), android.graphics.PorterDuff.Mode.SRC_IN);
((DataViewHolder) holder).scoreTextView.setText(Integer.toString(bestPostData.get(position).getScore()));
bestPostData.get(position).setScore(bestPostData.get(position).getScore());
}
}, id, RedditUtils.DIR_UNVOTE, holder.getAdapterPosition(), 1);
}
}
});
((DataViewHolder) holder).shareButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
String extraText = title + "\n" + permalink;
intent.putExtra(Intent.EXTRA_TEXT, extraText);
mContext.startActivity(Intent.createChooser(intent, "Share"));
}
});
}
} else if(holder instanceof LoadingViewHolder) {
((LoadingViewHolder) holder).retryButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mPaginationSynchronizer.getPaginationRetryNotifier().retry();
((LoadingViewHolder) holder).progressBar.setVisibility(View.VISIBLE);
((LoadingViewHolder) holder).relativeLayout.setVisibility(View.GONE);
}
});
PaginationNotifier mPaginationNotifier = new PaginationNotifier() {
@Override
public void LoadMorePostSuccess() {
isLoadingMorePostSuccess = true;
}
@Override
public void LoadMorePostFail() {
((LoadingViewHolder) holder).progressBar.setVisibility(View.GONE);
((LoadingViewHolder) holder).relativeLayout.setVisibility(View.VISIBLE);
isLoadingMorePostSuccess = false;
}
};
mPaginationSynchronizer.setPaginationNotifier(mPaginationNotifier);
if(!mPaginationSynchronizer.isLoadSuccess()) {
((LoadingViewHolder) holder).progressBar.setVisibility(View.GONE);
((LoadingViewHolder) holder).relativeLayout.setVisibility(View.VISIBLE);
}
}
}
@Override
public int getItemCount() {
return bestPostData.size() + 1;
}
@Override
public int getItemViewType(int position) {
return (position >= bestPostData.size() ? VIEW_TYPE_LOADING : VIEW_TYPE_DATA);
}
class DataViewHolder extends RecyclerView.ViewHolder {
private CardView cardView;
private CircleImageView subredditImageView;
private TextView subredditNameTextView;
private TextView postTimeTextView;
private TextView titleTextView;
private TextView typeTextView;
private TextView nsfwTextView;
private RelativeLayout relativeLayout;
private ProgressBar progressBar;
private ImageView imageView;
private ImageView noPreviewLinkImageView;
private ImageView plusButton;
private TextView scoreTextView;
private ImageView minusButton;
private ImageView shareButton;
DataViewHolder(CardView itemView) {
super(itemView);
cardView = itemView.findViewById(R.id.card_view_view_post_detail);
subredditImageView = itemView.findViewById(R.id.subreddit_icon_circle_image_view_best_post_item);
subredditNameTextView = itemView.findViewById(R.id.subreddit_text_view_best_post_item);
postTimeTextView = itemView.findViewById(R.id.post_time_text_view_best_post_item);
titleTextView = itemView.findViewById(R.id.title_text_view_best_post_item);
typeTextView = itemView.findViewById(R.id.type_text_view_item_best_post);
nsfwTextView = itemView.findViewById(R.id.nsfw_text_view_item_best_post);
relativeLayout = itemView.findViewById(R.id.image_view_wrapper_item_best_post);
progressBar = itemView.findViewById(R.id.progress_bar_best_post_item);
imageView = itemView.findViewById(R.id.image_view_best_post_item);
noPreviewLinkImageView = itemView.findViewById(R.id.image_view_no_preview_link_best_post_item);
plusButton = itemView.findViewById(R.id.plus_button_item_best_post);
scoreTextView = itemView.findViewById(R.id.score_text_view_item_best_post);
minusButton = itemView.findViewById(R.id.minus_button_item_best_post);
shareButton = itemView.findViewById(R.id.share_button_item_best_post);
}
}
class LoadingViewHolder extends RecyclerView.ViewHolder {
private ProgressBar progressBar;
private RelativeLayout relativeLayout;
private Button retryButton;
LoadingViewHolder(LinearLayout itemView) {
super(itemView);
progressBar = itemView.findViewById(R.id.progress_bar_footer_progress_bar_item);
relativeLayout = itemView.findViewById(R.id.relative_layout_footer_progress_bar_item);
retryButton = itemView.findViewById(R.id.retry_button_footer_progress_bar_item);
}
}
@Override
public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) {
if(holder instanceof DataViewHolder) {
glide.clear(((DataViewHolder) holder).imageView);
((DataViewHolder) holder).relativeLayout.setVisibility(View.GONE);
((DataViewHolder) holder).nsfwTextView.setVisibility(View.GONE);
((DataViewHolder) holder).progressBar.setVisibility(View.GONE);
((DataViewHolder) holder).imageView.setVisibility(View.GONE);
((DataViewHolder) holder).noPreviewLinkImageView.setVisibility(View.GONE);
((DataViewHolder) holder).plusButton.clearColorFilter();
((DataViewHolder) holder).minusButton.clearColorFilter();
} else if(holder instanceof LoadingViewHolder) {
if(isLoadingMorePostSuccess) {
((LoadingViewHolder) holder).relativeLayout.setVisibility(View.GONE);
((LoadingViewHolder) holder).progressBar.setVisibility(View.VISIBLE);
} else {
((LoadingViewHolder) holder).relativeLayout.setVisibility(View.VISIBLE);
((LoadingViewHolder) holder).progressBar.setVisibility(View.GONE);
}
}
}
}

View File

@ -0,0 +1,84 @@
package ml.docilealligator.infinityforreddit;
class CommentData {
private String id;
private String author;
private String commentTime;
private String commentContent;
private int score;
private boolean isSubmitter;
private String permalink;
private int depth;
private boolean collapsed;
private boolean hasReply;
private boolean scoreHidden;
CommentData(String id, String author, String commentTime, String commentContent, int score,
boolean isSubmitter, String permalink, int depth, boolean collapsed, boolean hasReply,
boolean scoreHidden) {
this.id = id;
this.author = author;
this.commentTime = commentTime;
this.commentContent = commentContent;
this.score = score;
this.isSubmitter = isSubmitter;
this.permalink = RedditUtils.API_BASE_URI + permalink;
this.depth = depth;
this.collapsed = collapsed;
this.hasReply = hasReply;
this.scoreHidden = scoreHidden;
}
public String getId() {
return id;
}
public String getAuthor() {
return author;
}
public String getCommentTime() {
return commentTime;
}
public String getCommentContent() {
return commentContent;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
public boolean isSubmitter() {
return isSubmitter;
}
public String getPermalink() {
return permalink;
}
public int getDepth() {
return depth;
}
public boolean isCollapsed() {
return collapsed;
}
public boolean isHasReply() {
return hasReply;
}
public void setHasReply(boolean hasReply) {
this.hasReply = hasReply;
}
public boolean isScoreHidden() {
return scoreHidden;
}
}

View File

@ -0,0 +1,79 @@
package ml.docilealligator.infinityforreddit;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.volley.RequestQueue;
import java.util.ArrayList;
class CommentRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context mContext;
private ArrayList<CommentData> mCommentData;
private RequestQueue mVoteThingRequestQueue;
CommentRecyclerViewAdapter(Context context, ArrayList<CommentData> commentData,
RequestQueue voteThingRequestQueue) {
mContext = context;
mCommentData = commentData;
mVoteThingRequestQueue = voteThingRequestQueue;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new CommentViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_post_comment, parent, false));
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
((CommentViewHolder) holder).authorTextView.setText(mCommentData.get(position).getAuthor());
((CommentViewHolder) holder).commentTimeTextView.setText(mCommentData.get(position).getCommentTime());
((CommentViewHolder) holder).commentTextView.setText(mCommentData.get(position).getCommentContent());
((CommentViewHolder) holder).scoreTextView.setText(Integer.toString(mCommentData.get(position).getScore()));
((CommentViewHolder) holder).upvoteButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
}
});
((CommentViewHolder) holder).downvoteButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
}
});
}
@Override
public int getItemCount() {
return mCommentData.size();
}
private class CommentViewHolder extends RecyclerView.ViewHolder {
private TextView authorTextView;
private TextView commentTimeTextView;
private TextView commentTextView;
private ImageView upvoteButton;
private ImageView downvoteButton;
private TextView scoreTextView;
private ImageView replyButton;
public CommentViewHolder(View itemView) {
super(itemView);
authorTextView = itemView.findViewById(R.id.author_text_view_item_post_comment);
commentTimeTextView = itemView.findViewById(R.id.comment_time_text_view_item_post_comment);
commentTextView = itemView.findViewById(R.id.comment_text_view_item_post_comment);
upvoteButton = itemView.findViewById(R.id.plus_button_item_post_comment);
downvoteButton = itemView.findViewById(R.id.minus_button_item_post_comment);
scoreTextView = itemView.findViewById(R.id.score_text_view_item_post_comment);
replyButton = itemView.findViewById(R.id.reply_button_item_post_comment);
}
}
}

View File

@ -0,0 +1,42 @@
package ml.docilealligator.infinityforreddit;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
class FetchComment {
interface FetchCommentListener {
void onFetchCommentSuccess(String response);
void onFetchCommentFail();
}
private RequestQueue requestQueue;
private String subredditName;
private String article;
private FetchCommentListener mFetchCommentListener;
FetchComment(RequestQueue requestQueue, String subredditName, String article) {
this.requestQueue = requestQueue;
this.subredditName = subredditName;
this.article = article;
}
void queryComment(FetchCommentListener fetchCommentListener) {
mFetchCommentListener = fetchCommentListener;
StringRequest commentRequest = new StringRequest(Request.Method.GET, RedditUtils.getQueryCommentURI(subredditName, article), new Response.Listener<String>() {
@Override
public void onResponse(String response) {
mFetchCommentListener.onFetchCommentSuccess(response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
mFetchCommentListener.onFetchCommentFail();
}
}) {};
commentRequest.setTag(FetchComment.class);
requestQueue.add(commentRequest);
}
}

View File

@ -0,0 +1,5 @@
package ml.docilealligator.infinityforreddit;
class FetchSubscribedSubreddits {
}

View File

@ -0,0 +1,69 @@
package ml.docilealligator.infinityforreddit;
import android.content.Context;
import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import java.util.Map;
class FetchUserInfo {
interface FetchUserInfoListener {
void onFetchUserInfoSuccess(String response);
void onFetchUserInfoFail();
}
private Context context;
private RequestQueue requestQueue;
private FetchUserInfoListener mFetchUserInfoListener;
FetchUserInfo(Context context, RequestQueue requestQueue) {
this.context = context;
this.requestQueue = requestQueue;
}
void queryUserInfo(FetchUserInfoListener fetchUserInfoListener, final int refreshTime) {
if(refreshTime < 0) {
mFetchUserInfoListener.onFetchUserInfoFail();
return;
}
mFetchUserInfoListener = fetchUserInfoListener;
StringRequest commentRequest = new StringRequest(Request.Method.GET, RedditUtils.OAUTH_API_BASE_URI + RedditUtils.USER_INFO_SUFFIX, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
mFetchUserInfoListener.onFetchUserInfoSuccess(response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
if(error instanceof AuthFailureError) {
new AcquireAccessToken(context).refreshAccessToken(requestQueue, new AcquireAccessToken.AcquireAccessTokenListener() {
@Override
public void onAcquireAccessTokenSuccess() {
queryUserInfo(mFetchUserInfoListener, refreshTime - 1);
}
@Override
public void onAcquireAccessTokenFail() {}
});
} else {
mFetchUserInfoListener.onFetchUserInfoFail();
}
}
}) {
@Override
public Map<String, String> getHeaders() {
String accessToken = context.getSharedPreferences(SharedPreferencesUtils.AUTH_CODE_FILE_KEY, Context.MODE_PRIVATE).getString(SharedPreferencesUtils.ACCESS_TOKEN_KEY, "");
return RedditUtils.getOAuthHeader(accessToken);
}
};
commentRequest.setTag(FetchComment.class);
requestQueue.add(commentRequest);
}
}

View File

@ -0,0 +1,59 @@
package ml.docilealligator.infinityforreddit;
/**
* Created by alex on 2/25/18.
*/
class JSONUtils {
static final String DATA_KEY = "data";
static final String AFTER_KEY = "after";
static final String MODHASH_KEY = "modhash";
static final String CHILDREN_KEY = "children";
static final String COUNT_KEY = "count";
static final String TITLE_KEY = "title";
static final String NAME_KEY = "name";
static final String SUBREDDIT_NAME_PREFIX_KEY = "subreddit_name_prefixed";
static final String SELF_TEXT_KEY = "selftext";
static final String AUTHOR_KEY = "author";
static final String DOMAIN_KEY = "domain";
static final String LINK_FLAIR_TEXT_KEY = "link_flair_text";
static final String NUM_CROSSPOST_KEY = "num_crossposts";
static final String CAN_MOD_POST_KEY = "can_mod_post";
static final String SCORE_KEY = "score";
static final String LIKES_KEY = "likes";
static final String NSFW_KEY = "over_18";
static final String GILDED_KEY = "gilded";
static final String POST_HINT_KEY = "post_hint";
static final String PERMALINK_KEY = "permalink";
static final String CREATED_UTC_KEY = "created_utc";
static final String PREVIEW_KEY = "preview";
static final String IMAGES_KEY = "images";
static final String WIDTH_KEY = "width";
static final String HEIGHT_KEY = "height";
static final String VARIANTS_KEY = "variants";
static final String GIF_KEY = "gif";
static final String MP4_KEY = "mp4";
static final String SOURCE_KEY = "source";
static final String URL_KEY = "url";
static final String MEDIA_KEY = "media";
static final String REDDIT_VIDEO_KEY = "reddit_video";
static final String FALLBACK_URL_KEY = "fallback_url";
static final String DASH_URL_KEY = "dash_url";
static final String IS_VIDEO_KEY = "is_video";
static final String CROSSPOST_PARENT_LIST = "crosspost_parent_list";
static final String REDDIT_VIDEO_PREVIEW_KEY = "reddit_video_preview";
static final String IS_REDDIT_MEDIA_DOMAIN = "is_reddit_media_domain";
static final String STICKIED_KEY = "stickied";
static final String BODY_KEY = "body";
static final String COLLAPSED_KEY = "collapsed";
static final String IS_SUBMITTER_KEY = "is_submitter";
static final String REPLIES_KEY = "replies";
static final String DEPTH_KEY = "depth";
static final String ID_KEY = "id";
static final String SCORE_HIDDEN_KEY = "score_hidden";
static final String SUBREDDIT_KEY = "subreddit";
static final String BANNER_IMG_KEY = "banner_img";
static final String ICON_IMG_KEY = "icon_img";
static final String LINK_KARMA_KEY = "link_karma";
static final String COMMENT_KARMA_KEY = "comment_karma";
}

View File

@ -0,0 +1,5 @@
package ml.docilealligator.infinityforreddit;
interface LastItemSynchronizer {
void lastItemChanged(String lastItem);
}

View File

@ -0,0 +1,147 @@
package ml.docilealligator.infinityforreddit;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
public class LoginActivity extends AppCompatActivity {
private String authCode;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
WebView webView = findViewById(R.id.webview_login_activity);
webView.getSettings().setJavaScriptEnabled(true);
Uri baseUri = Uri.parse(RedditUtils.OAUTH_URL);
Uri.Builder uriBuilder = baseUri.buildUpon();
uriBuilder.appendQueryParameter(RedditUtils.CLIENT_ID_KEY, RedditUtils.CLIENT_ID);
uriBuilder.appendQueryParameter(RedditUtils.RESPONSE_TYPE_KEY, RedditUtils.RESPONSE_TYPE);
uriBuilder.appendQueryParameter(RedditUtils.STATE_KEY, RedditUtils.STATE);
uriBuilder.appendQueryParameter(RedditUtils.REDIRECT_URI_KEY, RedditUtils.REDIRECT_URI);
uriBuilder.appendQueryParameter(RedditUtils.DURATION_KEY, RedditUtils.DURATION);
uriBuilder.appendQueryParameter(RedditUtils.SCOPE_KEY, RedditUtils.SCOPE);
String url = uriBuilder.toString();
webView.loadUrl(url);
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if(url.contains("&code=") || url.contains("?code=")) {
Uri uri = Uri.parse(url);
String state = uri.getQueryParameter("state");
if(state.equals(RedditUtils.STATE)) {
authCode = uri.getQueryParameter("code");
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
intent.putExtra("authCode", authCode);
final SharedPreferences.Editor editor = getSharedPreferences(SharedPreferencesUtils.AUTH_CODE_FILE_KEY, Context.MODE_PRIVATE).edit();
editor.putString(SharedPreferencesUtils.AUTH_CODE_KEY, authCode);
editor.apply();
RequestQueue queue = Volley.newRequestQueue(LoginActivity.this);
String tokenRetrievalUrl = "https://www.reddit.com/api/v1/access_token";
StringRequest requestToken = new StringRequest(Request.Method.POST, tokenRetrievalUrl, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
try {
JSONObject responseJSON = new JSONObject(response);
String accessToken = responseJSON.getString(RedditUtils.ACCESS_TOKEN_KEY);
String refreshToken = responseJSON.getString(RedditUtils.REFRESH_TOKEN_KEY);
editor.putString(SharedPreferencesUtils.ACCESS_TOKEN_KEY, accessToken);
editor.putString(SharedPreferencesUtils.REFRESH_TOKEN_KEY, refreshToken);
editor.apply();
finish();
} catch (JSONException e) {
e.printStackTrace();
Toast.makeText(LoginActivity.this, "Error occurred when parsing the JSON response", Toast.LENGTH_SHORT).show();
finish();
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(LoginActivity.this, "Error Retrieving the token", Toast.LENGTH_SHORT).show();
finish();
}
}){
@Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> params = new HashMap<>();
params.put(RedditUtils.GRANT_TYPE_KEY, "authorization_code");
params.put("code", authCode);
params.put("redirect_uri", RedditUtils.REDIRECT_URI);
return params;
}
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
return RedditUtils.getHttpBasicAuthHeader();
}
};
queue.add(requestToken);
} else {
Toast.makeText(LoginActivity.this, "Something went wrong. Try again later.", Toast.LENGTH_SHORT).show();
finish();
}
} else if (url.contains("error=access_denied")) {
Toast.makeText(LoginActivity.this, "Access denied", Toast.LENGTH_SHORT).show();
finish();
}
}
});
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
}
return false;
}
}

View File

@ -0,0 +1,232 @@
package ml.docilealligator.infinityforreddit;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.design.widget.NavigationView;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.volley.toolbox.Volley;
import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestManager;
import de.hdodenhof.circleimageview.CircleImageView;
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
private String nameState = "NS";
private String profileImageUrlState = "PIUS";
private String bannerImageUrlState = "BIUS";
private String karmaState = "KS";
private TextView mNameTextView;
private TextView mKarmaTextView;
private CircleImageView mProfileImageView;
private ImageView mBannerImageView;
private Fragment mFragment;
private RequestManager glide;
private String mName;
private String mProfileImageUrl;
private String mBannerImageUrl;
private String mKarma;
private boolean mFetchUserInfoSuccess;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
DrawerLayout drawer = findViewById(R.id.drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();
NavigationView navigationView = findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
View header = navigationView.getHeaderView(0);
mNameTextView = header.findViewById(R.id.name_text_view_nav_header_main);
mKarmaTextView = header.findViewById(R.id.karma_text_view_nav_header_main);
mProfileImageView = header.findViewById(R.id.profile_image_view_nav_header_main);
mBannerImageView = header.findViewById(R.id.banner_image_view_nav_header_main);
mName = getSharedPreferences(SharedPreferencesUtils.USER_INFO_FILE_KEY, Context.MODE_PRIVATE).getString(SharedPreferencesUtils.USER_KEY, "");
mProfileImageUrl = getSharedPreferences(SharedPreferencesUtils.USER_INFO_FILE_KEY, Context.MODE_PRIVATE).getString(SharedPreferencesUtils.PROFILE_IMAGE_URL_KEY, "");
mBannerImageUrl = getSharedPreferences(SharedPreferencesUtils.USER_INFO_FILE_KEY, Context.MODE_PRIVATE).getString(SharedPreferencesUtils.BANNER_IMAGE_URL_KEY, "");
mKarma = getSharedPreferences(SharedPreferencesUtils.USER_INFO_FILE_KEY, Context.MODE_PRIVATE).getString(SharedPreferencesUtils.KARMA_KEY, "");
mNameTextView.setText(mName);
mKarmaTextView.setText(mKarma);
glide = Glide.with(this);
if(!mProfileImageUrl.equals("")) {
glide.load(mProfileImageUrl).into(mProfileImageView);
}
if(!mBannerImageUrl.equals("")) {
glide.load(mBannerImageUrl).into(mBannerImageView);
}
String accessToken = getSharedPreferences(SharedPreferencesUtils.AUTH_CODE_FILE_KEY, Context.MODE_PRIVATE).getString(SharedPreferencesUtils.ACCESS_TOKEN_KEY, "");
if(accessToken.equals("")) {
Intent loginIntent = new Intent(this, LoginActivity.class);
startActivity(loginIntent);
} else {
if(savedInstanceState == null) {
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
mFragment = new BestPostFragment();
fragmentTransaction.replace(R.id.frame_layout_content_main, mFragment).commit();
} else {
mFragment = getFragmentManager().getFragment(savedInstanceState, "outStateFragment");
getFragmentManager().beginTransaction().replace(R.id.frame_layout_content_main, mFragment).commit();
}
}
if(savedInstanceState == null && !mFetchUserInfoSuccess) {
new FetchUserInfo(this, Volley.newRequestQueue(this)).queryUserInfo(new FetchUserInfo.FetchUserInfoListener() {
@Override
public void onFetchUserInfoSuccess(String response) {
new ParseUserInfo().parseUserInfo(MainActivity.this, response, new ParseUserInfo.ParseUserInfoListener() {
@Override
public void onParseUserInfoSuccess(String name, String profileImageUrl, String bannerImageUrl, int karma) {
mNameTextView.setText(name);
if(!mProfileImageUrl.equals("")) {
glide.load(profileImageUrl).into(mProfileImageView);
}
if(!mBannerImageUrl.equals("")) {
glide.load(bannerImageUrl).into(mBannerImageView);
}
mName = name;
mProfileImageUrl = profileImageUrl;
mBannerImageUrl = bannerImageUrl;
mKarma = getString(R.string.karma_info, karma);
mKarmaTextView.setText(mKarma);
SharedPreferences.Editor editor = getSharedPreferences(SharedPreferencesUtils.USER_INFO_FILE_KEY, Context.MODE_PRIVATE).edit();
editor.putString(SharedPreferencesUtils.USER_KEY, name);
editor.putString(SharedPreferencesUtils.PROFILE_IMAGE_URL_KEY, profileImageUrl);
editor.putString(SharedPreferencesUtils.BANNER_IMAGE_URL_KEY, bannerImageUrl);
editor.putString(SharedPreferencesUtils.KARMA_KEY, mKarma);
editor.apply();
mFetchUserInfoSuccess = true;
}
@Override
public void onParseUserInfoFail() {
mFetchUserInfoSuccess = false;
}
});
}
@Override
public void onFetchUserInfoFail() {
}
}, 1);
}
}
@Override
public void onBackPressed() {
DrawerLayout drawer = findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@SuppressWarnings("StatementWithEmptyBody")
@Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
if (id == R.id.nav_camera) {
// Handle the camera action
} else if (id == R.id.nav_gallery) {
} else if (id == R.id.nav_slideshow) {
} else if (id == R.id.nav_manage) {
} else if (id == R.id.nav_share) {
} else if (id == R.id.nav_send) {
}
DrawerLayout drawer = findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if(mFragment != null) {
getFragmentManager().putFragment(outState, "outStateFragment", mFragment);
}
outState.putString(nameState, mName);
outState.putString(profileImageUrlState, mProfileImageUrl);
outState.putString(bannerImageUrlState, mBannerImageUrl);
outState.putString(karmaState, mKarma);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mName = savedInstanceState.getString(nameState);
mProfileImageUrl = savedInstanceState.getString(profileImageUrlState);
mBannerImageUrl = savedInstanceState.getString(bannerImageUrlState);
mKarma = savedInstanceState.getString(karmaState);
mNameTextView.setText(mName);
mKarmaTextView.setText(mKarma);
if(!mProfileImageUrl.equals("")) {
glide.load(mProfileImageUrl).into(mProfileImageView);
}
if(!mBannerImageUrl.equals("")) {
glide.load(mBannerImageUrl).into(mBannerImageView);
}
}
}

View File

@ -0,0 +1,6 @@
package ml.docilealligator.infinityforreddit;
interface PaginationNotifier {
void LoadMorePostSuccess();
void LoadMorePostFail();
}

View File

@ -0,0 +1,7 @@
package ml.docilealligator.infinityforreddit;
import com.android.volley.RequestQueue;
interface PaginationRequestQueueSynchronizer {
void passQueue(RequestQueue q);
}

View File

@ -0,0 +1,5 @@
package ml.docilealligator.infinityforreddit;
interface PaginationRetryNotifier {
void retry();
}

View File

@ -0,0 +1,96 @@
package ml.docilealligator.infinityforreddit;
import android.os.Parcel;
import android.os.Parcelable;
class PaginationSynchronizer implements Parcelable {
private boolean loadingState;
private boolean loadSuccess;
private PaginationNotifier paginationNotifier;
private PaginationRetryNotifier paginationRetryNotifier;
private LastItemSynchronizer lastItemSynchronizer;
private PaginationRequestQueueSynchronizer paginationRequestQueueSynchronizer;
PaginationSynchronizer() {
loadingState = false;
loadSuccess = true;
}
protected PaginationSynchronizer(Parcel in) {
loadingState = in.readByte() != 0;
loadSuccess = in.readByte() != 0;
}
public static final Creator<PaginationSynchronizer> CREATOR = new Creator<PaginationSynchronizer>() {
@Override
public PaginationSynchronizer createFromParcel(Parcel in) {
return new PaginationSynchronizer(in);
}
@Override
public PaginationSynchronizer[] newArray(int size) {
return new PaginationSynchronizer[size];
}
};
public void setLoading(boolean isLoading) {
this.loadingState = isLoading;
}
public boolean isLoading() {
return loadingState;
}
public void setLoadingState(boolean state) {
loadSuccess = state;
if(loadSuccess) {
paginationNotifier.LoadMorePostSuccess();
} else {
paginationNotifier.LoadMorePostFail();
}
}
public boolean isLoadSuccess() {
return loadSuccess;
}
public void setPaginationNotifier(PaginationNotifier paginationNotifier) {
this.paginationNotifier = paginationNotifier;
}
public void setPaginationRetryNotifier(PaginationRetryNotifier paginationRetryNotifier) {
this.paginationRetryNotifier = paginationRetryNotifier;
}
public PaginationRetryNotifier getPaginationRetryNotifier() {
return paginationRetryNotifier;
}
public void setLastItemSynchronizer(LastItemSynchronizer lastItemSynchronizer) {
this.lastItemSynchronizer = lastItemSynchronizer;
}
public LastItemSynchronizer getLastItemSynchronizer() {
return lastItemSynchronizer;
}
public void setPaginationRequestQueueSynchronizer(PaginationRequestQueueSynchronizer paginationRequestQueueSynchronizer) {
this.paginationRequestQueueSynchronizer = paginationRequestQueueSynchronizer;
}
public PaginationRequestQueueSynchronizer getPaginationRequestQueueSynchronizer() {
return paginationRequestQueueSynchronizer;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeByte((byte) (loadingState ? 1 : 0));
parcel.writeByte((byte) (loadSuccess ? 1 : 0));
}
}

View File

@ -0,0 +1,304 @@
package ml.docilealligator.infinityforreddit;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
/**
* Created by alex on 3/21/18.
*/
class ParseBestPost {
interface ParseBestPostListener {
void onParseBestPostSuccess(ArrayList<BestPostData> bestPostData, String lastItem);
void onParseBestPostFail();
}
private Context mContext;
private ParseBestPostListener mParseBetPostListener;
ParseBestPost(Context context, ParseBestPostListener parseBestPostListener) {
mContext = context;
mParseBetPostListener = parseBestPostListener;
}
void parseBestPost(String response, ArrayList<BestPostData> bestPostData) {
new ParseBestPostDataAsyncTask(response, bestPostData).execute();
}
private class ParseBestPostDataAsyncTask extends AsyncTask<Void, Void, Void> {
private JSONObject jsonResponse;
private ArrayList<BestPostData> bestPostData;
private ArrayList<BestPostData> newBestPostData;
private String lastItem;
private boolean parseFailed;
ParseBestPostDataAsyncTask(String response, ArrayList<BestPostData> bestPostData) {
try {
jsonResponse = new JSONObject(response);
this.bestPostData = bestPostData;
newBestPostData = new ArrayList<>();
parseFailed = false;
} catch (JSONException e) {
e.printStackTrace();
Toast.makeText(mContext, "Error converting response to JSON", Toast.LENGTH_SHORT).show();
}
}
@Override
protected Void doInBackground(Void... voids) {
try {
JSONArray allData = jsonResponse.getJSONObject(JSONUtils.DATA_KEY).getJSONArray(JSONUtils.CHILDREN_KEY);
if(bestPostData == null) {
bestPostData = new ArrayList<>();
}
lastItem = jsonResponse.getJSONObject(JSONUtils.DATA_KEY).getString(JSONUtils.AFTER_KEY);
for(int i = 0; i < allData.length(); i++) {
JSONObject data = allData.getJSONObject(i).getJSONObject(JSONUtils.DATA_KEY);
String id = data.getString(JSONUtils.ID_KEY);
String fullName = data.getString(JSONUtils.NAME_KEY);
String subredditName = data.getString(JSONUtils.SUBREDDIT_NAME_PREFIX_KEY);
long postTime = data.getLong(JSONUtils.CREATED_UTC_KEY) * 1000;
String title = data.getString(JSONUtils.TITLE_KEY);
int score = data.getInt(JSONUtils.SCORE_KEY);
int voteType;
boolean nsfw = data.getBoolean(JSONUtils.NSFW_KEY);
if(data.isNull(JSONUtils.LIKES_KEY)) {
voteType = 0;
} else {
voteType = data.getBoolean(JSONUtils.LIKES_KEY) ? 1 : -1;
}
Calendar postTimeCalendar = Calendar.getInstance();
postTimeCalendar.setTimeInMillis(postTime);
String formattedPostTime = new SimpleDateFormat("MMM d, YYYY, HH:mm",
mContext.getResources().getConfiguration().locale).format(postTimeCalendar.getTime());
String permalink = data.getString(JSONUtils.PERMALINK_KEY);
String previewUrl = "";
if(data.has(JSONUtils.PREVIEW_KEY)) {
previewUrl = data.getJSONObject(JSONUtils.PREVIEW_KEY).getJSONArray(JSONUtils.IMAGES_KEY).getJSONObject(0)
.getJSONObject(JSONUtils.SOURCE_KEY).getString(JSONUtils.URL_KEY);
}
if(data.has(JSONUtils.CROSSPOST_PARENT_LIST)) {
//Cross post
data = data.getJSONArray(JSONUtils.CROSSPOST_PARENT_LIST).getJSONObject(0);
parseData(data, permalink, newBestPostData, id, fullName, subredditName,
formattedPostTime, title, previewUrl, score, voteType, nsfw, i);
} else {
parseData(data, permalink, newBestPostData, id, fullName, subredditName,
formattedPostTime, title, previewUrl, score, voteType, nsfw, i);
}
}
} catch (JSONException e) {
Log.e("error", e.getMessage());
Log.i("Best post", "Error parsing data");
parseFailed = true;
}
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
if(!parseFailed) {
bestPostData.addAll(newBestPostData);
mParseBetPostListener.onParseBestPostSuccess(bestPostData, lastItem);
} else {
mParseBetPostListener.onParseBestPostFail();
}
}
}
private void parseData(JSONObject data, String permalink, ArrayList<BestPostData> bestPostData,
String id, String fullName, String subredditName, String formattedPostTime, String title,
String previewUrl, int score, int voteType, boolean nsfw, int i) throws JSONException {
boolean isVideo = data.getBoolean(JSONUtils.IS_VIDEO_KEY);
String url = data.getString(JSONUtils.URL_KEY);
if(!data.has(JSONUtils.PREVIEW_KEY) && previewUrl.equals("")) {
if(url.contains(permalink)) {
//Text post
Log.i("text", Integer.toString(i));
int postType = BestPostData.TEXT_TYPE;
BestPostData postData = new BestPostData(id, fullName, subredditName, formattedPostTime, title, permalink, score, postType, voteType, nsfw);
postData.setSelfText(data.getString(JSONUtils.SELF_TEXT_KEY).trim());
bestPostData.add(postData);
} else {
//No preview link post
Log.i("no preview link", Integer.toString(i));
int postType = BestPostData.NO_PREVIEW_LINK_TYPE;
BestPostData linkPostData = new BestPostData(id, fullName, subredditName, formattedPostTime, title, previewUrl, url, permalink, score, postType, voteType, nsfw);
bestPostData.add(linkPostData);
}
} else if(isVideo) {
//Video post
Log.i("video", Integer.toString(i));
JSONObject redditVideoObject = data.getJSONObject(JSONUtils.MEDIA_KEY).getJSONObject(JSONUtils.REDDIT_VIDEO_KEY);
int postType = BestPostData.VIDEO_TYPE;
String videoUrl = redditVideoObject.getString(JSONUtils.DASH_URL_KEY);
BestPostData videoPostData = new BestPostData(id, fullName, subredditName, formattedPostTime, title, previewUrl, permalink, score, postType, voteType, nsfw, true);
videoPostData.setVideoUrl(videoUrl);
videoPostData.setDownloadableGifOrVideo(false);
bestPostData.add(videoPostData);
} else {
JSONObject variations = data.getJSONObject(JSONUtils.PREVIEW_KEY).getJSONArray(JSONUtils.IMAGES_KEY).getJSONObject(0);
if (variations.has(JSONUtils.VARIANTS_KEY) && variations.getJSONObject(JSONUtils.VARIANTS_KEY).has(JSONUtils.MP4_KEY)) {
//Gif video post (MP4)
Log.i("gif video mp4", Integer.toString(i));
int postType = BestPostData.GIF_VIDEO_TYPE;
String videoUrl = variations.getJSONObject(JSONUtils.VARIANTS_KEY).getJSONObject(JSONUtils.MP4_KEY).getJSONObject(JSONUtils.SOURCE_KEY).getString(JSONUtils.URL_KEY);
String gifDownloadUrl = variations.getJSONObject(JSONUtils.VARIANTS_KEY).getJSONObject(JSONUtils.GIF_KEY).getJSONObject(JSONUtils.SOURCE_KEY).getString(JSONUtils.URL_KEY);
BestPostData post = new BestPostData(id, fullName, subredditName, formattedPostTime, title, previewUrl, permalink, score, postType, voteType, nsfw, false);
post.setVideoUrl(videoUrl);
post.setDownloadableGifOrVideo(true);
post.setGifOrVideoDownloadUrl(gifDownloadUrl);
bestPostData.add(post);
} else if(data.getJSONObject(JSONUtils.PREVIEW_KEY).has(JSONUtils.REDDIT_VIDEO_PREVIEW_KEY)) {
//Gif video post (Dash)
Log.i("gif video dash", Integer.toString(i));
int postType = BestPostData.GIF_VIDEO_TYPE;
String videoUrl = data.getJSONObject(JSONUtils.PREVIEW_KEY)
.getJSONObject(JSONUtils.REDDIT_VIDEO_PREVIEW_KEY).getString(JSONUtils.DASH_URL_KEY);
BestPostData post = new BestPostData(id, fullName, subredditName, formattedPostTime, title, previewUrl, permalink, score, postType, voteType, nsfw, true);
post.setVideoUrl(videoUrl);
post.setDownloadableGifOrVideo(false);
bestPostData.add(post);
} else {
if (url.endsWith("jpg") || url.endsWith("png")) {
//Image post
Log.i("image", Integer.toString(i));
int postType = BestPostData.IMAGE_TYPE;
bestPostData.add(new BestPostData(id, fullName, subredditName, formattedPostTime, title, url, url, permalink, score, postType, voteType, nsfw));
} else {
//Link post
Log.i("link", Integer.toString(i));
int postType = BestPostData.LINK_TYPE;
BestPostData linkPostData = new BestPostData(id, fullName, subredditName, formattedPostTime, title, previewUrl, url, permalink, score, postType, voteType, nsfw);
bestPostData.add(linkPostData);
}
}
}
}
/*private void parseData(JSONObject data, String permalink, ArrayList<BestPostData> bestPostData,
String id, String fullName, String subredditName, String formattedPostTime, String title,
int score, int voteType, boolean nsfw, int i) throws JSONException {
boolean isVideo = data.getBoolean(JSONUtils.IS_VIDEO_KEY);
if(!data.has(JSONUtils.PREVIEW_KEY)) {
String url = data.getString(JSONUtils.URL_KEY);
if(url.contains(permalink)) {
//Text post
Log.i("text", Integer.toString(i));
int postType = BestPostData.TEXT_TYPE;
BestPostData postData = new BestPostData(id, fullName, subredditName, formattedPostTime, title, permalink, score, postType, voteType, nsfw);
postData.setSelfText(data.getString(JSONUtils.SELF_TEXT_KEY).trim());
bestPostData.add(postData);
} else {
//No preview link post
Log.i("no preview link", Integer.toString(i));
int postType = BestPostData.NO_PREVIEW_LINK_TYPE;
BestPostData post = new BestPostData(id, fullName, subredditName, formattedPostTime, title, permalink, score, postType, voteType, nsfw);
post.setLinkUrl(url);
bestPostData.add(post);
}
} else if (!isVideo) {
JSONObject variations = data.getJSONObject(JSONUtils.PREVIEW_KEY).getJSONArray(JSONUtils.IMAGES_KEY).getJSONObject(0);
String previewUrl = variations.getJSONObject(JSONUtils.SOURCE_KEY).getString(JSONUtils.URL_KEY);
if (variations.has(JSONUtils.VARIANTS_KEY)) {
if (variations.getJSONObject(JSONUtils.VARIANTS_KEY).has(JSONUtils.MP4_KEY)) {
//Gif video
Log.i("gif video", Integer.toString(i));
int postType = BestPostData.GIF_VIDEO_TYPE;
String videoUrl = variations.getJSONObject(JSONUtils.VARIANTS_KEY).getJSONObject(JSONUtils.MP4_KEY).getJSONObject(JSONUtils.SOURCE_KEY).getString(JSONUtils.URL_KEY);
BestPostData post = new BestPostData(id, fullName, subredditName, formattedPostTime, title, previewUrl, permalink, score, postType, voteType, nsfw);
post.setVideoUrl(videoUrl);
bestPostData.add(post);
} else if (variations.getJSONObject(JSONUtils.VARIANTS_KEY).has(JSONUtils.GIF_KEY)) {
//Gif post
Log.i("gif", Integer.toString(i));
int postType = BestPostData.GIF_TYPE;
String gifUrl = variations.getJSONObject(JSONUtils.VARIANTS_KEY).getJSONObject(JSONUtils.GIF_KEY).getJSONObject(JSONUtils.SOURCE_KEY).getString(JSONUtils.URL_KEY);
BestPostData post = new BestPostData(id, fullName, subredditName, formattedPostTime, title, previewUrl, permalink, score, postType, voteType, nsfw);
post.setGifUrl(gifUrl);
bestPostData.add(post);
} else {
if(data.getJSONObject(JSONUtils.PREVIEW_KEY).has(JSONUtils.REDDIT_VIDEO_PREVIEW_KEY)) {
//Gif link post
Log.i("gif link", Integer.toString(i));
int postType = BestPostData.LINK_TYPE;
String gifUrl = data.getString(JSONUtils.URL_KEY);
BestPostData gifLinkPostData = new BestPostData(id, fullName, subredditName, formattedPostTime, title, previewUrl, permalink, score, postType, voteType, nsfw);
gifLinkPostData.setLinkUrl(gifUrl);
bestPostData.add(gifLinkPostData);
} else {
if(!data.isNull(JSONUtils.MEDIA_KEY)) {
//Video link post
Log.i("video link", Integer.toString(i));
int postType = BestPostData.LINK_TYPE;
String videoUrl = data.getString(JSONUtils.URL_KEY);
BestPostData videoLinkPostData = new BestPostData(id, fullName, subredditName, formattedPostTime, title, previewUrl, permalink, score, postType, voteType, nsfw);
videoLinkPostData.setLinkUrl(videoUrl);
bestPostData.add(videoLinkPostData);
} else {
if(data.getBoolean(JSONUtils.IS_REDDIT_MEDIA_DOMAIN)) {
//Image post
Log.i("image", Integer.toString(i));
int postType = BestPostData.IMAGE_TYPE;
bestPostData.add(new BestPostData(id, fullName, subredditName, formattedPostTime, title, previewUrl, permalink, score, postType, voteType, nsfw));
} else {
//Link post
Log.i("link", Integer.toString(i));
int postType = BestPostData.LINK_TYPE;
String linkUrl = data.getString(JSONUtils.URL_KEY);
BestPostData linkPostData = new BestPostData(id, fullName, subredditName, formattedPostTime, title, previewUrl, permalink, score, postType, voteType, nsfw);
linkPostData.setLinkUrl(linkUrl);
bestPostData.add(linkPostData);
}
}
}
}
} else {
//Image post
Toast.makeText(mContext, "Fixed post" + Integer.toString(i), Toast.LENGTH_SHORT).show();
Log.i("fixed image", Integer.toString(i));
int postType = BestPostData.IMAGE_TYPE;
bestPostData.add(new BestPostData(id, fullName, subredditName, formattedPostTime, title, previewUrl, permalink, score, postType, voteType, nsfw));
}
} else {
//Video post
Log.i("video", Integer.toString(i));
JSONObject redditVideoObject = data.getJSONObject(JSONUtils.MEDIA_KEY).getJSONObject(JSONUtils.REDDIT_VIDEO_KEY);
int postType = BestPostData.VIDEO_TYPE;
String videoUrl = redditVideoObject.getString(JSONUtils.DASH_URL_KEY);
String videoPreviewUrl = data.getJSONObject(JSONUtils.PREVIEW_KEY).getJSONArray(JSONUtils.IMAGES_KEY).getJSONObject(0).getJSONObject(JSONUtils.SOURCE_KEY).getString(JSONUtils.URL_KEY);
BestPostData videoPostData = new BestPostData(id, fullName, subredditName, formattedPostTime, title, videoPreviewUrl, permalink, score, postType, voteType, nsfw);
videoPostData.setVideoUrl(videoUrl);
bestPostData.add(videoPostData);
}
}*/
}

View File

@ -0,0 +1,105 @@
package ml.docilealligator.infinityforreddit;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
class ParseComment {
interface ParseCommentListener {
void onParseCommentSuccess(ArrayList<CommentData> commentData, int moreCommentCount);
void onParseCommentFail();
}
private Context mContext;
private ParseCommentListener mParseCommentListener;
void parseComment(Context context, String response, ArrayList<CommentData> commentData, ParseCommentListener parseCommentListener) {
mParseCommentListener = parseCommentListener;
mContext = context;
new ParseCommentAsyncTask(response, commentData).execute();
}
private class ParseCommentAsyncTask extends AsyncTask<Void, Void, Void> {
private JSONArray jsonResponse;
private ArrayList<CommentData> commentData;
private ArrayList<CommentData> newcommentData;
private boolean parseFailed;
int moreCommentCount;
ParseCommentAsyncTask(String response, ArrayList<CommentData> commentData){
try {
jsonResponse = new JSONArray(response);
this.commentData = commentData;
newcommentData = new ArrayList<>();
parseFailed = false;
} catch (JSONException e) {
Log.i("comment json error", e.getMessage());
mParseCommentListener.onParseCommentFail();
}
}
@Override
protected Void doInBackground(Void... voids) {
try {
moreCommentCount = 0;
int actualCommentLength;
JSONArray allComments = jsonResponse.getJSONObject(1).getJSONObject(JSONUtils.DATA_KEY).getJSONArray(JSONUtils.CHILDREN_KEY);
JSONObject more = allComments.getJSONObject(allComments.length() - 1).getJSONObject(JSONUtils.DATA_KEY);
if(more.has(JSONUtils.COUNT_KEY)) {
moreCommentCount = more.getInt(JSONUtils.COUNT_KEY);
actualCommentLength = allComments.length() - 1;
} else {
actualCommentLength = allComments.length();
}
for (int i = 0; i < actualCommentLength; i++) {
JSONObject data = allComments.getJSONObject(i).getJSONObject(JSONUtils.DATA_KEY);
String id = data.getString(JSONUtils.ID_KEY);
String author = data.getString(JSONUtils.AUTHOR_KEY);
boolean isSubmitter = data.getBoolean(JSONUtils.IS_SUBMITTER_KEY);
String commentContent = data.getString(JSONUtils.BODY_KEY);
String permalink = data.getString(JSONUtils.PERMALINK_KEY);
int score = data.getInt(JSONUtils.SCORE_KEY);
long submitTime = data.getLong(JSONUtils.CREATED_UTC_KEY) * 1000;
boolean scoreHidden = data.getBoolean(JSONUtils.SCORE_HIDDEN_KEY);
Calendar submitTimeCalendar = Calendar.getInstance();
submitTimeCalendar.setTimeInMillis(submitTime);
String formattedSubmitTime = new SimpleDateFormat("MMM d, YYYY, HH:mm",
mContext.getResources().getConfiguration().locale).format(submitTimeCalendar.getTime());
int depth = data.getInt(JSONUtils.DEPTH_KEY);
boolean collapsed = data.getBoolean(JSONUtils.COLLAPSED_KEY);
boolean hasReply = !(data.get(JSONUtils.REPLIES_KEY) instanceof String);
newcommentData.add(new CommentData(id, author, formattedSubmitTime, commentContent, score, isSubmitter, permalink, depth, collapsed, hasReply, scoreHidden));
}
} catch (JSONException e) {
parseFailed = true;
Log.i("parse comment error", e.getMessage());
mParseCommentListener.onParseCommentFail();
}
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
if(!parseFailed) {
commentData.addAll(newcommentData);
mParseCommentListener.onParseCommentSuccess(commentData, moreCommentCount);
} else {
mParseCommentListener.onParseCommentFail();
}
}
}
}

View File

@ -0,0 +1,4 @@
package ml.docilealligator.infinityforreddit;
class ParseSubscribedSubreddits {
}

View File

@ -0,0 +1,70 @@
package ml.docilealligator.infinityforreddit;
import android.content.Context;
import android.os.AsyncTask;
import android.text.Html;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
class ParseUserInfo {
interface ParseUserInfoListener {
void onParseUserInfoSuccess(String name, String profileImageUrl, String bannerImageUrl, int karma);
void onParseUserInfoFail();
}
private Context mContext;
private ParseUserInfoListener mParseUserInfoListener;
void parseUserInfo(Context context, String response, ParseUserInfoListener parseUserInfoListener) {
mParseUserInfoListener = parseUserInfoListener;
mContext = context;
new ParseUserInfo.ParseUserInfoAsyncTask(response).execute();
}
private class ParseUserInfoAsyncTask extends AsyncTask<Void, Void, Void> {
private JSONObject jsonResponse;
private boolean parseFailed;
private String name;
private String profileImageUrl;
private String bannerImageUrl;
private int karma;
ParseUserInfoAsyncTask(String response){
try {
jsonResponse = new JSONObject(response);
parseFailed = false;
} catch (JSONException e) {
Log.i("user info json error", e.getMessage());
mParseUserInfoListener.onParseUserInfoFail();
}
}
@Override
protected Void doInBackground(Void... voids) {
try {
name = jsonResponse.getString(JSONUtils.NAME_KEY);
profileImageUrl = Html.fromHtml(jsonResponse.getString(JSONUtils.ICON_IMG_KEY)).toString();
bannerImageUrl = Html.fromHtml(jsonResponse.getJSONObject(JSONUtils.SUBREDDIT_KEY).getString(JSONUtils.BANNER_IMG_KEY)).toString();
int linkKarma = jsonResponse.getInt(JSONUtils.LINK_KARMA_KEY);
int commentKarma = jsonResponse.getInt(JSONUtils.COMMENT_KARMA_KEY);
karma = linkKarma + commentKarma;
} catch (JSONException e) {
parseFailed = true;
Log.i("parse comment error", e.getMessage());
}
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
if(!parseFailed) {
mParseUserInfoListener.onParseUserInfoSuccess(name, profileImageUrl, bannerImageUrl, karma);
} else {
mParseUserInfoListener.onParseUserInfoFail();
}
}
}
}

View File

@ -0,0 +1,73 @@
package ml.docilealligator.infinityforreddit;
import android.util.Base64;
import java.util.HashMap;
import java.util.Map;
/**
* Created by alex on 2/23/18.
*/
class RedditUtils {
static final String OAUTH_URL ="https://www.reddit.com/api/v1/authorize.compact";
static final String ACQUIRE_ACCESS_TOKEN_URL = "https://www.reddit.com/api/v1/access_token";
static final String OAUTH_API_BASE_URI = "https://oauth.reddit.com";
static final String API_BASE_URI = "https://www.reddit.com";
static final String BEST_POST_SUFFIX = "/best?raw_json=1";
static final String VOTE_SUFFIX = "/api/vote";
static final String USER_INFO_SUFFIX = "/api/v1/me?raw_json=1";
static final String CLIENT_ID_KEY = "client_id";
static final String CLIENT_ID = "";
static final String RESPONSE_TYPE_KEY = "response_type";
static final String RESPONSE_TYPE = "code";
static final String STATE_KEY = "state";
static final String STATE = "";
static final String REDIRECT_URI_KEY = "redirect_uri";
static final String REDIRECT_URI = "";
static final String DURATION_KEY = "duration";
static final String DURATION = "permanent";
static final String SCOPE_KEY = "scope";
static final String SCOPE = "identity edit flair history modconfig modflair modlog modposts modwiki mysubreddits privatemessages read report save submit subscribe vote wikiedit wikiread";
static final String ACCESS_TOKEN_KEY = "access_token";
static final String EXPIRES_IN_KEY = "expires_in";
static final String AUTHORIZATION_KEY = "Authorization";
static final String AUTHORIZATION_BASE = "bearer ";
static final String USER_AGENT_KEY = "User-Agent";
static final String USER_AGENT = "";
static final String GRANT_TYPE_KEY = "grant_type";
static final String GRANT_TYPE_REFRESH_TOKEN = "refresh_token";
static final String REFRESH_TOKEN_KEY = "refresh_token";
static final String DIR_KEY = "dir";
static final String ID_KEY = "id";
static final String RANK_KEY = "rank";
static final String DIR_UPVOTE = "1";
static final String DIR_UNVOTE = "0";
static final String DIR_DOWNVOTE = "-1";
static final String RANK = "10";
static final String AFTER_KEY = "after";
static Map<String, String> getHttpBasicAuthHeader() {
Map<String, String> params = new HashMap<>();
String credentials = String.format("%s:%s", RedditUtils.CLIENT_ID, "");
String auth = "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
params.put(RedditUtils.AUTHORIZATION_KEY, auth);
return params;
}
static Map<String, String> getOAuthHeader(String accessToken) {
Map<String, String> params = new HashMap<>();
params.put(RedditUtils.AUTHORIZATION_KEY, RedditUtils.AUTHORIZATION_BASE + accessToken);
params.put(RedditUtils.USER_AGENT_KEY, RedditUtils.USER_AGENT);
return params;
}
static String getQueryCommentURI(String subredditName, String article) {
return API_BASE_URI + "/" + subredditName + "/comments/" + article + ".json";
}
}

View File

@ -0,0 +1,20 @@
package ml.docilealligator.infinityforreddit;
/**
* Created by alex on 2/23/18.
*/
class SharedPreferencesUtils {
static final String AUTH_CODE_FILE_KEY = "Auth_Code_Pref";
static final String USER_INFO_FILE_KEY = "User_Info";
static final String AUTH_CODE_KEY = "code";
static final String ACCESS_TOKEN_KEY = "accessToken";
static final String REFRESH_TOKEN_KEY = "refreshToken";
static final String QUERY_ACCESS_TOKEN_TIME_KEY = "queryAccessTokenTime";
static final String ACCESS_TOKEN_EXPIRE_TIME_KEY = "accessTokenExpireTime";
static final String MODHASH_KEY = "modhash";
static final String USER_KEY = "user";
static final String PROFILE_IMAGE_URL_KEY = "profileImageUrl";
static final String BANNER_IMAGE_URL_KEY = "bannerImageUrl";
static final String KARMA_KEY = "karma";
}

View File

@ -0,0 +1,425 @@
package ml.docilealligator.infinityforreddit;
import android.Manifest;
import android.animation.Animator;
import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.PorterDuff;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.Toast;
import com.alexvasilkov.gestures.views.GestureImageView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.RequestOptions;
import com.bumptech.glide.request.target.SimpleTarget;
import com.bumptech.glide.request.target.Target;
import com.bumptech.glide.request.transition.Transition;
import com.github.pwittchen.swipe.library.rx2.SimpleSwipeListener;
import com.github.pwittchen.swipe.library.rx2.Swipe;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class ViewImageActivity extends AppCompatActivity {
private static final int PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE = 0;
static final String TITLE_KEY = "TK";
static final String IMAGE_URL_KEY = "IUK";
static final String SUBREDDIT_KEY = "SK";
static final String ID_KEY = "IK";
private boolean isActionBarHidden = false;
private boolean isDownloading = false;
private Menu mMenu;
private Swipe swipe;
private String mImageUrl;
private String mImageFileName;
private float totalLengthY = 0.0f;
private float touchY = -1.0f;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_image);
final ActionBar actionBar = getSupportActionBar();
final Drawable upArrow = getResources().getDrawable(R.drawable.ic_arrow_back_white_24dp);
actionBar.setHomeAsUpIndicator(upArrow);
Intent intent = getIntent();
mImageUrl = intent.getExtras().getString(IMAGE_URL_KEY);
String title = intent.getExtras().getString(TITLE_KEY);
final Spannable text = new SpannableString(title);
setTitle(text);
mImageFileName = intent.getExtras().getString(SUBREDDIT_KEY).substring(2) + "-" + intent.getExtras().getString(ID_KEY).substring(3);
final RelativeLayout relativeLayout = findViewById(R.id.parent_relative_layout_view_image_activity);
final GestureImageView imageView = findViewById(R.id.image_view_view_image_activity);
final ProgressBar progressBar = findViewById(R.id.progress_bar_view_image_activity);
final float pxHeight = getResources().getDisplayMetrics().heightPixels;
int activityColorFrom = getResources().getColor(android.R.color.black);
int actionBarColorFrom = getResources().getColor(R.color.transparentActionBarColor);
int actionBarElementColorFrom = getResources().getColor(android.R.color.white);
int colorTo = getResources().getColor(android.R.color.transparent);
final ValueAnimator activityColorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), activityColorFrom, colorTo);
final ValueAnimator actionBarColorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), actionBarColorFrom, colorTo);
final ValueAnimator actionBarElementColorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), actionBarElementColorFrom, colorTo);
activityColorAnimation.setDuration(300); // milliseconds
actionBarColorAnimation.setDuration(300);
actionBarElementColorAnimation.setDuration(300);
activityColorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
relativeLayout.setBackgroundColor((int) valueAnimator.getAnimatedValue());
}
});
actionBarColorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
actionBar.setBackgroundDrawable(new ColorDrawable((int) valueAnimator.getAnimatedValue()));
}
});
actionBarElementColorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
text.setSpan(new ForegroundColorSpan((int) valueAnimator.getAnimatedValue()), 0, text.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
actionBar.setTitle(text);
upArrow.setColorFilter((int) valueAnimator.getAnimatedValue(), PorterDuff.Mode.SRC_IN);
if(mMenu != null) {
Drawable drawable = mMenu.getItem(0).getIcon();
//drawable.mutate();
drawable.setColorFilter((int) valueAnimator.getAnimatedValue(), PorterDuff.Mode.SRC_IN);
}
}
});
swipe = new Swipe();
swipe.setListener(new SimpleSwipeListener() {
@Override
public void onSwipingUp(final MotionEvent event) {
float nowY = event.getY();
float offset;
if (touchY == -1.0f) {
offset = 0.0f;
} else {
offset = nowY - touchY;
}
totalLengthY += offset;
touchY = nowY;
imageView.animate()
.y(totalLengthY)
.setDuration(0)
.start();
}
@Override
public boolean onSwipedUp(final MotionEvent event) {
imageView.animate()
.y(0)
.setDuration(300)
.start();
if (totalLengthY < -pxHeight / 8) {
imageView.animate()
.y(-pxHeight)
.setDuration(300)
.setListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
activityColorAnimation.start();
actionBarColorAnimation.start();
actionBarElementColorAnimation.start();
}
@Override
public void onAnimationEnd(Animator animator) {
finish();
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
})
.start();
} else {
imageView.animate()
.y(0)
.setDuration(300)
.start();
}
totalLengthY = 0.0f;
touchY = -1.0f;
return false;
}
@Override
public void onSwipingDown(final MotionEvent event) {
float nowY = event.getY();
float offset;
if (touchY == -1.0f) {
offset = 0.0f;
} else {
offset = nowY - touchY;
}
totalLengthY += offset;
touchY = nowY;
imageView.animate()
.y(totalLengthY)
.setDuration(0)
.start();
}
@Override
public boolean onSwipedDown(final MotionEvent event) {
if (totalLengthY > pxHeight / 8) {
imageView.animate()
.y(pxHeight)
.setDuration(300)
.setListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
activityColorAnimation.start();
actionBarColorAnimation.start();
actionBarElementColorAnimation.start();
}
@Override
public void onAnimationEnd(Animator animator) {
finish();
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
})
.start();
} else {
imageView.animate()
.y(0)
.setDuration(300)
.start();
}
totalLengthY = 0.0f;
touchY = -1.0f;
return false;
}
});
imageView.getController().getSettings()
.setPanEnabled(true)
.setRotationEnabled(true)
.setRestrictRotation(true);
Glide.with(this).load(mImageUrl).listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
progressBar.setVisibility(View.GONE);
return false;
}
}).apply(new RequestOptions().fitCenter()).into(imageView);
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (isActionBarHidden) {
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
isActionBarHidden = false;
} else {
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE);
isActionBarHidden = true;
}
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
mMenu = menu;
getMenuInflater().inflate(R.menu.view_image, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
case R.id.action_download_view_image:
if (isDownloading) {
return false;
}
isDownloading = true;
if (Build.VERSION.SDK_INT >= 23) {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
// Permission is not granted
// No explanation needed; request the permission
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE);
} else {
// Permission has already been granted
saveImage();
}
} else {
saveImage();
}
return true;
}
return false;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
swipe.dispatchTouchEvent(ev);
return super.dispatchTouchEvent(ev);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if(requestCode == PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE && grantResults.length > 0) {
if(grantResults[0] == PackageManager.PERMISSION_DENIED) {
Toast.makeText(this, "No storage permission to save this file", Toast.LENGTH_SHORT).show();
} else if(grantResults[0] == PackageManager.PERMISSION_GRANTED && isDownloading) {
saveImage();
}
isDownloading = false;
}
}
private void saveImage() {
Glide.with(this)
.asBitmap()
.load(mImageUrl)
.into(new SimpleTarget<Bitmap>() {
@SuppressLint("StaticFieldLeak")
@Override
public void onResourceReady(@NonNull final Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
new AsyncTask<Void, Void, Void>() {
private boolean saveSuccess = true;
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
isDownloading = false;
if(saveSuccess) {
Toast.makeText(ViewImageActivity.this, "Download completed", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(ViewImageActivity.this, "Download failed", Toast.LENGTH_SHORT).show();
}
}
@Override
protected Void doInBackground(Void... params) {
try {
String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString();
File directory = new File(path + "/Infinity/");
if(!directory.exists()) {
if(!directory.mkdir()) {
saveSuccess = false;
return null;
}
} else {
if(directory.isFile()) {
if(!directory.delete() && !directory.mkdir()) {
saveSuccess = false;
return null;
}
}
}
File file = new File(path + "/Infinity/", mImageFileName + ".jpg");
int postfix = 1;
while(file.exists()) {
file = new File(path + "/Infinity/", mImageFileName + "-" + postfix + ".jpg");
}
OutputStream outputStream = new FileOutputStream(file);
resource.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
outputStream.flush();
outputStream.close();
} catch (IOException e) {
saveSuccess = false;
e.printStackTrace();
}
return null;
}
}.execute();
}
});
}
}

View File

@ -0,0 +1,342 @@
package ml.docilealligator.infinityforreddit;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.customtabs.CustomTabsIntent;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.CardView;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.Volley;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;
import java.util.ArrayList;
import de.hdodenhof.circleimageview.CircleImageView;
public class ViewPostDetailActivity extends AppCompatActivity {
static final String EXTRA_TITLE = "ET";
static final String EXTRA_POST_DATA = "EPD";
private int orientation;
private String orientationState = "OS";
private int mMoreCommentCount;
private BestPostData mPostData;
private CoordinatorLayout mCoordinatorLayout;
private ProgressBar mCommentProgressbar;
private CardView mCommentCardView;
private RecyclerView mRecyclerView;
private RequestQueue mVoteThingQueue;
private RequestQueue mCommentQueue;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_post_detail);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
orientation = getResources().getConfiguration().orientation;
mPostData = getIntent().getExtras().getParcelable(EXTRA_POST_DATA);
TextView titleTextView = findViewById(R.id.title_text_view_view_post_detail);
titleTextView.setText(mPostData.getTitle());
mCoordinatorLayout = findViewById(R.id.coordinator_layout_view_post_detail);
CircleImageView subredditImageView = findViewById(R.id.subreddit_icon_circle_image_view_view_post_detail);
TextView postTimeTextView = findViewById(R.id.post_time_text_view_view_post_detail);
TextView subredditTextView = findViewById(R.id.subreddit_text_view_view_post_detail);
TextView contentTextView = findViewById(R.id.content_text_view_view_post_detail);
TextView typeTextView = findViewById(R.id.type_text_view_view_post_detail);
TextView nsfwTextView = findViewById(R.id.nsfw_text_view_view_post_detail);
RelativeLayout relativeLayout = findViewById(R.id.image_view_wrapper_view_post_detail);
final ProgressBar progressBar = findViewById(R.id.progress_bar_view_post_detail);
ImageView imageView = findViewById(R.id.image_view_view_post_detail);
ImageView noPreviewLinkImageView = findViewById(R.id.image_view_no_preview_link_view_post_detail);
ImageView plusButton = findViewById(R.id.plus_button_view_post_detail);
TextView scoreTextView = findViewById(R.id.score_text_view_view_post_detail);
ImageView minusButton = findViewById(R.id.minus_button_view_post_detail);
ImageView shareButton = findViewById(R.id.share_button_view_post_detail);
mCommentProgressbar = findViewById(R.id.comment_progress_bar_view_post_detail);
mCommentCardView = findViewById(R.id.comment_card_view_view_post_detail);
mRecyclerView = findViewById(R.id.recycler_view_view_post_detail);
mRecyclerView.setNestedScrollingEnabled(false);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
mVoteThingQueue = Volley.newRequestQueue(this);
mCommentQueue = Volley.newRequestQueue(this);
subredditTextView.setText(mPostData.getSubredditName());
postTimeTextView.setText(mPostData.getPostTime());
if(mPostData.getNSFW()) {
nsfwTextView.setVisibility(View.VISIBLE);
}
scoreTextView.setText(Integer.toString(mPostData.getScore()));
shareButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
String extraText = mPostData.getTitle() + "\n" + mPostData.getPermalink();
intent.putExtra(Intent.EXTRA_TEXT, extraText);
startActivity(Intent.createChooser(intent, "Share"));
}
});
switch (mPostData.getPostType()) {
case BestPostData.IMAGE_TYPE:
typeTextView.setText("IMAGE");
relativeLayout.setVisibility(View.VISIBLE);
Glide.with(this).load(mPostData.getPreviewUrl()).listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
//Need to be implemented
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
progressBar.setVisibility(View.GONE);
return false;
}
}).into(imageView);
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(ViewPostDetailActivity.this, ViewImageActivity.class);
intent.putExtra(ViewImageActivity.IMAGE_URL_KEY, mPostData.getPreviewUrl());
intent.putExtra(ViewImageActivity.TITLE_KEY, mPostData.getTitle());
startActivity(intent);
}
});
break;
case BestPostData.LINK_TYPE:
relativeLayout.setVisibility(View.VISIBLE);
typeTextView.setText("LINK");
String linkPreviewUrl = mPostData.getPreviewUrl();
Glide.with(this).load(linkPreviewUrl).listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
progressBar.setVisibility(View.GONE);
return false;
}
}).into(imageView);
final String linkUrl = mPostData.getUrl();
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
// add share action to menu list
builder.addDefaultShareMenuItem();
builder.setToolbarColor(getResources().getColor(R.color.colorPrimary));
CustomTabsIntent customTabsIntent = builder.build();
customTabsIntent.launchUrl(ViewPostDetailActivity.this, Uri.parse(linkUrl));
}
});
break;
case BestPostData.GIF_VIDEO_TYPE:
relativeLayout.setVisibility(View.VISIBLE);
typeTextView.setText("VIDEO");
String gifVideoPreviewUrl = mPostData.getPreviewUrl();
Glide.with(this).load(gifVideoPreviewUrl).listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
progressBar.setVisibility(View.GONE);
return false;
}
}).into(imageView);
String gifVideoUrl = mPostData.getVideoUrl();
final Uri gifVideoUri = Uri.parse(gifVideoUrl);
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(ViewPostDetailActivity.this, ViewVideoActivity.class);
intent.setData(gifVideoUri);
intent.putExtra(ViewVideoActivity.TITLE_KEY, mPostData.getTitle());
intent.putExtra(ViewVideoActivity.IS_DASH_VIDEO_KEY, mPostData.isDashVideo());
intent.putExtra(ViewVideoActivity.IS_DOWNLOADABLE_KEY, mPostData.isDownloadableGifOrVideo());
if(mPostData.isDownloadableGifOrVideo()) {
intent.putExtra(ViewVideoActivity.DOWNLOAD_URL_KEY, mPostData.getGifOrVideoDownloadUrl());
intent.putExtra(ViewVideoActivity.SUBREDDIT_KEY, mPostData.getSubredditName());
intent.putExtra(ViewVideoActivity.ID_KEY, mPostData.getId());
}
startActivity(intent);
}
});
break;
case BestPostData.VIDEO_TYPE:
relativeLayout.setVisibility(View.VISIBLE);
typeTextView.setText("VIDEO");
String videoPreviewUrl = mPostData.getPreviewUrl();
Glide.with(this).load(videoPreviewUrl).listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
progressBar.setVisibility(View.GONE);
return false;
}
}).into(imageView);
String videoUrl = mPostData.getVideoUrl();
final Uri videoUri = Uri.parse(videoUrl);
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(ViewPostDetailActivity.this, ViewVideoActivity.class);
intent.setData(videoUri);
intent.putExtra(ViewVideoActivity.TITLE_KEY, mPostData.getTitle());
intent.putExtra(ViewVideoActivity.IS_DASH_VIDEO_KEY, mPostData.isDashVideo());
intent.putExtra(ViewVideoActivity.IS_DOWNLOADABLE_KEY, mPostData.isDownloadableGifOrVideo());
if(mPostData.isDownloadableGifOrVideo()) {
intent.putExtra(ViewVideoActivity.DOWNLOAD_URL_KEY, mPostData.getGifOrVideoDownloadUrl());
intent.putExtra(ViewVideoActivity.SUBREDDIT_KEY, mPostData.getSubredditName());
intent.putExtra(ViewVideoActivity.ID_KEY, mPostData.getId());
}
startActivity(intent);
}
});
break;
case BestPostData.NO_PREVIEW_LINK_TYPE:
typeTextView.setText("LINK");
noPreviewLinkImageView.setVisibility(View.VISIBLE);
noPreviewLinkImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
// add share action to menu list
builder.addDefaultShareMenuItem();
builder.setToolbarColor(getResources().getColor(R.color.colorPrimary));
CustomTabsIntent customTabsIntent = builder.build();
customTabsIntent.launchUrl(ViewPostDetailActivity.this, Uri.parse(mPostData.getUrl()));
}
});
break;
case BestPostData.TEXT_TYPE:
typeTextView.setText("TEXT");
if(!mPostData.getSelfText().equals("")) {
contentTextView.setVisibility(View.VISIBLE);
contentTextView.setText(mPostData.getSelfText());
}
}
queryComment();
}
private void queryComment() {
mCommentProgressbar.setVisibility(View.VISIBLE);
new FetchComment(mCommentQueue, mPostData.getSubredditName(), mPostData.getId()).queryComment(new FetchComment.FetchCommentListener() {
@Override
public void onFetchCommentSuccess(String response) {
new ParseComment().parseComment(ViewPostDetailActivity.this, response, new ArrayList<CommentData>(), new ParseComment.ParseCommentListener() {
@Override
public void onParseCommentSuccess(ArrayList<CommentData> commentData, int moreCommentCount) {
mCommentProgressbar.setVisibility(View.GONE);
mMoreCommentCount = moreCommentCount;
if(commentData.size() > 0) {
CommentRecyclerViewAdapter adapter = new CommentRecyclerViewAdapter(ViewPostDetailActivity.this, commentData, mVoteThingQueue);
mRecyclerView.setAdapter(adapter);
mCommentCardView.setVisibility(View.VISIBLE);
}
}
@Override
public void onParseCommentFail() {
mCommentProgressbar.setVisibility(View.GONE);
showRetrySnackbar();
}
});
}
@Override
public void onFetchCommentFail() {
mCommentProgressbar.setVisibility(View.GONE);
showRetrySnackbar();
}
});
}
private void showRetrySnackbar() {
Snackbar snackbar = Snackbar.make(mCoordinatorLayout, R.string.load_comment_failed, Snackbar.LENGTH_INDEFINITE);
snackbar.setAction(R.string.retry, new View.OnClickListener() {
@Override
public void onClick(View view) {
queryComment();
}
});
snackbar.show();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
return true;
}
return false;
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(orientationState, orientation);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
orientation = savedInstanceState.getInt(orientationState);
}
@Override
public void onBackPressed() {
if(orientation == getResources().getConfiguration().orientation) {
super.onBackPressed();
} else {
finish();
}
}
}

View File

@ -0,0 +1,406 @@
package ml.docilealligator.infinityforreddit;
import android.Manifest;
import android.animation.Animator;
import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
import android.app.DownloadManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.PorterDuff;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.Toast;
import com.github.pwittchen.swipe.library.rx2.SimpleSwipeListener;
import com.github.pwittchen.swipe.library.rx2.Swipe;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.dash.DashChunkSource;
import com.google.android.exoplayer2.source.dash.DashMediaSource;
import com.google.android.exoplayer2.source.dash.DefaultDashChunkSource;
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.ui.PlayerControlView;
import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.util.Util;
public class ViewVideoActivity extends AppCompatActivity {
private static final int PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE = 0;
static final String TITLE_KEY = "TK";
static final String IS_DASH_VIDEO_KEY = "IDVK";
static final String IS_DOWNLOADABLE_KEY = "IDK";
static final String DOWNLOAD_URL_KEY = "DUK";
static final String SUBREDDIT_KEY = "SK";
static final String ID_KEY = "IK";
private Uri mVideoUri;
private SimpleExoPlayer player;
private Menu mMenu;
private Swipe swipe;
private String mGifOrVideoFileName;
private String mDownloadUrl;
private boolean mIsDashVideo;
private boolean wasPlaying;
private boolean isDownloading = false;
private float totalLengthY = 0.0f;
private float touchY = -1.0f;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_video);
final ActionBar actionBar = getSupportActionBar();
final Drawable upArrow = getResources().getDrawable(R.drawable.ic_arrow_back_white_24dp);
getSupportActionBar().setHomeAsUpIndicator(upArrow);
//Set player controller margin bottom in order to display it above the navbar
int resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
LinearLayout controllerLinearLayout = findViewById(R.id.linear_layout_exo_playback_control_view);
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) controllerLinearLayout.getLayoutParams();
params.bottomMargin = getResources().getDimensionPixelSize(resourceId);
Intent intent = getIntent();
mVideoUri = intent.getData();
mIsDashVideo = intent.getExtras().getBoolean(IS_DASH_VIDEO_KEY);
String title = intent.getExtras().getString(TITLE_KEY);
final Spannable text = new SpannableString(title);
setTitle(text);
if(intent.getExtras().getBoolean(IS_DOWNLOADABLE_KEY)) {
mGifOrVideoFileName = intent.getExtras().getString(SUBREDDIT_KEY).substring(2)
+ "-" + intent.getExtras().getString(ID_KEY).substring(3) + ".gif";
mDownloadUrl = intent.getExtras().getString(DOWNLOAD_URL_KEY);
}
final RelativeLayout relativeLayout = findViewById(R.id.relative_layout_view_video_activity);
final PlayerView videoPlayerView = findViewById(R.id.player_view_view_video_activity);
final float pxHeight = getResources().getDisplayMetrics().heightPixels;
int activityColorFrom = getResources().getColor(android.R.color.black);
int actionBarColorFrom = getResources().getColor(R.color.transparentActionBarColor);
int actionBarElementColorFrom = getResources().getColor(android.R.color.white);
int colorTo = getResources().getColor(android.R.color.transparent);
final ValueAnimator activityColorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), activityColorFrom, colorTo);
final ValueAnimator actionBarColorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), actionBarColorFrom, colorTo);
final ValueAnimator actionBarElementColorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), actionBarElementColorFrom, colorTo);
activityColorAnimation.setDuration(300); // milliseconds
actionBarColorAnimation.setDuration(300);
actionBarElementColorAnimation.setDuration(300);
activityColorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
relativeLayout.setBackgroundColor((int) valueAnimator.getAnimatedValue());
}
});
actionBarColorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
actionBar.setBackgroundDrawable(new ColorDrawable((int) valueAnimator.getAnimatedValue()));
}
});
actionBarElementColorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
text.setSpan(new ForegroundColorSpan((int) valueAnimator.getAnimatedValue()), 0, text.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
actionBar.setTitle(text);
upArrow.setColorFilter((int) valueAnimator.getAnimatedValue(), PorterDuff.Mode.SRC_IN);
if(mMenu != null) {
Drawable drawable = mMenu.getItem(0).getIcon();
drawable.setColorFilter((int) valueAnimator.getAnimatedValue(), PorterDuff.Mode.SRC_IN);
}
}
});
swipe = new Swipe();
swipe.setListener(new SimpleSwipeListener() {
@Override
public void onSwipingUp(final MotionEvent event) {
float nowY = event.getY();
float offset;
if(touchY == -1.0f) {
offset = 0.0f;
} else {
offset = nowY - touchY;
}
totalLengthY += offset;
touchY = nowY;
videoPlayerView.animate()
.y(totalLengthY)
.setDuration(0)
.start();
}
@Override
public boolean onSwipedUp(final MotionEvent event) {
videoPlayerView.animate()
.y(0)
.setDuration(300)
.start();
if(totalLengthY < -pxHeight / 8) {
videoPlayerView.animate()
.y(-pxHeight)
.setDuration(300)
.setListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
activityColorAnimation.start();
actionBarColorAnimation.start();
actionBarElementColorAnimation.start();
}
@Override
public void onAnimationEnd(Animator animator) {
finish();
}
@Override
public void onAnimationCancel(Animator animator) {}
@Override
public void onAnimationRepeat(Animator animator) {}
})
.start();
} else {
videoPlayerView.animate()
.y(0)
.setDuration(300)
.start();
}
totalLengthY = 0.0f;
touchY = -1.0f;
return false;
}
@Override
public void onSwipingDown(final MotionEvent event) {
float nowY = event.getY();
float offset;
if(touchY == -1.0f) {
offset = 0.0f;
} else {
offset = nowY - touchY;
}
totalLengthY += offset;
touchY = nowY;
videoPlayerView.animate()
.y(totalLengthY)
.setDuration(0)
.start();
}
@Override
public boolean onSwipedDown(final MotionEvent event) {
if(totalLengthY > pxHeight / 8) {
videoPlayerView.animate()
.y(pxHeight)
.setDuration(300)
.setListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
activityColorAnimation.start();
actionBarColorAnimation.start();
actionBarElementColorAnimation.start();
}
@Override
public void onAnimationEnd(Animator animator) {
finish();
}
@Override
public void onAnimationCancel(Animator animator) {}
@Override
public void onAnimationRepeat(Animator animator) {}
})
.start();
} else {
videoPlayerView.animate()
.y(0)
.setDuration(300)
.start();
}
totalLengthY = 0.0f;
touchY = -1.0f;
return false;
}
});
videoPlayerView.setControllerVisibilityListener(new PlayerControlView.VisibilityListener() {
@Override
public void onVisibilityChange(int visibility) {
switch (visibility) {
case View.GONE:
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE);
break;
case View.VISIBLE:
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
}
});
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter);
TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
player = ExoPlayerFactory.newSimpleInstance(this, trackSelector);
videoPlayerView.setPlayer(player);
// Produces DataSource instances through which media data is loaded.
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(this,
Util.getUserAgent(this, "Infinity"), bandwidthMeter);
// Prepare the player with the source.
if(mIsDashVideo) {
DashChunkSource.Factory dashChunkSourceFactory = new DefaultDashChunkSource.Factory(dataSourceFactory);
player.prepare(new DashMediaSource(mVideoUri, dataSourceFactory, dashChunkSourceFactory, null, null));
} else {
player.prepare(new ExtractorMediaSource.Factory(dataSourceFactory).createMediaSource(mVideoUri));
}
player.setRepeatMode(Player.REPEAT_MODE_ALL);
player.setPlayWhenReady(true);
wasPlaying = true;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
if(!mIsDashVideo) {
getMenuInflater().inflate(R.menu.view_video, menu);
mMenu = menu;
}
return true;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
swipe.dispatchTouchEvent(ev);
return super.dispatchTouchEvent(ev);
}
@Override
protected void onDestroy() {
super.onDestroy();
player.release();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
case R.id.action_download_view_video:
isDownloading = true;
if (Build.VERSION.SDK_INT >= 23) {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
// Permission is not granted
// No explanation needed; request the permission
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE);
} else {
// Permission has already been granted
download();
}
} else {
download();
}
return true;
}
return false;
}
@Override
protected void onStart() {
super.onStart();
if(wasPlaying) {
player.setPlayWhenReady(true);
}
}
@Override
protected void onStop() {
super.onStop();
wasPlaying = player.getPlayWhenReady();
player.setPlayWhenReady(false);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if(requestCode == PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE && grantResults.length > 0) {
if(grantResults[0] == PackageManager.PERMISSION_DENIED) {
Toast.makeText(this, "No storage permission to save this file", Toast.LENGTH_SHORT).show();
} else if(grantResults[0] == PackageManager.PERMISSION_GRANTED && isDownloading) {
download();
}
isDownloading = false;
}
}
private void download() {
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(mDownloadUrl));
request.setTitle(mGifOrVideoFileName);
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_PICTURES + "/Infinity/", mGifOrVideoFileName);
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);
}
}

View File

@ -0,0 +1,88 @@
package ml.docilealligator.infinityforreddit;
import android.content.Context;
import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import java.util.HashMap;
import java.util.Map;
/**
* Created by alex on 3/14/18.
*/
class VoteThing {
interface VoteThingListener {
void onVoteThingSuccess(int position);
void onVoteThingFail(int position);
}
private Context mContext;
private VoteThingListener mVoteThingListener;
private RequestQueue mQueue;
private RequestQueue mAcquireAccessTokenRequestQueue;
VoteThing(Context context, RequestQueue queue, RequestQueue acquireAccessTokenRequestQueue) {
mContext = context;
mQueue = queue;
mAcquireAccessTokenRequestQueue = acquireAccessTokenRequestQueue;
}
void votePost(VoteThingListener voteThingListener, final String fullName, final String point, final int position, final int refreshTime) {
if(mContext != null) {
if(refreshTime < 0) {
mVoteThingListener.onVoteThingFail(position);
return;
}
mVoteThingListener = voteThingListener;
StringRequest voteRequest = new StringRequest(Request.Method.POST, RedditUtils.OAUTH_API_BASE_URI + RedditUtils.VOTE_SUFFIX, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
mVoteThingListener.onVoteThingSuccess(position);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
if (error instanceof AuthFailureError) {
//Access token expired
new AcquireAccessToken(mContext).refreshAccessToken(mAcquireAccessTokenRequestQueue,
new AcquireAccessToken.AcquireAccessTokenListener() {
@Override
public void onAcquireAccessTokenSuccess() {
votePost(mVoteThingListener, fullName, point, position, refreshTime - 1);
}
@Override
public void onAcquireAccessTokenFail() {}
});
} else {
mVoteThingListener.onVoteThingFail(position);
}
}
}) {
@Override
protected Map<String, String> getParams() {
HashMap<String, String> params = new HashMap<>();
params.put(RedditUtils.DIR_KEY, point);
params.put(RedditUtils.ID_KEY, fullName);
params.put(RedditUtils.RANK_KEY, RedditUtils.RANK);
return params;
}
@Override
public Map<String, String> getHeaders() {
String accessToken = mContext.getSharedPreferences(SharedPreferencesUtils.AUTH_CODE_FILE_KEY, Context.MODE_PRIVATE).getString(SharedPreferencesUtils.ACCESS_TOKEN_KEY, "");
return RedditUtils.getOAuthHeader(accessToken);
}
};
voteRequest.setTag(VoteThing.class);
mQueue.add(voteRequest);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 B

View File

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0" />
<path
android:fillColor="#FF000000"
android:pathData="M9,2L7.17,4H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2H9zm3,15c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M22,16V4c0,-1.1 -0.9,-2 -2,-2H8c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2zm-11,-4l2.03,2.71L16,11l4,5H8l3,-4zM2,6v14c0,1.1 0.9,2 2,2h14v-2H4V6H2z" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M22.7,19l-9.1,-9.1c0.9,-2.3 0.4,-5 -1.5,-6.9 -2,-2 -5,-2.4 -7.4,-1.3L9,6 6,9 1.6,4.7C0.4,7.1 0.9,10.1 2.9,12.1c1.9,1.9 4.6,2.4 6.9,1.5l9.1,9.1c0.4,0.4 1,0.4 1.4,0l2.3,-2.3c0.5,-0.4 0.5,-1.1 0.1,-1.4z" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M4,6H2v14c0,1.1 0.9,2 2,2h14v-2H4V6zm16,-4H8c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-8,12.5v-9l6,4.5 -6,4.5z" />
</vector>

View File

@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeColor="#00000000"
android:strokeWidth="1">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 476 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 483 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 832 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 888 B

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
</vector>

View File

@ -0,0 +1,4 @@
<vector android:height="12dp" android:viewportHeight="24.0"
android:viewportWidth="24.0" android:width="12dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#010101" android:pathData="M20,12l-1.41,-1.41L13,16.17V4h-2v12.17l-5.58,-5.59L4,12l8,8 8,-8z"/>
</vector>

View File

@ -0,0 +1,4 @@
<vector android:height="12dp" android:viewportHeight="24.0"
android:viewportWidth="24.0" android:width="12dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M4,12l1.41,1.41L11,7.83V20h2V7.83l5.58,5.59L20,12l-8,-8 -8,8z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M21.99,4c0,-1.1 -0.89,-2 -1.99,-2L4,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h14l4,4 -0.01,-18zM18,14L6,14v-2h12v2zM18,11L6,11L6,9h12v2zM18,8L6,8L6,6h12v2z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z"/>
</vector>

View File

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillColor="#26A69A"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
</vector>

View File

@ -0,0 +1,4 @@
<vector android:height="12dp" android:viewportHeight="24.0"
android:viewportWidth="24.0" android:width="12dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M10,9V5l-7,7 7,7v-4.1c5,0 8.5,1.6 11,5.1 -1,-5 -4,-10 -11,-11z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/>
</vector>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<!--<stroke
android:width="1dp"
android:color="@color/colorPrimary" />-->
<solid android:color="@color/colorAccent" />
<padding
android:left="4dp"
android:right="4dp"
android:top="4dp"
android:bottom="4dp"/>
<corners android:radius="4dp" />
</shape>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<!--<stroke
android:width="1dp"
android:color="@color/colorPrimary" />-->
<solid android:color="@color/colorPrimary" />
<padding
android:left="4dp"
android:right="4dp"
android:top="4dp"
android:bottom="4dp"/>
<corners android:radius="4dp" />
</shape>

View File

@ -0,0 +1,9 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:angle="135"
android:centerColor="#1565C0"
android:endColor="#0D47A1"
android:startColor="#1976D2"
android:type="linear" />
</shape>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="ml.docilealligator.infinityforreddit.LoginActivity">
<WebView
android:id="@+id/webview_login_activity"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.constraint.ConstraintLayout>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<include
layout="@layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="@layout/nav_header_main"
app:menu="@menu/activity_main_drawer" />
</android.support.v4.widget.DrawerLayout>

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/parent_relative_layout_view_image_activity"
android:background="@android:color/black"
tools:context="ml.docilealligator.infinityforreddit.ViewImageActivity">
<ProgressBar
android:id="@+id/progress_bar_view_image_activity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
<com.alexvasilkov.gestures.views.GestureImageView
android:id="@+id/image_view_view_image_activity"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:gest_fillViewport="true" />
</RelativeLayout>

View File

@ -0,0 +1,257 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordinator_layout_view_post_detail"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ViewPostDetailActivity">
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:descendantFocusability="blocksDescendants">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/relative_view_view_post_detail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="16dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/subreddit_icon_circle_image_view_view_post_detail"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignParentStart="true"
android:layout_centerVertical="true" />
<TextView
android:id="@+id/subreddit_text_view_view_post_detail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="16dp"
android:layout_toEndOf="@id/subreddit_icon_circle_image_view_view_post_detail"
android:layout_toStartOf="@id/post_time_text_view_view_post_detail"
android:textColor="#E91E63" />
<TextView
android:id="@+id/post_time_text_view_view_post_detail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true" />
</RelativeLayout>
<TextView
android:id="@+id/title_text_view_view_post_detail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginStart="16dp"
android:textColor="#000000"
android:textSize="18sp" />
<TextView
android:id="@+id/content_text_view_view_post_detail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="16dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:textSize="16sp"
android:visibility="gone" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/type_text_view_view_post_detail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:background="@drawable/rounded_corner"
android:textColor="@android:color/white" />
<TextView
android:id="@+id/nsfw_text_view_view_post_detail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_marginEnd="16dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:background="@drawable/nsfw_rounded_corner"
android:textColor="@android:color/white"
android:visibility="gone" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/image_view_wrapper_view_post_detail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:visibility="gone">
<ProgressBar
android:id="@+id/progress_bar_view_post_detail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
<ImageView
android:id="@+id/image_view_view_post_detail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="fitStart"/>
</RelativeLayout>
<ImageView
android:id="@+id/image_view_no_preview_link_view_post_detail"
android:layout_width="match_parent"
android:layout_height="150dp"
android:layout_marginTop="16dp"
android:background="#E0E0E0"
android:scaleType="center"
android:src="@drawable/ic_link"
android:visibility="gone" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/plus_button_view_post_detail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:background="?actionBarItemBackground"
android:clickable="true"
android:focusable="true"
android:padding="16dp"
android:src="@drawable/ic_arrow_upward_black_24dp"
android:tint="@android:color/tab_indicator_text" />
<TextView
android:id="@+id/score_text_view_view_post_detail"
android:layout_width="64dp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@id/plus_button_view_post_detail"
android:gravity="center" />
<ImageView
android:id="@+id/minus_button_view_post_detail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@id/score_text_view_view_post_detail"
android:background="?actionBarItemBackground"
android:clickable="true"
android:focusable="true"
android:padding="16dp"
android:src="@drawable/ic_arrow_downward_black_24dp"
android:tint="@android:color/tab_indicator_text" />
<ImageView
android:id="@+id/share_button_view_post_detail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:background="?actionBarItemBackground"
android:clickable="true"
android:focusable="true"
android:padding="16dp"
android:src="@drawable/ic_share_black_24dp"
android:tint="@android:color/tab_indicator_text" />
</RelativeLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
<ProgressBar
android:id="@+id/comment_progress_bar_view_post_detail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="16dp"
android:visibility="gone"/>
<android.support.v7.widget.CardView
android:id="@+id/comment_card_view_view_post_detail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingEnd="16dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingStart="16dp"
android:paddingTop="16dp"
android:text="@string/comments"
android:textColor="#000000"
android:textSize="18sp" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view_view_post_detail"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
android:id="@+id/relative_layout_view_video_activity"
android:keepScreenOn="true"
tools:context="ml.docilealligator.infinityforreddit.ViewVideoActivity">
<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/player_view_view_video_activity"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:controller_layout_id="@layout/exo_playback_control_view"/>
</RelativeLayout>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="ml.docilealligator.infinityforreddit.MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include layout="@layout/content_main" />
</android.support.design.widget.CoordinatorLayout>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/frame_layout_content_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="ml.docilealligator.infinityforreddit.MainActivity"
tools:showIn="@layout/app_bar_main" />

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/linear_layout_exo_playback_control_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="@color/transparentActionBarColor"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingTop="4dp"
android:orientation="horizontal">
<ImageButton android:id="@id/exo_rew"
style="@style/ExoMediaButton.Rewind"/>
<ImageButton android:id="@id/exo_play"
style="@style/ExoMediaButton.Play"/>
<ImageButton android:id="@id/exo_pause"
style="@style/ExoMediaButton.Pause"/>
<ImageButton android:id="@id/exo_ffwd"
style="@style/ExoMediaButton.FastForward"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView android:id="@id/exo_position"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textStyle="bold"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:includeFontPadding="false"
android:textColor="#FFBEBEBE"/>
<com.google.android.exoplayer2.ui.DefaultTimeBar
android:id="@id/exo_progress"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="26dp"/>
<TextView android:id="@id/exo_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textStyle="bold"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:includeFontPadding="false"
android:textColor="#FFBEBEBE"/>
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,28 @@
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/coordinator_layout_best_post_fragment"
tools:context="ml.docilealligator.infinityforreddit.BestPostFragment">
<ProgressBar
android:id="@+id/progress_bar_best_post_fragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view_best_post_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab_best_post_fragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
app:srcCompat="@drawable/baseline_add_white_24" />
</android.support.design.widget.CoordinatorLayout>

View File

@ -0,0 +1,179 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:id="@+id/card_view_view_post_detail">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/relative_view_item_best_post"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="16dp"
android:layout_marginStart="16dp"
android:layout_marginRight="16dp"
android:layout_marginEnd="16dp"
android:layout_marginTop="16dp">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/subreddit_icon_circle_image_view_best_post_item"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"/>
<TextView
android:id="@+id/subreddit_text_view_best_post_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_toEndOf="@id/subreddit_icon_circle_image_view_best_post_item"
android:layout_centerVertical="true"
android:textColor="#E91E63"/>
<TextView
android:id="@+id/post_time_text_view_best_post_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"/>
</RelativeLayout>
<TextView
android:id="@+id/title_text_view_best_post_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginStart="16dp"
android:layout_marginRight="16dp"
android:layout_marginEnd="16dp"
android:textSize="18sp"
android:textColor="#000000"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/type_text_view_item_best_post"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/rounded_corner"
android:layout_marginTop="8dp"
android:layout_marginLeft="16dp"
android:layout_marginStart="16dp"
android:layout_marginRight="16dp"
android:layout_marginEnd="16dp"
android:textColor="@android:color/white"/>
<TextView
android:id="@+id/nsfw_text_view_item_best_post"
android:text="@string/nsfw"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/nsfw_rounded_corner"
android:layout_alignParentEnd="true"
android:layout_marginTop="8dp"
android:layout_marginLeft="16dp"
android:layout_marginStart="16dp"
android:layout_marginRight="16dp"
android:layout_marginEnd="16dp"
android:textColor="@android:color/white"
android:visibility="gone"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/image_view_wrapper_item_best_post"
android:layout_width="match_parent"
android:layout_height="350dp"
android:layout_marginTop="16dp"
android:visibility="gone">
<ProgressBar
android:id="@+id/progress_bar_best_post_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
<ImageView
android:id="@+id/image_view_best_post_item"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop" />
</RelativeLayout>
<ImageView
android:id="@+id/image_view_no_preview_link_best_post_item"
android:layout_width="match_parent"
android:layout_height="150dp"
android:layout_marginTop="16dp"
android:scaleType="center"
android:src="@drawable/ic_link"
android:background="#E0E0E0"
android:visibility="gone"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/plus_button_item_best_post"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:padding="16dp"
android:src="@drawable/ic_arrow_upward_black_24dp"
android:layout_centerVertical="true"
android:tint="@android:color/tab_indicator_text"
android:background="?actionBarItemBackground"
android:clickable="true"
android:focusable="true"/>
<TextView
android:id="@+id/score_text_view_item_best_post"
android:layout_width="64dp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@id/plus_button_item_best_post"
android:gravity="center"/>
<ImageView
android:id="@+id/minus_button_item_best_post"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@id/score_text_view_item_best_post"
android:padding="16dp"
android:src="@drawable/ic_arrow_downward_black_24dp"
android:tint="@android:color/tab_indicator_text"
android:background="?actionBarItemBackground"
android:clickable="true"
android:focusable="true"/>
<ImageView
android:id="@+id/share_button_item_best_post"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:padding="16dp"
android:src="@drawable/ic_share_black_24dp"
android:tint="@android:color/tab_indicator_text"
android:background="?actionBarItemBackground"
android:clickable="true"
android:focusable="true" />
</RelativeLayout>
</LinearLayout>
</android.support.v7.widget.CardView>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/linear_layout_footer_progress_bar_item"
android:orientation="vertical">
<ProgressBar
android:id="@+id/progress_bar_footer_progress_bar_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_gravity="center" />
<RelativeLayout
android:id="@+id/relative_layout_footer_progress_bar_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginLeft="16dp"
android:layout_marginStart="16dp"
android:layout_marginRight="16dp"
android:layout_marginEnd="16dp"
android:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toStartOf="@id/retry_button_footer_progress_bar_item"
android:layout_alignParentStart="true"
android:text="@string/load_data_failed"
android:textSize="18sp"
android:layout_centerVertical="true" />
<Button
android:id="@+id/retry_button_footer_progress_bar_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:text="@string/retry" />
</RelativeLayout>
</LinearLayout>

View File

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/linear_layout_item_post_comment"
android:orientation="vertical"
android:layout_marginTop="12dp"
android:layout_marginBottom="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/author_text_view_item_post_comment"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginLeft="16dp"
android:layout_marginStart="16dp"
android:layout_marginRight="16dp"
android:layout_marginEnd="16dp"
android:textColor="@color/colorPrimary"/>
<TextView
android:id="@+id/comment_time_text_view_item_post_comment"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="end"
android:layout_marginEnd="16dp" />
</LinearLayout>
<TextView
android:id="@+id/comment_text_view_item_post_comment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:layout_marginLeft="32dp"
android:layout_marginStart="32dp"
android:layout_marginRight="32dp"
android:layout_marginEnd="32dp"
android:textColor="#000000"/>
<RelativeLayout
android:id="@+id/relative_layout_item_post_comment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp">
<ImageView
android:id="@+id/plus_button_item_post_comment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:padding="8dp"
android:src="@drawable/ic_arrow_upward_black_12dp"
android:layout_centerVertical="true"
android:tint="@android:color/tab_indicator_text"
android:background="?actionBarItemBackground"
android:clickable="true"
android:focusable="true"/>
<TextView
android:id="@+id/score_text_view_item_post_comment"
android:layout_width="64dp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@id/plus_button_item_post_comment"
android:gravity="center"/>
<ImageView
android:id="@+id/minus_button_item_post_comment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@id/score_text_view_item_post_comment"
android:padding="8dp"
android:src="@drawable/ic_arrow_downward_black_12dp"
android:tint="@android:color/tab_indicator_text"
android:background="?actionBarItemBackground"
android:clickable="true"
android:focusable="true"/>
<ImageView
android:id="@+id/reply_button_item_post_comment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:padding="8dp"
android:layout_marginEnd="16dp"
android:layout_alignParentEnd="true"
android:src="@drawable/ic_reply_black_12dp"
android:tint="@android:color/tab_indicator_text"
android:background="?actionBarItemBackground"
android:clickable="true"
android:focusable="true"/>
</RelativeLayout>
</LinearLayout>

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="@dimen/nav_header_height"
android:background="@drawable/side_nav_bar"
android:gravity="bottom"
android:orientation="vertical"
android:theme="@style/ThemeOverlay.AppCompat.Dark">
<ImageView
android:id="@+id/banner_image_view_nav_header_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"/>
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/profile_image_view_nav_header_main"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginTop="40dp"
android:layout_marginBottom="16dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"/>
<TextView
android:id="@+id/name_text_view_nav_header_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/nav_header_vertical_spacing"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:layout_below="@id/profile_image_view_nav_header_main"/>
<TextView
android:id="@+id/karma_text_view_nav_header_main"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_below="@id/name_text_view_nav_header_main"/>
</RelativeLayout>

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:showIn="navigation_view">
<group android:checkableBehavior="single">
<item
android:id="@+id/nav_camera"
android:icon="@drawable/ic_menu_camera"
android:title="Import" />
<item
android:id="@+id/nav_gallery"
android:icon="@drawable/ic_menu_gallery"
android:title="Gallery" />
<item
android:id="@+id/nav_slideshow"
android:icon="@drawable/ic_menu_slideshow"
android:title="Slideshow" />
<item
android:id="@+id/nav_manage"
android:icon="@drawable/ic_menu_manage"
android:title="Tools" />
</group>
<item android:title="Communicate">
<menu>
<item
android:id="@+id/nav_share"
android:icon="@drawable/ic_menu_share"
android:title="Share" />
<item
android:id="@+id/nav_send"
android:icon="@drawable/ic_menu_send"
android:title="Send" />
</menu>
</item>
</menu>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:title="@string/action_settings"
app:showAsAction="never" />
</menu>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_download_view_image"
android:orderInCategory="1"
android:title="@string/action_download"
android:icon="@drawable/ic_file_download_white_24dp"
app:showAsAction="always" />
</menu>

Some files were not shown because too many files have changed in this diff Show More