1

Currently, my HiSense Q8 android TV has a start up screen that has App, Youtube and Netflix icons. There is a series of clicks on the remote I need to go through to have it show input from HDMI input 1. What I'd like to have is an android app causes HDMI input 1 to be displayed on boot, without me need to use the remote (aka, make it behave like a simple monitor). However, I seem to be flubbing the step of selecting the HDMI input programmatically. Any suggestions or pointers to relevant source code examples?

The TV has android 8 and I'm targeting android 7.1.1 in the app.

With the below calls, I can get a list of inputs to iterate through:

  TvInputManager mTvInputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE);
  List<TvInputInfo> inputs = mTvInputManager.getTvInputList();

The id fields of the HDMI TvInputInfo items look like:

"com.mediatex.tvinput/.hdmi.HDMInputService/HW4"
"com.mediatex.tvinput/.hdmi.HDMInputService/HW3"
"com.mediatex.tvinput/.hdmi.HDMInputService/HW2"

I then try to set the displayed input to the HW2 using

TvView view = new TvView(this);
view.tune("com.mediatex.tvinput/.hdmi.HDMInputService/HW2", null);

or similarly for HW4, but nothing happens, I still see the app displayed, not the HDMI input. Adding a callback to TvView object doesn't catch any errors. The way I'm creating a TvView object seems a bit suspect to me.

Below is the entire code:


package org.ericdavies.sethdmi1;

import android.app.Activity;
import android.content.Context;
import android.media.tv.TvContentRating;
import android.media.tv.TvTrackInfo;
import android.media.tv.TvView;
import android.os.Bundle;
import android.util.Log;

import android.media.tv.TvContract;
import android.net.Uri;

import android.media.tv.TvInputManager;
import android.media.tv.TvInputInfo;

import android.media.tv.TvInputService;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.util.List;

/*
 * Main Activity class that loads {@link MainFragment}.
 */
public class MainActivity extends Activity {

    TvView view;
    TextView tv;
    StringBuilder sb;

    private void setUpButton(final String inputId, int buttonTag) {
        Button bt = findViewById(R.id.buttonhw4);
        bt.setEnabled(true);
        bt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                view.setCallback(new TvView.TvInputCallback() {

                });
                view.tune(inputId, null);
            }
        });
    }

    public void reportState(final String state) {
        this.runOnUiThread(new Runnable() {
            public void run() {
                sb.append(state);
                tv.setText(sb.toString());
            }
        });
    }
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TvInputManager mTvInputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE);
        sb = new StringBuilder();
        List<TvInputInfo> inputs = mTvInputManager.getTvInputList();
        view = new TvView(this);
        tv = findViewById(R.id.mytextfield);

        view.setCallback(new TvView.TvInputCallback() {
                             @Override
                             public void onConnectionFailed(String inputId) {
                                 super.onConnectionFailed(inputId);
                                 reportState("tvview.onconnectionFailed\n");
                             }

                             @Override
                             public void onDisconnected(String inputId) {
                                 super.onDisconnected(inputId);
                                 reportState("tvview.onDisconnected\n");
                             }

                             @Override
                             public void onChannelRetuned(String inputId, Uri channelUri) {
                                 super.onChannelRetuned(inputId, channelUri);
                                 reportState("tvview.onChannelRetuned\n");
                             }

                             @Override
                             public void onTracksChanged(String inputId, List<TvTrackInfo> tracks) {
                                 super.onTracksChanged(inputId, tracks);
                                 reportState("tvview.onTracksChanged\n");
                             }

                             @Override
                             public void onTrackSelected(String inputId, int type, String trackId) {
                                 super.onTrackSelected(inputId, type, trackId);
                                 reportState("tvview.onTrackSelected\n");
                             }

                             @Override
                             public void onVideoUnavailable(String inputId, int reason) {
                                 super.onVideoUnavailable(inputId, reason);
                                 reportState("tvview.onVideoUnavailable\n");
                             }

                             @Override
                             public void onContentBlocked(String inputId, TvContentRating rating) {
                                 super.onContentBlocked(inputId, rating);
                                 reportState("tvview.onContentBlocked\n");
                             }
                         }
                );

        for (TvInputInfo input : inputs) {
            String id = input.getId();
            if( input.isPassthroughInput() && id.contains("HDMIInputService")) {
                sb.append("inputid = " + input.getId() + "\n");
                if( id.contains("HW4")) {
                    setUpButton(id, R.id.buttonhw4);
                }
                if( id.contains("HW2")) {
                    setUpButton(id, R.id.buttonHw2);
                }
                if( id.contains("HW3")) {
                    setUpButton(id, R.id.buttonhw3);
                }
            }
        }

        if( tv != null) {
            tv.setText(sb.toString());
        }

    }
}

