Commit f5319d4f by Oleksandr Barabash

init

parents
#built application files
*.apk
*.ap_
# files for the dex VM
*.dex
# Java class files
*.class
# generated files
bin/
gen/
# Local configuration file (sdk path, etc)
local.properties
# Windows thumbnail db
Thumbs.db
# OSX files
.DS_Store
# Eclipse project files
.classpath
.project
# Android Studio
.idea
*.iml
#.idea/workspace.xml - remove # and delete .idea if it better suit your needs.
.gradle
build/
tv
gradle
mobile/libs/
mobile/src/androidTest/
mobile/src/test/
# tv_mobile_hls_stream
stream app (tv, mobile modules)
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
mavenCentral()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
classpath 'com.jakewharton:butterknife-gradle-plugin:8.4.0'
classpath 'org.greenrobot:greendao-gradle-plugin:3.2.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
ext {
sdk = 25
buildTools = "25.0.2"
minSdk = 16
libraryVersion = "1.0.2"
supportVersion = "25.3.0"
}
allprojects {
repositories {
mavenCentral()
jcenter()
}
}
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
apply plugin: 'com.android.application'
apply plugin: 'com.jakewharton.butterknife'
apply plugin: 'org.greenrobot.greendao'
android {
compileSdkVersion project.sdk
buildToolsVersion project.buildTools
defaultConfig {
applicationId "com.android.mobile"
minSdkVersion project.minSdk
targetSdkVersion project.sdk
versionCode 1
versionName "1.0"
buildConfigField "String", "MOBILE_TV_URL", "\"https://stb.tv.s1z.info\""
buildConfigField "String", "USER_AGENT", "\"AndroidTV\""
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
ext {
support = "25.3.1"
glide = "3.6.1"
exoplayer = "r1.5.9"
retrofit = "2.1.0"
converter_gson = retrofit
okhttp = "3.4.1"
butterknife = "8.4.0"
easing = "2.0@aar"
android_animations = "2.2@aar"
green_dao = "3.2.0"
butterknife_compiler = butterknife
dagger = "2.13"
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile "com.android.support:appcompat-v7:$support"
compile "com.android.support:recyclerview-v7:$support"
compile "com.android.support:cardview-v7:$support"
compile "com.android.support:preference-v7:$support"
compile "com.daimajia.easing:library:$easing"
compile "com.daimajia.androidanimations:library:$android_animations"
compile "com.github.bumptech.glide:glide:$glide"
compile "com.google.android.exoplayer:exoplayer:$exoplayer"
compile "com.squareup.retrofit2:retrofit:$retrofit"
compile "com.squareup.retrofit2:converter-gson:$converter_gson"
compile "com.squareup.okhttp3:okhttp:$okhttp"
compile "com.jakewharton:butterknife:$butterknife"
compile "org.greenrobot:greendao:$green_dao"
annotationProcessor "com.jakewharton:butterknife-compiler:$butterknife_compiler"
compile "com.google.dagger:dagger:$dagger"
annotationProcessor "com.google.dagger:dagger-compiler:$dagger"
}
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /home/oem/Android/Sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# 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 *;
#}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.mobile">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:name=".ApplicationContext">
<activity
android:name=".MainActivity"
android:screenOrientation="landscape"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".PlayerActivity"
android:theme="@android:style/Theme.Holo.NoActionBar.Fullscreen"
android:screenOrientation="landscape" />
<activity android:name=".IdentifyUserActivity"
android:windowSoftInputMode="adjustPan"
android:screenOrientation="landscape" />
</application>
</manifest>
\ No newline at end of file
package com.android.mobile;
import android.app.Application;
import com.android.mobile.dependencies.component.ApplicationComponent;
import com.android.mobile.dependencies.component.DaggerApplicationComponent;
import com.android.mobile.dependencies.module.AnimationModule;
import com.android.mobile.dependencies.module.ApplicationModule;
import com.android.mobile.dependencies.module.DatabaseModule;
import com.android.mobile.dependencies.module.NetworkModule;
import com.android.mobile.dependencies.module.RetrofitModule;
import com.android.mobile.security.TelevisionTrustStore;
public class ApplicationContext extends Application {
private static ApplicationComponent component;
@Override
public void onCreate() {
super.onCreate();
component = DaggerApplicationComponent.builder()
.networkModule(new NetworkModule(new TelevisionTrustStore(this)))
.retrofitModule(new RetrofitModule(BuildConfig.MOBILE_TV_URL))
.databaseModule(new DatabaseModule())
.applicationModule(new ApplicationModule(this))
.animationModule(new AnimationModule())
.build();
}
public static ApplicationComponent getComponent() {
return component;
}
}
package com.android.mobile;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.WindowManager;
import com.android.mobile.util.Preferences;
public abstract class BaseActivity extends AppCompatActivity {
private static final String TAG = BaseActivity.class.getSimpleName();
private static final int STATE_NORMAL = -1;
private static final int STATE_UNDEFINED = 0;
private static final int STATE_NOT_REGISTERED = 1;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setApplicationRoute();
super.onCreate(savedInstanceState);
}
private void setApplicationRoute() {
int state = getApplicationState();
Log.d(TAG, "App state: " + state);
Intent intent = getRouteIntent(state);
if (intent != null) {
startActivity(intent);
finish();
}
}
private Intent getRouteIntent(int state) {
Intent intent = new Intent(this, IdentifyUserActivity.class);
switch (state) {
case STATE_UNDEFINED:
return intent.putExtra(IdentifyUserActivity.STATUS, IdentifyUserActivity.CHECK_SERIAL);
case STATE_NOT_REGISTERED:
return intent.putExtra(IdentifyUserActivity.STATUS, IdentifyUserActivity.DEVICE_NOT_REGISTERED);
}
return null;
}
private int getApplicationState() {
if (isUndefined()) {
return STATE_UNDEFINED;
} else if (!isRegistered()) {
return STATE_NOT_REGISTERED;
}
return STATE_NORMAL;
}
private boolean hasIdentifier() {
return Preferences.getUserIdentifier(this) != null
&& !Preferences.getUserIdentifier(this).equals(Build.UNKNOWN);
}
private boolean isRegistered() {
return Preferences.getUserLogin(this) != null &&
Preferences.getUserPassword(this) != null;
}
private boolean isUndefined() {
return !hasIdentifier() && !isRegistered();
}
}
package com.android.mobile;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import android.widget.ImageView;
import com.android.mobile.ui.fragments.CheckSerialFragment;
import com.android.mobile.ui.fragments.DeviceNotRegisteredFragment;
import com.android.mobile.util.APIUtil;
import com.android.mobile.util.Preferences;
import com.android.mobile.util.ViewUtil;
public class IdentifyUserActivity extends AppCompatActivity {
private static final String TAG = IdentifyUserActivity.class.getSimpleName();
public static final String STATUS = "status";
public static final int DEVICE_NOT_REGISTERED = 0;
public static final int CHECK_SERIAL = 1;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.identify_user_activity);
int status = getIntent().getIntExtra(STATUS, -1);
ViewUtil.setImageDrawable(this,
(ImageView) findViewById(R.id.logotype),
APIUtil.getISPLogo(Preferences.getUserIsp(this)));
if (status != -1) {
getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out)
.add(R.id.container, getFragment(status))
.commit();
}
}
private Fragment getFragment(int status) {
switch (status) {
case CHECK_SERIAL:
return CheckSerialFragment.newInstance(null);
case DEVICE_NOT_REGISTERED:
return DeviceNotRegisteredFragment.newInstance(null);
}
return null;
}
}
package com.android.mobile;
import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.view.KeyEvent;
import com.android.mobile.ui.fragments.ChannelsFragment;
import com.android.mobile.ui.fragments.MenuFragment;
import com.android.mobile.util.FocusHandler;
public class MainActivity extends BaseActivity {
private static final String TAG = MainActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onResume() {
super.onResume();
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.container, ChannelsFragment.newInstance(null))
.commit();
}
@Override
public void onBackPressed() {
if (FocusHandler.isMenuOpen()) {
getSupportFragmentManager().popBackStack(MenuFragment.class.getName(),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
} else {
super.onBackPressed();
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
final boolean unique = event.getRepeatCount() == 0;
if (unique) {
if (keyCode == KeyEvent.KEYCODE_MENU) {
showMenuFragment();
return true;
} else if (keyCode == KeyEvent.KEYCODE_BACK) {
onBackPressed();
return true;
}
}
return false;
}
private void showMenuFragment() {
if (FocusHandler.isMenuOpen()) {
getSupportFragmentManager().popBackStack(MenuFragment.class.getName(),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
FocusHandler.setMenuOpen(false);
} else {
getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.slide_from_top, R.anim.slide_to_top,
R.anim.slide_from_top, R.anim.slide_to_top)
.add(R.id.container, MenuFragment.newInstance(null))
.addToBackStack(MenuFragment.class.getName())
.commit();
}
}
}
package com.android.mobile;
import com.android.mobile.database.model.ChannelRecord;
import com.android.mobile.dependencies.Injector;
import com.android.mobile.player.ExtractorRendererBuilder;
import com.android.mobile.player.HlsRendererBuilder;
import com.android.mobile.player.Player;
import com.android.mobile.ui.AutoHideView;
import com.android.mobile.ui.LoadingView;
import com.android.mobile.ui.divider.VerticalDividerItemDecoration;
import com.android.mobile.ui.adapters.LinearTrackSelectionAdapter;
import com.android.mobile.ui.snappysmoothscroller.SnapType;
import com.android.mobile.ui.snappysmoothscroller.SnappyLinearLayoutManager;
import com.android.mobile.util.AnimationUtil;
import com.android.mobile.util.ApplicationProvider;
import com.android.mobile.util.Dialogs;
import com.android.mobile.util.ViewUtil;
import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.MediaCodecTrackRenderer.DecoderInitializationException;
import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
import com.google.android.exoplayer.audio.AudioCapabilities;
import com.google.android.exoplayer.audio.AudioCapabilitiesReceiver;
import com.google.android.exoplayer.drm.UnsupportedDrmException;
import com.google.android.exoplayer.metadata.id3.ApicFrame;
import com.google.android.exoplayer.metadata.id3.GeobFrame;
import com.google.android.exoplayer.metadata.id3.Id3Frame;
import com.google.android.exoplayer.metadata.id3.PrivFrame;
import com.google.android.exoplayer.metadata.id3.TextInformationFrame;
import com.google.android.exoplayer.metadata.id3.TxxxFrame;
import com.google.android.exoplayer.util.Util;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.List;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
public class PlayerActivity extends Activity implements SurfaceHolder.Callback, Player.Listener,
Player.Id3MetadataListener, AudioCapabilitiesReceiver.Listener {
private static final String TAG = PlayerActivity.class.getSimpleName();
public static final String CHANNEL_ID_EXTRA = "channel_id";
private Player player;
private boolean playerNeedsPrepare;
private LinearTrackSelectionAdapter mChannelsAdapter;
@BindView(R.id.surface_view) public SurfaceView surfaceView;
@BindView(R.id.video_title) public TextView mVideoTitle;
@BindView(R.id.player_header) public LinearLayout mPlayerHeader;
private Uri contentUri;
private int contentType;
private ChannelRecord channelRecord;
private SurfaceHolder mSurfaceHolder;
@BindView(R.id.recycler_view) public RecyclerView mRecyclerView;
@BindView(R.id.quick_channel_panel) public AutoHideView mQuickChannelsPanel;
@BindView(R.id.progress_bar) public LoadingView mLoadingView;
@Inject AnimationUtil animationUtil;
private AudioCapabilitiesReceiver audioCapabilitiesReceiver;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.player_activity);
ButterKnife.bind(this);
Injector.inject(this);
channelRecord = ApplicationProvider.getById(getIntent().getIntExtra(CHANNEL_ID_EXTRA, -1));
initializeRecyclerView();
mVideoTitle.setText(channelRecord.getTitle());
mSurfaceHolder = surfaceView.getHolder();
mSurfaceHolder.addCallback(this);
audioCapabilitiesReceiver = new AudioCapabilitiesReceiver(this, this);
audioCapabilitiesReceiver.register();
}
private void highlightCurrentChannel() {
int playing = ApplicationProvider.getCurrent();
int focused = mChannelsAdapter.getFocusedItem();
if (playing != focused) {
mChannelsAdapter.setFocused(playing);
mChannelsAdapter.tryMoveSelection(mRecyclerView, ApplicationProvider.HOLD);
}
}
private void initializeRecyclerView() {
mQuickChannelsPanel.requestFocus();
mRecyclerView.addItemDecoration(new VerticalDividerItemDecoration.Builder(this)
.sizeResId(R.dimen.divider_vertical_margin)
.colorResId(android.R.color.transparent)
.build());
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setItemViewCacheSize(32);
mRecyclerView.setDrawingCacheEnabled(true);
mRecyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
mRecyclerView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
mRecyclerView.getItemAnimator().setChangeDuration(5);
mRecyclerView.getItemAnimator().setMoveDuration(100);
mChannelsAdapter = new LinearTrackSelectionAdapter(this, ApplicationProvider.getChannels());
mRecyclerView.setAdapter(mChannelsAdapter);
SnappyLinearLayoutManager snappyLinearLayoutManager = new SnappyLinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
snappyLinearLayoutManager.setSnapType(SnapType.CENTER);
snappyLinearLayoutManager.setSnapInterpolator(new DecelerateInterpolator());
mRecyclerView.setLayoutManager(snappyLinearLayoutManager);
mRecyclerView.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
boolean isUnique = event.getRepeatCount() == 0;
if (isUnique) {
mQuickChannelsPanel.reset();
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_RIGHT:
return mChannelsAdapter.tryMoveSelection(mRecyclerView, ApplicationProvider.MOVE_RIGHT);
case KeyEvent.KEYCODE_DPAD_LEFT:
return mChannelsAdapter.tryMoveSelection(mRecyclerView, ApplicationProvider.MOVE_LEFT);
case KeyEvent.KEYCODE_DPAD_CENTER:
startNewChannel();
return true;
}
}
}
return false;
}
});
highlightCurrentChannel();
}
private void startNewChannel() {
int mFocusChannel = mChannelsAdapter.getFocusedItem();
int mCurrentChannel = ApplicationProvider.getCurrent();
if (mFocusChannel == mCurrentChannel) {
hidePlayerPanel();
} else {
ChannelRecord focusedChannel = ApplicationProvider.getById(mFocusChannel);
mVideoTitle.setText(focusedChannel.getTitle());
releasePlayer();
contentUri = Uri.parse(focusedChannel.getUrl());
preparePlayer(true);
ApplicationProvider.setCurrent(mFocusChannel);
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
final boolean unique = event.getRepeatCount() == 0;
if (unique) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (ViewUtil.isVisible(mQuickChannelsPanel)) {
hidePlayerPanel();
} else {
super.onBackPressed();
}
return true;
} else if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
if (ViewUtil.isVisible(mQuickChannelsPanel)) {
startNewChannel();
} else {
showPlayerPanel();
}
return true;
}
}
return false;
}
private void hidePlayerPanel() {
mQuickChannelsPanel.hide();
mRecyclerView.clearFocus();
}
private void showPlayerPanel() {
mQuickChannelsPanel.show();
highlightCurrentChannel();
mRecyclerView.requestFocus();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
showPlayerPanel();
return false;
}
@Override
public void onNewIntent(Intent intent) {
releasePlayer();
setIntent(intent);
}
@Override
public void onStart() {
super.onStart();
if (Util.SDK_INT > Build.VERSION_CODES.M) {
onShown();
}
}
@Override
public void onResume() {
super.onResume();
if (Util.SDK_INT <= Build.VERSION_CODES.M || player == null) {
onShown();
}
}
private void onShown() {
contentUri = Uri.parse(channelRecord.getUrl());
contentType = Util.inferContentType(contentUri.getLastPathSegment());
if (player == null) {
preparePlayer(true);
} else {
player.setBackgrounded(false);
}
}
@Override
public void onPause() {
super.onPause();
if (Util.SDK_INT <= Build.VERSION_CODES.M) {
onHidden();
}
}
@Override
public void onStop() {
super.onStop();
if (Util.SDK_INT > Build.VERSION_CODES.M) {
onHidden();
}
}
private void onHidden() {
releasePlayer();
}
@Override
public void onDestroy() {
super.onDestroy();
audioCapabilitiesReceiver.unregister();
releasePlayer();
}
@Override
public void onAudioCapabilitiesChanged(AudioCapabilities audioCapabilities) {
if (player == null) {
return;
}
boolean backgrounded = player.getBackgrounded();
boolean playWhenReady = player.getPlayWhenReady();
releasePlayer();
preparePlayer(playWhenReady);
player.setBackgrounded(backgrounded);
}
private Player.RendererBuilder getRendererBuilder() {
String userAgent = Util.getUserAgent(this, BuildConfig.USER_AGENT);
switch (contentType) {
case Util.TYPE_HLS:
return new HlsRendererBuilder(this, userAgent, contentUri.toString());
case Util.TYPE_OTHER:
return new ExtractorRendererBuilder(this, userAgent, contentUri);
default:
throw new IllegalStateException("Unsupported type: " + contentType);
}
}
private void preparePlayer(boolean playWhenReady) {
if (player == null) {
player = new Player(getRendererBuilder());
player.addListener(this);
player.setMetadataListener(this);
playerNeedsPrepare = true;
}
if (playerNeedsPrepare) {
player.prepare();
playerNeedsPrepare = false;
}
player.setSurface(mSurfaceHolder.getSurface());
player.setPlayWhenReady(playWhenReady);
}
private void releasePlayer() {
if (player != null) {
player.release();
player = null;
}
}
private void showBuffering() {
mLoadingView.setVisibility(true);
}
private void hideBuffering() {
mLoadingView.setVisibility(false);
}
@Override
public void onStateChanged(boolean playWhenReady, int playbackState) {
switch (playbackState) {
case Player.STATE_IDLE:
break;
case Player.STATE_PREPARING:
case Player.STATE_BUFFERING:
showBuffering();
break;
case Player.STATE_READY:
hideBuffering();
break;
case Player.STATE_ENDED:
break;
case Player.TRACK_DISABLED:
break;
case Player.TRACK_DEFAULT:
break;
}
}
@Override
public void onError(Exception e) {
String errorString = null;
if (e instanceof UnsupportedDrmException) {
// Special case DRM failures.
UnsupportedDrmException unsupportedDrmException = (UnsupportedDrmException) e;
errorString = getString(Util.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2 ? R.string.player_activity__error_drm_not_supported
: unsupportedDrmException.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME
? R.string.player_activity__error_drm_unsupported_scheme : R.string.player_activity__error_drm_unknown);
} else if (e instanceof ExoPlaybackException
&& e.getCause() instanceof DecoderInitializationException) {
// Special case for decoder initialization failures.
DecoderInitializationException decoderInitializationException =
(DecoderInitializationException) e.getCause();
if (decoderInitializationException.decoderName == null) {
if (decoderInitializationException.getCause() instanceof DecoderQueryException) {
errorString = getString(R.string.player_activity__error_querying_decoders);
} else if (decoderInitializationException.secureDecoderRequired) {
errorString = getString(R.string.player_activity__error_no_secure_decoder,
decoderInitializationException.mimeType);
} else {
errorString = getString(R.string.player_activity__error_no_decoder,
decoderInitializationException.mimeType);
}
} else {
errorString = getString(R.string.player_activity__error_instantiating_decoder,
decoderInitializationException.decoderName);
}
} else if (e instanceof ExoPlaybackException) {
errorString = getString(R.string.player_activity__video_format_not_allowed);
}
if (errorString != null) {
Dialogs.showDialog(this, getString(R.string.player_activity__something_went_wrong), errorString);
}
playerNeedsPrepare = true;
}
@Override
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthAspectRatio) {
}
@Override
public void onId3Metadata(List<Id3Frame> id3Frames) {
for (Id3Frame id3Frame : id3Frames) {
if (id3Frame instanceof TxxxFrame) {
TxxxFrame txxxFrame = (TxxxFrame) id3Frame;
Log.i(TAG, String.format("ID3 TimedMetadata %s: description=%s, value=%s", txxxFrame.id,
txxxFrame.description, txxxFrame.value));
} else if (id3Frame instanceof PrivFrame) {
PrivFrame privFrame = (PrivFrame) id3Frame;
Log.i(TAG, String.format("ID3 TimedMetadata %s: owner=%s", privFrame.id, privFrame.owner));
} else if (id3Frame instanceof GeobFrame) {
GeobFrame geobFrame = (GeobFrame) id3Frame;
Log.i(TAG, String.format("ID3 TimedMetadata %s: mimeType=%s, filename=%s, description=%s",
geobFrame.id, geobFrame.mimeType, geobFrame.filename, geobFrame.description));
} else if (id3Frame instanceof ApicFrame) {
ApicFrame apicFrame = (ApicFrame) id3Frame;
Log.i(TAG, String.format("ID3 TimedMetadata %s: mimeType=%s, description=%s",
apicFrame.id, apicFrame.mimeType, apicFrame.description));
} else if (id3Frame instanceof TextInformationFrame) {
TextInformationFrame textInformationFrame = (TextInformationFrame) id3Frame;
Log.i(TAG, String.format("ID3 TimedMetadata %s: description=%s", textInformationFrame.id,
textInformationFrame.description));
} else {
Log.i(TAG, String.format("ID3 TimedMetadata %s", id3Frame.id));
}
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
mSurfaceHolder = holder;
if (player != null) {
player.setSurface(mSurfaceHolder.getSurface());
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (player != null) {
player.blockingClearSurface();
}
}
}
package com.android.mobile.api;
import com.google.gson.annotations.SerializedName;
import java.util.Map;
public final class STBResponse {
@SerializedName("status_code") private String statusCode;
@SerializedName("data") private Map<String, String> data;
@SerializedName("details") private String details;
private STBResponse(Builder builder) {
statusCode = builder.statusCode;
data = builder.data;
details = builder.details;
}
public String getStatusCode() {
return statusCode;
}
public Map<String, String> getData() {
return data;
}
public String getDetails() {
return details;
}
public boolean isSuccessful() {
return statusCode.equals("200");
}
@Override
public String toString() {
return "STBResponse{" +
"statusCode='" + statusCode + '\'' +
", data=" + data +
", details='" + details + '\'' +
'}';
}
public static final class Builder {
private String statusCode;
private Map<String, String> data;
private String details;
public Builder() {
}
public Builder statusCode(String statusCode) {
this.statusCode = statusCode;
return this;
}
public Builder data(Map<String, String> data) {
this.data = data;
return this;
}
public Builder details(String details) {
this.details = details;
return this;
}
public STBResponse build() {
return new STBResponse(this);
}
}
}
package com.android.mobile.api.controller;
import com.android.mobile.api.STBResponse;
import com.android.mobile.api.retrofit.RetrofitCallback;
import com.android.mobile.api.retrofit.service.AccountService;
import java.io.IOException;
import java.util.Map;
import retrofit2.Call;
import retrofit2.Response;
import retrofit2.Retrofit;
public class AccountController {
private static final String TAG = AccountController.class.getSimpleName();
private Callback mCallback;
private AccountService mAccountService;
public AccountController(Retrofit retrofit) {
mAccountService = retrofit.create(AccountService.class);
}
public void setCallback(Callback mCallback) {
this.mCallback = mCallback;
}
public void signIn(Map<String, String> params) {
if (mCallback != null) {
mCallback.onRun();
}
mAccountService.signIn(params).enqueue(new retrofit2.Callback<STBResponse>() {
@Override
public void onResponse(Call<STBResponse> call, Response<STBResponse> response) {
if (response.isSuccessful()) {
if (mCallback != null) {
mCallback.onSuccess(response.body());
}
} else {
if (mCallback != null) {
try {
mCallback.onFailure(new IOException(response.errorBody().string()));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Override
public void onFailure(Call<STBResponse> call, Throwable t) {
if (mCallback != null) {
mCallback.onFailure(t);
}
}
});
}
public void signUp(Map<String, String> params) {
if (mCallback != null) {
mCallback.onRun();
}
mAccountService.signUp(params).enqueue(new retrofit2.Callback<STBResponse>() {
@Override
public void onResponse(Call<STBResponse> call, Response<STBResponse> response) {
if (response.isSuccessful()) {
if (mCallback != null) {
mCallback.onSuccess(response.body());
}
} else {
if (mCallback != null) {
try {
mCallback.onFailure(new IOException(response.errorBody().string()));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Override
public void onFailure(Call<STBResponse> call, Throwable t) {
if (mCallback != null) {
mCallback.onFailure(t);
}
}
});
}
public void signOut() {
if (mCallback != null) {
mCallback.onRun();
}
mAccountService.signOut().enqueue(new retrofit2.Callback<STBResponse>() {
@Override
public void onResponse(Call<STBResponse> call, Response<STBResponse> response) {
if (response.isSuccessful()) {
if (mCallback != null) {
mCallback.onSuccess(response.body());
}
} else {
if (mCallback != null) {
try {
mCallback.onFailure(new IOException(response.errorBody().string()));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Override
public void onFailure(Call<STBResponse> call, Throwable t) {
if (mCallback != null) {
mCallback.onFailure(t);
}
}
});
}
public void setUserIdentifier(Map<String, String> params) {
if (mCallback != null) {
mCallback.onRun();
}
mAccountService.setUserIdentifier(params).enqueue(new retrofit2.Callback<STBResponse>() {
@Override
public void onResponse(Call<STBResponse> call, Response<STBResponse> response) {
if (response.isSuccessful()) {
if (mCallback != null) {
mCallback.onSuccess(response.body());
}
} else {
if (mCallback != null) {
try {
mCallback.onFailure(new IOException(response.errorBody().string()));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Override
public void onFailure(Call<STBResponse> call, Throwable t) {
if (mCallback != null) {
mCallback.onFailure(t);
}
}
});
}
public interface Callback extends RetrofitCallback {
void onRun();
void onSuccess(STBResponse response);
void onFailure(Throwable throwable);
}
}
package com.android.mobile.api.controller;
import com.android.mobile.api.STBResponse;
import com.android.mobile.api.retrofit.RetrofitCallback;
import com.android.mobile.api.retrofit.service.ChannelService;
import com.android.mobile.exception.NonSuccessfulResponseException;
import java.io.IOException;
import retrofit2.Call;
import retrofit2.Response;
import retrofit2.Retrofit;
public class ChannelController {
private ChannelService channelService;
private Callback callback;
public ChannelController(Retrofit retrofit) {
channelService = retrofit.create(ChannelService.class);
}
public void setCallback(Callback callback) {
this.callback = callback;
}
public void getChannels() {
callback.onRun();
Call<STBResponse> call = channelService.getChannels();
call.enqueue(new ChannelCallbackQueue());
}
private class ChannelCallbackQueue implements retrofit2.Callback<STBResponse> {
@Override
public void onResponse(Call<STBResponse> call, Response<STBResponse> response) {
if (response.isSuccessful()) {
callback.onSuccess(response.body());
} else {
try {
callback.onFailure(new NonSuccessfulResponseException(response.errorBody().string()));
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void onFailure(Call<STBResponse> call, Throwable t) {
callback.onFailure(new NonSuccessfulResponseException(t.getMessage()));
}
}
public interface Callback extends RetrofitCallback {
void onRun();
void onSuccess(STBResponse response);
void onFailure(Throwable throwable);
}
}
package com.android.mobile.api.retrofit;
public interface RetrofitCallback {
}
package com.android.mobile.api.retrofit.service;
import com.android.mobile.api.STBResponse;
import java.util.Map;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.POST;
public interface AccountService {
String EMAIL = "email", PASSWORD = "password", SERIAL = "serial";
@POST("/api/v1/signin") Call<STBResponse> signIn(@Body Map<String, String> params);
@POST("/api/v1/signup") Call<STBResponse> signUp(@Body Map<String, String> params);
@POST("/api/v1/signout") Call<STBResponse> signOut();
@POST("/api/v1/startup") Call<STBResponse> setUserIdentifier(@Body Map<String, String> params);
}
package com.android.mobile.api.retrofit.service;
import com.android.mobile.api.STBResponse;
import retrofit2.Call;
import retrofit2.http.GET;
public interface ChannelService {
@GET("/api/v1/channels") Call<STBResponse> getChannels();
}
package com.android.mobile.database;
import android.content.Context;
import com.android.mobile.database.model.DaoMaster;
public class DatabaseHelper extends DaoMaster.OpenHelper {
public DatabaseHelper(Context context) {
super(context, "channels.db");
}
}
package com.android.mobile.database.model;
import android.os.Parcel;
import android.os.Parcelable;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.Index;
import org.greenrobot.greendao.annotation.Property;
import org.greenrobot.greendao.annotation.Generated;
@Entity(nameInDb = "channels", indexes = {@Index(value = "title DESC", unique = true)})
public class ChannelRecord implements Parcelable {
@Id(autoincrement = true)
private Long id;
@SerializedName("title") @Property(nameInDb = "title")
private String title;
@SerializedName("category") @Property(nameInDb = "category")
private String category;
@SerializedName("url") @Property(nameInDb = "url")
private String url;
@SerializedName("bg_image") @Property(nameInDb = "bg_image")
private String bgImage;
public ChannelRecord() {
}
public ChannelRecord(String title, String category, String url, String bgImage) {
this.title = title;
this.category = category;
this.url = url;
this.bgImage = bgImage;
}
protected ChannelRecord(Parcel in) {
this.title = in.readString();
this.category = in.readString();
this.url = in.readString();
this.bgImage = in.readString();
}
@Generated(hash = 1905863218)
public ChannelRecord(Long id, String title, String category, String url, String bgImage) {
this.id = id;
this.title = title;
this.category = category;
this.url = url;
this.bgImage = bgImage;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(title);
dest.writeString(category);
dest.writeString(url);
dest.writeString(bgImage);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<ChannelRecord> CREATOR = new Creator<ChannelRecord>() {
@Override
public ChannelRecord createFromParcel(Parcel in) {
return new ChannelRecord(in);
}
@Override
public ChannelRecord[] newArray(int size) {
return new ChannelRecord[size];
}
};
public String getTitle() {
return title == null ? "" : title;
}
public void setTitle(String title) {
this.title = title;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getBgImage() {
return bgImage;
}
public void setBgImage(String bgImage) {
this.bgImage = bgImage;
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
@Override
public String toString() {
return new Gson().toJson(this);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ChannelRecord)) return false;
ChannelRecord channel = (ChannelRecord) o;
if (getTitle() != null ? !getTitle().equals(channel.getTitle()) : channel.getTitle() != null)
return false;
if (getCategory() != null ? !getCategory().equals(channel.getCategory()) : channel.getCategory() != null)
return false;
if (getUrl() != null ? !getUrl().equals(channel.getUrl()) : channel.getUrl() != null)
return false;
return getBgImage() != null ? getBgImage().equals(channel.getBgImage()) : channel.getBgImage() == null;
}
@Override
public int hashCode() {
int result = getTitle() != null ? getTitle().hashCode() : 0;
result = 31 * result + (getCategory() != null ? getCategory().hashCode() : 0);
result = 31 * result + (getUrl() != null ? getUrl().hashCode() : 0);
result = 31 * result + (getBgImage() != null ? getBgImage().hashCode() : 0);
return result;
}
}
package com.android.mobile.dependencies;
import com.android.mobile.ApplicationContext;
import com.android.mobile.PlayerActivity;
import com.android.mobile.dependencies.component.ApplicationComponent;
import com.android.mobile.ui.ScalableCardView;
import com.android.mobile.ui.fragments.ChangeSerialFragment;
import com.android.mobile.ui.fragments.ChannelsFragment;
import com.android.mobile.ui.fragments.CheckSerialFragment;
import com.android.mobile.ui.fragments.MenuFragment;
import com.android.mobile.ui.fragments.SignInFragment;
import com.android.mobile.ui.fragments.SignUpFragment;
public final class Injector {
private static ApplicationComponent component;
static {
component = ApplicationContext.getComponent();
}
public static void inject(SignInFragment signInFragment) {
component.inject(signInFragment);
}
public static void inject(ChangeSerialFragment changeSerialFragment) {
component.inject(changeSerialFragment);
}
public static void inject(SignUpFragment signUpFragment) {
component.inject(signUpFragment);
}
public static void inject(ChannelsFragment channelsFragment) {
component.inject(channelsFragment);
}
public static void inject(PlayerActivity playerActivity) {
component.inject(playerActivity);
}
public static void inject(ScalableCardView scalableCardView) {
component.inject(scalableCardView);
}
public static void inject(MenuFragment menuFragment) {
component.inject(menuFragment);
}
public static void inject(CheckSerialFragment checkSerialFragment) {
component.inject(checkSerialFragment);
}
}
package com.android.mobile.dependencies.component;
import com.android.mobile.PlayerActivity;
import com.android.mobile.dependencies.module.AnimationModule;
import com.android.mobile.dependencies.module.ApplicationModule;
import com.android.mobile.dependencies.module.DatabaseModule;
import com.android.mobile.dependencies.module.NetworkModule;
import com.android.mobile.dependencies.module.RetrofitModule;
import com.android.mobile.ui.ScalableCardView;
import com.android.mobile.ui.fragments.ChangeSerialFragment;
import com.android.mobile.ui.fragments.ChannelsFragment;
import com.android.mobile.ui.fragments.CheckSerialFragment;
import com.android.mobile.ui.fragments.MenuFragment;
import com.android.mobile.ui.fragments.SignInFragment;
import com.android.mobile.ui.fragments.SignUpFragment;
import javax.inject.Singleton;
import dagger.Component;
@Singleton
@Component(dependencies = {}, modules = {NetworkModule.class,
RetrofitModule.class,
DatabaseModule.class,
ApplicationModule.class,
AnimationModule.class})
public interface ApplicationComponent {
void inject(SignInFragment signInFragment);
void inject(ChangeSerialFragment changeSerialFragment);
void inject(SignUpFragment signUpFragment);
void inject(ChannelsFragment channelsFragment);
void inject(PlayerActivity playerActivity);
void inject(ScalableCardView scalableCardView);
void inject(MenuFragment menuFragment);
void inject(CheckSerialFragment checkSerialFragment);
}
package com.android.mobile.dependencies.module;
import com.android.mobile.util.AnimationUtil;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class AnimationModule {
@Provides
@Singleton
AnimationUtil provideAnimationUtil() {
return new AnimationUtil();
}
}
package com.android.mobile.dependencies.module;
import android.content.Context;
import com.android.mobile.ApplicationContext;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class ApplicationModule {
private ApplicationContext application;
public ApplicationModule(ApplicationContext application) {
this.application = application;
}
@Provides
@Singleton
ApplicationContext provideApplicationContext() {
return application;
}
@Provides
@Singleton
Context provideContext() {
return application.getApplicationContext();
}
}
package com.android.mobile.dependencies.module;
import android.content.Context;
import com.android.mobile.database.DatabaseHelper;
import com.android.mobile.database.model.ChannelRecordDao;
import com.android.mobile.database.model.DaoMaster;
import com.android.mobile.database.model.DaoSession;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class DatabaseModule {
@Provides
@Singleton
DatabaseHelper provideDatabaseHelper(Context context) {
return new DatabaseHelper(context);
}
@Provides
@Singleton
DaoSession provideDatabaseSession(DatabaseHelper helper) {
return new DaoMaster(helper.getWritableDatabase()).newSession();
}
@Provides
@Singleton
ChannelRecordDao provideChannelDatabase(DaoSession session) {
return session.getChannelRecordDao();
}
}
package com.android.mobile.dependencies.module;
import com.android.mobile.security.TrustStore;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.concurrent.TimeUnit;
import javax.inject.Named;
import javax.inject.Singleton;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import dagger.Module;
import dagger.Provides;
import okhttp3.OkHttpClient;
@Module
public class NetworkModule {
private SSLSocketFactory sslSocketFactory;
private X509TrustManager trustManager;
public NetworkModule(TrustStore trustStore) {
try {
TrustManager[] trustManagers = getTrustManager(trustStore);
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, trustManagers, null);
trustManager = (X509TrustManager) trustManagers[0];
sslSocketFactory = context.getSocketFactory();
} catch (NoSuchAlgorithmException | KeyManagementException e ){
throw new AssertionError(e);
}
}
@Provides
@Singleton
@Named("TLS")
OkHttpClient provideSecureHttpClient() {
return new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.sslSocketFactory(sslSocketFactory, trustManager)
.hostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier())
.build();
}
public TrustManager[] getTrustManager(TrustStore trustStore) {
try {
InputStream keyStoreInputStream = trustStore.getKeyStoreInputStream();
KeyStore keyStore = KeyStore.getInstance("BKS");
keyStore.load(keyStoreInputStream, trustStore.getKeyStorePassword().toCharArray());
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
trustManagerFactory.init(keyStore);
return trustManagerFactory.getTrustManagers();
} catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
}
}
package com.android.mobile.dependencies.module;
import com.android.mobile.api.controller.AccountController;
import com.android.mobile.api.controller.ChannelController;
import javax.inject.Named;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
@Module
public class RetrofitModule {
private String url;
public RetrofitModule(String url) {
this.url = url;
}
@Provides
@Singleton
@Named("TLSConnection")
Retrofit provideSecureRetrofit(@Named("TLS") OkHttpClient okHttpClient) {
return new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
}
@Provides
@Singleton
AccountController provideAccountController(@Named("TLSConnection") Retrofit retrofit) {
return new AccountController(retrofit);
}
@Provides
@Singleton
ChannelController provideChannelController(@Named("TLSConnection") Retrofit retrofit) {
return new ChannelController(retrofit);
}
}
package com.android.mobile.exception;
import java.io.IOException;
public class InvalidIdentifierException extends IOException {
public InvalidIdentifierException(String message) {
super(message);
}
}
package com.android.mobile.exception;
import java.io.IOException;
public class NonSuccessfulResponseException extends IOException {
public NonSuccessfulResponseException(String detailMessage) {
super(detailMessage);
}
}
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.mobile.player;
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
import com.google.android.exoplayer.MediaCodecSelector;
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.audio.AudioCapabilities;
import com.google.android.exoplayer.extractor.Extractor;
import com.google.android.exoplayer.extractor.ExtractorSampleSource;
import com.google.android.exoplayer.extractor.ts.PtsTimestampAdjuster;
import com.google.android.exoplayer.extractor.ts.TsExtractor;
import com.google.android.exoplayer.text.TextTrackRenderer;
import com.google.android.exoplayer.upstream.Allocator;
import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DefaultAllocator;
import com.google.android.exoplayer.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer.upstream.DefaultHttpDataSource;
import com.google.android.exoplayer.upstream.DefaultUriDataSource;
import android.content.Context;
import android.media.AudioManager;
import android.media.MediaCodec;
import android.net.Uri;
import android.os.Handler;
/**
* A {@link Player.RendererBuilder} for streams that can be read using an {@link Extractor}.
*/
public class ExtractorRendererBuilder implements Player.RendererBuilder {
private static final int BUFFER_SEGMENT_SIZE = 64 * 1024;
private static final int BUFFER_SEGMENT_COUNT = 256;
private static final int READ_TIMEOUT = 30 * 1000;
private static final int CONNECTION_TIMEOUT = 30 * 1000;
private final Context context;
private final String userAgent;
private final Uri uri;
public ExtractorRendererBuilder(Context context, String userAgent, Uri uri) {
this.context = context;
this.userAgent = userAgent;
this.uri = uri;
}
@Override
public void buildRenderers(Player player) {
Allocator allocator = new DefaultAllocator(BUFFER_SEGMENT_SIZE);
Handler mainHandler = player.getMainHandler();
// Build the video and audio renderers.
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(mainHandler, null);
// DataSource dataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);
// change timeouts from 8000 to 30000 @equa1s
DataSource dataSource = new DefaultUriDataSource(context, bandwidthMeter,
new DefaultHttpDataSource(userAgent, null, null, CONNECTION_TIMEOUT, READ_TIMEOUT, false));
TsExtractor tsExtractor = new TsExtractor(new PtsTimestampAdjuster(0));
ExtractorSampleSource sampleSource = new ExtractorSampleSource(uri, dataSource, allocator,
BUFFER_SEGMENT_COUNT * BUFFER_SEGMENT_SIZE, mainHandler, player, 0, tsExtractor);
MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context,
sampleSource, MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000,
mainHandler, player, 50);
MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource,
MediaCodecSelector.DEFAULT, null, true, mainHandler, player,
AudioCapabilities.getCapabilities(context), AudioManager.STREAM_MUSIC);
TrackRenderer textRenderer = new TextTrackRenderer(sampleSource, player,
mainHandler.getLooper());
// Invoke the addCallback.
TrackRenderer[] renderers = new TrackRenderer[Player.RENDERER_COUNT];
renderers[Player.TYPE_VIDEO] = videoRenderer;
renderers[Player.TYPE_AUDIO] = audioRenderer;
renderers[Player.TYPE_TEXT] = textRenderer;
player.onRenderers(renderers, bandwidthMeter);
}
@Override
public void cancel() {
// Do nothing.
}
}
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.mobile.player;
import com.google.android.exoplayer.DefaultLoadControl;
import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
import com.google.android.exoplayer.MediaCodecSelector;
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.audio.AudioCapabilities;
import com.google.android.exoplayer.hls.DefaultHlsTrackSelector;
import com.google.android.exoplayer.hls.HlsChunkSource;
import com.google.android.exoplayer.hls.HlsMasterPlaylist;
import com.google.android.exoplayer.hls.HlsPlaylist;
import com.google.android.exoplayer.hls.HlsPlaylistParser;
import com.google.android.exoplayer.hls.HlsSampleSource;
import com.google.android.exoplayer.hls.PtsTimestampAdjusterProvider;
import com.google.android.exoplayer.metadata.MetadataTrackRenderer;
import com.google.android.exoplayer.metadata.id3.Id3Frame;
import com.google.android.exoplayer.metadata.id3.Id3Parser;
import com.google.android.exoplayer.text.TextTrackRenderer;
import com.google.android.exoplayer.text.eia608.Eia608TrackRenderer;
import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DefaultAllocator;
import com.google.android.exoplayer.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer.upstream.DefaultUriDataSource;
import com.google.android.exoplayer.util.ManifestFetcher;
import com.google.android.exoplayer.util.ManifestFetcher.ManifestCallback;
import android.content.Context;
import android.media.AudioManager;
import android.media.MediaCodec;
import android.os.Handler;
import java.io.IOException;
import java.util.List;
/**
* A {@link Player.RendererBuilder} for HLS.
*/
public class HlsRendererBuilder implements Player.RendererBuilder {
private static final int BUFFER_SEGMENT_SIZE = 64 * 1024;
private static final int MAIN_BUFFER_SEGMENTS = 254;
private static final int AUDIO_BUFFER_SEGMENTS = 54;
private static final int TEXT_BUFFER_SEGMENTS = 2;
private final Context context;
private final String userAgent;
private final String url;
private AsyncRendererBuilder currentAsyncBuilder;
public HlsRendererBuilder(Context context, String userAgent, String url) {
this.context = context;
this.userAgent = userAgent;
this.url = url;
}
@Override
public void buildRenderers(Player player) {
currentAsyncBuilder = new AsyncRendererBuilder(context, userAgent, url, player);
currentAsyncBuilder.init();
}
@Override
public void cancel() {
if (currentAsyncBuilder != null) {
currentAsyncBuilder.cancel();
currentAsyncBuilder = null;
}
}
private static final class AsyncRendererBuilder implements ManifestCallback<HlsPlaylist> {
private final Context context;
private final String userAgent;
private final Player player;
private final ManifestFetcher<HlsPlaylist> playlistFetcher;
private boolean canceled;
public AsyncRendererBuilder(Context context, String userAgent, String url, Player player) {
this.context = context;
this.userAgent = userAgent;
this.player = player;
HlsPlaylistParser parser = new HlsPlaylistParser();
playlistFetcher = new ManifestFetcher<>(url, new DefaultUriDataSource(context, userAgent),
parser);
}
public void init() {
playlistFetcher.singleLoad(player.getMainHandler().getLooper(), this);
}
public void cancel() {
canceled = true;
}
@Override
public void onSingleManifestError(IOException e) {
if (canceled) {
return;
}
player.onRenderersError(e);
}
@Override
public void onSingleManifest(HlsPlaylist manifest) {
if (canceled) {
return;
}
Handler mainHandler = player.getMainHandler();
LoadControl loadControl = new DefaultLoadControl(new DefaultAllocator(BUFFER_SEGMENT_SIZE));
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
PtsTimestampAdjusterProvider timestampAdjusterProvider = new PtsTimestampAdjusterProvider();
boolean haveSubtitles = false;
boolean haveAudios = false;
if (manifest instanceof HlsMasterPlaylist) {
HlsMasterPlaylist masterPlaylist = (HlsMasterPlaylist) manifest;
haveSubtitles = !masterPlaylist.subtitles.isEmpty();
haveAudios = !masterPlaylist.audios.isEmpty();
}
// Build the video/id3 renderers.
DataSource dataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);
HlsChunkSource chunkSource = new HlsChunkSource(true /* isMaster */, dataSource, manifest,
DefaultHlsTrackSelector.newDefaultInstance(context), bandwidthMeter,
timestampAdjusterProvider, HlsChunkSource.ADAPTIVE_MODE_SPLICE);
HlsSampleSource sampleSource = new HlsSampleSource(chunkSource, loadControl,
MAIN_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, Player.TYPE_VIDEO);
MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context,
sampleSource, MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT,
5000, mainHandler, player, 50);
MetadataTrackRenderer<List<Id3Frame>> id3Renderer = new MetadataTrackRenderer<>(
sampleSource, new Id3Parser(), player, mainHandler.getLooper());
// Build the audio renderer.
MediaCodecAudioTrackRenderer audioRenderer;
if (haveAudios) {
DataSource audioDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);
HlsChunkSource audioChunkSource = new HlsChunkSource(false /* isMaster */, audioDataSource,
manifest, DefaultHlsTrackSelector.newAudioInstance(), bandwidthMeter,
timestampAdjusterProvider, HlsChunkSource.ADAPTIVE_MODE_SPLICE);
HlsSampleSource audioSampleSource = new HlsSampleSource(audioChunkSource, loadControl,
AUDIO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player,
Player.TYPE_AUDIO);
audioRenderer = new MediaCodecAudioTrackRenderer(
new SampleSource[] {sampleSource, audioSampleSource}, MediaCodecSelector.DEFAULT, null,
true, player.getMainHandler(), player, AudioCapabilities.getCapabilities(context),
AudioManager.STREAM_MUSIC);
} else {
audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource,
MediaCodecSelector.DEFAULT, null, true, player.getMainHandler(), player,
AudioCapabilities.getCapabilities(context), AudioManager.STREAM_MUSIC);
}
// Build the text renderer.
TrackRenderer textRenderer;
if (haveSubtitles) {
DataSource textDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);
HlsChunkSource textChunkSource = new HlsChunkSource(false /* isMaster */, textDataSource,
manifest, DefaultHlsTrackSelector.newSubtitleInstance(), bandwidthMeter,
timestampAdjusterProvider, HlsChunkSource.ADAPTIVE_MODE_SPLICE);
HlsSampleSource textSampleSource = new HlsSampleSource(textChunkSource, loadControl,
TEXT_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, Player.TYPE_TEXT);
textRenderer = new TextTrackRenderer(textSampleSource, player, mainHandler.getLooper());
} else {
textRenderer = new Eia608TrackRenderer(sampleSource, player, mainHandler.getLooper());
}
TrackRenderer[] renderers = new TrackRenderer[Player.RENDERER_COUNT];
renderers[Player.TYPE_VIDEO] = videoRenderer;
renderers[Player.TYPE_AUDIO] = audioRenderer;
renderers[Player.TYPE_METADATA] = id3Renderer;
renderers[Player.TYPE_TEXT] = textRenderer;
player.onRenderers(renderers, bandwidthMeter);
}
}
}
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.mobile.player;
import com.google.android.exoplayer.CodecCounters;
import com.google.android.exoplayer.DummyTrackRenderer;
import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.ExoPlayer;
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
import com.google.android.exoplayer.MediaCodecTrackRenderer;
import com.google.android.exoplayer.MediaCodecTrackRenderer.DecoderInitializationException;
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.SingleSampleSource;
import com.google.android.exoplayer.TimeRange;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.audio.AudioTrack;
import com.google.android.exoplayer.chunk.ChunkSampleSource;
import com.google.android.exoplayer.chunk.Format;
import com.google.android.exoplayer.dash.DashChunkSource;
import com.google.android.exoplayer.drm.StreamingDrmSessionManager;
import com.google.android.exoplayer.extractor.ExtractorSampleSource;
import com.google.android.exoplayer.hls.HlsSampleSource;
import com.google.android.exoplayer.metadata.MetadataTrackRenderer.MetadataRenderer;
import com.google.android.exoplayer.metadata.id3.Id3Frame;
import com.google.android.exoplayer.text.Cue;
import com.google.android.exoplayer.text.TextRenderer;
import com.google.android.exoplayer.upstream.BandwidthMeter;
import com.google.android.exoplayer.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer.util.DebugTextViewHelper;
import com.google.android.exoplayer.util.PlayerControl;
import android.media.MediaCodec.CryptoException;
import android.os.Handler;
import android.os.Looper;
import android.view.Surface;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* A wrapper around {@link ExoPlayer} that provides a higher level interface. It can be prepared
* with one of a number of {@link RendererBuilder} classes to suit different use cases (e.g. DASH,
* SmoothStreaming and so on).
*/
public class Player implements ExoPlayer.Listener, ChunkSampleSource.EventListener,
HlsSampleSource.EventListener, ExtractorSampleSource.EventListener,
SingleSampleSource.EventListener, DefaultBandwidthMeter.EventListener,
MediaCodecVideoTrackRenderer.EventListener, MediaCodecAudioTrackRenderer.EventListener,
StreamingDrmSessionManager.EventListener, DashChunkSource.EventListener, TextRenderer,
MetadataRenderer<List<Id3Frame>>, DebugTextViewHelper.Provider {
/**
* Builds renderers for the player.
*/
public interface RendererBuilder {
/**
* Builds renderers for playback.
*
* @param player The player for which renderers are being built. {@link Player#onRenderers}
* should be invoked once the renderers have been built. If building fails,
* {@link Player#onRenderersError} should be invoked.
*/
void buildRenderers(Player player);
/**
* Cancels the current build operation, if there is one. Else does nothing.
* <p>
* A canceled build operation must not invoke {@link Player#onRenderers} or
* {@link Player#onRenderersError} on the player, which may have been released.
*/
void cancel();
}
/**
* A listener for core events.
*/
public interface Listener {
void onStateChanged(boolean playWhenReady, int playbackState);
void onError(Exception e);
void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio);
}
/**
* A listener for internal errors.
* <p>
* These errors are not visible to the user, and hence this listener is provided for
* informational purposes only. Note however that an internal error may cause a fatal
* error if the player fails to recover. If this happens, {@link Listener#onError(Exception)}
* will be invoked.
*/
public interface InternalErrorListener {
void onRendererInitializationError(Exception e);
void onAudioTrackInitializationError(AudioTrack.InitializationException e);
void onAudioTrackWriteError(AudioTrack.WriteException e);
void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs);
void onDecoderInitializationError(DecoderInitializationException e);
void onCryptoError(CryptoException e);
void onLoadError(int sourceId, IOException e);
void onDrmSessionManagerError(Exception e);
}
/**
* A listener for debugging information.
*/
public interface InfoListener {
void onVideoFormatEnabled(Format format, int trigger, long mediaTimeMs);
void onAudioFormatEnabled(Format format, int trigger, long mediaTimeMs);
void onDroppedFrames(int count, long elapsed);
void onBandwidthSample(int elapsedMs, long bytes, long bitrateEstimate);
void onLoadStarted(int sourceId, long length, int type, int trigger, Format format, long mediaStartTimeMs, long mediaEndTimeMs);
void onLoadCompleted(int sourceId, long bytesLoaded, int type, int trigger, Format format, long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs);
void onDecoderInitialized(String decoderName, long elapsedRealtimeMs, long initializationDurationMs);
void onAvailableRangeChanged(int sourceId, TimeRange availableRange);
}
/**
* A listener for receiving notifications of timed text.
*/
public interface CaptionListener {
void onCues(List<Cue> cues);
}
/**
* A listener for receiving ID3 metadata parsed from the media stream.
*/
public interface Id3MetadataListener {
void onId3Metadata(List<Id3Frame> id3Frames);
}
// Constants pulled into this class for convenience.
public static final int STATE_IDLE = ExoPlayer.STATE_IDLE;
public static final int STATE_PREPARING = ExoPlayer.STATE_PREPARING;
public static final int STATE_BUFFERING = ExoPlayer.STATE_BUFFERING;
public static final int STATE_READY = ExoPlayer.STATE_READY;
public static final int STATE_ENDED = ExoPlayer.STATE_ENDED;
public static final int TRACK_DISABLED = ExoPlayer.TRACK_DISABLED;
public static final int TRACK_DEFAULT = ExoPlayer.TRACK_DEFAULT;
public static final int RENDERER_COUNT = 4;
public static final int TYPE_VIDEO = 0;
public static final int TYPE_AUDIO = 1;
public static final int TYPE_TEXT = 2;
public static final int TYPE_METADATA = 3;
private static final int RENDERER_BUILDING_STATE_IDLE = 1;
private static final int RENDERER_BUILDING_STATE_BUILDING = 2;
private static final int RENDERER_BUILDING_STATE_BUILT = 3;
private final RendererBuilder rendererBuilder;
private final ExoPlayer player;
private final PlayerControl playerControl;
private final Handler mainHandler;
private final CopyOnWriteArrayList<Listener> listeners;
private int rendererBuildingState;
private int lastReportedPlaybackState;
private boolean lastReportedPlayWhenReady;
private Surface surface;
private TrackRenderer videoRenderer;
private CodecCounters codecCounters;
private Format videoFormat;
private int videoTrackToRestore;
private BandwidthMeter bandwidthMeter;
private boolean backgrounded;
private CaptionListener captionListener;
private Id3MetadataListener id3MetadataListener;
private InternalErrorListener internalErrorListener;
private InfoListener infoListener;
public Player(RendererBuilder rendererBuilder) {
this.rendererBuilder = rendererBuilder;
player = ExoPlayer.Factory.newInstance(RENDERER_COUNT, 1000, 5000);
player.addListener(this);
playerControl = new PlayerControl(player);
mainHandler = new Handler();
listeners = new CopyOnWriteArrayList<>();
lastReportedPlaybackState = STATE_IDLE;
rendererBuildingState = RENDERER_BUILDING_STATE_IDLE;
// Disable text initially.
player.setSelectedTrack(TYPE_TEXT, TRACK_DISABLED);
}
public PlayerControl getPlayerControl() {
return playerControl;
}
public void addListener(Listener listener) {
listeners.add(listener);
}
public void removeListener(Listener listener) {
listeners.remove(listener);
}
public void setInternalErrorListener(InternalErrorListener listener) {
internalErrorListener = listener;
}
public void setInfoListener(InfoListener listener) {
infoListener = listener;
}
public void setCaptionListener(CaptionListener listener) {
captionListener = listener;
}
public void setMetadataListener(Id3MetadataListener listener) {
id3MetadataListener = listener;
}
public void setSurface(Surface surface) {
this.surface = surface;
pushSurface(false);
}
public Surface getSurface() {
return surface;
}
public void blockingClearSurface() {
surface = null;
pushSurface(true);
}
public int getTrackCount(int type) {
return player.getTrackCount(type);
}
public MediaFormat getTrackFormat(int type, int index) {
return player.getTrackFormat(type, index);
}
public int getSelectedTrack(int type) {
return player.getSelectedTrack(type);
}
public void setSelectedTrack(int type, int index) {
player.setSelectedTrack(type, index);
if (type == TYPE_TEXT && index < 0 && captionListener != null) {
captionListener.onCues(Collections.<Cue>emptyList());
}
}
public boolean getBackgrounded() {
return backgrounded;
}
public void setBackgrounded(boolean backgrounded) {
if (this.backgrounded == backgrounded) {
return;
}
this.backgrounded = backgrounded;
if (backgrounded) {
videoTrackToRestore = getSelectedTrack(TYPE_VIDEO);
setSelectedTrack(TYPE_VIDEO, TRACK_DISABLED);
blockingClearSurface();
} else {
setSelectedTrack(TYPE_VIDEO, videoTrackToRestore);
}
}
public void prepare() {
if (rendererBuildingState == RENDERER_BUILDING_STATE_BUILT) {
player.stop();
}
rendererBuilder.cancel();
videoFormat = null;
videoRenderer = null;
rendererBuildingState = RENDERER_BUILDING_STATE_BUILDING;
maybeReportPlayerState();
rendererBuilder.buildRenderers(this);
}
/**
* Invoked with the results from a {@link RendererBuilder}.
*
* @param renderers Renderers indexed by {@link Player} TYPE_* constants. An individual
* element may be null if there do not exist tracks of the corresponding type.
* @param bandwidthMeter Provides an estimate of the currently available bandwidth. May be null.
*/
/* package */ void onRenderers(TrackRenderer[] renderers, BandwidthMeter bandwidthMeter) {
for (int i = 0; i < RENDERER_COUNT; i++) {
if (renderers[i] == null) {
// Convert a null renderer to a dummy renderer.
renderers[i] = new DummyTrackRenderer();
}
}
// Complete preparation.
this.videoRenderer = renderers[TYPE_VIDEO];
this.codecCounters = videoRenderer instanceof MediaCodecTrackRenderer
? ((MediaCodecTrackRenderer) videoRenderer).codecCounters
: renderers[TYPE_AUDIO] instanceof MediaCodecTrackRenderer
? ((MediaCodecTrackRenderer) renderers[TYPE_AUDIO]).codecCounters : null;
this.bandwidthMeter = bandwidthMeter;
pushSurface(false);
player.prepare(renderers);
rendererBuildingState = RENDERER_BUILDING_STATE_BUILT;
}
/**
* Invoked if a {@link RendererBuilder} encounters an error.
*
* @param e Describes the error.
*/
/* package */ void onRenderersError(Exception e) {
if (internalErrorListener != null) {
internalErrorListener.onRendererInitializationError(e);
}
for (Listener listener : listeners) {
listener.onError(e);
}
rendererBuildingState = RENDERER_BUILDING_STATE_IDLE;
maybeReportPlayerState();
}
public void setPlayWhenReady(boolean playWhenReady) {
player.setPlayWhenReady(playWhenReady);
}
public void seekTo(long positionMs) {
player.seekTo(positionMs);
}
public void release() {
rendererBuilder.cancel();
rendererBuildingState = RENDERER_BUILDING_STATE_IDLE;
surface = null;
player.release();
}
public int getPlaybackState() {
if (rendererBuildingState == RENDERER_BUILDING_STATE_BUILDING) {
return STATE_PREPARING;
}
int playerState = player.getPlaybackState();
if (rendererBuildingState == RENDERER_BUILDING_STATE_BUILT && playerState == STATE_IDLE) {
// This is an edge case where the renderers are built, but are still being passed to the
// player's playback thread.
return STATE_PREPARING;
}
return playerState;
}
@Override
public Format getFormat() {
return videoFormat;
}
@Override
public BandwidthMeter getBandwidthMeter() {
return bandwidthMeter;
}
@Override
public CodecCounters getCodecCounters() {
return codecCounters;
}
@Override
public long getCurrentPosition() {
return player.getCurrentPosition();
}
public long getDuration() {
return player.getDuration();
}
public int getBufferedPercentage() {
return player.getBufferedPercentage();
}
public boolean getPlayWhenReady() {
return player.getPlayWhenReady();
}
/* package */ Looper getPlaybackLooper() {
return player.getPlaybackLooper();
}
/* package */ Handler getMainHandler() {
return mainHandler;
}
@Override
public void onPlayerStateChanged(boolean playWhenReady, int state) {
maybeReportPlayerState();
}
@Override
public void onPlayerError(ExoPlaybackException exception) {
rendererBuildingState = RENDERER_BUILDING_STATE_IDLE;
for (Listener listener : listeners) {
listener.onError(exception);
}
}
@Override
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
float pixelWidthHeightRatio) {
for (Listener listener : listeners) {
listener.onVideoSizeChanged(width, height, unappliedRotationDegrees, pixelWidthHeightRatio);
}
}
@Override
public void onDroppedFrames(int count, long elapsed) {
if (infoListener != null) {
infoListener.onDroppedFrames(count, elapsed);
}
}
@Override
public void onBandwidthSample(int elapsedMs, long bytes, long bitrateEstimate) {
if (infoListener != null) {
infoListener.onBandwidthSample(elapsedMs, bytes, bitrateEstimate);
}
}
@Override
public void onDownstreamFormatChanged(int sourceId, Format format, int trigger, long mediaTimeMs) {
if (infoListener == null) {
return;
}
if (sourceId == TYPE_VIDEO) {
videoFormat = format;
infoListener.onVideoFormatEnabled(format, trigger, mediaTimeMs);
} else if (sourceId == TYPE_AUDIO) {
infoListener.onAudioFormatEnabled(format, trigger, mediaTimeMs);
}
}
@Override
public void onDrmKeysLoaded() {
// Do nothing.
}
@Override
public void onDrmSessionManagerError(Exception e) {
if (internalErrorListener != null) {
internalErrorListener.onDrmSessionManagerError(e);
}
}
@Override
public void onDecoderInitializationError(DecoderInitializationException e) {
if (internalErrorListener != null) {
internalErrorListener.onDecoderInitializationError(e);
}
}
@Override
public void onAudioTrackInitializationError(AudioTrack.InitializationException e) {
if (internalErrorListener != null) {
internalErrorListener.onAudioTrackInitializationError(e);
}
}
@Override
public void onAudioTrackWriteError(AudioTrack.WriteException e) {
if (internalErrorListener != null) {
internalErrorListener.onAudioTrackWriteError(e);
}
}
@Override
public void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) {
if (internalErrorListener != null) {
internalErrorListener.onAudioTrackUnderrun(bufferSize, bufferSizeMs, elapsedSinceLastFeedMs);
}
}
@Override
public void onCryptoError(CryptoException e) {
if (internalErrorListener != null) {
internalErrorListener.onCryptoError(e);
}
}
@Override
public void onDecoderInitialized(String decoderName, long elapsedRealtimeMs, long initializationDurationMs) {
if (infoListener != null) {
infoListener.onDecoderInitialized(decoderName, elapsedRealtimeMs, initializationDurationMs);
}
}
@Override
public void onLoadError(int sourceId, IOException e) {
if (internalErrorListener != null) {
internalErrorListener.onLoadError(sourceId, e);
}
}
@Override
public void onCues(List<Cue> cues) {
if (captionListener != null && getSelectedTrack(TYPE_TEXT) != TRACK_DISABLED) {
captionListener.onCues(cues);
}
}
@Override
public void onMetadata(List<Id3Frame> id3Frames) {
if (id3MetadataListener != null && getSelectedTrack(TYPE_METADATA) != TRACK_DISABLED) {
id3MetadataListener.onId3Metadata(id3Frames);
}
}
@Override
public void onAvailableRangeChanged(int sourceId, TimeRange availableRange) {
if (infoListener != null) {
infoListener.onAvailableRangeChanged(sourceId, availableRange);
}
}
@Override
public void onPlayWhenReadyCommitted() {
// Do nothing.
}
@Override
public void onDrawnToSurface(Surface surface) {
// Do nothing.
}
@Override
public void onLoadStarted(int sourceId, long length, int type, int trigger, Format format,
long mediaStartTimeMs, long mediaEndTimeMs) {
if (infoListener != null) {
infoListener.onLoadStarted(sourceId, length, type, trigger, format, mediaStartTimeMs,
mediaEndTimeMs);
}
}
@Override
public void onLoadCompleted(int sourceId, long bytesLoaded, int type, int trigger, Format format,
long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs) {
if (infoListener != null) {
infoListener.onLoadCompleted(sourceId, bytesLoaded, type, trigger, format, mediaStartTimeMs,
mediaEndTimeMs, elapsedRealtimeMs, loadDurationMs);
}
}
@Override
public void onLoadCanceled(int sourceId, long bytesLoaded) {
// Do nothing.
}
@Override
public void onUpstreamDiscarded(int sourceId, long mediaStartTimeMs, long mediaEndTimeMs) {
// Do nothing.
}
private void maybeReportPlayerState() {
boolean playWhenReady = player.getPlayWhenReady();
int playbackState = getPlaybackState();
if (lastReportedPlayWhenReady != playWhenReady || lastReportedPlaybackState != playbackState) {
for (Listener listener : listeners) {
listener.onStateChanged(playWhenReady, playbackState);
}
lastReportedPlayWhenReady = playWhenReady;
lastReportedPlaybackState = playbackState;
}
}
private void pushSurface(boolean blockForSurfacePush) {
if (videoRenderer == null) {
return;
}
if (blockForSurfacePush) {
player.blockingSendMessage(
videoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, surface);
} else {
player.sendMessage(
videoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, surface);
}
}
}
package com.android.mobile.security;
import android.content.Context;
import com.android.mobile.R;
import java.io.InputStream;
public class TelevisionTrustStore implements TrustStore {
private final Context context;
public TelevisionTrustStore(Context context) {
this.context = context;
}
@Override
public InputStream getKeyStoreInputStream() {
return context.getResources().openRawResource(R.raw.television);
}
@Override
public String getKeyStorePassword() {
return "whisper";
}
}
package com.android.mobile.security;
import java.io.InputStream;
public interface TrustStore {
InputStream getKeyStoreInputStream();
String getKeyStorePassword();
}
package com.android.mobile.ui;
public interface AutoHideInterface {
void reset();
void show();
void hide();
}
package com.android.mobile.ui;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import java.util.TimerTask;
public class AutoHideTimerTask extends TimerTask {
private static final String TAG = AutoHideView.class.getSimpleName();
private Handler handler = new Handler(Looper.getMainLooper());
private View mView;
private boolean isAnimate = false;
private Animation animation;
public AutoHideTimerTask(View view) {
this(view, false);
}
public AutoHideTimerTask(View view, boolean isAnimate) {
this.mView = view;
if (isAnimate) {
animation = AnimationUtils.loadAnimation(view.getContext(), android.R.anim.fade_out);
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {}
@Override
public void onAnimationEnd(Animation animation) {
mView.setVisibility(View.GONE);
}
@Override
public void onAnimationRepeat(Animation animation) {}
});
}
this.isAnimate = isAnimate;
}
@Override
public void run() {
hide();
}
private void hide() {
handler.post(new Runnable() {
@Override
public void run() {
if (isAnimate) {
mView.startAnimation(animation);
} else {
mView.setVisibility(View.GONE);
}
}
});
}
}
package com.android.mobile.ui;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import com.android.mobile.R;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class AutoHideView extends FrameLayout implements AutoHideInterface {
private final static String TAG = AutoHideView.class.getSimpleName();
private final int DEFAULT_TIME = 3000;
private ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
private AutoHideTimerTask task = null;
private boolean isAnimate = false;
private Animation mFadeIn;
private Animation mFadeOut;
private int time;
public AutoHideView(Context context) {
super(context);
task = new AutoHideTimerTask(this);
service.schedule(task, DEFAULT_TIME, TimeUnit.MILLISECONDS);
}
public AutoHideView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.AutoHideView, 0, 0);
try {
isAnimate = a.getBoolean(R.styleable.AutoHideView_isAnimate, false);
time = a.getInteger(R.styleable.AutoHideView_time, DEFAULT_TIME);
} finally {
a.recycle();
}
if (isAnimate) {
mFadeIn = AnimationUtils.loadAnimation(context, android.R.anim.fade_in);
mFadeIn.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {}
@Override
public void onAnimationEnd(Animation animation) {
setVisibility(VISIBLE);
}
@Override
public void onAnimationRepeat(Animation animation) {}
});
mFadeOut = AnimationUtils.loadAnimation(context, android.R.anim.fade_out);
mFadeOut.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
setVisibility(GONE);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
task = new AutoHideTimerTask(this, isAnimate);
service.schedule(task, time, TimeUnit.MILLISECONDS);
}
@Override
public void reset() {
if (service != null) {
service.shutdown();
service.shutdownNow();
service = null;
}
service = Executors.newSingleThreadScheduledExecutor();
task = new AutoHideTimerTask(this, isAnimate);
service.schedule(task, time, TimeUnit.MILLISECONDS);
}
@Override
public void show() {
if (isAnimate) {
startAnimation(mFadeIn);
} else {
setVisibility(VISIBLE);
}
reset();
}
@Override
public void hide() {
if (service != null) {
service.shutdown();
service.shutdownNow();
service = null;
}
if (isAnimate) {
startAnimation(mFadeOut);
} else {
setVisibility(GONE);
}
}
}
package com.android.mobile.ui;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.CardView;
import android.util.AttributeSet;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.android.mobile.R;
import com.android.mobile.database.model.ChannelRecord;
import com.android.mobile.util.ViewUtil;
import butterknife.BindView;
import butterknife.ButterKnife;
public class BaseCardView extends CardView implements BindableCardView {
private ChannelRecord channel;
@BindView(R.id.poster) public ImageView poster;
@BindView(R.id.title) public TextView title;
@BindView(R.id.description) public TextView description;
@BindView(R.id.container) public RelativeLayout container;
public BaseCardView(Context context) {
super(context);
}
public BaseCardView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
ButterKnife.bind(this);
}
@Override
public void bind(@NonNull ChannelRecord channel, boolean isSelected, int position) {
ViewUtil.loadImage(getContext(), poster, channel.getBgImage());
setBackground(isSelected);
title.setText(channel.getTitle());
description.setText(channel.getCategory());
}
@Override
public void unbind() {
if (channel != null) {
channel = null;
}
}
private void setBackground(boolean isSelected) {
if (isSelected) {
ViewUtil.setBackgroundColor(getContext(), container, R.color.blue_600);
} else {
ViewUtil.setBackgroundColor(getContext(), container, R.color.grey_600);
}
}
}
package com.android.mobile.ui;
import android.support.annotation.NonNull;
import com.android.mobile.database.model.ChannelRecord;
interface BindableCardView extends Unbindable {
void bind(@NonNull ChannelRecord channel, boolean isSelected, int position);
}
package com.android.mobile.ui;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.util.AttributeSet;
import android.widget.ProgressBar;
import com.android.mobile.R;
public class LoadingView extends ProgressBar {
private static final int DEFAULT_COLOR = Color.WHITE;
public LoadingView(Context context) {
super(context);
}
public LoadingView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.LoadingView, 0, 0);
int color;
try {
color = a.getColor(R.styleable.LoadingView_color_filter, DEFAULT_COLOR);
} finally {
a.recycle();
}
getIndeterminateDrawable().setColorFilter(color, PorterDuff.Mode.SRC_IN);
}
public void setVisibility(boolean visibility) {
if (visibility) {
show();
} else {
hide();
}
}
private void show() {
setVisibility(VISIBLE);
}
private void hide() {
setVisibility(GONE);
}
}
package com.android.mobile.ui;
import android.content.Context;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import com.android.mobile.ApplicationContext;
import com.android.mobile.database.model.ChannelRecord;
import com.android.mobile.dependencies.Injector;
import com.android.mobile.util.AnimationUtil;
import javax.inject.Inject;
public class ScalableCardView extends BaseCardView {
@Inject AnimationUtil mAnimationUtil;
public ScalableCardView(Context context) {
super(context);
}
public ScalableCardView(Context context, AttributeSet attrs) {
super(context, attrs);
Injector.inject(this);
}
@Override
public void bind(@NonNull ChannelRecord channel, boolean isSelected, int position) {
super.bind(channel, isSelected, position);
setScale(isSelected);
}
public void setScale(boolean isScale) {
if (isScale) {
mAnimationUtil.zoomIn(this);
} else {
mAnimationUtil.zoomOut(this);
}
}
}
package com.android.mobile.ui;
public interface Unbindable {
void unbind();
}
package com.android.mobile.ui.adapters;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.mobile.R;
import com.android.mobile.util.ViewUtil;
import java.util.List;
public class BaseListAdapter extends TrackSelectionAdapter<BaseListAdapter.ViewHolder> {
class ViewHolder extends TrackSelectionAdapter.ViewHolder {
private TextView mTextView;
public ViewHolder(View itemView) {
super(itemView);
this.mTextView = (TextView) itemView.findViewById(R.id.item);
}
public TextView getTextItem() {
return mTextView;
}
public LinearLayout getItem() {
return (LinearLayout) itemView;
}
}
private Context context;
private List<String> data;
private LayoutInflater inflater;
public BaseListAdapter(Context context, List<String> data) {
setHasStableIds(true);
this.context = context;
this.data = data;
this.inflater = LayoutInflater.from(context);
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(inflater.inflate(R.layout.account_menu_item, parent, false));
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
super.onBindViewHolder(holder, position);
LinearLayout item = holder.getItem();
holder.getTextItem().setText(data.get(position));
if (item.isSelected()) {
ViewUtil.setBackgroundColor(context, item, R.color.amber_900);
} else {
ViewUtil.setBackgroundColor(context, item, R.color.grey_800);
}
}
@Override
public int getItemCount() {
return data != null ? data.size() : 0;
}
@Override
public int getItemViewType(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
}
package com.android.mobile.ui.adapters;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.android.mobile.R;
import com.android.mobile.database.model.ChannelRecord;
import com.android.mobile.ui.ScalableCardView;
import java.util.List;
public class GridTrackSelectionAdapter extends TrackSelectionAdapter<GridTrackSelectionAdapter.ViewHolder> {
private LayoutInflater inflater;
private List<ChannelRecord> channels;
public GridTrackSelectionAdapter(@NonNull Context context, @Nullable List<ChannelRecord> channels) {
this.inflater = LayoutInflater.from(context);
this.channels = channels;
}
class ViewHolder extends TrackSelectionAdapter.ViewHolder {
public ViewHolder(View itemView) {
super(itemView);
}
public ScalableCardView getView() {
return (ScalableCardView) itemView;
}
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ScalableCardView view = (ScalableCardView)
inflater.inflate(R.layout.scaled_card_view_item, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder viewHolder, int index) {
super.onBindViewHolder(viewHolder, index);
ChannelRecord record = channels.get(index);
viewHolder.getView().bind(record, getFocusedItem() == index, index);
}
@Override
public int getItemCount() {
return channels != null ? channels.size() : 0;
}
@Override
public int getItemViewType(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public void onViewDetachedFromWindow(ViewHolder holder) {
super.onViewDetachedFromWindow(holder);
holder.getView().clearAnimation();
}
public void swap(List<ChannelRecord> channels) {
this.channels = channels;
notifyDataSetChanged();
}
}
package com.android.mobile.ui.adapters;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.android.mobile.R;
import com.android.mobile.database.model.ChannelRecord;
import com.android.mobile.ui.ScalableCardView;
import java.util.List;
public class LinearTrackSelectionAdapter extends TrackSelectionAdapter<LinearTrackSelectionAdapter.ViewHolder> {
private LayoutInflater mLayoutInflater;
private List<ChannelRecord> mChannels;
class ViewHolder extends TrackSelectionAdapter.ViewHolder {
ViewHolder(View itemView) {
super(itemView);
}
public ScalableCardView getView() {
return (ScalableCardView) itemView;
}
}
public LinearTrackSelectionAdapter(Context context, List<ChannelRecord> mChannels) {
this.mChannels = mChannels;
this.mLayoutInflater = LayoutInflater.from(context);
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final ScalableCardView view = (ScalableCardView) mLayoutInflater.inflate(R.layout.scaled_card_view_item, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
super.onBindViewHolder(viewHolder, position);
ScalableCardView view = viewHolder.getView();
view.bind(mChannels.get(position), view.isSelected(), position);
}
@Override
public void onViewRecycled(ViewHolder holder) {
super.onViewRecycled(holder);
holder.getView().unbind();
}
@Override
public int getItemCount() {
return mChannels != null ? mChannels.size() : 0;
}
@Override
public int getItemViewType(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
public List<ChannelRecord> getChannels() {
return mChannels;
}
public void swap(List<ChannelRecord> channels) {
this.mChannels = channels;
notifyDataSetChanged();
}
}
package com.android.mobile.ui.adapters;
import android.support.v7.widget.RecyclerView;
import android.view.View;
public abstract class TrackSelectionAdapter<VH extends TrackSelectionAdapter.ViewHolder> extends RecyclerView.Adapter<VH> {
private int focused = 0;
TrackSelectionAdapter() {
setHasStableIds(true);
}
public boolean tryMoveSelection(RecyclerView mRecyclerView, int direction) {
int tryFocusItem = focused + direction;
if (tryFocusItem >= 0 && tryFocusItem < getItemCount()) {
notifyItemChanged(focused);
focused = tryFocusItem;
mRecyclerView.smoothScrollToPosition(focused);
notifyItemChanged(focused);
return true;
}
return false;
}
@Override
public void onBindViewHolder(VH viewHolder, int i) {
viewHolder.itemView.setSelected(focused == i);
}
public int getFocusedItem() {
return focused;
}
public void setFocused(Integer focused) {
notifyItemChanged(this.focused);
this.focused = focused;
notifyItemChanged(focused);
}
public class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
notifyItemChanged(focused);
focused = getLayoutPosition();
notifyItemChanged(focused);
}
});
}
public View getView() {
return itemView;
}
}
}
package com.android.mobile.ui.animation;
import android.view.View;
import com.daimajia.androidanimations.library.YoYo;
import com.daimajia.androidanimations.library.fading_entrances.FadeInAnimator;
import com.daimajia.androidanimations.library.fading_exits.FadeOutAnimator;
public class Fade {
private long duration = 500;
private FadeInAnimator fadeIn;
private FadeOutAnimator fadeOut;
public Fade() {
this.fadeIn = new FadeInAnimator();
this.fadeOut = new FadeOutAnimator();
}
public void setDuration(long duration) {
this.duration = duration;
}
public long getDuration() {
return duration;
}
public void fadeIn(View view) {
YoYo.with(fadeIn).duration(duration).playOn(view);
}
public void fadeOut(View view) {
YoYo.with(fadeOut).duration(duration).playOn(view);
}
}
package com.android.mobile.ui.animation;
import android.support.v4.view.ViewCompat;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.ScaleAnimation;
public class Zoom {
private static final float CENTER_PIVOT = .5f;
private static final float START_X_SCALE = 1f;
private static final float START_Y_SCALE = 1f;
private static final float END_X_SCALE = 1.1f;
private static final float END_Y_SCALE = 1.2f;
private static final float TRANSLATION_IN_Z = 2f;
private static final float TRANSLATION_OUT_Z = 1f;
private static final long DURATION_OUT = 250;
private static final long DURATION_IN = 350;
public Zoom() {
}
public void zoomIn(View view) {
Animation scaleIn = new ScaleAnimation(START_X_SCALE, END_X_SCALE, START_Y_SCALE, END_Y_SCALE,
Animation.RELATIVE_TO_SELF, CENTER_PIVOT, Animation.RELATIVE_TO_SELF, CENTER_PIVOT);
scaleIn.setFillAfter(true);
scaleIn.setDuration(DURATION_IN);
view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
ViewCompat.setTranslationZ(view, TRANSLATION_IN_Z);
view.startAnimation(scaleIn);
}
public void zoomOut(View view) {
Animation scaleOut = new ScaleAnimation(END_X_SCALE, START_X_SCALE, END_Y_SCALE, START_Y_SCALE,
Animation.RELATIVE_TO_SELF, CENTER_PIVOT, Animation.RELATIVE_TO_SELF, CENTER_PIVOT);
scaleOut.setFillAfter(true);
scaleOut.setDuration(DURATION_OUT);
view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
ViewCompat.setTranslationZ(view, TRANSLATION_OUT_Z);
view.startAnimation(scaleOut);
}
}
package com.android.mobile.ui.divider;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.annotation.ColorRes;
import android.support.annotation.DimenRes;
import android.support.annotation.DrawableRes;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
/**
* @author yqritc on 2015/01/08.
*/
abstract class FlexibleDividerDecoration extends RecyclerView.ItemDecoration {
private static final int DEFAULT_SIZE = 2;
private static final int[] ATTRS = new int[]{
android.R.attr.listDivider
};
protected enum DividerType {
DRAWABLE, PAINT, COLOR
}
protected DividerType mDividerType = DividerType.DRAWABLE;
protected VisibilityProvider mVisibilityProvider;
protected PaintProvider mPaintProvider;
protected ColorProvider mColorProvider;
protected DrawableProvider mDrawableProvider;
protected SizeProvider mSizeProvider;
protected boolean mShowLastDivider;
protected boolean mPositionInsideItem;
private Paint mPaint;
protected FlexibleDividerDecoration(Builder builder) {
if (builder.mPaintProvider != null) {
mDividerType = DividerType.PAINT;
mPaintProvider = builder.mPaintProvider;
} else if (builder.mColorProvider != null) {
mDividerType = DividerType.COLOR;
mColorProvider = builder.mColorProvider;
mPaint = new Paint();
setSizeProvider(builder);
} else {
mDividerType = DividerType.DRAWABLE;
if (builder.mDrawableProvider == null) {
TypedArray a = builder.mContext.obtainStyledAttributes(ATTRS);
final Drawable divider = a.getDrawable(0);
a.recycle();
mDrawableProvider = new DrawableProvider() {
@Override
public Drawable drawableProvider(int position, RecyclerView parent) {
return divider;
}
};
} else {
mDrawableProvider = builder.mDrawableProvider;
}
mSizeProvider = builder.mSizeProvider;
}
mVisibilityProvider = builder.mVisibilityProvider;
mShowLastDivider = builder.mShowLastDivider;
mPositionInsideItem = builder.mPositionInsideItem;
}
private void setSizeProvider(Builder builder) {
mSizeProvider = builder.mSizeProvider;
if (mSizeProvider == null) {
mSizeProvider = new SizeProvider() {
@Override
public int dividerSize(int position, RecyclerView parent) {
return DEFAULT_SIZE;
}
};
}
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
RecyclerView.Adapter adapter = parent.getAdapter();
if (adapter == null) {
return;
}
int itemCount = adapter.getItemCount();
int lastDividerOffset = getLastDividerOffset(parent);
int validChildCount = parent.getChildCount();
int lastChildPosition = -1;
for (int i = 0; i < validChildCount; i++) {
View child = parent.getChildAt(i);
int childPosition = parent.getChildAdapterPosition(child);
if (childPosition < lastChildPosition) {
// Avoid remaining divider when animation starts
continue;
}
lastChildPosition = childPosition;
if (!mShowLastDivider && childPosition >= itemCount - lastDividerOffset) {
// Don't draw divider for last line if mShowLastDivider = false
continue;
}
if (wasDividerAlreadyDrawn(childPosition, parent)) {
// No need to draw divider again as it was drawn already by previous column
continue;
}
int groupIndex = getGroupIndex(childPosition, parent);
if (mVisibilityProvider.shouldHideDivider(groupIndex, parent)) {
continue;
}
Rect bounds = getDividerBound(groupIndex, parent, child);
switch (mDividerType) {
case DRAWABLE:
Drawable drawable = mDrawableProvider.drawableProvider(groupIndex, parent);
drawable.setBounds(bounds);
drawable.draw(c);
break;
case PAINT:
mPaint = mPaintProvider.dividerPaint(groupIndex, parent);
c.drawLine(bounds.left, bounds.top, bounds.right, bounds.bottom, mPaint);
break;
case COLOR:
mPaint.setColor(mColorProvider.dividerColor(groupIndex, parent));
mPaint.setStrokeWidth(mSizeProvider.dividerSize(groupIndex, parent));
c.drawLine(bounds.left, bounds.top, bounds.right, bounds.bottom, mPaint);
break;
}
}
}
@Override
public void getItemOffsets(Rect rect, View v, RecyclerView parent, RecyclerView.State state) {
int position = parent.getChildAdapterPosition(v);
int itemCount = parent.getAdapter().getItemCount();
int lastDividerOffset = getLastDividerOffset(parent);
if (!mShowLastDivider && position >= itemCount - lastDividerOffset) {
// Don't set item offset for last line if mShowLastDivider = false
return;
}
int groupIndex = getGroupIndex(position, parent);
if (mVisibilityProvider.shouldHideDivider(groupIndex, parent)) {
return;
}
setItemOffsets(rect, groupIndex, parent);
}
/**
* Check if recyclerview is reverse layout
*
* @param parent RecyclerView
* @return true if recyclerview is reverse layout
*/
protected boolean isReverseLayout(RecyclerView parent) {
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof LinearLayoutManager) {
return ((LinearLayoutManager) layoutManager).getReverseLayout();
} else {
return false;
}
}
/**
* In the case mShowLastDivider = false,
* Returns offset for how many views we don't have to draw a divider for,
* for LinearLayoutManager it is as simple as not drawing the last child divider,
* but for a GridLayoutManager it needs to take the span count for the last items into account
* until we use the span count configured for the grid.
*
* @param parent RecyclerView
* @return offset for how many views we don't have to draw a divider or 1 if its a
* LinearLayoutManager
*/
private int getLastDividerOffset(RecyclerView parent) {
if (parent.getLayoutManager() instanceof GridLayoutManager) {
GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
GridLayoutManager.SpanSizeLookup spanSizeLookup = layoutManager.getSpanSizeLookup();
int spanCount = layoutManager.getSpanCount();
int itemCount = parent.getAdapter().getItemCount();
for (int i = itemCount - 1; i >= 0; i--) {
if (spanSizeLookup.getSpanIndex(i, spanCount) == 0) {
return itemCount - i;
}
}
}
return 1;
}
/**
* Determines whether divider was already drawn for the row the item is in,
* effectively only makes sense for a grid
*
* @param position current view position to draw divider
* @param parent RecyclerView
* @return true if the divider can be skipped as it is in the same row as the previous one.
*/
private boolean wasDividerAlreadyDrawn(int position, RecyclerView parent) {
if (parent.getLayoutManager() instanceof GridLayoutManager) {
GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
GridLayoutManager.SpanSizeLookup spanSizeLookup = layoutManager.getSpanSizeLookup();
int spanCount = layoutManager.getSpanCount();
return spanSizeLookup.getSpanIndex(position, spanCount) > 0;
}
return false;
}
/**
* Returns a group index for GridLayoutManager.
* for LinearLayoutManager, always returns position.
*
* @param position current view position to draw divider
* @param parent RecyclerView
* @return group index of items
*/
private int getGroupIndex(int position, RecyclerView parent) {
if (parent.getLayoutManager() instanceof GridLayoutManager) {
GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
GridLayoutManager.SpanSizeLookup spanSizeLookup = layoutManager.getSpanSizeLookup();
int spanCount = layoutManager.getSpanCount();
return spanSizeLookup.getSpanGroupIndex(position, spanCount);
}
return position;
}
protected abstract Rect getDividerBound(int position, RecyclerView parent, View child);
protected abstract void setItemOffsets(Rect outRect, int position, RecyclerView parent);
/**
* Interface for controlling divider visibility
*/
public interface VisibilityProvider {
/**
* Returns true if divider should be hidden.
*
* @param position Divider position (or group index for GridLayoutManager)
* @param parent RecyclerView
* @return True if the divider at position should be hidden
*/
boolean shouldHideDivider(int position, RecyclerView parent);
}
/**
* Interface for controlling paint newInstance for divider drawing
*/
public interface PaintProvider {
/**
* Returns {@link android.graphics.Paint} for divider
*
* @param position Divider position (or group index for GridLayoutManager)
* @param parent RecyclerView
* @return Paint newInstance
*/
Paint dividerPaint(int position, RecyclerView parent);
}
/**
* Interface for controlling divider color
*/
public interface ColorProvider {
/**
* Returns {@link android.graphics.Color} value of divider
*
* @param position Divider position (or group index for GridLayoutManager)
* @param parent RecyclerView
* @return Color value
*/
int dividerColor(int position, RecyclerView parent);
}
/**
* Interface for controlling drawable object for divider drawing
*/
public interface DrawableProvider {
/**
* Returns drawable newInstance for divider
*
* @param position Divider position (or group index for GridLayoutManager)
* @param parent RecyclerView
* @return Drawable newInstance
*/
Drawable drawableProvider(int position, RecyclerView parent);
}
/**
* Interface for controlling divider size
*/
public interface SizeProvider {
/**
* Returns size value of divider.
* Height for horizontal divider, width for vertical divider
*
* @param position Divider position (or group index for GridLayoutManager)
* @param parent RecyclerView
* @return Size of divider
*/
int dividerSize(int position, RecyclerView parent);
}
public static class Builder<T extends Builder> {
private Context mContext;
protected Resources mResources;
private PaintProvider mPaintProvider;
private ColorProvider mColorProvider;
private DrawableProvider mDrawableProvider;
private SizeProvider mSizeProvider;
private VisibilityProvider mVisibilityProvider = new VisibilityProvider() {
@Override
public boolean shouldHideDivider(int position, RecyclerView parent) {
return false;
}
};
private boolean mShowLastDivider = false;
private boolean mPositionInsideItem = false;
public Builder(Context context) {
mContext = context;
mResources = context.getResources();
}
public T paint(final Paint paint) {
return paintProvider(new PaintProvider() {
@Override
public Paint dividerPaint(int position, RecyclerView parent) {
return paint;
}
});
}
public T paintProvider(PaintProvider provider) {
mPaintProvider = provider;
return (T) this;
}
public T color(final int color) {
return colorProvider(new ColorProvider() {
@Override
public int dividerColor(int position, RecyclerView parent) {
return color;
}
});
}
public T colorResId(@ColorRes int colorId) {
return color(ContextCompat.getColor(mContext, colorId));
}
public T colorProvider(ColorProvider provider) {
mColorProvider = provider;
return (T) this;
}
public T drawable(@DrawableRes int id) {
return drawable(ContextCompat.getDrawable(mContext, id));
}
public T drawable(final Drawable drawable) {
return drawableProvider(new DrawableProvider() {
@Override
public Drawable drawableProvider(int position, RecyclerView parent) {
return drawable;
}
});
}
public T drawableProvider(DrawableProvider provider) {
mDrawableProvider = provider;
return (T) this;
}
public T size(final int size) {
return sizeProvider(new SizeProvider() {
@Override
public int dividerSize(int position, RecyclerView parent) {
return size;
}
});
}
public T sizeResId(@DimenRes int sizeId) {
return size(mResources.getDimensionPixelSize(sizeId));
}
public T sizeProvider(SizeProvider provider) {
mSizeProvider = provider;
return (T) this;
}
public T visibilityProvider(VisibilityProvider provider) {
mVisibilityProvider = provider;
return (T) this;
}
public T showLastDivider() {
mShowLastDivider = true;
return (T) this;
}
public T positionInsideItem(boolean positionInsideItem) {
mPositionInsideItem = positionInsideItem;
return (T) this;
}
protected void checkBuilderParams() {
if (mPaintProvider != null) {
if (mColorProvider != null) {
throw new IllegalArgumentException(
"Use setColor method of Paint class to specify line color. Do not provider ColorProvider if you set PaintProvider.");
}
if (mSizeProvider != null) {
throw new IllegalArgumentException(
"Use setStrokeWidth method of Paint class to specify line size. Do not provider SizeProvider if you set PaintProvider.");
}
}
}
}
}
package com.android.mobile.ui.divider;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.annotation.DimenRes;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.RecyclerView;
import android.view.View;
/**
* @author yqritc on 2015/01/08.
*/
public class HorizontalDividerItemDecoration extends FlexibleDividerDecoration {
private MarginProvider mMarginProvider;
protected HorizontalDividerItemDecoration(Builder builder) {
super(builder);
mMarginProvider = builder.mMarginProvider;
}
@Override
protected Rect getDividerBound(int position, RecyclerView parent, View child) {
Rect bounds = new Rect(0, 0, 0, 0);
int transitionX = (int) ViewCompat.getTranslationX(child);
int transitionY = (int) ViewCompat.getTranslationY(child);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
bounds.left = parent.getPaddingLeft() +
mMarginProvider.dividerLeftMargin(position, parent) + transitionX;
bounds.right = parent.getWidth() - parent.getPaddingRight() -
mMarginProvider.dividerRightMargin(position, parent) + transitionX;
int dividerSize = getDividerSize(position, parent);
boolean isReverseLayout = isReverseLayout(parent);
if (mDividerType == DividerType.DRAWABLE) {
// set top and bottom position of divider
if (isReverseLayout) {
bounds.bottom = child.getTop() - params.topMargin + transitionY;
bounds.top = bounds.bottom - dividerSize;
} else {
bounds.top = child.getBottom() + params.bottomMargin + transitionY;
bounds.bottom = bounds.top + dividerSize;
}
} else {
// set center point of divider
int halfSize = dividerSize / 2;
if (isReverseLayout) {
bounds.top = child.getTop() - params.topMargin - halfSize + transitionY;
} else {
bounds.top = child.getBottom() + params.bottomMargin + halfSize + transitionY;
}
bounds.bottom = bounds.top;
}
if (mPositionInsideItem) {
if (isReverseLayout) {
bounds.top += dividerSize;
bounds.bottom += dividerSize;
} else {
bounds.top -= dividerSize;
bounds.bottom -= dividerSize;
}
}
return bounds;
}
@Override
protected void setItemOffsets(Rect outRect, int position, RecyclerView parent) {
if (mPositionInsideItem) {
outRect.set(0, 0, 0, 0);
return;
}
if (isReverseLayout(parent)) {
outRect.set(0, getDividerSize(position, parent), 0, 0);
} else {
outRect.set(0, 0, 0, getDividerSize(position, parent));
}
}
private int getDividerSize(int position, RecyclerView parent) {
if (mPaintProvider != null) {
return (int) mPaintProvider.dividerPaint(position, parent).getStrokeWidth();
} else if (mSizeProvider != null) {
return mSizeProvider.dividerSize(position, parent);
} else if (mDrawableProvider != null) {
Drawable drawable = mDrawableProvider.drawableProvider(position, parent);
return drawable.getIntrinsicHeight();
}
throw new RuntimeException("failed to get size");
}
/**
* Interface for controlling divider margin
*/
public interface MarginProvider {
/**
* Returns left margin of divider.
*
* @param position Divider position (or group index for GridLayoutManager)
* @param parent RecyclerView
* @return left margin
*/
int dividerLeftMargin(int position, RecyclerView parent);
/**
* Returns right margin of divider.
*
* @param position Divider position (or group index for GridLayoutManager)
* @param parent RecyclerView
* @return right margin
*/
int dividerRightMargin(int position, RecyclerView parent);
}
public static class Builder extends FlexibleDividerDecoration.Builder<Builder> {
private MarginProvider mMarginProvider = new MarginProvider() {
@Override
public int dividerLeftMargin(int position, RecyclerView parent) {
return 0;
}
@Override
public int dividerRightMargin(int position, RecyclerView parent) {
return 0;
}
};
public Builder(Context context) {
super(context);
}
public Builder margin(final int leftMargin, final int rightMargin) {
return marginProvider(new MarginProvider() {
@Override
public int dividerLeftMargin(int position, RecyclerView parent) {
return leftMargin;
}
@Override
public int dividerRightMargin(int position, RecyclerView parent) {
return rightMargin;
}
});
}
public Builder margin(int horizontalMargin) {
return margin(horizontalMargin, horizontalMargin);
}
public Builder marginResId(@DimenRes int leftMarginId, @DimenRes int rightMarginId) {
return margin(mResources.getDimensionPixelSize(leftMarginId),
mResources.getDimensionPixelSize(rightMarginId));
}
public Builder marginResId(@DimenRes int horizontalMarginId) {
return marginResId(horizontalMarginId, horizontalMarginId);
}
public Builder marginProvider(MarginProvider provider) {
mMarginProvider = provider;
return this;
}
public HorizontalDividerItemDecoration build() {
checkBuilderParams();
return new HorizontalDividerItemDecoration(this);
}
}
}
package com.android.mobile.ui.divider;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.annotation.DimenRes;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.RecyclerView;
import android.view.View;
/**
* @author yqritc on 2015/01/08.
*/
public class VerticalDividerItemDecoration extends FlexibleDividerDecoration {
private MarginProvider mMarginProvider;
protected VerticalDividerItemDecoration(Builder builder) {
super(builder);
mMarginProvider = builder.mMarginProvider;
}
@Override
protected Rect getDividerBound(int position, RecyclerView parent, View child) {
Rect bounds = new Rect(0, 0, 0, 0);
int transitionX = (int) ViewCompat.getTranslationX(child);
int transitionY = (int) ViewCompat.getTranslationY(child);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
bounds.top = parent.getPaddingTop() +
mMarginProvider.dividerTopMargin(position, parent) + transitionY;
bounds.bottom = parent.getHeight() - parent.getPaddingBottom() -
mMarginProvider.dividerBottomMargin(position, parent) + transitionY;
int dividerSize = getDividerSize(position, parent);
boolean isReverseLayout = isReverseLayout(parent);
if (mDividerType == DividerType.DRAWABLE) {
// set left and right position of divider
if (isReverseLayout) {
bounds.right = child.getLeft() - params.leftMargin + transitionX;
bounds.left = bounds.right - dividerSize;
} else {
bounds.left = child.getRight() + params.rightMargin + transitionX;
bounds.right = bounds.left + dividerSize;
}
} else {
// set center point of divider
int halfSize = dividerSize / 2;
if (isReverseLayout) {
bounds.left = child.getLeft() - params.leftMargin - halfSize + transitionX;
} else {
bounds.left = child.getRight() + params.rightMargin + halfSize + transitionX;
}
bounds.right = bounds.left;
}
if (mPositionInsideItem) {
if (isReverseLayout) {
bounds.left += dividerSize;
bounds.right += dividerSize;
} else {
bounds.left -= dividerSize;
bounds.right -= dividerSize;
}
}
return bounds;
}
@Override
protected void setItemOffsets(Rect outRect, int position, RecyclerView parent) {
if (mPositionInsideItem) {
outRect.set(0, 0, 0, 0);
return;
}
if (isReverseLayout(parent)) {
outRect.set(getDividerSize(position, parent), 0, 0, 0);
} else {
outRect.set(0, 0, getDividerSize(position, parent), 0);
}
}
private int getDividerSize(int position, RecyclerView parent) {
if (mPaintProvider != null) {
return (int) mPaintProvider.dividerPaint(position, parent).getStrokeWidth();
} else if (mSizeProvider != null) {
return mSizeProvider.dividerSize(position, parent);
} else if (mDrawableProvider != null) {
Drawable drawable = mDrawableProvider.drawableProvider(position, parent);
return drawable.getIntrinsicWidth();
}
throw new RuntimeException("failed to get size");
}
/**
* Interface for controlling divider margin
*/
public interface MarginProvider {
/**
* Returns top margin of divider.
*
* @param position Divider position (or group index for GridLayoutManager)
* @param parent RecyclerView
* @return top margin
*/
int dividerTopMargin(int position, RecyclerView parent);
/**
* Returns bottom margin of divider.
*
* @param position Divider position (or group index for GridLayoutManager)
* @param parent RecyclerView
* @return bottom margin
*/
int dividerBottomMargin(int position, RecyclerView parent);
}
public static class Builder extends FlexibleDividerDecoration.Builder<Builder> {
private MarginProvider mMarginProvider = new MarginProvider() {
@Override
public int dividerTopMargin(int position, RecyclerView parent) {
return 0;
}
@Override
public int dividerBottomMargin(int position, RecyclerView parent) {
return 0;
}
};
public Builder(Context context) {
super(context);
}
public Builder margin(final int topMargin, final int bottomMargin) {
return marginProvider(new MarginProvider() {
@Override
public int dividerTopMargin(int position, RecyclerView parent) {
return topMargin;
}
@Override
public int dividerBottomMargin(int position, RecyclerView parent) {
return bottomMargin;
}
});
}
public Builder margin(int verticalMargin) {
return margin(verticalMargin, verticalMargin);
}
public Builder marginResId(@DimenRes int topMarginId, @DimenRes int bottomMarginId) {
return margin(mResources.getDimensionPixelSize(topMarginId),
mResources.getDimensionPixelSize(bottomMarginId));
}
public Builder marginResId(@DimenRes int verticalMarginId) {
return marginResId(verticalMarginId, verticalMarginId);
}
public Builder marginProvider(MarginProvider provider) {
mMarginProvider = provider;
return this;
}
public VerticalDividerItemDecoration build() {
checkBuilderParams();
return new VerticalDividerItemDecoration(this);
}
}
}
package com.android.mobile.ui.fragments;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.android.mobile.R;
import com.android.mobile.ui.LoadingView;
import com.android.mobile.ui.adapters.BaseListAdapter;
import com.android.mobile.util.ApplicationProvider;
import com.android.mobile.util.FocusHandler;
import java.util.Arrays;
import butterknife.BindArray;
import butterknife.BindView;
import butterknife.ButterKnife;
public class AccountFragment extends Fragment implements View.OnKeyListener {
private static final String TAG = AccountFragment.class.getSimpleName();
public static final int ID = 6;
@BindArray(R.array.account_menu_items) public String[] mMenuItems;
@BindView(R.id.menu_recycler_view) public RecyclerView mMenuRecyclerView;
@BindView(R.id.menu_item_details) public FrameLayout mMenuItemDetails;
@BindView(R.id.loading_view) public LoadingView mLoadingView;
public static AccountFragment newInstance(Bundle bundle) {
AccountFragment fragment = new AccountFragment();
if (bundle != null) {
fragment.setArguments(bundle);
}
return fragment;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.account_fragment, container, false);
ButterKnife.bind(this, view);
return view;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mMenuRecyclerView.setHasFixedSize(true);
mMenuRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
mMenuRecyclerView.setAdapter(new BaseListAdapter(getActivity(), Arrays.asList(mMenuItems)));
mMenuRecyclerView.setOnKeyListener(this);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
addFragment();
}
@Override
public void onResume() {
super.onResume();
FocusHandler.setVisible(ID);
}
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
boolean isUnique = event.getRepeatCount() == 0;
int direction = 0;
if (isUnique) {
if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
direction = ApplicationProvider.Account.MOVE_UP;
} else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
direction = ApplicationProvider.Account.MOVE_DOWN;
}
getAdapter().tryMoveSelection(mMenuRecyclerView, direction);
replaceFragment();
}
}
return false;
}
private BaseListAdapter getAdapter() {
return (BaseListAdapter) mMenuRecyclerView.getAdapter();
}
private void replaceFragment() {
getFragmentManager().beginTransaction()
.replace(R.id.menu_item_details, DummyFragment.newInstance(getPackage()))
.setCustomAnimations(R.anim.fade_scale_in, R.anim.fade_scale_out)
.commit();
}
private void addFragment() {
getFragmentManager().beginTransaction()
.add(R.id.menu_item_details, DummyFragment.newInstance(getPackage()))
.setCustomAnimations(R.anim.fade_scale_in, R.anim.fade_scale_out)
.commit();
}
private Bundle getPackage() {
Bundle bundle = new Bundle();
bundle.putInt(DummyFragment.DUMMY_KEY, getAdapter().getFocusedItem());
return bundle;
}
}
package com.android.mobile.ui.fragments;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import com.android.mobile.ApplicationContext;
import com.android.mobile.R;
import com.android.mobile.dependencies.Injector;
import com.android.mobile.util.AnimationUtil;
import com.android.mobile.util.Preferences;
import com.android.mobile.util.Util;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class ChangeSerialFragment extends Fragment {
private static final String TAG = ChangeSerialFragment.class.getSimpleName();
public static final int ID = 2;
@BindView(R.id.serial) public EditText mSerial;
@BindView(R.id.save_serial) public Button mSaveSerial;
@Inject AnimationUtil animationUtil;
public static ChangeSerialFragment newInstance(Bundle bundle) {
ChangeSerialFragment instance = new ChangeSerialFragment();
if (bundle != null) {
instance.setArguments(bundle);
}
return instance;
}
public ChangeSerialFragment() {
Injector.inject(this);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.change_serial_fragment, container, false);
ButterKnife.bind(this, view);
return view;
}
@OnClick(R.id.save_serial) public void saveSerial() {
String serial = mSerial.getText().toString();
if (serial.isEmpty()) {
mSerial.setError(getString(R.string.change_serial_fragment__save_serial));
animationUtil.wobble(mSerial);
return;
}
if (!Util.isValidSerial(serial)) {
mSerial.setError(getString(R.string.change_serial_fragment__serial_is_not_valid));
animationUtil.wobble(mSerial);
return;
}
Preferences.setUserIdentifier(getContext(), serial);
getFragmentManager().beginTransaction()
.replace(R.id.container, CheckSerialFragment.newInstance(null))
.addToBackStack(CheckSerialFragment.class.getSimpleName())
.commit();
}
}
package com.android.mobile.ui.fragments;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.Toast;
import com.android.mobile.PlayerActivity;
import com.android.mobile.R;
import com.android.mobile.api.STBResponse;
import com.android.mobile.api.controller.ChannelController;
import com.android.mobile.database.model.ChannelRecord;
import com.android.mobile.dependencies.Injector;
import com.android.mobile.ui.adapters.GridTrackSelectionAdapter;
import com.android.mobile.ui.divider.HorizontalDividerItemDecoration;
import com.android.mobile.ui.divider.VerticalDividerItemDecoration;
import com.android.mobile.ui.snappysmoothscroller.SnapType;
import com.android.mobile.ui.snappysmoothscroller.SnappyGridLayoutManager;
import com.android.mobile.util.ApplicationProvider;
import com.android.mobile.util.FocusHandler;
import com.android.mobile.util.Util;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
public class ChannelsFragment extends Fragment implements ChannelController.Callback {
private static final String TAG = ChannelsFragment.class.getSimpleName();
public static final int ID = 5;
private ProgressDialog progressDialog;
@BindView(R.id.recycler_view) public RecyclerView recyclerView;
private GridTrackSelectionAdapter adapter;
@Inject ChannelController mChannelController;
public static ChannelsFragment newInstance(Bundle bundle) {
ChannelsFragment instance = new ChannelsFragment();
if (bundle != null) {
instance.setArguments(bundle);
}
return instance;
}
public ChannelsFragment() {
Injector.inject(this);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mChannelController.setCallback(this);
mChannelController.getChannels();
}
public void startPlayerActivity(int id) {
setCurrentPlay(id);
Intent intent = new Intent(getActivity(), PlayerActivity.class);
intent.putExtra(PlayerActivity.CHANNEL_ID_EXTRA, id);
startActivity(intent);
getActivity().overridePendingTransition(R.anim.slide_from_left, R.anim.slide_to_right);
}
private void setCurrentPlay(int id) {
ApplicationProvider.setCurrent(id);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.channels_fragment, container, false);
ButterKnife.bind(this, view);
return view;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
recyclerView.setHasFixedSize(true);
recyclerView.addItemDecoration(new VerticalDividerItemDecoration.Builder(getContext())
.sizeResId(R.dimen.divider_vertical_margin)
.colorResId(android.R.color.transparent)
.build());
recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getContext())
.sizeResId(R.dimen.divider_horizontal_margin)
.colorResId(android.R.color.transparent)
.build());
adapter = new GridTrackSelectionAdapter(getActivity(), null);
SnappyGridLayoutManager snappyGridLayoutManager = new SnappyGridLayoutManager(getContext(), ApplicationProvider.COLUMN_COUNT);
snappyGridLayoutManager.setSnapType(SnapType.CENTER);
snappyGridLayoutManager.setSnapInterpolator(new DecelerateInterpolator());
recyclerView.setLayoutManager(snappyGridLayoutManager);
recyclerView.setAdapter(adapter);
recyclerView.setItemViewCacheSize(32);
recyclerView.setDrawingCacheEnabled(true);
recyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
recyclerView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
recyclerView.getItemAnimator().setChangeDuration(5);
recyclerView.getItemAnimator().setMoveDuration(100);
recyclerView.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
boolean isUnique = event.getRepeatCount() == 0;
if (isUnique) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_RIGHT:
return adapter.tryMoveSelection(recyclerView, ApplicationProvider.MOVE_RIGHT);
case KeyEvent.KEYCODE_DPAD_LEFT:
return adapter.tryMoveSelection(recyclerView, ApplicationProvider.MOVE_LEFT);
case KeyEvent.KEYCODE_DPAD_UP:
return adapter.tryMoveSelection(recyclerView, ApplicationProvider.MOVE_UP);
case KeyEvent.KEYCODE_DPAD_DOWN:
return adapter.tryMoveSelection(recyclerView, ApplicationProvider.MOVE_DOWN);
case KeyEvent.KEYCODE_DPAD_CENTER:
startPlayerActivity(adapter.getFocusedItem());
return true;
}
}
}
return false;
}
});
}
@Override
public void onResume() {
super.onResume();
FocusHandler.setVisible(ID);
}
@Override
public void onDestroy() {
super.onDestroy();
dismiss();
}
@Override
public void onRun() {
progressDialog = ProgressDialog.show(getActivity(), getString(R.string.ChannelsFragment__loading),
getString(R.string.ChannelsFragment__wait_please_while_loading_channels));
progressDialog.setCancelable(false);
}
@Override
public void onSuccess(final STBResponse response) {
// FIXME: It's not smart enough @equa1s
new AsyncTask<Void,Void,List<ChannelRecord>>() {
@Override
protected List<ChannelRecord> doInBackground(Void[] params) {
Map<String, String> data = response.getData();
if (data != null && !data.isEmpty()) {
List<ChannelRecord> remoteRecords = new Gson().fromJson(data.get("channels"), new TypeToken<ArrayList<ChannelRecord>>(){}.getType());
if (remoteRecords != null && !remoteRecords.isEmpty()) {
ApplicationProvider.fillCache(remoteRecords);
return remoteRecords;
}
}
return null;
}
@Override
protected void onPostExecute(final List<ChannelRecord> channels) {
super.onPostExecute(channels);
Util.runOnMainThread(new Runnable() {
@Override
public void run() {
if (adapter != null) {
adapter.swap(channels);
}
}
});
dismiss();
}
}.execute();
}
@Override
public void onFailure(Throwable throwable) {
dismiss();
Toast.makeText(getActivity(), throwable.getMessage().substring(0, 50) + "...", Toast.LENGTH_SHORT).show();
}
private void dismiss() {
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
progressDialog = null;
}
}
}
package com.android.mobile.ui.fragments;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
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.TextView;
import com.android.mobile.ApplicationContext;
import com.android.mobile.MainActivity;
import com.android.mobile.R;
import com.android.mobile.api.STBResponse;
import com.android.mobile.api.controller.AccountController;
import com.android.mobile.api.retrofit.service.AccountService;
import com.android.mobile.dependencies.Injector;
import com.android.mobile.ui.LoadingView;
import com.android.mobile.util.APIUtil;
import com.android.mobile.util.AnimationUtil;
import com.android.mobile.util.Preferences;
import com.android.mobile.util.Dialogs;
import com.android.mobile.util.Util;
import com.android.mobile.util.ViewUtil;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class CheckSerialFragment extends Fragment implements AccountController.Callback {
private static final String TAG = CheckSerialFragment.class.getSimpleName();
public static final int ID = 1;
@BindView(R.id.loading_view) public LoadingView mLoadingView;
@BindView(R.id.check_serial_button) public Button mCheckSerial;
@BindView(R.id.change_serial_button) public Button mChangeSerial;
@BindView(R.id.serial) public TextView mSerial;
@BindView(R.id.content) public LinearLayout mContent;
@Inject AccountController mAccountController;
@Inject AnimationUtil animationUtil;
public static CheckSerialFragment newInstance(Bundle bundle) {
CheckSerialFragment instance = new CheckSerialFragment();
if (bundle != null) {
instance.setArguments(bundle);
}
return instance;
}
public CheckSerialFragment() {
Injector.inject(this);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAccountController.setCallback(this);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.check_serial_fragment, container, false);
ButterKnife.bind(this, view);
return view;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mSerial.setText(Preferences.getUserIdentifier(getContext()));
}
@OnClick(R.id.check_serial_button)
public void checkSerial() {
String serial = mSerial.getText().toString();
if (serial.isEmpty()) {
mSerial.setError(getString(R.string.check_serial_fragment__save_serial));
animationUtil.wobble(mSerial);
return;
}
if (!Util.isValidSerial(serial)) {
mSerial.setError(getString(R.string.check_serial_fragment__serial_is_not_valid));
animationUtil.wobble(mSerial);
return;
}
Map<String, String> params = new HashMap<>();
params.put(AccountService.SERIAL, serial);
mAccountController.setUserIdentifier(params);
}
@OnClick(R.id.change_serial_button)
public void changeSerial() {
getFragmentManager().beginTransaction()
.replace(R.id.container, ChangeSerialFragment.newInstance(null))
.addToBackStack(ChangeSerialFragment.class.getSimpleName())
.commit();
}
@Override
public void onRun() {
setLoading(true);
}
@Override
public void onSuccess(STBResponse response) {
setLoading(false);
Log.d(TAG, "onSuccess: " + response.toString());
Map<String, String> data = response.getData();
String isp = data.get("isp");
ViewUtil.setImageDrawable(getActivity(),
(ImageView) getActivity().findViewById(R.id.logotype),
APIUtil.getISPLogo(isp != null ? isp : "0"));
Preferences.setUserIsp(getContext(), isp);
Preferences.setUserIdentifier(getContext(), mSerial.getText().toString());
Activity activity = getActivity();
startActivity(new Intent(activity, MainActivity.class));
activity.finish();
}
@Override
public void onFailure(Throwable throwable) {
setLoading(false);
Log.d(TAG, "onFailure: " + throwable.getMessage());
Dialogs.showDialog(getContext(),
getString(R.string.check_serial_fragment__something_went_wrong),
throwable.getMessage());
mCheckSerial.requestFocus();
}
private void setLoading(boolean isLoading) {
mLoadingView.setVisibility(isLoading);
mContent.setVisibility(isLoading ? View.GONE : View.VISIBLE);
}
}
package com.android.mobile.ui.fragments;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import com.android.mobile.R;
import com.android.mobile.util.Preferences;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class DeviceNotRegisteredFragment extends Fragment {
private static final String TAG = DeviceNotRegisteredFragment.class.getSimpleName();
public static final int ID = 0;
@BindView(R.id.sign_in_button) public Button mSignIn;
@BindView(R.id.sign_up_button) public Button mSignUp;
@BindView(R.id.change_serial) public Button mChangeSerial;
@BindView(R.id.serial_number) public TextView mSerial;
public static DeviceNotRegisteredFragment newInstance(Bundle bundle) {
DeviceNotRegisteredFragment instance = new DeviceNotRegisteredFragment();
if (bundle != null) {
instance.setArguments(bundle);
}
return instance;
}
public DeviceNotRegisteredFragment() {
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.device_not_registered_fragment, container, false);
ButterKnife.bind(this, view);
return view;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mSerial.setText(Preferences.getUserIdentifier(getContext()));
}
@OnClick(R.id.sign_in_button) public void signIn() {
getFragmentManager().beginTransaction()
.replace(R.id.container, SignInFragment.newInstance(null))
.addToBackStack(SignInFragment.class.getSimpleName())
.commit();
}
@OnClick(R.id.sign_up_button) public void signUp() {
getFragmentManager().beginTransaction()
.replace(R.id.container, SignUpFragment.newInstance(null))
.addToBackStack(SignUpFragment.class.getSimpleName())
.commit();
}
@OnClick(R.id.change_serial) public void changeSerial() {
getFragmentManager().beginTransaction()
.replace(R.id.container, ChangeSerialFragment.newInstance(null))
.addToBackStack(ChangeSerialFragment.class.getSimpleName())
.commit();
}
}
package com.android.mobile.ui.fragments;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.android.mobile.R;
import butterknife.BindView;
import butterknife.ButterKnife;
public class DummyFragment extends Fragment {
private static final String TAG = DummyFragment.class.getSimpleName();
public static final String DUMMY_KEY = "dummy_key";
public int mDummyId;
@BindView(R.id.dummy) public TextView mDummyTextView;
public static DummyFragment newInstance(Bundle bundle) {
DummyFragment fragment = new DummyFragment();
if (bundle != null) {
fragment.setArguments(bundle);
}
return fragment;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.dummy_fragment, container, false);
ButterKnife.bind(this, view);
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mDummyId = getArguments().getInt(DUMMY_KEY);
String text = "Dummy";
switch (mDummyId) {
case 0: text = "One"; break;
case 1: text = "Two"; break;
case 2: text = "Three"; break;
case 3: text = "Four"; break;
case 4: text = "Five"; break;
case 5: text = "Six"; break;
}
mDummyTextView.setText(text);
}
}
package com.android.mobile.ui.fragments;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.android.mobile.ApplicationContext;
import com.android.mobile.MainActivity;
import com.android.mobile.R;
import com.android.mobile.api.STBResponse;
import com.android.mobile.api.controller.AccountController;
import com.android.mobile.dependencies.Injector;
import com.android.mobile.util.AnimationUtil;
import com.android.mobile.util.ApplicationProvider;
import com.android.mobile.util.ApplicationProvider.Menu;
import com.android.mobile.util.FocusHandler;
import com.android.mobile.util.Preferences;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class MenuFragment extends Fragment implements AccountController.Callback {
private static final String TAG = MenuFragment.class.getSimpleName();
private ProgressDialog progressDialog;
@Inject AccountController mAccountController;
@Inject AnimationUtil animation;
private MainActivity mActivity;
@BindView(R.id.channels) public ImageView mChannelsView;
@BindView(R.id.account) public ImageView mAccountView;
@BindView(R.id.preferences) public ImageView mPreferenceView;
@BindView(R.id.log_out) public ImageView mLogOutView;
@BindView(R.id.title) public TextView mMenuTitle;
private int current = 1;
public static MenuFragment newInstance(Bundle bundle) {
MenuFragment fragment = new MenuFragment();
if (bundle != null) {
fragment.setArguments(bundle);
}
return fragment;
}
public MenuFragment() {
Injector.inject(this);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
mActivity = (MainActivity) context;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAccountController.setCallback(this);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.menu_fragment, container, false);
ButterKnife.bind(this, view);
return view;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.requestFocus();
tryMove(Menu.CHANNELS);
view.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
boolean unique = event.getRepeatCount() == 0;
boolean isDown = event.getAction() == KeyEvent.ACTION_DOWN;
if (unique && isDown) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_LEFT:
tryMove(ApplicationProvider.MOVE_LEFT);
return true;
case KeyEvent.KEYCODE_DPAD_RIGHT:
tryMove(ApplicationProvider.MOVE_RIGHT);
return true;
case KeyEvent.KEYCODE_DPAD_CENTER:
performClick();
return true;
}
}
return false;
}
});
}
private void tryMove(int direction) {
int focus = current + direction;
if (focus >= 1 && focus <= 4) {
ImageView view = getViewById(focus);
animation.zoomIn(view);
view = getViewById(current);
animation.zoomOut(view);
current = focus;
}
showMenuTitle();
}
private void showMenuTitle() {
mMenuTitle.setText(getTitleById());
animation.fadeOut(mMenuTitle);
animation.fadeIn(mMenuTitle);
}
private boolean performClick() {
switch (current) {
case Menu.CHANNELS:
onChannelsClick();
return true;
case Menu.ACCOUNT:
onAccountClick();
return true;
case Menu.PREFERENCES:
onPreferencesClick();
return true;
case Menu.LOGOUT:
onLogOutClick();
return true;
}
return false;
}
private String getTitleById() {
switch (current) {
case Menu.CHANNELS:
return getString(R.string.menu__channels);
case Menu.ACCOUNT:
return getString(R.string.menu__private_office);
case Menu.PREFERENCES:
return getString(R.string.menu__preferences);
case Menu.LOGOUT:
return getString(R.string.menu__log_out);
default:
throw new AssertionError("Unknown identifier!");
}
}
private ImageView getViewById(int id) {
switch (id) {
case Menu.CHANNELS:
return mChannelsView;
case Menu.ACCOUNT:
return mAccountView;
case Menu.PREFERENCES:
return mPreferenceView;
case Menu.LOGOUT:
return mLogOutView;
default:
throw new AssertionError("Unknown identifier!");
}
}
private FragmentTransaction getFragmentTransaction() {
FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction();
transaction.setCustomAnimations(R.anim.fade_scale_in, R.anim.fade_scale_out,
R.anim.fade_scale_in, R.anim.fade_scale_out);
return transaction;
}
@Override
public void onResume() {
super.onResume();
FocusHandler.setMenuOpen(true);
}
@Override
public void onDestroy() {
super.onDestroy();
getFragmentManager().popBackStack(MenuFragment.class.getName(),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
FocusHandler.setMenuOpen(false);
}
@Override
public void onDetach() {
super.onDetach();
if (mActivity != null) {
mActivity = null;
}
}
@Override
public void onRun() {
progressDialog = ProgressDialog.show(getActivity(), getString(R.string.MenuFragment__logging_out),
getString(R.string.MenuFragment__wait_please_this_can_take_a_while));
progressDialog.setCancelable(false);
}
@Override
public void onSuccess(STBResponse response) {
logOut();
progressDialog.dismiss();
FocusHandler.setMenuOpen(false);
getFragmentManager().popBackStack(MenuFragment.class.getName(),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
mActivity.startActivity(new Intent(getActivity(), MainActivity.class));
mActivity.overridePendingTransition(R.anim.fade_scale_in, R.anim.fade_scale_out);
}
@Override
public void onFailure(Throwable throwable) {
progressDialog.dismiss();
Toast.makeText(getContext(), R.string.MenuFragment__sorry_something_went_wrong, Toast.LENGTH_SHORT)
.show();
}
@OnClick(R.id.channels)
public void onChannelsClick() {
FocusHandler.setMenuOpen(false);
getFragmentManager().popBackStack(MenuFragment.class.getName(),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
getFragmentTransaction()
.replace(R.id.container, ChannelsFragment.newInstance(null))
.commit();
}
@OnClick(R.id.account)
public void onAccountClick() {
FocusHandler.setMenuOpen(false);
getFragmentManager().popBackStack(MenuFragment.class.getName(),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
getFragmentTransaction()
.replace(R.id.container, AccountFragment.newInstance(null))
.commit();
}
@OnClick(R.id.preferences)
public void onPreferencesClick() {
FocusHandler.setMenuOpen(false);
getFragmentManager().popBackStack(MenuFragment.class.getName(),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
getFragmentTransaction()
.replace(R.id.container, PreferencesFragment.newInstance(null))
.commit();
}
@OnClick(R.id.log_out)
public void onLogOutClick() {
mAccountController.signOut();
}
private void logOut() {
Preferences.setUserLogin(getActivity(), null);
Preferences.setUserPassword(getActivity(), null);
}
}
package com.android.mobile.ui.fragments;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceFragmentCompat;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.android.mobile.R;
import com.android.mobile.util.FocusHandler;
public class PreferencesFragment extends PreferenceFragmentCompat {
public static final int ID = 7;
private static final String PREFERENCES_PARENTAL_CONTROL = "preferences_parental_control";
private static final String PREFERENCES_LANGUAGE = "preferences_language";
private static final String PREFERENCES_DEVICE_INFORMATION = "preferences_device_information";
public static PreferencesFragment newInstance(Bundle bundle) {
PreferencesFragment fragment = new PreferencesFragment();
if (bundle != null) {
fragment.setArguments(bundle);
}
return fragment;
}
public PreferencesFragment() {
}
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.app_preference);
findPreference(PREFERENCES_PARENTAL_CONTROL).setOnPreferenceClickListener(
new CategoryClickListener(PREFERENCES_PARENTAL_CONTROL)
);
findPreference(PREFERENCES_LANGUAGE).setOnPreferenceClickListener(
new CategoryClickListener(PREFERENCES_LANGUAGE)
);
findPreference(PREFERENCES_DEVICE_INFORMATION).setOnPreferenceClickListener(
new CategoryClickListener(PREFERENCES_DEVICE_INFORMATION)
);
}
@Override
public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
RecyclerView view = super.onCreateRecyclerView(inflater, parent, savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
view.setBackgroundColor(getResources().getColor(android.R.color.white, null));
} else {
view.setBackgroundColor(getResources().getColor(android.R.color.white));
}
return view;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void onResume() {
super.onResume();
FocusHandler.setVisible(ID);
}
private class CategoryClickListener implements Preference.OnPreferenceClickListener {
private String category;
public CategoryClickListener(String category) {
this.category = category;
}
@Override
public boolean onPreferenceClick(Preference preference) {
Fragment fragment = null;
switch (category) {
case PREFERENCES_PARENTAL_CONTROL:
fragment = new AccountFragment();
break;
case PREFERENCES_LANGUAGE:
fragment = new AccountFragment();
break;
case PREFERENCES_DEVICE_INFORMATION:
fragment = new AccountFragment();
break;
default:
throw new AssertionError();
}
if (fragment != null) {
FragmentTransaction fragmentTransaction = getActivity()
.getSupportFragmentManager()
.beginTransaction();
fragmentTransaction.replace(R.id.container, fragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
return true;
}
}
}
package com.android.mobile.ui.fragments;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import com.android.mobile.ApplicationContext;
import com.android.mobile.MainActivity;
import com.android.mobile.R;
import com.android.mobile.api.STBResponse;
import com.android.mobile.api.controller.AccountController;
import com.android.mobile.api.retrofit.service.AccountService;
import com.android.mobile.dependencies.Injector;
import com.android.mobile.ui.LoadingView;
import com.android.mobile.util.AnimationUtil;
import com.android.mobile.util.FocusHandler;
import com.android.mobile.util.Preferences;
import com.android.mobile.util.Dialogs;
import com.android.mobile.util.Util;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class SignInFragment extends Fragment implements AccountController.Callback {
private static final String TAG = SignInFragment.class.getSimpleName();
public static final int ID = 3;
@BindView(R.id.sign_in) public Button mSignIn;
@BindView(R.id.email) public EditText mEmail;
@BindView(R.id.password) public EditText mPassword;
@BindView(R.id.content) public LinearLayout mContent;
@BindView(R.id.loading_view) public LoadingView mLoadingView;
@Inject AnimationUtil mAnimationUtil;
@Inject AccountController mAccountController;
public static SignInFragment newInstance(Bundle bundle) {
SignInFragment instance = new SignInFragment();
if (bundle != null) {
instance.setArguments(bundle);
}
return instance;
}
public SignInFragment() {
Injector.inject(this);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAccountController.setCallback(this);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.sign_in_fragment, container, false);
ButterKnife.bind(this, view);
return view;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
FocusHandler.setVisible(ID);
mEmail.requestFocus();
}
@Override
public void onRun() {
setLoading(true);
}
@Override
public void onSuccess(STBResponse response) {
setLoading(false);
Log.d(TAG, "onSuccess: " + response.toString());
if (response.isSuccessful()) {
Activity activity = getActivity();
Preferences.setUserLogin(getContext(), mEmail.getText().toString());
Preferences.setUserPassword(getContext(), mPassword.getText().toString());
startActivity(new Intent(activity, MainActivity.class));
activity.finish();
}
}
@Override
public void onFailure(Throwable throwable) {
setLoading(false);
Log.d(TAG, "onFailure: " + throwable.getMessage());
Dialogs.showDialog(getContext(),
getString(R.string.sign_in_fragment__something_went_wrong),
throwable.getMessage());
mEmail.requestFocus();
}
@OnClick(R.id.sign_in) public void signIn() {
String email = mEmail.getText().toString();
String password = mPassword.getText().toString();
if (email.isEmpty()) {
mEmail.setError(getString(R.string.sign_in_fragment__please_fill_this_field));
mAnimationUtil.wobble(mEmail);
return;
}
if (!Util.isValidEmail(email)) {
mEmail.setError(getString(R.string.sign_in_fragment__please_enter_an_email));
mAnimationUtil.wobble(mEmail);
return;
}
if (password.isEmpty()) {
mPassword.setError(getString(R.string.sign_in_fragment__please_fill_this_field));
mAnimationUtil.wobble(mPassword);
return;
}
if (!Util.isValidPassword(password)) {
mPassword.setError(getString(R.string.sign_in_fragment__please_enter_a_password));
mAnimationUtil.wobble(mPassword);
return;
}
Map<String, String> params = new HashMap<>();
params.put(AccountService.EMAIL, email);
params.put(AccountService.PASSWORD, Util.md5(password));
params.put(AccountService.SERIAL, Preferences.getUserIdentifier(getContext()));
mAccountController.signIn(params);
}
private void setLoading(boolean isLoading) {
mLoadingView.setVisibility(isLoading);
mContent.setVisibility(isLoading ? View.GONE : View.VISIBLE);
}
}
package com.android.mobile.ui.fragments;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import com.android.mobile.ApplicationContext;
import com.android.mobile.MainActivity;
import com.android.mobile.R;
import com.android.mobile.api.STBResponse;
import com.android.mobile.api.controller.AccountController;
import com.android.mobile.api.retrofit.service.AccountService;
import com.android.mobile.dependencies.Injector;
import com.android.mobile.ui.LoadingView;
import com.android.mobile.util.AnimationUtil;
import com.android.mobile.util.Preferences;
import com.android.mobile.util.Dialogs;
import com.android.mobile.util.Util;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class SignUpFragment extends Fragment implements AccountController.Callback {
private static final String TAG = SignUpFragment.class.getSimpleName();
public static final int ID = 4;
@BindView(R.id.email) public EditText mEmail;
@BindView(R.id.password) public EditText mPassword;
@BindView(R.id.password_repeat) public EditText mPasswordRepeat;
@BindView(R.id.sign_up) public Button mSignUp;
@BindView(R.id.content) public LinearLayout mContent;
@BindView(R.id.loading_view) public LoadingView mLoadingView;
@Inject AnimationUtil mAnimationUtil;
@Inject AccountController mAccountController;
public static SignUpFragment newInstance(Bundle bundle) {
SignUpFragment instance = new SignUpFragment();
if (bundle != null) {
instance.setArguments(bundle);
}
return instance;
}
public SignUpFragment() {
Injector.inject(this);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAccountController.setCallback(this);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.sign_up_fragment, container, false);
ButterKnife.bind(this, view);
return view;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mEmail.requestFocus();
}
@Override
public void onRun() {
setLoading(true);
}
@Override
public void onSuccess(STBResponse response) {
setLoading(false);
if (response.isSuccessful()) {
Activity activity = getActivity();
Preferences.setUserLogin(getContext(), mEmail.getText().toString());
Preferences.setUserPassword(getContext(), mPassword.getText().toString());
startActivity(new Intent(activity, MainActivity.class));
activity.finish();
}
}
@Override
public void onFailure(Throwable throwable) {
setLoading(false);
Dialogs.showDialog(getContext(),
getString(R.string.sign_up_fragment__something_went_wrong),
throwable.getMessage());
mEmail.requestFocus();
}
@OnClick(R.id.sign_up) public void signUp() {
String email = mEmail.getText().toString();
String password = mPassword.getText().toString();
String repeatPassword = mPasswordRepeat.getText().toString();
if (email.isEmpty()) {
mEmail.setError(getString(R.string.sign_up_fragment__please_fill_this_field));
mAnimationUtil.wobble(mEmail);
return;
}
if (!Util.isValidEmail(email)) {
mEmail.setError(getString(R.string.sign_up_fragment__please_enter_an_email));
mAnimationUtil.wobble(mEmail);
return;
}
if (password.isEmpty()) {
mPassword.setError(getString(R.string.sign_up_fragment__please_fill_this_field));
mAnimationUtil.wobble(mPassword);
return;
}
if (!Util.isValidPassword(password)) {
mPassword.setError(getString(R.string.sign_up_fragment__please_enter_a_password));
mAnimationUtil.wobble(mPassword);
return;
}
if (repeatPassword.isEmpty()) {
mPasswordRepeat.setError(getString(R.string.sign_up_fragment__please_fill_this_field));
mAnimationUtil.wobble(mPasswordRepeat);
return;
}
if (!repeatPassword.equals(password)) {
mPasswordRepeat.setError(getString(R.string.sign_up_fragment__enter_correct_password));
mAnimationUtil.wobble(mPasswordRepeat);
return;
}
Map<String, String> params = new HashMap<>();
params.put(AccountService.EMAIL, email);
params.put(AccountService.PASSWORD, Util.md5(password));
params.put(AccountService.SERIAL, Preferences.getUserIdentifier(getContext()));
mAccountController.signUp(params);
}
private void setLoading(boolean isLoading) {
mLoadingView.setVisibility(isLoading);
mContent.setVisibility(isLoading ? View.GONE : View.VISIBLE);
}
}
package com.android.mobile.ui.snappysmoothscroller;
import android.graphics.PointF;
import android.support.v7.widget.LinearLayoutManager;
public class LinearLayoutScrollVectorDetector implements SnappySmoothScroller.ScrollVectorDetector {
private LinearLayoutManager layoutManager;
public LinearLayoutScrollVectorDetector(LinearLayoutManager layoutManager) {
this.layoutManager = layoutManager;
}
@Override
public PointF computeScrollVectorForPosition(int targetPosition) {
return layoutManager.computeScrollVectorForPosition(targetPosition);
}
}
package com.android.mobile.ui.snappysmoothscroller;
public enum SnapType {
START,
END,
CENTER,
VISIBLE
}
package com.android.mobile.ui.snappysmoothscroller;
import android.content.Context;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.animation.Interpolator;
public class SnappyGridLayoutManager extends GridLayoutManager implements SnappyLayoutManager {
private SnappySmoothScroller.Builder builder;
public SnappyGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
public SnappyGridLayoutManager(Context context, int spanCount) {
super(context, spanCount);
init();
}
public SnappyGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
super(context, spanCount, orientation, reverseLayout);
init();
}
private void init() {
builder = new SnappySmoothScroller.Builder();
}
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
SnappySmoothScroller scroller = builder
.setPosition(position)
.setScrollVectorDetector(new LinearLayoutScrollVectorDetector(this))
.build(recyclerView.getContext());
startSmoothScroll(scroller);
}
@Override
public void setSnapType(SnapType snapType) {
builder.setSnapType(snapType);
}
@Override
public void setSnapDuration(int snapDuration) {
builder.setSnapDuration(snapDuration);
}
@Override
public void setSnapInterpolator(Interpolator snapInterpolator) {
builder.setSnapInterpolator(snapInterpolator);
}
@Override
public void setSnapPadding(int snapPadding) {
builder.setSnapPadding(snapPadding);
}
@Override
public void setSnapPaddingStart(int snapPaddingStart) {
builder.setSnapPaddingStart(snapPaddingStart);
}
@Override
public void setSnapPaddingEnd(int snapPaddingEnd) {
builder.setSnapPaddingEnd(snapPaddingEnd);
}
@Override
public void setSeekDuration(int seekDuration) {
builder.setSeekDuration(seekDuration);
}
}
package com.android.mobile.ui.snappysmoothscroller;
import android.view.animation.Interpolator;
public interface SnappyLayoutManager {
void setSnapType(SnapType snapType);
void setSnapDuration(int snapDuration);
void setSnapInterpolator(Interpolator snapInterpolator);
void setSnapPadding(int snapPadding);
void setSnapPaddingStart(int snapPaddingStart);
void setSnapPaddingEnd(int snapPaddingEnd);
void setSeekDuration(int seekDuration);
}
package com.android.mobile.ui.snappysmoothscroller;
import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.animation.Interpolator;
public class SnappyLinearLayoutManager extends LinearLayoutManager implements SnappyLayoutManager {
private SnappySmoothScroller.Builder builder;
public SnappyLinearLayoutManager(Context context) {
super(context);
init();
}
public SnappyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
init();
}
public SnappyLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init() {
builder = new SnappySmoothScroller.Builder();
}
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
SnappySmoothScroller scroller = builder
.setPosition(position)
.setScrollVectorDetector(new LinearLayoutScrollVectorDetector(this))
.build(recyclerView.getContext());
startSmoothScroll(scroller);
}
@Override
public void setSnapType(SnapType snapType) {
builder.setSnapType(snapType);
}
@Override
public void setSnapDuration(int snapDuration) {
builder.setSnapDuration(snapDuration);
}
@Override
public void setSnapInterpolator(Interpolator snapInterpolator) {
builder.setSnapInterpolator(snapInterpolator);
}
@Override
public void setSnapPadding(int snapPadding) {
builder.setSnapPadding(snapPadding);
}
@Override
public void setSnapPaddingStart(int snapPaddingStart) {
builder.setSnapPaddingStart(snapPaddingStart);
}
@Override
public void setSnapPaddingEnd(int snapPaddingEnd) {
builder.setSnapPaddingEnd(snapPaddingEnd);
}
@Override
public void setSeekDuration(int seekDuration) {
builder.setSeekDuration(seekDuration);
}
}
package com.android.mobile.ui.snappysmoothscroller;
import android.content.Context;
import android.graphics.PointF;
import android.support.v7.widget.LinearSmoothScroller;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
public class SnappySmoothScroller extends LinearSmoothScroller {
private static final SeekDistance INVALID_SEEK_DISTANCE = new SeekDistance(0, 0);
private static final int SEEK_MIN_DURATION = 10000;
private static final int DEFAULT_SEEK_SCROLL_DURATION = 500;
private static final int DEFAULT_SNAP_DURATION = 600;
private SnapType snapType = SnapType.VISIBLE;
private ScrollVectorDetector scrollVectorDetector;
private Interpolator snapInterpolator = new DecelerateInterpolator();
private int snapDuration = DEFAULT_SNAP_DURATION;
private int seekDuration = DEFAULT_SEEK_SCROLL_DURATION;
private SeekDistance seekDistance;
private int snapPaddingStart;
private int snapPaddingEnd;
public static class Builder {
private SnapType snapType;
private Interpolator snapInterpolator;
private int snapDuration = -1;
private int seekDuration = -1;
private int snapPaddingStart;
private int snapPaddingEnd;
private int position;
private ScrollVectorDetector scrollVectorDetector;
public Builder setSnapType(SnapType snapType) {
this.snapType = snapType;
return this;
}
public Builder setSnapDuration(int snapDuration) {
this.snapDuration = snapDuration;
return this;
}
public Builder setSnapInterpolator(Interpolator snapInterpolator) {
this.snapInterpolator = snapInterpolator;
return this;
}
public Builder setSnapPadding(int snapPadding) {
this.snapPaddingStart = snapPadding;
this.snapPaddingEnd = snapPadding;
return this;
}
public Builder setSnapPaddingStart(int snapPaddingStart) {
this.snapPaddingStart = snapPaddingStart;
return this;
}
public Builder setSnapPaddingEnd(int snapPaddingEnd) {
this.snapPaddingEnd = snapPaddingEnd;
return this;
}
public Builder setSeekDuration(int seekDuration) {
this.seekDuration = seekDuration;
return this;
}
public Builder setPosition(int position) {
this.position = position;
return this;
}
public Builder setScrollVectorDetector(ScrollVectorDetector scrollVectorDetector) {
this.scrollVectorDetector = scrollVectorDetector;
return this;
}
public SnappySmoothScroller build(Context context) {
SnappySmoothScroller scroller = new SnappySmoothScroller(context);
scroller.setTargetPosition(position);
if (scrollVectorDetector != null) {
scroller.setScrollVectorDetector(scrollVectorDetector);
}
if (snapType != null) {
scroller.setSnapType(snapType);
}
if (snapDuration >= 0) {
scroller.setSnapDuration(snapDuration);
}
if (snapInterpolator != null) {
scroller.setSnapInterpolator(snapInterpolator);
}
if (seekDuration >= 0) {
scroller.setSeekDuration(seekDuration);
}
scroller.setSnapPaddingStart(snapPaddingStart);
scroller.setSnapPaddingEnd(snapPaddingEnd);
return scroller;
}
}
public interface ScrollVectorDetector {
PointF computeScrollVectorForPosition(int targetPosition);
}
private static class SeekDistance {
public final float distanceInPixels;
public final float duration;
private SeekDistance(float distanceInPixels, float duration) {
this.distanceInPixels = distanceInPixels;
this.duration = duration;
}
}
public SnappySmoothScroller(Context context) {
super(context);
}
public void setSnapType(SnapType snapType) {
this.snapType = snapType;
}
public void setSnapDuration(int snapDuration) {
this.snapDuration = snapDuration;
}
public void setScrollVectorDetector(ScrollVectorDetector scrollVectorDetector) {
this.scrollVectorDetector = scrollVectorDetector;
}
public void setSeekDuration(int seekDuration) {
this.seekDuration = seekDuration;
}
public void setSnapInterpolator(Interpolator snapIterpolator) {
snapInterpolator = snapIterpolator;
}
public void setSnapPaddingStart(int snapPaddingStart) {
this.snapPaddingStart = snapPaddingStart;
}
public void setSnapPaddingEnd(int snapPaddingEnd) {
this.snapPaddingEnd = snapPaddingEnd;
}
@Override
protected void onTargetFound(View targetView, RecyclerView.State state, Action action) {
final int dx = calculateDxToMakeVisible(targetView, getHorizontalSnapPreference());
final int dy = calculateDyToMakeVisible(targetView, getVerticalSnapPreference());
action.update(-dx, -dy, snapDuration, snapInterpolator);
}
@Override
protected void onSeekTargetStep(int dx, int dy, RecyclerView.State state, Action action) {
if (seekDistance == null) {
computeSeekDistance();
}
super.onSeekTargetStep(dx, dy, state, action);
}
private void computeSeekDistance() {
RecyclerView.LayoutManager layoutManager = getLayoutManager();
if (layoutManager != null
&& layoutManager.getChildCount() > 0
&& layoutManager.getItemCount() > 0
&& (layoutManager.canScrollHorizontally() || layoutManager.canScrollVertically())) {
int currentPosition = layoutManager.getPosition(layoutManager.getChildAt(0));
int totalWidth = 0;
int totalHeight = 0;
final int count = layoutManager.getChildCount();
for (int i = 0; i < count; i++) {
View child = layoutManager.getChildAt(i);
totalWidth += child.getWidth();
totalHeight += child.getHeight();
}
int distanceX = 0;
if (layoutManager.canScrollHorizontally()) {
final int averageWidth = totalWidth / count;
distanceX = Math.abs((currentPosition - getTargetPosition()) * averageWidth);
}
int distanceY = 0;
if (layoutManager.canScrollVertically()) {
final int averageHeight = totalHeight / count;
distanceY = Math.abs((currentPosition - getTargetPosition()) * averageHeight);
}
final int distanceInPixels = (int) Math.sqrt(distanceX * distanceX + distanceY * distanceY);
if (distanceInPixels > SEEK_MIN_DURATION) {
seekDistance = new SeekDistance(distanceInPixels, seekDuration);
}
}
if (seekDistance == null) {
seekDistance = INVALID_SEEK_DISTANCE;
}
}
@Override
public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int snapPreference) {
switch (snapType) {
case START:
return boxStart - viewStart + snapPaddingStart;
case END:
return boxEnd - viewEnd - snapPaddingEnd;
case CENTER:
final int boxDistance = boxEnd - boxStart;
final int viewDistance = viewEnd - viewStart;
return (boxDistance - viewDistance) / 2 - viewStart + boxStart;
case VISIBLE:
final int dtStart = boxStart - viewStart + snapPaddingStart;
if (dtStart > 0) {
return dtStart;
}
final int dtEnd = boxEnd - viewEnd - snapPaddingEnd;
if (dtEnd < 0) {
return dtEnd;
}
return 0;
default:
return super.calculateDtToFit(viewStart, viewEnd, boxStart, boxEnd, snapPreference);
}
}
@Override
public int calculateDxToMakeVisible(View view, int snapPreference) {
int dx = super.calculateDxToMakeVisible(view, snapPreference);
if (dx == 0) {
return dx;
}
switch (snapType) {
case START:
dx = adjustDxForLeft(dx);
break;
case END:
dx = adjustDxForRight(dx);
break;
case CENTER:
if (dx > 0) {
dx = adjustDxForRight(dx);
} else {
dx = adjustDxForLeft(dx);
}
break;
}
return dx;
}
@Override
public int calculateDyToMakeVisible(View view, int snapPreference) {
int dy = super.calculateDyToMakeVisible(view, snapPreference);
if (dy == 0) {
return dy;
}
switch (snapType) {
case START:
dy = adjustDyForUp(dy);
break;
case END:
dy = adjustDyForDown(dy);
break;
case CENTER:
if (dy > 0) {
dy = adjustDyForDown(dy);
} else {
dy = adjustDyForUp(dy);
}
break;
case VISIBLE:
}
return dy;
}
private int adjustDxForLeft(int dx) {
final RecyclerView.LayoutManager layoutManager = getLayoutManager();
if (layoutManager == null || !layoutManager.canScrollHorizontally()) {
return 0;
}
final View lastChild = layoutManager.getChildAt(layoutManager.getChildCount() - 1);
final int position = layoutManager.getPosition(lastChild);
if (position == layoutManager.getItemCount() - 1) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) lastChild.getLayoutParams();
final int maxDx = layoutManager.getWidth() - layoutManager.getPaddingRight()
- (layoutManager.getDecoratedRight(lastChild) + params.rightMargin);
if (dx < maxDx) {
return maxDx;
}
}
return dx;
}
private int adjustDxForRight(int dx) {
final RecyclerView.LayoutManager layoutManager = getLayoutManager();
if (layoutManager == null || !layoutManager.canScrollHorizontally()) {
return 0;
}
final View firstChild = layoutManager.getChildAt(0);
final int position = layoutManager.getPosition(firstChild);
if (position == 0) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) firstChild.getLayoutParams();
final int maxDx = -(layoutManager.getDecoratedLeft(firstChild) - params.leftMargin) + layoutManager.getPaddingLeft();
if (dx > maxDx) {
return maxDx;
}
}
return dx;
}
private int adjustDyForUp(int dy) {
final RecyclerView.LayoutManager layoutManager = getLayoutManager();
if (layoutManager == null || !layoutManager.canScrollVertically()) {
return 0;
}
final View lastChild = layoutManager.getChildAt(layoutManager.getChildCount() - 1);
final int position = layoutManager.getPosition(lastChild);
if (position == layoutManager.getItemCount() - 1) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) lastChild.getLayoutParams();
final int maxDy = layoutManager.getHeight() - layoutManager.getPaddingBottom()
- (layoutManager.getDecoratedBottom(lastChild) + params.bottomMargin);
if (dy < maxDy) {
return maxDy;
}
}
return dy;
}
private int adjustDyForDown(int dy) {
final RecyclerView.LayoutManager layoutManager = getLayoutManager();
if (layoutManager == null || !layoutManager.canScrollVertically()) {
return 0;
}
final View firstChild = layoutManager.getChildAt(0);
final int position = layoutManager.getPosition(firstChild);
if (position == 0) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) firstChild.getLayoutParams();
final int maxDy = -(layoutManager.getDecoratedTop(firstChild) - params.topMargin) + layoutManager.getPaddingTop();
if (dy > maxDy) {
return maxDy;
}
}
return dy;
}
@Override
public PointF computeScrollVectorForPosition(int targetPosition) {
if (scrollVectorDetector != null) {
return scrollVectorDetector.computeScrollVectorForPosition(targetPosition);
} else {
return null;
}
}
@Override
protected int calculateTimeForScrolling(int dx) {
if (seekDistance != null && seekDistance != INVALID_SEEK_DISTANCE) {
float proportion = (float) dx / seekDistance.distanceInPixels;
int time = (int) (seekDistance.duration * proportion);
if (time > 0) {
return time;
}
}
return super.calculateTimeForScrolling(dx);
}
}
package com.android.mobile.ui.snappysmoothscroller;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.util.AttributeSet;
import android.view.animation.Interpolator;
public class SnappyStaggeredGridLayoutManager extends StaggeredGridLayoutManager implements SnappyLayoutManager {
private SnappySmoothScroller.Builder builder;
public SnappyStaggeredGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
public SnappyStaggeredGridLayoutManager(int spanCount, int orientation) {
super(spanCount, orientation);
init();
}
private void init() {
builder = new SnappySmoothScroller.Builder();
}
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
SnappySmoothScroller scroller = builder
.setPosition(position)
.setScrollVectorDetector(new StaggeredGridLayoutScrollVectorDetector(this))
.build(recyclerView.getContext());
startSmoothScroll(scroller);
}
@Override
public void setSnapType(SnapType snapType) {
builder.setSnapType(snapType);
}
@Override
public void setSnapDuration(int snapDuration) {
builder.setSnapDuration(snapDuration);
}
@Override
public void setSnapInterpolator(Interpolator snapInterpolator) {
builder.setSnapInterpolator(snapInterpolator);
}
@Override
public void setSnapPadding(int snapPadding) {
builder.setSnapPadding(snapPadding);
}
@Override
public void setSnapPaddingStart(int snapPaddingStart) {
builder.setSnapPaddingStart(snapPaddingStart);
}
@Override
public void setSnapPaddingEnd(int snapPaddingEnd) {
builder.setSnapPaddingEnd(snapPaddingEnd);
}
@Override
public void setSeekDuration(int seekDuration) {
builder.setSeekDuration(seekDuration);
}
}
package com.android.mobile.ui.snappysmoothscroller;
import android.graphics.PointF;
import android.support.v7.widget.StaggeredGridLayoutManager;
public class StaggeredGridLayoutScrollVectorDetector implements SnappySmoothScroller.ScrollVectorDetector {
final static int LAYOUT_START = -1;
final static int LAYOUT_END = 1;
private StaggeredGridLayoutManager layoutManager;
public StaggeredGridLayoutScrollVectorDetector(StaggeredGridLayoutManager layoutManager) {
this.layoutManager = layoutManager;
}
private int getFirstChildPosition() {
final int childCount = layoutManager.getChildCount();
return childCount == 0 ? 0 : layoutManager.getPosition(layoutManager.getChildAt(0));
}
private int calculateScrollDirectionForPosition(int position) {
if (layoutManager.getChildCount() == 0) {
return layoutManager.getReverseLayout() ? LAYOUT_END : LAYOUT_START;
}
final int firstChildPos = getFirstChildPosition();
return position < firstChildPos != layoutManager.getReverseLayout() ? LAYOUT_START : LAYOUT_END;
}
@Override
public PointF computeScrollVectorForPosition(int targetPosition) {
final int direction = calculateScrollDirectionForPosition(targetPosition);
if (direction == 0) {
return null;
}
if (layoutManager.getOrientation() == StaggeredGridLayoutManager.HORIZONTAL) {
return new PointF(direction, 0);
} else {
return new PointF(0, direction);
}
}
}
package com.android.mobile.util;
import com.android.mobile.R;
import java.util.Map;
public class APIUtil {
// TODO: test values
private static final String MICROSOFT = "0";
private static final String ORACLE = "1";
private static final String PAY_PAL = "2";
public static boolean isResponseOK(Map<String, String> response) {
String status = response.get("status_code");
return status != null && status.equalsIgnoreCase("200");
}
public static boolean hasISP(Map<String, String> response) {
String isp = response.get("isp");
return isp != null && !isp.isEmpty();
}
public static int getISPLogo(String isp) {
switch (isp) {
case MICROSOFT:
return R.drawable.microsoft;
case ORACLE:
return R.drawable.oracle;
case PAY_PAL:
return R.drawable.paypal;
}
return R.drawable.google;
}
}
package com.android.mobile.util;
import android.view.View;
import com.android.mobile.ui.animation.Fade;
import com.android.mobile.ui.animation.Zoom;
import com.daimajia.androidanimations.library.YoYo;
import com.daimajia.androidanimations.library.attention.WobbleAnimator;
public class AnimationUtil {
private Zoom zoom;
private WobbleAnimator wobble;
private Fade fade;
public AnimationUtil() {
zoom = new Zoom();
wobble = new WobbleAnimator();
fade = new Fade();
}
public void zoomIn(View view) {
zoom.zoomIn(view);
}
public void zoomOut(View view) {
zoom.zoomOut(view);
}
public void wobble(View view) {
YoYo.with(wobble).playOn(view);
}
public void fadeIn(View... views) {
for (View v : views) {
fade.fadeIn(v);
}
}
public void fadeOut(View... views) {
for (View v : views) {
fade.fadeOut(v);
}
}
public Zoom getZoom() {
return zoom;
}
public WobbleAnimator getWobble() {
return wobble;
}
public Fade getFade() {
return fade;
}
}
package com.android.mobile.util;
import com.android.mobile.database.model.ChannelRecord;
import java.util.ArrayList;
import java.util.List;
public class ApplicationProvider {
public static final int COLUMN_COUNT = 3;
public static final int MOVE_UP = -COLUMN_COUNT;
public static final int MOVE_DOWN = +COLUMN_COUNT;
public static final int MOVE_LEFT = -1;
public static final int MOVE_RIGHT = +1;
public static final int HOLD = 0;
public interface Menu {
int CHANNELS = 1;
int ACCOUNT = 2;
int PREFERENCES = 3;
int LOGOUT = 4;
}
public interface Account {
int MOVE_UP = -1;
int MOVE_DOWN = +1;
}
private static Integer CURRENT = 0;
private static List<ChannelRecord> CHANNELS = new ArrayList<>();
public static void fillCache(List<ChannelRecord> channels) {
CHANNELS = channels;
}
public static List<ChannelRecord> getChannels() {
return CHANNELS;
}
public static ChannelRecord getById(int id) throws IllegalArgumentException {
if (id < 0) {
throw new IllegalArgumentException();
}
return CHANNELS.get(id);
}
public static void setCurrent(int current) {
CURRENT = current;
}
public static Integer getCurrent() {
return CURRENT;
}
public static ChannelRecord getChannel(String title) {
for (ChannelRecord channel : CHANNELS) {
if (channel.getTitle().equals(title)) {
return channel;
}
}
return null;
}
public static ChannelRecord getChannel(ChannelRecord channel) {
for (ChannelRecord channel1 : CHANNELS) {
if (channel1.equals(channel)) {
return channel1;
}
}
return null;
}
public static int size() {
return CHANNELS.size();
}
public static int lastIndex() {
return size() - 1;
}
}
package com.android.mobile.util;
import android.app.AlertDialog;
import android.content.Context;
public class Dialogs {
public static void showDialog(Context context, String title, String message) {
new AlertDialog.Builder(context)
.setCancelable(true)
.setTitle(title)
.setMessage(message)
.create()
.show();
}
}
package com.android.mobile.util;
public class FocusHandler {
public static int DEFAULT = -1;
private static int visible = DEFAULT;
private static boolean isMenuOpen = false;
public static void setVisible(int id) {
visible = id;
}
public static void setMenuOpen(boolean open) {
isMenuOpen = open;
}
public static boolean isMenuOpen() {
return isMenuOpen;
}
public static boolean isVisible(int id) {
return id == visible;
}
public static int getVisible() {
return visible;
}
}
package com.android.mobile.util;
public class Hex {
public static String byteArrayToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder(bytes.length * 2);
for (byte element : bytes) {
int v = element & 0xff;
if (v < 16) {
sb.append('0');
}
sb.append(Integer.toHexString(v));
}
return sb.toString();
}
public static byte[] hexStringToByteArray(String hexString) {
int len = hexString.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character
.digit(hexString.charAt(i + 1), 16));
}
return data;
}
}
package com.android.mobile.util;
import android.content.Context;
import android.os.Build;
import android.preference.PreferenceManager;
public class Preferences {
private static final String TAG = Preferences.class.getSimpleName();
private static final String USER_LOGIN = "pref_user_login";
private static final String USER_PASS = "pref_user_pass";
private static final String USER_IDENTIFIER = "pref_user_identifier";
private static final String USER_ISP = "pref_user_isp";
public static String getUserIsp(Context context) {
return getStringPreference(context, USER_ISP, "default");
}
public static void setUserIsp(Context context, String isp) {
setStringPreference(context, USER_ISP, isp);
}
public static void setUserIdentifier(Context context, String identifier) {
setStringPreference(context, USER_IDENTIFIER, identifier);
}
public static String getUserIdentifier(Context context) {
return getStringPreference(context, USER_IDENTIFIER, Build.SERIAL);
}
public static void setUserLogin(Context context, String login) {
setStringPreference(context, USER_LOGIN, login);
}
public static String getUserLogin(Context context) {
return getStringPreference(context, USER_LOGIN, null);
}
public static void setUserPassword(Context context, String pass) {
setStringPreference(context, USER_PASS, pass);
}
public static String getUserPassword(Context context) {
return getStringPreference(context, USER_PASS, null);
}
public static void setBooleanPreference(Context context, String key, boolean value) {
PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(key, value).apply();
}
public static boolean getBooleanPreference(Context context, String key, boolean defaultValue) {
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(key, defaultValue);
}
public static void setStringPreference(Context context, String key, String value) {
PreferenceManager.getDefaultSharedPreferences(context).edit().putString(key, value).apply();
}
public static String getStringPreference(Context context, String key, String defaultValue) {
return PreferenceManager.getDefaultSharedPreferences(context).getString(key, defaultValue);
}
private static int getIntegerPreference(Context context, String key, int defaultValue) {
return PreferenceManager.getDefaultSharedPreferences(context).getInt(key, defaultValue);
}
private static void setIntegerPrefrence(Context context, String key, int value) {
PreferenceManager.getDefaultSharedPreferences(context).edit().putInt(key, value).apply();
}
private static boolean setIntegerPrefrenceBlocking(Context context, String key, int value) {
return PreferenceManager.getDefaultSharedPreferences(context).edit().putInt(key, value).commit();
}
private static long getLongPreference(Context context, String key, long defaultValue) {
return PreferenceManager.getDefaultSharedPreferences(context).getLong(key, defaultValue);
}
private static void setLongPreference(Context context, String key, long value) {
PreferenceManager.getDefaultSharedPreferences(context).edit().putLong(key, value).apply();
}
}
package com.android.mobile.util;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.util.DisplayMetrics;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Util {
public static Handler handler = new Handler(Looper.getMainLooper());
private static final Pattern PASSWORD_PATTERN = Pattern.compile("[a-zA-Z_0-9]{6,16}");
private static final Pattern EMAIL_PATTERN = android.util.Patterns.EMAIL_ADDRESS;
private static final Pattern SERIAL_PATTERN = Pattern.compile("[A-Z]{0,3}[0-9]{5,30}");
public static boolean isValidPassword(String password) {
Matcher matcher = PASSWORD_PATTERN.matcher(password);
return matcher.matches();
}
public static boolean isValidEmail(String email) {
Matcher matcher = EMAIL_PATTERN.matcher(email);
return matcher.matches();
}
public static boolean isValidSerial(String serial) {
Matcher matcher = SERIAL_PATTERN.matcher(serial);
return matcher.matches();
}
public static int calculateNoOfColumns(Context context, int width) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
float dpWidth = displayMetrics.widthPixels / displayMetrics.density;
return (int) (dpWidth / width);
}
public static String md5(String input) {
return getHash("MD5", input);
}
private static String getHash(String alg, String input) {
MessageDigest algorithm = getAlgorithm(alg);
if (algorithm != null) {
try {
return Hex.byteArrayToHexString(algorithm.digest(input.getBytes("UTF-8")));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
}
return null;
}
public static void runOnMainThread(@NonNull Runnable runnable) {
if (isMainThread()) {
runnable.run();
} else {
handler.post(runnable);
}
}
public static boolean isMainThread() {
return Looper.myLooper() == Looper.getMainLooper();
}
private static MessageDigest getAlgorithm(String algo) {
try {
return MessageDigest.getInstance(algo);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
}
package com.android.mobile.util;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.ColorRes;
import android.support.annotation.NonNull;
import android.view.View;
import android.view.animation.Animation;
import android.widget.ImageView;
import com.android.mobile.R;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
public class ViewUtil {
public static void setBackgroundDrawable(@NonNull final View view, Drawable drawable) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
view.setBackground(drawable);
} else {
view.setBackgroundDrawable(drawable);
}
}
public static void loadImage(@NonNull Context context, @NonNull final ImageView view, String url) {
Glide.with(context).load(url)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.animate(android.R.anim.fade_in)
.placeholder(R.drawable.no_image_available)
.into(view);
}
public static void setBackgroundColor(@NonNull Context context, @NonNull View view, @ColorRes int color) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
view.setBackgroundColor(context.getResources().getColor(color, null));
} else {
view.setBackgroundColor(context.getResources().getColor(color));
}
}
public static void setImageDrawable(Context context, ImageView imageView, int res) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
imageView.setImageDrawable(context.getResources().getDrawable(res, null));
} else {
imageView.setImageDrawable(context.getResources().getDrawable(res));
}
}
public static void animateIn(final @NonNull View view, final @NonNull Animation animation) {
if (view.getVisibility() == View.VISIBLE) return;
view.clearAnimation();
animation.reset();
animation.setStartTime(0);
view.setVisibility(View.VISIBLE);
view.startAnimation(animation);
}
public static boolean isVisible(@NonNull View view) {
return view.getVisibility() == View.VISIBLE;
}
}
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/decelerate_interpolator">
<scale
android:duration="500"
android:fromXScale="0"
android:fromYScale="0"
android:toXScale="1.0"
android:toYScale="1.0"
android:pivotX="50%"
android:pivotY="50%" />
<alpha
android:duration="500"
android:fromAlpha="0"
android:toAlpha="1.0" />
</set>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/decelerate_interpolator">
<scale
android:duration="500"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale="0"
android:toYScale="0"
android:pivotX="50%"
android:pivotY="50%" />
<alpha
android:duration="500"
android:fromAlpha="1.0"
android:toAlpha="0" />
</set>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/decelerate_interpolator">
<translate
android:duration="500"
android:fromXDelta="0%"
android:toXDelta="100%" />
</set>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<alpha android:duration="1"
android:fromAlpha="0"
android:toAlpha="1"/>
<translate
android:duration="250"
android:fromYDelta="-100%"
android:toYDelta="0%" />
</set>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/decelerate_interpolator">
<translate
android:duration="500"
android:fromXDelta="0%"
android:toXDelta="100%" />
</set>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="250"
android:fromYDelta="0%"
android:toYDelta="-100%" />
</set>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="@color/button_pressed_background_color" />
<item android:state_focused="true"
android:drawable="@color/button_on_focus_background_color" />
<item android:drawable="@color/button_background_color" />
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="@color/edit_text_background_color"/>
<corners android:radius="1dp"/>
</shape>
</item>
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient android:startColor="@color/grey_900"
android:centerColor="@color/grey_600"
android:endColor="@color/grey_400_transparent"
android:angle="270"
android:type="linear"/>
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true">
<shape android:shape="rectangle">
<corners android:radius="5dp" />
<solid android:color="@color/blue_600" />
</shape>
</item>
<item android:state_pressed="true">
<shape android:shape="rectangle">
<corners android:radius="5dp" />
<solid android:color="@color/blue_600" />
</shape>
</item>
<item>
<shape android:shape="rectangle">
<corners android:radius="5dp" />
<solid android:color="@android:color/white" />
</shape>
</item>
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false"
android:background="@color/grey_800">
<android.support.v7.widget.RecyclerView
android:id="@+id/menu_recycler_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="1" />
<LinearLayout android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"
android:gravity="center"
android:orientation="horizontal">
<FrameLayout android:id="@+id/menu_item_details"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/grey_500" />
<com.android.mobile.ui.LoadingView
android:id="@+id/loading_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone" />
</LinearLayout>
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView android:id="@+id/item"
android:layout_width="match_parent"
android:layout_height="70dp"
android:fontFamily="sans-serif-condensed"
android:gravity="center"
android:padding="5dp"
android:textAllCaps="true"
android:textColor="@android:color/white"
android:textSize="30sp"
tools:text="One item" />
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/grey_700"
android:fitsSystemWindows="true" />
<?xml version="1.0" encoding="utf-8"?>
<com.android.mobile.ui.BaseCardView 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:orientation="vertical"
android:layout_width="@dimen/base_card_width"
android:layout_height="@dimen/base_card_height"
android:layout_gravity="center"
app:cardElevation="5dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView android:id="@+id/poster"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/white"
android:src="@drawable/no_image_available"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_above="@+id/container" />
<RelativeLayout android:id="@+id/container"
android:layout_width="match_parent"
android:background="@color/black_transparent"
android:layout_gravity="bottom"
android:padding="5dp"
android:layout_height="70dp"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true">
<TextView android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginTop="5dp"
android:fontFamily="sans-serif-condensed"
android:maxLines="1"
tools:text="No description"
android:textColor="#ffffff"
android:textStyle="normal"
android:textSize="25sp" />
<TextView android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:fontFamily="sans-serif-condensed"
android:maxLines="1"
android:textColor="#ffffff"
android:textSize="30sp"
android:textStyle="bold"
tools:text="1+1" />
</RelativeLayout>
</RelativeLayout>
</com.android.mobile.ui.BaseCardView>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:padding="5dp">
<TextView
android:layout_width="600dp"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-condensed"
android:gravity="center"
android:text="@string/change_serial_fragment__please_enter_serial_number_from_the_bottom_of_your_stb"
android:textColor="@android:color/white"
android:textSize="40sp"
android:textStyle="normal" />
<EditText
android:id="@+id/serial"
style="@style/AppTheme.EditText"
android:layout_width="500dp"
android:layout_height="@dimen/input_height"
android:layout_marginTop="15dp"
android:fontFamily="sans-serif-condensed"
android:inputType="text"
android:textAllCaps="true" />
<Button
android:id="@+id/save_serial"
style="@style/AppTheme.Button"
android:layout_width="500dp"
android:layout_height="@dimen/button_height"
android:layout_margin="15dp"
android:fontFamily="sans-serif-condensed"
android:text="@string/change_serial_fragment__save"
android:textStyle="bold" />
</LinearLayout>
</ScrollView>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_gravity="center"
android:gravity="center">
<android.support.v7.widget.RecyclerView android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:clipChildren="false"
android:clipToPadding="false"
android:layout_marginStart="7dp"
android:layout_marginLeft="7dp"
android:focusable="true"
android:focusableInTouchMode="true"
android:paddingRight="20dp"
android:paddingLeft="20dp"
android:paddingTop="30dp"
android:paddingBottom="30dp"/>
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center"
android:gravity="center"
android:padding="5dp">
<com.android.mobile.ui.LoadingView android:id="@+id/loading_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:color_filter="@android:color/white"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center"
android:gravity="center"
android:id="@+id/content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-condensed"
android:gravity="center"
android:text="@string/check_serial_fragment__please_check_your_serial_number"
android:textColor="@android:color/white"
android:textSize="40sp"
android:textStyle="normal" />
<TextView
android:id="@+id/serial"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="10dp"
android:fontFamily="sans-serif-condensed"
android:gravity="center"
android:textAllCaps="true"
android:textColor="@color/serial_number_color"
android:textSize="40sp"
android:textStyle="bold"
tools:text="IUY3244231231" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="500dp"
android:layout_height="70dp"
android:layout_marginTop="10dp">
<Button
android:id="@+id/check_serial_button"
style="@style/AppTheme.Button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="5dp"
android:layout_marginRight="5dp"
android:layout_weight="1"
android:fontFamily="sans-serif-condensed"
android:text="@string/check_serial_fragment__correct"
android:textStyle="bold" />
<Button
android:id="@+id/change_serial_button"
style="@style/AppTheme.Button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="5dp"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:fontFamily="sans-serif-condensed"
android:text="@string/check_serial_fragment__change"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="5dp">
<TextView
android:text="@string/device_not_registered_fragment__your_device_is_not_registered"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="35sp"
android:textColor="@android:color/white"
android:fontFamily="sans-serif-condensed"
android:gravity="center"
android:textStyle="bold" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="500dp"
android:layout_height="70dp"
android:layout_marginTop="20dp">
<Button
android:id="@+id/sign_in_button"
style="@style/AppTheme.Button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="5dp"
android:layout_marginRight="5dp"
android:layout_weight="1"
android:fontFamily="sans-serif-condensed"
android:textStyle="bold"
android:text="@string/device_not_registered_fragment__sign_in" />
<Button
android:id="@+id/sign_up_button"
style="@style/AppTheme.Button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="5dp"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:textStyle="bold"
android:fontFamily="sans-serif-condensed"
android:text="@string/device_not_registered_fragment__sign_up" />
</LinearLayout>
<TextView
android:id="@+id/serial_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAllCaps="true"
android:layout_marginTop="10dp"
android:fontFamily="sans-serif-condensed"
android:textStyle="bold"
android:textColor="@color/green_600"
style="@style/AppTheme.TextLabel"/>
<Button
android:id="@+id/change_serial"
style="@style/AppTheme.Button"
android:layout_width="500dp"
android:layout_height="70dp"
android:layout_marginTop="10dp"
android:fontFamily="sans-serif-condensed"
android:text="@string/device_not_registered_fragment__change_serial"
android:textStyle="bold" />
</LinearLayout>
</ScrollView>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/dummy"
android:clickable="true"
android:textStyle="bold"
android:background="@color/grey_500"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:textColor="@color/green_200"
android:textSize="36sp"
tools:text="This is dummy!)" />
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:background="@color/create_identifier_activity_background_color">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center"
android:gravity="center"
android:layout_marginBottom="50dp">
<ImageView android:id="@+id/logotype"
android:layout_width="150dp"
android:layout_height="50dp"
android:layout_gravity="center_horizontal" />
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/create_identifier_activity_navigation_background_color"
android:padding="20dp"
android:gravity="end"
android:layout_gravity="bottom"
android:orientation="horizontal">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_reply_white_48dp"
android:padding="5dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:fontFamily="sans-serif-condensed"
android:gravity="center"
android:padding="5dp"
android:text="@string/identify_user_activity__back"
android:textColor="@android:color/white"
android:textSize="26sp"
android:textStyle="bold" />
</LinearLayout>
</FrameLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:background="@drawable/gradient_background"
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="80dp"
android:elevation="5dp"
android:fontFamily="sans-serif-condensed"
android:paddingLeft="75dp"
android:paddingRight="75dp"
android:textColor="@android:color/white"
android:textSize="52sp"
android:textStyle="bold"
tools:text="CHANNELS" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal"
android:paddingLeft="25dp"
android:paddingRight="25dp"
android:clipChildren="true">
<ImageView android:id="@+id/channels"
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_margin="15dp"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true"
android:clipToPadding="false"
android:clipChildren="false"
android:src="@drawable/icon_channels" />
<ImageView android:id="@+id/account"
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_margin="15dp"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true"
android:clipToPadding="false"
android:clipChildren="false"
android:src="@drawable/icon_account" />
<ImageView android:id="@+id/preferences"
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_margin="15dp"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true"
android:clipToPadding="false"
android:clipChildren="false"
android:src="@drawable/icon_preferences" />
<ImageView android:id="@+id/log_out"
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_margin="15dp"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true"
android:clipToPadding="false"
android:clipChildren="false"
android:src="@drawable/icon_logout" />
</LinearLayout>
</LinearLayout>
<?xml version="1.0" encoding="UTF-8"?>
<RelativeLayout 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:id="@+id/root"
android:focusable="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true">
<RelativeLayout android:id="@+id/video_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center">
<SurfaceView android:id="@+id/surface_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"/>
<com.android.mobile.ui.LoadingView android:id="@+id/progress_bar"
android:layout_width="75dp"
android:layout_height="75dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
app:color_filter="@android:color/white" />
</RelativeLayout>
<com.android.mobile.ui.AutoHideView android:id="@+id/quick_channel_panel"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
app:isAnimate="true"
app:time="3000">
<LinearLayout android:id="@+id/player_header"
android:layout_width="match_parent"
android:layout_height="100dp"
android:orientation="horizontal"
android:layout_gravity="top"
android:padding="5dp"
android:background="#87000000">
<TextView android:id="@+id/video_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="40sp"
android:textColor="@android:color/white"
tools:text="How to catch a caterpillar?"
android:gravity="center_vertical"
android:layout_gravity="center_vertical"
android:layout_marginLeft="15dp"
android:layout_marginStart="15dp"
android:fontFamily="sans-serif-condensed"
android:textStyle="bold"
android:textAllCaps="true" />
</LinearLayout>
<android.support.v7.widget.RecyclerView android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:clipChildren="false"
android:clipToPadding="false"
android:smoothScrollbar="true"
android:layout_marginStart="7dp"
android:layout_marginLeft="7dp"
android:layout_marginBottom="5dp"
android:focusable="true"
android:focusableInTouchMode="true"
android:padding="20dp" />
</com.android.mobile.ui.AutoHideView>
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<com.android.mobile.ui.ScalableCardView 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:id="@+id/card_view"
android:layout_height="@dimen/base_card_height"
android:layout_width="@dimen/base_card_width"
android:layout_gravity="center"
app:cardElevation="5dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/poster"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitCenter"
android:background="@android:color/white"
android:src="@drawable/no_image_available"
android:layout_above="@+id/container" />
<RelativeLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="73dp"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_gravity="bottom"
android:background="@color/black_transparent"
android:gravity="center_vertical"
android:orientation="vertical"
android:padding="5dp">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:fontFamily="sans-serif-condensed"
android:maxLines="1"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:textColor="@android:color/white"
android:textSize="30sp"
android:textStyle="bold"
tools:text="1+1" />
<TextView
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:fontFamily="sans-serif-condensed"
android:maxLines="1"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:textColor="@android:color/white"
android:textSize="25sp"
tools:text="No description" />
</RelativeLayout>
</RelativeLayout>
</com.android.mobile.ui.ScalableCardView>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="center"
android:isScrollContainer="true"
android:layout_gravity="center"
android:padding="5dp">
<com.android.mobile.ui.LoadingView android:id="@+id/loading_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:color_filter="@android:color/white" />
<LinearLayout
android:id="@+id/content"
android:orientation="vertical"
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center">
<EditText
android:id="@+id/email"
android:layout_width="@dimen/input_width"
android:layout_height="@dimen/input_height"
android:padding="5dp"
android:layout_margin="10dp"
android:inputType="textEmailAddress"
android:textSize="@dimen/input_text_size"
android:hint="@string/sign_in_fragment__email"
android:gravity="center"
android:maxLines="1"
android:fontFamily="sans-serif-condensed"
style="@style/AppTheme.EditText" />
<EditText
android:id="@+id/password"
android:layout_width="@dimen/input_width"
android:layout_height="@dimen/input_height"
android:layout_margin="10dp"
android:padding="5dp"
android:inputType="textPassword"
android:textSize="@dimen/input_text_size"
android:maxLines="1"
android:hint="@string/sign_in_fragment__password"
android:gravity="center"
android:fontFamily="sans-serif-condensed"
style="@style/AppTheme.EditText" />
<Button
android:id="@+id/sign_in"
android:layout_width="@dimen/button_width"
android:layout_height="@dimen/button_height"
android:text="@string/sign_in_fragment__sign_in"
android:textColor="@color/default_text_color"
android:textSize="@dimen/button_text_size"
android:layout_margin="10dp"
android:fontFamily="sans-serif-condensed"
android:textStyle="bold"
style="@style/AppTheme.Button"/>
</LinearLayout>
</LinearLayout>
</ScrollView>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="center"
android:isScrollContainer="true"
android:layout_gravity="center"
android:padding="5dp">
<com.android.mobile.ui.LoadingView android:id="@+id/loading_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:color_filter="@android:color/white" />
<LinearLayout android:id="@+id/content"
android:orientation="vertical"
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center">
<EditText
android:id="@+id/email"
style="@style/AppTheme.EditText"
android:layout_width="@dimen/input_width"
android:layout_height="@dimen/input_height"
android:layout_margin="10dp"
android:fontFamily="sans-serif-condensed"
android:gravity="center"
android:hint="@string/sign_up_fragment__email"
android:inputType="textEmailAddress"
android:maxLines="1"
android:padding="5dp"
android:textSize="@dimen/input_text_size" />
<EditText
android:id="@+id/password"
style="@style/AppTheme.EditText"
android:layout_width="@dimen/input_width"
android:layout_height="@dimen/input_height"
android:layout_margin="10dp"
android:fontFamily="sans-serif-condensed"
android:gravity="center"
android:hint="@string/sign_up_fragment__password"
android:inputType="textPassword"
android:maxLines="1"
android:padding="5dp"
android:textSize="@dimen/input_text_size" />
<EditText
android:id="@+id/password_repeat"
style="@style/AppTheme.EditText"
android:layout_width="@dimen/input_width"
android:layout_height="@dimen/input_height"
android:layout_margin="10dp"
android:fontFamily="sans-serif-condensed"
android:gravity="center"
android:hint="@string/sign_up_fragment__and_repeat_it"
android:inputType="textPassword"
android:maxLines="1"
android:padding="5dp"
android:textSize="@dimen/input_text_size" />
<Button
android:id="@+id/sign_up"
style="@style/AppTheme.Button"
android:layout_width="@dimen/button_width"
android:layout_height="@dimen/button_height"
android:layout_margin="5dp"
android:fontFamily="sans-serif-condensed"
android:text="@string/sign_up_fragment__sign_up"
android:textColor="@color/default_text_color"
android:textSize="@dimen/button_text_size"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
</ScrollView>
\ No newline at end of file
<resources>
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
(such as screen margins) for screens with more than 820dp of available width. This
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
<dimen name="activity_horizontal_margin">64dp</dimen>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="AutoHideView">
<attr name="isAnimate" format="boolean"/>
<attr name="time" format="integer"/>
</declare-styleable>
<declare-styleable name="LoadingView">
<attr name="color_filter" format="color"/>
</declare-styleable>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="primary">@android:color/holo_blue_light</color>
<color name="primary_dark">@android:color/holo_blue_dark</color>
<color name="accent">#d41b4d5d</color>
<color name="item_background_light">#ffeeeeee</color>
<color name="item_background_dark">#ff333333</color>
<color name="item_background_shadow_light">#ffd5d5d5</color>
<color name="item_background_shadow_dark">#ff222222</color>
<color name="touch_highlight_light">#400099cc</color>
<color name="touch_highlight_dark">#40ffffff</color>
<color name="primary_pressed">#75edff</color>
<color name="ripple">#400099cc</color>
<color name="start_gradient">#83000000</color>
<color name="end_gradient">#01606060</color>
<color name="default_text_color">#ffffff</color>
<color name="selected_background">#7c55a2ff</color>
<color name="selected_background_grad">#30ffaa3f</color>
<color name="selected_text_color">#0096a6</color>
<color name="channel_fragment_background">#393939</color>
<color name="black_transparent">@color/grey_600</color>
<color name="selected_channel">@color/primary</color>
<color name="unselected_channel">#616161</color>
<color name="create_identifier_activity_background_color">#424242</color>
<color name="edit_text_hint_unfocus_color">#ad5c5c5c</color>
<color name="edit_text_hint_focus_color">@color/primary</color>
<!-- button's state colors -->
<color name="button_background_color">#a6a6a6</color>
<color name="button_on_focus_background_color">@color/primary</color>
<color name="button_pressed_background_color">#0088b5</color>
<!-- edit text's colors -->
<color name="edit_text_background_color">#FFFFFF</color>
<!-- loading view background color -->
<color name="loading_background_color">#96000000</color>
<color name="serial_number_color">#4caf50</color>
<color name="create_identifier_activity_navigation_background_color">#323232</color>
</resources>
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="nav_header_vertical_spacing">10dp</dimen>
<dimen name="input_height">70dp</dimen>
<dimen name="input_width">500dp</dimen>
<dimen name="button_height">70dp</dimen>
<dimen name="button_width">500dp</dimen>
<dimen name="input_text_size">25sp</dimen>
<dimen name="button_text_size">25sp</dimen>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">10dp</dimen>
<dimen name="activity_vertical_margin">10dp</dimen>
<dimen name="fab_margin">16dp</dimen>
<dimen name="min_card_item_height">200dp</dimen>
<dimen name="min_card_item_width">100dp</dimen>
<dimen name="divider">10dp</dimen>
<dimen name="divider_vertical_margin">15dp</dimen>
<dimen name="divider_horizontal_margin">15dp</dimen>
<dimen name="main_activity__navigation_view_item_text_size">30sp</dimen>
<dimen name="main_activity__navigation_view_text_size">25sp</dimen>
<dimen name="main_activity__nav_header_height">250dp</dimen>
<dimen name="base_card_width">400dp</dimen>
<dimen name="base_card_height">270dp</dimen>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="red_50">#FFEBEE</color>
<color name="red_100">#FFCDD2</color>
<color name="red_200">#EF9A9A</color>
<color name="red_300">#E57373</color>
<color name="red_400">#EF5350</color>
<color name="red_500">#F44336</color>
<color name="red_600">#E53935</color>
<color name="red_700">#D32F2F</color>
<color name="red_800">#C62828</color>
<color name="red_900">#B71C1C</color>
<color name="red_A100">#FF8A80</color>
<color name="red_A200">#FF5252</color>
<color name="red_A400">#FF1744</color>
<color name="red_A700">#D50000</color>
<color name="deep_purple_50">#EDE7F6</color>
<color name="deep_purple_100">#D1C4E9</color>
<color name="deep_purple_200">#B39DDB</color>
<color name="deep_purple_300">#9575CD</color>
<color name="deep_purple_400">#7E57C2</color>
<color name="deep_purple_500">#673AB7</color>
<color name="deep_purple_600">#5E35B1</color>
<color name="deep_purple_700">#512DA8</color>
<color name="deep_purple_800">#4527A0</color>
<color name="deep_purple_900">#311B92</color>
<color name="deep_purple_A100">#B388FF</color>
<color name="deep_purple_A200">#7C4DFF</color>
<color name="deep_purple_A400">#651FFF</color>
<color name="deep_purple_A700">#6200EA</color>
<color name="light_blue_50">#E1F5FE</color>
<color name="light_blue_100">#B3E5FC</color>
<color name="light_blue_200">#81D4FA</color>
<color name="light_blue_300">#4FC3F7</color>
<color name="light_blue_400">#29B6F6</color>
<color name="light_blue_500">#03A9F4</color>
<color name="light_blue_600">#039BE5</color>
<color name="light_blue_700">#0288D1</color>
<color name="light_blue_800">#0277BD</color>
<color name="light_blue_900">#01579B</color>
<color name="light_blue_A100">#80D8FF</color>
<color name="light_blue_A200">#40C4FF</color>
<color name="light_blue_A400">#00B0FF</color>
<color name="light_blue_A700">#0091EA</color>
<color name="green_50">#E8F5E9</color>
<color name="green_100">#C8E6C9</color>
<color name="green_200">#A5D6A7</color>
<color name="green_300">#81C784</color>
<color name="green_400">#66BB6A</color>
<color name="green_500">#4CAF50</color>
<color name="green_600">#43A047</color>
<color name="green_700">#388E3C</color>
<color name="green_800">#2E7D32</color>
<color name="green_900">#1B5E20</color>
<color name="green_A100">#B9F6CA</color>
<color name="green_A200">#69F0AE</color>
<color name="green_A400">#00E676</color>
<color name="green_A700">#00C853</color>
<color name="yellow_50">#FFFDE7</color>
<color name="yellow_100">#FFF9C4</color>
<color name="yellow_200">#FFF59D</color>
<color name="yellow_300">#FFF176</color>
<color name="yellow_400">#FFEE58</color>
<color name="yellow_500">#FFEB3B</color>
<color name="yellow_600">#FDD835</color>
<color name="yellow_700">#FBC02D</color>
<color name="yellow_800">#F9A825</color>
<color name="yellow_900">#F57F17</color>
<color name="yellow_A100">#FFFF8D</color>
<color name="yellow_A200">#FFFF00</color>
<color name="yellow_A400">#FFEA00</color>
<color name="yellow_A700">#FFD600</color>
<color name="deep_orange_50">#FBE9E7</color>
<color name="deep_orange_100">#FFCCBC</color>
<color name="deep_orange_200">#FFAB91</color>
<color name="deep_orange_300">#FF8A65</color>
<color name="deep_orange_400">#FF7043</color>
<color name="deep_orange_500">#FF5722</color>
<color name="deep_orange_600">#F4511E</color>
<color name="deep_orange_700">#E64A19</color>
<color name="deep_orange_800">#D84315</color>
<color name="deep_orange_900">#BF360C</color>
<color name="deep_orange_A100">#FF9E80</color>
<color name="deep_orange_A200">#FF6E40</color>
<color name="deep_orange_A400">#FF3D00</color>
<color name="deep_orange_A700">#DD2C00</color>
<color name="blue_grey_50">#ECEFF1</color>
<color name="blue_grey_100">#CFD8DC</color>
<color name="blue_grey_200">#B0BEC5</color>
<color name="blue_grey_300">#90A4AE</color>
<color name="blue_grey_400">#78909C</color>
<color name="blue_grey_500">#607D8B</color>
<color name="blue_grey_600">#546E7A</color>
<color name="blue_grey_700">#455A64</color>
<color name="blue_grey_800">#37474F</color>
<color name="blue_grey_900">#263238</color>
<color name="pink_50">#FCE4EC</color>
<color name="pink_100">#F8BBD0</color>
<color name="pink_200">#F48FB1</color>
<color name="pink_300">#F06292</color>
<color name="pink_400">#EC407A</color>
<color name="pink_500">#E91E63</color>
<color name="pink_600">#D81B60</color>
<color name="pink_700">#C2185B</color>
<color name="pink_800">#AD1457</color>
<color name="pink_900">#880E4F</color>
<color name="pink_A100">#FF80AB</color>
<color name="pink_A200">#FF4081</color>
<color name="pink_A400">#F50057</color>
<color name="pink_A700">#C51162</color>
<color name="indigo_50">#E8EAF6</color>
<color name="indigo_100">#C5CAE9</color>
<color name="indigo_200">#9FA8DA</color>
<color name="indigo_300">#7986CB</color>
<color name="indigo_400">#5C6BC0</color>
<color name="indigo_500">#3F51B5</color>
<color name="indigo_600">#3949AB</color>
<color name="indigo_700">#303F9F</color>
<color name="indigo_800">#283593</color>
<color name="indigo_900">#1A237E</color>
<color name="indigo_A100">#8C9EFF</color>
<color name="indigo_A200">#536DFE</color>
<color name="indigo_A400">#3D5AFE</color>
<color name="indigo_A700">#304FFE</color>
<color name="cyan_50">#E0F7FA</color>
<color name="cyan_100">#B2EBF2</color>
<color name="cyan_200">#80DEEA</color>
<color name="cyan_300">#4DD0E1</color>
<color name="cyan_400">#26C6DA</color>
<color name="cyan_500">#00BCD4</color>
<color name="cyan_600">#00ACC1</color>
<color name="cyan_700">#0097A7</color>
<color name="cyan_800">#00838F</color>
<color name="cyan_900">#006064</color>
<color name="cyan_A100">#84FFFF</color>
<color name="cyan_A200">#18FFFF</color>
<color name="cyan_A400">#00E5FF</color>
<color name="cyan_A700">#00B8D4</color>
<color name="light_green_50">#F1F8E9</color>
<color name="light_green_100">#DCEDC8</color>
<color name="light_green_200">#C5E1A5</color>
<color name="light_green_300">#AED581</color>
<color name="light_green_400">#9CCC65</color>
<color name="light_green_500">#8BC34A</color>
<color name="light_green_600">#7CB342</color>
<color name="light_green_700">#689F38</color>
<color name="light_green_800">#558B2F</color>
<color name="light_green_900">#33691E</color>
<color name="light_green_A100">#CCFF90</color>
<color name="light_green_A200">#B2FF59</color>
<color name="light_green_A400">#76FF03</color>
<color name="light_green_A700">#64DD17</color>
<color name="amber_50">#FFF8E1</color>
<color name="amber_100">#FFECB3</color>
<color name="amber_200">#FFE082</color>
<color name="amber_300">#FFD54F</color>
<color name="amber_400">#FFCA28</color>
<color name="amber_500">#FFC107</color>
<color name="amber_600">#FFB300</color>
<color name="amber_700">#FFA000</color>
<color name="amber_800">#FF8F00</color>
<color name="amber_900">#FF6F00</color>
<color name="amber_A100">#FFE57F</color>
<color name="amber_A200">#FFD740</color>
<color name="amber_A400">#FFC400</color>
<color name="amber_A700">#FFAB00</color>
<color name="brown_50">#EFEBE9</color>
<color name="brown_100">#D7CCC8</color>
<color name="brown_200">#BCAAA4</color>
<color name="brown_300">#A1887F</color>
<color name="brown_400">#8D6E63</color>
<color name="brown_500">#795548</color>
<color name="brown_600">#6D4C41</color>
<color name="brown_700">#5D4037</color>
<color name="brown_800">#4E342E</color>
<color name="brown_900">#3E2723</color>
<color name="purple_50">#F3E5F5</color>
<color name="purple_100">#E1BEE7</color>
<color name="purple_200">#CE93D8</color>
<color name="purple_300">#BA68C8</color>
<color name="purple_400">#AB47BC</color>
<color name="purple_500">#9C27B0</color>
<color name="purple_600">#8E24AA</color>
<color name="purple_700">#7B1FA2</color>
<color name="purple_800">#6A1B9A</color>
<color name="purple_900">#4A148C</color>
<color name="purple_A100">#EA80FC</color>
<color name="purple_A200">#E040FB</color>
<color name="purple_A400">#D500F9</color>
<color name="purple_A700">#AA00FF</color>
<color name="blue_50">#E3F2FD</color>
<color name="blue_100">#BBDEFB</color>
<color name="blue_200">#90CAF9</color>
<color name="blue_300">#64B5F6</color>
<color name="blue_400">#42A5F5</color>
<color name="blue_500">#2196F3</color>
<color name="blue_600">#1E88E5</color>
<color name="blue_700">#1976D2</color>
<color name="blue_800">#1565C0</color>
<color name="blue_900">#0D47A1</color>
<color name="blue_A100">#82B1FF</color>
<color name="blue_A200">#448AFF</color>
<color name="blue_A400">#2979FF</color>
<color name="blue_A700">#2962FF</color>
<color name="teal_50">#E0F2F1</color>
<color name="teal_100">#B2DFDB</color>
<color name="teal_200">#80CBC4</color>
<color name="teal_300">#4DB6AC</color>
<color name="teal_400">#26A69A</color>
<color name="teal_500">#009688</color>
<color name="teal_600">#00897B</color>
<color name="teal_700">#00796B</color>
<color name="teal_800">#00695C</color>
<color name="teal_900">#004D40</color>
<color name="teal_A100">#A7FFEB</color>
<color name="teal_A200">#64FFDA</color>
<color name="teal_A400">#1DE9B6</color>
<color name="teal_A700">#00BFA5</color>
<color name="lime_50">#F9FBE7</color>
<color name="lime_100">#F0F4C3</color>
<color name="lime_200">#E6EE9C</color>
<color name="lime_300">#DCE775</color>
<color name="lime_400">#D4E157</color>
<color name="lime_500">#CDDC39</color>
<color name="lime_600">#C0CA33</color>
<color name="lime_700">#AFB42B</color>
<color name="lime_800">#9E9D24</color>
<color name="lime_900">#827717</color>
<color name="lime_A100">#F4FF81</color>
<color name="lime_A200">#EEFF41</color>
<color name="lime_A400">#C6FF00</color>
<color name="lime_A700">#AEEA00</color>
<color name="orange_50">#FFF3E0</color>
<color name="orange_100">#FFE0B2</color>
<color name="orange_200">#FFCC80</color>
<color name="orange_300">#FFB74D</color>
<color name="orange_400">#FFA726</color>
<color name="orange_500">#FF9800</color>
<color name="orange_600">#FB8C00</color>
<color name="orange_700">#F57C00</color>
<color name="orange_800">#EF6C00</color>
<color name="orange_900">#E65100</color>
<color name="orange_A100">#FFD180</color>
<color name="orange_A200">#FFAB40</color>
<color name="orange_A400">#FF9100</color>
<color name="orange_A700">#FF6D00</color>
<color name="grey_50">#FAFAFA</color>
<color name="grey_100">#F5F5F5</color>
<color name="grey_200">#EEEEEE</color>
<color name="grey_300">#E0E0E0</color>
<color name="grey_400">#BDBDBD</color>
<color name="grey_500">#9E9E9E</color>
<color name="grey_600">#757575</color>
<color name="grey_700">#616161</color>
<color name="grey_800">#424242</color>
<color name="grey_900">#212121</color>
<color name="grey_400_transparent">#44BDBDBD</color>
</resources>
\ No newline at end of file
<resources xmlns:xliff="http://schemas.android.com/tools">
<string name="app_name">IP Television</string>
<!-- PlayerActivity.java -->
<string name="player_activity__error_drm_not_supported">Protected content not supported on API levels below 18</string>
<string name="player_activity__error_drm_unsupported_scheme">This device does not support the required DRM scheme</string>
<string name="player_activity__error_drm_unknown">An unknown DRM error occurred</string>
<string name="player_activity__error_no_decoder">This device does not provide a decoder for <xliff:g id="mime_type">%1$s</xliff:g></string>
<string name="player_activity__error_no_secure_decoder">This device does not provide a secure decoder for <xliff:g id="mime_type">%1$s</xliff:g></string>
<string name="player_activity__error_querying_decoders">Unable to query device decoders</string>
<string name="player_activity__error_instantiating_decoder">Unable to nstantiate decoder <xliff:g id="decoder_name">%1$s</xliff:g></string>
<string name="player_activity__storage_permission_denied">Permission to access storage was denied</string>
<string name="player_activity__something_went_wrong">Something went wrong</string>
<!-- menu_fragment.xml -->
<string name="menu__preferences">PREFERENCES</string>
<string name="menu__channels">CHANNELS</string>
<string name="menu__private_office">PRIVATE OFFICE</string>
<string name="menu__log_out">LOG OUT</string>
<!-- IdentifyUserActivity.java -->
<string name="create_identifier_activity__checking_device">Checking device</string>
<string name="create_identifier_activity__this_process_can_take_a_while">This process can take a while, please be patient, thank you. </string>
<string name="create_identifier_activity__please_type_the_serial_number_of_your_device">Please type the serial number of your device</string>
<string name="create_identifier_activity__check">Check</string>
<string name="create_identifier_activity__image_description">Image description</string>
<string name="create_identifier_activity__this_field_must_be_filled">This filed must be filled</string>
<!-- CheckSerialFragment.java -->
<string name="check_serial_fragment__please_check_your_serial_number">Please check your serial number</string>
<string name="check_serial_fragment__correct">Correct</string>
<string name="check_serial_fragment__change">Change</string>
<string name="check_serial_fragment__something_went_wrong">Something went wrong</string>
<string name="check_serial_fragment__internet_connection_title">Internet connection has been lost</string>
<string name="check_serial_fragment__internet_connection_message">Please check your internet connection.</string>
<string name="check_serial_fragment__serial_is_not_valid">Serial is not valid</string>
<string name="check_serial_fragment__save_serial">Please enter your serial number</string>
<!-- ChangeSerialFragment.java -->
<string name="change_serial_fragment__save">Save</string>
<string name="change_serial_fragment__save_serial">Please enter your serial number</string>
<string name="change_serial_fragment__serial_is_not_valid">Serial is not valid</string>
<!-- DeviceNotRegisteredFragment.java -->
<string name="device_not_registered_fragment__change_serial">Change serial</string>
<string name="device_not_registered_fragment__sign_up">Sign up</string>
<string name="device_not_registered_fragment__sign_in">Sign in</string>
<string name="device_not_registered_fragment__your_device_is_not_registered">
Your device is not registered!
</string>
<!-- SignUpFragment.java -->
<string name="sign_up_fragment__email">Please enter your email here</string>
<string name="sign_up_fragment__password">… and password here</string>
<string name="sign_up_fragment__and_repeat_it">… and repeat it</string>
<string name="sign_up_fragment__sign_up">Sign up</string>
<string name="sign_up_fragment__something_went_wrong">Something went wrong</string>
<string name="sign_up_fragment__please_fill_this_field">Please fill this field</string>
<string name="sign_up_fragment__please_enter_an_email">Please enter an email</string>
<string name="sign_up_fragment__please_enter_a_password">Please enter a password</string>
<!-- SignInFragment.java -->
<string name="sign_in_fragment__email">Please enter your email here</string>
<string name="sign_in_fragment__password">… and password here</string>
<string name="sign_in_fragment__sign_in">Sign in</string>
<string name="sign_in_fragment__please_fill_this_field">Please fill this field</string>
<string name="sign_in_fragment__please_enter_an_email">Please enter an email</string>
<string name="sign_in_fragment__please_enter_a_password">Please enter a password</string>
<string name="sign_in_fragment__something_went_wrong">Something went wrong</string>
<string name="sign_up_fragment__enter_correct_password">Enter correct password</string>
<string name="change_serial_fragment__please_enter_serial_number_from_the_bottom_of_your_stb">Please enter serial number from the bottom of your STB</string>
<string name="player_activity__video_format_not_allowed">"This video format is not allowed! Please switch to another channel. We apologize for the inconvenience. "</string>
<!-- PreferencesFragment.java -->
<string name="preferences_fragment_parental_control">Parental control</string>
<string name="preferences_fragment_language">Language</string>
<string name="preferences_fragment_device_information">Device information</string>
<!-- MenuFragment.java -->
<string name="MenuFragment__logging_out">Logging out!</string>
<string name="MenuFragment__wait_please_this_can_take_a_while">Wait please! This can take a while.</string>
<string name="MenuFragment__sorry_something_went_wrong">Sorry something went wrong. Try later!</string>
<string name="ChannelsFragment__loading">Loading...</string>
<string name="ChannelsFragment__wait_please_while_loading_channels">Wait please while loading channels</string>
<string name="identify_user_activity__back">BACK</string>
<!-- AccountFragment.java -->
<string-array name="account_menu_items">
<item>One</item>
<item>Two</item>
<item>Three</item>
<item>Four</item>
<item>Five</item>
<item>Six</item>
</string-array>
</resources>
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryDark">@color/primary_dark</item>
<item name="colorAccent">@color/accent</item>
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
</style>
<!-- Navigation view styles -->
<style name="AppTheme.NavigationView" parent="">
<item name="android:background">@android:color/transparent</item>
<item name="android:focusable">true</item>
<item name="android:focusableInTouchMode">true</item>
<item name="android:fitsSystemWindows">true</item>
<item name="android:windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="AppTheme.NavigationView.TextAppearance" parent="">
<item name="android:padding">5dp</item>
<item name="android:textSize">@dimen/main_activity__navigation_view_item_text_size</item>
<item name="android:textStyle">normal</item>
</style>
<style name="AppTheme.Button" parent="">
<item name="android:background">@drawable/button_style</item>
<item name="android:textSize">25sp</item>
<item name="android:textColor">@android:color/white</item>
<item name="android:textAllCaps">true</item>
</style>
<style name="AppTheme.EditText" parent="">
<item name="android:background">@drawable/edit_text_style</item>
<item name="android:padding">10dp</item>
</style>
<style name="AppTheme.TextLabel" parent="">
<item name="android:textSize">35sp</item>
<item name="android:textStyle">bold</item>
<item name="android:textColor">@android:color/white</item>
</style>
</resources>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:key="preferences_parental_control"
android:title="@string/preferences_fragment_parental_control">
</PreferenceScreen>
<PreferenceScreen
android:key="preferences_language"
android:title="@string/preferences_fragment_language">
</PreferenceScreen>
<PreferenceScreen
android:key="preferences_device_information"
android:title="@string/preferences_fragment_device_information">
</PreferenceScreen>
</PreferenceScreen>
\ No newline at end of file
include ':mobile'
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment