├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── misc.xml ├── modules.xml ├── scopes │ └── scope_settings.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── libraries │ ├── YouTubeAndroidPlayerApi.jar │ ├── android-async-http-1.4.5.jar │ └── universal-image-loader-1.9.3.jar ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── thenewboston │ │ └── thenewbostonvideotutorials │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── assets │ └── fonts │ │ ├── Roboto-Black.ttf │ │ ├── Roboto-BlackItalic.ttf │ │ ├── Roboto-Bold.ttf │ │ ├── Roboto-BoldItalic.ttf │ │ ├── Roboto-Italic.ttf │ │ ├── Roboto-Light.ttf │ │ ├── Roboto-LightItalic.ttf │ │ ├── Roboto-Medium.ttf │ │ ├── Roboto-MediumItalic.ttf │ │ ├── Roboto-Regular.ttf │ │ ├── Roboto-Thin.ttf │ │ ├── Roboto-ThinItalic.ttf │ │ ├── RobotoCondensed-Bold.ttf │ │ ├── RobotoCondensed-BoldItalic.ttf │ │ ├── RobotoCondensed-Italic.ttf │ │ ├── RobotoCondensed-Light.ttf │ │ ├── RobotoCondensed-LightItalic.ttf │ │ └── RobotoCondensed-Regular.ttf │ ├── java │ └── com │ │ └── thenewboston │ │ ├── navigation │ │ ├── NavigationDrawer.java │ │ ├── NavigationDrawerAdapter.java │ │ └── NavigationDrawerItem.java │ │ └── thenewbostonvideotutorials │ │ ├── api │ │ ├── CustomHttpClient.java │ │ ├── DeveloperKey.java │ │ └── VideoAPIManager.java │ │ ├── objects │ │ ├── VideoCategoryItem.java │ │ └── VideoItem.java │ │ └── ui │ │ ├── MainActivity.java │ │ ├── VideoCategoriesFragment.java │ │ ├── VideoPlayActivity.java │ │ └── YouTubeFailureRecoveryActivity.java │ └── res │ ├── color │ └── button_text_color.xml │ ├── drawable-hdpi │ ├── ic_launcher.png │ ├── menu_icon_group_collapsed.png │ └── menu_icon_group_expanded.png │ ├── drawable-mdpi │ └── ic_launcher.png │ ├── drawable-xhdpi │ └── ic_launcher.png │ ├── drawable-xxhdpi │ ├── back_arrow.png │ ├── group_collapsed.9.png │ ├── group_expanded.9.png │ ├── ic_launcher.png │ ├── menu_icon_home.png │ ├── menu_icon_logout.png │ ├── menu_icon_message.png │ ├── menu_icon_others.png │ ├── menu_icon_profile.png │ ├── menu_icon_search.png │ ├── menu_icon_videos.png │ ├── next_button_disabled.png │ ├── next_button_enabled.png │ ├── prev_button_disabled.png │ ├── prev_button_enabled.png │ └── shadow.png │ ├── drawable-xxxhdpi │ └── ic_launcher.png │ ├── drawable │ ├── button_bg.xml │ ├── group_indicator.xml │ ├── next_button.xml │ ├── prev_button.xml │ ├── red_badge_bg.xml │ └── shadow_bg.xml │ ├── layout-sw600dp-land │ └── activity_fullscreen_videoplayer.xml │ ├── layout-sw720dp-land │ └── activity_fullscreen_videoplayer.xml │ ├── layout │ ├── activity_fullscreen_videoplayer.xml │ ├── activity_main.xml │ ├── app_bar.xml │ ├── custom_navbar.xml │ ├── fragment_videocategories_list.xml │ ├── navigation_drawer.xml │ ├── navigation_drawer_child.xml │ ├── navigation_drawer_parent.xml │ ├── row_categories_group.xml │ ├── row_categories_sub.xml │ └── row_videolist.xml │ ├── menu │ └── menu_main.xml │ ├── values-large │ ├── constants.xml │ └── dimens.xml │ ├── values-sw600dp │ └── dimens.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── thenewboston-Videos-Android-App.iml /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | /.idea/libraries 5 | .DS_Store 6 | /build 7 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | thenewboston-Videos-Android-App -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ###Android App for thenewboston Videos & Tutorials 2 | 3 | This is the official Android app for all of thenewboston video tutorials. App includes courses in computer programming, web design, photoshop, game development, and more! 4 | 5 | [thenewboston Videos on Google Play](https://play.google.com/store/apps/details?id=com.thenewboston.thenewbostonvideotutorials&hl=en) 6 | 7 | ![](http://i.imgur.com/clpy8NN.png) 8 | 9 | *** 10 | 11 | ####Want to Join the Development Team? 12 | 13 | This app was created using thenewboston API which is currently in development. As of now, we are restricting API access to limited users only. You may [send me a message](https://www.thenewboston.com/profile.php?user=2) if you would like a developer key. 14 | 15 | *** 16 | 17 | ####How to Setup with Android Studio 18 | 19 | 1 - You first need a developer key for thenewboston's API. This key will identify you as a developer and also allow you to access all video data on our server. To obtain a developer key you can [send me a message here.](https://www.thenewboston.com/profile.php?user=2) 20 | 21 | 22 | 2 - You will also need a YouTube Android Player API key from Google to allow your app to play YouTube videos. [Register for a YouTube Android Player API Key from here](https://developers.google.com/youtube/android/player/register) 23 | 24 | 3 - Once you have both keys, copy this link: https://github.com/buckyroberts/thenewboston-Videos-Android-App.git 25 | 26 | 4 - From Android Studio, choose the “Check out project from Version Control” option. 27 | 28 | ![](http://i.imgur.com/Y7fPotR.png) 29 | 30 | 5 - Choose “GitHub” and paste in the repository URL. After you have chosen a location to save the project, click the “Clone” button. 31 | 32 | ![](http://i.imgur.com/hPTX7t5.png) 33 | 34 | 6 - Once the project has been downloaded, you will need to add your API keys to these two files. 35 | 36 | ![](http://i.imgur.com/017fSzB.png) 37 | 38 | 7 - You can now Build and Run the app! 39 | 40 | *** 41 | 42 | Thank you to Wu Xiaodong and Eric So for the original development of the app! 43 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 21 5 | buildToolsVersion "21.1.2" 6 | 7 | defaultConfig { 8 | applicationId "com.thenewboston.thenewbostonvideotutorials" 9 | minSdkVersion 14 10 | targetSdkVersion 21 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | compile 'com.android.support:appcompat-v7:21.0.3' 25 | compile files('libraries/android-async-http-1.4.5.jar') 26 | compile files('libraries/universal-image-loader-1.9.3.jar') 27 | compile files('libraries/YouTubeAndroidPlayerApi.jar') 28 | } 29 | -------------------------------------------------------------------------------- /app/libraries/YouTubeAndroidPlayerApi.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/libraries/YouTubeAndroidPlayerApi.jar -------------------------------------------------------------------------------- /app/libraries/android-async-http-1.4.5.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/libraries/android-async-http-1.4.5.jar -------------------------------------------------------------------------------- /app/libraries/universal-image-loader-1.9.3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/libraries/universal-image-loader-1.9.3.jar -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Users\Bucky\AppData\Local\Android\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/thenewboston/thenewbostonvideotutorials/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.thenewboston.thenewbostonvideotutorials; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 26 | 29 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/assets/fonts/Roboto-Black.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/assets/fonts/Roboto-BlackItalic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/assets/fonts/Roboto-Bold.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/assets/fonts/Roboto-BoldItalic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/assets/fonts/Roboto-Italic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/assets/fonts/Roboto-Light.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/assets/fonts/Roboto-LightItalic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/assets/fonts/Roboto-Medium.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/assets/fonts/Roboto-MediumItalic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/assets/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/assets/fonts/Roboto-Thin.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/assets/fonts/Roboto-ThinItalic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/RobotoCondensed-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/assets/fonts/RobotoCondensed-Bold.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/RobotoCondensed-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/assets/fonts/RobotoCondensed-BoldItalic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/RobotoCondensed-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/assets/fonts/RobotoCondensed-Italic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/RobotoCondensed-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/assets/fonts/RobotoCondensed-Light.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/RobotoCondensed-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/assets/fonts/RobotoCondensed-LightItalic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/RobotoCondensed-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/assets/fonts/RobotoCondensed-Regular.ttf -------------------------------------------------------------------------------- /app/src/main/java/com/thenewboston/navigation/NavigationDrawer.java: -------------------------------------------------------------------------------- 1 | package com.thenewboston.navigation; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.Fragment; 5 | import android.support.v4.widget.DrawerLayout; 6 | import android.support.v7.app.ActionBarDrawerToggle; 7 | import android.support.v7.widget.Toolbar; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import com.thenewboston.thenewbostonvideotutorials.R; 12 | 13 | public class NavigationDrawer extends Fragment { 14 | 15 | private DrawerLayout mainLayout; 16 | private View navigationDrawer; 17 | private ActionBarDrawerToggle actionBarDrawerToggle; 18 | 19 | //Required empty public constructor 20 | public NavigationDrawer() { 21 | } 22 | 23 | @Override 24 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 25 | return inflater.inflate(R.layout.navigation_drawer, container, false); 26 | } 27 | 28 | //Setting up the Navigation Drawer 29 | public void setUp(int fragmentId, DrawerLayout drawerLayout, Toolbar toolbar) { 30 | navigationDrawer = getActivity().findViewById(fragmentId); 31 | mainLayout = drawerLayout; 32 | 33 | actionBarDrawerToggle = new ActionBarDrawerToggle(getActivity(), drawerLayout, toolbar, R.string.drawer_open, R.string.drawer_close) { 34 | @Override 35 | public void onDrawerOpened(View drawerView) { 36 | super.onDrawerOpened(drawerView); 37 | getActivity().invalidateOptionsMenu(); 38 | mainLayout.openDrawer(navigationDrawer); 39 | } 40 | @Override 41 | public void onDrawerClosed(View drawerView) { 42 | super.onDrawerClosed(drawerView); 43 | getActivity().invalidateOptionsMenu(); 44 | } 45 | }; 46 | 47 | mainLayout.setDrawerListener(actionBarDrawerToggle); 48 | mainLayout.post(new Runnable() { 49 | @Override 50 | public void run() { 51 | actionBarDrawerToggle.syncState(); 52 | } 53 | }); 54 | } 55 | 56 | 57 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thenewboston/navigation/NavigationDrawerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.thenewboston.navigation; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.graphics.Typeface; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.BaseExpandableListAdapter; 10 | import android.widget.CheckedTextView; 11 | import android.widget.ImageView; 12 | import android.widget.LinearLayout; 13 | import android.widget.TextView; 14 | import com.thenewboston.thenewbostonvideotutorials.R; 15 | import java.util.ArrayList; 16 | 17 | public class NavigationDrawerAdapter extends BaseExpandableListAdapter { 18 | 19 | private LayoutInflater inflater; 20 | private ArrayList childItems; 21 | private ArrayList parentItems; 22 | private Context context; 23 | 24 | //Constructor 25 | public NavigationDrawerAdapter(Context context, ArrayList parents, ArrayList children) { 26 | this.context = context; 27 | this.parentItems = parents; 28 | this.childItems = children; 29 | } 30 | 31 | public void setInflater(LayoutInflater inflater, Activity activity) { 32 | this.inflater = inflater; 33 | } 34 | 35 | //Called automatically for each child view (implement per requirement) 36 | @Override 37 | public View getChildView(int groupPosition, final int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { 38 | ChildMenuHolder holder; 39 | if (convertView == null) { 40 | convertView = inflater.inflate(R.layout.navigation_drawer_child, parent, false); 41 | holder = new ChildMenuHolder(); 42 | holder.imageViewChildIcon = (ImageView) convertView.findViewById(R.id.childImage); 43 | holder.textViewChildName = (TextView) convertView.findViewById(R.id.textViewChild); 44 | holder.textViewBadge = (TextView) convertView.findViewById(R.id.badgeTextView); 45 | holder.layoutBadge = (LinearLayout) convertView.findViewById(R.id.childBadgeLayout); 46 | convertView.setTag(holder); 47 | } else { 48 | holder = (ChildMenuHolder) convertView.getTag(); 49 | } 50 | 51 | //Set the text 52 | NavigationDrawerItem item = (NavigationDrawerItem) getChild(groupPosition, childPosition); 53 | holder.textViewChildName.setText(item.getItemTitle()); 54 | holder.textViewBadge.setText(item.getBadgeNumber() + ""); 55 | holder.layoutBadge.setVisibility(item.getBadgeNumber() > 0 ? View.VISIBLE : View.GONE); 56 | holder.imageViewChildIcon.setImageResource(item.getItemIconResourceId()); 57 | return convertView; 58 | } 59 | 60 | 61 | //Called automatically for each parent item (implement per requirement) 62 | @Override 63 | public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { 64 | GroupMenuHolder holder; 65 | if (convertView == null) { 66 | convertView = inflater.inflate(R.layout.navigation_drawer_parent, parent, false); 67 | holder = new GroupMenuHolder(); 68 | holder.imageViewGroupIcon = (ImageView) convertView.findViewById(R.id.imageViewGroupIcon); 69 | holder.textViewGroupName = (CheckedTextView) convertView.findViewById(R.id.textViewGroupName); 70 | holder.imageViewExpanded = (ImageView) convertView.findViewById(R.id.imageViewExpanded); 71 | holder.leftColorShapeView = convertView.findViewById(R.id.leftColorShapeView); 72 | Typeface face = Typeface.createFromAsset(context.getAssets(), "fonts/Roboto-Medium.ttf"); 73 | holder.textViewGroupName.setTypeface(face); 74 | convertView.setTag(holder); 75 | } else { 76 | holder = (GroupMenuHolder) convertView.getTag(); 77 | } 78 | 79 | holder.textViewGroupName.setText(parentItems.get(groupPosition).getItemTitle()); 80 | holder.textViewGroupName.setChecked(isExpanded); 81 | 82 | if (parentItems.get(groupPosition).getItemIconResourceId() == -1) { 83 | holder.imageViewGroupIcon.setVisibility(View.GONE); 84 | } else { 85 | holder.imageViewGroupIcon.setVisibility(View.VISIBLE); 86 | holder.imageViewGroupIcon.setImageResource(parentItems.get(groupPosition).getItemIconResourceId()); 87 | } 88 | 89 | holder.leftColorShapeView.setBackgroundColor(parentItems.get(groupPosition).getShapeColor()); 90 | holder.imageViewExpanded.setVisibility(getChildrenCount(groupPosition) > 0 ? View.VISIBLE : View.GONE); 91 | holder.imageViewExpanded.setImageResource(isExpanded ? R.drawable.menu_icon_group_expanded : R.drawable.menu_icon_group_collapsed); 92 | return convertView; 93 | } 94 | 95 | @Override 96 | public Object getChild(int groupPosition, int childPosition) { 97 | return ((ArrayList) childItems.get(groupPosition)).get(childPosition); 98 | } 99 | 100 | @Override 101 | public long getChildId(int groupPosition, int childPosition) { 102 | return 0; 103 | } 104 | 105 | @Override 106 | public int getChildrenCount(int groupPosition) { 107 | return childItems != null ? ((ArrayList) childItems.get(groupPosition)).size() : 0; 108 | } 109 | 110 | @Override 111 | public Object getGroup(int groupPosition) { 112 | return parentItems.get(groupPosition); 113 | } 114 | 115 | @Override 116 | public int getGroupCount() { 117 | return parentItems.size(); 118 | } 119 | 120 | @Override 121 | public void onGroupCollapsed(int groupPosition) { 122 | super.onGroupCollapsed(groupPosition); 123 | } 124 | 125 | @Override 126 | public void onGroupExpanded(int groupPosition) { 127 | super.onGroupExpanded(groupPosition); 128 | } 129 | 130 | @Override 131 | public long getGroupId(int groupPosition) { 132 | return 0; 133 | } 134 | 135 | @Override 136 | public boolean hasStableIds() { 137 | return false; 138 | } 139 | 140 | @Override 141 | public boolean isChildSelectable(int groupPosition, int childPosition) { 142 | return false; 143 | } 144 | 145 | class GroupMenuHolder { 146 | View leftColorShapeView; 147 | ImageView imageViewGroupIcon; 148 | CheckedTextView textViewGroupName; 149 | ImageView imageViewExpanded; 150 | } 151 | 152 | class ChildMenuHolder { 153 | ImageView imageViewChildIcon; 154 | TextView textViewChildName; 155 | TextView textViewBadge; 156 | LinearLayout layoutBadge; 157 | } 158 | 159 | 160 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thenewboston/navigation/NavigationDrawerItem.java: -------------------------------------------------------------------------------- 1 | package com.thenewboston.navigation; 2 | 3 | public class NavigationDrawerItem { 4 | 5 | private String itemTitle; 6 | private int itemIconResourceId; 7 | private int badgeNumber; 8 | private int shapeColor; 9 | 10 | public NavigationDrawerItem(String title, int icon) { 11 | itemTitle = title; 12 | itemIconResourceId = icon; 13 | } 14 | 15 | public int getItemIconResourceId() { 16 | return itemIconResourceId; 17 | } 18 | 19 | public void setItemIconResourceId(int itemIconResourceId) { 20 | this.itemIconResourceId = itemIconResourceId; 21 | } 22 | 23 | public String getItemTitle() { 24 | return itemTitle; 25 | } 26 | 27 | public void setItemTitle(String itemTitle) { 28 | this.itemTitle = itemTitle; 29 | } 30 | 31 | public int getBadgeNumber() { 32 | return badgeNumber; 33 | } 34 | 35 | public void setBadgeNumber(int badgeNumber) { 36 | this.badgeNumber = badgeNumber; 37 | } 38 | 39 | public int getShapeColor() { 40 | return shapeColor; 41 | } 42 | 43 | public void setShapeColor(int color) { 44 | shapeColor = color; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/com/thenewboston/thenewbostonvideotutorials/api/CustomHttpClient.java: -------------------------------------------------------------------------------- 1 | package com.thenewboston.thenewbostonvideotutorials.api; 2 | 3 | import com.loopj.android.http.AsyncHttpClient; 4 | import com.loopj.android.http.AsyncHttpResponseHandler; 5 | import com.loopj.android.http.RequestHandle; 6 | import com.loopj.android.http.RequestParams; 7 | 8 | public class CustomHttpClient { 9 | 10 | public static final String THENEWBOSTON_PUBLIC_API_KEY = ""; 11 | public static final String API_BASE_URL = "https://www.thenewboston.com"; 12 | private static AsyncHttpClient client = new AsyncHttpClient(); 13 | 14 | public static RequestHandle get(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) { 15 | if (params == null) 16 | params = new RequestParams(); 17 | return client.get(getAbsoluteUrl(url), params, responseHandler); 18 | } 19 | 20 | private static String getAbsoluteUrl(String url) { 21 | return API_BASE_URL + url + "&TOKEN=" + THENEWBOSTON_PUBLIC_API_KEY; 22 | } 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/thenewboston/thenewbostonvideotutorials/api/DeveloperKey.java: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Google Inc. All Rights Reserved. 2 | 3 | package com.thenewboston.thenewbostonvideotutorials.api; 4 | 5 | /** 6 | * Static container class for holding a reference to your YouTube Developer Key. 7 | */ 8 | public class DeveloperKey { 9 | 10 | /** 11 | * Please replace this with a valid API key which is enabled for the YouTube Data API v3 service. 12 | * Go to the Google APIs Console to register a new developer key. 13 | */ 14 | public static final String DEVELOPER_KEY = ""; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/thenewboston/thenewbostonvideotutorials/api/VideoAPIManager.java: -------------------------------------------------------------------------------- 1 | package com.thenewboston.thenewbostonvideotutorials.api; 2 | 3 | import com.loopj.android.http.JsonHttpResponseHandler; 4 | import com.thenewboston.thenewbostonvideotutorials.objects.VideoCategoryItem; 5 | import com.thenewboston.thenewbostonvideotutorials.objects.VideoItem; 6 | import org.apache.http.Header; 7 | import org.json.JSONArray; 8 | import org.json.JSONException; 9 | import org.json.JSONObject; 10 | import java.util.ArrayList; 11 | import java.util.HashMap; 12 | import java.util.Iterator; 13 | 14 | public class VideoAPIManager { 15 | 16 | public static void getCategoriesFor(int subjectId, final VideoCategoryLoaderListener listener) { 17 | String apiEntry = "/api/v1/api.php?TYPE=video&ACTION=getCategories&subject=" + subjectId; 18 | CustomHttpClient.get(apiEntry, null, new JsonHttpResponseHandler() { 19 | 20 | @Override 21 | public void onSuccess(int statusCode, Header[] headers, JSONObject response) { 22 | super.onSuccess(statusCode, headers, response); 23 | Iterator catIds = response.keys(); 24 | HashMap> results = new HashMap>(); 25 | do { 26 | String catId = catIds.next(); 27 | try { 28 | String catName = response.getJSONObject(catId).getString("categoryName"); 29 | JSONArray subCategories = response.getJSONObject(catId).getJSONArray("categories"); 30 | 31 | VideoCategoryItem groupItem = new VideoCategoryItem(); 32 | groupItem.categoryId = Integer.valueOf(catId); 33 | groupItem.categoryName = catName; 34 | 35 | ArrayList array = new ArrayList(); 36 | for (int i = 0; i < subCategories.length(); i++) { 37 | array.add(VideoCategoryItem.fromJSON(subCategories.getJSONObject(i))); 38 | } 39 | results.put(groupItem, array); 40 | } catch (JSONException e) { 41 | e.printStackTrace(); 42 | } 43 | } while (catIds.hasNext()); 44 | listener.onSuccess(results); 45 | } 46 | 47 | @Override 48 | public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) { 49 | super.onFailure(statusCode, headers, responseString, throwable); 50 | if (throwable != null) 51 | listener.onFailure(new Error(throwable.getMessage())); 52 | else 53 | listener.onFailure(new Error("Status Code - " + statusCode)); 54 | } 55 | 56 | @Override 57 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) { 58 | super.onFailure(statusCode, headers, throwable, errorResponse); 59 | if (throwable != null) 60 | listener.onFailure(new Error(throwable.getMessage())); 61 | else 62 | listener.onFailure(new Error("Status Code - " + statusCode)); 63 | } 64 | 65 | @Override 66 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONArray errorResponse) { 67 | super.onFailure(statusCode, headers, throwable, errorResponse); 68 | if (throwable != null) 69 | listener.onFailure(new Error(throwable.getMessage())); 70 | else 71 | listener.onFailure(new Error("Status Code - " + statusCode)); 72 | } 73 | 74 | }); 75 | } 76 | 77 | public static void getVideosFor(int categoryId, final VideoLoaderListener listener) { 78 | String apiEntry = "/api/v1/api.php?TYPE=video&ACTION=getVideos&cat=" + categoryId; 79 | CustomHttpClient.get(apiEntry, null, new JsonHttpResponseHandler() { 80 | 81 | @Override 82 | public void onSuccess(int statusCode, Header[] headers, JSONArray response) { 83 | super.onSuccess(statusCode, headers, response); 84 | ArrayList results = new ArrayList(); 85 | for (int i = 0; i < response.length(); i++) { 86 | try { 87 | JSONObject videoDictionary = response.getJSONObject(i); 88 | results.add(VideoItem.fromJSONDictionary(videoDictionary)); 89 | } catch (JSONException e) { 90 | e.printStackTrace(); 91 | } 92 | } 93 | listener.onSuccess(results); 94 | } 95 | 96 | @Override 97 | public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) { 98 | super.onFailure(statusCode, headers, responseString, throwable); 99 | listener.onFailure(null); 100 | } 101 | 102 | @Override 103 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) { 104 | super.onFailure(statusCode, headers, throwable, errorResponse); 105 | listener.onFailure(null); 106 | } 107 | 108 | @Override 109 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONArray errorResponse) { 110 | super.onFailure(statusCode, headers, throwable, errorResponse); 111 | listener.onFailure(null); 112 | } 113 | 114 | }); 115 | } 116 | 117 | public interface VideoLoaderListener { 118 | public abstract void onSuccess(ArrayList results); 119 | public abstract void onFailure(Error error); 120 | } 121 | 122 | public interface VideoCategoryLoaderListener { 123 | public abstract void onSuccess(HashMap> results); 124 | public abstract void onFailure(Error error); 125 | } 126 | 127 | 128 | } 129 | -------------------------------------------------------------------------------- /app/src/main/java/com/thenewboston/thenewbostonvideotutorials/objects/VideoCategoryItem.java: -------------------------------------------------------------------------------- 1 | package com.thenewboston.thenewbostonvideotutorials.objects; 2 | 3 | import org.json.JSONException; 4 | import org.json.JSONObject; 5 | 6 | public class VideoCategoryItem { 7 | 8 | public int categoryId; 9 | public int parentId; 10 | public int subjectId; 11 | public String categoryName; 12 | public String categoryDesc; 13 | public int videoCount; 14 | public int forumCategoryId; 15 | 16 | public static VideoCategoryItem fromJSON(JSONObject videoCategoryObject) { 17 | VideoCategoryItem item = new VideoCategoryItem(); 18 | try { 19 | item.categoryId = videoCategoryObject.getInt("categoryID"); 20 | item.parentId = videoCategoryObject.getInt("parentID"); 21 | item.subjectId = videoCategoryObject.getInt("subjectID"); 22 | item.categoryName = videoCategoryObject.getString("categoryName"); 23 | item.categoryDesc = videoCategoryObject.getString("categoryDescription"); 24 | item.videoCount = videoCategoryObject.getInt("videosCount"); 25 | item.forumCategoryId = videoCategoryObject.getInt("forumCategoryID"); 26 | } catch (JSONException e) { 27 | e.printStackTrace(); 28 | } 29 | return item; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/thenewboston/thenewbostonvideotutorials/objects/VideoItem.java: -------------------------------------------------------------------------------- 1 | package com.thenewboston.thenewbostonvideotutorials.objects; 2 | 3 | import org.json.JSONException; 4 | import org.json.JSONObject; 5 | 6 | public class VideoItem { 7 | private int videoId; 8 | private int categoryId; 9 | private String title; 10 | private String code; 11 | 12 | public static VideoItem fromJSONDictionary(JSONObject videoDictionary) { 13 | VideoItem item = new VideoItem(); 14 | try { 15 | item.videoId = Integer.valueOf(videoDictionary.getString("videoID")); 16 | item.categoryId = Integer.valueOf(videoDictionary.getString("categoryID")); 17 | item.title = videoDictionary.getString("title"); 18 | item.code = videoDictionary.getString("code"); 19 | } catch (JSONException e) { 20 | e.printStackTrace(); 21 | } 22 | return item; 23 | } 24 | 25 | public int getVideoId() { 26 | return videoId; 27 | } 28 | 29 | public String getTitle() { 30 | return title; 31 | } 32 | 33 | public String getCode() { 34 | return code; 35 | } 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/thenewboston/thenewbostonvideotutorials/ui/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.thenewboston.thenewbostonvideotutorials.ui; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.support.annotation.NonNull; 6 | import android.support.v4.widget.DrawerLayout; 7 | import android.support.v7.app.ActionBarActivity; 8 | import android.support.v7.widget.Toolbar; 9 | import android.view.Gravity; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.widget.ExpandableListView; 13 | import com.thenewboston.navigation.NavigationDrawer; 14 | import com.thenewboston.navigation.NavigationDrawerAdapter; 15 | import com.thenewboston.navigation.NavigationDrawerItem; 16 | import com.thenewboston.thenewbostonvideotutorials.R; 17 | import java.util.ArrayList; 18 | 19 | public class MainActivity extends ActionBarActivity { 20 | 21 | private DrawerLayout drawerLayout; 22 | private ArrayList mainMenu = new ArrayList<>(); 23 | private int[] shapeColors = {0xff3a829b, 0xffa94545, 0xffaf6363, 0xffc89351, 0xff69aecb, 0xff6e8f5a, 0xff425e90, 0xffa2c5d8}; 24 | private int[] subjectIds = {8, 6, 0, 7, 5, 2, 3, 4}; 25 | private String[] subjectTitles = {"Beauty", "Business", "Computer Science", "Cooking", "Humanities", "Math", "Science", "Social Sciences"}; 26 | private static int currentSubjectId = 0; 27 | private static String currentTitle = "Computer Science"; 28 | 29 | @Override 30 | protected void onSaveInstanceState(Bundle outState) { 31 | super.onSaveInstanceState(outState); 32 | outState.putInt("currentSubjectId", currentSubjectId); 33 | outState.putString("currentTitle", currentTitle); 34 | } 35 | 36 | @Override 37 | protected void onRestoreInstanceState(@NonNull Bundle savedState) { 38 | super.onRestoreInstanceState(savedState); 39 | currentSubjectId = savedState.getInt("currentSubjectId"); 40 | currentTitle = savedState.getString("currentTitle"); 41 | } 42 | 43 | @Override 44 | protected void onCreate(Bundle savedInstanceState) { 45 | super.onCreate(savedInstanceState); 46 | setContentView(R.layout.activity_main); 47 | 48 | //Set Toolbar to act as Actionbar for this Activity 49 | Toolbar toolbar = (Toolbar) findViewById(R.id.app_bar); 50 | setSupportActionBar(toolbar); 51 | getSupportActionBar().setDisplayShowHomeEnabled(true); 52 | 53 | //Add NavigationDrawer 54 | drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); 55 | final NavigationDrawer drawerFragment = (NavigationDrawer) getSupportFragmentManager().findFragmentById(R.id.left_drawer); 56 | drawerFragment.setUp(R.id.left_drawer, drawerLayout, toolbar); 57 | getSupportActionBar().setHomeButtonEnabled(true); 58 | 59 | //Create ExpandableList and set properties 60 | ExpandableListView expandableList = (ExpandableListView) findViewById(R.id.expandableListView); 61 | expandableList.setDividerHeight(2); 62 | expandableList.setGroupIndicator(null); 63 | expandableList.setClickable(true); 64 | 65 | //Create navigation drawer items and display category fragment 66 | createMenuItems(); 67 | displayCategoriesFragment(); 68 | 69 | //Set drawer adapter and listen for clicks 70 | final NavigationDrawerAdapter adapter = new NavigationDrawerAdapter(this, mainMenu, null); 71 | adapter.setInflater((LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE), this); 72 | expandableList.setAdapter(adapter); 73 | expandableList.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() { 74 | @Override 75 | public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) { 76 | currentSubjectId = subjectIds[groupPosition]; 77 | currentTitle = subjectTitles[groupPosition]; 78 | displayCategoriesFragment(); 79 | drawerLayout.closeDrawer(Gravity.LEFT); 80 | return true; 81 | } 82 | }); 83 | } 84 | 85 | //Display the categories for current subject 86 | private void displayCategoriesFragment() { 87 | VideoCategoriesFragment fragment = VideoCategoriesFragment.newInstance(); 88 | getFragmentManager().beginTransaction().replace(R.id.mainFragment, fragment).commit(); 89 | fragment.setSubjectId(currentSubjectId); 90 | setTitle(currentTitle); 91 | } 92 | 93 | //Create menu items for navigation drawer 94 | public void createMenuItems() { 95 | for (int i = 0; i < subjectTitles.length; i++) { 96 | String subjectTitle = subjectTitles[i]; 97 | NavigationDrawerItem item = new NavigationDrawerItem(subjectTitle, -1); 98 | item.setShapeColor(shapeColors[i]); 99 | mainMenu.add(item); 100 | } 101 | } 102 | 103 | 104 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thenewboston/thenewbostonvideotutorials/ui/VideoCategoriesFragment.java: -------------------------------------------------------------------------------- 1 | package com.thenewboston.thenewbostonvideotutorials.ui; 2 | 3 | import android.app.Fragment; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.BaseExpandableListAdapter; 11 | import android.widget.ExpandableListView; 12 | import android.widget.ImageView; 13 | import android.widget.LinearLayout; 14 | import android.widget.TextView; 15 | import android.widget.Toast; 16 | import com.thenewboston.thenewbostonvideotutorials.R; 17 | import com.thenewboston.thenewbostonvideotutorials.api.VideoAPIManager; 18 | import com.thenewboston.thenewbostonvideotutorials.objects.VideoCategoryItem; 19 | import java.util.ArrayList; 20 | import java.util.Comparator; 21 | import java.util.HashMap; 22 | import java.util.SortedSet; 23 | import java.util.TreeSet; 24 | 25 | public class VideoCategoriesFragment extends Fragment { 26 | 27 | private int subjectId = -1; 28 | private CategoriesAdapter mAdapter; 29 | 30 | public static VideoCategoriesFragment newInstance() { 31 | return new VideoCategoriesFragment(); 32 | } 33 | 34 | @Override 35 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 36 | View view = inflater.inflate(R.layout.fragment_videocategories_list, container, false); 37 | mAdapter = new CategoriesAdapter(); 38 | ExpandableListView mListView = (ExpandableListView) view.findViewById(android.R.id.list); 39 | mListView.setAdapter(mAdapter); 40 | mListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() { 41 | @Override 42 | public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { 43 | Intent intent = new Intent(getActivity(), VideoPlayActivity.class); 44 | intent.putExtra(VideoPlayActivity.CATEGORY_ID, mAdapter.getChild(groupPosition, childPosition).categoryId); 45 | intent.putExtra(VideoPlayActivity.CATEGORY_NAME, mAdapter.getChild(groupPosition, childPosition).categoryName); 46 | startActivity(intent); 47 | return true; 48 | } 49 | }); 50 | return view; 51 | } 52 | 53 | //Create a sorted set of videos for a subject 54 | public void setSubjectId(int newSubjectId) { 55 | if (this.subjectId == newSubjectId) 56 | return; 57 | this.subjectId = newSubjectId; 58 | VideoAPIManager.getCategoriesFor(subjectId, new VideoAPIManager.VideoCategoryLoaderListener() { 59 | @Override 60 | public void onSuccess(HashMap> results) { 61 | mAdapter.setData(results); 62 | mAdapter.notifyDataSetChanged(); 63 | } 64 | @Override 65 | public void onFailure(Error error) { 66 | if (getActivity() == null) 67 | return; 68 | Toast.makeText(getActivity(), error != null ? error.getMessage() : "Unknown Error Occurred", Toast.LENGTH_SHORT).show(); 69 | } 70 | }); 71 | } 72 | 73 | 74 | //Adapter for video categories 75 | private class CategoriesAdapter extends BaseExpandableListAdapter { 76 | 77 | private int[] shapeColors = {0xff3a829b, 0xffa94545, 0xff6e8f5a, 0xffc89351, 0xff6b5876, 0xff68abc8, 0xff425e90, 0xffa2c5d8, 0xffca8ea7, 0xff7e3838}; 78 | private SortedSet sortedSet; 79 | private HashMap> categories = null; 80 | 81 | public CategoriesAdapter() { 82 | } 83 | 84 | //Sort categories alphabetically 85 | public void setData(HashMap> results) { 86 | categories = results; 87 | sortedSet = new TreeSet(new Comparator() { 88 | @Override 89 | public int compare(Object lhs, Object rhs) { 90 | VideoCategoryItem item1 = (VideoCategoryItem) lhs; 91 | VideoCategoryItem item2 = (VideoCategoryItem) rhs; 92 | return item1.categoryName.compareTo(item2.categoryName); 93 | } 94 | }); 95 | sortedSet.addAll(categories.keySet()); 96 | } 97 | 98 | @Override 99 | public int getGroupCount() { 100 | if (categories == null) return 0; 101 | return categories.keySet().size(); 102 | } 103 | 104 | @Override 105 | public int getChildrenCount(int groupPosition) { 106 | if (categories == null) return 0; 107 | Object key = sortedSet.toArray()[groupPosition]; 108 | if (categories.get(key) == null) return 0; 109 | return categories.get(key).size(); 110 | } 111 | 112 | @Override 113 | public VideoCategoryItem getGroup(int groupPosition) { 114 | if (categories == null) return null; 115 | return (VideoCategoryItem) sortedSet.toArray()[groupPosition]; 116 | } 117 | 118 | @Override 119 | public VideoCategoryItem getChild(int groupPosition, int childPosition) { 120 | if (categories == null) return null; 121 | try { 122 | return categories.get(getGroup(groupPosition)).get(childPosition); 123 | } catch (Exception ex) { 124 | return null; 125 | } 126 | } 127 | 128 | @Override 129 | public long getGroupId(int groupPosition) { 130 | return getGroup(groupPosition).categoryId; 131 | } 132 | 133 | @Override 134 | public long getChildId(int groupPosition, int childPosition) { 135 | return getChild(groupPosition, childPosition).categoryId; 136 | } 137 | 138 | @Override 139 | public boolean hasStableIds() { 140 | return false; 141 | } 142 | 143 | @Override 144 | public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { 145 | TextView textView; 146 | if (convertView == null) { 147 | LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 148 | convertView = inflater.inflate(R.layout.row_categories_group, parent, false); 149 | } 150 | textView = (TextView) convertView.findViewById(R.id.textView); 151 | textView.setText(getGroup(groupPosition).categoryName); 152 | 153 | LinearLayout layout = (LinearLayout) convertView.findViewById(R.id.listItemLayout); 154 | layout.setBackgroundColor(shapeColors[groupPosition % shapeColors.length]); 155 | 156 | convertView.findViewById(R.id.shadowView).setVisibility(isExpanded ? View.GONE : View.VISIBLE); 157 | ImageView indicator = (ImageView) convertView.findViewById(R.id.indicatorImageView); 158 | indicator.setImageResource(isExpanded ? R.drawable.group_expanded : R.drawable.group_collapsed); 159 | return convertView; 160 | } 161 | 162 | @Override 163 | public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { 164 | TextView textView; 165 | if (convertView == null) { 166 | LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 167 | convertView = inflater.inflate(R.layout.row_categories_sub, parent, false); 168 | } 169 | 170 | convertView.findViewById(R.id.shadowView).setVisibility(childPosition == 0 ? View.VISIBLE : View.GONE); 171 | textView = (TextView) convertView.findViewById(R.id.textView); 172 | textView.setText(getChild(groupPosition, childPosition).categoryName); 173 | convertView.setBackgroundColor(shapeColors[groupPosition % shapeColors.length]); 174 | return convertView; 175 | } 176 | 177 | @Override 178 | public boolean isChildSelectable(int groupPosition, int childPosition) { 179 | return true; 180 | } 181 | 182 | } 183 | 184 | 185 | } 186 | -------------------------------------------------------------------------------- /app/src/main/java/com/thenewboston/thenewbostonvideotutorials/ui/VideoPlayActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.thenewboston.thenewbostonvideotutorials.ui; 18 | 19 | import android.app.Activity; 20 | import android.content.pm.ActivityInfo; 21 | import android.content.res.Configuration; 22 | import android.database.DataSetObserver; 23 | import android.graphics.Bitmap; 24 | import android.graphics.Typeface; 25 | import android.os.Build; 26 | import android.os.Bundle; 27 | import android.view.LayoutInflater; 28 | import android.view.View; 29 | import android.view.ViewGroup; 30 | import android.widget.AdapterView; 31 | import android.widget.Button; 32 | import android.widget.ImageButton; 33 | import android.widget.ImageView; 34 | import android.widget.LinearLayout; 35 | import android.widget.LinearLayout.LayoutParams; 36 | import android.widget.ListAdapter; 37 | import android.widget.ListView; 38 | import android.widget.TextView; 39 | import com.google.android.youtube.player.YouTubePlayer; 40 | import com.google.android.youtube.player.YouTubePlayerView; 41 | import com.nostra13.universalimageloader.core.DisplayImageOptions; 42 | import com.nostra13.universalimageloader.core.ImageLoader; 43 | import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; 44 | import com.nostra13.universalimageloader.core.assist.FailReason; 45 | import com.nostra13.universalimageloader.core.assist.ImageSize; 46 | import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; 47 | import com.thenewboston.thenewbostonvideotutorials.R; 48 | import com.thenewboston.thenewbostonvideotutorials.api.DeveloperKey; 49 | import com.thenewboston.thenewbostonvideotutorials.api.VideoAPIManager; 50 | import com.thenewboston.thenewbostonvideotutorials.objects.VideoItem; 51 | import java.util.ArrayList; 52 | import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 53 | import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; 54 | 55 | /** 56 | * Sample activity showing how to properly enable custom fullscreen behavior. 57 | *

