Start adding RPAN support.

This commit is contained in:
Alex Ning 2021-07-05 22:38:28 +08:00
parent 09f05f3839
commit bd0068eb6c
13 changed files with 835 additions and 11 deletions

View File

@ -32,7 +32,12 @@
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true"
tools:replace="android:label">
<activity android:name=".activities.MotionTestActivity"
<activity android:name=".activities.RPANActivity"
android:label="@string/rpan_activity_label"
android:parentActivityName=".activities.MainActivity"
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".activities.MotionTestActivity"
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".activities.LockScreenActivity"

View File

@ -31,6 +31,7 @@ import ml.docilealligator.infinityforreddit.activities.PostImageActivity;
import ml.docilealligator.infinityforreddit.activities.PostLinkActivity;
import ml.docilealligator.infinityforreddit.activities.PostTextActivity;
import ml.docilealligator.infinityforreddit.activities.PostVideoActivity;
import ml.docilealligator.infinityforreddit.activities.RPANActivity;
import ml.docilealligator.infinityforreddit.activities.ReportActivity;
import ml.docilealligator.infinityforreddit.activities.RulesActivity;
import ml.docilealligator.infinityforreddit.activities.SearchActivity;
@ -69,6 +70,7 @@ import ml.docilealligator.infinityforreddit.fragments.UserListingFragment;
import ml.docilealligator.infinityforreddit.fragments.ViewImgurImageFragment;
import ml.docilealligator.infinityforreddit.fragments.ViewImgurVideoFragment;
import ml.docilealligator.infinityforreddit.fragments.ViewPostDetailFragment;
import ml.docilealligator.infinityforreddit.fragments.ViewRPANBroadcastFragment;
import ml.docilealligator.infinityforreddit.fragments.ViewRedditGalleryImageOrGifFragment;
import ml.docilealligator.infinityforreddit.fragments.ViewRedditGalleryVideoFragment;
import ml.docilealligator.infinityforreddit.services.DownloadMediaService;
@ -269,4 +271,8 @@ public interface AppComponent {
void inject(LockScreenActivity lockScreenActivity);
void inject(MaterialYouService materialYouService);
void inject(RPANActivity rpanActivity);
void inject(ViewRPANBroadcastFragment viewRPANBroadcastFragment);
}

View File

