diff --git a/app-java/build.gradle b/app-java/build.gradle new file mode 100644 index 0000000..5611247 --- /dev/null +++ b/app-java/build.gradle @@ -0,0 +1,53 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 32 + + defaultConfig { + minSdkVersion 18 + targetSdkVersion 32 + versionCode 20 + versionName "3.0" + applicationId="com.google.sample.cast.refplayer" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + + lintOptions.abortOnError false + + sourceSets { + main { + res.srcDirs = [projectDir.path+'/../resources'] + } + androidTest { + java.srcDirs = ['androidTest'] + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + namespace 'com.google.sample.cast.refplayer' +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.5.0' + implementation 'androidx.mediarouter:mediarouter:1.3.1' + implementation 'androidx.recyclerview:recyclerview:1.2.1' + implementation 'com.google.android.gms:play-services-cast-framework:21.1.0' + implementation 'com.android.volley:volley:1.2.1' + + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.4.0' + androidTestImplementation 'androidx.test:runner:1.4.0' + androidTestImplementation 'androidx.test:rules:1.4.0' + androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0' + testImplementation 'junit:junit:4.12' +} \ No newline at end of file diff --git a/AndroidManifest.xml b/app-java/src/main/AndroidManifest.xml similarity index 97% rename from AndroidManifest.xml rename to app-java/src/main/AndroidManifest.xml index e301489..92d5d2c 100644 --- a/AndroidManifest.xml +++ b/app-java/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ - + diff --git a/src/com/google/sample/cast/refplayer/CastOptionsProvider.java b/app-java/src/main/java/com/google/sample/cast/refplayer/CastOptionsProvider.java similarity index 98% rename from src/com/google/sample/cast/refplayer/CastOptionsProvider.java rename to app-java/src/main/java/com/google/sample/cast/refplayer/CastOptionsProvider.java index b27e636..9496cf5 100644 --- a/src/com/google/sample/cast/refplayer/CastOptionsProvider.java +++ b/app-java/src/main/java/com/google/sample/cast/refplayer/CastOptionsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Google LLC. All Rights Reserved. + * Copyright 2022 Google LLC. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/com/google/sample/cast/refplayer/VideoBrowserActivity.java b/app-java/src/main/java/com/google/sample/cast/refplayer/VideoBrowserActivity.java similarity index 94% rename from src/com/google/sample/cast/refplayer/VideoBrowserActivity.java rename to app-java/src/main/java/com/google/sample/cast/refplayer/VideoBrowserActivity.java index 0afca2e..2026817 100644 --- a/src/com/google/sample/cast/refplayer/VideoBrowserActivity.java +++ b/app-java/src/main/java/com/google/sample/cast/refplayer/VideoBrowserActivity.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Google LLC. All Rights Reserved. + * Copyright 2022 Google LLC. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,6 +39,9 @@ import android.view.Menu; import android.view.MenuItem; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + /** * The main activity that displays the list of videos. */ @@ -54,6 +57,7 @@ public class VideoBrowserActivity extends AppCompatActivity { private Toolbar mToolbar; private IntroductoryOverlay mIntroductoryOverlay; private CastStateListener mCastStateListener; + private Executor localExecutor = Executors.newSingleThreadExecutor(); private class MySessionManagerListener implements SessionManagerListener { @@ -121,7 +125,7 @@ public void onCastStateChanged(int newState) { } } }; - mCastContext = CastContext.getSharedInstance(this); + mCastContext = CastContext.getSharedInstance(this,localExecutor).getResult(); } private void setupActionBar() { @@ -184,7 +188,9 @@ protected void onResume() { mSessionManagerListener, CastSession.class); intentToJoin(); if (mCastSession == null) { - mCastSession = CastContext.getSharedInstance(this).getSessionManager() + mCastSession = CastContext.getSharedInstance(this,localExecutor) + .getResult() + .getSessionManager() .getCurrentCastSession(); } if (mQueueMenuItem != null) { diff --git a/src/com/google/sample/cast/refplayer/browser/VideoBrowserFragment.java b/app-java/src/main/java/com/google/sample/cast/refplayer/browser/VideoBrowserFragment.java similarity index 91% rename from src/com/google/sample/cast/refplayer/browser/VideoBrowserFragment.java rename to app-java/src/main/java/com/google/sample/cast/refplayer/browser/VideoBrowserFragment.java index b439efa..9dec53e 100644 --- a/src/com/google/sample/cast/refplayer/browser/VideoBrowserFragment.java +++ b/app-java/src/main/java/com/google/sample/cast/refplayer/browser/VideoBrowserFragment.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Google LLC. All Rights Reserved. + * Copyright 2022 Google LLC. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,6 +41,8 @@ import android.widget.ImageButton; import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; /** * A fragment to host a list view of the video catalog. @@ -57,7 +59,8 @@ public class VideoBrowserFragment extends Fragment implements VideoListAdapter.I private View mLoadingView; private final SessionManagerListener mSessionManagerListener = new MySessionManagerListener(); - + private Executor localExecutor = Executors.newSingleThreadExecutor(); + public VideoBrowserFragment() { } @@ -78,7 +81,7 @@ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { mRecyclerView.setLayoutManager(layoutManager); mAdapter = new VideoListAdapter(this, getContext()); mRecyclerView.setAdapter(mAdapter); - getLoaderManager().initLoader(0, null, this); + LoaderManager.getInstance(this).initLoader(0, null, this); } @Override @@ -88,7 +91,7 @@ public void itemClicked(View view, MediaInfo item, int position) { } else { String transitionName = getString(R.string.transition_image); VideoListAdapter.ViewHolder viewHolder = - (VideoListAdapter.ViewHolder) mRecyclerView.findViewHolderForPosition(position); + (VideoListAdapter.ViewHolder) mRecyclerView.findViewHolderForLayoutPosition(position); Pair imagePair = Pair .create((View) viewHolder.getImageView(), transitionName); ActivityOptionsCompat options = ActivityOptionsCompat @@ -120,14 +123,18 @@ public void onLoaderReset(Loader> loader) { @Override public void onStart() { - CastContext.getSharedInstance(getContext()).getSessionManager() + CastContext.getSharedInstance(getContext(),localExecutor) + .getResult() + .getSessionManager() .addSessionManagerListener(mSessionManagerListener, CastSession.class); super.onStart(); } @Override public void onStop() { - CastContext.getSharedInstance(getContext()).getSessionManager() + CastContext.getSharedInstance(getContext(),localExecutor) + .getResult() + .getSessionManager() .removeSessionManagerListener(mSessionManagerListener, CastSession.class); super.onStop(); } diff --git a/src/com/google/sample/cast/refplayer/browser/VideoItemLoader.java b/app-java/src/main/java/com/google/sample/cast/refplayer/browser/VideoItemLoader.java similarity index 97% rename from src/com/google/sample/cast/refplayer/browser/VideoItemLoader.java rename to app-java/src/main/java/com/google/sample/cast/refplayer/browser/VideoItemLoader.java index 46ac7d3..f6036b5 100644 --- a/src/com/google/sample/cast/refplayer/browser/VideoItemLoader.java +++ b/app-java/src/main/java/com/google/sample/cast/refplayer/browser/VideoItemLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Google LLC. All Rights Reserved. + * Copyright 2022 Google LLC. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/com/google/sample/cast/refplayer/browser/VideoListAdapter.java b/app-java/src/main/java/com/google/sample/cast/refplayer/browser/VideoListAdapter.java similarity index 95% rename from src/com/google/sample/cast/refplayer/browser/VideoListAdapter.java rename to app-java/src/main/java/com/google/sample/cast/refplayer/browser/VideoListAdapter.java index 8634e28..eb836f0 100644 --- a/src/com/google/sample/cast/refplayer/browser/VideoListAdapter.java +++ b/app-java/src/main/java/com/google/sample/cast/refplayer/browser/VideoListAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Google LLC. All Rights Reserved. + * Copyright 2022 Google LLC. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,8 @@ import com.google.sample.cast.refplayer.R; import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; /** * An {@link ArrayAdapter} to populate the list of videos. @@ -45,6 +47,7 @@ public class VideoListAdapter extends RecyclerView.Adapter videos; + private Executor localExecutor = Executors.newSingleThreadExecutor(); public VideoListAdapter(ItemClickListener clickListener, Context context) { mClickListener = clickListener; @@ -85,7 +88,9 @@ public void onClick(View view) { mClickListener.itemClicked(view, item, position); } }); - CastSession castSession = CastContext.getSharedInstance(mAppContext).getSessionManager() + CastSession castSession = CastContext.getSharedInstance(mAppContext,localExecutor) + .getResult() + .getSessionManager() .getCurrentCastSession(); viewHolder.mMenu.setVisibility( (castSession != null && castSession.isConnected()) ? View.VISIBLE : View.GONE); diff --git a/src/com/google/sample/cast/refplayer/browser/VideoProvider.java b/app-java/src/main/java/com/google/sample/cast/refplayer/browser/VideoProvider.java similarity index 99% rename from src/com/google/sample/cast/refplayer/browser/VideoProvider.java rename to app-java/src/main/java/com/google/sample/cast/refplayer/browser/VideoProvider.java index 6080e9d..5cdea65 100644 --- a/src/com/google/sample/cast/refplayer/browser/VideoProvider.java +++ b/app-java/src/main/java/com/google/sample/cast/refplayer/browser/VideoProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Google LLC. All Rights Reserved. + * Copyright 2022 Google LLC. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -99,6 +99,7 @@ protected JSONObject parseUrl(String urlString) { is.close(); } catch (IOException e) { // ignore + Log.w(TAG,"Ignore",e); } } } diff --git a/src/com/google/sample/cast/refplayer/expandedcontrols/ExpandedControlsActivity.java b/app-java/src/main/java/com/google/sample/cast/refplayer/expandedcontrols/ExpandedControlsActivity.java similarity index 96% rename from src/com/google/sample/cast/refplayer/expandedcontrols/ExpandedControlsActivity.java rename to app-java/src/main/java/com/google/sample/cast/refplayer/expandedcontrols/ExpandedControlsActivity.java index df030a9..1d97b1b 100644 --- a/src/com/google/sample/cast/refplayer/expandedcontrols/ExpandedControlsActivity.java +++ b/app-java/src/main/java/com/google/sample/cast/refplayer/expandedcontrols/ExpandedControlsActivity.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Google LLC. All Rights Reserved. + * Copyright 2022 Google LLC. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/com/google/sample/cast/refplayer/mediaplayer/LocalPlayerActivity.java b/app-java/src/main/java/com/google/sample/cast/refplayer/mediaplayer/LocalPlayerActivity.java similarity index 98% rename from src/com/google/sample/cast/refplayer/mediaplayer/LocalPlayerActivity.java rename to app-java/src/main/java/com/google/sample/cast/refplayer/mediaplayer/LocalPlayerActivity.java index 0f3bc9f..21ae5e4 100644 --- a/src/com/google/sample/cast/refplayer/mediaplayer/LocalPlayerActivity.java +++ b/app-java/src/main/java/com/google/sample/cast/refplayer/mediaplayer/LocalPlayerActivity.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Google LLC. All Rights Reserved. + * Copyright 2022 Google LLC. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -73,6 +73,8 @@ import java.util.Timer; import java.util.TimerTask; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; /** * Activity for the local media player. @@ -107,6 +109,7 @@ public class LocalPlayerActivity extends AppCompatActivity { private SessionManagerListener mSessionManagerListener; private MenuItem mQueueMenuItem; private ImageLoader mImageLoader; + private Executor localExecutor = Executors.newSingleThreadExecutor(); /** * indicates whether we are doing a local or a remote playback @@ -130,7 +133,8 @@ protected void onCreate(Bundle savedInstanceState) { loadViews(); setupControlsCallbacks(); setupCastListener(); - mCastContext = CastContext.getSharedInstance(this); + mCastContext = CastContext.getSharedInstance(this,localExecutor) + .getResult(); mCastSession = mCastContext.getSessionManager().getCurrentCastSession(); // see what we need to play and where Bundle bundle = getIntent().getExtras(); @@ -586,7 +590,7 @@ public void onStartTrackingTouch(SeekBar seekBar) { @Override public void onProgressChanged(SeekBar seekBar, int progress, - boolean fromUser) { + boolean fromUser) { mStartText.setText(Utils.formatMillis(progress)); } }); @@ -674,12 +678,11 @@ public void onConfigurationChanged(Configuration newConfig) { } private void updateMetadata(boolean visible) { - Point displaySize; + Point displaySize = Utils.getDisplaySize(this); if (!visible) { mDescriptionView.setVisibility(View.GONE); mTitleView.setVisibility(View.GONE); mAuthorView.setVisibility(View.GONE); - displaySize = Utils.getDisplaySize(this); RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(displaySize.x, displaySize.y + getSupportActionBar().getHeight()); @@ -695,7 +698,6 @@ private void updateMetadata(boolean visible) { mDescriptionView.setVisibility(View.VISIBLE); mTitleView.setVisibility(View.VISIBLE); mAuthorView.setVisibility(View.VISIBLE); - displaySize = Utils.getDisplaySize(this); RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(displaySize.x, (int) (displaySize.x * mAspectRatio)); diff --git a/src/com/google/sample/cast/refplayer/queue/QueueDataProvider.java b/app-java/src/main/java/com/google/sample/cast/refplayer/queue/QueueDataProvider.java similarity index 95% rename from src/com/google/sample/cast/refplayer/queue/QueueDataProvider.java rename to app-java/src/main/java/com/google/sample/cast/refplayer/queue/QueueDataProvider.java index 352913b..f2c547a 100644 --- a/src/com/google/sample/cast/refplayer/queue/QueueDataProvider.java +++ b/app-java/src/main/java/com/google/sample/cast/refplayer/queue/QueueDataProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Google LLC. All Rights Reserved. + * Copyright 2022 Google LLC. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,6 +27,9 @@ import com.google.android.gms.cast.framework.media.MediaQueue; import com.google.android.gms.cast.framework.media.RemoteMediaClient; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + /** * A singleton to manage the queue. Upon instantiation, it syncs up its own copy of the queue with * the one that the VideoCastManager holds. After that point, it maintains an up-to-date version of @@ -52,12 +55,15 @@ public class QueueDataProvider { private MediaQueueItem mUpcomingItem; private OnQueueDataChangedListener mListener; private boolean mDetachedQueue = true; + private Executor localExecutor = Executors.newSingleThreadExecutor(); private QueueDataProvider(Context context) { mAppContext = context.getApplicationContext(); mCurrentItem = null; - CastContext.getSharedInstance(mAppContext).getSessionManager().addSessionManagerListener( - mSessionManagerListener, CastSession.class); + CastContext.getSharedInstance(mAppContext,localExecutor) + .getResult() + .getSessionManager() + .addSessionManagerListener(mSessionManagerListener, CastSession.class); MediaQueue queue = getMediaQueue(); if (queue != null) { queue.setCacheCapacity(30); @@ -254,7 +260,9 @@ private void updateMediaQueue() { } private RemoteMediaClient getRemoteMediaClient() { - CastSession castSession = CastContext.getSharedInstance(mAppContext).getSessionManager() + CastSession castSession = CastContext.getSharedInstance(mAppContext,localExecutor) + .getResult() + .getSessionManager() .getCurrentCastSession(); if (castSession == null || !castSession.isConnected()) { Log.w(TAG, "Trying to get a RemoteMediaClient when no CastSession is started."); diff --git a/src/com/google/sample/cast/refplayer/queue/ui/QueueItemTouchHelperCallback.java b/app-java/src/main/java/com/google/sample/cast/refplayer/queue/ui/QueueItemTouchHelperCallback.java similarity index 97% rename from src/com/google/sample/cast/refplayer/queue/ui/QueueItemTouchHelperCallback.java rename to app-java/src/main/java/com/google/sample/cast/refplayer/queue/ui/QueueItemTouchHelperCallback.java index 74cb343..422b809 100644 --- a/src/com/google/sample/cast/refplayer/queue/ui/QueueItemTouchHelperCallback.java +++ b/app-java/src/main/java/com/google/sample/cast/refplayer/queue/ui/QueueItemTouchHelperCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Google LLC. All Rights Reserved. + * Copyright 2022 Google LLC. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -71,7 +71,7 @@ public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHo if (viewHolder instanceof QueueListAdapter.QueueItemViewHolder) { QueueListAdapter.QueueItemViewHolder queueHolder = (QueueListAdapter.QueueItemViewHolder) viewHolder; - ViewCompat.setTranslationX(queueHolder.mContainer, dX); + queueHolder.mContainer.setTranslationX(dX); } } else { super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); diff --git a/src/com/google/sample/cast/refplayer/queue/ui/QueueListAdapter.java b/app-java/src/main/java/com/google/sample/cast/refplayer/queue/ui/QueueListAdapter.java similarity index 98% rename from src/com/google/sample/cast/refplayer/queue/ui/QueueListAdapter.java rename to app-java/src/main/java/com/google/sample/cast/refplayer/queue/ui/QueueListAdapter.java index c9b48aa..600f792 100644 --- a/src/com/google/sample/cast/refplayer/queue/ui/QueueListAdapter.java +++ b/app-java/src/main/java/com/google/sample/cast/refplayer/queue/ui/QueueListAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Google LLC. All Rights Reserved. + * Copyright 2022 Google LLC. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,6 +53,8 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; /** * An adapter to show the list of queue items. @@ -70,6 +72,7 @@ public class QueueListAdapter extends MediaQueueRecyclerViewAdapter { @@ -148,7 +151,7 @@ protected void onCreate(Bundle savedInstanceState) { } setupActionBar(); mEmptyView = findViewById(R.id.empty); - mCastContext = CastContext.getSharedInstance(this); + mCastContext = CastContext.getSharedInstance(this,localExecutor).getResult(); } diff --git a/src/com/google/sample/cast/refplayer/queue/ui/QueueListViewFragment.java b/app-java/src/main/java/com/google/sample/cast/refplayer/queue/ui/QueueListViewFragment.java similarity index 91% rename from src/com/google/sample/cast/refplayer/queue/ui/QueueListViewFragment.java rename to app-java/src/main/java/com/google/sample/cast/refplayer/queue/ui/QueueListViewFragment.java index 34c6263..f2d9a4b 100644 --- a/src/com/google/sample/cast/refplayer/queue/ui/QueueListViewFragment.java +++ b/app-java/src/main/java/com/google/sample/cast/refplayer/queue/ui/QueueListViewFragment.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Google LLC. All Rights Reserved. + * Copyright 2022 Google LLC. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,6 +36,9 @@ import android.view.View; import android.view.ViewGroup; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + /** * A fragment to show the list of queue items. */ @@ -45,6 +48,7 @@ public class QueueListViewFragment extends Fragment private static final String TAG = "QueueListViewFragment"; private QueueDataProvider mProvider; private ItemTouchHelper mItemTouchHelper; + private Executor localExecutor = Executors.newSingleThreadExecutor(); public QueueListViewFragment() { super(); @@ -128,8 +132,10 @@ private void onContainerClicked(View view) { // We selected the one that is currently playing so we take the user to the // full screen controller CastSession castSession = CastContext.getSharedInstance( - getContext().getApplicationContext()) - .getSessionManager().getCurrentCastSession(); + getContext().getApplicationContext(),localExecutor) + .getResult() + .getSessionManager() + .getCurrentCastSession(); if (castSession != null) { Intent intent = new Intent(getActivity(), ExpandedControlsActivity.class); startActivity(intent); @@ -148,7 +154,9 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) { private RemoteMediaClient getRemoteMediaClient() { CastSession castSession = - CastContext.getSharedInstance(getContext()).getSessionManager() + CastContext.getSharedInstance(getContext(),localExecutor) + .getResult() + .getSessionManager() .getCurrentCastSession(); if (castSession != null && castSession.isConnected()) { return castSession.getRemoteMediaClient(); diff --git a/src/com/google/sample/cast/refplayer/settings/CastPreference.java b/app-java/src/main/java/com/google/sample/cast/refplayer/settings/CastPreference.java similarity index 97% rename from src/com/google/sample/cast/refplayer/settings/CastPreference.java rename to app-java/src/main/java/com/google/sample/cast/refplayer/settings/CastPreference.java index ed961dc..1914090 100644 --- a/src/com/google/sample/cast/refplayer/settings/CastPreference.java +++ b/app-java/src/main/java/com/google/sample/cast/refplayer/settings/CastPreference.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Google LLC. All Rights Reserved. + * Copyright 2022 Google LLC. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/com/google/sample/cast/refplayer/utils/CustomVolleyRequest.java b/app-java/src/main/java/com/google/sample/cast/refplayer/utils/CustomVolleyRequest.java similarity index 77% rename from src/com/google/sample/cast/refplayer/utils/CustomVolleyRequest.java rename to app-java/src/main/java/com/google/sample/cast/refplayer/utils/CustomVolleyRequest.java index de4664d..df795c6 100644 --- a/src/com/google/sample/cast/refplayer/utils/CustomVolleyRequest.java +++ b/app-java/src/main/java/com/google/sample/cast/refplayer/utils/CustomVolleyRequest.java @@ -1,3 +1,18 @@ +/* + * Copyright 2022 Google LLC. All Rights Reserved. + * + * 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.google.sample.cast.refplayer.utils; import android.content.Context; diff --git a/src/com/google/sample/cast/refplayer/utils/Utils.java b/app-java/src/main/java/com/google/sample/cast/refplayer/utils/Utils.java similarity index 99% rename from src/com/google/sample/cast/refplayer/utils/Utils.java rename to app-java/src/main/java/com/google/sample/cast/refplayer/utils/Utils.java index b1173c4..947a428 100644 --- a/src/com/google/sample/cast/refplayer/utils/Utils.java +++ b/app-java/src/main/java/com/google/sample/cast/refplayer/utils/Utils.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Google LLC. All Rights Reserved. + * Copyright 2022 Google LLC. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/app-kotlin/build.gradle b/app-kotlin/build.gradle new file mode 100644 index 0000000..5d32fc4 --- /dev/null +++ b/app-kotlin/build.gradle @@ -0,0 +1,58 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' + +android { + compileSdkVersion 32 + + defaultConfig { + minSdkVersion 18 + targetSdkVersion 32 + versionCode 20 + versionName "3.0" + applicationId="com.google.sample.cast.refplayer" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + + lintOptions.abortOnError false + + sourceSets { + main { + res.srcDirs = [projectDir.path+'/../resources'] + } + androidTest { + java.srcDirs = ['androidTest'] + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + namespace 'com.google.sample.cast.refplayer' +} + +dependencies { + implementation 'androidx.mediarouter:mediarouter:1.3.1' + implementation 'androidx.recyclerview:recyclerview:1.2.1' + implementation 'com.google.android.gms:play-services-cast-framework:21.1.0' + implementation 'com.android.volley:volley:1.2.1' + + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.4.0' + androidTestImplementation 'androidx.test:runner:1.4.0' + androidTestImplementation 'androidx.test:rules:1.4.0' + androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0' + testImplementation 'junit:junit:4.12' + + implementation 'androidx.preference:preference-ktx:1.2.0' + implementation "androidx.core:core-ktx:1.8.0" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" +} \ No newline at end of file diff --git a/app-kotlin/src/main/AndroidManifest.xml b/app-kotlin/src/main/AndroidManifest.xml new file mode 100644 index 0000000..92d5d2c --- /dev/null +++ b/app-kotlin/src/main/AndroidManifest.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/CastOptionsProvider.kt b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/CastOptionsProvider.kt new file mode 100644 index 0000000..49d852f --- /dev/null +++ b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/CastOptionsProvider.kt @@ -0,0 +1,85 @@ +/* + * Copyright 2022 Google LLC. All Rights Reserved. + * + * 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.google.sample.cast.refplayer + +import com.google.android.gms.cast.framework.OptionsProvider +import android.content.Context +import com.google.android.gms.cast.LaunchOptions +import com.google.android.gms.cast.MediaMetadata +import com.google.android.gms.cast.framework.CastOptions +import com.google.android.gms.cast.framework.media.NotificationOptions +import com.google.android.gms.cast.framework.media.MediaIntentReceiver +import com.google.android.gms.cast.framework.media.CastMediaOptions +import com.google.android.gms.cast.framework.SessionProvider +import com.google.android.gms.cast.framework.media.ImagePicker +import com.google.android.gms.cast.framework.media.ImageHints +import com.google.android.gms.common.images.WebImage +import com.google.sample.cast.refplayer.expandedcontrols.ExpandedControlsActivity + +/** + * Implements [OptionsProvider] to provide [CastOptions]. + */ +class CastOptionsProvider : OptionsProvider { + override fun getCastOptions(context: Context): CastOptions { + val notificationOptions = NotificationOptions.Builder() + .setActions( + listOf( + MediaIntentReceiver.ACTION_SKIP_NEXT, + MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK, + MediaIntentReceiver.ACTION_STOP_CASTING + ), intArrayOf(1, 2) + ) + .setTargetActivityClassName(ExpandedControlsActivity::class.java.name) + .build() + val mediaOptions = CastMediaOptions.Builder() + .setImagePicker(ImagePickerImpl()) + .setNotificationOptions(notificationOptions) + .setExpandedControllerActivityClassName(ExpandedControlsActivity::class.java.name) + .build() + /** Following lines enable Cast Connect */ + val launchOptions = LaunchOptions.Builder() + .setAndroidReceiverCompatible(true) + .build() + return CastOptions.Builder() + .setLaunchOptions(launchOptions) + .setReceiverApplicationId(context.getString(R.string.app_id)) + .setCastMediaOptions(mediaOptions) + .build() + } + + override fun getAdditionalSessionProviders(appContext: Context): List? { + return null + } + + private class ImagePickerImpl : ImagePicker() { + override fun onPickImage(mediaMetadata: MediaMetadata, hints: ImageHints): WebImage? { + val type = hints.type + if (!mediaMetadata.hasImages()) { + return null + } + val images = mediaMetadata.images + return if (images.size == 1) { + images[0] + } else { + if (type == IMAGE_TYPE_MEDIA_ROUTE_CONTROLLER_DIALOG_BACKGROUND) { + images[0] + } else { + images[1] + } + } + } + } +} \ No newline at end of file diff --git a/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/VideoBrowserActivity.kt b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/VideoBrowserActivity.kt new file mode 100644 index 0000000..af37a48 --- /dev/null +++ b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/VideoBrowserActivity.kt @@ -0,0 +1,191 @@ +/* + * Copyright 2022 Google LLC. All Rights Reserved. + * + * 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.google.sample.cast.refplayer + +import androidx.appcompat.app.AppCompatActivity +import com.google.android.gms.cast.framework.CastContext +import com.google.android.gms.cast.framework.SessionManagerListener +import com.google.android.gms.cast.framework.CastSession +import android.view.MenuItem +import com.google.android.gms.cast.framework.IntroductoryOverlay +import com.google.android.gms.cast.framework.CastStateListener +import android.os.Bundle +import com.google.android.gms.cast.framework.CastState +import android.view.View +import android.content.Intent +import android.net.Uri +import android.os.Handler +import android.os.Looper +import android.util.Log +import android.view.Menu +import com.google.android.gms.cast.framework.CastButtonFactory +import android.view.KeyEvent +import androidx.appcompat.widget.Toolbar +import com.google.sample.cast.refplayer.queue.ui.QueueListViewActivity +import com.google.sample.cast.refplayer.settings.CastPreference +import java.util.concurrent.Executor +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +/** + * The main activity that displays the list of videos. + */ +class VideoBrowserActivity : AppCompatActivity() { + private var mCastContext: CastContext? = null + private val mSessionManagerListener: SessionManagerListener = MySessionManagerListener() + private var mCastSession: CastSession? = null + private var mediaRouteMenuItem: MenuItem? = null + private var mQueueMenuItem: MenuItem? = null + private var mToolbar: Toolbar? = null + private var mIntroductoryOverlay: IntroductoryOverlay? = null + private var mCastStateListener: CastStateListener? = null + private val castExecutor: Executor = Executors.newSingleThreadExecutor(); + + private inner class MySessionManagerListener : SessionManagerListener { + override fun onSessionEnded(session: CastSession, error: Int) { + if (session === mCastSession) { + mCastSession = null + } + invalidateOptionsMenu() + } + + override fun onSessionResumed(session: CastSession, wasSuspended: Boolean) { + mCastSession = session + invalidateOptionsMenu() + } + + override fun onSessionStarted(session: CastSession, sessionId: String) { + mCastSession = session + invalidateOptionsMenu() + } + + override fun onSessionStarting(session: CastSession) {} + override fun onSessionStartFailed(session: CastSession, error: Int) {} + override fun onSessionEnding(session: CastSession) {} + override fun onSessionResuming(session: CastSession, sessionId: String) {} + override fun onSessionResumeFailed(session: CastSession, error: Int) {} + override fun onSessionSuspended(session: CastSession, reason: Int) {} + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.video_browser) + setupActionBar() + mCastStateListener = CastStateListener { newState -> + if (newState != CastState.NO_DEVICES_AVAILABLE) { + showIntroductoryOverlay() + } + } + mCastContext = CastContext.getSharedInstance(this,castExecutor).result + } + + private fun setupActionBar() { + mToolbar = findViewById(R.id.toolbar) as Toolbar + setSupportActionBar(mToolbar) + } + + private fun intentToJoin() { + val intent = intent + val intentToJoinUri = Uri.parse("https://castvideos.com/cast/join") + Log.i(TAG, "URI passed: $intentToJoinUri") + if (intent.data != null && intent.data == intentToJoinUri) { + mCastContext!!.sessionManager.startSession(intent) + Log.i(TAG, "Uri Joined: $intentToJoinUri") + } + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + super.onCreateOptionsMenu(menu) + menuInflater.inflate(R.menu.browse, menu) + mediaRouteMenuItem = CastButtonFactory.setUpMediaRouteButton( + applicationContext, menu, + R.id.media_route_menu_item + ) + mQueueMenuItem = menu.findItem(R.id.action_show_queue) + showIntroductoryOverlay() + return true + } + + override fun onPrepareOptionsMenu(menu: Menu): Boolean { + menu.findItem(R.id.action_show_queue).isVisible = + mCastSession != null && mCastSession!!.isConnected + return super.onPrepareOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + val intent: Intent + if (item.itemId == R.id.action_settings) { + intent = Intent(this@VideoBrowserActivity, CastPreference::class.java) + startActivity(intent) + } else if (item.itemId == R.id.action_show_queue) { + intent = Intent(this@VideoBrowserActivity, QueueListViewActivity::class.java) + startActivity(intent) + } + return true + } + + override fun dispatchKeyEvent(event: KeyEvent): Boolean { + return (mCastContext!!.onDispatchVolumeKeyEventBeforeJellyBean(event) + || super.dispatchKeyEvent(event)) + } + + override fun onResume() { + mCastContext!!.addCastStateListener(mCastStateListener!!) + mCastContext!!.sessionManager.addSessionManagerListener( + mSessionManagerListener, CastSession::class.java + ) + intentToJoin() + if (mCastSession == null) { + mCastSession = CastContext.getSharedInstance(this,castExecutor).result.sessionManager + .currentCastSession + } + if (mQueueMenuItem != null) { + mQueueMenuItem!!.isVisible = mCastSession != null && mCastSession!!.isConnected + } + super.onResume() + } + + override fun onPause() { + mCastContext!!.removeCastStateListener(mCastStateListener!!) + mCastContext!!.sessionManager.removeSessionManagerListener( + mSessionManagerListener, CastSession::class.java + ) + super.onPause() + } + + private fun showIntroductoryOverlay() { + if (mIntroductoryOverlay != null) { + mIntroductoryOverlay!!.remove() + } + if (mediaRouteMenuItem != null && mediaRouteMenuItem!!.isVisible) { + Handler(Looper.getMainLooper()).post { + mIntroductoryOverlay = IntroductoryOverlay.Builder( + this@VideoBrowserActivity, mediaRouteMenuItem!! + ) + .setTitleText(getString(R.string.introducing_cast)) + .setOverlayColor(R.color.primary) + .setSingleTime() + .setOnOverlayDismissedListener { mIntroductoryOverlay = null } + .build() + mIntroductoryOverlay!!.show() + } + } + } + + companion object { + private const val TAG = "VideoBrowserActivity" + } +} \ No newline at end of file diff --git a/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/browser/VideoBrowserFragment.kt b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/browser/VideoBrowserFragment.kt new file mode 100644 index 0000000..2e0e7e3 --- /dev/null +++ b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/browser/VideoBrowserFragment.kt @@ -0,0 +1,141 @@ +/* + * Copyright 2022 Google LLC. All Rights Reserved. + * + * 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.google.sample.cast.refplayer.browser + +import com.google.android.gms.cast.MediaInfo +import androidx.recyclerview.widget.RecyclerView +import android.view.ViewGroup +import android.view.View +import android.view.LayoutInflater +import com.google.sample.cast.refplayer.R +import com.google.android.gms.cast.framework.CastSession +import com.google.android.gms.cast.framework.CastContext +import com.google.android.gms.cast.framework.SessionManagerListener +import android.os.Bundle +import androidx.recyclerview.widget.LinearLayoutManager +import android.widget.ImageButton +import androidx.core.app.ActivityOptionsCompat +import android.content.Intent +import androidx.core.app.ActivityCompat +import androidx.core.util.Pair +import androidx.fragment.app.Fragment +import androidx.loader.app.LoaderManager +import androidx.loader.content.Loader +import com.google.sample.cast.refplayer.mediaplayer.LocalPlayerActivity +import com.google.sample.cast.refplayer.utils.Utils +import java.util.concurrent.Executor +import java.util.concurrent.Executors + +/** + * A fragment to host a list view of the video catalog. + */ +class VideoBrowserFragment : Fragment(), VideoListAdapter.ItemClickListener, + LoaderManager.LoaderCallbacks?> { + private var mRecyclerView: RecyclerView? = null + private var mAdapter: VideoListAdapter? = null + private var mEmptyView: View? = null + private var mLoadingView: View? = null + private val mSessionManagerListener: SessionManagerListener = MySessionManagerListener() + private val vidBrowserExecutor: Executor = Executors.newCachedThreadPool(); + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { + return inflater.inflate(R.layout.video_browser_fragment, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + mRecyclerView = requireView().findViewById(R.id.list) as RecyclerView + mEmptyView = requireView().findViewById(R.id.empty_view) + mLoadingView = requireView().findViewById(R.id.progress_indicator) + val layoutManager = LinearLayoutManager(activity) + layoutManager.orientation = LinearLayoutManager.VERTICAL + mRecyclerView!!.layoutManager = layoutManager + mAdapter = VideoListAdapter(this, context) + mRecyclerView!!.adapter = mAdapter + LoaderManager.getInstance(this).initLoader(0, null, this) + } + + override fun itemClicked(view: View?, item: MediaInfo?, position: Int) { + if (view is ImageButton) { + Utils.showQueuePopup(activity, view, item) + } else { + val transitionName = getString(R.string.transition_image) + val viewHolder = mRecyclerView!!.findViewHolderForLayoutPosition(position) as VideoListAdapter.ViewHolder? + val imagePair = Pair + .create(viewHolder!!.imageView as View, transitionName) + val options = ActivityOptionsCompat + .makeSceneTransitionAnimation(requireActivity(), imagePair) + val intent = Intent(activity, LocalPlayerActivity::class.java) + intent.putExtra("media", item) + intent.putExtra("shouldStart", false) + ActivityCompat.startActivity(requireActivity(), intent, options.toBundle()) + } + } + + override fun onCreateLoader(id: Int, args: Bundle?): Loader?> { + return VideoItemLoader(activity, CATALOG_URL) + } + + override fun onLoadFinished(loader: Loader?>, data: List?) { + mAdapter!!.setData(data) + mLoadingView!!.visibility = View.GONE + mEmptyView!!.visibility = if (null == data || data.isEmpty()) View.VISIBLE else View.GONE + } + + override fun onLoaderReset(loader: Loader?>) { + mAdapter!!.setData(null) + } + + override fun onStart() { + CastContext.getSharedInstance(requireContext(),vidBrowserExecutor).result.sessionManager + .addSessionManagerListener(mSessionManagerListener, CastSession::class.java) + super.onStart() + } + + override fun onStop() { + CastContext.getSharedInstance(requireContext(),vidBrowserExecutor).result.sessionManager + .removeSessionManagerListener(mSessionManagerListener, CastSession::class.java) + super.onStop() + } + + private inner class MySessionManagerListener : SessionManagerListener { + override fun onSessionEnded(session: CastSession, error: Int) { + mAdapter!!.notifyDataSetChanged() + } + + override fun onSessionResumed(session: CastSession, wasSuspended: Boolean) { + mAdapter!!.notifyDataSetChanged() + } + + override fun onSessionStarted(session: CastSession, sessionId: String) { + mAdapter!!.notifyDataSetChanged() + } + + override fun onSessionStarting(session: CastSession) {} + override fun onSessionStartFailed(session: CastSession, error: Int) {} + override fun onSessionEnding(session: CastSession) {} + override fun onSessionResuming(session: CastSession, sessionId: String) {} + override fun onSessionResumeFailed(session: CastSession, error: Int) {} + override fun onSessionSuspended(session: CastSession, reason: Int) {} + } + + companion object { + private const val TAG = "VideoBrowserFragment" + private const val CATALOG_URL = + "https://commondatastorage.googleapis.com/gtv-videos-bucket/CastVideos/f.json" + } +} \ No newline at end of file diff --git a/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/browser/VideoItemLoader.kt b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/browser/VideoItemLoader.kt new file mode 100644 index 0000000..d6315c3 --- /dev/null +++ b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/browser/VideoItemLoader.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2022 Google LLC. All Rights Reserved. + * + * 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.google.sample.cast.refplayer.browser + +import android.content.Context +import android.util.Log +import androidx.loader.content.AsyncTaskLoader +import com.google.android.gms.cast.MediaInfo + +/** + * An [AsyncTaskLoader] that loads the list of videos in the background. + */ +class VideoItemLoader(context: Context?, private val mUrl: String) : + AsyncTaskLoader?>( + context!! + ) { + override fun loadInBackground(): List? { + return try { + VideoProvider.Companion.buildMedia(mUrl) + } catch (e: Exception) { + Log.e(TAG, "Failed to fetch media data", e) + null + } + } + + override fun onStartLoading() { + super.onStartLoading() + forceLoad() + } + + /** + * Handles a request to stop the Loader. + */ + override fun onStopLoading() { + // Attempt to cancel the current load task if possible. + cancelLoad() + } + + companion object { + private const val TAG = "VideoItemLoader" + } +} \ No newline at end of file diff --git a/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/browser/VideoListAdapter.kt b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/browser/VideoListAdapter.kt new file mode 100644 index 0000000..67ba9c9 --- /dev/null +++ b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/browser/VideoListAdapter.kt @@ -0,0 +1,157 @@ +/* + * Copyright 2022 Google LLC. All Rights Reserved. + * + * 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.google.sample.cast.refplayer.browser + +import android.content.Context +import androidx.recyclerview.widget.RecyclerView +import android.view.ViewGroup +import android.view.View +import android.view.LayoutInflater +import com.google.sample.cast.refplayer.R +import com.google.android.gms.cast.framework.CastContext +import com.android.volley.toolbox.NetworkImageView +import android.widget.TextView +import com.android.volley.toolbox.ImageLoader +import android.widget.ImageView +import com.google.android.gms.cast.* +import com.google.sample.cast.refplayer.utils.CustomVolleyRequest +import java.util.concurrent.Executor +import java.util.concurrent.Executors + +/** + * An [ArrayAdapter] to populate the list of videos. + */ +class VideoListAdapter(private val mClickListener: ItemClickListener, context: Context?) : + RecyclerView.Adapter() { + private val mAppContext: Context + private var videos: List? = null + private val castExecutor: Executor = Executors.newSingleThreadExecutor(); + + init { + mAppContext = context!!.applicationContext + } + + override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder { + val context = viewGroup.context + val parent = LayoutInflater.from(context).inflate(R.layout.browse_row, viewGroup, false) + return ViewHolder.newInstance(parent) + } + + override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) { + val item = videos!![position] + val mm = item.metadata + viewHolder.setTitle(mm!!.getString(MediaMetadata.KEY_TITLE)) + viewHolder.setDescription(mm.getString(MediaMetadata.KEY_SUBTITLE)) + viewHolder.setImage(mm.images[0].url.toString(), mAppContext) + viewHolder.mMenu.setOnClickListener { view -> + mClickListener.itemClicked( + view, + item, + position + ) + } + viewHolder.mImgView.setOnClickListener { view -> + mClickListener.itemClicked( + view, + item, + position + ) + } + viewHolder.mTextContainer.setOnClickListener { view -> + mClickListener.itemClicked( + view, + item, + position + ) + } + val castSession = CastContext.getSharedInstance(mAppContext,castExecutor) + .result + .sessionManager + .currentCastSession + viewHolder.mMenu.visibility = + if (castSession != null && castSession.isConnected) View.VISIBLE else View.GONE + } + + override fun getItemCount(): Int { + return if (videos == null) 0 else videos!!.size + } + + /** + * A [RecyclerView.ViewHolder] that displays a single video in + * the video list. + */ + class ViewHolder private constructor( + private val mParent: View, + val mImgView: NetworkImageView, + val mTextContainer: View, + private val mTitleView: TextView, + private val mDescriptionView: TextView, + val mMenu: View + ) : RecyclerView.ViewHolder(mParent) { + private var mImageLoader: ImageLoader? = null + fun setTitle(title: String?) { + mTitleView.text = title + } + + fun setDescription(description: String?) { + mDescriptionView.text = description + } + + fun setImage(imgUrl: String?, context: Context) { + mImageLoader = CustomVolleyRequest.Companion.getInstance(context!!)?.imageLoader + mImageLoader!![imgUrl, ImageLoader.getImageListener(mImgView, 0, 0)] + mImgView.setImageUrl(imgUrl, mImageLoader) + } + + fun setOnClickListener(listener: View.OnClickListener?) { + mParent.setOnClickListener(listener) + } + + val imageView: ImageView + get() = mImgView + + companion object { + fun newInstance(parent: View): ViewHolder { + val imgView = parent.findViewById(R.id.imageView1) as NetworkImageView + val titleView = parent.findViewById(R.id.textView1) as TextView + val descriptionView = parent.findViewById(R.id.textView2) as TextView + val menu = parent.findViewById(R.id.menu) + val textContainer = parent.findViewById(R.id.text_container) + return ViewHolder(parent, imgView, textContainer, titleView, descriptionView, menu) + } + } + } + + fun setData(data: List?) { + videos = data + notifyDataSetChanged() + } + + /** + * A listener called when an item is clicked in the video list. + */ + interface ItemClickListener { + fun itemClicked(view: View?, item: MediaInfo?, position: Int) + } + + override fun getItemId(position: Int): Long { + return super.getItemId(position) + } + + companion object { + private const val ASPECT_RATIO = 9f / 16f + } +} \ No newline at end of file diff --git a/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/browser/VideoProvider.kt b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/browser/VideoProvider.kt new file mode 100644 index 0000000..3fc35c8 --- /dev/null +++ b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/browser/VideoProvider.kt @@ -0,0 +1,243 @@ +/* + * Copyright 2022 Google LLC. All Rights Reserved. + * + * 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.google.sample.cast.refplayer.browser + +import org.json.JSONObject +import java.io.InputStream +import java.net.URL +import java.io.BufferedInputStream +import java.io.BufferedReader +import java.io.InputStreamReader +import java.lang.StringBuilder +import java.lang.Exception +import android.util.Log +import java.io.IOException +import kotlin.Throws +import org.json.JSONException +import java.util.HashMap +import java.util.ArrayList +import com.google.android.gms.common.images.WebImage +import android.net.Uri +import com.google.android.gms.cast.* + +/** + * Provider of the list of videos. + */ +class VideoProvider { + protected fun parseUrl(urlString: String?): JSONObject? { + var inputStream: InputStream? = null + return try { + val url = URL(urlString) + val urlConnection = url.openConnection() + inputStream = BufferedInputStream(urlConnection.getInputStream()) + val reader = BufferedReader( + InputStreamReader( + urlConnection.getInputStream(), "iso-8859-1" + ), 1024 + ) + val sb = StringBuilder() + var line: String? + while (reader.readLine().also { line = it } != null) { + sb.append(line) + } + val json = sb.toString() + JSONObject(json) + } catch (e: Exception) { + Log.e(TAG, "Failed to parse the json for media list", e) + null + } finally { + if (null != inputStream) { + try { + inputStream.close() + } catch (e: IOException) { + // ignore + Log.w(TAG,e.message,e) + } + } + } + } + + companion object { + private const val TAG = "VideoProvider" + private const val TAG_VIDEOS = "videos" + private const val TAG_HLS = "hls" + private const val TAG_DASH = "dash" + private const val TAG_MP4 = "mp4" + private const val TAG_IMAGES = "images" + private const val TAG_VIDEO_TYPE = "type" + private const val TAG_VIDEO_URL = "url" + private const val TAG_VIDEO_MIME = "mime" + private const val TAG_CATEGORIES = "categories" + private const val TAG_NAME = "name" + private const val TAG_STUDIO = "studio" + private const val TAG_SOURCES = "sources" + private const val TAG_SUBTITLE = "subtitle" + private const val TAG_DURATION = "duration" + private const val TAG_TRACKS = "tracks" + private const val TAG_TRACK_ID = "id" + private const val TAG_TRACK_TYPE = "type" + private const val TAG_TRACK_SUBTYPE = "subtype" + private const val TAG_TRACK_CONTENT_ID = "contentId" + private const val TAG_TRACK_NAME = "name" + private const val TAG_TRACK_LANGUAGE = "language" + private const val TAG_THUMB = "image-480x270" // "thumb"; + private const val TAG_IMG_780_1200 = "image-780x1200" + private const val TAG_TITLE = "title" + const val KEY_DESCRIPTION = "description" + private const val TARGET_FORMAT = TAG_HLS + private var mediaList: MutableList? = null + @Throws(JSONException::class) + fun buildMedia(url: String?): List? { + if (null != mediaList) { + return mediaList + } + val urlPrefixMap: MutableMap = HashMap() + mediaList = ArrayList() + val jsonObj = VideoProvider().parseUrl(url) + val categories = jsonObj!!.getJSONArray(TAG_CATEGORIES) + if (null != categories) { + for (i in 0 until categories.length()) { + val category = categories.getJSONObject(i) + urlPrefixMap[TAG_HLS] = + category.getString(TAG_HLS) + urlPrefixMap[TAG_DASH] = + category.getString(TAG_DASH) + urlPrefixMap[TAG_MP4] = + category.getString(TAG_MP4) + urlPrefixMap[TAG_IMAGES] = + category.getString(TAG_IMAGES) + urlPrefixMap[TAG_TRACKS] = + category.getString(TAG_TRACKS) + category.getString(TAG_NAME) + val videos = category.getJSONArray(TAG_VIDEOS) + if (null != videos) { + for (j in 0 until videos.length()) { + var videoUrl: String? = null + var mimeType: String? = null + val video = videos.getJSONObject(j) + val subTitle = video.getString(TAG_SUBTITLE) + val videoSpecs = video.getJSONArray(TAG_SOURCES) + if (null == videoSpecs || videoSpecs.length() == 0) { + continue + } + for (k in 0 until videoSpecs.length()) { + val videoSpec = videoSpecs.getJSONObject(k) + if (TARGET_FORMAT == videoSpec.getString(TAG_VIDEO_TYPE)) { + videoUrl = urlPrefixMap[TARGET_FORMAT] + videoSpec + .getString(TAG_VIDEO_URL) + mimeType = videoSpec.getString(TAG_VIDEO_MIME) + } + } + if (videoUrl == null) { + continue + } + val imageUrl = urlPrefixMap[TAG_IMAGES] + video.getString(TAG_THUMB) + val bigImageUrl = urlPrefixMap[TAG_IMAGES] + video + .getString(TAG_IMG_780_1200) + val title = video.getString(TAG_TITLE) + val studio = video.getString(TAG_STUDIO) + val duration = video.getInt(TAG_DURATION) + var tracks: MutableList? = null + if (video.has(TAG_TRACKS)) { + val tracksArray = video.getJSONArray(TAG_TRACKS) + if (tracksArray != null) { + tracks = ArrayList() + for (k in 0 until tracksArray.length()) { + val track = tracksArray.getJSONObject(k) + tracks.add( + buildTrack( + track.getLong(TAG_TRACK_ID), + track.getString(TAG_TRACK_TYPE), + track.getString(TAG_TRACK_SUBTYPE), + urlPrefixMap[TAG_TRACKS] + track + .getString(TAG_TRACK_CONTENT_ID), + track.getString(TAG_TRACK_NAME), + track.getString(TAG_TRACK_LANGUAGE) + ) + ) + } + } + } + mediaList!!.add( + buildMediaInfo( + title, studio, subTitle, duration, videoUrl, + mimeType, imageUrl, bigImageUrl, tracks + ) + ) + } + } + } + } + return mediaList + } + + private fun buildMediaInfo( + title: String, studio: String, subTitle: String, + duration: Int, url: String, mimeType: String?, imgUrl: String, bigImageUrl: String, + tracks: List?): MediaInfo { + val movieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE) + movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, studio) + movieMetadata.putString(MediaMetadata.KEY_TITLE, title) + movieMetadata.addImage(WebImage(Uri.parse(imgUrl))) + movieMetadata.addImage(WebImage(Uri.parse(bigImageUrl))) + var jsonObj: JSONObject? = null + try { + jsonObj = JSONObject() + jsonObj.put(KEY_DESCRIPTION, subTitle) + } catch (e: JSONException) { + Log.e(TAG, "Failed to add description to the json object", e) + } + var mediaInfo: MediaInfo.Builder = MediaInfo.Builder(url) + .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) + .setContentType(mimeType!!) + .setMetadata(movieMetadata) + .setStreamDuration((duration * 1000).toLong()) + .setCustomData(jsonObj!!) + + if (tracks != null) { + mediaInfo.setMediaTracks(tracks) + } + + return mediaInfo.build(); + } + + private fun buildTrack( + id: Long, type: String, subType: String?, contentId: String, + name: String, language: String): MediaTrack { + var trackType = MediaTrack.TYPE_UNKNOWN + if ("text" == type) { + trackType = MediaTrack.TYPE_TEXT + } else if ("video" == type) { + trackType = MediaTrack.TYPE_VIDEO + } else if ("audio" == type) { + trackType = MediaTrack.TYPE_AUDIO + } + var trackSubType = MediaTrack.SUBTYPE_NONE + if (subType != null) { + if ("captions" == type) { + trackSubType = MediaTrack.SUBTYPE_CAPTIONS + } else if ("subtitle" == type) { + trackSubType = MediaTrack.SUBTYPE_SUBTITLES + } + } + return MediaTrack.Builder(id, trackType) + .setName(name) + .setSubtype(trackSubType) + .setContentId(contentId) + .setLanguage(language).build() + } + } +} \ No newline at end of file diff --git a/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/expandedcontrols/ExpandedControlsActivity.kt b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/expandedcontrols/ExpandedControlsActivity.kt new file mode 100644 index 0000000..3b3ea3f --- /dev/null +++ b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/expandedcontrols/ExpandedControlsActivity.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2022 Google LLC. All Rights Reserved. + * + * 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.google.sample.cast.refplayer.expandedcontrols + +import com.google.sample.cast.refplayer.R +import com.google.android.gms.cast.framework.media.widget.ExpandedControllerActivity +import android.view.Menu +import com.google.android.gms.cast.framework.CastButtonFactory + +/** + * An example of extending [ExpandedControllerActivity] to add a cast button. + */ +class ExpandedControlsActivity : ExpandedControllerActivity() { + override fun onCreateOptionsMenu(menu: Menu): Boolean { + super.onCreateOptionsMenu(menu) + menuInflater.inflate(R.menu.expanded_controller, menu) + CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item) + return true + } +} \ No newline at end of file diff --git a/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/mediaplayer/LocalPlayerActivity.kt b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/mediaplayer/LocalPlayerActivity.kt new file mode 100644 index 0000000..297ce17 --- /dev/null +++ b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/mediaplayer/LocalPlayerActivity.kt @@ -0,0 +1,690 @@ +/* + * Copyright 2022 Google LLC. All Rights Reserved. + * + * 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.google.sample.cast.refplayer.mediaplayer + +import android.util.Log +import android.view.View +import com.google.sample.cast.refplayer.R +import com.google.android.gms.cast.framework.CastSession +import com.google.android.gms.cast.framework.CastContext +import com.android.volley.toolbox.NetworkImageView +import android.widget.TextView +import com.android.volley.toolbox.ImageLoader +import android.widget.ImageView +import com.google.android.gms.cast.framework.SessionManagerListener +import android.os.Bundle +import android.widget.ImageButton +import android.content.Intent +import androidx.core.app.ActivityCompat +import android.view.Menu +import com.google.android.gms.cast.framework.CastButtonFactory +import androidx.appcompat.app.AppCompatActivity +import android.widget.VideoView +import android.widget.SeekBar +import android.widget.ProgressBar +import java.util.Timer +import android.view.MenuItem +import com.google.android.gms.cast.framework.media.MediaUtils +import com.google.android.gms.cast.framework.media.RemoteMediaClient +import android.view.KeyEvent +import java.util.TimerTask +import android.media.MediaPlayer +import android.widget.SeekBar.OnSeekBarChangeListener +import android.annotation.SuppressLint +import android.view.WindowManager +import android.os.Build +import android.widget.RelativeLayout +import android.text.method.ScrollingMovementMethod +import androidx.core.view.ViewCompat +import android.content.res.Configuration +import android.graphics.Point +import android.net.Uri +import android.os.Handler +import android.os.Looper +import androidx.appcompat.widget.Toolbar +import com.google.android.gms.cast.* +import com.google.sample.cast.refplayer.browser.VideoProvider +import com.google.sample.cast.refplayer.expandedcontrols.ExpandedControlsActivity +import com.google.sample.cast.refplayer.queue.ui.QueueListViewActivity +import com.google.sample.cast.refplayer.settings.CastPreference +import com.google.sample.cast.refplayer.utils.CustomVolleyRequest +import com.google.sample.cast.refplayer.utils.Utils +import java.util.concurrent.Executor +import java.util.concurrent.Executors + +/** + * Activity for the local media player. + */ +class LocalPlayerActivity : AppCompatActivity() { + private var mVideoView: VideoView? = null + private var mTitleView: TextView? = null + private var mDescriptionView: TextView? = null + private var mStartText: TextView? = null + private var mEndText: TextView? = null + private var mSeekbar: SeekBar? = null + private var mPlayPause: ImageView? = null + private var mLoading: ProgressBar? = null + private var mControllers: View? = null + private var mContainer: View? = null + private var mCoverArt: NetworkImageView? = null + private var mSeekbarTimer: Timer? = null + private var mControllersTimer: Timer? = null + private var mLocation: PlaybackLocation? = null + private var mPlaybackState: PlaybackState? = null + private val mHandler = Handler(Looper.getMainLooper()) + private val mAspectRatio = 72f / 128 + private var mSelectedMedia: MediaInfo? = null + private var mControllersVisible = false + private var mDuration = 0 + private var mAuthorView: TextView? = null + private var mPlayCircle: ImageButton? = null + private var mCastContext: CastContext? = null + private var mCastSession: CastSession? = null + private var mSessionManagerListener: SessionManagerListener? = null + private var mQueueMenuItem: MenuItem? = null + private var mImageLoader: ImageLoader? = null + private val localExecutor: Executor = Executors.newSingleThreadExecutor(); + + + /** + * indicates whether we are doing a local or a remote playback + */ + enum class PlaybackLocation { + LOCAL, REMOTE + } + + /** + * List of various states that we can be in + */ + enum class PlaybackState { + PLAYING, PAUSED, BUFFERING, IDLE + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.player_activity) + loadViews() + setupControlsCallbacks() + setupCastListener() + mCastContext = CastContext.getSharedInstance(this,localExecutor).result + mCastSession = mCastContext!!.sessionManager.currentCastSession + // see what we need to play and where + val bundle = intent.extras + if (bundle != null) { + mSelectedMedia = intent.getParcelableExtra("media") + setupActionBar() + val shouldStartPlayback = bundle.getBoolean("shouldStart") + val startPosition = bundle.getInt("startPosition", 0) + mVideoView!!.setVideoURI(Uri.parse(mSelectedMedia!!.contentId)) + Log.d(TAG, "Setting url of the VideoView to: " + mSelectedMedia!!.contentId) + if (shouldStartPlayback) { + // this will be the case only if we are coming from the + // CastControllerActivity by disconnecting from a device + mPlaybackState = PlaybackState.PLAYING + updatePlaybackLocation(PlaybackLocation.LOCAL) + updatePlayButton(mPlaybackState) + if (startPosition > 0) { + mVideoView!!.seekTo(startPosition) + } + mVideoView!!.start() + startControllersTimer() + } else { + // we should load the video but pause it + // and show the album art. + if (mCastSession != null && mCastSession!!.isConnected) { + updatePlaybackLocation(PlaybackLocation.REMOTE) + } else { + updatePlaybackLocation(PlaybackLocation.LOCAL) + } + mPlaybackState = PlaybackState.IDLE + updatePlayButton(mPlaybackState) + } + } + if (mTitleView != null) { + updateMetadata(true) + } + } + + private fun setupCastListener() { + mSessionManagerListener = object : SessionManagerListener { + override fun onSessionEnded(session: CastSession, error: Int) { + onApplicationDisconnected() + } + + override fun onSessionResumed(session: CastSession, wasSuspended: Boolean) { + onApplicationConnected(session) + } + + override fun onSessionResumeFailed(session: CastSession, error: Int) { + onApplicationDisconnected() + } + + override fun onSessionStarted(session: CastSession, sessionId: String) { + onApplicationConnected(session) + } + + override fun onSessionStartFailed(session: CastSession, error: Int) { + onApplicationDisconnected() + } + + override fun onSessionStarting(session: CastSession) {} + override fun onSessionEnding(session: CastSession) {} + override fun onSessionResuming(session: CastSession, sessionId: String) {} + override fun onSessionSuspended(session: CastSession, reason: Int) {} + private fun onApplicationConnected(castSession: CastSession) { + mCastSession = castSession + if (null != mSelectedMedia) { + if (mPlaybackState == PlaybackState.PLAYING) { + mVideoView!!.pause() + loadRemoteMedia(mSeekbar!!.progress, true) + return + } else { + mPlaybackState = PlaybackState.IDLE + updatePlaybackLocation(PlaybackLocation.REMOTE) + } + } + updatePlayButton(mPlaybackState) + invalidateOptionsMenu() + } + + private fun onApplicationDisconnected() { + updatePlaybackLocation(PlaybackLocation.LOCAL) + mPlaybackState = PlaybackState.IDLE + mLocation = PlaybackLocation.LOCAL + updatePlayButton(mPlaybackState) + invalidateOptionsMenu() + } + } + } + + private fun updatePlaybackLocation(location: PlaybackLocation) { + mLocation = location + if (location == PlaybackLocation.LOCAL) { + if (mPlaybackState == PlaybackState.PLAYING + || mPlaybackState == PlaybackState.BUFFERING + ) { + setCoverArtStatus(null) + startControllersTimer() + } else { + stopControllersTimer() + setCoverArtStatus(MediaUtils.getImageUrl(mSelectedMedia, 0)) + } + } else { + stopControllersTimer() + setCoverArtStatus(MediaUtils.getImageUrl(mSelectedMedia, 0)) + updateControllersVisibility(false) + } + } + + private fun play(position: Int) { + startControllersTimer() + when (mLocation) { + PlaybackLocation.LOCAL -> { + mVideoView!!.seekTo(position) + mVideoView!!.start() + } + PlaybackLocation.REMOTE -> { + mPlaybackState = PlaybackState.BUFFERING + updatePlayButton(mPlaybackState) + mCastSession!!.remoteMediaClient!!.seek(MediaSeekOptions + .Builder() + .setPosition(position.toLong()) + .build()) + } + else -> {} + } + restartTrickplayTimer() + } + + private fun togglePlayback() { + stopControllersTimer() + when (mPlaybackState) { + PlaybackState.PAUSED -> when (mLocation) { + PlaybackLocation.LOCAL -> { + mVideoView!!.start() + Log.d(TAG, "Playing locally...") + mPlaybackState = PlaybackState.PLAYING + startControllersTimer() + restartTrickplayTimer() + updatePlaybackLocation(PlaybackLocation.LOCAL) + } + PlaybackLocation.REMOTE -> { + loadRemoteMedia(0, true) + finish() + } + else -> {} + } + PlaybackState.PLAYING -> { + mPlaybackState = PlaybackState.PAUSED + mVideoView!!.pause() + } + PlaybackState.IDLE -> when (mLocation) { + PlaybackLocation.LOCAL -> { + mVideoView!!.setVideoURI(Uri.parse(mSelectedMedia!!.contentId)) + mVideoView!!.seekTo(0) + mVideoView!!.start() + mPlaybackState = PlaybackState.PLAYING + restartTrickplayTimer() + updatePlaybackLocation(PlaybackLocation.LOCAL) + } + PlaybackLocation.REMOTE -> if (mCastSession != null && mCastSession!!.isConnected) { + Utils.showQueuePopup(this, mPlayCircle, mSelectedMedia) + } + else -> {} + } + else -> {} + } + updatePlayButton(mPlaybackState) + } + + private fun loadRemoteMedia(position: Int, autoPlay: Boolean) { + if (mCastSession == null) { + return + } + val remoteMediaClient = mCastSession!!.remoteMediaClient ?: return + remoteMediaClient.registerCallback(object : RemoteMediaClient.Callback() { + override fun onStatusUpdated() { + val intent = Intent(this@LocalPlayerActivity, ExpandedControlsActivity::class.java) + startActivity(intent) + remoteMediaClient.unregisterCallback(this) + } + }) + remoteMediaClient.load( + MediaLoadRequestData.Builder() + .setMediaInfo(mSelectedMedia) + .setAutoplay(autoPlay) + .setCurrentTime(position.toLong()).build() + ) + } + + private fun setCoverArtStatus(url: String?) { + if (url != null) { + mImageLoader = CustomVolleyRequest.Companion.getInstance(this.applicationContext)?.imageLoader + mImageLoader!![url, ImageLoader.getImageListener(mCoverArt, 0, 0)] + mCoverArt!!.setImageUrl(url, mImageLoader) + mCoverArt!!.visibility = View.VISIBLE + mVideoView!!.visibility = View.INVISIBLE + } else { + mCoverArt!!.visibility = View.GONE + mVideoView!!.visibility = View.VISIBLE + } + } + + private fun stopTrickplayTimer() { + Log.d(TAG, "Stopped TrickPlay Timer") + if (mSeekbarTimer != null) { + mSeekbarTimer!!.cancel() + } + } + + private fun restartTrickplayTimer() { + stopTrickplayTimer() + mSeekbarTimer = Timer() + mSeekbarTimer!!.scheduleAtFixedRate(UpdateSeekbarTask(), 100, 1000) + Log.d(TAG, "Restarted TrickPlay Timer") + } + + private fun stopControllersTimer() { + if (mControllersTimer != null) { + mControllersTimer!!.cancel() + } + } + + private fun startControllersTimer() { + if (mControllersTimer != null) { + mControllersTimer!!.cancel() + } + if (mLocation == PlaybackLocation.REMOTE) { + return + } + mControllersTimer = Timer() + mControllersTimer!!.schedule(HideControllersTask(), 5000) + } + + // should be called from the main thread + private fun updateControllersVisibility(show: Boolean) { + if (show) { + supportActionBar!!.show() + mControllers!!.visibility = View.VISIBLE + } else { + if (!Utils.isOrientationPortrait(this)) { + supportActionBar!!.hide() + } + mControllers!!.visibility = View.INVISIBLE + } + } + + override fun onPause() { + super.onPause() + Log.d(TAG, "onPause() was called") + if (mLocation == PlaybackLocation.LOCAL) { + if (mSeekbarTimer != null) { + mSeekbarTimer!!.cancel() + mSeekbarTimer = null + } + if (mControllersTimer != null) { + mControllersTimer!!.cancel() + } + // since we are playing locally, we need to stop the playback of + // video (if user is not watching, pause it!) + mVideoView!!.pause() + mPlaybackState = PlaybackState.PAUSED + updatePlayButton(PlaybackState.PAUSED) + } + mCastContext!!.sessionManager.removeSessionManagerListener( + mSessionManagerListener!!, CastSession::class.java + ) + } + + override fun onStop() { + Log.d(TAG, "onStop() was called") + super.onStop() + } + + override fun onDestroy() { + Log.d(TAG, "onDestroy() is called") + stopControllersTimer() + stopTrickplayTimer() + super.onDestroy() + } + + override fun onStart() { + Log.d(TAG, "onStart was called") + super.onStart() + } + + override fun onResume() { + Log.d(TAG, "onResume() was called") + mCastContext!!.sessionManager.addSessionManagerListener( + mSessionManagerListener!!, CastSession::class.java + ) + if (mCastSession != null && mCastSession!!.isConnected) { + updatePlaybackLocation(PlaybackLocation.REMOTE) + } else { + updatePlaybackLocation(PlaybackLocation.LOCAL) + } + if (mQueueMenuItem != null) { + mQueueMenuItem!!.isVisible = mCastSession != null && mCastSession!!.isConnected + } + super.onResume() + } + + override fun dispatchKeyEvent(event: KeyEvent): Boolean { + return (mCastContext!!.onDispatchVolumeKeyEventBeforeJellyBean(event) + || super.dispatchKeyEvent(event)) + } + + private inner class HideControllersTask : TimerTask() { + override fun run() { + mHandler.post { + updateControllersVisibility(false) + mControllersVisible = false + } + } + } + + private inner class UpdateSeekbarTask : TimerTask() { + override fun run() { + mHandler.post { + if (mLocation == PlaybackLocation.LOCAL) { + val currentPos = mVideoView!!.currentPosition + updateSeekbar(currentPos, mDuration) + } + } + } + } + + private fun setupControlsCallbacks() { + mVideoView!!.setOnErrorListener { mp, what, extra -> + Log.e( + TAG, "OnErrorListener.onError(): VideoView encountered an " + + "error, what: " + what + ", extra: " + extra + ) + val msg: String + msg = if (extra == MediaPlayer.MEDIA_ERROR_TIMED_OUT) { + getString(R.string.video_error_media_load_timeout) + } else if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) { + getString(R.string.video_error_server_unaccessible) + } else { + getString(R.string.video_error_unknown_error) + } + Utils.showErrorDialog(this@LocalPlayerActivity, msg) + mVideoView!!.stopPlayback() + mPlaybackState = PlaybackState.IDLE + updatePlayButton(mPlaybackState) + true + } + mVideoView!!.setOnPreparedListener { mp -> + Log.d(TAG, "onPrepared is reached") + mDuration = mp.duration + mEndText!!.text = Utils.formatMillis(mDuration) + mSeekbar!!.max = mDuration + restartTrickplayTimer() + } + mVideoView!!.setOnCompletionListener { + stopTrickplayTimer() + Log.d(TAG, "setOnCompletionListener()") + mPlaybackState = PlaybackState.IDLE + updatePlayButton(mPlaybackState) + } + mVideoView!!.setOnTouchListener { v, event -> + if (!mControllersVisible) { + updateControllersVisibility(true) + } + startControllersTimer() + false + } + mSeekbar!!.setOnSeekBarChangeListener(object : OnSeekBarChangeListener { + override fun onStopTrackingTouch(seekBar: SeekBar) { + if (mPlaybackState == PlaybackState.PLAYING) { + play(seekBar.progress) + } else if (mPlaybackState != PlaybackState.IDLE) { + mVideoView!!.seekTo(seekBar.progress) + } + startControllersTimer() + } + + override fun onStartTrackingTouch(seekBar: SeekBar) { + stopTrickplayTimer() + mVideoView!!.pause() + stopControllersTimer() + } + + override fun onProgressChanged( + seekBar: SeekBar, progress: Int, + fromUser: Boolean + ) { + mStartText!!.text = Utils.formatMillis(progress) + } + }) + mPlayPause!!.setOnClickListener { + if (mLocation == PlaybackLocation.LOCAL) { + togglePlayback() + } + } + } + + private fun updateSeekbar(position: Int, duration: Int) { + mSeekbar!!.progress = position + mSeekbar!!.max = duration + mStartText!!.text = Utils.formatMillis(position) + mEndText!!.text = Utils.formatMillis(duration) + } + + private fun updatePlayButton(state: PlaybackState?) { + Log.d(TAG, "Controls: PlayBackState: $state") + val isConnected = (mCastSession != null + && (mCastSession!!.isConnected || mCastSession!!.isConnecting)) + mControllers!!.visibility = if (isConnected) View.GONE else View.VISIBLE + mPlayCircle!!.visibility = if (isConnected) View.GONE else View.VISIBLE + when (state) { + PlaybackState.PLAYING -> { + mLoading!!.visibility = View.INVISIBLE + mPlayPause!!.visibility = View.VISIBLE + mPlayPause!!.setImageDrawable( + resources.getDrawable(R.drawable.ic_av_pause_dark) + ) + mPlayCircle!!.visibility = if (isConnected) View.VISIBLE else View.GONE + } + PlaybackState.IDLE -> { + mPlayCircle!!.visibility = View.VISIBLE + mControllers!!.visibility = View.GONE + mCoverArt!!.visibility = View.VISIBLE + mVideoView!!.visibility = View.INVISIBLE + } + PlaybackState.PAUSED -> { + mLoading!!.visibility = View.INVISIBLE + mPlayPause!!.visibility = View.VISIBLE + mPlayPause!!.setImageDrawable( + resources.getDrawable(R.drawable.ic_av_play_dark) + ) + mPlayCircle!!.visibility = if (isConnected) View.VISIBLE else View.GONE + } + PlaybackState.BUFFERING -> { + mPlayPause!!.visibility = View.INVISIBLE + mLoading!!.visibility = View.VISIBLE + } + else -> {} + } + } + + @SuppressLint("NewApi") + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + supportActionBar!!.show() + if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { + window.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN) + window.setFlags( + WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN + ) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LOW_PROFILE + } + updateMetadata(false) + mContainer!!.setBackgroundColor(resources.getColor(R.color.black)) + } else { + window.setFlags( + WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN + ) + window.clearFlags( + WindowManager.LayoutParams.FLAG_FULLSCREEN + ) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE + } + updateMetadata(true) + mContainer!!.setBackgroundColor(resources.getColor(R.color.white)) + } + } + + private fun updateMetadata(visible: Boolean) { + val displaySize: Point? + if (!visible) { + mDescriptionView!!.visibility = View.GONE + mTitleView!!.visibility = View.GONE + mAuthorView!!.visibility = View.GONE + displaySize = Utils.getDisplaySize(this) + val lp = RelativeLayout.LayoutParams( + displaySize.x, + displaySize.y + supportActionBar!!.height + ) + lp.addRule(RelativeLayout.CENTER_IN_PARENT) + mVideoView!!.layoutParams = lp + mVideoView!!.invalidate() + } else { + val mm = mSelectedMedia!!.metadata + mDescriptionView!!.text = mSelectedMedia!!.customData!!.optString( + VideoProvider.Companion.KEY_DESCRIPTION + ) + mTitleView!!.text = mm!!.getString(MediaMetadata.KEY_TITLE) + mAuthorView!!.text = mm.getString(MediaMetadata.KEY_SUBTITLE) + mDescriptionView!!.visibility = View.VISIBLE + mTitleView!!.visibility = View.VISIBLE + mAuthorView!!.visibility = View.VISIBLE + displaySize = Utils.getDisplaySize(this) + val lp = + RelativeLayout.LayoutParams(displaySize.x, (displaySize.x * mAspectRatio).toInt()) + lp.addRule(RelativeLayout.BELOW, R.id.toolbar) + mVideoView!!.layoutParams = lp + mVideoView!!.invalidate() + } + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + super.onCreateOptionsMenu(menu) + menuInflater.inflate(R.menu.player, menu) + CastButtonFactory.setUpMediaRouteButton( + applicationContext, menu, + R.id.media_route_menu_item + ) + mQueueMenuItem = menu.findItem(R.id.action_show_queue) + return true + } + + override fun onPrepareOptionsMenu(menu: Menu): Boolean { + menu.findItem(R.id.action_show_queue).isVisible = + mCastSession != null && mCastSession!!.isConnected + return super.onPrepareOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + val intent: Intent + if (item.itemId == R.id.action_settings) { + intent = Intent(this@LocalPlayerActivity, CastPreference::class.java) + startActivity(intent) + } else if (item.itemId == R.id.action_show_queue) { + intent = Intent(this@LocalPlayerActivity, QueueListViewActivity::class.java) + startActivity(intent) + } else if (item.itemId == android.R.id.home) { + ActivityCompat.finishAfterTransition(this) + } + return true + } + + private fun setupActionBar() { + val toolbar = findViewById(R.id.toolbar) as Toolbar + toolbar.title = + mSelectedMedia!!.metadata!!.getString(MediaMetadata.KEY_TITLE) + setSupportActionBar(toolbar) + supportActionBar!!.setDisplayHomeAsUpEnabled(true) + } + + private fun loadViews() { + mVideoView = findViewById(R.id.videoView1) as VideoView + mTitleView = findViewById(R.id.titleTextView) as TextView + mDescriptionView = findViewById(R.id.descriptionTextView) as TextView + mDescriptionView!!.movementMethod = ScrollingMovementMethod() + mAuthorView = findViewById(R.id.authorTextView) as TextView + mStartText = findViewById(R.id.startText) as TextView + mStartText!!.text = Utils.formatMillis(0) + mEndText = findViewById(R.id.endText) as TextView + mSeekbar = findViewById(R.id.seekBar1) as SeekBar + mPlayPause = findViewById(R.id.playPauseImageView) as ImageView + mLoading = findViewById(R.id.progressBar1) as ProgressBar + mControllers = findViewById(R.id.controllers) + mContainer = findViewById(R.id.container) + mCoverArt = findViewById(R.id.coverArtView) as NetworkImageView + ViewCompat.setTransitionName(mCoverArt!!, getString(R.string.transition_image)) + mPlayCircle = findViewById(R.id.play_circle) as ImageButton + mPlayCircle!!.setOnClickListener { togglePlayback() } + } + + companion object { + private const val TAG = "LocalPlayerActivity" + } +} \ No newline at end of file diff --git a/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/queue/QueueDataProvider.kt b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/queue/QueueDataProvider.kt new file mode 100644 index 0000000..7ce9185 --- /dev/null +++ b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/queue/QueueDataProvider.kt @@ -0,0 +1,294 @@ +/* + * Copyright 2022 Google LLC. All Rights Reserved. + * + * 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.google.sample.cast.refplayer.queue + +import android.util.Log +import android.content.Context +import android.view.View +import com.google.android.gms.cast.framework.CastSession +import com.google.android.gms.cast.framework.CastContext +import com.google.android.gms.cast.framework.SessionManagerListener +import com.google.android.gms.cast.framework.media.RemoteMediaClient +import com.google.android.gms.cast.framework.media.MediaQueue +import com.google.android.gms.cast.MediaQueueItem +import org.json.JSONObject +import java.util.concurrent.Executor +import java.util.concurrent.Executors +import kotlin.jvm.Synchronized + +/** + * A singleton to manage the queue. Upon instantiation, it syncs up its own copy of the queue with + * the one that the VideoCastManager holds. After that point, it maintains an up-to-date version of + * the queue. UI elements get their data from this class. A boolean field, `mDetachedQueue` + * is used to manage whether this changes to the queue coming from the cast framework should be + * reflected here or not; when in "detached" mode, it means that its own copy of the queue is not + * kept up to date with the one that the cast framework has. This is needed to preserve the queue + * when the media session ends. + */ +class QueueDataProvider private constructor(context: Context?) { + private val mAppContext: Context + private val castExecutor: Executor = Executors.newSingleThreadExecutor(); + // Locks modification to the remove queue. + private val mLock: Any = Any() + private val mSessionManagerListener: SessionManagerListener = + MySessionManagerListener() + private val mRemoteMediaClientCallback: RemoteMediaClient.Callback = + MyRemoteMediaClientCallback() + private var mCurrentItem: MediaQueueItem? + var upcomingItem: MediaQueueItem? = null + private set + private var mListener: OnQueueDataChangedListener? = null + var isQueueDetached: Boolean = true + private set + + init { + mAppContext = context!!.getApplicationContext() + mCurrentItem = null + CastContext.getSharedInstance( + mAppContext, + castExecutor + ).result?.sessionManager?.addSessionManagerListener( + mSessionManagerListener, CastSession::class.java + ) + mediaQueue?.setCacheCapacity(30) + registerQueueCallbackAndUpdateQueue() + } + + fun onUpcomingStopClicked(view: View?, upcomingItem: MediaQueueItem) { + val remoteMediaClient: RemoteMediaClient = remoteMediaClient ?: return + // need to truncate the queue on the remote device so that we can complete the playback of + // the current item but not go any further. Alternatively, one could just stop the playback + // here, if that was acceptable. + val position: Int = getPositionByItemId(upcomingItem.itemId) + val itemIds: IntArray = IntArray(count - position) + for ( i in itemIds.indices) { + itemIds[i] = mediaQueue!!.getItemAtIndex(i + position)!!.itemId + } + remoteMediaClient.queueRemoveItems(itemIds, JSONObject() ) + } + + fun onUpcomingPlayClicked(view: View?, upcomingItem: MediaQueueItem) { + val remoteMediaClient: RemoteMediaClient = remoteMediaClient ?: return + remoteMediaClient.queueJumpToItem(upcomingItem.itemId, JSONObject() ) + } + + val mediaQueue: MediaQueue? + get() { + val queue: MediaQueue? = + if (remoteMediaClient == null) null else remoteMediaClient!!.getMediaQueue() + return queue + } + + fun getPositionByItemId(itemId: Int): Int { + if (mediaQueue == null || mediaQueue?.itemIds == null) { + return INVALID + } + val ids: IntArray = mediaQueue!!.itemIds + for (i in ids.indices) { + if (ids.get(i) == itemId) { + return i + } + } + return INVALID + } + + fun removeFromQueue(position: Int) { + synchronized(mLock) { + val queue: MediaQueue = mediaQueue ?: return + val item: MediaQueueItem? = queue.getItemAtIndex(position) + val remoteMediaClient: RemoteMediaClient? = remoteMediaClient + if (item != null && remoteMediaClient != null) { + remoteMediaClient.queueRemoveItem(item.itemId, JSONObject()) + } + } + } + + fun removeAll() { + synchronized(mLock) { + val remoteMediaClient: RemoteMediaClient = remoteMediaClient ?: return + val queue: MediaQueue = mediaQueue ?: return + val ids: IntArray = queue.itemIds + if (ids.isNotEmpty()) { + remoteMediaClient.queueRemoveItems(ids, JSONObject()) + } + } + } + + fun moveItem(fromPosition: Int, toPosition: Int) { + if (fromPosition == toPosition) { + return + } + val remoteMediaClient: RemoteMediaClient = remoteMediaClient ?: return + val queue: MediaQueue = mediaQueue ?: return + val itemId: Int = queue.itemIdAtIndex(fromPosition) + remoteMediaClient.queueMoveItemToNewIndex(itemId, toPosition, JSONObject() ) + } + + val count: Int + get() { + val queue: MediaQueue = mediaQueue ?: return 0 + return queue.itemCount + } + + fun getItem(position: Int): MediaQueueItem? { + val queue: MediaQueue = mediaQueue ?: return null + return queue.getItemAtIndex(position, true) + } + + fun destroyQueue() { + removeAll() + isQueueDetached = true + mCurrentItem = null + upcomingItem = null + } + + fun isCurrentItem(item: MediaQueueItem?): Boolean { + if ((item != null + ) && (mCurrentItem != null + ) && ((item === mCurrentItem + || item.itemId == mCurrentItem!!.itemId)) + ) { + return true + } + return false + } + + val currentItemId: Int + get() { + return if (mCurrentItem == null) -1 else mCurrentItem!!.itemId + } + + fun isUpcomingItem(item: MediaQueueItem?): Boolean { + if ((item != null + ) && (upcomingItem != null + ) && ((item === upcomingItem + || item.itemId == upcomingItem!!.itemId)) + ) { + return true + } + return false + } + + fun setOnQueueDataChangedListener(listener: OnQueueDataChangedListener?) { + mListener = listener + } + + /** + * Listener notifies the data of the queue has changed. + */ + open interface OnQueueDataChangedListener { + fun onQueueDataChanged() + } + + private fun registerQueueCallbackAndUpdateQueue() { + val remoteMediaClient: RemoteMediaClient? = remoteMediaClient + if (remoteMediaClient != null) { + remoteMediaClient.registerCallback(mRemoteMediaClientCallback) + updateMediaQueue() + } + } + + private fun updateMediaQueue() { + Log.d(TAG, "updateMediaQueue ") + val remoteMediaClient: RemoteMediaClient? = remoteMediaClient + isQueueDetached = true + if (remoteMediaClient != null) { + mCurrentItem = remoteMediaClient.currentItem + upcomingItem = remoteMediaClient.preloadedItem + isQueueDetached = false + Log.d(TAG, "updateMediaQueue() with mCurrentItem=" + mCurrentItem) + Log.d(TAG, "updateMediaQueue() with mUpcomingItem=" + upcomingItem) + } + } + + private val remoteMediaClient: RemoteMediaClient? + private get() { + val castSession: CastSession? = + CastContext.getSharedInstance(mAppContext,castExecutor) + .result + .sessionManager + .currentCastSession + if (castSession == null || !castSession.isConnected) { + Log.w(TAG, "Trying to get a RemoteMediaClient when no CastSession is started.") + return null + } + return castSession.remoteMediaClient + } + + private inner class MySessionManagerListener constructor() : + SessionManagerListener { + override fun onSessionResumed(session: CastSession, wasSuspended: Boolean) { + registerQueueCallbackAndUpdateQueue() + } + + override fun onSessionStarted(session: CastSession, sessionId: String) { + registerQueueCallbackAndUpdateQueue() + } + + override fun onSessionEnded(session: CastSession, error: Int) { + destroyQueue() + if (mListener != null) { + mListener!!.onQueueDataChanged() + } + } + + override fun onSessionStarting(session: CastSession) {} + override fun onSessionStartFailed(session: CastSession, error: Int) {} + override fun onSessionEnding(session: CastSession) {} + override fun onSessionResuming(session: CastSession, sessionId: String) {} + override fun onSessionResumeFailed(session: CastSession, error: Int) {} + override fun onSessionSuspended(session: CastSession, reason: Int) {} + } + + private inner class MyRemoteMediaClientCallback constructor() : RemoteMediaClient.Callback() { + override fun onPreloadStatusUpdated() { + updateMediaQueue() + if (mListener != null) { + mListener!!.onQueueDataChanged() + } + Log.d(TAG, "onPreloadStatusUpdated Queue was updated") + } + + override fun onQueueStatusUpdated() { + updateMediaQueue() + if (mListener != null) { + mListener!!.onQueueDataChanged() + } + Log.d(TAG, "onQueueStatusUpdated Queue was updated") + } + + override fun onStatusUpdated() { + updateMediaQueue() + if (mListener != null) { + mListener!!.onQueueDataChanged() + } + Log.d(TAG, "onStatusUpdated Queue was updated") + } + } + + companion object { + private val TAG: String = "QueueDataProvider" + val INVALID: Int = -1 + private var mInstance: QueueDataProvider? = null + @Synchronized + fun getInstance(context: Context?): QueueDataProvider? { + if (mInstance == null) { + mInstance = QueueDataProvider(context) + } + return mInstance + } + } +} \ No newline at end of file diff --git a/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/queue/ui/QueueItemTouchHelperCallback.kt b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/queue/ui/QueueItemTouchHelperCallback.kt new file mode 100644 index 0000000..ecbc852 --- /dev/null +++ b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/queue/ui/QueueItemTouchHelperCallback.kt @@ -0,0 +1,110 @@ +/* + * Copyright 2022 Google LLC. All Rights Reserved. + * + * 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.google.sample.cast.refplayer.queue.ui + +import androidx.recyclerview.widget.RecyclerView +import androidx.core.view.ViewCompat +import androidx.recyclerview.widget.ItemTouchHelper +import android.graphics.Canvas + +/** + * An implementation of the [androidx.recyclerview.widget.ItemTouchHelper.Callback]. + */ +class QueueItemTouchHelperCallback constructor(private val mAdapter: ItemTouchHelperAdapter) : + ItemTouchHelper.Callback() { + override fun isLongPressDragEnabled(): Boolean { + return true + } + + override fun isItemViewSwipeEnabled(): Boolean { + return true + } + + override fun getMovementFlags( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder + ): Int { + val dragFlags: Int = ItemTouchHelper.UP or ItemTouchHelper.DOWN + val swipeFlags: Int = ItemTouchHelper.END + return makeMovementFlags(dragFlags, swipeFlags) + } + + override fun onMove( + recyclerView: RecyclerView, source: RecyclerView.ViewHolder, + target: RecyclerView.ViewHolder + ): Boolean { + if (source.getItemViewType() != target.getItemViewType()) { + return false + } + mAdapter.onItemMove(source.getAdapterPosition(), target.getAdapterPosition()) + return true + } + + override fun onSwiped(viewHolder: RecyclerView.ViewHolder, i: Int) { + mAdapter.onItemDismiss(viewHolder.getAdapterPosition()) + } + + override fun onChildDraw( + c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, + dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean + ) { + if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { + if (viewHolder is QueueListAdapter.QueueItemViewHolder) { + ViewCompat.setTranslationX(viewHolder.mContainer, dX) + } + } else { + super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive) + } + } + + override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { + if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) { + if (viewHolder is QueueListAdapter.ItemTouchHelperViewHolder) { + viewHolder.onItemSelected() + } + } + super.onSelectedChanged(viewHolder, actionState) + } + + override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) { + super.clearView(recyclerView, viewHolder) + if (viewHolder is QueueListAdapter.ItemTouchHelperViewHolder) { + viewHolder.onItemClear() + } + } + + /** + * An interface to listen for a move or dismissal event from an + * [ItemTouchHelper.Callback]. + */ + open interface ItemTouchHelperAdapter { + /** + * Called when an item has been dragged far enough to trigger a move. This is called every + * time an item is shifted, and **not** at the end of a "drop" event. + * + * @param fromPosition Original position of the item before move. + * @param toPosition Target position of the item after move. + */ + fun onItemMove(fromPosition: Int, toPosition: Int): Boolean + + /** + * Called when an item has been dismissed by a swipe. + * + * @param position The position of the swiped item. + */ + fun onItemDismiss(position: Int) + } +} \ No newline at end of file diff --git a/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/queue/ui/QueueListAdapter.kt b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/queue/ui/QueueListAdapter.kt new file mode 100644 index 0000000..9b204ec --- /dev/null +++ b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/queue/ui/QueueListAdapter.kt @@ -0,0 +1,378 @@ +/* + * Copyright 2022 Google LLC. All Rights Reserved. + * + * 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.google.sample.cast.refplayer.queue.ui + +import android.util.Log +import android.content.Context +import androidx.recyclerview.widget.RecyclerView +import android.view.ViewGroup +import android.view.View +import android.view.LayoutInflater +import com.google.sample.cast.refplayer.R +import com.google.android.gms.cast.framework.CastContext +import com.android.volley.toolbox.NetworkImageView +import android.widget.TextView +import com.android.volley.toolbox.ImageLoader +import android.widget.ImageView +import android.widget.ImageButton +import android.widget.ProgressBar +import android.view.View.OnTouchListener +import android.view.MotionEvent +import com.google.android.gms.cast.framework.media.MediaQueue +import com.google.android.gms.cast.framework.media.MediaQueueRecyclerViewAdapter +import com.android.volley.toolbox.ImageLoader.ImageListener +import com.android.volley.VolleyError +import com.android.volley.toolbox.ImageLoader.ImageContainer +import androidx.core.view.MotionEventCompat +import androidx.annotation.IntDef +import androidx.recyclerview.widget.ItemTouchHelper +import com.google.android.gms.cast.* +import com.google.sample.cast.refplayer.queue.QueueDataProvider +import com.google.sample.cast.refplayer.utils.CustomVolleyRequest +import java.lang.annotation.Retention +import java.lang.annotation.RetentionPolicy + +/** + * An adapter to show the list of queue items. + */ +class QueueListAdapter( + mediaQueue: MediaQueue, + context: Context, + dragStartListener: OnStartDragListener +) : MediaQueueRecyclerViewAdapter(mediaQueue), + QueueItemTouchHelperCallback.ItemTouchHelperAdapter { + private val mAppContext: Context + private val mProvider: QueueDataProvider? + private val mDragStartListener: OnStartDragListener + private val mItemViewOnClickListener: View.OnClickListener + private var mEventListener: EventListener? = null + private var mImageLoader: ImageLoader? = null + private val myMediaQueueCallback: ListAdapterMediaQueueCallback = + ListAdapterMediaQueueCallback() + + init { + mAppContext = context.applicationContext + mProvider = QueueDataProvider.Companion.getInstance(context) + mDragStartListener = dragStartListener + mItemViewOnClickListener = View.OnClickListener { view -> + if (view.getTag(R.string.queue_tag_item) != null) { + val item = view.getTag(R.string.queue_tag_item) as MediaQueueItem + Log.d(TAG, item.itemId.toString()) + } + onItemViewClick(view) + } + getMediaQueue().registerCallback(myMediaQueueCallback) + setHasStableIds(false) + } + + private fun onItemViewClick(view: View) { + mEventListener?.onItemViewClicked(view) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QueueItemViewHolder { + val inflater = LayoutInflater.from(parent.context) + val view = inflater.inflate(R.layout.queue_row, parent, false) + return QueueItemViewHolder(view) + } + + override fun onBindViewHolder(holder: QueueItemViewHolder, position: Int) { + Log.d(TAG, "[upcoming] onBindViewHolder() for position: $position") + holder.setIsRecyclable(false) + val item = getItem(position) + if (item == null) { + holder.updateControlsStatus(QueueItemViewHolder.NONE) + } else if (mProvider!!.isCurrentItem(item)) { + holder.updateControlsStatus(QueueItemViewHolder.CURRENT) + updatePlayPauseButtonImageResource(holder.mPlayPause) + } else if (mProvider.isUpcomingItem(item)) { + holder.updateControlsStatus(QueueItemViewHolder.UPCOMING) + } else { + holder.updateControlsStatus(QueueItemViewHolder.NONE) + } + holder.mContainer.setTag(R.string.queue_tag_item, item) + holder.mPlayPause.setTag(R.string.queue_tag_item, item) + holder.mPlayUpcoming.setTag(R.string.queue_tag_item, item) + holder.mStopUpcoming.setTag(R.string.queue_tag_item, item) + + // Set listeners + holder.mContainer.setOnClickListener(mItemViewOnClickListener) + holder.mPlayPause.setOnClickListener(mItemViewOnClickListener) + holder.mPlayUpcoming.setOnClickListener(mItemViewOnClickListener) + holder.mStopUpcoming.setOnClickListener(mItemViewOnClickListener) + val info = item?.media + var imageUrl: String? = null + if (info != null && info.metadata != null) { + val metaData = info.metadata + holder.mTitleView.text = metaData!!.getString(MediaMetadata.KEY_TITLE) + holder.mDescriptionView.text = metaData.getString(MediaMetadata.KEY_SUBTITLE) + val images = metaData.images + if (images != null && images.isNotEmpty()) { + imageUrl = images[0].url.toString() + } + } else { + holder.mTitleView.text = null + holder.mDescriptionView.text = null + } + if (imageUrl != null) { + mImageLoader = CustomVolleyRequest.Companion.getInstance(mAppContext)?.imageLoader + val imageListener: ImageListener = object : ImageListener { + override fun onErrorResponse(error: VolleyError) { + holder.mProgressLoading.visibility = View.GONE + holder.mImageView.setErrorImageResId(R.drawable.ic_action_alerts_and_states_warning) + } + + override fun onResponse(response: ImageContainer, isImmediate: Boolean) { + if (response.bitmap != null) { + holder.mProgressLoading.visibility = View.GONE + holder.mImageView.setImageBitmap(response.bitmap) + } + } + } + holder.mImageView.setImageUrl( + mImageLoader!![imageUrl, imageListener].requestUrl, + mImageLoader + ) + } else { + holder.mProgressLoading.postDelayed({ + holder.mProgressLoading.visibility = View.GONE + holder.mImageView.setDefaultImageResId(R.drawable.cast_album_art_placeholder) + holder.mImageView.setImageResource(R.drawable.cast_album_art_placeholder) + }, 3000) + } + holder.mDragHandle.setOnTouchListener(OnTouchListener { view, event -> + if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) { + mDragStartListener.onStartDrag(holder) + } else if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_BUTTON_RELEASE) { + view.clearFocus() + view.clearAnimation() + holder.setIsRecyclable(false) + return@OnTouchListener true + } + false + }) + } + + override fun onItemDismiss(position: Int) { + mProvider!!.removeFromQueue(position) + } + + override fun onItemMove(fromPosition: Int, toPosition: Int): Boolean { + if (fromPosition == toPosition) { + return false + } + mProvider!!.moveItem(fromPosition, toPosition) + notifyItemMoved(fromPosition, toPosition) + return true + } + + override fun dispose() { + super.dispose() + //unregister callback + val queue = mediaQueue + if (queue != null) { + queue.unregisterCallback(myMediaQueueCallback) + } + } + + fun setEventListener(eventListener: EventListener?) { + mEventListener = eventListener + } + + private fun updatePlayPauseButtonImageResource(button: ImageButton) { + val castSession = CastContext.getSharedInstance(mAppContext) + .sessionManager.currentCastSession + val remoteMediaClient = castSession?.remoteMediaClient + if (remoteMediaClient == null) { + button.visibility = View.GONE + return + } + val status = remoteMediaClient.playerState + when (status) { + MediaStatus.PLAYER_STATE_PLAYING -> button.setImageResource(PAUSE_RESOURCE) + MediaStatus.PLAYER_STATE_PAUSED -> button.setImageResource(PLAY_RESOURCE) + else -> button.visibility = View.GONE + } + } + + /** + * Handles ListAdapter notification upon MediaQueue data changes. + */ + internal inner class ListAdapterMediaQueueCallback : MediaQueue.Callback() { + override fun itemsInsertedInRange(start: Int, end: Int) { + notifyItemRangeInserted(start, end) + } + + override fun itemsReloaded() { + notifyDataSetChanged() + } + + override fun itemsRemovedAtIndexes(ints: IntArray) { + for (i in ints) { + notifyItemRemoved(i) + } + } + + override fun itemsReorderedAtIndexes(list: List, i: Int) { + notifyDataSetChanged() + } + + override fun itemsUpdatedAtIndexes(ints: IntArray) { + for (i in ints) { + notifyItemChanged(i) + } + } + + override fun mediaQueueChanged() { + notifyDataSetChanged() + } + } + + class QueueItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), + ItemTouchHelperViewHolder { + private val mContext: Context + val mPlayPause: ImageButton + private val mControls: View + private val mUpcomingControls: View + val mPlayUpcoming: ImageButton + val mStopUpcoming: ImageButton + var mImageView: NetworkImageView + var mContainer: ViewGroup + var mDragHandle: ImageView + var mTitleView: TextView + var mDescriptionView: TextView + var mProgressLoading: ProgressBar + override fun onItemSelected() { + } + + override fun onItemClear() { + itemView.setBackgroundColor(0) + } + + @Retention(RetentionPolicy.SOURCE) + @IntDef(CURRENT, UPCOMING, NONE) + private annotation class ControlStatus + + init { + mContext = itemView.context + mContainer = itemView.findViewById(R.id.container) as ViewGroup + mDragHandle = itemView.findViewById(R.id.drag_handle) as ImageView + mTitleView = itemView.findViewById(R.id.textView1) as TextView + mDescriptionView = itemView.findViewById(R.id.textView2) as TextView + mImageView = itemView.findViewById(R.id.imageView1) as NetworkImageView + mPlayPause = itemView.findViewById(R.id.play_pause) as ImageButton + mControls = itemView.findViewById(R.id.controls) + mUpcomingControls = itemView.findViewById(R.id.controls_upcoming) + mPlayUpcoming = itemView.findViewById(R.id.play_upcoming) as ImageButton + mStopUpcoming = itemView.findViewById(R.id.stop_upcoming) as ImageButton + mProgressLoading = itemView.findViewById(R.id.item_progress) as ProgressBar + } + + fun updateControlsStatus(@ControlStatus status: Int) { + var bgResId = R.drawable.bg_item_normal_state + mTitleView.setTextAppearance(mContext, R.style.Base_TextAppearance_AppCompat_Subhead) + mDescriptionView.setTextAppearance( + mContext, + R.style.Base_TextAppearance_AppCompat_Caption + ) + Log.d(TAG, "updateControlsStatus for status = $status") + when (status) { + CURRENT -> { + bgResId = R.drawable.bg_item_normal_state + mControls.visibility = View.VISIBLE + mPlayPause.visibility = View.VISIBLE + mUpcomingControls.visibility = View.GONE + mDragHandle.setImageResource(DRAG_HANDLER_DARK_RESOURCE) + } + UPCOMING -> { + mControls.visibility = View.VISIBLE + mPlayPause.visibility = View.GONE + mUpcomingControls.visibility = View.VISIBLE + mDragHandle.setImageResource(DRAG_HANDLER_LIGHT_RESOURCE) + bgResId = R.drawable.bg_item_upcoming_state + mTitleView.setTextAppearance( + mContext, + R.style.TextAppearance_AppCompat_Small_Inverse + ) + mTitleView.setTextAppearance( + mTitleView.context, + R.style.Base_TextAppearance_AppCompat_Subhead_Inverse + ) + mDescriptionView.setTextAppearance( + mContext, + R.style.Base_TextAppearance_AppCompat_Caption + ) + } + else -> { + mControls.visibility = View.GONE + mPlayPause.visibility = View.GONE + mUpcomingControls.visibility = View.GONE + mDragHandle.setImageResource(DRAG_HANDLER_DARK_RESOURCE) + } + } + mContainer.setBackgroundResource(bgResId) + super.itemView.requestLayout() + } + + companion object { + const val CURRENT = 0 + const val UPCOMING = 1 + const val NONE = 2 + } + } + + /** + * Interface for catching clicks on the ViewHolder items + */ + interface EventListener { + fun onItemViewClicked(view: View) + } + + /** + * Interface to notify an item ViewHolder of relevant callbacks from [ ]. + */ + interface ItemTouchHelperViewHolder { + /** + * Called when the [ItemTouchHelper] first registers an item as being moved or + * swiped. + * Implementations should update the item view to indicate it's active state. + */ + fun onItemSelected() + + /** + * Called when the [ItemTouchHelper] has completed the move or swipe, and the active + * item state should be cleared. + */ + fun onItemClear() + } + + /** + * Listener for manual initiation of a drag. + */ + interface OnStartDragListener { + /** + * Called when a view is requesting a start of a drag. + */ + fun onStartDrag(viewHolder: RecyclerView.ViewHolder?) + } + + companion object { + private const val TAG = "QueueListAdapter" + private const val PLAY_RESOURCE = R.drawable.ic_play_arrow_grey600_48dp + private const val PAUSE_RESOURCE = R.drawable.ic_pause_grey600_48dp + private const val DRAG_HANDLER_DARK_RESOURCE = R.drawable.ic_drag_updown_grey_24dp + private const val DRAG_HANDLER_LIGHT_RESOURCE = R.drawable.ic_drag_updown_white_24dp + } +} \ No newline at end of file diff --git a/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/queue/ui/QueueListViewActivity.kt b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/queue/ui/QueueListViewActivity.kt new file mode 100644 index 0000000..70e5e58 --- /dev/null +++ b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/queue/ui/QueueListViewActivity.kt @@ -0,0 +1,197 @@ +/* + * Copyright 2022 Google LLC. All Rights Reserved. + * + * 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.google.sample.cast.refplayer.queue.ui + +import android.util.Log +import android.view.View +import com.google.sample.cast.refplayer.R +import com.google.android.gms.cast.framework.CastSession +import com.google.android.gms.cast.framework.CastContext +import com.google.android.gms.cast.framework.SessionManagerListener +import android.os.Bundle +import android.content.Intent +import android.view.Menu +import com.google.android.gms.cast.framework.CastButtonFactory +import androidx.appcompat.app.AppCompatActivity +import android.view.MenuItem +import com.google.android.gms.cast.framework.media.RemoteMediaClient +import android.view.KeyEvent +import androidx.appcompat.widget.Toolbar +import com.google.sample.cast.refplayer.queue.QueueDataProvider +import com.google.sample.cast.refplayer.settings.CastPreference +import java.util.concurrent.Executor +import java.util.concurrent.Executors + +/** + * An activity to show the queue list + */ +class QueueListViewActivity : AppCompatActivity() { + private val mRemoteMediaClientCallback: RemoteMediaClient.Callback = + MyRemoteMediaClientCallback() + private val mSessionManagerListener: SessionManagerListener = + MySessionManagerListener() + private var mCastContext: CastContext? = null + private val castExecutor: Executor = Executors.newSingleThreadExecutor(); + private var mRemoteMediaClient: RemoteMediaClient? = null + private var mEmptyView: View? = null + + private inner class MySessionManagerListener : SessionManagerListener { + override fun onSessionEnded(session: CastSession, error: Int) { + if (mRemoteMediaClient != null) { + mRemoteMediaClient!!.unregisterCallback(mRemoteMediaClientCallback) + } + mRemoteMediaClient = null + mEmptyView!!.visibility = View.VISIBLE + } + + override fun onSessionResumed(session: CastSession, wasSuspended: Boolean) { + mRemoteMediaClient = remoteMediaClient + if (mRemoteMediaClient != null) { + mRemoteMediaClient!!.registerCallback(mRemoteMediaClientCallback) + } + } + + override fun onSessionStarted(session: CastSession, sessionId: String) { + mRemoteMediaClient = remoteMediaClient + if (mRemoteMediaClient != null) { + mRemoteMediaClient!!.registerCallback(mRemoteMediaClientCallback) + } + } + + override fun onSessionStarting(session: CastSession) {} + override fun onSessionStartFailed(session: CastSession, error: Int) {} + override fun onSessionEnding(session: CastSession) {} + override fun onSessionResuming(session: CastSession, sessionId: String) {} + override fun onSessionResumeFailed(session: CastSession, error: Int) {} + override fun onSessionSuspended(session: CastSession, reason: Int) { + if (mRemoteMediaClient != null) { + mRemoteMediaClient!!.unregisterCallback(mRemoteMediaClientCallback) + } + mRemoteMediaClient = null + } + } + + private inner class MyRemoteMediaClientCallback : RemoteMediaClient.Callback() { + override fun onStatusUpdated() { + updateMediaQueue() + } + + override fun onQueueStatusUpdated() { + updateMediaQueue() + } + + private fun updateMediaQueue() { + val mediaStatus = mRemoteMediaClient!!.mediaStatus + val queueItems = mediaStatus?.queueItems + if (queueItems == null || queueItems.isEmpty()) { + mEmptyView!!.visibility = View.VISIBLE + } else { + mEmptyView!!.visibility = View.GONE + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.queue_activity) + Log.d(TAG, "onCreate() was called") + if (savedInstanceState == null) { + supportFragmentManager.beginTransaction() + .add(R.id.container, QueueListViewFragment(), FRAGMENT_LIST_VIEW) + .commit() + } + setupActionBar() + mEmptyView = findViewById(R.id.empty) + mCastContext = CastContext.getSharedInstance(this,castExecutor).result + } + + private fun setupActionBar() { + val toolbar = findViewById(R.id.toolbar) as Toolbar + toolbar.setTitle(R.string.queue_list) + setSupportActionBar(toolbar) + supportActionBar!!.setDisplayHomeAsUpEnabled(true) + } + + override fun onPause() { + if (mRemoteMediaClient != null) { + mRemoteMediaClient!!.unregisterCallback(mRemoteMediaClientCallback) + } + mCastContext!!.sessionManager.removeSessionManagerListener( + mSessionManagerListener, CastSession::class.java + ) + super.onPause() + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + super.onCreateOptionsMenu(menu) + menuInflater.inflate(R.menu.queue_menu, menu) + CastButtonFactory.setUpMediaRouteButton( + applicationContext, menu, + R.id.media_route_menu_item + ) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.action_settings -> startActivity( + Intent( + this@QueueListViewActivity, + CastPreference::class.java + ) + ) + R.id.action_clear_queue -> QueueDataProvider.Companion.getInstance( + applicationContext + )!!.removeAll() + android.R.id.home -> finish() + } + return true + } + + override fun dispatchKeyEvent(event: KeyEvent): Boolean { + return (mCastContext!!.onDispatchVolumeKeyEventBeforeJellyBean(event) + || super.dispatchKeyEvent(event)) + } + + override fun onResume() { + mCastContext!!.sessionManager.addSessionManagerListener( + mSessionManagerListener, CastSession::class.java + ) + if (mRemoteMediaClient == null) { + mRemoteMediaClient = remoteMediaClient + } + if (mRemoteMediaClient != null) { + mRemoteMediaClient!!.registerCallback(mRemoteMediaClientCallback) + val mediaStatus = mRemoteMediaClient!!.mediaStatus + val queueItems = mediaStatus?.queueItems + if (queueItems != null && queueItems.isNotEmpty()) { + mEmptyView!!.visibility = View.GONE + } + } + super.onResume() + } + + private val remoteMediaClient: RemoteMediaClient? + private get() { + val castSession = mCastContext!!.sessionManager.currentCastSession + return if (castSession != null && castSession.isConnected) castSession.remoteMediaClient else null + } + + companion object { + private const val FRAGMENT_LIST_VIEW = "list view" + private const val TAG = "QueueListViewActivity" + } +} \ No newline at end of file diff --git a/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/queue/ui/QueueListViewFragment.kt b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/queue/ui/QueueListViewFragment.kt new file mode 100644 index 0000000..d6f20d8 --- /dev/null +++ b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/queue/ui/QueueListViewFragment.kt @@ -0,0 +1,149 @@ +/* + * Copyright 2022 Google LLC. All Rights Reserved. + * + * 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.google.sample.cast.refplayer.queue.ui + +import org.json.JSONObject +import android.util.Log +import androidx.recyclerview.widget.RecyclerView +import android.view.ViewGroup +import android.view.View +import android.view.LayoutInflater +import com.google.sample.cast.refplayer.R +import com.google.android.gms.cast.framework.CastContext +import android.os.Bundle +import androidx.recyclerview.widget.LinearLayoutManager +import android.content.Intent +import com.google.android.gms.cast.framework.media.RemoteMediaClient +import com.google.android.gms.cast.MediaQueueItem +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.fragment.app.Fragment +import com.google.sample.cast.refplayer.expandedcontrols.ExpandedControlsActivity +import com.google.sample.cast.refplayer.queue.QueueDataProvider +import java.util.concurrent.Executor +import java.util.concurrent.Executors + +/** + * A fragment to show the list of queue items. + */ +class QueueListViewFragment() : Fragment(), QueueListAdapter.OnStartDragListener { + private var mProvider: QueueDataProvider? = null + private var mItemTouchHelper: ItemTouchHelper? = null + private val castExecutor: Executor = Executors.newSingleThreadExecutor(); + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.fragment_recycler_list_view, container, false) + } + + override fun onStartDrag(viewHolder: RecyclerView.ViewHolder?) { + mItemTouchHelper!!.startDrag((viewHolder)!!) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val recyclerView = requireView().findViewById(R.id.recycler_view) as RecyclerView + mProvider = QueueDataProvider.Companion.getInstance(context) + val adapter = QueueListAdapter(mProvider!!.mediaQueue!! , (activity)!!, this) + recyclerView.setHasFixedSize(true) + recyclerView.adapter = adapter + recyclerView.layoutManager = LinearLayoutManager(activity) + val callback: ItemTouchHelper.Callback = QueueItemTouchHelperCallback(adapter) + mItemTouchHelper = ItemTouchHelper(callback) + mItemTouchHelper!!.attachToRecyclerView(recyclerView) + adapter.setEventListener( object: QueueListAdapter.EventListener { + override fun onItemViewClicked(view: View) { + when (view.id) { + R.id.container -> { + Log.d( + TAG, ("onItemViewClicked() container " + + view.getTag(R.string.queue_tag_item)) + ) + onContainerClicked(view) + } + R.id.play_pause -> { + Log.d( + TAG, ("onItemViewClicked() play-pause " + + view.getTag(R.string.queue_tag_item)) + ) + onPlayPauseClicked(view) + } + R.id.play_upcoming -> mProvider!!.onUpcomingPlayClicked( + view, + view.getTag(R.string.queue_tag_item) as MediaQueueItem + ) + R.id.stop_upcoming -> mProvider!!.onUpcomingStopClicked( + view, + view.getTag(R.string.queue_tag_item) as MediaQueueItem + ) + } + }}) + + mProvider!!.setOnQueueDataChangedListener( + object: QueueDataProvider.OnQueueDataChangedListener { + override fun onQueueDataChanged() { + adapter.notifyDataSetChanged() + } + }) + + } + + private fun onPlayPauseClicked(view: View) { + val remoteMediaClient = remoteMediaClient + remoteMediaClient?.togglePlayback() + } + + private fun onContainerClicked(view: View) { + val remoteMediaClient = remoteMediaClient ?: return + val item = view.getTag(R.string.queue_tag_item) as MediaQueueItem + val currentItemId = mProvider!!.currentItemId + if (currentItemId == item.itemId) { + // We selected the one that is currently playing so we take the user to the + // full screen controller + val castSession = CastContext.getSharedInstance(requireContext().applicationContext,castExecutor) + .result + .sessionManager + .currentCastSession + if (castSession != null) { + val intent = Intent(activity, ExpandedControlsActivity::class.java) + startActivity(intent) + } + } else { + // a different item in the queue was selected so we jump there + remoteMediaClient.queueJumpToItem(item.itemId, JSONObject() ) + } + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + retainInstance = true + } + + private val remoteMediaClient: RemoteMediaClient? + private get() { + val castSession = CastContext.getSharedInstance((context)!!,castExecutor) + .result + .sessionManager + .currentCastSession + return if (castSession != null && castSession.isConnected) { + castSession.remoteMediaClient + } else null + } + + companion object { + private val TAG = "QueueListViewFragment" + } +} \ No newline at end of file diff --git a/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/settings/CastPreference.kt b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/settings/CastPreference.kt new file mode 100644 index 0000000..6e7179f --- /dev/null +++ b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/settings/CastPreference.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2022 Google LLC. All Rights Reserved. + * + * 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.google.sample.cast.refplayer.settings + +import android.content.SharedPreferences +import android.content.SharedPreferences.OnSharedPreferenceChangeListener +import android.os.Bundle +import androidx.fragment.app.FragmentActivity +import androidx.preference.EditTextPreference +import androidx.preference.PreferenceFragmentCompat +import com.google.sample.cast.refplayer.R +import com.google.sample.cast.refplayer.utils.Utils + + +/** + * An Preference Activity. + */ +class CastPreference constructor() : FragmentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + supportFragmentManager.beginTransaction().replace(android.R.id.content, CastPreference.MyPreferenceFragment()).commit(); + } + + class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeListener { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + } + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + setPreferencesFromResource(R.xml.application_preference, rootKey) + + val versionPref: EditTextPreference? = findPreference("app_version") as EditTextPreference? + versionPref?.setTitle(getString(R.string.version, Utils.getAppVersionName(requireActivity().applicationContext))) + preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this) + + } + + override fun onSharedPreferenceChanged( + sharedPreferences: SharedPreferences, + key: String + ) { + // Handle volume and caption preferences. + } + } +} \ No newline at end of file diff --git a/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/utils/CustomVolleyRequest.kt b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/utils/CustomVolleyRequest.kt new file mode 100644 index 0000000..cf29cf2 --- /dev/null +++ b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/utils/CustomVolleyRequest.kt @@ -0,0 +1,73 @@ +/* + * Copyright 2022 Google LLC. All Rights Reserved. + * + * 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.google.sample.cast.refplayer.utils + +import android.content.Context +import com.android.volley.toolbox.ImageLoader +import kotlin.jvm.Synchronized +import com.android.volley.RequestQueue +import com.android.volley.toolbox.ImageLoader.ImageCache +import android.graphics.Bitmap +import androidx.collection.LruCache +import com.android.volley.Cache +import com.android.volley.Network +import com.android.volley.toolbox.DiskBasedCache +import com.android.volley.toolbox.BasicNetwork +import com.android.volley.toolbox.HurlStack + +class CustomVolleyRequest private constructor(context: Context) { + private var requestQueue: RequestQueue? + val imageLoader: ImageLoader + private var context: Context + + init { + this.context = context + requestQueue = getRequestQueue() + imageLoader = ImageLoader(requestQueue, + object : ImageCache { + private val cache: LruCache = LruCache(20) + override fun getBitmap(url: String): Bitmap? { + return cache.get(url) + } + + override fun putBitmap(url: String, bitmap: Bitmap) { + cache.put(url, bitmap) + } + }) + } + + private fun getRequestQueue(): RequestQueue { + if (requestQueue == null) { + val cache: Cache = DiskBasedCache(context.getCacheDir(), 10 * 1024 * 1024) + val network: Network = BasicNetwork(HurlStack()) + requestQueue = RequestQueue(cache, network) + requestQueue!!.start() + } + return requestQueue!! + } + + companion object { + private var customVolleyRequest: CustomVolleyRequest? = null + + @Synchronized + fun getInstance(context: Context): CustomVolleyRequest? { + if (customVolleyRequest == null) { + customVolleyRequest = CustomVolleyRequest(context) + } + return customVolleyRequest + } + } +} \ No newline at end of file diff --git a/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/utils/Utils.kt b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/utils/Utils.kt new file mode 100644 index 0000000..a1d721a --- /dev/null +++ b/app-kotlin/src/main/kotlin/com/google/sample/cast/refplayer/utils/Utils.kt @@ -0,0 +1,209 @@ +/* + * Copyright 2022 Google LLC. All Rights Reserved. + * + * 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.google.sample.cast.refplayer.utils + +import java.lang.Exception +import android.util.Log +import com.google.android.gms.cast.MediaInfo +import android.content.Context +import android.view.View +import com.google.sample.cast.refplayer.R +import com.google.android.gms.cast.framework.CastSession +import com.google.android.gms.cast.framework.CastContext +import android.content.Intent +import android.view.MenuItem +import com.google.android.gms.cast.framework.media.RemoteMediaClient +import android.app.AlertDialog +import android.view.WindowManager +import com.google.android.gms.cast.MediaQueueItem +import com.google.android.gms.cast.MediaStatus +import android.view.Display +import android.content.DialogInterface +import android.content.pm.PackageInfo +import android.content.res.Configuration +import android.widget.Toast +import java.util.Locale +import android.graphics.Point +import android.text.TextUtils +import androidx.appcompat.widget.PopupMenu +import com.google.sample.cast.refplayer.expandedcontrols.ExpandedControlsActivity +import com.google.sample.cast.refplayer.queue.QueueDataProvider +import org.json.JSONObject +import java.util.concurrent.Executor +import java.util.concurrent.Executors + +/** + * A collection of utility methods, all static. + */ +object Utils { + private const val TAG: String = "Utils" + private const val PRELOAD_TIME_S: Int = 20 + private val utilCastExecutor: Executor = Executors.newSingleThreadExecutor(); + + /** + * Returns the screen/display size + * + */ + fun getDisplaySize(context: Context): Point { + val wm: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager + val display: Display = wm.defaultDisplay + val width: Int = display.width + val height: Int = display.height + return Point(width, height) + } + + /** + * Returns `true` if and only if the screen orientation is portrait. + */ + fun isOrientationPortrait(context: Context): Boolean { + return (context.resources.configuration.orientation + == Configuration.ORIENTATION_PORTRAIT) + } + + /** + * Shows an error dialog with a given text message. + */ + fun showErrorDialog(context: Context?, errorString: String?) { + AlertDialog.Builder(context).setTitle(R.string.error) + .setMessage(errorString) + .setPositiveButton(R.string.ok, object : DialogInterface.OnClickListener { + public override fun onClick(dialog: DialogInterface, id: Int) { + dialog.cancel() + } + }) + .create() + .show() + } + + /** + * Gets the version of app. + */ + fun getAppVersionName(context: Context): String? { + var versionString: String? = null + try { + val info: PackageInfo = context.packageManager.getPackageInfo( + context.packageName, + 0 /* basic info */ + ) + versionString = info.versionName + } catch (e: Exception) { + //do nothing + Log.w(TAG,e.message,e); + } + return versionString + } + + /** + * Formats time from milliseconds to hh:mm:ss string format. + */ + fun formatMillis(millisec: Int): String { + var seconds: Int = (millisec / 1000) + val hours: Int = seconds / (60 * 60) + seconds %= (60 * 60) + val minutes: Int = seconds / 60 + seconds %= 60 + val time: String = if (hours > 0) { + String.format(Locale.ROOT, "%d:%02d:%02d", hours, minutes, seconds) + } else { + String.format(Locale.ROOT, "%d:%02d", minutes, seconds) + } + return time + } + + /** + * Show a popup to select whether the selected item should play immediately, be added to the + * end of queue or be added to the queue right after the current item. + */ + fun showQueuePopup(context: Context?, view: View?, mediaInfo: MediaInfo?) { + val castSession: CastSession? = + CastContext.getSharedInstance(context!!,utilCastExecutor).result.sessionManager.currentCastSession + if (castSession == null || !castSession.isConnected) { + Log.w(TAG, "showQueuePopup(): not connected to a cast device") + return + } + val remoteMediaClient: RemoteMediaClient? = castSession.remoteMediaClient + if (remoteMediaClient == null) { + Log.w(TAG, "showQueuePopup(): null RemoteMediaClient") + return + } + val provider: QueueDataProvider? = QueueDataProvider.Companion.getInstance(context) + val popup = PopupMenu((context)!!, (view)!!) + popup.menuInflater.inflate( + if (provider!!.isQueueDetached || provider!!.count == 0) R.menu.detached_popup_add_to_queue else R.menu.popup_add_to_queue, + popup.menu + ) + val clickListener: PopupMenu.OnMenuItemClickListener = + object : PopupMenu.OnMenuItemClickListener { + override fun onMenuItemClick(menuItem: MenuItem): Boolean { + val queueItem: MediaQueueItem = + MediaQueueItem.Builder((mediaInfo)!!).setAutoplay( + true + ).setPreloadTime(PRELOAD_TIME_S.toDouble()).build() + val newItemArray: Array = arrayOf(queueItem) + var toastMessage: String? = null + if (provider?.count == 0) { + remoteMediaClient.queueLoad( + newItemArray, 0, + MediaStatus.REPEAT_MODE_REPEAT_OFF, JSONObject() + ) + } else { + val currentId: Int = provider!!.currentItemId + if (menuItem.itemId == R.id.action_play_now) { + remoteMediaClient.queueInsertAndPlayItem(queueItem, currentId, JSONObject() ) + } else if (menuItem.itemId == R.id.action_play_next) { + val currentPosition: Int = provider!!.getPositionByItemId(currentId) + if (currentPosition == provider!!.count - 1) { + //we are adding to the end of queue + remoteMediaClient.queueAppendItem(queueItem, JSONObject() ) + } else { + val nextItem: MediaQueueItem? = + provider.getItem(currentPosition + 1) + if (nextItem != null) { + val nextItemId: Int = nextItem.itemId + remoteMediaClient.queueInsertItems( + newItemArray, + nextItemId, + JSONObject() + ) + } else { + //remote queue is not ready with item; try again. + return false + } + } + toastMessage = context.getString( + R.string.queue_item_added_to_play_next + ) + } else if (menuItem.itemId == R.id.action_add_to_queue) { + remoteMediaClient.queueAppendItem(queueItem, JSONObject() ) + toastMessage = context.getString(R.string.queue_item_added_to_queue) + } else { + return false + } + } + if (menuItem.getItemId() == R.id.action_play_now) { + val intent: Intent = Intent(context, ExpandedControlsActivity::class.java) + context!!.startActivity(intent) + } + if (!TextUtils.isEmpty(toastMessage)) { + Toast.makeText(context, toastMessage, Toast.LENGTH_SHORT).show() + } + return true + } + } + popup.setOnMenuItemClickListener(clickListener) + popup.show() + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle index 238e386..5c76502 100644 --- a/build.gradle +++ b/build.gradle @@ -1,65 +1,22 @@ -apply plugin: 'com.android.application' - buildscript { + ext.kotlin_version = '1.7.0' repositories { jcenter() google() } dependencies { classpath 'com.android.tools.build:gradle:7.2.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } -repositories { - jcenter() - google() -} - -android { - compileSdkVersion 32 - - defaultConfig { - minSdkVersion 18 - targetSdkVersion 32 - versionCode 20 - versionName "3.0" - applicationId="com.google.sample.cast.refplayer" - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - dexOptions { - javaMaxHeapSize "4g" - } - - lintOptions.abortOnError false - - sourceSets { - main { - manifest.srcFile 'AndroidManifest.xml' - java.srcDirs = ['src'] - res.srcDirs = ['res'] - } - androidTest { - java.srcDirs = ['androidTest'] - } - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 +allprojects { + repositories { + google() + jcenter() } } -dependencies { - implementation 'androidx.appcompat:appcompat:1.5.0' - implementation 'androidx.mediarouter:mediarouter:1.3.1' - implementation 'androidx.recyclerview:recyclerview:1.2.1' - implementation 'com.google.android.gms:play-services-cast-framework:21.1.0' - implementation 'com.android.volley:volley:1.2.1' - - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' - androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.4.0' - androidTestImplementation 'androidx.test:runner:1.4.0' - androidTestImplementation 'androidx.test:rules:1.4.0' - androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0' - testImplementation 'junit:junit:4.12' +task clean(type: Delete) { + delete rootProject.buildDir } \ No newline at end of file diff --git a/res/drawable-hdpi-v11/ic_stat_action_notification.png b/resources/drawable-hdpi-v11/ic_stat_action_notification.png similarity index 100% rename from res/drawable-hdpi-v11/ic_stat_action_notification.png rename to resources/drawable-hdpi-v11/ic_stat_action_notification.png diff --git a/res/drawable-hdpi-v11/ic_stat_hardware_headphones.png b/resources/drawable-hdpi-v11/ic_stat_hardware_headphones.png similarity index 100% rename from res/drawable-hdpi-v11/ic_stat_hardware_headphones.png rename to resources/drawable-hdpi-v11/ic_stat_hardware_headphones.png diff --git a/res/drawable-hdpi/ab_bottom_solid_castvideo.9.png b/resources/drawable-hdpi/ab_bottom_solid_castvideo.9.png similarity index 100% rename from res/drawable-hdpi/ab_bottom_solid_castvideo.9.png rename to resources/drawable-hdpi/ab_bottom_solid_castvideo.9.png diff --git a/res/drawable-hdpi/ab_bottom_solid_democast.9.png b/resources/drawable-hdpi/ab_bottom_solid_democast.9.png similarity index 100% rename from res/drawable-hdpi/ab_bottom_solid_democast.9.png rename to resources/drawable-hdpi/ab_bottom_solid_democast.9.png diff --git a/res/drawable-hdpi/ab_solid_castvideo.9.png b/resources/drawable-hdpi/ab_solid_castvideo.9.png similarity index 100% rename from res/drawable-hdpi/ab_solid_castvideo.9.png rename to resources/drawable-hdpi/ab_solid_castvideo.9.png diff --git a/res/drawable-hdpi/ab_solid_democast.9.png b/resources/drawable-hdpi/ab_solid_democast.9.png similarity index 100% rename from res/drawable-hdpi/ab_solid_democast.9.png rename to resources/drawable-hdpi/ab_solid_democast.9.png diff --git a/res/drawable-hdpi/ab_stacked_solid_castvideo.9.png b/resources/drawable-hdpi/ab_stacked_solid_castvideo.9.png similarity index 100% rename from res/drawable-hdpi/ab_stacked_solid_castvideo.9.png rename to resources/drawable-hdpi/ab_stacked_solid_castvideo.9.png diff --git a/res/drawable-hdpi/ab_stacked_solid_democast.9.png b/resources/drawable-hdpi/ab_stacked_solid_democast.9.png similarity index 100% rename from res/drawable-hdpi/ab_stacked_solid_democast.9.png rename to resources/drawable-hdpi/ab_stacked_solid_democast.9.png diff --git a/res/drawable-hdpi/ab_texture_tile_castvideo.png b/resources/drawable-hdpi/ab_texture_tile_castvideo.png similarity index 100% rename from res/drawable-hdpi/ab_texture_tile_castvideo.png rename to resources/drawable-hdpi/ab_texture_tile_castvideo.png diff --git a/res/drawable-hdpi/ab_texture_tile_democast.png b/resources/drawable-hdpi/ab_texture_tile_democast.png similarity index 100% rename from res/drawable-hdpi/ab_texture_tile_democast.png rename to resources/drawable-hdpi/ab_texture_tile_democast.png diff --git a/res/drawable-hdpi/ab_transparent_castvideo.9.png b/resources/drawable-hdpi/ab_transparent_castvideo.9.png similarity index 100% rename from res/drawable-hdpi/ab_transparent_castvideo.9.png rename to resources/drawable-hdpi/ab_transparent_castvideo.9.png diff --git a/res/drawable-hdpi/ab_transparent_democast.9.png b/resources/drawable-hdpi/ab_transparent_democast.9.png similarity index 100% rename from res/drawable-hdpi/ab_transparent_democast.9.png rename to resources/drawable-hdpi/ab_transparent_democast.9.png diff --git a/res/drawable-hdpi/ab_transparent_democastoverlay.9.png b/resources/drawable-hdpi/ab_transparent_democastoverlay.9.png similarity index 100% rename from res/drawable-hdpi/ab_transparent_democastoverlay.9.png rename to resources/drawable-hdpi/ab_transparent_democastoverlay.9.png diff --git a/res/drawable-hdpi/actionbar_logo.png b/resources/drawable-hdpi/actionbar_logo.png similarity index 100% rename from res/drawable-hdpi/actionbar_logo.png rename to resources/drawable-hdpi/actionbar_logo.png diff --git a/res/drawable-hdpi/actionbar_logo_castvideos.png b/resources/drawable-hdpi/actionbar_logo_castvideos.png similarity index 100% rename from res/drawable-hdpi/actionbar_logo_castvideos.png rename to resources/drawable-hdpi/actionbar_logo_castvideos.png diff --git a/res/drawable-hdpi/apptheme_scrubber_control_disabled_holo.png b/resources/drawable-hdpi/apptheme_scrubber_control_disabled_holo.png similarity index 100% rename from res/drawable-hdpi/apptheme_scrubber_control_disabled_holo.png rename to resources/drawable-hdpi/apptheme_scrubber_control_disabled_holo.png diff --git a/res/drawable-hdpi/apptheme_scrubber_control_focused_holo.png b/resources/drawable-hdpi/apptheme_scrubber_control_focused_holo.png similarity index 100% rename from res/drawable-hdpi/apptheme_scrubber_control_focused_holo.png rename to resources/drawable-hdpi/apptheme_scrubber_control_focused_holo.png diff --git a/res/drawable-hdpi/apptheme_scrubber_control_normal_holo.png b/resources/drawable-hdpi/apptheme_scrubber_control_normal_holo.png similarity index 100% rename from res/drawable-hdpi/apptheme_scrubber_control_normal_holo.png rename to resources/drawable-hdpi/apptheme_scrubber_control_normal_holo.png diff --git a/res/drawable-hdpi/apptheme_scrubber_control_pressed_holo.png b/resources/drawable-hdpi/apptheme_scrubber_control_pressed_holo.png similarity index 100% rename from res/drawable-hdpi/apptheme_scrubber_control_pressed_holo.png rename to resources/drawable-hdpi/apptheme_scrubber_control_pressed_holo.png diff --git a/res/drawable-hdpi/apptheme_scrubber_primary_holo.9.png b/resources/drawable-hdpi/apptheme_scrubber_primary_holo.9.png similarity index 100% rename from res/drawable-hdpi/apptheme_scrubber_primary_holo.9.png rename to resources/drawable-hdpi/apptheme_scrubber_primary_holo.9.png diff --git a/res/drawable-hdpi/apptheme_scrubber_secondary_holo.9.png b/resources/drawable-hdpi/apptheme_scrubber_secondary_holo.9.png similarity index 100% rename from res/drawable-hdpi/apptheme_scrubber_secondary_holo.9.png rename to resources/drawable-hdpi/apptheme_scrubber_secondary_holo.9.png diff --git a/res/drawable-hdpi/apptheme_scrubber_track_holo_light.9.png b/resources/drawable-hdpi/apptheme_scrubber_track_holo_light.9.png similarity index 100% rename from res/drawable-hdpi/apptheme_scrubber_track_holo_light.9.png rename to resources/drawable-hdpi/apptheme_scrubber_track_holo_light.9.png diff --git a/res/drawable-hdpi/arrow_down_white.png b/resources/drawable-hdpi/arrow_down_white.png similarity index 100% rename from res/drawable-hdpi/arrow_down_white.png rename to resources/drawable-hdpi/arrow_down_white.png diff --git a/res/drawable-hdpi/btn_cab_done_default_castvideo.9.png b/resources/drawable-hdpi/btn_cab_done_default_castvideo.9.png similarity index 100% rename from res/drawable-hdpi/btn_cab_done_default_castvideo.9.png rename to resources/drawable-hdpi/btn_cab_done_default_castvideo.9.png diff --git a/res/drawable-hdpi/btn_cab_done_default_democast.9.png b/resources/drawable-hdpi/btn_cab_done_default_democast.9.png similarity index 100% rename from res/drawable-hdpi/btn_cab_done_default_democast.9.png rename to resources/drawable-hdpi/btn_cab_done_default_democast.9.png diff --git a/res/drawable-hdpi/btn_cab_done_focused_castvideo.9.png b/resources/drawable-hdpi/btn_cab_done_focused_castvideo.9.png similarity index 100% rename from res/drawable-hdpi/btn_cab_done_focused_castvideo.9.png rename to resources/drawable-hdpi/btn_cab_done_focused_castvideo.9.png diff --git a/res/drawable-hdpi/btn_cab_done_focused_democast.9.png b/resources/drawable-hdpi/btn_cab_done_focused_democast.9.png similarity index 100% rename from res/drawable-hdpi/btn_cab_done_focused_democast.9.png rename to resources/drawable-hdpi/btn_cab_done_focused_democast.9.png diff --git a/res/drawable-hdpi/btn_cab_done_pressed_castvideo.9.png b/resources/drawable-hdpi/btn_cab_done_pressed_castvideo.9.png similarity index 100% rename from res/drawable-hdpi/btn_cab_done_pressed_castvideo.9.png rename to resources/drawable-hdpi/btn_cab_done_pressed_castvideo.9.png diff --git a/res/drawable-hdpi/btn_cab_done_pressed_democast.9.png b/resources/drawable-hdpi/btn_cab_done_pressed_democast.9.png similarity index 100% rename from res/drawable-hdpi/btn_cab_done_pressed_democast.9.png rename to resources/drawable-hdpi/btn_cab_done_pressed_democast.9.png diff --git a/res/drawable-hdpi/cab_background_bottom_castvideo.9.png b/resources/drawable-hdpi/cab_background_bottom_castvideo.9.png similarity index 100% rename from res/drawable-hdpi/cab_background_bottom_castvideo.9.png rename to resources/drawable-hdpi/cab_background_bottom_castvideo.9.png diff --git a/res/drawable-hdpi/cab_background_bottom_democast.9.png b/resources/drawable-hdpi/cab_background_bottom_democast.9.png similarity index 100% rename from res/drawable-hdpi/cab_background_bottom_democast.9.png rename to resources/drawable-hdpi/cab_background_bottom_democast.9.png diff --git a/res/drawable-hdpi/cab_background_top_castvideo.9.png b/resources/drawable-hdpi/cab_background_top_castvideo.9.png similarity index 100% rename from res/drawable-hdpi/cab_background_top_castvideo.9.png rename to resources/drawable-hdpi/cab_background_top_castvideo.9.png diff --git a/res/drawable-hdpi/cab_background_top_democast.9.png b/resources/drawable-hdpi/cab_background_top_democast.9.png similarity index 100% rename from res/drawable-hdpi/cab_background_top_democast.9.png rename to resources/drawable-hdpi/cab_background_top_democast.9.png diff --git a/res/drawable-hdpi/ic_action_alerts_and_states_warning.png b/resources/drawable-hdpi/ic_action_alerts_and_states_warning.png similarity index 100% rename from res/drawable-hdpi/ic_action_alerts_and_states_warning.png rename to resources/drawable-hdpi/ic_action_alerts_and_states_warning.png diff --git a/res/drawable-hdpi/ic_add_circle_outline_white.png b/resources/drawable-hdpi/ic_add_circle_outline_white.png similarity index 100% rename from res/drawable-hdpi/ic_add_circle_outline_white.png rename to resources/drawable-hdpi/ic_add_circle_outline_white.png diff --git a/res/drawable-hdpi/ic_add_white_18dp.png b/resources/drawable-hdpi/ic_add_white_18dp.png similarity index 100% rename from res/drawable-hdpi/ic_add_white_18dp.png rename to resources/drawable-hdpi/ic_add_white_18dp.png diff --git a/res/drawable-hdpi/ic_add_white_24dp.png b/resources/drawable-hdpi/ic_add_white_24dp.png similarity index 100% rename from res/drawable-hdpi/ic_add_white_24dp.png rename to resources/drawable-hdpi/ic_add_white_24dp.png diff --git a/res/drawable-hdpi/ic_add_white_36dp.png b/resources/drawable-hdpi/ic_add_white_36dp.png similarity index 100% rename from res/drawable-hdpi/ic_add_white_36dp.png rename to resources/drawable-hdpi/ic_add_white_36dp.png diff --git a/res/drawable-hdpi/ic_add_white_48dp.png b/resources/drawable-hdpi/ic_add_white_48dp.png similarity index 100% rename from res/drawable-hdpi/ic_add_white_48dp.png rename to resources/drawable-hdpi/ic_add_white_48dp.png diff --git a/res/drawable-hdpi/ic_av_next.png b/resources/drawable-hdpi/ic_av_next.png similarity index 100% rename from res/drawable-hdpi/ic_av_next.png rename to resources/drawable-hdpi/ic_av_next.png diff --git a/res/drawable-hdpi/ic_av_pause.png b/resources/drawable-hdpi/ic_av_pause.png similarity index 100% rename from res/drawable-hdpi/ic_av_pause.png rename to resources/drawable-hdpi/ic_av_pause.png diff --git a/res/drawable-hdpi/ic_av_pause_dark.png b/resources/drawable-hdpi/ic_av_pause_dark.png similarity index 100% rename from res/drawable-hdpi/ic_av_pause_dark.png rename to resources/drawable-hdpi/ic_av_pause_dark.png diff --git a/res/drawable-hdpi/ic_av_pause_light.png b/resources/drawable-hdpi/ic_av_pause_light.png similarity index 100% rename from res/drawable-hdpi/ic_av_pause_light.png rename to resources/drawable-hdpi/ic_av_pause_light.png diff --git a/res/drawable-hdpi/ic_av_pause_over_video.png b/resources/drawable-hdpi/ic_av_pause_over_video.png similarity index 100% rename from res/drawable-hdpi/ic_av_pause_over_video.png rename to resources/drawable-hdpi/ic_av_pause_over_video.png diff --git a/res/drawable-hdpi/ic_av_pause_over_video_large.png b/resources/drawable-hdpi/ic_av_pause_over_video_large.png similarity index 100% rename from res/drawable-hdpi/ic_av_pause_over_video_large.png rename to resources/drawable-hdpi/ic_av_pause_over_video_large.png diff --git a/res/drawable-hdpi/ic_av_play.png b/resources/drawable-hdpi/ic_av_play.png similarity index 100% rename from res/drawable-hdpi/ic_av_play.png rename to resources/drawable-hdpi/ic_av_play.png diff --git a/res/drawable-hdpi/ic_av_play_dark.png b/resources/drawable-hdpi/ic_av_play_dark.png similarity index 100% rename from res/drawable-hdpi/ic_av_play_dark.png rename to resources/drawable-hdpi/ic_av_play_dark.png diff --git a/res/drawable-hdpi/ic_av_play_light.png b/resources/drawable-hdpi/ic_av_play_light.png similarity index 100% rename from res/drawable-hdpi/ic_av_play_light.png rename to resources/drawable-hdpi/ic_av_play_light.png diff --git a/res/drawable-hdpi/ic_av_play_over_video.png b/resources/drawable-hdpi/ic_av_play_over_video.png similarity index 100% rename from res/drawable-hdpi/ic_av_play_over_video.png rename to resources/drawable-hdpi/ic_av_play_over_video.png diff --git a/res/drawable-hdpi/ic_av_play_over_video_large.png b/resources/drawable-hdpi/ic_av_play_over_video_large.png similarity index 100% rename from res/drawable-hdpi/ic_av_play_over_video_large.png rename to resources/drawable-hdpi/ic_av_play_over_video_large.png diff --git a/res/drawable-hdpi/ic_av_previous.png b/resources/drawable-hdpi/ic_av_previous.png similarity index 100% rename from res/drawable-hdpi/ic_av_previous.png rename to resources/drawable-hdpi/ic_av_previous.png diff --git a/res/drawable-hdpi/ic_av_stop.png b/resources/drawable-hdpi/ic_av_stop.png similarity index 100% rename from res/drawable-hdpi/ic_av_stop.png rename to resources/drawable-hdpi/ic_av_stop.png diff --git a/res/drawable-hdpi/ic_closed_caption_blue.png b/resources/drawable-hdpi/ic_closed_caption_blue.png similarity index 100% rename from res/drawable-hdpi/ic_closed_caption_blue.png rename to resources/drawable-hdpi/ic_closed_caption_blue.png diff --git a/res/drawable-hdpi/ic_closed_caption_googblue_24dp.png b/resources/drawable-hdpi/ic_closed_caption_googblue_24dp.png similarity index 100% rename from res/drawable-hdpi/ic_closed_caption_googblue_24dp.png rename to resources/drawable-hdpi/ic_closed_caption_googblue_24dp.png diff --git a/res/drawable-hdpi/ic_closed_caption_googblue_36dp.png b/resources/drawable-hdpi/ic_closed_caption_googblue_36dp.png similarity index 100% rename from res/drawable-hdpi/ic_closed_caption_googblue_36dp.png rename to resources/drawable-hdpi/ic_closed_caption_googblue_36dp.png diff --git a/res/drawable-hdpi/ic_closed_caption_googblue_48dp.png b/resources/drawable-hdpi/ic_closed_caption_googblue_48dp.png similarity index 100% rename from res/drawable-hdpi/ic_closed_caption_googblue_48dp.png rename to resources/drawable-hdpi/ic_closed_caption_googblue_48dp.png diff --git a/res/drawable-hdpi/ic_closed_caption_grey.png b/resources/drawable-hdpi/ic_closed_caption_grey.png similarity index 100% rename from res/drawable-hdpi/ic_closed_caption_grey.png rename to resources/drawable-hdpi/ic_closed_caption_grey.png diff --git a/res/drawable-hdpi/ic_closed_caption_grey600_24dp.png b/resources/drawable-hdpi/ic_closed_caption_grey600_24dp.png similarity index 100% rename from res/drawable-hdpi/ic_closed_caption_grey600_24dp.png rename to resources/drawable-hdpi/ic_closed_caption_grey600_24dp.png diff --git a/res/drawable-hdpi/ic_closed_caption_grey600_36dp.png b/resources/drawable-hdpi/ic_closed_caption_grey600_36dp.png similarity index 100% rename from res/drawable-hdpi/ic_closed_caption_grey600_36dp.png rename to resources/drawable-hdpi/ic_closed_caption_grey600_36dp.png diff --git a/res/drawable-hdpi/ic_closed_caption_grey600_48dp.png b/resources/drawable-hdpi/ic_closed_caption_grey600_48dp.png similarity index 100% rename from res/drawable-hdpi/ic_closed_caption_grey600_48dp.png rename to resources/drawable-hdpi/ic_closed_caption_grey600_48dp.png diff --git a/res/drawable-hdpi/ic_closed_caption_white.png b/resources/drawable-hdpi/ic_closed_caption_white.png similarity index 100% rename from res/drawable-hdpi/ic_closed_caption_white.png rename to resources/drawable-hdpi/ic_closed_caption_white.png diff --git a/res/drawable-hdpi/ic_closed_caption_white_24dp.png b/resources/drawable-hdpi/ic_closed_caption_white_24dp.png similarity index 100% rename from res/drawable-hdpi/ic_closed_caption_white_24dp.png rename to resources/drawable-hdpi/ic_closed_caption_white_24dp.png diff --git a/res/drawable-hdpi/ic_closed_caption_white_36dp.png b/resources/drawable-hdpi/ic_closed_caption_white_36dp.png similarity index 100% rename from res/drawable-hdpi/ic_closed_caption_white_36dp.png rename to resources/drawable-hdpi/ic_closed_caption_white_36dp.png diff --git a/res/drawable-hdpi/ic_closed_caption_white_48dp.png b/resources/drawable-hdpi/ic_closed_caption_white_48dp.png similarity index 100% rename from res/drawable-hdpi/ic_closed_caption_white_48dp.png rename to resources/drawable-hdpi/ic_closed_caption_white_48dp.png diff --git a/res/drawable-hdpi/ic_device_access_volume_muted.png b/resources/drawable-hdpi/ic_device_access_volume_muted.png similarity index 100% rename from res/drawable-hdpi/ic_device_access_volume_muted.png rename to resources/drawable-hdpi/ic_device_access_volume_muted.png diff --git a/res/drawable-hdpi/ic_device_access_volume_on.png b/resources/drawable-hdpi/ic_device_access_volume_on.png similarity index 100% rename from res/drawable-hdpi/ic_device_access_volume_on.png rename to resources/drawable-hdpi/ic_device_access_volume_on.png diff --git a/res/drawable-hdpi/ic_drag_updown_grey_24dp.png b/resources/drawable-hdpi/ic_drag_updown_grey_24dp.png similarity index 100% rename from res/drawable-hdpi/ic_drag_updown_grey_24dp.png rename to resources/drawable-hdpi/ic_drag_updown_grey_24dp.png diff --git a/res/drawable-hdpi/ic_expand_list.png b/resources/drawable-hdpi/ic_expand_list.png similarity index 100% rename from res/drawable-hdpi/ic_expand_list.png rename to resources/drawable-hdpi/ic_expand_list.png diff --git a/res/drawable-hdpi/ic_launcher.png b/resources/drawable-hdpi/ic_launcher.png similarity index 100% rename from res/drawable-hdpi/ic_launcher.png rename to resources/drawable-hdpi/ic_launcher.png diff --git a/res/drawable-hdpi/ic_more_vert_grey600_24dp.png b/resources/drawable-hdpi/ic_more_vert_grey600_24dp.png similarity index 100% rename from res/drawable-hdpi/ic_more_vert_grey600_24dp.png rename to resources/drawable-hdpi/ic_more_vert_grey600_24dp.png diff --git a/res/drawable-hdpi/ic_music_albumart.png b/resources/drawable-hdpi/ic_music_albumart.png similarity index 100% rename from res/drawable-hdpi/ic_music_albumart.png rename to resources/drawable-hdpi/ic_music_albumart.png diff --git a/res/drawable-hdpi/ic_pause_circle_white_80dp.png b/resources/drawable-hdpi/ic_pause_circle_white_80dp.png similarity index 100% rename from res/drawable-hdpi/ic_pause_circle_white_80dp.png rename to resources/drawable-hdpi/ic_pause_circle_white_80dp.png diff --git a/res/drawable-hdpi/ic_pause_grey600_48dp.png b/resources/drawable-hdpi/ic_pause_grey600_48dp.png similarity index 100% rename from res/drawable-hdpi/ic_pause_grey600_48dp.png rename to resources/drawable-hdpi/ic_pause_grey600_48dp.png diff --git a/res/drawable-hdpi/ic_play_arrow_grey600_48dp.png b/resources/drawable-hdpi/ic_play_arrow_grey600_48dp.png similarity index 100% rename from res/drawable-hdpi/ic_play_arrow_grey600_48dp.png rename to resources/drawable-hdpi/ic_play_arrow_grey600_48dp.png diff --git a/res/drawable-hdpi/ic_play_arrow_white_24dp.png b/resources/drawable-hdpi/ic_play_arrow_white_24dp.png similarity index 100% rename from res/drawable-hdpi/ic_play_arrow_white_24dp.png rename to resources/drawable-hdpi/ic_play_arrow_white_24dp.png diff --git a/res/drawable-hdpi/ic_play_circle_outline_white.png b/resources/drawable-hdpi/ic_play_circle_outline_white.png similarity index 100% rename from res/drawable-hdpi/ic_play_circle_outline_white.png rename to resources/drawable-hdpi/ic_play_circle_outline_white.png diff --git a/res/drawable-hdpi/ic_play_circle_white_80dp.png b/resources/drawable-hdpi/ic_play_circle_white_80dp.png similarity index 100% rename from res/drawable-hdpi/ic_play_circle_white_80dp.png rename to resources/drawable-hdpi/ic_play_circle_white_80dp.png diff --git a/res/drawable-hdpi/ic_playlist_white_24dp.png b/resources/drawable-hdpi/ic_playlist_white_24dp.png similarity index 100% rename from res/drawable-hdpi/ic_playlist_white_24dp.png rename to resources/drawable-hdpi/ic_playlist_white_24dp.png diff --git a/res/drawable-hdpi/ic_remove_circle_white_24dp.png b/resources/drawable-hdpi/ic_remove_circle_white_24dp.png similarity index 100% rename from res/drawable-hdpi/ic_remove_circle_white_24dp.png rename to resources/drawable-hdpi/ic_remove_circle_white_24dp.png diff --git a/res/drawable-hdpi/ic_skip_next_grey600_48dp.png b/resources/drawable-hdpi/ic_skip_next_grey600_48dp.png similarity index 100% rename from res/drawable-hdpi/ic_skip_next_grey600_48dp.png rename to resources/drawable-hdpi/ic_skip_next_grey600_48dp.png diff --git a/res/drawable-hdpi/ic_skip_next_white_48dp.png b/resources/drawable-hdpi/ic_skip_next_white_48dp.png similarity index 100% rename from res/drawable-hdpi/ic_skip_next_white_48dp.png rename to resources/drawable-hdpi/ic_skip_next_white_48dp.png diff --git a/res/drawable-hdpi/ic_skip_previous_grey600_48dp.png b/resources/drawable-hdpi/ic_skip_previous_grey600_48dp.png similarity index 100% rename from res/drawable-hdpi/ic_skip_previous_grey600_48dp.png rename to resources/drawable-hdpi/ic_skip_previous_grey600_48dp.png diff --git a/res/drawable-hdpi/ic_skip_previous_white_48dp.png b/resources/drawable-hdpi/ic_skip_previous_white_48dp.png similarity index 100% rename from res/drawable-hdpi/ic_skip_previous_white_48dp.png rename to resources/drawable-hdpi/ic_skip_previous_white_48dp.png diff --git a/res/drawable-hdpi/ic_stat_action_notification.png b/resources/drawable-hdpi/ic_stat_action_notification.png similarity index 100% rename from res/drawable-hdpi/ic_stat_action_notification.png rename to resources/drawable-hdpi/ic_stat_action_notification.png diff --git a/res/drawable-hdpi/ic_stat_hardware_headphones.png b/resources/drawable-hdpi/ic_stat_hardware_headphones.png similarity index 100% rename from res/drawable-hdpi/ic_stat_hardware_headphones.png rename to resources/drawable-hdpi/ic_stat_hardware_headphones.png diff --git a/res/drawable-hdpi/ic_stop_white_24dp.png b/resources/drawable-hdpi/ic_stop_white_24dp.png similarity index 100% rename from res/drawable-hdpi/ic_stop_white_24dp.png rename to resources/drawable-hdpi/ic_stop_white_24dp.png diff --git a/res/drawable-hdpi/list_focused_castvideo.9.png b/resources/drawable-hdpi/list_focused_castvideo.9.png similarity index 100% rename from res/drawable-hdpi/list_focused_castvideo.9.png rename to resources/drawable-hdpi/list_focused_castvideo.9.png diff --git a/res/drawable-hdpi/list_focused_democast.9.png b/resources/drawable-hdpi/list_focused_democast.9.png similarity index 100% rename from res/drawable-hdpi/list_focused_democast.9.png rename to resources/drawable-hdpi/list_focused_democast.9.png diff --git a/res/drawable-hdpi/list_pressed_castvideo.9.png b/resources/drawable-hdpi/list_pressed_castvideo.9.png similarity index 100% rename from res/drawable-hdpi/list_pressed_castvideo.9.png rename to resources/drawable-hdpi/list_pressed_castvideo.9.png diff --git a/res/drawable-hdpi/menu_dropdown_panel_castvideo.9.png b/resources/drawable-hdpi/menu_dropdown_panel_castvideo.9.png similarity index 100% rename from res/drawable-hdpi/menu_dropdown_panel_castvideo.9.png rename to resources/drawable-hdpi/menu_dropdown_panel_castvideo.9.png diff --git a/res/drawable-hdpi/menu_dropdown_panel_democast.9.png b/resources/drawable-hdpi/menu_dropdown_panel_democast.9.png similarity index 100% rename from res/drawable-hdpi/menu_dropdown_panel_democast.9.png rename to resources/drawable-hdpi/menu_dropdown_panel_democast.9.png diff --git a/res/drawable-hdpi/minibg2.png b/resources/drawable-hdpi/minibg2.png similarity index 100% rename from res/drawable-hdpi/minibg2.png rename to resources/drawable-hdpi/minibg2.png diff --git a/res/drawable-hdpi/mr_ic_media_route_disabled_holo_dark.png b/resources/drawable-hdpi/mr_ic_media_route_disabled_holo_dark.png similarity index 100% rename from res/drawable-hdpi/mr_ic_media_route_disabled_holo_dark.png rename to resources/drawable-hdpi/mr_ic_media_route_disabled_holo_dark.png diff --git a/res/drawable-hdpi/mr_ic_media_route_off_holo_dark.png b/resources/drawable-hdpi/mr_ic_media_route_off_holo_dark.png similarity index 100% rename from res/drawable-hdpi/mr_ic_media_route_off_holo_dark.png rename to resources/drawable-hdpi/mr_ic_media_route_off_holo_dark.png diff --git a/res/drawable-hdpi/mr_ic_media_route_on_0_holo_dark.png b/resources/drawable-hdpi/mr_ic_media_route_on_0_holo_dark.png similarity index 100% rename from res/drawable-hdpi/mr_ic_media_route_on_0_holo_dark.png rename to resources/drawable-hdpi/mr_ic_media_route_on_0_holo_dark.png diff --git a/res/drawable-hdpi/mr_ic_media_route_on_1_holo_dark.png b/resources/drawable-hdpi/mr_ic_media_route_on_1_holo_dark.png similarity index 100% rename from res/drawable-hdpi/mr_ic_media_route_on_1_holo_dark.png rename to resources/drawable-hdpi/mr_ic_media_route_on_1_holo_dark.png diff --git a/res/drawable-hdpi/mr_ic_media_route_on_2_holo_dark.png b/resources/drawable-hdpi/mr_ic_media_route_on_2_holo_dark.png similarity index 100% rename from res/drawable-hdpi/mr_ic_media_route_on_2_holo_dark.png rename to resources/drawable-hdpi/mr_ic_media_route_on_2_holo_dark.png diff --git a/res/drawable-hdpi/mr_ic_media_route_on_holo_dark.png b/resources/drawable-hdpi/mr_ic_media_route_on_holo_dark.png similarity index 100% rename from res/drawable-hdpi/mr_ic_media_route_on_holo_dark.png rename to resources/drawable-hdpi/mr_ic_media_route_on_holo_dark.png diff --git a/res/drawable-hdpi/progress_bg_castvideo.9.png b/resources/drawable-hdpi/progress_bg_castvideo.9.png similarity index 100% rename from res/drawable-hdpi/progress_bg_castvideo.9.png rename to resources/drawable-hdpi/progress_bg_castvideo.9.png diff --git a/res/drawable-hdpi/progress_bg_democast.9.png b/resources/drawable-hdpi/progress_bg_democast.9.png similarity index 100% rename from res/drawable-hdpi/progress_bg_democast.9.png rename to resources/drawable-hdpi/progress_bg_democast.9.png diff --git a/res/drawable-hdpi/progress_primary_castvideo.9.png b/resources/drawable-hdpi/progress_primary_castvideo.9.png similarity index 100% rename from res/drawable-hdpi/progress_primary_castvideo.9.png rename to resources/drawable-hdpi/progress_primary_castvideo.9.png diff --git a/res/drawable-hdpi/progress_primary_democast.9.png b/resources/drawable-hdpi/progress_primary_democast.9.png similarity index 100% rename from res/drawable-hdpi/progress_primary_democast.9.png rename to resources/drawable-hdpi/progress_primary_democast.9.png diff --git a/res/drawable-hdpi/progress_secondary_castvideo.9.png b/resources/drawable-hdpi/progress_secondary_castvideo.9.png similarity index 100% rename from res/drawable-hdpi/progress_secondary_castvideo.9.png rename to resources/drawable-hdpi/progress_secondary_castvideo.9.png diff --git a/res/drawable-hdpi/progress_secondary_democast.9.png b/resources/drawable-hdpi/progress_secondary_democast.9.png similarity index 100% rename from res/drawable-hdpi/progress_secondary_democast.9.png rename to resources/drawable-hdpi/progress_secondary_democast.9.png diff --git a/res/drawable-hdpi/shadow6.9.png b/resources/drawable-hdpi/shadow6.9.png similarity index 100% rename from res/drawable-hdpi/shadow6.9.png rename to resources/drawable-hdpi/shadow6.9.png diff --git a/res/drawable-hdpi/spinner_ab_default_castvideo.9.png b/resources/drawable-hdpi/spinner_ab_default_castvideo.9.png similarity index 100% rename from res/drawable-hdpi/spinner_ab_default_castvideo.9.png rename to resources/drawable-hdpi/spinner_ab_default_castvideo.9.png diff --git a/res/drawable-hdpi/spinner_ab_default_democast.9.png b/resources/drawable-hdpi/spinner_ab_default_democast.9.png similarity index 100% rename from res/drawable-hdpi/spinner_ab_default_democast.9.png rename to resources/drawable-hdpi/spinner_ab_default_democast.9.png diff --git a/res/drawable-hdpi/spinner_ab_disabled_castvideo.9.png b/resources/drawable-hdpi/spinner_ab_disabled_castvideo.9.png similarity index 100% rename from res/drawable-hdpi/spinner_ab_disabled_castvideo.9.png rename to resources/drawable-hdpi/spinner_ab_disabled_castvideo.9.png diff --git a/res/drawable-hdpi/spinner_ab_disabled_democast.9.png b/resources/drawable-hdpi/spinner_ab_disabled_democast.9.png similarity index 100% rename from res/drawable-hdpi/spinner_ab_disabled_democast.9.png rename to resources/drawable-hdpi/spinner_ab_disabled_democast.9.png diff --git a/res/drawable-hdpi/spinner_ab_focused_castvideo.9.png b/resources/drawable-hdpi/spinner_ab_focused_castvideo.9.png similarity index 100% rename from res/drawable-hdpi/spinner_ab_focused_castvideo.9.png rename to resources/drawable-hdpi/spinner_ab_focused_castvideo.9.png diff --git a/res/drawable-hdpi/spinner_ab_focused_democast.9.png b/resources/drawable-hdpi/spinner_ab_focused_democast.9.png similarity index 100% rename from res/drawable-hdpi/spinner_ab_focused_democast.9.png rename to resources/drawable-hdpi/spinner_ab_focused_democast.9.png diff --git a/res/drawable-hdpi/spinner_ab_pressed_castvideo.9.png b/resources/drawable-hdpi/spinner_ab_pressed_castvideo.9.png similarity index 100% rename from res/drawable-hdpi/spinner_ab_pressed_castvideo.9.png rename to resources/drawable-hdpi/spinner_ab_pressed_castvideo.9.png diff --git a/res/drawable-hdpi/spinner_ab_pressed_democast.9.png b/resources/drawable-hdpi/spinner_ab_pressed_democast.9.png similarity index 100% rename from res/drawable-hdpi/spinner_ab_pressed_democast.9.png rename to resources/drawable-hdpi/spinner_ab_pressed_democast.9.png diff --git a/res/drawable-hdpi/tab_selected_castvideo.9.png b/resources/drawable-hdpi/tab_selected_castvideo.9.png similarity index 100% rename from res/drawable-hdpi/tab_selected_castvideo.9.png rename to resources/drawable-hdpi/tab_selected_castvideo.9.png diff --git a/res/drawable-hdpi/tab_selected_democast.9.png b/resources/drawable-hdpi/tab_selected_democast.9.png similarity index 100% rename from res/drawable-hdpi/tab_selected_democast.9.png rename to resources/drawable-hdpi/tab_selected_democast.9.png diff --git a/res/drawable-hdpi/tab_selected_focused_castvideo.9.png b/resources/drawable-hdpi/tab_selected_focused_castvideo.9.png similarity index 100% rename from res/drawable-hdpi/tab_selected_focused_castvideo.9.png rename to resources/drawable-hdpi/tab_selected_focused_castvideo.9.png diff --git a/res/drawable-hdpi/tab_selected_focused_democast.9.png b/resources/drawable-hdpi/tab_selected_focused_democast.9.png similarity index 100% rename from res/drawable-hdpi/tab_selected_focused_democast.9.png rename to resources/drawable-hdpi/tab_selected_focused_democast.9.png diff --git a/res/drawable-hdpi/tab_selected_pressed_castvideo.9.png b/resources/drawable-hdpi/tab_selected_pressed_castvideo.9.png similarity index 100% rename from res/drawable-hdpi/tab_selected_pressed_castvideo.9.png rename to resources/drawable-hdpi/tab_selected_pressed_castvideo.9.png diff --git a/res/drawable-hdpi/tab_selected_pressed_democast.9.png b/resources/drawable-hdpi/tab_selected_pressed_democast.9.png similarity index 100% rename from res/drawable-hdpi/tab_selected_pressed_democast.9.png rename to resources/drawable-hdpi/tab_selected_pressed_democast.9.png diff --git a/res/drawable-hdpi/tab_unselected_castvideo.9.png b/resources/drawable-hdpi/tab_unselected_castvideo.9.png similarity index 100% rename from res/drawable-hdpi/tab_unselected_castvideo.9.png rename to resources/drawable-hdpi/tab_unselected_castvideo.9.png diff --git a/res/drawable-hdpi/tab_unselected_democast.9.png b/resources/drawable-hdpi/tab_unselected_democast.9.png similarity index 100% rename from res/drawable-hdpi/tab_unselected_democast.9.png rename to resources/drawable-hdpi/tab_unselected_democast.9.png diff --git a/res/drawable-hdpi/tab_unselected_focused_castvideo.9.png b/resources/drawable-hdpi/tab_unselected_focused_castvideo.9.png similarity index 100% rename from res/drawable-hdpi/tab_unselected_focused_castvideo.9.png rename to resources/drawable-hdpi/tab_unselected_focused_castvideo.9.png diff --git a/res/drawable-hdpi/tab_unselected_focused_democast.9.png b/resources/drawable-hdpi/tab_unselected_focused_democast.9.png similarity index 100% rename from res/drawable-hdpi/tab_unselected_focused_democast.9.png rename to resources/drawable-hdpi/tab_unselected_focused_democast.9.png diff --git a/res/drawable-hdpi/tab_unselected_pressed_castvideo.9.png b/resources/drawable-hdpi/tab_unselected_pressed_castvideo.9.png similarity index 100% rename from res/drawable-hdpi/tab_unselected_pressed_castvideo.9.png rename to resources/drawable-hdpi/tab_unselected_pressed_castvideo.9.png diff --git a/res/drawable-hdpi/tab_unselected_pressed_democast.9.png b/resources/drawable-hdpi/tab_unselected_pressed_democast.9.png similarity index 100% rename from res/drawable-hdpi/tab_unselected_pressed_democast.9.png rename to resources/drawable-hdpi/tab_unselected_pressed_democast.9.png diff --git a/res/drawable-mdpi-v11/ic_stat_action_notification.png b/resources/drawable-mdpi-v11/ic_stat_action_notification.png similarity index 100% rename from res/drawable-mdpi-v11/ic_stat_action_notification.png rename to resources/drawable-mdpi-v11/ic_stat_action_notification.png diff --git a/res/drawable-mdpi-v11/ic_stat_action_notifications.png b/resources/drawable-mdpi-v11/ic_stat_action_notifications.png similarity index 100% rename from res/drawable-mdpi-v11/ic_stat_action_notifications.png rename to resources/drawable-mdpi-v11/ic_stat_action_notifications.png diff --git a/res/drawable-mdpi-v11/ic_stat_hardware_headphones.png b/resources/drawable-mdpi-v11/ic_stat_hardware_headphones.png similarity index 100% rename from res/drawable-mdpi-v11/ic_stat_hardware_headphones.png rename to resources/drawable-mdpi-v11/ic_stat_hardware_headphones.png diff --git a/res/drawable-mdpi/ab_bottom_solid_castvideo.9.png b/resources/drawable-mdpi/ab_bottom_solid_castvideo.9.png similarity index 100% rename from res/drawable-mdpi/ab_bottom_solid_castvideo.9.png rename to resources/drawable-mdpi/ab_bottom_solid_castvideo.9.png diff --git a/res/drawable-mdpi/ab_bottom_solid_democast.9.png b/resources/drawable-mdpi/ab_bottom_solid_democast.9.png similarity index 100% rename from res/drawable-mdpi/ab_bottom_solid_democast.9.png rename to resources/drawable-mdpi/ab_bottom_solid_democast.9.png diff --git a/res/drawable-mdpi/ab_solid_castvideo.9.png b/resources/drawable-mdpi/ab_solid_castvideo.9.png similarity index 100% rename from res/drawable-mdpi/ab_solid_castvideo.9.png rename to resources/drawable-mdpi/ab_solid_castvideo.9.png diff --git a/res/drawable-mdpi/ab_solid_democast.9.png b/resources/drawable-mdpi/ab_solid_democast.9.png similarity index 100% rename from res/drawable-mdpi/ab_solid_democast.9.png rename to resources/drawable-mdpi/ab_solid_democast.9.png diff --git a/res/drawable-mdpi/ab_stacked_solid_castvideo.9.png b/resources/drawable-mdpi/ab_stacked_solid_castvideo.9.png similarity index 100% rename from res/drawable-mdpi/ab_stacked_solid_castvideo.9.png rename to resources/drawable-mdpi/ab_stacked_solid_castvideo.9.png diff --git a/res/drawable-mdpi/ab_stacked_solid_democast.9.png b/resources/drawable-mdpi/ab_stacked_solid_democast.9.png similarity index 100% rename from res/drawable-mdpi/ab_stacked_solid_democast.9.png rename to resources/drawable-mdpi/ab_stacked_solid_democast.9.png diff --git a/res/drawable-mdpi/ab_texture_tile_castvideo.png b/resources/drawable-mdpi/ab_texture_tile_castvideo.png similarity index 100% rename from res/drawable-mdpi/ab_texture_tile_castvideo.png rename to resources/drawable-mdpi/ab_texture_tile_castvideo.png diff --git a/res/drawable-mdpi/ab_texture_tile_democast.png b/resources/drawable-mdpi/ab_texture_tile_democast.png similarity index 100% rename from res/drawable-mdpi/ab_texture_tile_democast.png rename to resources/drawable-mdpi/ab_texture_tile_democast.png diff --git a/res/drawable-mdpi/ab_transparent_castvideo.9.png b/resources/drawable-mdpi/ab_transparent_castvideo.9.png similarity index 100% rename from res/drawable-mdpi/ab_transparent_castvideo.9.png rename to resources/drawable-mdpi/ab_transparent_castvideo.9.png diff --git a/res/drawable-mdpi/ab_transparent_democast.9.png b/resources/drawable-mdpi/ab_transparent_democast.9.png similarity index 100% rename from res/drawable-mdpi/ab_transparent_democast.9.png rename to resources/drawable-mdpi/ab_transparent_democast.9.png diff --git a/res/drawable-mdpi/ab_transparent_democastoverlay.9.png b/resources/drawable-mdpi/ab_transparent_democastoverlay.9.png similarity index 100% rename from res/drawable-mdpi/ab_transparent_democastoverlay.9.png rename to resources/drawable-mdpi/ab_transparent_democastoverlay.9.png diff --git a/res/drawable-mdpi/actionbar_logo.png b/resources/drawable-mdpi/actionbar_logo.png similarity index 100% rename from res/drawable-mdpi/actionbar_logo.png rename to resources/drawable-mdpi/actionbar_logo.png diff --git a/res/drawable-mdpi/actionbar_logo_castvideos.png b/resources/drawable-mdpi/actionbar_logo_castvideos.png similarity index 100% rename from res/drawable-mdpi/actionbar_logo_castvideos.png rename to resources/drawable-mdpi/actionbar_logo_castvideos.png diff --git a/res/drawable-mdpi/apptheme_scrubber_control_disabled_holo.png b/resources/drawable-mdpi/apptheme_scrubber_control_disabled_holo.png similarity index 100% rename from res/drawable-mdpi/apptheme_scrubber_control_disabled_holo.png rename to resources/drawable-mdpi/apptheme_scrubber_control_disabled_holo.png diff --git a/res/drawable-mdpi/apptheme_scrubber_control_focused_holo.png b/resources/drawable-mdpi/apptheme_scrubber_control_focused_holo.png similarity index 100% rename from res/drawable-mdpi/apptheme_scrubber_control_focused_holo.png rename to resources/drawable-mdpi/apptheme_scrubber_control_focused_holo.png diff --git a/res/drawable-mdpi/apptheme_scrubber_control_normal_holo.png b/resources/drawable-mdpi/apptheme_scrubber_control_normal_holo.png similarity index 100% rename from res/drawable-mdpi/apptheme_scrubber_control_normal_holo.png rename to resources/drawable-mdpi/apptheme_scrubber_control_normal_holo.png diff --git a/res/drawable-mdpi/apptheme_scrubber_control_pressed_holo.png b/resources/drawable-mdpi/apptheme_scrubber_control_pressed_holo.png similarity index 100% rename from res/drawable-mdpi/apptheme_scrubber_control_pressed_holo.png rename to resources/drawable-mdpi/apptheme_scrubber_control_pressed_holo.png diff --git a/res/drawable-mdpi/apptheme_scrubber_primary_holo.9.png b/resources/drawable-mdpi/apptheme_scrubber_primary_holo.9.png similarity index 100% rename from res/drawable-mdpi/apptheme_scrubber_primary_holo.9.png rename to resources/drawable-mdpi/apptheme_scrubber_primary_holo.9.png diff --git a/res/drawable-mdpi/apptheme_scrubber_secondary_holo.9.png b/resources/drawable-mdpi/apptheme_scrubber_secondary_holo.9.png similarity index 100% rename from res/drawable-mdpi/apptheme_scrubber_secondary_holo.9.png rename to resources/drawable-mdpi/apptheme_scrubber_secondary_holo.9.png diff --git a/res/drawable-mdpi/apptheme_scrubber_track_holo_light.9.png b/resources/drawable-mdpi/apptheme_scrubber_track_holo_light.9.png similarity index 100% rename from res/drawable-mdpi/apptheme_scrubber_track_holo_light.9.png rename to resources/drawable-mdpi/apptheme_scrubber_track_holo_light.9.png diff --git a/res/drawable-mdpi/arrow_down_white.png b/resources/drawable-mdpi/arrow_down_white.png similarity index 100% rename from res/drawable-mdpi/arrow_down_white.png rename to resources/drawable-mdpi/arrow_down_white.png diff --git a/res/drawable-mdpi/btn_cab_done_default_castvideo.9.png b/resources/drawable-mdpi/btn_cab_done_default_castvideo.9.png similarity index 100% rename from res/drawable-mdpi/btn_cab_done_default_castvideo.9.png rename to resources/drawable-mdpi/btn_cab_done_default_castvideo.9.png diff --git a/res/drawable-mdpi/btn_cab_done_default_democast.9.png b/resources/drawable-mdpi/btn_cab_done_default_democast.9.png similarity index 100% rename from res/drawable-mdpi/btn_cab_done_default_democast.9.png rename to resources/drawable-mdpi/btn_cab_done_default_democast.9.png diff --git a/res/drawable-mdpi/btn_cab_done_focused_castvideo.9.png b/resources/drawable-mdpi/btn_cab_done_focused_castvideo.9.png similarity index 100% rename from res/drawable-mdpi/btn_cab_done_focused_castvideo.9.png rename to resources/drawable-mdpi/btn_cab_done_focused_castvideo.9.png diff --git a/res/drawable-mdpi/btn_cab_done_focused_democast.9.png b/resources/drawable-mdpi/btn_cab_done_focused_democast.9.png similarity index 100% rename from res/drawable-mdpi/btn_cab_done_focused_democast.9.png rename to resources/drawable-mdpi/btn_cab_done_focused_democast.9.png diff --git a/res/drawable-mdpi/btn_cab_done_pressed_castvideo.9.png b/resources/drawable-mdpi/btn_cab_done_pressed_castvideo.9.png similarity index 100% rename from res/drawable-mdpi/btn_cab_done_pressed_castvideo.9.png rename to resources/drawable-mdpi/btn_cab_done_pressed_castvideo.9.png diff --git a/res/drawable-mdpi/btn_cab_done_pressed_democast.9.png b/resources/drawable-mdpi/btn_cab_done_pressed_democast.9.png similarity index 100% rename from res/drawable-mdpi/btn_cab_done_pressed_democast.9.png rename to resources/drawable-mdpi/btn_cab_done_pressed_democast.9.png diff --git a/res/drawable-mdpi/cab_background_bottom_castvideo.9.png b/resources/drawable-mdpi/cab_background_bottom_castvideo.9.png similarity index 100% rename from res/drawable-mdpi/cab_background_bottom_castvideo.9.png rename to resources/drawable-mdpi/cab_background_bottom_castvideo.9.png diff --git a/res/drawable-mdpi/cab_background_bottom_democast.9.png b/resources/drawable-mdpi/cab_background_bottom_democast.9.png similarity index 100% rename from res/drawable-mdpi/cab_background_bottom_democast.9.png rename to resources/drawable-mdpi/cab_background_bottom_democast.9.png diff --git a/res/drawable-mdpi/cab_background_top_castvideo.9.png b/resources/drawable-mdpi/cab_background_top_castvideo.9.png similarity index 100% rename from res/drawable-mdpi/cab_background_top_castvideo.9.png rename to resources/drawable-mdpi/cab_background_top_castvideo.9.png diff --git a/res/drawable-mdpi/cab_background_top_democast.9.png b/resources/drawable-mdpi/cab_background_top_democast.9.png similarity index 100% rename from res/drawable-mdpi/cab_background_top_democast.9.png rename to resources/drawable-mdpi/cab_background_top_democast.9.png diff --git a/res/drawable-mdpi/ic_action_alerts_and_states_warning.png b/resources/drawable-mdpi/ic_action_alerts_and_states_warning.png similarity index 100% rename from res/drawable-mdpi/ic_action_alerts_and_states_warning.png rename to resources/drawable-mdpi/ic_action_alerts_and_states_warning.png diff --git a/res/drawable-mdpi/ic_av_next.png b/resources/drawable-mdpi/ic_av_next.png similarity index 100% rename from res/drawable-mdpi/ic_av_next.png rename to resources/drawable-mdpi/ic_av_next.png diff --git a/res/drawable-mdpi/ic_av_pause.png b/resources/drawable-mdpi/ic_av_pause.png similarity index 100% rename from res/drawable-mdpi/ic_av_pause.png rename to resources/drawable-mdpi/ic_av_pause.png diff --git a/res/drawable-mdpi/ic_av_pause_dark.png b/resources/drawable-mdpi/ic_av_pause_dark.png similarity index 100% rename from res/drawable-mdpi/ic_av_pause_dark.png rename to resources/drawable-mdpi/ic_av_pause_dark.png diff --git a/res/drawable-mdpi/ic_av_pause_light.png b/resources/drawable-mdpi/ic_av_pause_light.png similarity index 100% rename from res/drawable-mdpi/ic_av_pause_light.png rename to resources/drawable-mdpi/ic_av_pause_light.png diff --git a/res/drawable-mdpi/ic_av_pause_over_video.png b/resources/drawable-mdpi/ic_av_pause_over_video.png similarity index 100% rename from res/drawable-mdpi/ic_av_pause_over_video.png rename to resources/drawable-mdpi/ic_av_pause_over_video.png diff --git a/res/drawable-mdpi/ic_av_pause_over_video_large.png b/resources/drawable-mdpi/ic_av_pause_over_video_large.png similarity index 100% rename from res/drawable-mdpi/ic_av_pause_over_video_large.png rename to resources/drawable-mdpi/ic_av_pause_over_video_large.png diff --git a/res/drawable-mdpi/ic_av_play.png b/resources/drawable-mdpi/ic_av_play.png similarity index 100% rename from res/drawable-mdpi/ic_av_play.png rename to resources/drawable-mdpi/ic_av_play.png diff --git a/res/drawable-mdpi/ic_av_play_dark.png b/resources/drawable-mdpi/ic_av_play_dark.png similarity index 100% rename from res/drawable-mdpi/ic_av_play_dark.png rename to resources/drawable-mdpi/ic_av_play_dark.png diff --git a/res/drawable-mdpi/ic_av_play_light.png b/resources/drawable-mdpi/ic_av_play_light.png similarity index 100% rename from res/drawable-mdpi/ic_av_play_light.png rename to resources/drawable-mdpi/ic_av_play_light.png diff --git a/res/drawable-mdpi/ic_av_play_over_video.png b/resources/drawable-mdpi/ic_av_play_over_video.png similarity index 100% rename from res/drawable-mdpi/ic_av_play_over_video.png rename to resources/drawable-mdpi/ic_av_play_over_video.png diff --git a/res/drawable-mdpi/ic_av_play_over_video_large.png b/resources/drawable-mdpi/ic_av_play_over_video_large.png similarity index 100% rename from res/drawable-mdpi/ic_av_play_over_video_large.png rename to resources/drawable-mdpi/ic_av_play_over_video_large.png diff --git a/res/drawable-mdpi/ic_av_previous.png b/resources/drawable-mdpi/ic_av_previous.png similarity index 100% rename from res/drawable-mdpi/ic_av_previous.png rename to resources/drawable-mdpi/ic_av_previous.png diff --git a/res/drawable-mdpi/ic_av_stop.png b/resources/drawable-mdpi/ic_av_stop.png similarity index 100% rename from res/drawable-mdpi/ic_av_stop.png rename to resources/drawable-mdpi/ic_av_stop.png diff --git a/res/drawable-mdpi/ic_device_access_volume_muted.png b/resources/drawable-mdpi/ic_device_access_volume_muted.png similarity index 100% rename from res/drawable-mdpi/ic_device_access_volume_muted.png rename to resources/drawable-mdpi/ic_device_access_volume_muted.png diff --git a/res/drawable-mdpi/ic_device_access_volume_on.png b/resources/drawable-mdpi/ic_device_access_volume_on.png similarity index 100% rename from res/drawable-mdpi/ic_device_access_volume_on.png rename to resources/drawable-mdpi/ic_device_access_volume_on.png diff --git a/res/drawable-mdpi/ic_launcher.png b/resources/drawable-mdpi/ic_launcher.png similarity index 100% rename from res/drawable-mdpi/ic_launcher.png rename to resources/drawable-mdpi/ic_launcher.png diff --git a/res/drawable-mdpi/ic_music_albumart.png b/resources/drawable-mdpi/ic_music_albumart.png similarity index 100% rename from res/drawable-mdpi/ic_music_albumart.png rename to resources/drawable-mdpi/ic_music_albumart.png diff --git a/res/drawable-mdpi/ic_stat_action_notification.png b/resources/drawable-mdpi/ic_stat_action_notification.png similarity index 100% rename from res/drawable-mdpi/ic_stat_action_notification.png rename to resources/drawable-mdpi/ic_stat_action_notification.png diff --git a/res/drawable-mdpi/ic_stat_hardware_headphones.png b/resources/drawable-mdpi/ic_stat_hardware_headphones.png similarity index 100% rename from res/drawable-mdpi/ic_stat_hardware_headphones.png rename to resources/drawable-mdpi/ic_stat_hardware_headphones.png diff --git a/res/drawable-mdpi/list_focused_castvideo.9.png b/resources/drawable-mdpi/list_focused_castvideo.9.png similarity index 100% rename from res/drawable-mdpi/list_focused_castvideo.9.png rename to resources/drawable-mdpi/list_focused_castvideo.9.png diff --git a/res/drawable-mdpi/list_focused_democast.9.png b/resources/drawable-mdpi/list_focused_democast.9.png similarity index 100% rename from res/drawable-mdpi/list_focused_democast.9.png rename to resources/drawable-mdpi/list_focused_democast.9.png diff --git a/res/drawable-mdpi/list_pressed_castvideo.9.png b/resources/drawable-mdpi/list_pressed_castvideo.9.png similarity index 100% rename from res/drawable-mdpi/list_pressed_castvideo.9.png rename to resources/drawable-mdpi/list_pressed_castvideo.9.png diff --git a/res/drawable-mdpi/menu_dropdown_panel_castvideo.9.png b/resources/drawable-mdpi/menu_dropdown_panel_castvideo.9.png similarity index 100% rename from res/drawable-mdpi/menu_dropdown_panel_castvideo.9.png rename to resources/drawable-mdpi/menu_dropdown_panel_castvideo.9.png diff --git a/res/drawable-mdpi/menu_dropdown_panel_democast.9.png b/resources/drawable-mdpi/menu_dropdown_panel_democast.9.png similarity index 100% rename from res/drawable-mdpi/menu_dropdown_panel_democast.9.png rename to resources/drawable-mdpi/menu_dropdown_panel_democast.9.png diff --git a/res/drawable-mdpi/minibg2.png b/resources/drawable-mdpi/minibg2.png similarity index 100% rename from res/drawable-mdpi/minibg2.png rename to resources/drawable-mdpi/minibg2.png diff --git a/res/drawable-mdpi/mr_ic_media_route_disabled_holo_dark.png b/resources/drawable-mdpi/mr_ic_media_route_disabled_holo_dark.png similarity index 100% rename from res/drawable-mdpi/mr_ic_media_route_disabled_holo_dark.png rename to resources/drawable-mdpi/mr_ic_media_route_disabled_holo_dark.png diff --git a/res/drawable-mdpi/mr_ic_media_route_off_holo_dark.png b/resources/drawable-mdpi/mr_ic_media_route_off_holo_dark.png similarity index 100% rename from res/drawable-mdpi/mr_ic_media_route_off_holo_dark.png rename to resources/drawable-mdpi/mr_ic_media_route_off_holo_dark.png diff --git a/res/drawable-mdpi/mr_ic_media_route_on_0_holo_dark.png b/resources/drawable-mdpi/mr_ic_media_route_on_0_holo_dark.png similarity index 100% rename from res/drawable-mdpi/mr_ic_media_route_on_0_holo_dark.png rename to resources/drawable-mdpi/mr_ic_media_route_on_0_holo_dark.png diff --git a/res/drawable-mdpi/mr_ic_media_route_on_1_holo_dark.png b/resources/drawable-mdpi/mr_ic_media_route_on_1_holo_dark.png similarity index 100% rename from res/drawable-mdpi/mr_ic_media_route_on_1_holo_dark.png rename to resources/drawable-mdpi/mr_ic_media_route_on_1_holo_dark.png diff --git a/res/drawable-mdpi/mr_ic_media_route_on_2_holo_dark.png b/resources/drawable-mdpi/mr_ic_media_route_on_2_holo_dark.png similarity index 100% rename from res/drawable-mdpi/mr_ic_media_route_on_2_holo_dark.png rename to resources/drawable-mdpi/mr_ic_media_route_on_2_holo_dark.png diff --git a/res/drawable-mdpi/mr_ic_media_route_on_holo_dark.png b/resources/drawable-mdpi/mr_ic_media_route_on_holo_dark.png similarity index 100% rename from res/drawable-mdpi/mr_ic_media_route_on_holo_dark.png rename to resources/drawable-mdpi/mr_ic_media_route_on_holo_dark.png diff --git a/res/drawable-mdpi/progress_bg_castvideo.9.png b/resources/drawable-mdpi/progress_bg_castvideo.9.png similarity index 100% rename from res/drawable-mdpi/progress_bg_castvideo.9.png rename to resources/drawable-mdpi/progress_bg_castvideo.9.png diff --git a/res/drawable-mdpi/progress_bg_democast.9.png b/resources/drawable-mdpi/progress_bg_democast.9.png similarity index 100% rename from res/drawable-mdpi/progress_bg_democast.9.png rename to resources/drawable-mdpi/progress_bg_democast.9.png diff --git a/res/drawable-mdpi/progress_primary_castvideo.9.png b/resources/drawable-mdpi/progress_primary_castvideo.9.png similarity index 100% rename from res/drawable-mdpi/progress_primary_castvideo.9.png rename to resources/drawable-mdpi/progress_primary_castvideo.9.png diff --git a/res/drawable-mdpi/progress_primary_democast.9.png b/resources/drawable-mdpi/progress_primary_democast.9.png similarity index 100% rename from res/drawable-mdpi/progress_primary_democast.9.png rename to resources/drawable-mdpi/progress_primary_democast.9.png diff --git a/res/drawable-mdpi/progress_secondary_castvideo.9.png b/resources/drawable-mdpi/progress_secondary_castvideo.9.png similarity index 100% rename from res/drawable-mdpi/progress_secondary_castvideo.9.png rename to resources/drawable-mdpi/progress_secondary_castvideo.9.png diff --git a/res/drawable-mdpi/progress_secondary_democast.9.png b/resources/drawable-mdpi/progress_secondary_democast.9.png similarity index 100% rename from res/drawable-mdpi/progress_secondary_democast.9.png rename to resources/drawable-mdpi/progress_secondary_democast.9.png diff --git a/res/drawable-mdpi/shadow6.9.png b/resources/drawable-mdpi/shadow6.9.png similarity index 100% rename from res/drawable-mdpi/shadow6.9.png rename to resources/drawable-mdpi/shadow6.9.png diff --git a/res/drawable-mdpi/spinner_ab_default_castvideo.9.png b/resources/drawable-mdpi/spinner_ab_default_castvideo.9.png similarity index 100% rename from res/drawable-mdpi/spinner_ab_default_castvideo.9.png rename to resources/drawable-mdpi/spinner_ab_default_castvideo.9.png diff --git a/res/drawable-mdpi/spinner_ab_default_democast.9.png b/resources/drawable-mdpi/spinner_ab_default_democast.9.png similarity index 100% rename from res/drawable-mdpi/spinner_ab_default_democast.9.png rename to resources/drawable-mdpi/spinner_ab_default_democast.9.png diff --git a/res/drawable-mdpi/spinner_ab_disabled_castvideo.9.png b/resources/drawable-mdpi/spinner_ab_disabled_castvideo.9.png similarity index 100% rename from res/drawable-mdpi/spinner_ab_disabled_castvideo.9.png rename to resources/drawable-mdpi/spinner_ab_disabled_castvideo.9.png diff --git a/res/drawable-mdpi/spinner_ab_disabled_democast.9.png b/resources/drawable-mdpi/spinner_ab_disabled_democast.9.png similarity index 100% rename from res/drawable-mdpi/spinner_ab_disabled_democast.9.png rename to resources/drawable-mdpi/spinner_ab_disabled_democast.9.png diff --git a/res/drawable-mdpi/spinner_ab_focused_castvideo.9.png b/resources/drawable-mdpi/spinner_ab_focused_castvideo.9.png similarity index 100% rename from res/drawable-mdpi/spinner_ab_focused_castvideo.9.png rename to resources/drawable-mdpi/spinner_ab_focused_castvideo.9.png diff --git a/res/drawable-mdpi/spinner_ab_focused_democast.9.png b/resources/drawable-mdpi/spinner_ab_focused_democast.9.png similarity index 100% rename from res/drawable-mdpi/spinner_ab_focused_democast.9.png rename to resources/drawable-mdpi/spinner_ab_focused_democast.9.png diff --git a/res/drawable-mdpi/spinner_ab_pressed_castvideo.9.png b/resources/drawable-mdpi/spinner_ab_pressed_castvideo.9.png similarity index 100% rename from res/drawable-mdpi/spinner_ab_pressed_castvideo.9.png rename to resources/drawable-mdpi/spinner_ab_pressed_castvideo.9.png diff --git a/res/drawable-mdpi/spinner_ab_pressed_democast.9.png b/resources/drawable-mdpi/spinner_ab_pressed_democast.9.png similarity index 100% rename from res/drawable-mdpi/spinner_ab_pressed_democast.9.png rename to resources/drawable-mdpi/spinner_ab_pressed_democast.9.png diff --git a/res/drawable-mdpi/tab_selected_castvideo.9.png b/resources/drawable-mdpi/tab_selected_castvideo.9.png similarity index 100% rename from res/drawable-mdpi/tab_selected_castvideo.9.png rename to resources/drawable-mdpi/tab_selected_castvideo.9.png diff --git a/res/drawable-mdpi/tab_selected_democast.9.png b/resources/drawable-mdpi/tab_selected_democast.9.png similarity index 100% rename from res/drawable-mdpi/tab_selected_democast.9.png rename to resources/drawable-mdpi/tab_selected_democast.9.png diff --git a/res/drawable-mdpi/tab_selected_focused_castvideo.9.png b/resources/drawable-mdpi/tab_selected_focused_castvideo.9.png similarity index 100% rename from res/drawable-mdpi/tab_selected_focused_castvideo.9.png rename to resources/drawable-mdpi/tab_selected_focused_castvideo.9.png diff --git a/res/drawable-mdpi/tab_selected_focused_democast.9.png b/resources/drawable-mdpi/tab_selected_focused_democast.9.png similarity index 100% rename from res/drawable-mdpi/tab_selected_focused_democast.9.png rename to resources/drawable-mdpi/tab_selected_focused_democast.9.png diff --git a/res/drawable-mdpi/tab_selected_pressed_castvideo.9.png b/resources/drawable-mdpi/tab_selected_pressed_castvideo.9.png similarity index 100% rename from res/drawable-mdpi/tab_selected_pressed_castvideo.9.png rename to resources/drawable-mdpi/tab_selected_pressed_castvideo.9.png diff --git a/res/drawable-mdpi/tab_selected_pressed_democast.9.png b/resources/drawable-mdpi/tab_selected_pressed_democast.9.png similarity index 100% rename from res/drawable-mdpi/tab_selected_pressed_democast.9.png rename to resources/drawable-mdpi/tab_selected_pressed_democast.9.png diff --git a/res/drawable-mdpi/tab_unselected_castvideo.9.png b/resources/drawable-mdpi/tab_unselected_castvideo.9.png similarity index 100% rename from res/drawable-mdpi/tab_unselected_castvideo.9.png rename to resources/drawable-mdpi/tab_unselected_castvideo.9.png diff --git a/res/drawable-mdpi/tab_unselected_democast.9.png b/resources/drawable-mdpi/tab_unselected_democast.9.png similarity index 100% rename from res/drawable-mdpi/tab_unselected_democast.9.png rename to resources/drawable-mdpi/tab_unselected_democast.9.png diff --git a/res/drawable-mdpi/tab_unselected_focused_castvideo.9.png b/resources/drawable-mdpi/tab_unselected_focused_castvideo.9.png similarity index 100% rename from res/drawable-mdpi/tab_unselected_focused_castvideo.9.png rename to resources/drawable-mdpi/tab_unselected_focused_castvideo.9.png diff --git a/res/drawable-mdpi/tab_unselected_focused_democast.9.png b/resources/drawable-mdpi/tab_unselected_focused_democast.9.png similarity index 100% rename from res/drawable-mdpi/tab_unselected_focused_democast.9.png rename to resources/drawable-mdpi/tab_unselected_focused_democast.9.png diff --git a/res/drawable-mdpi/tab_unselected_pressed_castvideo.9.png b/resources/drawable-mdpi/tab_unselected_pressed_castvideo.9.png similarity index 100% rename from res/drawable-mdpi/tab_unselected_pressed_castvideo.9.png rename to resources/drawable-mdpi/tab_unselected_pressed_castvideo.9.png diff --git a/res/drawable-mdpi/tab_unselected_pressed_democast.9.png b/resources/drawable-mdpi/tab_unselected_pressed_democast.9.png similarity index 100% rename from res/drawable-mdpi/tab_unselected_pressed_democast.9.png rename to resources/drawable-mdpi/tab_unselected_pressed_democast.9.png diff --git a/res/drawable-nodpi/pattern.png b/resources/drawable-nodpi/pattern.png similarity index 100% rename from res/drawable-nodpi/pattern.png rename to resources/drawable-nodpi/pattern.png diff --git a/res/drawable-nodpi/shadow7.9.png b/resources/drawable-nodpi/shadow7.9.png similarity index 100% rename from res/drawable-nodpi/shadow7.9.png rename to resources/drawable-nodpi/shadow7.9.png diff --git a/res/drawable-xhdpi-v11/ic_stat_action_notification.png b/resources/drawable-xhdpi-v11/ic_stat_action_notification.png similarity index 100% rename from res/drawable-xhdpi-v11/ic_stat_action_notification.png rename to resources/drawable-xhdpi-v11/ic_stat_action_notification.png diff --git a/res/drawable-xhdpi-v11/ic_stat_hardware_headphones.png b/resources/drawable-xhdpi-v11/ic_stat_hardware_headphones.png similarity index 100% rename from res/drawable-xhdpi-v11/ic_stat_hardware_headphones.png rename to resources/drawable-xhdpi-v11/ic_stat_hardware_headphones.png diff --git a/res/drawable-xhdpi-v11/mr_ic_media_route_disabled_holo_dark.png b/resources/drawable-xhdpi-v11/mr_ic_media_route_disabled_holo_dark.png similarity index 100% rename from res/drawable-xhdpi-v11/mr_ic_media_route_disabled_holo_dark.png rename to resources/drawable-xhdpi-v11/mr_ic_media_route_disabled_holo_dark.png diff --git a/res/drawable-xhdpi-v11/mr_ic_media_route_off_holo_dark.png b/resources/drawable-xhdpi-v11/mr_ic_media_route_off_holo_dark.png similarity index 100% rename from res/drawable-xhdpi-v11/mr_ic_media_route_off_holo_dark.png rename to resources/drawable-xhdpi-v11/mr_ic_media_route_off_holo_dark.png diff --git a/res/drawable-xhdpi-v11/mr_ic_media_route_on_0_holo_dark.png b/resources/drawable-xhdpi-v11/mr_ic_media_route_on_0_holo_dark.png similarity index 100% rename from res/drawable-xhdpi-v11/mr_ic_media_route_on_0_holo_dark.png rename to resources/drawable-xhdpi-v11/mr_ic_media_route_on_0_holo_dark.png diff --git a/res/drawable-xhdpi-v11/mr_ic_media_route_on_1_holo_dark.png b/resources/drawable-xhdpi-v11/mr_ic_media_route_on_1_holo_dark.png similarity index 100% rename from res/drawable-xhdpi-v11/mr_ic_media_route_on_1_holo_dark.png rename to resources/drawable-xhdpi-v11/mr_ic_media_route_on_1_holo_dark.png diff --git a/res/drawable-xhdpi-v11/mr_ic_media_route_on_2_holo_dark.png b/resources/drawable-xhdpi-v11/mr_ic_media_route_on_2_holo_dark.png similarity index 100% rename from res/drawable-xhdpi-v11/mr_ic_media_route_on_2_holo_dark.png rename to resources/drawable-xhdpi-v11/mr_ic_media_route_on_2_holo_dark.png diff --git a/res/drawable-xhdpi-v11/mr_ic_media_route_on_holo_dark.png b/resources/drawable-xhdpi-v11/mr_ic_media_route_on_holo_dark.png similarity index 100% rename from res/drawable-xhdpi-v11/mr_ic_media_route_on_holo_dark.png rename to resources/drawable-xhdpi-v11/mr_ic_media_route_on_holo_dark.png diff --git a/res/drawable-xhdpi/ab_bottom_solid_castvideo.9.png b/resources/drawable-xhdpi/ab_bottom_solid_castvideo.9.png similarity index 100% rename from res/drawable-xhdpi/ab_bottom_solid_castvideo.9.png rename to resources/drawable-xhdpi/ab_bottom_solid_castvideo.9.png diff --git a/res/drawable-xhdpi/ab_bottom_solid_democast.9.png b/resources/drawable-xhdpi/ab_bottom_solid_democast.9.png similarity index 100% rename from res/drawable-xhdpi/ab_bottom_solid_democast.9.png rename to resources/drawable-xhdpi/ab_bottom_solid_democast.9.png diff --git a/res/drawable-xhdpi/ab_solid_castvideo.9.png b/resources/drawable-xhdpi/ab_solid_castvideo.9.png similarity index 100% rename from res/drawable-xhdpi/ab_solid_castvideo.9.png rename to resources/drawable-xhdpi/ab_solid_castvideo.9.png diff --git a/res/drawable-xhdpi/ab_solid_democast.9.png b/resources/drawable-xhdpi/ab_solid_democast.9.png similarity index 100% rename from res/drawable-xhdpi/ab_solid_democast.9.png rename to resources/drawable-xhdpi/ab_solid_democast.9.png diff --git a/res/drawable-xhdpi/ab_stacked_solid_castvideo.9.png b/resources/drawable-xhdpi/ab_stacked_solid_castvideo.9.png similarity index 100% rename from res/drawable-xhdpi/ab_stacked_solid_castvideo.9.png rename to resources/drawable-xhdpi/ab_stacked_solid_castvideo.9.png diff --git a/res/drawable-xhdpi/ab_stacked_solid_democast.9.png b/resources/drawable-xhdpi/ab_stacked_solid_democast.9.png similarity index 100% rename from res/drawable-xhdpi/ab_stacked_solid_democast.9.png rename to resources/drawable-xhdpi/ab_stacked_solid_democast.9.png diff --git a/res/drawable-xhdpi/ab_texture_tile_castvideo.png b/resources/drawable-xhdpi/ab_texture_tile_castvideo.png similarity index 100% rename from res/drawable-xhdpi/ab_texture_tile_castvideo.png rename to resources/drawable-xhdpi/ab_texture_tile_castvideo.png diff --git a/res/drawable-xhdpi/ab_texture_tile_democast.png b/resources/drawable-xhdpi/ab_texture_tile_democast.png similarity index 100% rename from res/drawable-xhdpi/ab_texture_tile_democast.png rename to resources/drawable-xhdpi/ab_texture_tile_democast.png diff --git a/res/drawable-xhdpi/ab_transparent_castvideo.9.png b/resources/drawable-xhdpi/ab_transparent_castvideo.9.png similarity index 100% rename from res/drawable-xhdpi/ab_transparent_castvideo.9.png rename to resources/drawable-xhdpi/ab_transparent_castvideo.9.png diff --git a/res/drawable-xhdpi/ab_transparent_democast.9.png b/resources/drawable-xhdpi/ab_transparent_democast.9.png similarity index 100% rename from res/drawable-xhdpi/ab_transparent_democast.9.png rename to resources/drawable-xhdpi/ab_transparent_democast.9.png diff --git a/res/drawable-xhdpi/ab_transparent_democastoverlay.9.png b/resources/drawable-xhdpi/ab_transparent_democastoverlay.9.png similarity index 100% rename from res/drawable-xhdpi/ab_transparent_democastoverlay.9.png rename to resources/drawable-xhdpi/ab_transparent_democastoverlay.9.png diff --git a/res/drawable-xhdpi/actionbar_logo.png b/resources/drawable-xhdpi/actionbar_logo.png similarity index 100% rename from res/drawable-xhdpi/actionbar_logo.png rename to resources/drawable-xhdpi/actionbar_logo.png diff --git a/res/drawable-xhdpi/actionbar_logo_castvideos.png b/resources/drawable-xhdpi/actionbar_logo_castvideos.png similarity index 100% rename from res/drawable-xhdpi/actionbar_logo_castvideos.png rename to resources/drawable-xhdpi/actionbar_logo_castvideos.png diff --git a/res/drawable-xhdpi/apptheme_scrubber_control_disabled_holo.png b/resources/drawable-xhdpi/apptheme_scrubber_control_disabled_holo.png similarity index 100% rename from res/drawable-xhdpi/apptheme_scrubber_control_disabled_holo.png rename to resources/drawable-xhdpi/apptheme_scrubber_control_disabled_holo.png diff --git a/res/drawable-xhdpi/apptheme_scrubber_control_focused_holo.png b/resources/drawable-xhdpi/apptheme_scrubber_control_focused_holo.png similarity index 100% rename from res/drawable-xhdpi/apptheme_scrubber_control_focused_holo.png rename to resources/drawable-xhdpi/apptheme_scrubber_control_focused_holo.png diff --git a/res/drawable-xhdpi/apptheme_scrubber_control_normal_holo.png b/resources/drawable-xhdpi/apptheme_scrubber_control_normal_holo.png similarity index 100% rename from res/drawable-xhdpi/apptheme_scrubber_control_normal_holo.png rename to resources/drawable-xhdpi/apptheme_scrubber_control_normal_holo.png diff --git a/res/drawable-xhdpi/apptheme_scrubber_control_pressed_holo.png b/resources/drawable-xhdpi/apptheme_scrubber_control_pressed_holo.png similarity index 100% rename from res/drawable-xhdpi/apptheme_scrubber_control_pressed_holo.png rename to resources/drawable-xhdpi/apptheme_scrubber_control_pressed_holo.png diff --git a/res/drawable-xhdpi/apptheme_scrubber_primary_holo.9.png b/resources/drawable-xhdpi/apptheme_scrubber_primary_holo.9.png similarity index 100% rename from res/drawable-xhdpi/apptheme_scrubber_primary_holo.9.png rename to resources/drawable-xhdpi/apptheme_scrubber_primary_holo.9.png diff --git a/res/drawable-xhdpi/apptheme_scrubber_secondary_holo.9.png b/resources/drawable-xhdpi/apptheme_scrubber_secondary_holo.9.png similarity index 100% rename from res/drawable-xhdpi/apptheme_scrubber_secondary_holo.9.png rename to resources/drawable-xhdpi/apptheme_scrubber_secondary_holo.9.png diff --git a/res/drawable-xhdpi/apptheme_scrubber_track_holo_light.9.png b/resources/drawable-xhdpi/apptheme_scrubber_track_holo_light.9.png similarity index 100% rename from res/drawable-xhdpi/apptheme_scrubber_track_holo_light.9.png rename to resources/drawable-xhdpi/apptheme_scrubber_track_holo_light.9.png diff --git a/res/drawable-xhdpi/arrow_down_white.png b/resources/drawable-xhdpi/arrow_down_white.png similarity index 100% rename from res/drawable-xhdpi/arrow_down_white.png rename to resources/drawable-xhdpi/arrow_down_white.png diff --git a/res/drawable-xhdpi/btn_cab_done_default_castvideo.9.png b/resources/drawable-xhdpi/btn_cab_done_default_castvideo.9.png similarity index 100% rename from res/drawable-xhdpi/btn_cab_done_default_castvideo.9.png rename to resources/drawable-xhdpi/btn_cab_done_default_castvideo.9.png diff --git a/res/drawable-xhdpi/btn_cab_done_default_democast.9.png b/resources/drawable-xhdpi/btn_cab_done_default_democast.9.png similarity index 100% rename from res/drawable-xhdpi/btn_cab_done_default_democast.9.png rename to resources/drawable-xhdpi/btn_cab_done_default_democast.9.png diff --git a/res/drawable-xhdpi/btn_cab_done_focused_castvideo.9.png b/resources/drawable-xhdpi/btn_cab_done_focused_castvideo.9.png similarity index 100% rename from res/drawable-xhdpi/btn_cab_done_focused_castvideo.9.png rename to resources/drawable-xhdpi/btn_cab_done_focused_castvideo.9.png diff --git a/res/drawable-xhdpi/btn_cab_done_focused_democast.9.png b/resources/drawable-xhdpi/btn_cab_done_focused_democast.9.png similarity index 100% rename from res/drawable-xhdpi/btn_cab_done_focused_democast.9.png rename to resources/drawable-xhdpi/btn_cab_done_focused_democast.9.png diff --git a/res/drawable-xhdpi/btn_cab_done_pressed_castvideo.9.png b/resources/drawable-xhdpi/btn_cab_done_pressed_castvideo.9.png similarity index 100% rename from res/drawable-xhdpi/btn_cab_done_pressed_castvideo.9.png rename to resources/drawable-xhdpi/btn_cab_done_pressed_castvideo.9.png diff --git a/res/drawable-xhdpi/btn_cab_done_pressed_democast.9.png b/resources/drawable-xhdpi/btn_cab_done_pressed_democast.9.png similarity index 100% rename from res/drawable-xhdpi/btn_cab_done_pressed_democast.9.png rename to resources/drawable-xhdpi/btn_cab_done_pressed_democast.9.png diff --git a/res/drawable-xhdpi/cab_background_bottom_castvideo.9.png b/resources/drawable-xhdpi/cab_background_bottom_castvideo.9.png similarity index 100% rename from res/drawable-xhdpi/cab_background_bottom_castvideo.9.png rename to resources/drawable-xhdpi/cab_background_bottom_castvideo.9.png diff --git a/res/drawable-xhdpi/cab_background_bottom_democast.9.png b/resources/drawable-xhdpi/cab_background_bottom_democast.9.png similarity index 100% rename from res/drawable-xhdpi/cab_background_bottom_democast.9.png rename to resources/drawable-xhdpi/cab_background_bottom_democast.9.png diff --git a/res/drawable-xhdpi/cab_background_top_castvideo.9.png b/resources/drawable-xhdpi/cab_background_top_castvideo.9.png similarity index 100% rename from res/drawable-xhdpi/cab_background_top_castvideo.9.png rename to resources/drawable-xhdpi/cab_background_top_castvideo.9.png diff --git a/res/drawable-xhdpi/cab_background_top_democast.9.png b/resources/drawable-xhdpi/cab_background_top_democast.9.png similarity index 100% rename from res/drawable-xhdpi/cab_background_top_democast.9.png rename to resources/drawable-xhdpi/cab_background_top_democast.9.png diff --git a/res/drawable-xhdpi/default_video.png b/resources/drawable-xhdpi/default_video.png similarity index 100% rename from res/drawable-xhdpi/default_video.png rename to resources/drawable-xhdpi/default_video.png diff --git a/res/drawable-xhdpi/ic_action_alerts_and_states_warning.png b/resources/drawable-xhdpi/ic_action_alerts_and_states_warning.png similarity index 100% rename from res/drawable-xhdpi/ic_action_alerts_and_states_warning.png rename to resources/drawable-xhdpi/ic_action_alerts_and_states_warning.png diff --git a/res/drawable-xhdpi/ic_add_circle_outline_white.png b/resources/drawable-xhdpi/ic_add_circle_outline_white.png similarity index 100% rename from res/drawable-xhdpi/ic_add_circle_outline_white.png rename to resources/drawable-xhdpi/ic_add_circle_outline_white.png diff --git a/res/drawable-xhdpi/ic_add_white_18dp.png b/resources/drawable-xhdpi/ic_add_white_18dp.png similarity index 100% rename from res/drawable-xhdpi/ic_add_white_18dp.png rename to resources/drawable-xhdpi/ic_add_white_18dp.png diff --git a/res/drawable-xhdpi/ic_add_white_24dp.png b/resources/drawable-xhdpi/ic_add_white_24dp.png similarity index 100% rename from res/drawable-xhdpi/ic_add_white_24dp.png rename to resources/drawable-xhdpi/ic_add_white_24dp.png diff --git a/res/drawable-xhdpi/ic_add_white_36dp.png b/resources/drawable-xhdpi/ic_add_white_36dp.png similarity index 100% rename from res/drawable-xhdpi/ic_add_white_36dp.png rename to resources/drawable-xhdpi/ic_add_white_36dp.png diff --git a/res/drawable-xhdpi/ic_add_white_48dp.png b/resources/drawable-xhdpi/ic_add_white_48dp.png similarity index 100% rename from res/drawable-xhdpi/ic_add_white_48dp.png rename to resources/drawable-xhdpi/ic_add_white_48dp.png diff --git a/res/drawable-xhdpi/ic_av_next.png b/resources/drawable-xhdpi/ic_av_next.png similarity index 100% rename from res/drawable-xhdpi/ic_av_next.png rename to resources/drawable-xhdpi/ic_av_next.png diff --git a/res/drawable-xhdpi/ic_av_pause.png b/resources/drawable-xhdpi/ic_av_pause.png similarity index 100% rename from res/drawable-xhdpi/ic_av_pause.png rename to resources/drawable-xhdpi/ic_av_pause.png diff --git a/res/drawable-xhdpi/ic_av_pause_dark.png b/resources/drawable-xhdpi/ic_av_pause_dark.png similarity index 100% rename from res/drawable-xhdpi/ic_av_pause_dark.png rename to resources/drawable-xhdpi/ic_av_pause_dark.png diff --git a/res/drawable-xhdpi/ic_av_pause_light.png b/resources/drawable-xhdpi/ic_av_pause_light.png similarity index 100% rename from res/drawable-xhdpi/ic_av_pause_light.png rename to resources/drawable-xhdpi/ic_av_pause_light.png diff --git a/res/drawable-xhdpi/ic_av_pause_over_video.png b/resources/drawable-xhdpi/ic_av_pause_over_video.png similarity index 100% rename from res/drawable-xhdpi/ic_av_pause_over_video.png rename to resources/drawable-xhdpi/ic_av_pause_over_video.png diff --git a/res/drawable-xhdpi/ic_av_pause_over_video_large.png b/resources/drawable-xhdpi/ic_av_pause_over_video_large.png similarity index 100% rename from res/drawable-xhdpi/ic_av_pause_over_video_large.png rename to resources/drawable-xhdpi/ic_av_pause_over_video_large.png diff --git a/res/drawable-xhdpi/ic_av_play.png b/resources/drawable-xhdpi/ic_av_play.png similarity index 100% rename from res/drawable-xhdpi/ic_av_play.png rename to resources/drawable-xhdpi/ic_av_play.png diff --git a/res/drawable-xhdpi/ic_av_play_dark.png b/resources/drawable-xhdpi/ic_av_play_dark.png similarity index 100% rename from res/drawable-xhdpi/ic_av_play_dark.png rename to resources/drawable-xhdpi/ic_av_play_dark.png diff --git a/res/drawable-xhdpi/ic_av_play_light.png b/resources/drawable-xhdpi/ic_av_play_light.png similarity index 100% rename from res/drawable-xhdpi/ic_av_play_light.png rename to resources/drawable-xhdpi/ic_av_play_light.png diff --git a/res/drawable-xhdpi/ic_av_play_over_video.png b/resources/drawable-xhdpi/ic_av_play_over_video.png similarity index 100% rename from res/drawable-xhdpi/ic_av_play_over_video.png rename to resources/drawable-xhdpi/ic_av_play_over_video.png diff --git a/res/drawable-xhdpi/ic_av_play_over_video_large.png b/resources/drawable-xhdpi/ic_av_play_over_video_large.png similarity index 100% rename from res/drawable-xhdpi/ic_av_play_over_video_large.png rename to resources/drawable-xhdpi/ic_av_play_over_video_large.png diff --git a/res/drawable-xhdpi/ic_av_previous.png b/resources/drawable-xhdpi/ic_av_previous.png similarity index 100% rename from res/drawable-xhdpi/ic_av_previous.png rename to resources/drawable-xhdpi/ic_av_previous.png diff --git a/res/drawable-xhdpi/ic_av_stop.png b/resources/drawable-xhdpi/ic_av_stop.png similarity index 100% rename from res/drawable-xhdpi/ic_av_stop.png rename to resources/drawable-xhdpi/ic_av_stop.png diff --git a/res/drawable-xhdpi/ic_closed_caption_blue.png b/resources/drawable-xhdpi/ic_closed_caption_blue.png similarity index 100% rename from res/drawable-xhdpi/ic_closed_caption_blue.png rename to resources/drawable-xhdpi/ic_closed_caption_blue.png diff --git a/res/drawable-xhdpi/ic_closed_caption_googblue_24dp.png b/resources/drawable-xhdpi/ic_closed_caption_googblue_24dp.png similarity index 100% rename from res/drawable-xhdpi/ic_closed_caption_googblue_24dp.png rename to resources/drawable-xhdpi/ic_closed_caption_googblue_24dp.png diff --git a/res/drawable-xhdpi/ic_closed_caption_googblue_36dp.png b/resources/drawable-xhdpi/ic_closed_caption_googblue_36dp.png similarity index 100% rename from res/drawable-xhdpi/ic_closed_caption_googblue_36dp.png rename to resources/drawable-xhdpi/ic_closed_caption_googblue_36dp.png diff --git a/res/drawable-xhdpi/ic_closed_caption_googblue_48dp.png b/resources/drawable-xhdpi/ic_closed_caption_googblue_48dp.png similarity index 100% rename from res/drawable-xhdpi/ic_closed_caption_googblue_48dp.png rename to resources/drawable-xhdpi/ic_closed_caption_googblue_48dp.png diff --git a/res/drawable-xhdpi/ic_closed_caption_grey.png b/resources/drawable-xhdpi/ic_closed_caption_grey.png similarity index 100% rename from res/drawable-xhdpi/ic_closed_caption_grey.png rename to resources/drawable-xhdpi/ic_closed_caption_grey.png diff --git a/res/drawable-xhdpi/ic_closed_caption_grey600_24dp.png b/resources/drawable-xhdpi/ic_closed_caption_grey600_24dp.png similarity index 100% rename from res/drawable-xhdpi/ic_closed_caption_grey600_24dp.png rename to resources/drawable-xhdpi/ic_closed_caption_grey600_24dp.png diff --git a/res/drawable-xhdpi/ic_closed_caption_grey600_36dp.png b/resources/drawable-xhdpi/ic_closed_caption_grey600_36dp.png similarity index 100% rename from res/drawable-xhdpi/ic_closed_caption_grey600_36dp.png rename to resources/drawable-xhdpi/ic_closed_caption_grey600_36dp.png diff --git a/res/drawable-xhdpi/ic_closed_caption_grey600_48dp.png b/resources/drawable-xhdpi/ic_closed_caption_grey600_48dp.png similarity index 100% rename from res/drawable-xhdpi/ic_closed_caption_grey600_48dp.png rename to resources/drawable-xhdpi/ic_closed_caption_grey600_48dp.png diff --git a/res/drawable-xhdpi/ic_closed_caption_white.png b/resources/drawable-xhdpi/ic_closed_caption_white.png similarity index 100% rename from res/drawable-xhdpi/ic_closed_caption_white.png rename to resources/drawable-xhdpi/ic_closed_caption_white.png diff --git a/res/drawable-xhdpi/ic_closed_caption_white_24dp.png b/resources/drawable-xhdpi/ic_closed_caption_white_24dp.png similarity index 100% rename from res/drawable-xhdpi/ic_closed_caption_white_24dp.png rename to resources/drawable-xhdpi/ic_closed_caption_white_24dp.png diff --git a/res/drawable-xhdpi/ic_closed_caption_white_36dp.png b/resources/drawable-xhdpi/ic_closed_caption_white_36dp.png similarity index 100% rename from res/drawable-xhdpi/ic_closed_caption_white_36dp.png rename to resources/drawable-xhdpi/ic_closed_caption_white_36dp.png diff --git a/res/drawable-xhdpi/ic_closed_caption_white_48dp.png b/resources/drawable-xhdpi/ic_closed_caption_white_48dp.png similarity index 100% rename from res/drawable-xhdpi/ic_closed_caption_white_48dp.png rename to resources/drawable-xhdpi/ic_closed_caption_white_48dp.png diff --git a/res/drawable-xhdpi/ic_device_access_volume_muted.png b/resources/drawable-xhdpi/ic_device_access_volume_muted.png similarity index 100% rename from res/drawable-xhdpi/ic_device_access_volume_muted.png rename to resources/drawable-xhdpi/ic_device_access_volume_muted.png diff --git a/res/drawable-xhdpi/ic_device_access_volume_on.png b/resources/drawable-xhdpi/ic_device_access_volume_on.png similarity index 100% rename from res/drawable-xhdpi/ic_device_access_volume_on.png rename to resources/drawable-xhdpi/ic_device_access_volume_on.png diff --git a/res/drawable-xhdpi/ic_drag_updown_grey_24dp.png b/resources/drawable-xhdpi/ic_drag_updown_grey_24dp.png similarity index 100% rename from res/drawable-xhdpi/ic_drag_updown_grey_24dp.png rename to resources/drawable-xhdpi/ic_drag_updown_grey_24dp.png diff --git a/res/drawable-xhdpi/ic_drag_updown_white_24dp.png b/resources/drawable-xhdpi/ic_drag_updown_white_24dp.png similarity index 100% rename from res/drawable-xhdpi/ic_drag_updown_white_24dp.png rename to resources/drawable-xhdpi/ic_drag_updown_white_24dp.png diff --git a/res/drawable-xhdpi/ic_expand_list.png b/resources/drawable-xhdpi/ic_expand_list.png similarity index 100% rename from res/drawable-xhdpi/ic_expand_list.png rename to resources/drawable-xhdpi/ic_expand_list.png diff --git a/res/drawable-xhdpi/ic_launcher.png b/resources/drawable-xhdpi/ic_launcher.png similarity index 100% rename from res/drawable-xhdpi/ic_launcher.png rename to resources/drawable-xhdpi/ic_launcher.png diff --git a/res/drawable-xhdpi/ic_more_vert_grey600_24dp.png b/resources/drawable-xhdpi/ic_more_vert_grey600_24dp.png similarity index 100% rename from res/drawable-xhdpi/ic_more_vert_grey600_24dp.png rename to resources/drawable-xhdpi/ic_more_vert_grey600_24dp.png diff --git a/res/drawable-xhdpi/ic_music_albumart.png b/resources/drawable-xhdpi/ic_music_albumart.png similarity index 100% rename from res/drawable-xhdpi/ic_music_albumart.png rename to resources/drawable-xhdpi/ic_music_albumart.png diff --git a/res/drawable-xhdpi/ic_pause_circle_white_80dp.png b/resources/drawable-xhdpi/ic_pause_circle_white_80dp.png similarity index 100% rename from res/drawable-xhdpi/ic_pause_circle_white_80dp.png rename to resources/drawable-xhdpi/ic_pause_circle_white_80dp.png diff --git a/res/drawable-xhdpi/ic_pause_grey600_48dp.png b/resources/drawable-xhdpi/ic_pause_grey600_48dp.png similarity index 100% rename from res/drawable-xhdpi/ic_pause_grey600_48dp.png rename to resources/drawable-xhdpi/ic_pause_grey600_48dp.png diff --git a/res/drawable-xhdpi/ic_play_arrow_grey600_48dp.png b/resources/drawable-xhdpi/ic_play_arrow_grey600_48dp.png similarity index 100% rename from res/drawable-xhdpi/ic_play_arrow_grey600_48dp.png rename to resources/drawable-xhdpi/ic_play_arrow_grey600_48dp.png diff --git a/res/drawable-xhdpi/ic_play_arrow_white_24dp.png b/resources/drawable-xhdpi/ic_play_arrow_white_24dp.png similarity index 100% rename from res/drawable-xhdpi/ic_play_arrow_white_24dp.png rename to resources/drawable-xhdpi/ic_play_arrow_white_24dp.png diff --git a/res/drawable-xhdpi/ic_play_circle_outline_white.png b/resources/drawable-xhdpi/ic_play_circle_outline_white.png similarity index 100% rename from res/drawable-xhdpi/ic_play_circle_outline_white.png rename to resources/drawable-xhdpi/ic_play_circle_outline_white.png diff --git a/res/drawable-xhdpi/ic_play_circle_white_80dp.png b/resources/drawable-xhdpi/ic_play_circle_white_80dp.png similarity index 100% rename from res/drawable-xhdpi/ic_play_circle_white_80dp.png rename to resources/drawable-xhdpi/ic_play_circle_white_80dp.png diff --git a/res/drawable-xhdpi/ic_playlist_white_24dp.png b/resources/drawable-xhdpi/ic_playlist_white_24dp.png similarity index 100% rename from res/drawable-xhdpi/ic_playlist_white_24dp.png rename to resources/drawable-xhdpi/ic_playlist_white_24dp.png diff --git a/res/drawable-xhdpi/ic_remove_circle_white_24dp.png b/resources/drawable-xhdpi/ic_remove_circle_white_24dp.png similarity index 100% rename from res/drawable-xhdpi/ic_remove_circle_white_24dp.png rename to resources/drawable-xhdpi/ic_remove_circle_white_24dp.png diff --git a/res/drawable-xhdpi/ic_skip_next_grey600_48dp.png b/resources/drawable-xhdpi/ic_skip_next_grey600_48dp.png similarity index 100% rename from res/drawable-xhdpi/ic_skip_next_grey600_48dp.png rename to resources/drawable-xhdpi/ic_skip_next_grey600_48dp.png diff --git a/res/drawable-xhdpi/ic_skip_next_white_48dp.png b/resources/drawable-xhdpi/ic_skip_next_white_48dp.png similarity index 100% rename from res/drawable-xhdpi/ic_skip_next_white_48dp.png rename to resources/drawable-xhdpi/ic_skip_next_white_48dp.png diff --git a/res/drawable-xhdpi/ic_skip_previous_grey600_48dp.png b/resources/drawable-xhdpi/ic_skip_previous_grey600_48dp.png similarity index 100% rename from res/drawable-xhdpi/ic_skip_previous_grey600_48dp.png rename to resources/drawable-xhdpi/ic_skip_previous_grey600_48dp.png diff --git a/res/drawable-xhdpi/ic_skip_previous_white_48dp.png b/resources/drawable-xhdpi/ic_skip_previous_white_48dp.png similarity index 100% rename from res/drawable-xhdpi/ic_skip_previous_white_48dp.png rename to resources/drawable-xhdpi/ic_skip_previous_white_48dp.png diff --git a/res/drawable-xhdpi/ic_stat_action_notification.png b/resources/drawable-xhdpi/ic_stat_action_notification.png similarity index 100% rename from res/drawable-xhdpi/ic_stat_action_notification.png rename to resources/drawable-xhdpi/ic_stat_action_notification.png diff --git a/res/drawable-xhdpi/ic_stat_hardware_headphones.png b/resources/drawable-xhdpi/ic_stat_hardware_headphones.png similarity index 100% rename from res/drawable-xhdpi/ic_stat_hardware_headphones.png rename to resources/drawable-xhdpi/ic_stat_hardware_headphones.png diff --git a/res/drawable-xhdpi/ic_stop_white_24dp.png b/resources/drawable-xhdpi/ic_stop_white_24dp.png similarity index 100% rename from res/drawable-xhdpi/ic_stop_white_24dp.png rename to resources/drawable-xhdpi/ic_stop_white_24dp.png diff --git a/res/drawable-xhdpi/list_focused_castvideo.9.png b/resources/drawable-xhdpi/list_focused_castvideo.9.png similarity index 100% rename from res/drawable-xhdpi/list_focused_castvideo.9.png rename to resources/drawable-xhdpi/list_focused_castvideo.9.png diff --git a/res/drawable-xhdpi/list_focused_democast.9.png b/resources/drawable-xhdpi/list_focused_democast.9.png similarity index 100% rename from res/drawable-xhdpi/list_focused_democast.9.png rename to resources/drawable-xhdpi/list_focused_democast.9.png diff --git a/res/drawable-xhdpi/list_pressed_castvideo.9.png b/resources/drawable-xhdpi/list_pressed_castvideo.9.png similarity index 100% rename from res/drawable-xhdpi/list_pressed_castvideo.9.png rename to resources/drawable-xhdpi/list_pressed_castvideo.9.png diff --git a/res/drawable-xhdpi/menu_dropdown_panel_castvideo.9.png b/resources/drawable-xhdpi/menu_dropdown_panel_castvideo.9.png similarity index 100% rename from res/drawable-xhdpi/menu_dropdown_panel_castvideo.9.png rename to resources/drawable-xhdpi/menu_dropdown_panel_castvideo.9.png diff --git a/res/drawable-xhdpi/menu_dropdown_panel_democast.9.png b/resources/drawable-xhdpi/menu_dropdown_panel_democast.9.png similarity index 100% rename from res/drawable-xhdpi/menu_dropdown_panel_democast.9.png rename to resources/drawable-xhdpi/menu_dropdown_panel_democast.9.png diff --git a/res/drawable-xhdpi/minibg.9.png b/resources/drawable-xhdpi/minibg.9.png similarity index 100% rename from res/drawable-xhdpi/minibg.9.png rename to resources/drawable-xhdpi/minibg.9.png diff --git a/res/drawable-xhdpi/mr_ic_media_route_disabled_holo_dark.png b/resources/drawable-xhdpi/mr_ic_media_route_disabled_holo_dark.png similarity index 100% rename from res/drawable-xhdpi/mr_ic_media_route_disabled_holo_dark.png rename to resources/drawable-xhdpi/mr_ic_media_route_disabled_holo_dark.png diff --git a/res/drawable-xhdpi/mr_ic_media_route_off_holo_dark.png b/resources/drawable-xhdpi/mr_ic_media_route_off_holo_dark.png similarity index 100% rename from res/drawable-xhdpi/mr_ic_media_route_off_holo_dark.png rename to resources/drawable-xhdpi/mr_ic_media_route_off_holo_dark.png diff --git a/res/drawable-xhdpi/mr_ic_media_route_on_0_holo_dark.png b/resources/drawable-xhdpi/mr_ic_media_route_on_0_holo_dark.png similarity index 100% rename from res/drawable-xhdpi/mr_ic_media_route_on_0_holo_dark.png rename to resources/drawable-xhdpi/mr_ic_media_route_on_0_holo_dark.png diff --git a/res/drawable-xhdpi/mr_ic_media_route_on_1_holo_dark.png b/resources/drawable-xhdpi/mr_ic_media_route_on_1_holo_dark.png similarity index 100% rename from res/drawable-xhdpi/mr_ic_media_route_on_1_holo_dark.png rename to resources/drawable-xhdpi/mr_ic_media_route_on_1_holo_dark.png diff --git a/res/drawable-xhdpi/mr_ic_media_route_on_2_holo_dark.png b/resources/drawable-xhdpi/mr_ic_media_route_on_2_holo_dark.png similarity index 100% rename from res/drawable-xhdpi/mr_ic_media_route_on_2_holo_dark.png rename to resources/drawable-xhdpi/mr_ic_media_route_on_2_holo_dark.png diff --git a/res/drawable-xhdpi/mr_ic_media_route_on_holo_dark.png b/resources/drawable-xhdpi/mr_ic_media_route_on_holo_dark.png similarity index 100% rename from res/drawable-xhdpi/mr_ic_media_route_on_holo_dark.png rename to resources/drawable-xhdpi/mr_ic_media_route_on_holo_dark.png diff --git a/res/drawable-xhdpi/progress_bg_castvideo.9.png b/resources/drawable-xhdpi/progress_bg_castvideo.9.png similarity index 100% rename from res/drawable-xhdpi/progress_bg_castvideo.9.png rename to resources/drawable-xhdpi/progress_bg_castvideo.9.png diff --git a/res/drawable-xhdpi/progress_bg_democast.9.png b/resources/drawable-xhdpi/progress_bg_democast.9.png similarity index 100% rename from res/drawable-xhdpi/progress_bg_democast.9.png rename to resources/drawable-xhdpi/progress_bg_democast.9.png diff --git a/res/drawable-xhdpi/progress_primary_castvideo.9.png b/resources/drawable-xhdpi/progress_primary_castvideo.9.png similarity index 100% rename from res/drawable-xhdpi/progress_primary_castvideo.9.png rename to resources/drawable-xhdpi/progress_primary_castvideo.9.png diff --git a/res/drawable-xhdpi/progress_primary_democast.9.png b/resources/drawable-xhdpi/progress_primary_democast.9.png similarity index 100% rename from res/drawable-xhdpi/progress_primary_democast.9.png rename to resources/drawable-xhdpi/progress_primary_democast.9.png diff --git a/res/drawable-xhdpi/progress_secondary_castvideo.9.png b/resources/drawable-xhdpi/progress_secondary_castvideo.9.png similarity index 100% rename from res/drawable-xhdpi/progress_secondary_castvideo.9.png rename to resources/drawable-xhdpi/progress_secondary_castvideo.9.png diff --git a/res/drawable-xhdpi/progress_secondary_democast.9.png b/resources/drawable-xhdpi/progress_secondary_democast.9.png similarity index 100% rename from res/drawable-xhdpi/progress_secondary_democast.9.png rename to resources/drawable-xhdpi/progress_secondary_democast.9.png diff --git a/res/drawable-xhdpi/shadow6.9.png b/resources/drawable-xhdpi/shadow6.9.png similarity index 100% rename from res/drawable-xhdpi/shadow6.9.png rename to resources/drawable-xhdpi/shadow6.9.png diff --git a/res/drawable-xhdpi/shadow7.9.png b/resources/drawable-xhdpi/shadow7.9.png similarity index 100% rename from res/drawable-xhdpi/shadow7.9.png rename to resources/drawable-xhdpi/shadow7.9.png diff --git a/res/drawable-xhdpi/spinner_ab_default_castvideo.9.png b/resources/drawable-xhdpi/spinner_ab_default_castvideo.9.png similarity index 100% rename from res/drawable-xhdpi/spinner_ab_default_castvideo.9.png rename to resources/drawable-xhdpi/spinner_ab_default_castvideo.9.png diff --git a/res/drawable-xhdpi/spinner_ab_default_democast.9.png b/resources/drawable-xhdpi/spinner_ab_default_democast.9.png similarity index 100% rename from res/drawable-xhdpi/spinner_ab_default_democast.9.png rename to resources/drawable-xhdpi/spinner_ab_default_democast.9.png diff --git a/res/drawable-xhdpi/spinner_ab_disabled_castvideo.9.png b/resources/drawable-xhdpi/spinner_ab_disabled_castvideo.9.png similarity index 100% rename from res/drawable-xhdpi/spinner_ab_disabled_castvideo.9.png rename to resources/drawable-xhdpi/spinner_ab_disabled_castvideo.9.png diff --git a/res/drawable-xhdpi/spinner_ab_disabled_democast.9.png b/resources/drawable-xhdpi/spinner_ab_disabled_democast.9.png similarity index 100% rename from res/drawable-xhdpi/spinner_ab_disabled_democast.9.png rename to resources/drawable-xhdpi/spinner_ab_disabled_democast.9.png diff --git a/res/drawable-xhdpi/spinner_ab_focused_castvideo.9.png b/resources/drawable-xhdpi/spinner_ab_focused_castvideo.9.png similarity index 100% rename from res/drawable-xhdpi/spinner_ab_focused_castvideo.9.png rename to resources/drawable-xhdpi/spinner_ab_focused_castvideo.9.png diff --git a/res/drawable-xhdpi/spinner_ab_focused_democast.9.png b/resources/drawable-xhdpi/spinner_ab_focused_democast.9.png similarity index 100% rename from res/drawable-xhdpi/spinner_ab_focused_democast.9.png rename to resources/drawable-xhdpi/spinner_ab_focused_democast.9.png diff --git a/res/drawable-xhdpi/spinner_ab_pressed_castvideo.9.png b/resources/drawable-xhdpi/spinner_ab_pressed_castvideo.9.png similarity index 100% rename from res/drawable-xhdpi/spinner_ab_pressed_castvideo.9.png rename to resources/drawable-xhdpi/spinner_ab_pressed_castvideo.9.png diff --git a/res/drawable-xhdpi/spinner_ab_pressed_democast.9.png b/resources/drawable-xhdpi/spinner_ab_pressed_democast.9.png similarity index 100% rename from res/drawable-xhdpi/spinner_ab_pressed_democast.9.png rename to resources/drawable-xhdpi/spinner_ab_pressed_democast.9.png diff --git a/res/drawable-xhdpi/tab_selected_castvideo.9.png b/resources/drawable-xhdpi/tab_selected_castvideo.9.png similarity index 100% rename from res/drawable-xhdpi/tab_selected_castvideo.9.png rename to resources/drawable-xhdpi/tab_selected_castvideo.9.png diff --git a/res/drawable-xhdpi/tab_selected_democast.9.png b/resources/drawable-xhdpi/tab_selected_democast.9.png similarity index 100% rename from res/drawable-xhdpi/tab_selected_democast.9.png rename to resources/drawable-xhdpi/tab_selected_democast.9.png diff --git a/res/drawable-xhdpi/tab_selected_focused_castvideo.9.png b/resources/drawable-xhdpi/tab_selected_focused_castvideo.9.png similarity index 100% rename from res/drawable-xhdpi/tab_selected_focused_castvideo.9.png rename to resources/drawable-xhdpi/tab_selected_focused_castvideo.9.png diff --git a/res/drawable-xhdpi/tab_selected_focused_democast.9.png b/resources/drawable-xhdpi/tab_selected_focused_democast.9.png similarity index 100% rename from res/drawable-xhdpi/tab_selected_focused_democast.9.png rename to resources/drawable-xhdpi/tab_selected_focused_democast.9.png diff --git a/res/drawable-xhdpi/tab_selected_pressed_castvideo.9.png b/resources/drawable-xhdpi/tab_selected_pressed_castvideo.9.png similarity index 100% rename from res/drawable-xhdpi/tab_selected_pressed_castvideo.9.png rename to resources/drawable-xhdpi/tab_selected_pressed_castvideo.9.png diff --git a/res/drawable-xhdpi/tab_selected_pressed_democast.9.png b/resources/drawable-xhdpi/tab_selected_pressed_democast.9.png similarity index 100% rename from res/drawable-xhdpi/tab_selected_pressed_democast.9.png rename to resources/drawable-xhdpi/tab_selected_pressed_democast.9.png diff --git a/res/drawable-xhdpi/tab_unselected_castvideo.9.png b/resources/drawable-xhdpi/tab_unselected_castvideo.9.png similarity index 100% rename from res/drawable-xhdpi/tab_unselected_castvideo.9.png rename to resources/drawable-xhdpi/tab_unselected_castvideo.9.png diff --git a/res/drawable-xhdpi/tab_unselected_democast.9.png b/resources/drawable-xhdpi/tab_unselected_democast.9.png similarity index 100% rename from res/drawable-xhdpi/tab_unselected_democast.9.png rename to resources/drawable-xhdpi/tab_unselected_democast.9.png diff --git a/res/drawable-xhdpi/tab_unselected_focused_castvideo.9.png b/resources/drawable-xhdpi/tab_unselected_focused_castvideo.9.png similarity index 100% rename from res/drawable-xhdpi/tab_unselected_focused_castvideo.9.png rename to resources/drawable-xhdpi/tab_unselected_focused_castvideo.9.png diff --git a/res/drawable-xhdpi/tab_unselected_focused_democast.9.png b/resources/drawable-xhdpi/tab_unselected_focused_democast.9.png similarity index 100% rename from res/drawable-xhdpi/tab_unselected_focused_democast.9.png rename to resources/drawable-xhdpi/tab_unselected_focused_democast.9.png diff --git a/res/drawable-xhdpi/tab_unselected_pressed_castvideo.9.png b/resources/drawable-xhdpi/tab_unselected_pressed_castvideo.9.png similarity index 100% rename from res/drawable-xhdpi/tab_unselected_pressed_castvideo.9.png rename to resources/drawable-xhdpi/tab_unselected_pressed_castvideo.9.png diff --git a/res/drawable-xhdpi/tab_unselected_pressed_democast.9.png b/resources/drawable-xhdpi/tab_unselected_pressed_democast.9.png similarity index 100% rename from res/drawable-xhdpi/tab_unselected_pressed_democast.9.png rename to resources/drawable-xhdpi/tab_unselected_pressed_democast.9.png diff --git a/res/drawable-xxhdpi-v11/ic_stat_action_notification.png b/resources/drawable-xxhdpi-v11/ic_stat_action_notification.png similarity index 100% rename from res/drawable-xxhdpi-v11/ic_stat_action_notification.png rename to resources/drawable-xxhdpi-v11/ic_stat_action_notification.png diff --git a/res/drawable-xxhdpi-v11/ic_stat_hardware_headphones.png b/resources/drawable-xxhdpi-v11/ic_stat_hardware_headphones.png similarity index 100% rename from res/drawable-xxhdpi-v11/ic_stat_hardware_headphones.png rename to resources/drawable-xxhdpi-v11/ic_stat_hardware_headphones.png diff --git a/res/drawable-xxhdpi-v11/mr_ic_media_route_disabled_holo_dark.png b/resources/drawable-xxhdpi-v11/mr_ic_media_route_disabled_holo_dark.png similarity index 100% rename from res/drawable-xxhdpi-v11/mr_ic_media_route_disabled_holo_dark.png rename to resources/drawable-xxhdpi-v11/mr_ic_media_route_disabled_holo_dark.png diff --git a/res/drawable-xxhdpi-v11/mr_ic_media_route_off_holo_dark.png b/resources/drawable-xxhdpi-v11/mr_ic_media_route_off_holo_dark.png similarity index 100% rename from res/drawable-xxhdpi-v11/mr_ic_media_route_off_holo_dark.png rename to resources/drawable-xxhdpi-v11/mr_ic_media_route_off_holo_dark.png diff --git a/res/drawable-xxhdpi-v11/mr_ic_media_route_on_0_holo_dark.png b/resources/drawable-xxhdpi-v11/mr_ic_media_route_on_0_holo_dark.png similarity index 100% rename from res/drawable-xxhdpi-v11/mr_ic_media_route_on_0_holo_dark.png rename to resources/drawable-xxhdpi-v11/mr_ic_media_route_on_0_holo_dark.png diff --git a/res/drawable-xxhdpi-v11/mr_ic_media_route_on_1_holo_dark.png b/resources/drawable-xxhdpi-v11/mr_ic_media_route_on_1_holo_dark.png similarity index 100% rename from res/drawable-xxhdpi-v11/mr_ic_media_route_on_1_holo_dark.png rename to resources/drawable-xxhdpi-v11/mr_ic_media_route_on_1_holo_dark.png diff --git a/res/drawable-xxhdpi-v11/mr_ic_media_route_on_2_holo_dark.png b/resources/drawable-xxhdpi-v11/mr_ic_media_route_on_2_holo_dark.png similarity index 100% rename from res/drawable-xxhdpi-v11/mr_ic_media_route_on_2_holo_dark.png rename to resources/drawable-xxhdpi-v11/mr_ic_media_route_on_2_holo_dark.png diff --git a/res/drawable-xxhdpi-v11/mr_ic_media_route_on_holo_dark.png b/resources/drawable-xxhdpi-v11/mr_ic_media_route_on_holo_dark.png similarity index 100% rename from res/drawable-xxhdpi-v11/mr_ic_media_route_on_holo_dark.png rename to resources/drawable-xxhdpi-v11/mr_ic_media_route_on_holo_dark.png diff --git a/res/drawable-xxhdpi/ab_bottom_solid_castvideo.9.png b/resources/drawable-xxhdpi/ab_bottom_solid_castvideo.9.png similarity index 100% rename from res/drawable-xxhdpi/ab_bottom_solid_castvideo.9.png rename to resources/drawable-xxhdpi/ab_bottom_solid_castvideo.9.png diff --git a/res/drawable-xxhdpi/ab_solid_castvideo.9.png b/resources/drawable-xxhdpi/ab_solid_castvideo.9.png similarity index 100% rename from res/drawable-xxhdpi/ab_solid_castvideo.9.png rename to resources/drawable-xxhdpi/ab_solid_castvideo.9.png diff --git a/res/drawable-xxhdpi/ab_stacked_solid_castvideo.9.png b/resources/drawable-xxhdpi/ab_stacked_solid_castvideo.9.png similarity index 100% rename from res/drawable-xxhdpi/ab_stacked_solid_castvideo.9.png rename to resources/drawable-xxhdpi/ab_stacked_solid_castvideo.9.png diff --git a/res/drawable-xxhdpi/ab_texture_tile_castvideo.png b/resources/drawable-xxhdpi/ab_texture_tile_castvideo.png similarity index 100% rename from res/drawable-xxhdpi/ab_texture_tile_castvideo.png rename to resources/drawable-xxhdpi/ab_texture_tile_castvideo.png diff --git a/res/drawable-xxhdpi/ab_transparent_castvideo.9.png b/resources/drawable-xxhdpi/ab_transparent_castvideo.9.png similarity index 100% rename from res/drawable-xxhdpi/ab_transparent_castvideo.9.png rename to resources/drawable-xxhdpi/ab_transparent_castvideo.9.png diff --git a/res/drawable-xxhdpi/actionbar_logo.png b/resources/drawable-xxhdpi/actionbar_logo.png similarity index 100% rename from res/drawable-xxhdpi/actionbar_logo.png rename to resources/drawable-xxhdpi/actionbar_logo.png diff --git a/res/drawable-xxhdpi/actionbar_logo_castvideos.png b/resources/drawable-xxhdpi/actionbar_logo_castvideos.png similarity index 100% rename from res/drawable-xxhdpi/actionbar_logo_castvideos.png rename to resources/drawable-xxhdpi/actionbar_logo_castvideos.png diff --git a/res/drawable-xxhdpi/apptheme_scrubber_control_disabled_holo.png b/resources/drawable-xxhdpi/apptheme_scrubber_control_disabled_holo.png similarity index 100% rename from res/drawable-xxhdpi/apptheme_scrubber_control_disabled_holo.png rename to resources/drawable-xxhdpi/apptheme_scrubber_control_disabled_holo.png diff --git a/res/drawable-xxhdpi/apptheme_scrubber_control_focused_holo.png b/resources/drawable-xxhdpi/apptheme_scrubber_control_focused_holo.png similarity index 100% rename from res/drawable-xxhdpi/apptheme_scrubber_control_focused_holo.png rename to resources/drawable-xxhdpi/apptheme_scrubber_control_focused_holo.png diff --git a/res/drawable-xxhdpi/apptheme_scrubber_control_normal_holo.png b/resources/drawable-xxhdpi/apptheme_scrubber_control_normal_holo.png similarity index 100% rename from res/drawable-xxhdpi/apptheme_scrubber_control_normal_holo.png rename to resources/drawable-xxhdpi/apptheme_scrubber_control_normal_holo.png diff --git a/res/drawable-xxhdpi/apptheme_scrubber_control_pressed_holo.png b/resources/drawable-xxhdpi/apptheme_scrubber_control_pressed_holo.png similarity index 100% rename from res/drawable-xxhdpi/apptheme_scrubber_control_pressed_holo.png rename to resources/drawable-xxhdpi/apptheme_scrubber_control_pressed_holo.png diff --git a/res/drawable-xxhdpi/apptheme_scrubber_primary_holo.9.png b/resources/drawable-xxhdpi/apptheme_scrubber_primary_holo.9.png similarity index 100% rename from res/drawable-xxhdpi/apptheme_scrubber_primary_holo.9.png rename to resources/drawable-xxhdpi/apptheme_scrubber_primary_holo.9.png diff --git a/res/drawable-xxhdpi/apptheme_scrubber_secondary_holo.9.png b/resources/drawable-xxhdpi/apptheme_scrubber_secondary_holo.9.png similarity index 100% rename from res/drawable-xxhdpi/apptheme_scrubber_secondary_holo.9.png rename to resources/drawable-xxhdpi/apptheme_scrubber_secondary_holo.9.png diff --git a/res/drawable-xxhdpi/apptheme_scrubber_track_holo_light.9.png b/resources/drawable-xxhdpi/apptheme_scrubber_track_holo_light.9.png similarity index 100% rename from res/drawable-xxhdpi/apptheme_scrubber_track_holo_light.9.png rename to resources/drawable-xxhdpi/apptheme_scrubber_track_holo_light.9.png diff --git a/res/drawable-xxhdpi/arrow_down_white.png b/resources/drawable-xxhdpi/arrow_down_white.png similarity index 100% rename from res/drawable-xxhdpi/arrow_down_white.png rename to resources/drawable-xxhdpi/arrow_down_white.png diff --git a/res/drawable-xxhdpi/btn_cab_done_default_castvideo.9.png b/resources/drawable-xxhdpi/btn_cab_done_default_castvideo.9.png similarity index 100% rename from res/drawable-xxhdpi/btn_cab_done_default_castvideo.9.png rename to resources/drawable-xxhdpi/btn_cab_done_default_castvideo.9.png diff --git a/res/drawable-xxhdpi/btn_cab_done_focused_castvideo.9.png b/resources/drawable-xxhdpi/btn_cab_done_focused_castvideo.9.png similarity index 100% rename from res/drawable-xxhdpi/btn_cab_done_focused_castvideo.9.png rename to resources/drawable-xxhdpi/btn_cab_done_focused_castvideo.9.png diff --git a/res/drawable-xxhdpi/btn_cab_done_pressed_castvideo.9.png b/resources/drawable-xxhdpi/btn_cab_done_pressed_castvideo.9.png similarity index 100% rename from res/drawable-xxhdpi/btn_cab_done_pressed_castvideo.9.png rename to resources/drawable-xxhdpi/btn_cab_done_pressed_castvideo.9.png diff --git a/res/drawable-xxhdpi/cab_background_bottom_castvideo.9.png b/resources/drawable-xxhdpi/cab_background_bottom_castvideo.9.png similarity index 100% rename from res/drawable-xxhdpi/cab_background_bottom_castvideo.9.png rename to resources/drawable-xxhdpi/cab_background_bottom_castvideo.9.png diff --git a/res/drawable-xxhdpi/cab_background_top_castvideo.9.png b/resources/drawable-xxhdpi/cab_background_top_castvideo.9.png similarity index 100% rename from res/drawable-xxhdpi/cab_background_top_castvideo.9.png rename to resources/drawable-xxhdpi/cab_background_top_castvideo.9.png diff --git a/res/drawable-xxhdpi/ic_action_alerts_and_states_warning.png b/resources/drawable-xxhdpi/ic_action_alerts_and_states_warning.png similarity index 100% rename from res/drawable-xxhdpi/ic_action_alerts_and_states_warning.png rename to resources/drawable-xxhdpi/ic_action_alerts_and_states_warning.png diff --git a/res/drawable-xxhdpi/ic_add_circle_outline_white.png b/resources/drawable-xxhdpi/ic_add_circle_outline_white.png similarity index 100% rename from res/drawable-xxhdpi/ic_add_circle_outline_white.png rename to resources/drawable-xxhdpi/ic_add_circle_outline_white.png diff --git a/res/drawable-xxhdpi/ic_add_white_18dp.png b/resources/drawable-xxhdpi/ic_add_white_18dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_add_white_18dp.png rename to resources/drawable-xxhdpi/ic_add_white_18dp.png diff --git a/res/drawable-xxhdpi/ic_add_white_24dp.png b/resources/drawable-xxhdpi/ic_add_white_24dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_add_white_24dp.png rename to resources/drawable-xxhdpi/ic_add_white_24dp.png diff --git a/res/drawable-xxhdpi/ic_add_white_36dp.png b/resources/drawable-xxhdpi/ic_add_white_36dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_add_white_36dp.png rename to resources/drawable-xxhdpi/ic_add_white_36dp.png diff --git a/res/drawable-xxhdpi/ic_add_white_48dp.png b/resources/drawable-xxhdpi/ic_add_white_48dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_add_white_48dp.png rename to resources/drawable-xxhdpi/ic_add_white_48dp.png diff --git a/res/drawable-xxhdpi/ic_av_next.png b/resources/drawable-xxhdpi/ic_av_next.png similarity index 100% rename from res/drawable-xxhdpi/ic_av_next.png rename to resources/drawable-xxhdpi/ic_av_next.png diff --git a/res/drawable-xxhdpi/ic_av_pause.png b/resources/drawable-xxhdpi/ic_av_pause.png similarity index 100% rename from res/drawable-xxhdpi/ic_av_pause.png rename to resources/drawable-xxhdpi/ic_av_pause.png diff --git a/res/drawable-xxhdpi/ic_av_pause_dark.png b/resources/drawable-xxhdpi/ic_av_pause_dark.png similarity index 100% rename from res/drawable-xxhdpi/ic_av_pause_dark.png rename to resources/drawable-xxhdpi/ic_av_pause_dark.png diff --git a/res/drawable-xxhdpi/ic_av_pause_light.png b/resources/drawable-xxhdpi/ic_av_pause_light.png similarity index 100% rename from res/drawable-xxhdpi/ic_av_pause_light.png rename to resources/drawable-xxhdpi/ic_av_pause_light.png diff --git a/res/drawable-xxhdpi/ic_av_pause_over_video.png b/resources/drawable-xxhdpi/ic_av_pause_over_video.png similarity index 100% rename from res/drawable-xxhdpi/ic_av_pause_over_video.png rename to resources/drawable-xxhdpi/ic_av_pause_over_video.png diff --git a/res/drawable-xxhdpi/ic_av_pause_over_video_large.png b/resources/drawable-xxhdpi/ic_av_pause_over_video_large.png similarity index 100% rename from res/drawable-xxhdpi/ic_av_pause_over_video_large.png rename to resources/drawable-xxhdpi/ic_av_pause_over_video_large.png diff --git a/res/drawable-xxhdpi/ic_av_play.png b/resources/drawable-xxhdpi/ic_av_play.png similarity index 100% rename from res/drawable-xxhdpi/ic_av_play.png rename to resources/drawable-xxhdpi/ic_av_play.png diff --git a/res/drawable-xxhdpi/ic_av_play_dark.png b/resources/drawable-xxhdpi/ic_av_play_dark.png similarity index 100% rename from res/drawable-xxhdpi/ic_av_play_dark.png rename to resources/drawable-xxhdpi/ic_av_play_dark.png diff --git a/res/drawable-xxhdpi/ic_av_play_light.png b/resources/drawable-xxhdpi/ic_av_play_light.png similarity index 100% rename from res/drawable-xxhdpi/ic_av_play_light.png rename to resources/drawable-xxhdpi/ic_av_play_light.png diff --git a/res/drawable-xxhdpi/ic_av_play_over_video.png b/resources/drawable-xxhdpi/ic_av_play_over_video.png similarity index 100% rename from res/drawable-xxhdpi/ic_av_play_over_video.png rename to resources/drawable-xxhdpi/ic_av_play_over_video.png diff --git a/res/drawable-xxhdpi/ic_av_play_over_video_large.png b/resources/drawable-xxhdpi/ic_av_play_over_video_large.png similarity index 100% rename from res/drawable-xxhdpi/ic_av_play_over_video_large.png rename to resources/drawable-xxhdpi/ic_av_play_over_video_large.png diff --git a/res/drawable-xxhdpi/ic_av_previous.png b/resources/drawable-xxhdpi/ic_av_previous.png similarity index 100% rename from res/drawable-xxhdpi/ic_av_previous.png rename to resources/drawable-xxhdpi/ic_av_previous.png diff --git a/res/drawable-xxhdpi/ic_av_stop.png b/resources/drawable-xxhdpi/ic_av_stop.png similarity index 100% rename from res/drawable-xxhdpi/ic_av_stop.png rename to resources/drawable-xxhdpi/ic_av_stop.png diff --git a/res/drawable-xxhdpi/ic_closed_caption_blue.png b/resources/drawable-xxhdpi/ic_closed_caption_blue.png similarity index 100% rename from res/drawable-xxhdpi/ic_closed_caption_blue.png rename to resources/drawable-xxhdpi/ic_closed_caption_blue.png diff --git a/res/drawable-xxhdpi/ic_closed_caption_googblue_24dp.png b/resources/drawable-xxhdpi/ic_closed_caption_googblue_24dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_closed_caption_googblue_24dp.png rename to resources/drawable-xxhdpi/ic_closed_caption_googblue_24dp.png diff --git a/res/drawable-xxhdpi/ic_closed_caption_googblue_36dp.png b/resources/drawable-xxhdpi/ic_closed_caption_googblue_36dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_closed_caption_googblue_36dp.png rename to resources/drawable-xxhdpi/ic_closed_caption_googblue_36dp.png diff --git a/res/drawable-xxhdpi/ic_closed_caption_googblue_48dp.png b/resources/drawable-xxhdpi/ic_closed_caption_googblue_48dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_closed_caption_googblue_48dp.png rename to resources/drawable-xxhdpi/ic_closed_caption_googblue_48dp.png diff --git a/res/drawable-xxhdpi/ic_closed_caption_grey.png b/resources/drawable-xxhdpi/ic_closed_caption_grey.png similarity index 100% rename from res/drawable-xxhdpi/ic_closed_caption_grey.png rename to resources/drawable-xxhdpi/ic_closed_caption_grey.png diff --git a/res/drawable-xxhdpi/ic_closed_caption_grey600_24dp.png b/resources/drawable-xxhdpi/ic_closed_caption_grey600_24dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_closed_caption_grey600_24dp.png rename to resources/drawable-xxhdpi/ic_closed_caption_grey600_24dp.png diff --git a/res/drawable-xxhdpi/ic_closed_caption_grey600_36dp.png b/resources/drawable-xxhdpi/ic_closed_caption_grey600_36dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_closed_caption_grey600_36dp.png rename to resources/drawable-xxhdpi/ic_closed_caption_grey600_36dp.png diff --git a/res/drawable-xxhdpi/ic_closed_caption_grey600_48dp.png b/resources/drawable-xxhdpi/ic_closed_caption_grey600_48dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_closed_caption_grey600_48dp.png rename to resources/drawable-xxhdpi/ic_closed_caption_grey600_48dp.png diff --git a/res/drawable-xxhdpi/ic_closed_caption_white.png b/resources/drawable-xxhdpi/ic_closed_caption_white.png similarity index 100% rename from res/drawable-xxhdpi/ic_closed_caption_white.png rename to resources/drawable-xxhdpi/ic_closed_caption_white.png diff --git a/res/drawable-xxhdpi/ic_closed_caption_white_24dp.png b/resources/drawable-xxhdpi/ic_closed_caption_white_24dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_closed_caption_white_24dp.png rename to resources/drawable-xxhdpi/ic_closed_caption_white_24dp.png diff --git a/res/drawable-xxhdpi/ic_closed_caption_white_36dp.png b/resources/drawable-xxhdpi/ic_closed_caption_white_36dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_closed_caption_white_36dp.png rename to resources/drawable-xxhdpi/ic_closed_caption_white_36dp.png diff --git a/res/drawable-xxhdpi/ic_closed_caption_white_48dp.png b/resources/drawable-xxhdpi/ic_closed_caption_white_48dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_closed_caption_white_48dp.png rename to resources/drawable-xxhdpi/ic_closed_caption_white_48dp.png diff --git a/res/drawable-xxhdpi/ic_device_access_volume_muted.png b/resources/drawable-xxhdpi/ic_device_access_volume_muted.png similarity index 100% rename from res/drawable-xxhdpi/ic_device_access_volume_muted.png rename to resources/drawable-xxhdpi/ic_device_access_volume_muted.png diff --git a/res/drawable-xxhdpi/ic_device_access_volume_on.png b/resources/drawable-xxhdpi/ic_device_access_volume_on.png similarity index 100% rename from res/drawable-xxhdpi/ic_device_access_volume_on.png rename to resources/drawable-xxhdpi/ic_device_access_volume_on.png diff --git a/res/drawable-xxhdpi/ic_drag_updown_grey_24dp.png b/resources/drawable-xxhdpi/ic_drag_updown_grey_24dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_drag_updown_grey_24dp.png rename to resources/drawable-xxhdpi/ic_drag_updown_grey_24dp.png diff --git a/res/drawable-xxhdpi/ic_drag_updown_white_24dp.png b/resources/drawable-xxhdpi/ic_drag_updown_white_24dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_drag_updown_white_24dp.png rename to resources/drawable-xxhdpi/ic_drag_updown_white_24dp.png diff --git a/res/drawable-xxhdpi/ic_launcher.png b/resources/drawable-xxhdpi/ic_launcher.png similarity index 100% rename from res/drawable-xxhdpi/ic_launcher.png rename to resources/drawable-xxhdpi/ic_launcher.png diff --git a/res/drawable-xxhdpi/ic_more_vert_grey600_24dp.png b/resources/drawable-xxhdpi/ic_more_vert_grey600_24dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_more_vert_grey600_24dp.png rename to resources/drawable-xxhdpi/ic_more_vert_grey600_24dp.png diff --git a/res/drawable-xxhdpi/ic_music_albumart.png b/resources/drawable-xxhdpi/ic_music_albumart.png similarity index 100% rename from res/drawable-xxhdpi/ic_music_albumart.png rename to resources/drawable-xxhdpi/ic_music_albumart.png diff --git a/res/drawable-xxhdpi/ic_pause_circle_white_80dp.png b/resources/drawable-xxhdpi/ic_pause_circle_white_80dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_pause_circle_white_80dp.png rename to resources/drawable-xxhdpi/ic_pause_circle_white_80dp.png diff --git a/res/drawable-xxhdpi/ic_pause_grey600_48dp.png b/resources/drawable-xxhdpi/ic_pause_grey600_48dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_pause_grey600_48dp.png rename to resources/drawable-xxhdpi/ic_pause_grey600_48dp.png diff --git a/res/drawable-xxhdpi/ic_play_arrow_grey600_48dp.png b/resources/drawable-xxhdpi/ic_play_arrow_grey600_48dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_play_arrow_grey600_48dp.png rename to resources/drawable-xxhdpi/ic_play_arrow_grey600_48dp.png diff --git a/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png b/resources/drawable-xxhdpi/ic_play_arrow_white_24dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_play_arrow_white_24dp.png rename to resources/drawable-xxhdpi/ic_play_arrow_white_24dp.png diff --git a/res/drawable-xxhdpi/ic_play_circle_outline_white.png b/resources/drawable-xxhdpi/ic_play_circle_outline_white.png similarity index 100% rename from res/drawable-xxhdpi/ic_play_circle_outline_white.png rename to resources/drawable-xxhdpi/ic_play_circle_outline_white.png diff --git a/res/drawable-xxhdpi/ic_play_circle_white_80dp.png b/resources/drawable-xxhdpi/ic_play_circle_white_80dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_play_circle_white_80dp.png rename to resources/drawable-xxhdpi/ic_play_circle_white_80dp.png diff --git a/res/drawable-xxhdpi/ic_playlist_white_24dp.png b/resources/drawable-xxhdpi/ic_playlist_white_24dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_playlist_white_24dp.png rename to resources/drawable-xxhdpi/ic_playlist_white_24dp.png diff --git a/res/drawable-xxhdpi/ic_remove_circle_white_24dp.png b/resources/drawable-xxhdpi/ic_remove_circle_white_24dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_remove_circle_white_24dp.png rename to resources/drawable-xxhdpi/ic_remove_circle_white_24dp.png diff --git a/res/drawable-xxhdpi/ic_skip_next_grey600_48dp.png b/resources/drawable-xxhdpi/ic_skip_next_grey600_48dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_skip_next_grey600_48dp.png rename to resources/drawable-xxhdpi/ic_skip_next_grey600_48dp.png diff --git a/res/drawable-xxhdpi/ic_skip_next_white_48dp.png b/resources/drawable-xxhdpi/ic_skip_next_white_48dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_skip_next_white_48dp.png rename to resources/drawable-xxhdpi/ic_skip_next_white_48dp.png diff --git a/res/drawable-xxhdpi/ic_skip_previous_grey600_48dp.png b/resources/drawable-xxhdpi/ic_skip_previous_grey600_48dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_skip_previous_grey600_48dp.png rename to resources/drawable-xxhdpi/ic_skip_previous_grey600_48dp.png diff --git a/res/drawable-xxhdpi/ic_skip_previous_white_48dp.png b/resources/drawable-xxhdpi/ic_skip_previous_white_48dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_skip_previous_white_48dp.png rename to resources/drawable-xxhdpi/ic_skip_previous_white_48dp.png diff --git a/res/drawable-xxhdpi/ic_stat_action_notification.png b/resources/drawable-xxhdpi/ic_stat_action_notification.png similarity index 100% rename from res/drawable-xxhdpi/ic_stat_action_notification.png rename to resources/drawable-xxhdpi/ic_stat_action_notification.png diff --git a/res/drawable-xxhdpi/ic_stat_hardware_headphones.png b/resources/drawable-xxhdpi/ic_stat_hardware_headphones.png similarity index 100% rename from res/drawable-xxhdpi/ic_stat_hardware_headphones.png rename to resources/drawable-xxhdpi/ic_stat_hardware_headphones.png diff --git a/res/drawable-xxhdpi/ic_stop_white_24dp.png b/resources/drawable-xxhdpi/ic_stop_white_24dp.png similarity index 100% rename from res/drawable-xxhdpi/ic_stop_white_24dp.png rename to resources/drawable-xxhdpi/ic_stop_white_24dp.png diff --git a/res/drawable-xxhdpi/list_focused_castvideo.9.png b/resources/drawable-xxhdpi/list_focused_castvideo.9.png similarity index 100% rename from res/drawable-xxhdpi/list_focused_castvideo.9.png rename to resources/drawable-xxhdpi/list_focused_castvideo.9.png diff --git a/res/drawable-xxhdpi/list_pressed_castvideo.9.png b/resources/drawable-xxhdpi/list_pressed_castvideo.9.png similarity index 100% rename from res/drawable-xxhdpi/list_pressed_castvideo.9.png rename to resources/drawable-xxhdpi/list_pressed_castvideo.9.png diff --git a/res/drawable-xxhdpi/menu_dropdown_panel_castvideo.9.png b/resources/drawable-xxhdpi/menu_dropdown_panel_castvideo.9.png similarity index 100% rename from res/drawable-xxhdpi/menu_dropdown_panel_castvideo.9.png rename to resources/drawable-xxhdpi/menu_dropdown_panel_castvideo.9.png diff --git a/res/drawable-xxhdpi/minibg.9.png b/resources/drawable-xxhdpi/minibg.9.png similarity index 100% rename from res/drawable-xxhdpi/minibg.9.png rename to resources/drawable-xxhdpi/minibg.9.png diff --git a/res/drawable-xxhdpi/mr_ic_media_route_disabled_holo_dark.png b/resources/drawable-xxhdpi/mr_ic_media_route_disabled_holo_dark.png similarity index 100% rename from res/drawable-xxhdpi/mr_ic_media_route_disabled_holo_dark.png rename to resources/drawable-xxhdpi/mr_ic_media_route_disabled_holo_dark.png diff --git a/res/drawable-xxhdpi/mr_ic_media_route_off_holo_dark.png b/resources/drawable-xxhdpi/mr_ic_media_route_off_holo_dark.png similarity index 100% rename from res/drawable-xxhdpi/mr_ic_media_route_off_holo_dark.png rename to resources/drawable-xxhdpi/mr_ic_media_route_off_holo_dark.png diff --git a/res/drawable-xxhdpi/mr_ic_media_route_on_0_holo_dark.png b/resources/drawable-xxhdpi/mr_ic_media_route_on_0_holo_dark.png similarity index 100% rename from res/drawable-xxhdpi/mr_ic_media_route_on_0_holo_dark.png rename to resources/drawable-xxhdpi/mr_ic_media_route_on_0_holo_dark.png diff --git a/res/drawable-xxhdpi/mr_ic_media_route_on_1_holo_dark.png b/resources/drawable-xxhdpi/mr_ic_media_route_on_1_holo_dark.png similarity index 100% rename from res/drawable-xxhdpi/mr_ic_media_route_on_1_holo_dark.png rename to resources/drawable-xxhdpi/mr_ic_media_route_on_1_holo_dark.png diff --git a/res/drawable-xxhdpi/mr_ic_media_route_on_2_holo_dark.png b/resources/drawable-xxhdpi/mr_ic_media_route_on_2_holo_dark.png similarity index 100% rename from res/drawable-xxhdpi/mr_ic_media_route_on_2_holo_dark.png rename to resources/drawable-xxhdpi/mr_ic_media_route_on_2_holo_dark.png diff --git a/res/drawable-xxhdpi/mr_ic_media_route_on_holo_dark.png b/resources/drawable-xxhdpi/mr_ic_media_route_on_holo_dark.png similarity index 100% rename from res/drawable-xxhdpi/mr_ic_media_route_on_holo_dark.png rename to resources/drawable-xxhdpi/mr_ic_media_route_on_holo_dark.png diff --git a/res/drawable-xxhdpi/progress_bg_castvideo.9.png b/resources/drawable-xxhdpi/progress_bg_castvideo.9.png similarity index 100% rename from res/drawable-xxhdpi/progress_bg_castvideo.9.png rename to resources/drawable-xxhdpi/progress_bg_castvideo.9.png diff --git a/res/drawable-xxhdpi/progress_primary_castvideo.9.png b/resources/drawable-xxhdpi/progress_primary_castvideo.9.png similarity index 100% rename from res/drawable-xxhdpi/progress_primary_castvideo.9.png rename to resources/drawable-xxhdpi/progress_primary_castvideo.9.png diff --git a/res/drawable-xxhdpi/progress_secondary_castvideo.9.png b/resources/drawable-xxhdpi/progress_secondary_castvideo.9.png similarity index 100% rename from res/drawable-xxhdpi/progress_secondary_castvideo.9.png rename to resources/drawable-xxhdpi/progress_secondary_castvideo.9.png diff --git a/res/drawable-xxhdpi/shadow6.9.png b/resources/drawable-xxhdpi/shadow6.9.png similarity index 100% rename from res/drawable-xxhdpi/shadow6.9.png rename to resources/drawable-xxhdpi/shadow6.9.png diff --git a/res/drawable-xxhdpi/spinner_ab_default_castvideo.9.png b/resources/drawable-xxhdpi/spinner_ab_default_castvideo.9.png similarity index 100% rename from res/drawable-xxhdpi/spinner_ab_default_castvideo.9.png rename to resources/drawable-xxhdpi/spinner_ab_default_castvideo.9.png diff --git a/res/drawable-xxhdpi/spinner_ab_disabled_castvideo.9.png b/resources/drawable-xxhdpi/spinner_ab_disabled_castvideo.9.png similarity index 100% rename from res/drawable-xxhdpi/spinner_ab_disabled_castvideo.9.png rename to resources/drawable-xxhdpi/spinner_ab_disabled_castvideo.9.png diff --git a/res/drawable-xxhdpi/spinner_ab_focused_castvideo.9.png b/resources/drawable-xxhdpi/spinner_ab_focused_castvideo.9.png similarity index 100% rename from res/drawable-xxhdpi/spinner_ab_focused_castvideo.9.png rename to resources/drawable-xxhdpi/spinner_ab_focused_castvideo.9.png diff --git a/res/drawable-xxhdpi/spinner_ab_pressed_castvideo.9.png b/resources/drawable-xxhdpi/spinner_ab_pressed_castvideo.9.png similarity index 100% rename from res/drawable-xxhdpi/spinner_ab_pressed_castvideo.9.png rename to resources/drawable-xxhdpi/spinner_ab_pressed_castvideo.9.png diff --git a/res/drawable-xxhdpi/tab_selected_castvideo.9.png b/resources/drawable-xxhdpi/tab_selected_castvideo.9.png similarity index 100% rename from res/drawable-xxhdpi/tab_selected_castvideo.9.png rename to resources/drawable-xxhdpi/tab_selected_castvideo.9.png diff --git a/res/drawable-xxhdpi/tab_selected_focused_castvideo.9.png b/resources/drawable-xxhdpi/tab_selected_focused_castvideo.9.png similarity index 100% rename from res/drawable-xxhdpi/tab_selected_focused_castvideo.9.png rename to resources/drawable-xxhdpi/tab_selected_focused_castvideo.9.png diff --git a/res/drawable-xxhdpi/tab_selected_pressed_castvideo.9.png b/resources/drawable-xxhdpi/tab_selected_pressed_castvideo.9.png similarity index 100% rename from res/drawable-xxhdpi/tab_selected_pressed_castvideo.9.png rename to resources/drawable-xxhdpi/tab_selected_pressed_castvideo.9.png diff --git a/res/drawable-xxhdpi/tab_unselected_castvideo.9.png b/resources/drawable-xxhdpi/tab_unselected_castvideo.9.png similarity index 100% rename from res/drawable-xxhdpi/tab_unselected_castvideo.9.png rename to resources/drawable-xxhdpi/tab_unselected_castvideo.9.png diff --git a/res/drawable-xxhdpi/tab_unselected_focused_castvideo.9.png b/resources/drawable-xxhdpi/tab_unselected_focused_castvideo.9.png similarity index 100% rename from res/drawable-xxhdpi/tab_unselected_focused_castvideo.9.png rename to resources/drawable-xxhdpi/tab_unselected_focused_castvideo.9.png diff --git a/res/drawable-xxhdpi/tab_unselected_pressed_castvideo.9.png b/resources/drawable-xxhdpi/tab_unselected_pressed_castvideo.9.png similarity index 100% rename from res/drawable-xxhdpi/tab_unselected_pressed_castvideo.9.png rename to resources/drawable-xxhdpi/tab_unselected_pressed_castvideo.9.png diff --git a/res/drawable-xxxhdpi/arrow_down_white.png b/resources/drawable-xxxhdpi/arrow_down_white.png similarity index 100% rename from res/drawable-xxxhdpi/arrow_down_white.png rename to resources/drawable-xxxhdpi/arrow_down_white.png diff --git a/res/drawable-xxxhdpi/ic_add_circle_outline_white.png b/resources/drawable-xxxhdpi/ic_add_circle_outline_white.png similarity index 100% rename from res/drawable-xxxhdpi/ic_add_circle_outline_white.png rename to resources/drawable-xxxhdpi/ic_add_circle_outline_white.png diff --git a/res/drawable-xxxhdpi/ic_add_white_18dp.png b/resources/drawable-xxxhdpi/ic_add_white_18dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_add_white_18dp.png rename to resources/drawable-xxxhdpi/ic_add_white_18dp.png diff --git a/res/drawable-xxxhdpi/ic_add_white_24dp.png b/resources/drawable-xxxhdpi/ic_add_white_24dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_add_white_24dp.png rename to resources/drawable-xxxhdpi/ic_add_white_24dp.png diff --git a/res/drawable-xxxhdpi/ic_add_white_36dp.png b/resources/drawable-xxxhdpi/ic_add_white_36dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_add_white_36dp.png rename to resources/drawable-xxxhdpi/ic_add_white_36dp.png diff --git a/res/drawable-xxxhdpi/ic_add_white_48dp.png b/resources/drawable-xxxhdpi/ic_add_white_48dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_add_white_48dp.png rename to resources/drawable-xxxhdpi/ic_add_white_48dp.png diff --git a/res/drawable-xxxhdpi/ic_closed_caption_googblue_24dp.png b/resources/drawable-xxxhdpi/ic_closed_caption_googblue_24dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_closed_caption_googblue_24dp.png rename to resources/drawable-xxxhdpi/ic_closed_caption_googblue_24dp.png diff --git a/res/drawable-xxxhdpi/ic_closed_caption_googblue_36dp.png b/resources/drawable-xxxhdpi/ic_closed_caption_googblue_36dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_closed_caption_googblue_36dp.png rename to resources/drawable-xxxhdpi/ic_closed_caption_googblue_36dp.png diff --git a/res/drawable-xxxhdpi/ic_closed_caption_googblue_48dp.png b/resources/drawable-xxxhdpi/ic_closed_caption_googblue_48dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_closed_caption_googblue_48dp.png rename to resources/drawable-xxxhdpi/ic_closed_caption_googblue_48dp.png diff --git a/res/drawable-xxxhdpi/ic_closed_caption_grey600_24dp.png b/resources/drawable-xxxhdpi/ic_closed_caption_grey600_24dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_closed_caption_grey600_24dp.png rename to resources/drawable-xxxhdpi/ic_closed_caption_grey600_24dp.png diff --git a/res/drawable-xxxhdpi/ic_closed_caption_grey600_36dp.png b/resources/drawable-xxxhdpi/ic_closed_caption_grey600_36dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_closed_caption_grey600_36dp.png rename to resources/drawable-xxxhdpi/ic_closed_caption_grey600_36dp.png diff --git a/res/drawable-xxxhdpi/ic_closed_caption_grey600_48dp.png b/resources/drawable-xxxhdpi/ic_closed_caption_grey600_48dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_closed_caption_grey600_48dp.png rename to resources/drawable-xxxhdpi/ic_closed_caption_grey600_48dp.png diff --git a/res/drawable-xxxhdpi/ic_closed_caption_white_24dp.png b/resources/drawable-xxxhdpi/ic_closed_caption_white_24dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_closed_caption_white_24dp.png rename to resources/drawable-xxxhdpi/ic_closed_caption_white_24dp.png diff --git a/res/drawable-xxxhdpi/ic_closed_caption_white_36dp.png b/resources/drawable-xxxhdpi/ic_closed_caption_white_36dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_closed_caption_white_36dp.png rename to resources/drawable-xxxhdpi/ic_closed_caption_white_36dp.png diff --git a/res/drawable-xxxhdpi/ic_closed_caption_white_48dp.png b/resources/drawable-xxxhdpi/ic_closed_caption_white_48dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_closed_caption_white_48dp.png rename to resources/drawable-xxxhdpi/ic_closed_caption_white_48dp.png diff --git a/res/drawable-xxxhdpi/ic_drag_updown_grey_24dp.png b/resources/drawable-xxxhdpi/ic_drag_updown_grey_24dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_drag_updown_grey_24dp.png rename to resources/drawable-xxxhdpi/ic_drag_updown_grey_24dp.png diff --git a/res/drawable-xxxhdpi/ic_drag_updown_white_24dp.png b/resources/drawable-xxxhdpi/ic_drag_updown_white_24dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_drag_updown_white_24dp.png rename to resources/drawable-xxxhdpi/ic_drag_updown_white_24dp.png diff --git a/res/drawable-xxxhdpi/ic_launcher.png b/resources/drawable-xxxhdpi/ic_launcher.png similarity index 100% rename from res/drawable-xxxhdpi/ic_launcher.png rename to resources/drawable-xxxhdpi/ic_launcher.png diff --git a/res/drawable-xxxhdpi/ic_more_vert_grey600_24dp.png b/resources/drawable-xxxhdpi/ic_more_vert_grey600_24dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_more_vert_grey600_24dp.png rename to resources/drawable-xxxhdpi/ic_more_vert_grey600_24dp.png diff --git a/res/drawable-xxxhdpi/ic_pause_circle_white_80dp.png b/resources/drawable-xxxhdpi/ic_pause_circle_white_80dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_pause_circle_white_80dp.png rename to resources/drawable-xxxhdpi/ic_pause_circle_white_80dp.png diff --git a/res/drawable-xxxhdpi/ic_pause_grey600_48dp.png b/resources/drawable-xxxhdpi/ic_pause_grey600_48dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_pause_grey600_48dp.png rename to resources/drawable-xxxhdpi/ic_pause_grey600_48dp.png diff --git a/res/drawable-xxxhdpi/ic_play_arrow_grey600_48dp.png b/resources/drawable-xxxhdpi/ic_play_arrow_grey600_48dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_play_arrow_grey600_48dp.png rename to resources/drawable-xxxhdpi/ic_play_arrow_grey600_48dp.png diff --git a/res/drawable-xxxhdpi/ic_play_circle_outline_white.png b/resources/drawable-xxxhdpi/ic_play_circle_outline_white.png similarity index 100% rename from res/drawable-xxxhdpi/ic_play_circle_outline_white.png rename to resources/drawable-xxxhdpi/ic_play_circle_outline_white.png diff --git a/res/drawable-xxxhdpi/ic_play_circle_white_80dp.png b/resources/drawable-xxxhdpi/ic_play_circle_white_80dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_play_circle_white_80dp.png rename to resources/drawable-xxxhdpi/ic_play_circle_white_80dp.png diff --git a/res/drawable-xxxhdpi/ic_playlist_white_24dp.png b/resources/drawable-xxxhdpi/ic_playlist_white_24dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_playlist_white_24dp.png rename to resources/drawable-xxxhdpi/ic_playlist_white_24dp.png diff --git a/res/drawable-xxxhdpi/ic_remove_circle_white_24dp.png b/resources/drawable-xxxhdpi/ic_remove_circle_white_24dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_remove_circle_white_24dp.png rename to resources/drawable-xxxhdpi/ic_remove_circle_white_24dp.png diff --git a/res/drawable-xxxhdpi/ic_skip_next_grey600_48dp.png b/resources/drawable-xxxhdpi/ic_skip_next_grey600_48dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_skip_next_grey600_48dp.png rename to resources/drawable-xxxhdpi/ic_skip_next_grey600_48dp.png diff --git a/res/drawable-xxxhdpi/ic_skip_next_white_48dp.png b/resources/drawable-xxxhdpi/ic_skip_next_white_48dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_skip_next_white_48dp.png rename to resources/drawable-xxxhdpi/ic_skip_next_white_48dp.png diff --git a/res/drawable-xxxhdpi/ic_skip_previous_grey600_48dp.png b/resources/drawable-xxxhdpi/ic_skip_previous_grey600_48dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_skip_previous_grey600_48dp.png rename to resources/drawable-xxxhdpi/ic_skip_previous_grey600_48dp.png diff --git a/res/drawable-xxxhdpi/ic_skip_previous_white_48dp.png b/resources/drawable-xxxhdpi/ic_skip_previous_white_48dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_skip_previous_white_48dp.png rename to resources/drawable-xxxhdpi/ic_skip_previous_white_48dp.png diff --git a/res/drawable-xxxhdpi/ic_stop_white_24dp.png b/resources/drawable-xxxhdpi/ic_stop_white_24dp.png similarity index 100% rename from res/drawable-xxxhdpi/ic_stop_white_24dp.png rename to resources/drawable-xxxhdpi/ic_stop_white_24dp.png diff --git a/res/drawable/ab_background_textured_castvideo.xml b/resources/drawable/ab_background_textured_castvideo.xml similarity index 100% rename from res/drawable/ab_background_textured_castvideo.xml rename to resources/drawable/ab_background_textured_castvideo.xml diff --git a/res/drawable/ab_background_textured_democast.xml b/resources/drawable/ab_background_textured_democast.xml similarity index 100% rename from res/drawable/ab_background_textured_democast.xml rename to resources/drawable/ab_background_textured_democast.xml diff --git a/res/drawable/actionbar_bg_gradient_light.xml b/resources/drawable/actionbar_bg_gradient_light.xml similarity index 100% rename from res/drawable/actionbar_bg_gradient_light.xml rename to resources/drawable/actionbar_bg_gradient_light.xml diff --git a/res/drawable/apptheme_scrubber_control_selector_holo_light.xml b/resources/drawable/apptheme_scrubber_control_selector_holo_light.xml similarity index 100% rename from res/drawable/apptheme_scrubber_control_selector_holo_light.xml rename to resources/drawable/apptheme_scrubber_control_selector_holo_light.xml diff --git a/res/drawable/apptheme_scrubber_progress_horizontal_holo_light.xml b/resources/drawable/apptheme_scrubber_progress_horizontal_holo_light.xml similarity index 100% rename from res/drawable/apptheme_scrubber_progress_horizontal_holo_light.xml rename to resources/drawable/apptheme_scrubber_progress_horizontal_holo_light.xml diff --git a/res/drawable/bg_item_dragging_active_state.xml b/resources/drawable/bg_item_dragging_active_state.xml similarity index 100% rename from res/drawable/bg_item_dragging_active_state.xml rename to resources/drawable/bg_item_dragging_active_state.xml diff --git a/res/drawable/bg_item_dragging_state.xml b/resources/drawable/bg_item_dragging_state.xml similarity index 100% rename from res/drawable/bg_item_dragging_state.xml rename to resources/drawable/bg_item_dragging_state.xml diff --git a/res/drawable/bg_item_normal_state.xml b/resources/drawable/bg_item_normal_state.xml similarity index 100% rename from res/drawable/bg_item_normal_state.xml rename to resources/drawable/bg_item_normal_state.xml diff --git a/res/drawable/bg_item_swiping_active_state.xml b/resources/drawable/bg_item_swiping_active_state.xml similarity index 100% rename from res/drawable/bg_item_swiping_active_state.xml rename to resources/drawable/bg_item_swiping_active_state.xml diff --git a/res/drawable/bg_item_swiping_state.xml b/resources/drawable/bg_item_swiping_state.xml similarity index 100% rename from res/drawable/bg_item_swiping_state.xml rename to resources/drawable/bg_item_swiping_state.xml diff --git a/res/drawable/bg_item_upcoming_state.xml b/resources/drawable/bg_item_upcoming_state.xml similarity index 100% rename from res/drawable/bg_item_upcoming_state.xml rename to resources/drawable/bg_item_upcoming_state.xml diff --git a/res/drawable/bg_swipe_item_right.xml b/resources/drawable/bg_swipe_item_right.xml similarity index 100% rename from res/drawable/bg_swipe_item_right.xml rename to resources/drawable/bg_swipe_item_right.xml diff --git a/res/drawable/btn_cab_done_castvideo.xml b/resources/drawable/btn_cab_done_castvideo.xml similarity index 100% rename from res/drawable/btn_cab_done_castvideo.xml rename to resources/drawable/btn_cab_done_castvideo.xml diff --git a/res/drawable/btn_cab_done_democast.xml b/resources/drawable/btn_cab_done_democast.xml similarity index 100% rename from res/drawable/btn_cab_done_democast.xml rename to resources/drawable/btn_cab_done_democast.xml diff --git a/res/drawable/cast_player_bg_gradient_light.xml b/resources/drawable/cast_player_bg_gradient_light.xml similarity index 100% rename from res/drawable/cast_player_bg_gradient_light.xml rename to resources/drawable/cast_player_bg_gradient_light.xml diff --git a/res/drawable/cast_player_bg_gradient_light_overall.xml b/resources/drawable/cast_player_bg_gradient_light_overall.xml similarity index 100% rename from res/drawable/cast_player_bg_gradient_light_overall.xml rename to resources/drawable/cast_player_bg_gradient_light_overall.xml diff --git a/res/drawable/cc.xml b/resources/drawable/cc.xml similarity index 100% rename from res/drawable/cc.xml rename to resources/drawable/cc.xml diff --git a/res/drawable/container_gradient_bottom.xml b/resources/drawable/container_gradient_bottom.xml similarity index 100% rename from res/drawable/container_gradient_bottom.xml rename to resources/drawable/container_gradient_bottom.xml diff --git a/res/drawable/container_gradient_top.xml b/resources/drawable/container_gradient_top.xml similarity index 100% rename from res/drawable/container_gradient_top.xml rename to resources/drawable/container_gradient_top.xml diff --git a/res/drawable/default_video.png b/resources/drawable/default_video.png similarity index 100% rename from res/drawable/default_video.png rename to resources/drawable/default_video.png diff --git a/res/drawable/horizontal_divider.xml b/resources/drawable/horizontal_divider.xml similarity index 100% rename from res/drawable/horizontal_divider.xml rename to resources/drawable/horizontal_divider.xml diff --git a/res/drawable/ic_media_route_connecting_holo_dark.xml b/resources/drawable/ic_media_route_connecting_holo_dark.xml similarity index 100% rename from res/drawable/ic_media_route_connecting_holo_dark.xml rename to resources/drawable/ic_media_route_connecting_holo_dark.xml diff --git a/res/drawable/ic_mini_controller_upcoming_play.xml b/resources/drawable/ic_mini_controller_upcoming_play.xml similarity index 100% rename from res/drawable/ic_mini_controller_upcoming_play.xml rename to resources/drawable/ic_mini_controller_upcoming_play.xml diff --git a/res/drawable/ic_mini_controller_upcoming_stop.xml b/resources/drawable/ic_mini_controller_upcoming_stop.xml similarity index 100% rename from res/drawable/ic_mini_controller_upcoming_stop.xml rename to resources/drawable/ic_mini_controller_upcoming_stop.xml diff --git a/res/drawable/list_divider.xml b/resources/drawable/list_divider.xml similarity index 100% rename from res/drawable/list_divider.xml rename to resources/drawable/list_divider.xml diff --git a/res/drawable/mr_ic_media_route_connecting_holo_dark.xml b/resources/drawable/mr_ic_media_route_connecting_holo_dark.xml similarity index 100% rename from res/drawable/mr_ic_media_route_connecting_holo_dark.xml rename to resources/drawable/mr_ic_media_route_connecting_holo_dark.xml diff --git a/res/drawable/player_bg_gradient_dark.xml b/resources/drawable/player_bg_gradient_dark.xml similarity index 100% rename from res/drawable/player_bg_gradient_dark.xml rename to resources/drawable/player_bg_gradient_dark.xml diff --git a/res/drawable/player_bg_gradient_light.xml b/resources/drawable/player_bg_gradient_light.xml similarity index 100% rename from res/drawable/player_bg_gradient_light.xml rename to resources/drawable/player_bg_gradient_light.xml diff --git a/res/drawable/pressed_background_democast.xml b/resources/drawable/pressed_background_democast.xml similarity index 100% rename from res/drawable/pressed_background_democast.xml rename to resources/drawable/pressed_background_democast.xml diff --git a/res/drawable/progress_horizontal_castvideo.xml b/resources/drawable/progress_horizontal_castvideo.xml similarity index 100% rename from res/drawable/progress_horizontal_castvideo.xml rename to resources/drawable/progress_horizontal_castvideo.xml diff --git a/res/drawable/progress_horizontal_democast.xml b/resources/drawable/progress_horizontal_democast.xml similarity index 100% rename from res/drawable/progress_horizontal_democast.xml rename to resources/drawable/progress_horizontal_democast.xml diff --git a/res/drawable/selectable_background_castvideo.xml b/resources/drawable/selectable_background_castvideo.xml similarity index 100% rename from res/drawable/selectable_background_castvideo.xml rename to resources/drawable/selectable_background_castvideo.xml diff --git a/res/drawable/selectable_background_democast.xml b/resources/drawable/selectable_background_democast.xml similarity index 100% rename from res/drawable/selectable_background_democast.xml rename to resources/drawable/selectable_background_democast.xml diff --git a/res/drawable/shadow7.9.png b/resources/drawable/shadow7.9.png similarity index 100% rename from res/drawable/shadow7.9.png rename to resources/drawable/shadow7.9.png diff --git a/res/drawable/skip_next_button.xml b/resources/drawable/skip_next_button.xml similarity index 100% rename from res/drawable/skip_next_button.xml rename to resources/drawable/skip_next_button.xml diff --git a/res/drawable/skip_previous_button.xml b/resources/drawable/skip_previous_button.xml similarity index 100% rename from res/drawable/skip_previous_button.xml rename to resources/drawable/skip_previous_button.xml diff --git a/res/drawable/spinner_background_ab_castvideo.xml b/resources/drawable/spinner_background_ab_castvideo.xml similarity index 100% rename from res/drawable/spinner_background_ab_castvideo.xml rename to resources/drawable/spinner_background_ab_castvideo.xml diff --git a/res/drawable/spinner_background_ab_democast.xml b/resources/drawable/spinner_background_ab_democast.xml similarity index 100% rename from res/drawable/spinner_background_ab_democast.xml rename to resources/drawable/spinner_background_ab_democast.xml diff --git a/res/drawable/tab_indicator_ab_castvideo.xml b/resources/drawable/tab_indicator_ab_castvideo.xml similarity index 100% rename from res/drawable/tab_indicator_ab_castvideo.xml rename to resources/drawable/tab_indicator_ab_castvideo.xml diff --git a/res/drawable/tab_indicator_ab_democast.xml b/resources/drawable/tab_indicator_ab_democast.xml similarity index 100% rename from res/drawable/tab_indicator_ab_democast.xml rename to resources/drawable/tab_indicator_ab_democast.xml diff --git a/res/drawable/text_selector.xml b/resources/drawable/text_selector.xml similarity index 100% rename from res/drawable/text_selector.xml rename to resources/drawable/text_selector.xml diff --git a/res/drawable/vertical_divider.xml b/resources/drawable/vertical_divider.xml similarity index 100% rename from res/drawable/vertical_divider.xml rename to resources/drawable/vertical_divider.xml diff --git a/res/layout/browse_row.xml b/resources/layout/browse_row.xml similarity index 100% rename from res/layout/browse_row.xml rename to resources/layout/browse_row.xml diff --git a/res/layout/fragment_recycler_list_view.xml b/resources/layout/fragment_recycler_list_view.xml similarity index 100% rename from res/layout/fragment_recycler_list_view.xml rename to resources/layout/fragment_recycler_list_view.xml diff --git a/res/layout/player_activity.xml b/resources/layout/player_activity.xml similarity index 100% rename from res/layout/player_activity.xml rename to resources/layout/player_activity.xml diff --git a/res/layout/queue_activity.xml b/resources/layout/queue_activity.xml similarity index 100% rename from res/layout/queue_activity.xml rename to resources/layout/queue_activity.xml diff --git a/res/layout/queue_row.xml b/resources/layout/queue_row.xml similarity index 100% rename from res/layout/queue_row.xml rename to resources/layout/queue_row.xml diff --git a/res/layout/video_browser.xml b/resources/layout/video_browser.xml similarity index 100% rename from res/layout/video_browser.xml rename to resources/layout/video_browser.xml diff --git a/res/layout/video_browser_fragment.xml b/resources/layout/video_browser_fragment.xml similarity index 100% rename from res/layout/video_browser_fragment.xml rename to resources/layout/video_browser_fragment.xml diff --git a/res/menu/browse.xml b/resources/menu/browse.xml similarity index 100% rename from res/menu/browse.xml rename to resources/menu/browse.xml diff --git a/res/menu/detached_popup_add_to_queue.xml b/resources/menu/detached_popup_add_to_queue.xml similarity index 100% rename from res/menu/detached_popup_add_to_queue.xml rename to resources/menu/detached_popup_add_to_queue.xml diff --git a/res/menu/expanded_controller.xml b/resources/menu/expanded_controller.xml similarity index 100% rename from res/menu/expanded_controller.xml rename to resources/menu/expanded_controller.xml diff --git a/res/menu/player.xml b/resources/menu/player.xml similarity index 100% rename from res/menu/player.xml rename to resources/menu/player.xml diff --git a/res/menu/popup_add_to_queue.xml b/resources/menu/popup_add_to_queue.xml similarity index 100% rename from res/menu/popup_add_to_queue.xml rename to resources/menu/popup_add_to_queue.xml diff --git a/res/menu/queue_menu.xml b/resources/menu/queue_menu.xml similarity index 100% rename from res/menu/queue_menu.xml rename to resources/menu/queue_menu.xml diff --git a/res/mipmap-hdpi/ic_launcher.png b/resources/mipmap-hdpi/ic_launcher.png similarity index 100% rename from res/mipmap-hdpi/ic_launcher.png rename to resources/mipmap-hdpi/ic_launcher.png diff --git a/res/mipmap-mdpi/ic_launcher.png b/resources/mipmap-mdpi/ic_launcher.png similarity index 100% rename from res/mipmap-mdpi/ic_launcher.png rename to resources/mipmap-mdpi/ic_launcher.png diff --git a/res/mipmap-xhdpi/ic_launcher.png b/resources/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from res/mipmap-xhdpi/ic_launcher.png rename to resources/mipmap-xhdpi/ic_launcher.png diff --git a/res/mipmap-xxhdpi/ic_launcher.png b/resources/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from res/mipmap-xxhdpi/ic_launcher.png rename to resources/mipmap-xxhdpi/ic_launcher.png diff --git a/res/mipmap-xxxhdpi/ic_launcher.png b/resources/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from res/mipmap-xxxhdpi/ic_launcher.png rename to resources/mipmap-xxxhdpi/ic_launcher.png diff --git a/res/values-sw600dp/dimens.xml b/resources/values-sw600dp/dimens.xml similarity index 100% rename from res/values-sw600dp/dimens.xml rename to resources/values-sw600dp/dimens.xml diff --git a/res/values-sw600dp/integers.xml b/resources/values-sw600dp/integers.xml similarity index 100% rename from res/values-sw600dp/integers.xml rename to resources/values-sw600dp/integers.xml diff --git a/res/values-sw720dp-land/dimens.xml b/resources/values-sw720dp-land/dimens.xml similarity index 100% rename from res/values-sw720dp-land/dimens.xml rename to resources/values-sw720dp-land/dimens.xml diff --git a/res/values-sw720dp-land/integers.xml b/resources/values-sw720dp-land/integers.xml similarity index 100% rename from res/values-sw720dp-land/integers.xml rename to resources/values-sw720dp-land/integers.xml diff --git a/res/values-v11/styles.xml b/resources/values-v11/styles.xml similarity index 100% rename from res/values-v11/styles.xml rename to resources/values-v11/styles.xml diff --git a/res/values-v21/dimens.xml b/resources/values-v21/dimens.xml similarity index 100% rename from res/values-v21/dimens.xml rename to resources/values-v21/dimens.xml diff --git a/res/values-v21/styles.xml b/resources/values-v21/styles.xml similarity index 100% rename from res/values-v21/styles.xml rename to resources/values-v21/styles.xml diff --git a/res/values/arrays.xml b/resources/values/arrays.xml similarity index 100% rename from res/values/arrays.xml rename to resources/values/arrays.xml diff --git a/res/values/cast_test.xml b/resources/values/cast_test.xml similarity index 100% rename from res/values/cast_test.xml rename to resources/values/cast_test.xml diff --git a/res/values/colors.xml b/resources/values/colors.xml similarity index 100% rename from res/values/colors.xml rename to resources/values/colors.xml diff --git a/res/values/colors_democast.xml b/resources/values/colors_democast.xml similarity index 100% rename from res/values/colors_democast.xml rename to resources/values/colors_democast.xml diff --git a/res/values/dimens.xml b/resources/values/dimens.xml similarity index 100% rename from res/values/dimens.xml rename to resources/values/dimens.xml diff --git a/res/values/integers.xml b/resources/values/integers.xml similarity index 100% rename from res/values/integers.xml rename to resources/values/integers.xml diff --git a/res/values/strings.xml b/resources/values/strings.xml similarity index 100% rename from res/values/strings.xml rename to resources/values/strings.xml diff --git a/res/values/styles_castvideo.xml b/resources/values/styles_castvideo.xml similarity index 100% rename from res/values/styles_castvideo.xml rename to resources/values/styles_castvideo.xml diff --git a/res/xml/application_preference.xml b/resources/xml/application_preference.xml similarity index 100% rename from res/xml/application_preference.xml rename to resources/xml/application_preference.xml diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..d4a3b5f --- /dev/null +++ b/settings.gradle @@ -0,0 +1,3 @@ +include ':app-java' +include ':app-kotlin' +rootProject.name='CastVideos-android'