The layout is

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/top"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:visibility="visible">

    <EditText
        android:id="@+id/mytextfield"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:autoText="false"
        android:clickable="false"
        android:editable="false"
        android:ems="10"
        android:enabled="false"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:gravity="start|top"
        android:inputType="textMultiLine"
        android:selectAllOnFocus="false"
        android:text="-----" />

    <Button
        android:id="@+id/buttonhw4"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="hw4" />

    <Button
        android:id="@+id/buttonHw2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="hw2" />

    <Button
        android:id="@+id/buttonhw3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:enabled="false"
        android:text="hw3" />
</LinearLayout>

The manifest is

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="org.ericdavies.sethdmi1">

    <uses-feature
        android:name="android.hardware.touchscreen"
        android:required="false" />
    <uses-feature
        android:name="android.software.leanback"
        android:required="true" />
    <uses-feature
        android:name="android.software.LIVE_TV"
        android:required="true" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:banner="@drawable/app_icon_your_company"
            android:icon="@drawable/app_icon_your_company"
            android:label="@string/app_name"
            android:logo="@drawable/app_icon_your_company"
            android:screenOrientation="landscape">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".DetailsActivity" />
        <activity android:name=".PlaybackActivity" />
        <activity android:name=".BrowseErrorActivity" />
    </application>
</manifest>

The build.gradle for the app is

apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "org.ericdavies.sethdmi1"
        minSdkVersion 25
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.leanback:leanback:1.0.0'
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'com.github.bumptech.glide:glide:3.8.0'
}
Eric Davies
  • 78
  • 1
  • 9
  • I think you miss the TVInputService like mentioned in the documentation. I would love to see that work because it's something I have thought of but had no time to do. https://developer.android.com/training/tv/tif/tvinput – pbertsch Jul 08 '20 at 16:51
  • From what I can understand of the TVInputService, its a means of providing content that the user can interactively select. Its how you would implement a netflix app or a youtube app. I already locate a TVInputService for the HDMI port, the trick is connecting it to the display. – Eric Davies Jul 11 '20 at 21:53
  • Did you ever solve this? – Mitch Buz Stringer Oct 14 '20 at 12:44

2 Answers2

1

Instead of null for the second parameter (URI):

TvView view = new TvView(this);
view.tune("com.mediatex.tvinput/.hdmi.HDMInputService/HW2", null);

You need to make and send a valid Uri:

TvView view = new TvView(this)
mInitChannelUri = TvContract.buildChannelUriForPassthroughInput("com.mediatex.tvinput/.hdmi.HDMInputService/HW2")
view.tune("com.mediatex.tvinput/.hdmi.HDMInputService/HW2", mInitChannelUri)

It's a little goofy because you basically send the same input name string twice. But this works for me.

Finally, to be TV brand independent you should use the input id parameter instead of static string constants (left as an exercise for the reader haha)

Drunken Daddy
  • 7,326
  • 14
  • 70
  • 104
1

You don't need to use TvView. Just use an implicit intent with ACTION_VIEW.

I tested this code on my Sony TV and it worked well. (I am using Kotlin)

// Passthrough inputs are "hardware" inputs like HDMI / Components. Non-passthrough input are
// usually internal tv tuners. You can also filter out non-passthrough inputs before this step.
val uri =
    if (inputInfo.isPassthroughInput) TvContract.buildChannelUriForPassthroughInput(inputInfo.id)
    else TvContract.buildChannelsUriForInput(inputInfo.id)

val intent = Intent(Intent.ACTION_VIEW, uri)

if (intent.resolveActivity(packageManager) != null) {
  context.startActivity(intent)
}
WANG Cheng
  • 11
  • 2