@ -152,6 +152,17 @@ class AppModule {
.build();
}
@Provides
@Named("strapi")
@Singleton
Retrofit providestrapiRetrofit(OkHttpClient okHttpClient) {
return new Retrofit.Builder()
.baseUrl(APIUtils.STRAPI_BASE_URI)
.client(okHttpClient)
.addConverterFactory(ScalarsConverterFactory.create())
.build();
}
@Provides
@Singleton
OkHttpClient provideOkHttpClient(@Named("no_oauth") Retrofit retrofit, RedditDataRoomDatabase accountRoomDatabase,

View File

@ -0,0 +1,226 @@
package ml.docilealligator.infinityforreddit;
import android.os.Parcel;
import android.os.Parcelable;
public class RPANBroadcast implements Parcelable {
public int upvotes;
public int downvotes;
public int uniqueWatchers;
public int continuousWatchers;
public int totalContinuousWatchers;
public boolean chatDisabled;
public double broadcastTime;
public double estimatedRemainingTime;
public RPANPost rpanPost;
public RPANStream rpanStream;
public RPANBroadcast(int upvotes, int downvotes, int uniqueWatchers, int continuousWatchers,
int totalContinuousWatchers, boolean chatDisabled, double broadcastTime,
double estimatedRemainingTime, RPANPost rpanPost, RPANStream rpanStream) {
this.upvotes = upvotes;
this.downvotes = downvotes;
this.uniqueWatchers = uniqueWatchers;
this.continuousWatchers = continuousWatchers;
this.totalContinuousWatchers = totalContinuousWatchers;
this.chatDisabled = chatDisabled;
this.broadcastTime = broadcastTime;
this.estimatedRemainingTime = estimatedRemainingTime;
this.rpanPost = rpanPost;
this.rpanStream = rpanStream;
}
protected RPANBroadcast(Parcel in) {
upvotes = in.readInt();
downvotes = in.readInt();
uniqueWatchers = in.readInt();
continuousWatchers = in.readInt();
totalContinuousWatchers = in.readInt();
chatDisabled = in.readByte() != 0;
broadcastTime = in.readDouble();
estimatedRemainingTime = in.readDouble();
rpanPost = in.readParcelable(RPANPost.class.getClassLoader());
rpanStream = in.readParcelable(RPANStream.class.getClassLoader());
}
public static final Creator<RPANBroadcast> CREATOR = new Creator<RPANBroadcast>() {
@Override
public RPANBroadcast createFromParcel(Parcel in) {
return new RPANBroadcast(in);
}
@Override
public RPANBroadcast[] newArray(int size) {
return new RPANBroadcast[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(upvotes);
parcel.writeInt(downvotes);
parcel.writeInt(uniqueWatchers);
parcel.writeInt(continuousWatchers);
parcel.writeInt(totalContinuousWatchers);
parcel.writeByte((byte) (chatDisabled ? 1 : 0));
parcel.writeDouble(broadcastTime);
parcel.writeDouble(estimatedRemainingTime);
parcel.writeParcelable(rpanPost, i);
parcel.writeParcelable(rpanStream, i);
}
public static class RPANPost implements Parcelable{
public String title;
public String subredditName;
public String subredditIconUrl;
public String username;
public int postScore;
public String voteState;
public double upvoteRatio;
public String postPermalink;
public boolean isNsfw;
public boolean isLocked;
public boolean isArchived;
public boolean isSpoiler;
public String suggestedCommentSort;
public String liveCommentsWebsocketUrl;
public RPANPost(String title, String subredditName, String subredditIconUrl, String username,
int postScore, String voteState, double upvoteRatio, String postPermalink, String rpanUrl,
boolean isNsfw, boolean isLocked, boolean isArchived, boolean isSpoiler,
String suggestedCommentSort, String liveCommentsWebsocketUrl) {
this.title = title;
this.subredditName = subredditName;
this.subredditIconUrl = subredditIconUrl;
this.username = username;
this.postScore = postScore;
this.voteState = voteState;
this.upvoteRatio = upvoteRatio;
this.postPermalink = postPermalink;
this.isNsfw = isNsfw;
this.isLocked = isLocked;
this.isArchived = isArchived;
this.isSpoiler = isSpoiler;
this.suggestedCommentSort = suggestedCommentSort;
this.liveCommentsWebsocketUrl = liveCommentsWebsocketUrl;
}
protected RPANPost(Parcel in) {
title = in.readString();
subredditName = in.readString();
subredditIconUrl = in.readString();
username = in.readString();
postScore = in.readInt();
voteState = in.readString();
upvoteRatio = in.readDouble();
postPermalink = in.readString();
isNsfw = in.readByte() != 0;
isLocked = in.readByte() != 0;
isArchived = in.readByte() != 0;
isSpoiler = in.readByte() != 0;
suggestedCommentSort = in.readString();
liveCommentsWebsocketUrl = in.readString();
}
public static final Creator<RPANPost> CREATOR = new Creator<RPANPost>() {
@Override
public RPANPost createFromParcel(Parcel in) {
return new RPANPost(in);
}
@Override
public RPANPost[] newArray(int size) {
return new RPANPost[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(title);
parcel.writeString(subredditName);
parcel.writeString(subredditIconUrl);
parcel.writeString(username);
parcel.writeInt(postScore);
parcel.writeString(voteState);
parcel.writeDouble(upvoteRatio);
parcel.writeString(postPermalink);
parcel.writeByte((byte) (isNsfw ? 1 : 0));
parcel.writeByte((byte) (isLocked ? 1 : 0));
parcel.writeByte((byte) (isArchived ? 1 : 0));
parcel.writeByte((byte) (isSpoiler ? 1 : 0));
parcel.writeString(suggestedCommentSort);
parcel.writeString(liveCommentsWebsocketUrl);
}
}
public static class RPANStream implements Parcelable {
public String streamId;
public String hlsUrl;
public String thumbnail;
public int width;
public int height;
public long publishAt;
public String state;
public RPANStream(String streamId, String hlsUrl, String thumbnail, int width, int height, long publishAt,
String state) {
this.streamId = streamId;
this.hlsUrl = hlsUrl;
this.thumbnail = thumbnail;
this.width = width;
this.height = height;
this.publishAt = publishAt;
this.state = state;
}
protected RPANStream(Parcel in) {
streamId = in.readString();
hlsUrl = in.readString();
thumbnail = in.readString();
width = in.readInt();
height = in.readInt();
publishAt = in.readLong();
state = in.readString();
}
public static final Creator<RPANStream> CREATOR = new Creator<RPANStream>() {
@Override
public RPANStream createFromParcel(Parcel in) {
return new RPANStream(in);
}
@Override
public RPANStream[] newArray(int size) {
return new RPANStream[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(streamId);
parcel.writeString(hlsUrl);
parcel.writeString(thumbnail);
parcel.writeInt(width);
parcel.writeInt(height);
parcel.writeLong(publishAt);
parcel.writeString(state);
}
}
}

View File

@ -9,8 +9,6 @@ import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.Menu;
@ -83,8 +81,6 @@ import ml.docilealligator.infinityforreddit.SortType;
import ml.docilealligator.infinityforreddit.SortTypeSelectionCallback;
import ml.docilealligator.infinityforreddit.account.AccountViewModel;
import ml.docilealligator.infinityforreddit.adapters.NavigationDrawerRecyclerViewAdapter;
import ml.docilealligator.infinityforreddit.adapters.SubredditAutocompleteRecyclerViewAdapter;
import ml.docilealligator.infinityforreddit.apis.RedditAPI;
import ml.docilealligator.infinityforreddit.asynctasks.InsertSubscribedThings;
import ml.docilealligator.infinityforreddit.asynctasks.SwitchAccount;
import ml.docilealligator.infinityforreddit.asynctasks.SwitchToAnonymousMode;
@ -110,20 +106,16 @@ import ml.docilealligator.infinityforreddit.multireddit.MultiRedditViewModel;
import ml.docilealligator.infinityforreddit.post.Post;
import ml.docilealligator.infinityforreddit.post.PostDataSource;
import ml.docilealligator.infinityforreddit.readpost.InsertReadPost;
import ml.docilealligator.infinityforreddit.subreddit.ParseSubredditData;
import ml.docilealligator.infinityforreddit.subreddit.SubredditData;
import ml.docilealligator.infinityforreddit.subscribedsubreddit.SubscribedSubredditData;
import ml.docilealligator.infinityforreddit.subscribedsubreddit.SubscribedSubredditViewModel;
import ml.docilealligator.infinityforreddit.subscribeduser.SubscribedUserData;
import ml.docilealligator.infinityforreddit.user.FetchUserData;
import ml.docilealligator.infinityforreddit.user.UserData;
import ml.docilealligator.infinityforreddit.utils.APIUtils;
import ml.docilealligator.infinityforreddit.utils.CustomThemeSharedPreferencesUtils;
import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils;
import ml.docilealligator.infinityforreddit.utils.Utils;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import static androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO;
@ -1381,7 +1373,7 @@ public class MainActivity extends BaseActivity implements SortTypeSelectionCallb
}
private void goToSubreddit() {
View rootView = getLayoutInflater().inflate(R.layout.dialog_go_to_thing_edit_text, coordinatorLayout, false);
/*View rootView = getLayoutInflater().inflate(R.layout.dialog_go_to_thing_edit_text, coordinatorLayout, false);
TextInputEditText thingEditText = rootView.findViewById(R.id.text_input_edit_text_go_to_thing_edit_text);
RecyclerView recyclerView = rootView.findViewById(R.id.recycler_view_go_to_thing_edit_text);
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
@ -1475,7 +1467,9 @@ public class MainActivity extends BaseActivity implements SortTypeSelectionCallb
imm.hideSoftInputFromWindow(thingEditText.getWindowToken(), 0);
}
})
.show();
.show();*/
Intent intent = new Intent(this, RPANActivity.class);
startActivity(intent);
}
private void goToUser() {

View File

@ -0,0 +1,258 @@
package ml.docilealligator.infinityforreddit.activities;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;
import com.google.android.material.appbar.AppBarLayout;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.concurrent.Executor;
import javax.inject.Inject;
import javax.inject.Named;
import butterknife.BindView;
import butterknife.ButterKnife;
import ml.docilealligator.infinityforreddit.Infinity;
import ml.docilealligator.infinityforreddit.R;
import ml.docilealligator.infinityforreddit.RPANBroadcast;
import ml.docilealligator.infinityforreddit.apis.Strapi;
import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper;
import ml.docilealligator.infinityforreddit.fragments.ViewRPANBroadcastFragment;
import ml.docilealligator.infinityforreddit.utils.APIUtils;
import ml.docilealligator.infinityforreddit.utils.JSONUtils;
import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
public class RPANActivity extends BaseActivity {
@BindView(R.id.coordinator_layout_rpan_activity)
CoordinatorLayout coordinatorLayout;
@BindView(R.id.appbar_layout_rpan_activity)
AppBarLayout appBarLayout;
@BindView(R.id.toolbar_rpan_activity)
Toolbar toolbar;
@BindView(R.id.view_pager_2_rpan_activity)
ViewPager2 viewPager2;
@Inject
@Named("strapi")
Retrofit strapiRetrofit;
@Inject
@Named("default")
SharedPreferences mSharedPreferences;
@Inject
@Named("current_account")
SharedPreferences mCurrentAccountSharedPreferences;
@Inject
CustomThemeWrapper mCustomThemeWrapper;
@Inject
Executor mExecutor;
private String mAccessToken;
private String mAccountName;
private ArrayList<RPANBroadcast> rpanBroadcasts;
private String nextCursor;
private SectionsPagerAdapter sectionsPagerAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
((Infinity) getApplication()).getAppComponent().inject(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_rpanactivity);
ButterKnife.bind(this);
applyCustomTheme();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Window window = getWindow();
if (isChangeStatusBarIconColor()) {
addOnOffsetChangedListener(appBarLayout);
}
if (isImmersiveInterface()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
coordinatorLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
} else {
window.setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
}
adjustToolbar(toolbar);
}
}
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mAccessToken = mCurrentAccountSharedPreferences.getString(SharedPreferencesUtils.ACCESS_TOKEN, null);
mAccountName = mCurrentAccountSharedPreferences.getString(SharedPreferencesUtils.ACCOUNT_NAME, null);
loadRPANVideos();
}
private void loadRPANVideos() {
strapiRetrofit.create(Strapi.class).getAllBroadcasts(APIUtils.getOAuthHeader(mAccessToken)).enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull Call<String> call, @NonNull Response<String> response) {
if (response.isSuccessful()) {
parseRPANBroadcasts(response.body());
} else {
Toast.makeText(RPANActivity.this,
R.string.load_rpan_broadcasts_failed, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) {
Toast.makeText(RPANActivity.this,
R.string.load_rpan_broadcasts_failed, Toast.LENGTH_SHORT).show();
}
});
}
private void parseRPANBroadcasts(String response) {
Handler handler = new Handler();
mExecutor.execute(() -> {
try {
ArrayList<RPANBroadcast> rpanBroadcasts = new ArrayList<>();
JSONObject responseObject = new JSONObject(response);
String nextCursor = responseObject.getString(JSONUtils.NEXT_CURSOR_KEY);
JSONArray dataArray = responseObject.getJSONArray(JSONUtils.DATA_KEY);
for (int i = 0; i < dataArray.length(); i++) {
JSONObject singleData = dataArray.getJSONObject(i);
JSONObject rpanPostObject = singleData.getJSONObject(JSONUtils.POST_KEY);
RPANBroadcast.RPANPost rpanPost = new RPANBroadcast.RPANPost(
rpanPostObject.getString(JSONUtils.TITLE_KEY),
rpanPostObject.getJSONObject(JSONUtils.SUBREDDIT_KEY).getString(JSONUtils.NAME_KEY),
rpanPostObject.getJSONObject(JSONUtils.SUBREDDIT_KEY).getJSONObject(JSONUtils.STYLES_KEY).getString(JSONUtils.ICON_KEY),
rpanPostObject.getJSONObject(JSONUtils.AUTHOR_INFO_KEY).getString(JSONUtils.NAME_KEY),
rpanPostObject.getInt(JSONUtils.SCORE_KEY),
rpanPostObject.getString(JSONUtils.VOTE_STATE_KEY),
rpanPostObject.getDouble(JSONUtils.UPVOTE_RATIO_CAMEL_CASE_KEY),
rpanPostObject.getString(JSONUtils.PERMALINK_KEY),
rpanPostObject.getJSONObject(JSONUtils.OUTBOUND_LINK_KEY).getString(JSONUtils.URL_KEY),
rpanPostObject.getBoolean(JSONUtils.IS_NSFW_KEY),
rpanPostObject.getBoolean(JSONUtils.IS_LOCKED_KEY),
rpanPostObject.getBoolean(JSONUtils.IS_ARCHIVED_KEY),
rpanPostObject.getBoolean(JSONUtils.IS_SPOILER),
rpanPostObject.getString(JSONUtils.SUGGESTED_COMMENT_SORT_CAMEL_CASE_KEY),
rpanPostObject.getString(JSONUtils.LIVE_COMMENTS_WEBSOCKET_KEY)
);
JSONObject rpanStreamObject = singleData.getJSONObject(JSONUtils.STREAM_KEY);
RPANBroadcast.RPANStream rpanStream = new RPANBroadcast.RPANStream(
rpanStreamObject.getString(JSONUtils.STREAM_ID_KEY),
rpanStreamObject.getString(JSONUtils.HLS_URL_KEY),
rpanStreamObject.getString(JSONUtils.THUMBNAIL_KEY),
rpanStreamObject.getInt(JSONUtils.WIDTH_KEY),
rpanStreamObject.getInt(JSONUtils.HEIGHT_KEY),
rpanStreamObject.getLong(JSONUtils.PUBLISH_AT_KEY),
rpanStreamObject.getString(JSONUtils.STATE_KEY)
);
rpanBroadcasts.add(new RPANBroadcast(
singleData.getInt(JSONUtils.UPVOTES_KEY),
singleData.getInt(JSONUtils.DOWNVOTES_KEY),
singleData.getInt(JSONUtils.UNIQUE_WATCHERS_KEY),
singleData.getInt(JSONUtils.CONTINUOUS_WATCHERS_KEY),
singleData.getInt(JSONUtils.TOTAL_CONTINUOUS_WATCHERS_KEY),
singleData.getBoolean(JSONUtils.CHAT_DISABLED_KEY),
singleData.getDouble(JSONUtils.BROADCAST_TIME_KEY),
singleData.getDouble(JSONUtils.ESTIMATED_REMAINING_TIME_KEY),
rpanPost,
rpanStream
));
}
handler.post(() -> {
RPANActivity.this.rpanBroadcasts = rpanBroadcasts;
RPANActivity.this.nextCursor = nextCursor;
initializeViewPager();
});
} catch (JSONException e) {
e.printStackTrace();
handler.post(() -> Toast.makeText(RPANActivity.this,
R.string.parse_rpan_broadcasts_failed, Toast.LENGTH_SHORT).show());
}
});
}
private void initializeViewPager() {
sectionsPagerAdapter = new SectionsPagerAdapter(this);
viewPager2.setAdapter(sectionsPagerAdapter);
viewPager2.setOffscreenPageLimit(3);
viewPager2.setUserInputEnabled(!mSharedPreferences.getBoolean(SharedPreferencesUtils.DISABLE_SWIPING_BETWEEN_TABS, false));
fixViewPager2Sensitivity(viewPager2);
}
@Override
protected SharedPreferences getDefaultSharedPreferences() {
return mSharedPreferences;
}
@Override
protected CustomThemeWrapper getCustomThemeWrapper() {
return mCustomThemeWrapper;
}
@Override
protected void applyCustomTheme() {
}
private class SectionsPagerAdapter extends FragmentStateAdapter {
public SectionsPagerAdapter(FragmentActivity fa) {
super(fa);
}
@NonNull
@Override
public Fragment createFragment(int position) {
ViewRPANBroadcastFragment fragment = new ViewRPANBroadcastFragment();
Bundle bundle = new Bundle();
bundle.putParcelable(ViewRPANBroadcastFragment.EXTRA_RPAN_BROADCAST, rpanBroadcasts.get(position));
fragment.setArguments(bundle);
return fragment;
}
@Nullable
private Fragment getCurrentFragment() {
if (viewPager2 == null || getSupportFragmentManager() == null) {
return null;
}
return getSupportFragmentManager().findFragmentByTag("f" + viewPager2.getCurrentItem());
}
@Override
public int getItemCount() {
return rpanBroadcasts == null ? 0 : rpanBroadcasts.size();
}
}
}

View File

@ -0,0 +1,12 @@
package ml.docilealligator.infinityforreddit.apis;
import java.util.Map;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.HeaderMap;
public interface Strapi {
@GET("/broadcasts")
Call<String> getAllBroadcasts(@HeaderMap Map<String ,String> headers);
}

View File

@ -0,0 +1,225 @@
package ml.docilealligator.infinityforreddit.fragments;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.BehindLiveWindowException;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.hls.HlsMediaSource;
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.TrackSelectionArray;
import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.ui.TrackSelectionDialogBuilder;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
import com.google.android.exoplayer2.util.Util;
import javax.inject.Inject;
import javax.inject.Named;
import butterknife.BindView;
import butterknife.ButterKnife;
import ml.docilealligator.infinityforreddit.Infinity;
import ml.docilealligator.infinityforreddit.R;
import ml.docilealligator.infinityforreddit.RPANBroadcast;
import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper;
import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils;
public class ViewRPANBroadcastFragment extends Fragment {
public static final String EXTRA_RPAN_BROADCAST = "ERB";
private static final String IS_MUTE_STATE = "IMS";
@BindView(R.id.player_view_view_rpan_broadcast_fragment)
PlayerView playerView;
@BindView(R.id.progress_bar_view_rpan_broadcast_fragment)
ProgressBar progressBar;
@BindView(R.id.mute_exo_playback_control_view)
ImageButton muteButton;
@BindView(R.id.hd_exo_playback_control_view)
ImageButton hdButton;
@Inject
@Named("default")
SharedPreferences mSharedPreferences;
@Inject
@Named("current_account")
SharedPreferences mCurrentAccountSharedPreferences;
@Inject
CustomThemeWrapper mCustomThemeWrapper;
@Inject
SimpleCache mSimpleCache;
private AppCompatActivity mActivity;
private RPANBroadcast rpanBroadcast;
private SimpleExoPlayer player;
private DefaultTrackSelector trackSelector;
private DataSource.Factory dataSourceFactory;
private boolean wasPlaying;
private boolean isMute = false;
private long resumePosition = -1;
private boolean isDataSavingMode;
public ViewRPANBroadcastFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_view_rpan_broadcast, container, false);
((Infinity) mActivity.getApplication()).getAppComponent().inject(this);
ButterKnife.bind(this, rootView);
rpanBroadcast = getArguments().getParcelable(EXTRA_RPAN_BROADCAST);
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory();
trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
player = ExoPlayerFactory.newSimpleInstance(mActivity, trackSelector);
playerView.setPlayer(player);
dataSourceFactory = new DefaultHttpDataSourceFactory(Util.getUserAgent(mActivity, "Infinity"));
// Prepare the player with the source.
player.prepare(new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(Uri.parse(rpanBroadcast.rpanStream.hlsUrl)));
player.setRepeatMode(Player.REPEAT_MODE_ALL);
if (resumePosition > 0) {
player.seekTo(resumePosition);
}
player.setPlayWhenReady(true);
wasPlaying = true;
boolean muteVideo = mSharedPreferences.getBoolean(SharedPreferencesUtils.MUTE_VIDEO, false) ||
(mSharedPreferences.getBoolean(SharedPreferencesUtils.MUTE_NSFW_VIDEO, false) && rpanBroadcast.rpanPost.isNsfw);
if (savedInstanceState != null) {
isMute = savedInstanceState.getBoolean(IS_MUTE_STATE);
if (isMute) {
player.setVolume(0f);
muteButton.setImageResource(R.drawable.ic_mute_24dp);
} else {
player.setVolume(1f);
muteButton.setImageResource(R.drawable.ic_unmute_24dp);
}
} else if (muteVideo) {
isMute = true;
player.setVolume(0f);
muteButton.setImageResource(R.drawable.ic_mute_24dp);
} else {
muteButton.setImageResource(R.drawable.ic_unmute_24dp);
}
player.addListener(new Player.EventListener() {
@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
if (!trackGroups.isEmpty()) {
if (isDataSavingMode) {
trackSelector.setParameters(
trackSelector.buildUponParameters()
.setMaxVideoSize(720, 720));
}
hdButton.setVisibility(View.VISIBLE);
hdButton.setOnClickListener(view -> {
TrackSelectionDialogBuilder build = new TrackSelectionDialogBuilder(mActivity,
getString(R.string.select_video_quality), trackSelector, 0);
build.setShowDisableOption(true);
build.setAllowAdaptiveSelections(false);
build.build().show();
});
for (int i = 0; i < trackGroups.length; i++) {
String mimeType = trackGroups.get(i).getFormat(0).sampleMimeType;
if (mimeType != null && mimeType.contains("audio")) {
muteButton.setVisibility(View.VISIBLE);
muteButton.setOnClickListener(view -> {
if (isMute) {
isMute = false;
player.setVolume(1f);
muteButton.setImageResource(R.drawable.ic_unmute_24dp);
} else {
isMute = true;
player.setVolume(0f);
muteButton.setImageResource(R.drawable.ic_mute_24dp);
}
});
break;
}
}
} else {
muteButton.setVisibility(View.GONE);
}
}
});
return rootView;
}
private boolean isBehindLiveWindow(ExoPlaybackException e) {
if (e.type != ExoPlaybackException.TYPE_SOURCE) {
return false;
}
Throwable cause = e.getSourceException();
while (cause != null) {
if (cause instanceof BehindLiveWindowException) {
return true;
}
cause = cause.getCause();
}
return false;
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(IS_MUTE_STATE, isMute);
}
@Override
public void onStart() {
super.onStart();
if (wasPlaying) {
player.setPlayWhenReady(true);
}
}
@Override
public void onStop() {
super.onStop();
wasPlaying = player.getPlayWhenReady();
player.setPlayWhenReady(false);
}
@Override
public void onDestroy() {
super.onDestroy();
player.seekToDefaultPosition();
player.stop(true);
player.release();
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
mActivity = (AppCompatActivity) context;
}
}