58 | * This is the preferred way of handling fullscreen because the default fullscreen implementation 59 | * will cause re-buffering of the video. 60 | */ 61 | public class VideoPlayActivity extends YouTubeFailureRecoveryActivity implements 62 | View.OnClickListener, 63 | YouTubePlayer.OnFullscreenListener { 64 | 65 | private static final int PORTRAIT_ORIENTATION = Build.VERSION.SDK_INT < 9 66 | ? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT 67 | : ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT; 68 | public static String CATEGORY_ID = "categoryId"; 69 | public static String CATEGORY_NAME = "categoryName"; 70 | private LinearLayout baseLayout; 71 | private YouTubePlayerView playerView; 72 | private YouTubePlayer player; 73 | private View otherViews; 74 | private boolean fullscreen; 75 | private LinearLayout navigationBar; 76 | private LinearLayout playControlLayout; 77 | private TextView currentVideoTitle, currentVideoTitle2; 78 | private Button prevVideo, nextVideo; 79 | private Button prevVideo2, nextVideo2; 80 | private ListView allVideosListView; 81 | private ArrayList videos; 82 | private int currentIndex = -1; 83 | private TextView navigationTitleTextView; 84 | private ImageButton backButton; 85 | 86 | @Override 87 | protected void onCreate(Bundle savedInstanceState) { 88 | super.onCreate(savedInstanceState); 89 | setContentView(R.layout.activity_fullscreen_videoplayer); 90 | findViewsAndBind(); 91 | setClickListeners(); 92 | doLayout(); 93 | initImageLoader(); 94 | int categoryId = getIntent().getIntExtra(VideoPlayActivity.CATEGORY_ID, -1); 95 | String categoryName = getIntent().getStringExtra(VideoPlayActivity.CATEGORY_NAME); 96 | navigationTitleTextView.setText(categoryName + " Tutorials"); 97 | loadVideosFor(categoryId); 98 | } 99 | 100 | private void initImageLoader() { 101 | DisplayImageOptions defaultOptions = new DisplayImageOptions.Builder().cacheInMemory(true).cacheOnDisk(true).build(); 102 | ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(this).defaultDisplayImageOptions(defaultOptions).build(); 103 | ImageLoader.getInstance().init(config); 104 | } 105 | 106 | private void findViewsAndBind() { 107 | navigationBar = (LinearLayout) findViewById(R.id.navigationBar); 108 | backButton = (ImageButton) findViewById(R.id.navigationLeftButton); 109 | navigationTitleTextView = (TextView) findViewById(R.id.navigationTitleTextView); 110 | 111 | baseLayout = (LinearLayout) findViewById(R.id.layout); 112 | playerView = (YouTubePlayerView) findViewById(R.id.player); 113 | otherViews = findViewById(R.id.other_views); 114 | 115 | currentVideoTitle = (TextView) findViewById(R.id.currentVideoTitle); 116 | prevVideo = (Button) findViewById(R.id.prevVideoButton); 117 | nextVideo = (Button) findViewById(R.id.nextVideoButton); 118 | 119 | currentVideoTitle.setTypeface(Typeface.createFromAsset(getAssets(), "fonts/Roboto-Bold.ttf")); 120 | playControlLayout = (LinearLayout) findViewById(R.id.fullscreenPlayControlLayout); 121 | currentVideoTitle2 = (TextView) findViewById(R.id.currentVideoTitle2); 122 | prevVideo2 = (Button) findViewById(R.id.prevVideoButton2); 123 | nextVideo2 = (Button) findViewById(R.id.nextVideoButton2); 124 | allVideosListView = (ListView) findViewById(R.id.allVideoslistView); 125 | 126 | playerView.initialize(DeveloperKey.DEVELOPER_KEY, this); 127 | } 128 | 129 | private void setClickListeners() { 130 | 131 | backButton.setOnClickListener(new View.OnClickListener() { 132 | @Override 133 | public void onClick(View v) { 134 | finish(); 135 | } 136 | }); 137 | 138 | allVideosListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 139 | @Override 140 | public void onItemClick(AdapterView parent, View view, int position, long id) { 141 | setCurrentVideo(position); 142 | } 143 | }); 144 | 145 | nextVideo.setOnClickListener(new View.OnClickListener() { 146 | @Override 147 | public void onClick(View v) { 148 | if (videos == null) return; 149 | setCurrentVideo(Math.min(currentIndex + 1, videos.size() - 1)); 150 | } 151 | }); 152 | 153 | prevVideo.setOnClickListener(new View.OnClickListener() { 154 | @Override 155 | public void onClick(View v) { 156 | setCurrentVideo(Math.max(0, currentIndex - 1)); 157 | } 158 | }); 159 | 160 | nextVideo2.setOnClickListener(new View.OnClickListener() { 161 | @Override 162 | public void onClick(View v) { 163 | if (videos == null) return; 164 | setCurrentVideo(Math.min(currentIndex + 1, videos.size() - 1)); 165 | } 166 | }); 167 | 168 | prevVideo2.setOnClickListener(new View.OnClickListener() { 169 | @Override 170 | public void onClick(View v) { 171 | setCurrentVideo(Math.max(0, currentIndex - 1)); 172 | } 173 | }); 174 | } 175 | 176 | @Override 177 | public void onInitializationSuccess(YouTubePlayer.Provider provider, YouTubePlayer player, boolean wasRestored) { 178 | this.player = player; 179 | setControlsEnabled(); 180 | 181 | // Specify that we want to handle fullscreen behavior ourselves. 182 | player.addFullscreenControlFlag(YouTubePlayer.FULLSCREEN_FLAG_CUSTOM_LAYOUT); 183 | player.setOnFullscreenListener(this); 184 | if (!wasRestored && videos != null && videos.size() > 0 && currentIndex > 0) { 185 | player.cueVideo(videos.get(currentIndex).getCode()); 186 | } 187 | setFullscreenMode(true); 188 | } 189 | 190 | @Override 191 | protected YouTubePlayer.Provider getYouTubePlayerProvider() { 192 | return playerView; 193 | } 194 | 195 | @Override 196 | public void onClick(View v) { 197 | player.setFullscreen(!fullscreen); 198 | } 199 | 200 | public void setFullscreenMode(boolean isChecked) { 201 | int controlFlags = player.getFullscreenControlFlags(); 202 | setRequestedOrientation(PORTRAIT_ORIENTATION); 203 | controlFlags |= YouTubePlayer.FULLSCREEN_FLAG_ALWAYS_FULLSCREEN_IN_LANDSCAPE; 204 | player.setFullscreenControlFlags(controlFlags); 205 | } 206 | 207 | private void doLayout() { 208 | LinearLayout.LayoutParams playerParams = 209 | (LinearLayout.LayoutParams) playerView.getLayoutParams(); 210 | if (fullscreen) { 211 | // When in fullscreen, the visibility of all other views than the player should be set to 212 | // GONE and the player should be laid out across the whole screen. 213 | playerParams.width = LayoutParams.MATCH_PARENT; 214 | playerParams.height = LayoutParams.MATCH_PARENT; 215 | // playControlLayout.setVisibility(View.VISIBLE); 216 | navigationBar.setVisibility(View.GONE); 217 | otherViews.setVisibility(View.GONE); 218 | } else { 219 | // This layout is up to you - this is just a simple example (vertically stacked boxes in 220 | // portrait, horizontally stacked in landscape). 221 | playControlLayout.setVisibility(View.GONE); 222 | navigationBar.setVisibility(View.VISIBLE); 223 | otherViews.setVisibility(View.VISIBLE); 224 | ViewGroup.LayoutParams otherViewsParams = otherViews.getLayoutParams(); 225 | if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { 226 | playerParams.width = otherViewsParams.width = 0; 227 | playerParams.height = WRAP_CONTENT; 228 | otherViewsParams.height = MATCH_PARENT; 229 | playerParams.weight = 1; 230 | baseLayout.setOrientation(LinearLayout.HORIZONTAL); 231 | } else { 232 | playerParams.width = otherViewsParams.width = MATCH_PARENT; 233 | playerParams.height = WRAP_CONTENT; 234 | playerParams.weight = 0; 235 | otherViewsParams.height = 0; 236 | baseLayout.setOrientation(LinearLayout.VERTICAL); 237 | } 238 | setControlsEnabled(); 239 | } 240 | } 241 | 242 | private void setCurrentVideo(int index) { 243 | if (videos == null || videos.size() < 1) { 244 | prevVideo.setEnabled(false); 245 | nextVideo.setEnabled(false); 246 | prevVideo2.setEnabled(false); 247 | nextVideo2.setEnabled(false); 248 | return; 249 | } 250 | if (currentIndex == index) return; 251 | 252 | currentIndex = index; 253 | currentVideoTitle.setText((index + 1) + " - " + videos.get(index).getTitle()); 254 | currentVideoTitle2.setText((index + 1) + " - " + videos.get(index).getTitle()); 255 | if (player != null) 256 | player.cueVideo(videos.get(index).getCode()); 257 | 258 | prevVideo.setEnabled(currentIndex > 0); 259 | nextVideo.setEnabled(currentIndex < videos.size() - 1); 260 | prevVideo2.setEnabled(currentIndex > 0); 261 | nextVideo2.setEnabled(currentIndex < videos.size() - 1); 262 | } 263 | 264 | private void loadVideosFor(int categoryId) { 265 | VideoAPIManager.getVideosFor(categoryId, new VideoAPIManager.VideoLoaderListener() { 266 | @Override 267 | public void onSuccess(ArrayList results) { 268 | videos = results; 269 | currentIndex = -1; 270 | setCurrentVideo(0); 271 | createVideoListAdapter(); 272 | } 273 | @Override 274 | public void onFailure(Error error) { 275 | } 276 | }); 277 | } 278 | 279 | private void createVideoListAdapter() { 280 | VideosListAdapter listAdapter = new VideosListAdapter(this.videos); 281 | allVideosListView.setAdapter(listAdapter); 282 | } 283 | 284 | private void setControlsEnabled() { 285 | } 286 | 287 | @Override 288 | public void onFullscreen(boolean isFullscreen) { 289 | fullscreen = isFullscreen; 290 | doLayout(); 291 | } 292 | 293 | @Override 294 | public void onConfigurationChanged(Configuration newConfig) { 295 | super.onConfigurationChanged(newConfig); 296 | doLayout(); 297 | } 298 | 299 | class VideosListAdapter implements ListAdapter { 300 | 301 | private ArrayList _videos; 302 | 303 | VideosListAdapter(ArrayList videos) { 304 | _videos = videos; 305 | } 306 | 307 | @Override 308 | public boolean areAllItemsEnabled() { 309 | return false; 310 | } 311 | 312 | @Override 313 | public boolean isEnabled(int position) { 314 | return position != currentIndex; 315 | } 316 | 317 | @Override 318 | public void registerDataSetObserver(DataSetObserver observer) { 319 | 320 | } 321 | 322 | @Override 323 | public void unregisterDataSetObserver(DataSetObserver observer) { 324 | 325 | } 326 | 327 | @Override 328 | public int getCount() { 329 | return _videos != null ? _videos.size() : 0; 330 | } 331 | 332 | @Override 333 | public VideoItem getItem(int position) { 334 | return _videos != null ? _videos.get(position) : null; 335 | } 336 | 337 | @Override 338 | public long getItemId(int position) { 339 | return _videos != null ? _videos.get(position).getVideoId() : 0; 340 | } 341 | 342 | @Override 343 | public boolean hasStableIds() { 344 | return true; 345 | } 346 | 347 | @Override 348 | public View getView(int position, View convertView, ViewGroup parent) { 349 | final VideoRowHolder holder; 350 | 351 | if (convertView == null) { 352 | LayoutInflater inflater = (LayoutInflater) getSystemService(Activity.LAYOUT_INFLATER_SERVICE); 353 | convertView = inflater.inflate(R.layout.row_videolist, null); 354 | holder = new VideoRowHolder(); 355 | holder.textView = (TextView) convertView.findViewById(R.id.videoTitleTextView); 356 | holder.imageView = (ImageView) convertView.findViewById(R.id.videoPreviewThumb); 357 | holder.textView.setTypeface(Typeface.createFromAsset(getAssets(), "fonts/Roboto-Light.ttf")); 358 | convertView.setTag(holder); 359 | } else { 360 | holder = (VideoRowHolder) convertView.getTag(); 361 | } 362 | 363 | holder.textView.setText((position + 1) + " - " + getItem(position).getTitle()); 364 | loadImageFor(holder, position); 365 | return convertView; 366 | } 367 | 368 | private void loadImageFor(final VideoRowHolder holder, final int position) { 369 | final String thumbUrl = getYoutubeVideoThumbUrl(getItem(position).getCode()); 370 | holder.imageView.setTag(thumbUrl); 371 | holder.imageView.setImageBitmap(null); 372 | ImageLoader.getInstance().loadImage( 373 | thumbUrl, 374 | new ImageSize((int) getResources().getDimension(R.dimen.videoThumbWidth), 375 | (int) getResources().getDimension(R.dimen.videoThumbHeight)), 376 | new SimpleImageLoadingListener() { 377 | @Override 378 | public void onLoadingFailed(String imageUri, View view, FailReason failReason) { 379 | (new Thread(new Runnable() { 380 | @Override 381 | public void run() { 382 | try { 383 | Thread.sleep(10, 0); 384 | loadImageFor(holder, position); 385 | } catch (InterruptedException e) { 386 | e.printStackTrace(); 387 | } 388 | } 389 | })).run(); 390 | } 391 | 392 | @Override 393 | public void onLoadingCancelled(String imageUri, View view) { 394 | (new Thread(new Runnable() { 395 | @Override 396 | public void run() { 397 | try { 398 | Thread.sleep(10, 0); 399 | loadImageFor(holder, position); 400 | } catch (InterruptedException e) { 401 | e.printStackTrace(); 402 | } 403 | } 404 | })).run(); 405 | 406 | } 407 | 408 | @Override 409 | public void onLoadingComplete(String imageUri, 410 | View view, Bitmap loadedImage) { 411 | if (holder.imageView.getTag().equals(imageUri)) { 412 | holder.imageView.setImageBitmap(loadedImage); 413 | } else { 414 | (new Thread(new Runnable() { 415 | @Override 416 | public void run() { 417 | try { 418 | Thread.sleep(10, 0); 419 | loadImageFor(holder, position); 420 | } catch (InterruptedException e) { 421 | e.printStackTrace(); 422 | } 423 | } 424 | })).run(); 425 | } 426 | } 427 | }); 428 | } 429 | 430 | private String getYoutubeVideoThumbUrl(String vid) { 431 | return String.format("http://img.youtube.com/vi/%s/0.jpg", vid); 432 | } 433 | 434 | @Override 435 | public int getItemViewType(int position) { 436 | return 0; 437 | } 438 | 439 | @Override 440 | public int getViewTypeCount() { 441 | return 1; 442 | } 443 | 444 | @Override 445 | public boolean isEmpty() { 446 | return false; 447 | } 448 | 449 | class VideoRowHolder { 450 | ImageView imageView; 451 | TextView textView; 452 | } 453 | 454 | } 455 | 456 | 457 | } 458 | -------------------------------------------------------------------------------- /app/src/main/java/com/thenewboston/thenewbostonvideotutorials/ui/YouTubeFailureRecoveryActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.thenewboston.thenewbostonvideotutorials.ui; 18 | 19 | import android.content.Intent; 20 | import android.widget.Toast; 21 | 22 | import com.google.android.youtube.player.YouTubeBaseActivity; 23 | import com.google.android.youtube.player.YouTubeInitializationResult; 24 | import com.google.android.youtube.player.YouTubePlayer; 25 | import com.thenewboston.thenewbostonvideotutorials.R; 26 | import com.thenewboston.thenewbostonvideotutorials.api.DeveloperKey; 27 | 28 | /** 29 | * An abstract activity which deals with recovering from errors which may occur during API 30 | * initialization, but can be corrected through user action. 31 | */ 32 | public abstract class YouTubeFailureRecoveryActivity extends YouTubeBaseActivity implements 33 | YouTubePlayer.OnInitializedListener { 34 | 35 | private static final int RECOVERY_DIALOG_REQUEST = 1; 36 | 37 | @Override 38 | public void onInitializationFailure(YouTubePlayer.Provider provider, 39 | YouTubeInitializationResult errorReason) { 40 | if (errorReason.isUserRecoverableError()) { 41 | errorReason.getErrorDialog(this, RECOVERY_DIALOG_REQUEST).show(); 42 | } else { 43 | String errorMessage = String.format(getString(R.string.error_player), errorReason.toString()); 44 | Toast.makeText(this, errorMessage, Toast.LENGTH_LONG).show(); 45 | } 46 | } 47 | 48 | @Override 49 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 50 | if (requestCode == RECOVERY_DIALOG_REQUEST) { 51 | // Retry initialization if user performed a recovery action 52 | getYouTubePlayerProvider().initialize(DeveloperKey.DEVELOPER_KEY, this); 53 | } 54 | } 55 | 56 | protected abstract YouTubePlayer.Provider getYouTubePlayerProvider(); 57 | 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/res/color/button_text_color.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/menu_icon_group_collapsed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/res/drawable-hdpi/menu_icon_group_collapsed.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/menu_icon_group_expanded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/res/drawable-hdpi/menu_icon_group_expanded.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/back_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/res/drawable-xxhdpi/back_arrow.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/group_collapsed.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/res/drawable-xxhdpi/group_collapsed.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/group_expanded.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/res/drawable-xxhdpi/group_expanded.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/menu_icon_home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/res/drawable-xxhdpi/menu_icon_home.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/menu_icon_logout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/res/drawable-xxhdpi/menu_icon_logout.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/menu_icon_message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/res/drawable-xxhdpi/menu_icon_message.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/menu_icon_others.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/res/drawable-xxhdpi/menu_icon_others.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/menu_icon_profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/res/drawable-xxhdpi/menu_icon_profile.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/menu_icon_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/res/drawable-xxhdpi/menu_icon_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/menu_icon_videos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/res/drawable-xxhdpi/menu_icon_videos.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/next_button_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/res/drawable-xxhdpi/next_button_disabled.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/next_button_enabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/res/drawable-xxhdpi/next_button_enabled.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/prev_button_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/res/drawable-xxhdpi/prev_button_disabled.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/prev_button_enabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/res/drawable-xxhdpi/prev_button_enabled.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/res/drawable-xxhdpi/shadow.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Videos-Android-App/3ad9db183bb18249aab3412451ff1d574776c584/app/src/main/res/drawable-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/button_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/group_indicator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/next_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/prev_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/red_badge_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shadow_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/layout-sw600dp-land/activity_fullscreen_videoplayer.xml: -------------------------------------------------------------------------------- 1 | 16 | 21 | 22 | 27 | 28 | 32 | 33 | 38 | 39 | 43 | 44 | 47 | 48 | 52 | 53 | 54 | 63 | 64 |