Skip to content

Commit

Permalink
Add private listening support
Browse files Browse the repository at this point in the history
  • Loading branch information
wseemann committed Aug 7, 2020
1 parent 1dfdaf1 commit 3a5bf85
Show file tree
Hide file tree
Showing 12 changed files with 182 additions and 5 deletions.
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ android {
applicationId "wseemann.media.romote"
minSdkVersion 21
targetSdkVersion 29
versionCode 17
versionName "1.0.16"
versionCode 18
versionName "1.0.17"
}
buildTypes {
release {
Expand Down
Binary file modified app/release/app-release.apk
Binary file not shown.
2 changes: 1 addition & 1 deletion app/release/output.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":11,"versionName":"1.0.10","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":18,"versionName":"1.0.17","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="wseemann.media.romote">

<uses-permission android:name="wseemann.media.romote.audio.REMOTE_AUDIO"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package wseemann.media.romote.audio;

interface IRemoteAudioInterface {
void setDevice(String host);
void toggleRemoteAudio();
boolean isRemoteAudioActive();
}
166 changes: 166 additions & 0 deletions app/src/main/java/wseemann/media/romote/fragment/RemoteFragment.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,19 @@
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.speech.RecognizerIntent;
import android.util.Log;
import android.view.LayoutInflater;
Expand All @@ -21,6 +29,7 @@
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.fragment.app.Fragment;

import com.jaku.core.JakuRequest;
Expand All @@ -35,6 +44,7 @@
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import wseemann.media.romote.R;
import wseemann.media.romote.audio.IRemoteAudioInterface;
import wseemann.media.romote.model.Device;
import wseemann.media.romote.tasks.RequestCallback;
import wseemann.media.romote.tasks.RequestTask;
Expand All @@ -44,6 +54,7 @@
import wseemann.media.romote.utils.PreferenceUtils;
import wseemann.media.romote.utils.RokuRequestTypes;
import wseemann.media.romote.view.RepeatingImageButton;
import wseemann.media.romote.view.VibratingImageButton;

/**
* Created by wseemann on 6/19/16.
Expand All @@ -52,6 +63,12 @@ public class RemoteFragment extends Fragment {

private static final String TAG = "RemoteFragment";

private boolean remoteAudioStarted = false;

/** The primary interface we will be calling on the service. */
private IRemoteAudioInterface mService = null;
private Boolean isBound = false;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Expand All @@ -64,6 +81,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
return inflater.inflate(R.layout.fragment_remote, container, false);
}

@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Expand Down Expand Up @@ -102,6 +120,20 @@ public void onClick(View v) {
fragment.show(RemoteFragment.this.getFragmentManager(), TextInputDialog.class.getName());
});

VibratingImageButton remoteAudioButton = getView().findViewById(R.id.remote_audio);
remoteAudioButton.setOnClickListener(view -> {
if (isBound) {
try {
mService.toggleRemoteAudio();
} catch (RemoteException ex) {
ex.printStackTrace();
}
} else {
bindToRemoteAudio();
}
updatePrivateListening();
});

