Permalink
Browse files

Android: Support for AndroidTV Oreo Homescreen channels

  • Loading branch information...
zackhow committed Jul 8, 2018
1 parent 14317c8 commit 0488fe1d30b0645a7f7af63b5c0d0090adf738a6
@@ -86,6 +86,7 @@ dependencies {
// Android TV UI libraries.
implementation "com.android.support:leanback-v17:$androidSupportVersion"
implementation "com.android.support:support-tv-provider:$androidSupportVersion"
// For showing the banner as a circle a-la Material Design Guidelines
implementation 'de.hdodenhof:circleimageview:2.1.0'
@@ -16,6 +16,8 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" />
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
<application
android:name=".DolphinApplication"
@@ -69,9 +71,27 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name=".activities.AppLinkActivity" >
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="@string/host"
android:scheme="@string/scheme" />
</intent-filter>
</activity>
<service android:name=".services.DirectoryInitializationService"/>
<service android:name=".services.GameFileCacheService"/>
<service
android:name=".services.SyncChannelJobService"
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE" />
<service
android:name=".services.SyncProgramsJobService"
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE" />
<provider
android:name="android.support.v4.content.FileProvider"
@@ -0,0 +1,134 @@
package org.dolphinemu.dolphinemu.activities;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import android.widget.Toast;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.model.GameFile;
import org.dolphinemu.dolphinemu.services.DirectoryInitializationService;
import org.dolphinemu.dolphinemu.services.GameFileCacheService;
import org.dolphinemu.dolphinemu.ui.main.TvMainActivity;
import org.dolphinemu.dolphinemu.utils.AppLinkHelper;
import org.dolphinemu.dolphinemu.utils.DirectoryStateReceiver;
/**
* Linker between leanback homescreen and app
*/
public class AppLinkActivity extends FragmentActivity
{
private static final String TAG = "AppLinkActivity";
private AppLinkHelper.PlayAction playAction;
private DirectoryStateReceiver directoryStateReceiver;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Intent intent = getIntent();
Uri uri = intent.getData();
Log.v(TAG, uri.toString());
if (uri.getPathSegments().isEmpty())
{
Log.e(TAG, "Invalid uri " + uri);
finish();
return;
}
AppLinkHelper.AppLinkAction action = AppLinkHelper.extractAction(uri);
switch (action.getAction())
{
case AppLinkHelper.PLAY:
playAction = (AppLinkHelper.PlayAction) action;
initResources();
break;
case AppLinkHelper.BROWSE:
browse();
break;
default:
throw new IllegalArgumentException("Invalid Action " + action);
}
}
/**
* Need to init these since they usually occur in the main activity.
*/
private void initResources()
{
IntentFilter statusIntentFilter = new IntentFilter(
DirectoryInitializationService.BROADCAST_ACTION);
directoryStateReceiver =
new DirectoryStateReceiver(directoryInitializationState ->
{
if (directoryInitializationState == DirectoryInitializationService.DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED)
{
play(playAction);
}
else if (directoryInitializationState == DirectoryInitializationService.DirectoryInitializationState.EXTERNAL_STORAGE_PERMISSION_NEEDED)
{
Toast.makeText(this, R.string.write_permission_needed, Toast.LENGTH_SHORT)
.show();
}
else if (directoryInitializationState == DirectoryInitializationService.DirectoryInitializationState.CANT_FIND_EXTERNAL_STORAGE)
{
Toast.makeText(this, R.string.external_storage_not_mounted, Toast.LENGTH_SHORT)
.show();
}
});
// Registers the DirectoryStateReceiver and its intent filters
LocalBroadcastManager.getInstance(this).registerReceiver(
directoryStateReceiver,
statusIntentFilter);
DirectoryInitializationService.startService(this);
GameFileCacheService.startLoad(this);
}
/**
* Action if channel icon is selected
*/
private void browse()
{
Intent openApp = new Intent(this, TvMainActivity.class);
startActivity(openApp);
finish();
}
/**
* Action if program(game) is selected
*/
private void play(AppLinkHelper.PlayAction action)
{
Log.d(TAG, "Playing game "
+ action.getGameId()
+ " from channel "
+ action.getChannelId());
GameFile game = GameFileCacheService.getGameFileByGameId(action.getGameId());
if (game == null)
Log.e(TAG, "Invalid Game: " + action.getGameId());
else
startGame(game);
finish();
}
private void startGame(GameFile game)
{
if (directoryStateReceiver != null)
{
LocalBroadcastManager.getInstance(this).unregisterReceiver(directoryStateReceiver);
directoryStateReceiver = null;
}
EmulationActivity.launch(this, game, -1, null);
}
}
@@ -158,15 +158,21 @@ public static void launch(FragmentActivity activity, GameFile gameFile, int posi
launcher.putExtra(EXTRA_PLATFORM, gameFile.getPlatform());
launcher.putExtra(EXTRA_SCREEN_PATH, gameFile.getScreenshotPath());
launcher.putExtra(EXTRA_GRID_POSITION, position);
Bundle options = new Bundle();
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(
// Will be null if launched from homescreen
if (sharedView != null)
{
ActivityOptionsCompat transition = ActivityOptionsCompat.makeSceneTransitionAnimation(
activity,
sharedView,
"image_game_screenshot");
options = transition.toBundle();
}
// I believe this warning is a bug. Activities are FragmentActivity from the support lib
//noinspection RestrictedApi
activity.startActivityForResult(launcher, MainPresenter.REQUEST_EMULATE_GAME, options.toBundle());
activity.startActivityForResult(launcher, MainPresenter.REQUEST_EMULATE_GAME, options);
}
@Override
@@ -0,0 +1,64 @@
package org.dolphinemu.dolphinemu.model;
/**
* Represents a home screen channel for Android TV api 26+
*/
public class HomeScreenChannel
{
private long channelId;
private String name;
private String description;
private String appLinkIntentUri;
public HomeScreenChannel()
{
}
public HomeScreenChannel(String name, String description, String appLinkIntentUri)
{
this.name = name;
this.description = description;
this.appLinkIntentUri = appLinkIntentUri;
}
public long getChannelId()
{
return channelId;
}
public void setChannelId(long channelId)
{
this.channelId = channelId;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getDescription()
{
return description;
}
public void setDescription(String description)
{
this.description = description;
}
public String getAppLinkIntentUri()
{
return appLinkIntentUri;
}
public void setAppLinkIntentUri(String appLinkIntentUri)
{
this.appLinkIntentUri = appLinkIntentUri;
}
}
@@ -50,6 +50,19 @@ public GameFileCacheService()
return platformGames;
}
public static GameFile getGameFileByGameId(String gameId)
{
GameFile[] allGames = gameFiles.get();
for (GameFile game : allGames)
{
if (game.getGameId().equals(gameId))
{
return game;
}
}
return null;
}
private static void startService(Context context, String action)
{
Intent intent = new Intent(context, GameFileCacheService.class);
Oops, something went wrong.

0 comments on commit 0488fe1

Please sign in to comment.