From 245b58124eeca6c6a72f072c10ad5d8dfb9e0d93 Mon Sep 17 00:00:00 2001 From: sigmabeta Date: Mon, 20 Jul 2015 22:46:12 -0400 Subject: [PATCH] Android TV: Add settings row, enabling access to other screens. --- .../activities/EmulationActivity.java | 4 +- .../dolphinemu/activities/MainActivity.java | 3 +- .../dolphinemu/activities/TvMainActivity.java | 117 +++++++++++++++--- ...mePresenter.java => GameRowPresenter.java} | 48 ++++--- .../adapters/SettingsRowPresenter.java | 47 +++++++ .../dolphinemu/model/TvSettingsItem.java | 31 +++++ .../viewholders/TvGameViewHolder.java | 7 +- .../viewholders/TvSettingsViewHolder.java | 23 ++++ .../app/src/main/res/drawable/ic_add_tv.png | Bin 0 -> 1708 bytes .../src/main/res/drawable/ic_refresh_tv.png | Bin 0 -> 1969 bytes .../src/main/res/drawable/ic_settings_tv.png | Bin 0 -> 2248 bytes 11 files changed, 232 insertions(+), 48 deletions(-) rename Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/{GamePresenter.java => GameRowPresenter.java} (81%) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/SettingsRowPresenter.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/TvSettingsItem.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/TvSettingsViewHolder.java create mode 100644 Source/Android/app/src/main/res/drawable/ic_add_tv.png create mode 100644 Source/Android/app/src/main/res/drawable/ic_refresh_tv.png create mode 100644 Source/Android/app/src/main/res/drawable/ic_settings_tv.png diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java index c4cc9e1f3a..04b5149a0e 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java @@ -50,8 +50,8 @@ public final class EmulationActivity extends AppCompatActivity private boolean mSystemUiVisible; private boolean mMenuVisible; - private static Interpolator sDecelerator = new DecelerateInterpolator(); - private static Interpolator sAccelerator = new AccelerateInterpolator(); + private static final Interpolator sDecelerator = new DecelerateInterpolator(); + private static final Interpolator sAccelerator = new AccelerateInterpolator(); /** * Handlers are a way to pass a message to an Activity telling it to do something diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/MainActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/MainActivity.java index a32b65d566..9ffa651cc2 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/MainActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/MainActivity.java @@ -35,7 +35,7 @@ import org.dolphinemu.dolphinemu.services.AssetCopyService; */ public final class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks { - private static final int REQUEST_ADD_DIRECTORY = 1; + public static final int REQUEST_ADD_DIRECTORY = 1; public static final int REQUEST_EMULATE_GAME = 2; /** @@ -139,6 +139,7 @@ public final class MainActivity extends AppCompatActivity implements LoaderManag { fragment.refreshScreenshotAtPosition(resultCode); } + break; } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/TvMainActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/TvMainActivity.java index 0e716d6503..1f58698a7e 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/TvMainActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/TvMainActivity.java @@ -17,12 +17,15 @@ import android.support.v17.leanback.widget.OnItemViewClickedListener; import android.support.v17.leanback.widget.Presenter; import android.support.v17.leanback.widget.Row; import android.support.v17.leanback.widget.RowPresenter; +import android.widget.Toast; import org.dolphinemu.dolphinemu.R; -import org.dolphinemu.dolphinemu.adapters.GamePresenter; +import org.dolphinemu.dolphinemu.adapters.GameRowPresenter; +import org.dolphinemu.dolphinemu.adapters.SettingsRowPresenter; import org.dolphinemu.dolphinemu.model.Game; import org.dolphinemu.dolphinemu.model.GameDatabase; import org.dolphinemu.dolphinemu.model.GameProvider; +import org.dolphinemu.dolphinemu.model.TvSettingsItem; import org.dolphinemu.dolphinemu.viewholders.TvGameViewHolder; public final class TvMainActivity extends Activity @@ -56,24 +59,88 @@ public final class TvMainActivity extends Activity @Override public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, RowPresenter.ViewHolder rowViewHolder, Row row) { - TvGameViewHolder holder = (TvGameViewHolder) itemViewHolder; - // Start the emulation activity and send the path of the clicked ISO to it. - Intent intent = new Intent(TvMainActivity.this, EmulationActivity.class); + // Special case: user clicked on a settings row item. + if (item instanceof TvSettingsItem) + { + TvSettingsItem settingsItem = (TvSettingsItem) item; - intent.putExtra("SelectedGame", holder.path); - intent.putExtra("SelectedTitle", holder.title); - intent.putExtra("ScreenPath", holder.screenshotPath); + switch (settingsItem.getItemId()) + { + case R.id.menu_refresh: + getContentResolver().insert(GameProvider.URI_REFRESH, null); - ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation( - TvMainActivity.this, - holder.imageScreenshot, - "image_game_screenshot"); + // TODO Let the Activity know the data is refreshed in some other, better way. + recreate(); + break; - startActivity(intent, options.toBundle()); + case R.id.menu_settings: + // Launch the Settings Actvity. + Intent settings = new Intent(TvMainActivity.this, SettingsActivity.class); + startActivity(settings); + break; + + case R.id.button_add_directory: + Intent fileChooser = new Intent(TvMainActivity.this, AddDirectoryActivity.class); + + // The second argument to this method is read below in onActivityResult(). + startActivityForResult(fileChooser, MainActivity.REQUEST_ADD_DIRECTORY); + + break; + + default: + Toast.makeText(TvMainActivity.this, "Unimplemented menu option.", Toast.LENGTH_SHORT).show(); + break; + } + } + else + { + TvGameViewHolder holder = (TvGameViewHolder) itemViewHolder; + // Start the emulation activity and send the path of the clicked ISO to it. + Intent intent = new Intent(TvMainActivity.this, EmulationActivity.class); + + intent.putExtra("SelectedGame", holder.path); + intent.putExtra("SelectedTitle", holder.title); + intent.putExtra("ScreenPath", holder.screenshotPath); + + ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation( + TvMainActivity.this, + holder.imageScreenshot, + "image_game_screenshot"); + + startActivity(intent, options.toBundle()); + } } }); } + /** + * Callback from AddDirectoryActivity. Applies any changes necessary to the GameGridActivity. + * + * @param requestCode An int describing whether the Activity that is returning did so successfully. + * @param resultCode An int describing what Activity is giving us this callback. + * @param result The information the returning Activity is providing us. + */ + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent result) + { + switch (requestCode) + { + case MainActivity.REQUEST_ADD_DIRECTORY: + // If the user picked a file, as opposed to just backing out. + if (resultCode == RESULT_OK) + { + // Sanity check to make sure the Activity that just returned was the AddDirectoryActivity; + // other activities might use this callback in the future (don't forget to change Javadoc!) + if (requestCode == MainActivity.REQUEST_ADD_DIRECTORY) + { + // TODO Let the Activity know the data is refreshed in some other, better way. + recreate(); + } + } + break; + } + } + private void buildRowsAdapter() { mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter()); @@ -90,13 +157,16 @@ public final class TvMainActivity extends Activity } } + ListRow settingsRow = buildSettingsRow(); + mRowsAdapter.add(settingsRow); + mBrowseFragment.setAdapter(mRowsAdapter); } private ListRow buildGamesRow(int platform) { // Create an adapter for this row. - CursorObjectAdapter row = new CursorObjectAdapter(new GamePresenter(platform)); + CursorObjectAdapter row = new CursorObjectAdapter(new GameRowPresenter()); Cursor games; if (platform == Game.PLATFORM_ALL) @@ -175,8 +245,25 @@ public final class TvMainActivity extends Activity return new ListRow(header, row); } - /*private ListRow buildSettingsRow() + private ListRow buildSettingsRow() { + ArrayObjectAdapter rowItems = new ArrayObjectAdapter(new SettingsRowPresenter()); - }*/ + rowItems.add(new TvSettingsItem(R.id.menu_refresh, + R.drawable.ic_refresh_tv, + R.string.grid_menu_refresh)); + + rowItems.add(new TvSettingsItem(R.id.menu_settings, + R.drawable.ic_settings_tv, + R.string.grid_menu_settings)); + + rowItems.add(new TvSettingsItem(R.id.button_add_directory, + R.drawable.ic_add_tv, + R.string.add_directory_title)); + + // Create a header for this row. + HeaderItem header = new HeaderItem(R.string.settings, getString(R.string.settings)); + + return new ListRow(header, rowItems); + } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GamePresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameRowPresenter.java similarity index 81% rename from Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GamePresenter.java rename to Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameRowPresenter.java index d9b4487278..3b0b8095ce 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GamePresenter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameRowPresenter.java @@ -16,15 +16,8 @@ import org.dolphinemu.dolphinemu.viewholders.TvGameViewHolder; * The Leanback library / docs call this a Presenter, but it works very * similarly to a RecyclerView.ViewHolder. */ -public final class GamePresenter extends Presenter +public final class GameRowPresenter extends Presenter { - private int mPlatform; - - public GamePresenter(int platform) - { - mPlatform = platform; - } - public ViewHolder onCreateViewHolder(ViewGroup parent) { // Create a new view. @@ -80,6 +73,25 @@ public final class GamePresenter extends Presenter holder.country = game.getCountry(); holder.company = game.getCompany(); holder.screenshotPath = game.getScreenshotPath(); + + switch (game.getPlatform()) + { + case Game.PLATFORM_GC: + holder.cardParent.setTag(R.color.dolphin_accent_gamecube); + break; + + case Game.PLATFORM_WII: + holder.cardParent.setTag(R.color.dolphin_accent_wii); + break; + + case Game.PLATFORM_WII_WARE: + holder.cardParent.setTag(R.color.dolphin_accent_wiiware); + break; + + default: + holder.cardParent.setTag(android.R.color.holo_red_dark); + break; + } } public void onUnbindViewHolder(ViewHolder viewHolder) @@ -93,24 +105,8 @@ public final class GamePresenter extends Presenter if (selected) { - switch (mPlatform) - { - case Game.PLATFORM_GC: - backgroundColor = R.color.dolphin_accent_gamecube; - break; - - case Game.PLATFORM_WII: - backgroundColor = R.color.dolphin_accent_wii; - break; - - case Game.PLATFORM_WII_WARE: - backgroundColor = R.color.dolphin_accent_wiiware; - break; - - default: - backgroundColor = android.R.color.holo_red_dark; - break; - } + // TODO: 7/20/15 Try using view tag to set color + backgroundColor = (int) view.getTag(); } else { diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/SettingsRowPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/SettingsRowPresenter.java new file mode 100644 index 0000000000..beef06a218 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/SettingsRowPresenter.java @@ -0,0 +1,47 @@ +package org.dolphinemu.dolphinemu.adapters; + + +import android.content.res.Resources; +import android.support.v17.leanback.widget.ImageCardView; +import android.support.v17.leanback.widget.Presenter; +import android.view.ViewGroup; + +import org.dolphinemu.dolphinemu.model.TvSettingsItem; +import org.dolphinemu.dolphinemu.viewholders.TvSettingsViewHolder; + +public final class SettingsRowPresenter extends Presenter +{ + public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) + { + // Create a new view. + ImageCardView settingsCard = new ImageCardView(parent.getContext()); + + settingsCard.setMainImageAdjustViewBounds(true); + settingsCard.setMainImageDimensions(192, 160); + + + settingsCard.setFocusable(true); + settingsCard.setFocusableInTouchMode(true); + + // Use that view to create a ViewHolder. + return new TvSettingsViewHolder(settingsCard); + } + + public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) + { + TvSettingsViewHolder holder = (TvSettingsViewHolder) viewHolder; + TvSettingsItem settingsItem = (TvSettingsItem) item; + + Resources resources = holder.cardParent.getResources(); + + holder.itemId = settingsItem.getItemId(); + + holder.cardParent.setTitleText(resources.getString(settingsItem.getLabelId())); + holder.cardParent.setMainImage(resources.getDrawable(settingsItem.getIconId(), null)); + } + + public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) + { + // no op + } +} \ No newline at end of file diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/TvSettingsItem.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/TvSettingsItem.java new file mode 100644 index 0000000000..ccd87bfa4c --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/TvSettingsItem.java @@ -0,0 +1,31 @@ +package org.dolphinemu.dolphinemu.model; + + +public final class TvSettingsItem +{ + private final int mItemId; + private final int mIconId; + private final int mLabelId; + + public TvSettingsItem(int itemId, int iconId, int labelId) + { + mItemId = itemId; + mIconId = iconId; + mLabelId = labelId; + } + + public int getItemId() + { + return mItemId; + } + + public int getIconId() + { + return mIconId; + } + + public int getLabelId() + { + return mLabelId; + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/TvGameViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/TvGameViewHolder.java index dd996ccbe7..d27a671c2f 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/TvGameViewHolder.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/TvGameViewHolder.java @@ -4,19 +4,16 @@ import android.support.v17.leanback.widget.ImageCardView; import android.support.v17.leanback.widget.Presenter; import android.view.View; import android.widget.ImageView; -import android.widget.TextView; /** * A simple class that stores references to views so that the GameAdapter doesn't need to * keep calling findViewById(), which is expensive. */ -public class TvGameViewHolder extends Presenter.ViewHolder +public final class TvGameViewHolder extends Presenter.ViewHolder { public ImageCardView cardParent; public ImageView imageScreenshot; - public TextView textGameTitle; - public TextView textCompany; public String gameId; @@ -28,6 +25,8 @@ public class TvGameViewHolder extends Presenter.ViewHolder public String company; public String screenshotPath; + public int backgroundColor; + public TvGameViewHolder(View itemView) { super(itemView); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/TvSettingsViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/TvSettingsViewHolder.java new file mode 100644 index 0000000000..3264e93f5b --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/TvSettingsViewHolder.java @@ -0,0 +1,23 @@ +package org.dolphinemu.dolphinemu.viewholders; + + +import android.support.v17.leanback.widget.ImageCardView; +import android.support.v17.leanback.widget.Presenter; +import android.view.View; + +public final class TvSettingsViewHolder extends Presenter.ViewHolder +{ + public ImageCardView cardParent; + + // Determines what action to take when this item is clicked. + public int itemId; + + public TvSettingsViewHolder(View itemView) + { + super(itemView); + + itemView.setTag(this); + + cardParent = (ImageCardView) itemView; + } +} diff --git a/Source/Android/app/src/main/res/drawable/ic_add_tv.png b/Source/Android/app/src/main/res/drawable/ic_add_tv.png new file mode 100644 index 0000000000000000000000000000000000000000..7332c7572767695645983c7c261daef6cb088fb6 GIT binary patch literal 1708 zcmeAS@N?(olHy`uVBq!ia0y~yU^oE694rhB3_q?e>0w}CEB17845?szd;4sD$aUHF zkM}K>Y}>t}^5!I|he^kbIu@yP>qMkHS9ue)#3DbXaf-skR56|h8`fNy&?FKh6msj) z1uNA|;jNBug1WSmEhjQf)m0T*Gx71QLZOt$Gk5xn>$rI|q`$NL{`Ysy|K^__;qKq> z7*6e*66B@16i&FB-@b8HI>hq4>aratb-HyfE;uOEeWqx#rrt-!P`(Yb9d~WiIpgv9 z+GiQ%nj_{W8Qa<7iyH;!rOsuzb(gKp@-Pph9oLL!we!;VGKAePyZ1wC-4Z#hwZV z|AL7oU-`?#*b^rBd&D!!2cNS)z~&dIzV@oc$z6Y#N(ve(FWr+_AACyrfP!$!(n7XR ztRFPocREkoby)D)6V?UuH81qL%;mVEc*{x+ZcCAs=l7PRC_hU?4MmWUs(4> zvQ*5f{B%iX_a?RvM&&1hPxGX6Cbaw!EijN~Q1zQ|ebr0$7Y}$U-<40f#B<@GM&tEV9wE>N3NH zv*99J^!igGD3mhZSuVQm z81n~CjtBc&1rp z21&*(7fbIm+;(KhHGLrdK$mgLO~w5T;j9<=sj99FdbVd&vdNNNAWP@~c?q4y`_4>tzI*&i7% z_&PHvuD<+|Zv!ujz^+Z!2l5y#zVuihkY*~#*l+C+%D^XE%x=+tU+3xP+m_`G-b@8A z7Fi#d!XPuNgnfr5Lwnv1<}+Le7Dm5g=w>R&I4GOo$-sBFj6H+nz`|V&M*JJ5=rqVV zf}|8b9}kdq&gVL?PKNv!Ui`w|Tgt-35!gKcC!@hK zR)Iy?GxQt2vkEK<{>Ly++@YiHEq{V7FXNWTUb%XPV=WA~-f%q=e$c^iYt19^gxL(Y z*1QovFhi{2x8N1l1Q&+fkdOQq^c&1xedbSy5p?LT?Q>=P(8Un-rZt0MSEPUg_uu|= z84Nw!SuZ$+>S#5@fg)AZ%#A&vhcjWS+eQsWIq?HpZ#%>e{8-K)cj^Ad6^vy<91j>- zbNb~D{D@%S&*151Uf|=)@IA9t_alRZI_ra?f932E$7Q}ROMBVLf1qM+b@1;E7K~nh z+S6XePkF>@z_#P?-77+WBknMq?XC(vIEf+RaHvSnXAdI=H+lPjFJd=aR9O@DcwMX( z^cFhsgW2_+txjfx-o7O(d_PTo!q^k{Oe1M?&XG$D32%~SF4}q|I3kLnCy}Wuzglfm9^4sK#ec$3t($K-Ty#5IPVxP2=8zMmwfxiT#7-kh@c z`->x!m<#U5?NeW^`$?%SRrs7joLNh@imv*SPm>@$)dZ!XGQm&pf{^k6G#&o#f5icp&%Hm7Qmqw(mHoGQY4d v`AvGq+IRUMSs$b`U0_jQ@MXY4z5V|(x3X}Pio0w}Cukdtn45?szbI_2Hfq~wMcG2a(q#y@X0%6@&lW7;G$=A)w5YZ-eNiN4X% z&0*TR_xsED{|fA$-?@GH{O_77x%K}a{PlUaSH^F_G*O!a#c!0(a{iKdq7=C)CS`)1 zUqMH#&1v%=Jf;;VX5`2Q-C%jUyKQ&HJmnaL9!0*-%wdMMk3K)nYcQJj+2(z~inw`; z8z*I)d!N=IWOuro$8Z+g56<~&rn{b(8y@&-b2f6(C;n@}y#YUF9oWlo+QOr6Me0rG z*oV){bq_rMshqsBx$V&T_g)RwmR-EQ86u!SnB(8aVej@cA0q_?@b$GiR!4ku!O<;8sN)J5x>L_lv9Mv(5i( zEwG@&_l4P^!zcddGVc&vFekjDIafsbocw8q%N28-crW=HXGglq7SD7QUAkn3hv@ad z<63fQ(y=uT3vaD7m}6hYn0CSY-?@uk%AY?>VDR49;Py>iWYJ`8p6C6c4OxDZ<};cF z`v=amFAM)5B!1$&p;@Ce5 zjxr1HbbemVy3;Rs=)BT=^1w)-}ouYI>V1`@|NP~;UW(VeT>eOzWbBKR59mJN_X?~aNz_^HOVyV ziaJxqx{i~4>gU(1I0)AMI9;TiROtJlsKLtTn{`#4D&w`pO_wfy*5x}VWY)^>jv z!(_>W_tVnPuU9&-++bz-w?A4;HjVp>MHI!h@f~w-VE&O^`s0rl(;wM-eyf@~Rz@wu zna@A`6lIBM+V5!7etx~efxACmm;L&~#kA&VyzwNRQw-4`wu^a+r8eyS5#6fNZR8+T zU+S?*gZayYd_9o#@(;5F#B`R56Yn0sP&vT0yIyfJ%MQ7E=L(NaFPKD( zd-l(skaCe#pz7_uX_Htoj^5V>*=y6b-!TZJ;7VeV_9UHM3`gf2zaOR&S;E+2_Gj*5 zp3a+gM}Ggfy;%Lg_JcdsoxUf2O0{JaODJOKyxI3y|FnrMqs_mz{W9`yNn2EBlqgj( zarh^FQW9FcHStr&I)-8c$%SH_C+*ri`=VkQTdo`4Y&(*&cE%s6i((4|C-Zz3`1_de zOxSB~gI*pr*@})&qL-UJ?U(*y;y62Ft>cvSf}4FijxiLUNc@z)&P8PXH0~KZ#@^`+ ziRNk5f?K^WcHS&=$!=g}x!9p`ckztW6&Ix)EIMwciYakz@8LfM?>QgM_qinRuw#Bg+#zR? zHB-u(ekuvIGq}od&so5-aS!Jfp)=VYHcTAbC-DBA-+ue1=n~Fn%y}LD3=cV*UZrUz zWg1)f1;jf)5?Ro3U+7AFL`%et+&|q5%9KBHDa4$WY}%Fc@axh1Ns22j$4KsHaAJ;Z zy6rA%k7q_Q^UEtJ9r8R<4Za-}o^5Ip* z)D&3P^<4cksYWJE=#dk{L#e8h=M`@6>=l`G+-t|&vpf^#`>83fV-Z+(!S`R=57zsS z()Kjmw>JM=`mJ!@AK65sM?nk=o65LK=Plmy!J<6yyzdo*sYYHaQrNBsp1*5zDs0yd zkA-{zjXT5~G#0j6R5U6bIm*y<+^s0X><}}@qbvr;2tWI%ubX8GZ5s?G^O^kOH5RzT z-!S2kXHkaHneq<;dPk_Ym5I_c;p+4?Cz;$vyb~TaU62`Hc>h2@Ea5P`&SvBoqe`@k8gPT-g5a!(K3Zy z>POP2>OAw#d6IhTbMpW-$7F7CUa?V(h!-p>W7!#g8)#?tS0??L`BqI(4}4 p|N2{wBb;0U4GfGNgouXy4F9f|E3CTt$q`i1db;|#taD0e0swxDhWG#g literal 0 HcmV?d00001 diff --git a/Source/Android/app/src/main/res/drawable/ic_settings_tv.png b/Source/Android/app/src/main/res/drawable/ic_settings_tv.png new file mode 100644 index 0000000000000000000000000000000000000000..9e242e7748071f029d35d43492b9619b7c2104fa GIT binary patch literal 2248 zcmeAS@N?(olHy`uVBq!ia0y~yU^oE694rhB3_q?e>0w~t==XGS45?szbFh(tfq|i6 z!QYkQE15t{h8=r%|6jXl&tr?atseYaY<2`KTD)Z{Yv3}Tl8!>73ukqG(%E=?%$gO; zF7PSZ=^W-wJkol0q3k(!6^*7h;;ECLH+EV~-RPwo!lrGKa>if+D|f8-?c!f2D&L#W z_NuPB`+T2V-kI-TfAaroyL|cgo$IxK5_POQgu~{%jp^@tB@%cyd}o6uZ_iW)$L-vc z?@eMjc&$Io=aqsQW78Y$8D?QjEH}#y-^OtYq;MO&4dfI^S#6NJm4#!|+!^1tv2bjX z^7$RX^g@4fn9u7a`≪3YzsyWZ1QvTl(EZhF#i6+uBtd-ksX;kd<@6?%W$6rmWWSD#Q_%v4`hjjns)5Ss#>Cqcfr?XtR zDVSsV?G88-=+1|+daqj|4`SK5LwE=ymHBDL%E0EnS1~L zSCmh>?U**XlWWgHokg?sy!!f6C3Ae-^V2Kh%7wEt zm8QN8S?AX?}aw3zu37WWKw4y)1K}ZH}A)?%;{QQXlW-JX*~!xSjVWhI{v+OKMzx79FJ|N@JFxv0tXg%#L_UGf>At@Y`-z7%wam;}$ov1+ zj%zUuSAT?^p2Sct&-0k`+{4>Z#f*Fpf`z+Jy0OOa?O!4}pKmtz2fKfJJnK3>PmBJT zzJDh}{{#1?q>vL+FEq*THxO6o{$B82#P^oEjziFOLQ9Taq@e7NK@_RABQxF{N(7SLJe+d zf9`F6r#{?fFMRIFc9q=t6PvaKz4;~k_s$)0leyeCJ}vv}?ig9vI8!g*SH{`Y%w%G| zse@u+j>W0cb?^JmB?gzg+wi3(`k-3TpE(mMd-!}-sot(?kU5te#NPkQW{LUK%`-N$ zJWV)l@LVUjX1?&b=gem^l&^a>ByLG|vs7MG5M>>+TC zC!H}(Fl^56#orDr6K%PCFd?0>?UJX(>A*nl4SfY#0es~R1#w+*4R>3D%OY!CPknpA zxl7E!BJ!dFZCtUWWBI zem}lVRr#_^uNPk!@#(9JS@&Z*8gE%{vJvR^zOEauKA>64lAly?!|WUC)D z=QA|l%=sTPwN>}%9$%g!*#sTa8y9>SPd!?v*vRWH`Pk@y&a4fotFJOxZ+yyh`7XKA}-=KLi_#1x)&-2)v>_nzhhw_$*-u^Zr-T1)TImcT4HMV?s^*=u52fuJG ztHx5^@T?Qgl6MOF9(!pYea6g_z?^qg;!k|Qk-|GwtuszC7@jJ8Z}OOT@3;8}_kMqo zuXiBnPsTzK(IW25qy4SVnEvqAZwr)oB>vv-&&^~#2UWYHkxqV6*K4LtI1QV1zUGD1{ z9rR;3%I0!E&g~{wfobm&H7SWa{f0+{LBjnv|4FSD?zktreE(gBu9qG6?mm9BuEVxt z-dnew90kgAJG@H^_HJd6ihrT-uc1g(VY-z|`STr@Vi}Gqxs)$mxBtq1sf$nLQ+Dw# z_`$d6wCsv6X^$54-{|=ym|&9IkdZiLK10`X#*fld)-yC)dRv{_+^Z5eDMh<<$zvYN ze@q%(oLTbUg`Eq{x4O9MUJl?%;CssQmElx+gAd=L)$^Y3TzGpq_wJ6SxiPK3avoQJ zO27#Z%^Mz-m4p~fZ(QMRHr=Xb)$5P(1!hM>E^SxY?)GrWpVvz+otQPH+>-yf>7*YM zQk+i(bC_?OrWW*5S&NI2%j3dkt!J6C59S=Pi@AMTH!9!Q%fjXSEXjw84bN_VPP@ix zz3T8@nL|^x-np^fP`M_2-R}B(MTZ{#7ey`YmukM8`F^uK_x&V*mMpfI2 zQG_}26l32+?H@!jdQ`~ zsW&z`F}=u<+jeFuxav~#W|%9QaQdo{L%O8OhJ}Kvj917@acSREqb8s%$?Gt6F+c$~fmvviayQ;&4w;!s1FJOsi&D2(P zn4o)LOQq0(_h;Xido(CWw`MA