View File

@ -22,6 +22,7 @@ public class APIUtils {
public static final String REDGIFS_API_BASE_URI = "https://api.redgifs.com/v1/gfycats/";
public static final String IMGUR_API_BASE_URI = "https://api.imgur.com/3/";
public static final String PUSHSHIFT_API_BASE_URI = "https://api.pushshift.io/";
public static final String STRAPI_BASE_URI = "https://strapi.reddit.com";
public static final String CLIENT_ID_KEY = "client_id";
public static final String CLIENT_ID = "NOe2iKrPPzwscA";

View File

@ -135,4 +135,31 @@ public class JSONUtils {
public static final String WEBM_URL_KEY = "webmUrl";
public static final String UPVOTE_RATIO_KEY = "upvote_ratio";
public static final String INBOX_COUNT_KEY = "inbox_count";
public static final String NEXT_CURSOR_KEY = "next_cursor";
public static final String POST_KEY = "post";
public static final String STYLES_KEY = "styles";
public static final String AUTHOR_INFO_KEY= "authorInfo";
public static final String VOTE_STATE_KEY = "voteState";
public static final String UPVOTE_RATIO_CAMEL_CASE_KEY = "upvoteRatio";
public static final String OUTBOUND_LINK_KEY = "outboundLink";
public static final String IS_NSFW_KEY = "isNsfw";
public static final String IS_LOCKED_KEY = "isLocked";
public static final String IS_ARCHIVED_KEY = "isArchived";
public static final String IS_SPOILER = "isSpoiler";
public static final String SUGGESTED_COMMENT_SORT_CAMEL_CASE_KEY = "suggestedCommentSort";
public static final String LIVE_COMMENTS_WEBSOCKET_KEY = "liveCommentsWebsocket";
public static final String ICON_KEY = "icon";
public static final String STREAM_KEY = "stream";
public static final String STREAM_ID_KEY = "stream_id";
public static final String THUMBNAIL_KEY = "thumbnail";
public static final String PUBLISH_AT_KEY = "publish_at";
public static final String STATE_KEY = "state";
public static final String UPVOTES_KEY = "upvotes";
public static final String DOWNVOTES_KEY = "downvotes";
public static final String UNIQUE_WATCHERS_KEY = "unique_watchers";
public static final String CONTINUOUS_WATCHERS_KEY = "continuous_watchers";
public static final String TOTAL_CONTINUOUS_WATCHERS_KEY = "total_continuous_watchers";
public static final String CHAT_DISABLED_KEY = "chat_disabled";
public static final String BROADCAST_TIME_KEY = "broadcast_time";
public static final String ESTIMATED_REMAINING_TIME_KEY = "estimated_remaining_time";
}

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.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"
android:background="#000000"
android:id="@+id/coordinator_layout_rpan_activity"
tools:context=".activities.RPANActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_layout_rpan_activity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar_rpan_activity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:minHeight="?attr/actionBarSize"
app:popupTheme="@style/AppTheme.PopupOverlay"
app:navigationIcon="?attr/homeAsUpIndicator" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/view_pager_2_rpan_activity"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".fragments.ViewRPANBroadcastFragment">
<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/player_view_view_rpan_broadcast_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:controller_layout_id="@layout/exo_playback_control_view" />
<ProgressBar
android:id="@+id/progress_bar_view_rpan_broadcast_fragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone" />
</FrameLayout>

View File

@ -39,6 +39,7 @@
<string name="post_filter_preference_activity_label">Post Filter</string>
<string name="search_users_result_activity_label">Users</string>
<string name="multireddit_selection_activity_label">Select a Multireddit</string>
<string name="rpan_activity_label">RPAN</string>
<string name="navigation_drawer_open">Open navigation drawer</string>
<string name="navigation_drawer_close">Close navigation drawer</string>
@ -1126,4 +1127,7 @@
<string name="get_image_bitmap_failed">Unable to get the bitmap of the image</string>
<string name="upload_image_failed">Unable to upload the image</string>
<string name="load_rpan_broadcasts_failed">Cannot load RPAN broadcasts</string>
<string name="parse_rpan_broadcasts_failed">Cannot parse RPAN broadcasts</string>
</resources>