ImageButton powerButton = getView().findViewById(R.id.power_button);
powerButton.setOnClickListener(view -> {
obtainPowerMode();
Expand All @@ -110,12 +142,19 @@ public void onClick(View v) {
getView().findViewById(R.id.remote_dpad_controls).bringToFront();
updateVolumeControls();
updateRokuDeviceName();
updatePrivateListening();

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Constants.UPDATE_DEVICE_BROADCAST);
getActivity().registerReceiver(mUpdateReceiver, intentFilter);
}

@Override
public void onResume() {
super.onResume();
updatePrivateListening();
}

@Override
public void onDestroy() {
super.onDestroy();
Expand Down Expand Up @@ -270,6 +309,7 @@ public void onActivityResult(int requestCode, int resultCode,
public void onReceive(Context context, Intent intent) {
updateVolumeControls();
updateRokuDeviceName();
updatePrivateListening();
}
};

Expand Down Expand Up @@ -305,4 +345,130 @@ private void updateRokuDeviceName() {
Log.e(TAG, "Error updating roku device name for newly connected device.");
}
}

public void updatePrivateListening() {
VibratingImageButton remoteAudioButton = getView().findViewById(R.id.remote_audio);

boolean supportsRemoteAudio = false;

try {
Device device = PreferenceUtils.getConnectedDevice(getContext());

if (device.getSupportsPrivateListening() != null) {
supportsRemoteAudio = Boolean.valueOf(device.getSupportsPrivateListening());
}
} catch (Exception ex) {
Log.e(TAG, "Error updating remote layout for newly connected device.");
}

if (!supportsRemoteAudio || !privateListeningInstalled()) {
remoteAudioButton.setImageResource(R.mipmap.remote_private_listening_unavailable);
return;
}
if (mService != null) {
try {
if (mService.isRemoteAudioActive()) {
remoteAudioButton.setImageResource(R.mipmap.remote_private_listening_on);
} else {
remoteAudioButton.setImageResource(R.mipmap.remote_private_listening_available);
}
} catch (RemoteException ex) {
ex.printStackTrace();
}
} else {
remoteAudioButton.setImageResource(R.mipmap.remote_private_listening_available);
}
}

private void bindToRemoteAudio() {
Intent intent = new Intent();
intent.setComponent(
new ComponentName(
"wseemann.media.romote.audio",
"wseemann.media.romote.audio.remoteaudio.RemoteAudio"
)
);

if (!privateListeningInstalled()) {
showDownloadPrivateListeningDialog();
} else {
getContext().bindService(intent, remoteAudioConnection, Context.BIND_AUTO_CREATE);
}
}

private void showDownloadPrivateListeningDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage(getString(R.string.download_private_listening));
builder.setPositiveButton(R.string.install_channel_dialog_button, (dialog, id) -> {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(Constants.PRIVATE_LISTENING_URL));
startActivity(intent);
});
builder.setNegativeButton(R.string.close, (dialog, id) -> dialog.cancel());

AlertDialog dialog = builder.create();
dialog.show();
}

private boolean privateListeningInstalled() {
Intent intent = new Intent();
intent.setComponent(
new ComponentName(
"wseemann.media.romote.audio",
"wseemann.media.romote.audio.remoteaudio.RemoteAudio"
)
);
List<ResolveInfo> list = getContext().getPackageManager().queryIntentServices(intent,
PackageManager.MATCH_DEFAULT_ONLY);

return list.size() != 0;
}

/**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection remoteAudioConnection = new ServiceConnection() {

@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.d(TAG, "onServiceConnected");
mService = IRemoteAudioInterface.Stub.asInterface(iBinder);
isBound = true;

try {
Device device = PreferenceUtils.getConnectedDevice(getContext());
mService.setDevice(device.getHost());
mService.toggleRemoteAudio();
updatePrivateListening();
} catch (RemoteException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}

@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.d(TAG, "onServiceDisconnected");
isBound = false;
mService = null;
updatePrivateListening();
}

@Override
public void onBindingDied(ComponentName name) {
Log.d(TAG, "onBindingDied");
isBound = false;
mService = null;
updatePrivateListening();
}

@Override
public void onNullBinding(ComponentName name) {
Log.d(TAG, "onNullBinding");
isBound = false;
mService = null;
updatePrivateListening();
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ public enum WIFI_AP_STATE {
public static final String UPDATE_DEVICE_BROADCAST = "wseemann.media.romote.UPDATE_DEVICE";
public static final String PAYPAL_DONATION_LINK = "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=C4RNUUK83P3E2";
public static final String DISMISS_CONECTIVITY_DIALOG = "wseemann.media.romote.DISMISS_CONECTIVITY_DIALOG";
public static final String PRIVATE_LISTENING_URL = "https://github.com/wseemann";
}
3 changes: 1 addition & 2 deletions app/src/main/res/layout/fragment_remote.xml
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,7 @@
android:layout_marginLeft="10dip"
android:layout_weight="1"
android:background="@drawable/remote_button_bg"
android:src="@mipmap/remote_private_listening_available"
android:visibility="invisible" />
android:src="@mipmap/remote_private_listening_available" />

</LinearLayout>

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,7 @@
<string name="power_dialog_title">Power Off</string>
<string name="power_dialog_message">Are you sure you want to turn off the device?</string>

<string name="download_private_listening">Private listening is experimental and support may be discontinued at
any time. In order to enable private listening please download and install the companion plugin.</string>
<string name="download">Download</string>
</resources>

0 comments on commit 3a5bf85

Please sign in to comment.