I followed the Android official documentation on connecting MediaBrowserCompat but it's refused to connect, as a matter of fact neither onConnected(), onConnectionSuspended() or onConnectionFailed() is called.
I have also tried this answer but it didn't work.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_audio_player);
ButterKnife.bind(this);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
setupMediaComponents();
// initializeViews();
}
private void setupMediaComponents() {
Log.d(TAG, "setupMediaComponents");
setVolumeControlStream(AudioManager.STREAM_MUSIC);
mediaBrowserCompat = new MediaBrowserCompat(this, new ComponentName(this, SongPlayerService.class),
mediaBrowserCompatConnectionCallback, null);
}
#Override
protected void onStart() {
super.onStart();
mediaBrowserCompat.connect();
}
#Override
public void onStop() {
super.onStop();
// (see "stay in sync with the MediaSession")
if (MediaControllerCompat.getMediaController(this) != null) {
MediaControllerCompat.getMediaController(this).unregisterCallback(controllerCallback);
}
mediaBrowserCompat.disconnect();
}
private MediaBrowserCompat.ConnectionCallback mediaBrowserCompatConnectionCallback = new MediaBrowserCompat.ConnectionCallback() {
#Override
public void onConnected() {
Log.d(TAG, "onConnected");
try {
// Create a MediaControllerCompat
MediaControllerCompat mediaController =
new MediaControllerCompat(AudioPlayerActivity.this, mediaBrowserCompat.getSessionToken());
// Save the controller
MediaControllerCompat.setMediaController(AudioPlayerActivity.this, mediaController);
setControlClickListeners();
} catch (RemoteException e) {
e.printStackTrace();
}
}
#Override
public void onConnectionSuspended() {
Log.d(TAG, "onConnectionSuspended");
// We were connected, but no longer :-(
}
#Override
public void onConnectionFailed() {
Log.d(TAG, "onConnectionFailed");
// The attempt to connect failed completely.
// Check the ComponentName!
}
};
I am using version 25.3.1 of the support library and all sdk tools are upto to date.
EDIT
Part of SongPlayerService
#Nullable
#Override
public BrowserRoot onGetRoot(#NonNull String clientPackageName, int clientUid, #Nullable Bundle rootHints) {
// Returning null means no one can connect so we’ll return something
return new BrowserRoot(getString(R.string.app_name), null); // Name visible in Android auto, Bundle is for optional params
// TODO: Support Android auto
}
Finally found the source of the problem. I was overriding onBind and returning null.
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
removing the above lines fixed it for me.
You must implement two methods of MediaBrowserServiceCompat.
At least:
#Nullable
#Override
public BrowserRoot onGetRoot(#NonNull String clientPackageName, int clientUid, #Nullable Bundle rootHints) {
return new BrowserRoot(MEDIA_ID_ROOT, null);
}
#Override
public void onLoadChildren(#NonNull String parentId, #NonNull Result<List<MediaBrowserCompat.MediaItem>> result) {
result.sendResult(null);
}
onConnectionFailed() can also be called if you fail to declare the service in the AndroidManifest file. Make sure the intent-filter is also correct. source
<service
android:name=".media.MusicService"
android:enabled="true"
android:exported="true"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
Related
I got an email from google play support saying "Intent Redirection Your app(s) are vulnerable to Intent Redirection. To address this issue, follow the steps in this Google Help Center article."
After reading through the article, I'm guessing the key is my app should not call startActivity, startService, sendBroadcast, or setResult on untrusted Intents (intents used by external apps to invoke my app for example) without validating or sanitizing these Intents.
However, solution 1 in the article doesn't work in my case because my component needs to receive Intents from other apps.
Solution 2 is not applicable to my case because I don't know in advance which app would invoke my app, so I don't know what would getCallingActivity returns.
Solution 3 seems to be the most promising one, I tried to removeFlags of intents, however, when I resubmit my app, Google Play again alerts this vulnerability. I am about to try checking whether an Intent grants a URI permission using methods like getFlags and submit my app again to see the result. Does anyone know how do Google check this vulnerability anyway, and could someone spot the vulnerability in my source code and suggest a way to resolve it?
The exact message from Google Play is
Intent Redirection
Your app(s) are vulnerable to Intent Redirection.
To address this issue, follow the steps in this Google Help Center article.
com.mydomain.somepackage.a->a
And the following is the simplified source code.
// MainActivity.java
public class MainActivity extends CordovaActivity
{
SpecialUtil specialUtil;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
specialUtil = new specialUtil(MainActivity.this);
}
#Override
public void onResume() {
super.onResume();
specialUtil.verifyServerIfNeeded(MainActivity.this);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == this.specialUtil.CERT_INVALID_POPUP_REQUEST_CODE) {
// the user clicked the return button in the alert dialog within WhiteScreen activity
this.specialUtil.declareAsFailure();
}
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
}
}
// com/mydomain/somepackage/SpecialUtil.java
public class SpecialUtil {
private SharedPreferences mSharedPreferences;
private SharedPreferences.Editor mSharedPreferencesEditor;
private SharedPreferences.OnSharedPreferenceChangeListener listener;
private Activity activity;
private boolean shownCertInvalidPopup = false;
public final int CERT_INVALID_POPUP_REQUEST_CODE = 1000;
public SpecialUtil(Activity activity) {
this.activity = activity;
this.mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity);
this.mSharedPreferencesEditor = mSharedPreferences.edit();
this.listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals("SOME_RESULT")) {
String result = mSharedPreferences.getString("SOME_RESULT", "");
if (result.equals("RESULT_OK")) {
SpecialUtil.this.declareAsSuccess();
} else if (result.equals("RESULT_CANCELED")) {
SpecialUtil.this.declareAsFailure();
}
}
}
};
this.mSharedPreferences.registerOnSharedPreferenceChangeListener(listener);
}
public void verifyServerIfNeeded(Activity activity) {
Intent intent = activity.getIntent();
if (this.isFlowA(intent)) {
this.removePermission(intent);
String url = intent.getStringExtra("url");
this.verifyServer(url);
} else if (this.isFlowB(intent)) {
this.removePermission(intent);
String payment_request_object_url = intent.getData().getQueryParameter("pay_req_obj");
String callback_url = intent.getData().getQueryParameter("callback");
this.verifyServer(payment_request_object_url);
}
}
public boolean isFlowA(Intent intent) {
if (intent.getAction().equals("someAction")) {
return true;
}
return false;
}
public boolean isFlowB(Intent intent) {
if (intent.getData() != null) {
String path = intent.getData().getPath();
if (path.equals("something")) {
return true;
}
}
return false;
}
public void verifyServer(final String httpsURL) {
new Thread(new Runnable() {
#Override
public void run() {
try {
boolean isCertValid = SpecialUtil.this.verify(httpsURL);
if (isCertValid) {
// do somthing
} else {
// show a white screen with an alert msg
SpecialUtil.this.activity.runOnUiThread(new Runnable() {
public void run() {
if (!shownCertInvalidPopup) {
shownCertInvalidPopup = true;
Intent intent = new Intent(SpecialUtil.this.activity, WhiteScreen.class);
SpecialUtil.this.activity.startActivityForResult(intent, CERT_INVALID_POPUP_REQUEST_CODE);
}
}
});
}
} catch (IOException e) {
SpecialUtil.this.declareAsFailure();
}
}
}).start();
}
private void declareAsSuccess() {
this.activity.setResult(Activity.RESULT_OK, SpecialUtil.this.activity.getIntent());
this.activity.finishAndRemoveTask();
}
public void declareAsFailure() {
this.activity.setResult(Activity.RESULT_CANCELED, this.activity.getIntent());
this.activity.finishAndRemoveTask();
}
private void removePermission(Intent intent) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
intent.removeFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.removeFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
}
}
// com/mydomain/somepackage/WhiteScreen.java
public class WhiteScreen extends Activity {
SpecialUtil specialUtil;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
specialUtil = new SpecialUtil(WhiteScreen.this);
String title = "someTitle";
final AlertDialog.Builder builder = new AlertDialog.Builder(WhiteScreen.this)
.setTitle(title)
.setPositiveButton(btn_text, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Don't start the process, quit App immediately
WhiteScreen.this.setResult(Activity.RESULT_CANCELED, WhiteScreen.this.getIntent());
WhiteScreen.this.finishAndRemoveTask();
}
});
AlertDialog alertDialog = builder.create();
alertDialog.show();
}
}
I want to pass data from an activity to a fragment via BroadcastReceiver. But I cant get the data in the fragment,cause the onReceive() there not fire up.
In my AppConfig:
public static final String CREATE_POST = "created_post";
In activity A implement all this stuff :
StringRequest request = new StringRequest(Request.Method.POST, AppConfig.MYURL, new Response.Listener<String>(){
#Override
public void onResponse(String response) {
Intent intent = new Intent(AppConfig.CREATE_POST);
intent.putExtra("myId",myId);
intent.putExtra("userId",userId);
intent.putExtra("username",username);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
In the fragment with should be receive the data I already implement this :
private BroadcastReceiver broadcasterReceiver;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//..other code here
broadcasterReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(AppConfig.CREATE_POST)){
Log.d("Broadcast","get called")
//HERE I get the intent here
}
}
}
//I already register the boardcast in onResume() and onPause()
#Override
public void onResume() {
super.onResume();
//register the broadcaster
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(broadcasterReceiver,
new IntentFilter(AppConfig.CREATE_POST));
}
#Override
public void onPause() {
super.onPause();
//unregister the broadcaster
LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(broadcasterReceiver);
}
What I still missing out? In order to make this work.
First of all, create an interface anywhere in your package. For eg -
public interface SyncDataListener {
void refreshDashboard(String myId, String username, String userId);
}
Then in your Activity, create a global declaration and setter/resetter methods like -
private SyncDataListener syncDataListener;
public void setSyncDataListener(SyncDataListener syncDataListener) {
this.syncDataListener = syncDataListener;
}
public void resetSyncDataListener(){
syncDataListener = null;
}
Next in your Fragment implement the above interface and override the method like -
public class DashboardFragment extends Fragment implements SyncDataListener {
#Override
public void refreshDashboard(String myId, String username, String userId) {
//Your code that deals with the data received from activity
}
}
Also in the Fragment's onAttach(Context context) method call the setter method created in the activity like -
#Override
public void onAttach(Context context) {
super.onAttach(context);
((MainActivity) getActivity()).setSyncDataListener(this);
}
Also make sure you reset the listener instance when your Fragment gets destroyed like -
#Override
public void onDestroyView() {
super.onDestroyView();
((MainActivity) getActivity()).resetSyncDataListener();
}
Now whenever you need to send data from Activity to Fragment you can call -
if (syncDataListener != null) {
syncDataListener.refreshDashboard(myId, username, userId);
}
Use this code , this is work in my side :
private BroadcastReceiver broadcasterReceiver;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//..other code here
broadcasterReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(AppConfig.CREATE_POST)){
Log.d("Broadcast","get called")
//HERE I get the intent here
}
}
}
//I already register the boardcast in onResume() and onPause()
#Override
public void onResume() {
super.onResume();
//register the broadcaster
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(broadcasterReceiver,
new IntentFilter(AppConfig.CREATE_POST));
}
#Override
public void onPause() {
super.onPause();
//unregister the broadcaster
LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(broadcasterReceiver);
}
use this code to register the broadcast
I'm working with Neura sdk in order to detect special events of my users( arriving/leaving home)
I'm trying to initiate their authentication, as described below (fetchPermissions() and initNeuraConnection() are the same as in the documentations, and mAuthenticationRequest is initiated on fetchPermissions())
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getMainActivity().initNeuraConnection();
fetchPermissions();
getMainActivity().getClient().authenticate(NEURA_AUTHENTICATION_REQUEST_CODE, mAuthenticateRequest);
}
My issue is that once i call authenticate - nothing happens and the neura login screen isn't opened
There are few things you can check :
Have you declared initNueraConnection() and fetchPermissions() as described in the Neura dev site ?
If so, I suspect you're sending authenticate(...) a nullable mAuthenticateRequest instance.
Since fetchPermissions() is asynchronous(its a network call), you're calling authenticate(...) before the results are fetched from fetchPermissions(), so, mAuthenticateRequest is null, since it's not initiated yet.
You should call authenticate(...) only after you recieve the data on fetchPermissions().
For example, you can do this :
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getMainActivity().initNeuraConnection();
fetchPermissions();
}
private void fetchPermissions() {
loadProgress(true);
getMainActivity().getClient().getAppPermissions(new GetPermissionsRequestCallbacks() {
#Override
public void onSuccess(final List<Permission> permissions) throws RemoteException {
if (getActivity() == null)
return;
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
loadProgress(false);
mPermissions = new ArrayList<>(permissions);
mAuthenticateRequest = new AuthenticationRequest();
mAuthenticateRequest.setAppId(getMainActivity().getClient().getAppUid());
mAuthenticateRequest.setAppSecret(getMainActivity().getClient().getAppSecret());
mAuthenticateRequest.setPermissions(mPermissions);
getMainActivity().getClient().authenticate(NEURA_AUTHENTICATION_REQUEST_CODE, mAuthenticateRequest);
}
});
}
#Override
public void onFailure(Bundle resultData, int errorCode) throws RemoteException {
loadProgress(false);
mRequestPermissions.setEnabled(true);
}
#Override
public IBinder asBinder() {
return null;
}
});
}
Fyi, you can check your logcat for this error : authenticationRequest is nullable, couldn't create authenticate request.
I am trying to create chat in app using sampe-chat codes.
I authorized user, got opponend id, after that successfully created dialog:
ChatService.initIfNeed(this);
QBUsers.getUserByLogin(login, new QBEntityCallbackImpl<QBUser>() {
#Override
public void onSuccess(QBUser qbUser, Bundle args) {
QBPrivateChatManager privateChatManager = QBChatService.getInstance().getPrivateChatManager();
privateChatManager.createDialog(qbUser.getId(), new QBEntityCallbackImpl<QBDialog>() {
#Override
public void onSuccess(QBDialog dialog, Bundle args) {
ChatActivity.this.dialog = dialog;
setContentView(R.layout.activity_chat);
initViews();
if (isSessionActive()) {
initChat();
}
ChatService.getInstance().addConnectionListener(chatConnectionListener);
}
#Override
public void onError(List<String> errors) {
Log.e("chat", errors.toString());
}
});
}
#Override
public void onError(List errors) {
Log.e("chat", errors.toString());
}
});
But at initViews() I've got error at
else if (dialog.getType() == QBDialogType.PRIVATE) {
Integer opponentID = ChatService.getInstance().getOpponentIDForPrivateDialog(dialog);
companionLabel.setText(ChatService.getInstance().getDialogsUsers().get(opponentID).getLogin());
}
The error is because ChatService.getInstance().getDialogsUsers() returns 0 elements: , so ChatService.getInstance().getDialogsUsers().get(opponentID).getLogin() gives exception.
Any explanations of this I have not found in documentation:
Why I have got 0 elements at ChatService.getInstance().getDialogsUsers()? Dialog is successfully created.
The 'getDialogsUsers' is not an SDK method, it's from code sample
You can just look into it, what does it do
https://github.com/QuickBlox/quickblox-android-sdk/blob/master/sample-chat/src/com/quickblox/sample/chat/core/ChatService.java#L195
It returns a map of users, set in L172
https://github.com/QuickBlox/quickblox-android-sdk/blob/master/sample-chat/src/com/quickblox/sample/chat/core/ChatService.java#L172
Try to follow this file and move all needed logic to your app
I am trying to use the Google Drive API for Android to open a file. From the following official tutorial, I have the following:
GoogleDriveActivity.class
public class GoogleDriveActivity extends Activity implements GoogleApiClient.OnConnectionFailedListener, GoogleApiClient.ConnectionCallbacks {
private GoogleApiClient mGoogleApiClient;
private int REQUEST_CODE_RESOLUTION = 1;
private int REQUEST_CODE_OPENER = 2;
private ListView filesLv;
private DataBufferAdapter<Metadata> mResultsAdapter;
private String mNextPageToken;
private boolean hasMore;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_google_drive);
filesLv = (ListView) findViewById(R.id.listViewResults);
hasMore = true;
mResultsAdapter = new ResultsAdapter(this);
filesLv.setAdapter(mResultsAdapter);
filesLv.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int first, int visible, int total) {
if (mNextPageToken != null && first + visible + 5 < total) {
retrieveNextPage();
}
}
});
}
private void retrieveNextPage() {
// retrieve the results for the next page.
Query query = new Query.Builder()
.setPageToken(mNextPageToken)
.build();
Drive.DriveApi.query(mGoogleApiClient, query)
.setResultCallback(metadataBufferCallback);
}
private final ResultCallback<DriveApi.MetadataBufferResult> metadataBufferCallback = new
ResultCallback<DriveApi.MetadataBufferResult>() {
#Override
public void onResult(DriveApi.MetadataBufferResult result) {
if (!result.getStatus().isSuccess()) {
return;
}
mResultsAdapter.clear();
mResultsAdapter.append(result.getMetadataBuffer());
mNextPageToken = result.getMetadataBuffer().getNextPageToken();
hasMore = mNextPageToken != null;
}
};
#Override
public void onResume() {
super.onResume();
if (mGoogleApiClient == null) {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Drive.API)
.addScope(Drive.SCOPE_FILE)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
}
mGoogleApiClient.connect();
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_RESOLUTION && resultCode == RESULT_OK) {
mGoogleApiClient.connect();
}
}
#Override
protected void onPause() {
if (mGoogleApiClient != null) {
mGoogleApiClient.disconnect();
}
super.onPause();
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
if (!connectionResult.hasResolution()) {
return;
}
try {
connectionResult.startResolutionForResult(this, REQUEST_CODE_RESOLUTION);
} catch (IntentSender.SendIntentException e) {
}
}
#Override
public void onConnected(Bundle bundle) {
retrieveNextPage();
}
#Override
public void onConnectionSuspended(int i) {
}
}
The ResultsAdapter.class:
public class ResultsAdapter extends DataBufferAdapter<Metadata> {
public ResultsAdapter(Context context) {
super(context, android.R.layout.simple_list_item_1);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = View.inflate(getContext(),
android.R.layout.simple_list_item_1, null);
}
Metadata metadata = getItem(position);
TextView titleTextView =
(TextView) convertView.findViewById(android.R.id.text1);
titleTextView.setText(metadata.getTitle());
return convertView;
}
}
I am including the dependency in the Gradle file like this:
compile 'com.google.android.gms:play-services-drive:7.8.0'
The Activity in the Manifest.xml looks like the following:
<activity
android:name="com.myproject.GoogleDriveActivity"
android:label="#string/app_name"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustPan">
<meta-data android:name="com.google.android.apps.drive.APP_ID" android:value="id=<google project number>"/>
</activity>
Please note that I have added the SHA1 to the Google API condole with the package name. Also, the fields in the content screen are filled out as explained here.
When I try to run this code, I keep getting the following error message in the onConnectionFailed callback:
{statusCode=INTERNAL_ERROR, resolution=null}
Any idea on what could be going wrong? I am not able to figure out what the problem is.
I found an answer. The problem was the debug key. Essentially, I ran the keytool command and generated the SHA1 which I then added to the API console on Google. I then ran the project from Android Studio. This was giving me the error.
I then created a new keystore from Android Studio -> Menu -> Build -> Generate signed apk. Ran the same command to generate SHA1 which I uploaded to the API console. Then with the updated apk file, I was able to get the contents of the file.
The problem was that the key could not be authenticated by Google.