Google Play Sign in Failure Api Exception 4 - google-play-services

Im trying to integrate google sign in and took the code from another project that it works on, but for the new app it goes thru the log in but the dialog to allow doesnt show up and instead I get sign in failure with Api Exception 4.
Here is all the applicable code:
//Create the client used to sign in to Google services
mGoogleSignInClient = GoogleSignIn.getClient(this,
new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN)
.requestServerAuthCode(getString(R.string.client_id))
.requestEmail()
.build());
private void signInSilently(){
Log.d(TAG, "signInSilently()");
mGoogleSignInClient.silentSignIn().addOnCompleteListener(this,
new OnCompleteListener<GoogleSignInAccount>() {
#Override
public void onComplete(#NonNull Task<GoogleSignInAccount> task) {
if (task.isSuccessful()) {
Log.d(TAG, "signInSilently(): success");
onConnected(task.getResult());
} else {
Log.d(TAG, "signInSilently(): failure", task.getException());
onDisconnected();
}
}
});
}
private void startSignInIntent() {
startActivityForResult(mGoogleSignInClient.getSignInIntent(), RC_SIGN_IN);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_SIGN_IN) {
Task<GoogleSignInAccount> task =
GoogleSignIn.getSignedInAccountFromIntent(data);
try {
GoogleSignInAccount account = task.getResult(ApiException.class);
onConnected(account);
} catch (ApiException apiException) {
String message = apiException.getMessage();
if (message == null || message.isEmpty()) {
message = getString(R.string.signin_other_error);
}
onDisconnected();
new AlertDialog.Builder(this)
.setMessage(message)
.setNeutralButton(android.R.string.ok, null)
.show();
}
}
}
private void onDisconnected() {
Log.d(TAG, "onDisconnected()");
mPlayersClient = null;
// Show sign-in button on main menu
if(game.user != null) {
game.user.setDisplayName(null);
game.user.setPlayerID(null);
}
}
private void onConnected(final GoogleSignInAccount googleSignInAccount) {
Log.d(TAG, "onConnected(): connected to Google APIs");
GamesClient gamesClient = Games.getGamesClient(AndroidLauncher.this, googleSignInAccount);
View view = ((AndroidGraphics) Gdx.graphics).getView();
gamesClient.setViewForPopups(view);
gamesClient.setGravityForPopups(Gravity.TOP | Gravity.CENTER_HORIZONTAL);
mPlayersClient = Games.getPlayersClient(this, googleSignInAccount);
// Set the greeting appropriately on main menu
mPlayersClient.getCurrentPlayer()
.addOnCompleteListener(new OnCompleteListener<Player>() {
#Override
public void onComplete(#NonNull Task<Player> task) {
String displayName;
if (task.isSuccessful()) {
user = new User();
user.setDisplayName(task.getResult().getDisplayName());
user.setPlayerID(task.getResult().getPlayerId());
game.user = user;
firebaseAuthWithPlayGames(googleSignInAccount);
} else {
Exception e = task.getException();
handleException(e, getString(R.string.players_exception));
displayName = "???";
}
}
});
}
private void handleException(Exception e, String details) {
int status = 0;
if (e instanceof ApiException) {
ApiException apiException = (ApiException) e;
status = apiException.getStatusCode();
}
String message = getString(R.string.status_exception_error, details, status, e);
new AlertDialog.Builder(AndroidLauncher.this)
.setMessage(message)
.setNeutralButton(android.R.string.ok, null)
.show();
}

Related

Can't read the value that comes with the GATT Service characteristic; Not sure if I setup the GATT server correctly

The BLE is advertising just fine. I can establish a connection with it but I can't read the characteristic value I set here on the initialization of GATT server.
This is the GATT Server setup
#ReactMethod
public void startServer(Callback srvCallBack) {
mBluetoothGattServer = mBluetoothManager.openGattServer(getReactApplicationContext(), mGattServerCallback);
if (mBluetoothGattServer == null) {
srvCallBack.invoke(false);
return;
}
BluetoothGattService gattService = new BluetoothGattService(SERVICE_UUID,
BluetoothGattService.SERVICE_TYPE_PRIMARY);
BluetoothGattCharacteristic mCharacteristics = new BluetoothGattCharacteristic(SERVICE_UUID, 2, 1);
String currentDeviceName = BluetoothAdapter.getDefaultAdapter().getName();
boolean isValAdded = mCharacteristics.setValue(currentDeviceName);
if (isValAdded == false) {
Toast.makeText(getReactApplicationContext(), "Can't add value to characteristics.", Toast.LENGTH_SHORT)
.show();
} else {
Toast.makeText(getReactApplicationContext(), "Value added to characteristics.", Toast.LENGTH_SHORT).show();
}
boolean isCharAdded = gattService.addCharacteristic(mCharacteristics);
if (isCharAdded == false) {
Toast.makeText(getReactApplicationContext(), "Can't add characteristics.", Toast.LENGTH_SHORT).show();
}
boolean isServAdded = mBluetoothGattServer.addService(gattService);
if (isServAdded == true) {
srvCallBack.invoke(true, gattService.getUuid().toString());
return;
}
}
Im not sure if the problem is with the GATT server callback, or is it even needed? Here is my callback
private BluetoothGattServerCallback mGattServerCallback = new BluetoothGattServerCallback() {
#Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
return;
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
return;
}
}
#Override
public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset,
BluetoothGattCharacteristic characteristic) {
if (SERVICE_UUID.equals(characteristic.getUuid())) {
Toast.makeText(getReactApplicationContext(), "Sending device name.", Toast.LENGTH_SHORT).show();
mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0,
characteristic.getValue());
} else {
Toast.makeText(getReactApplicationContext(), "uuid didn't matched.", Toast.LENGTH_SHORT).show();
}
}
};

onMessageReceived method is not called, when app is not open

I have implemented FCM in my app, and I need to pass some data from Firebase service to Activity. I have implemented the following code, which works fine, when the app is in foreground(open). When the app is killed or in background, onMessageReceived method is not called, and the launcher activity is loaded while click on the push notification. Also, when the app is open, push message is blank. Kindly advise what I have done wrong. FYI, from backend, they are sending data payload, not notification.
public class FirebaseMessagingService extends com.google.firebase.messaging.FirebaseMessagingService {
public FirebaseMessagingService() {
}
private static final String TAG = com.google.firebase.messaging.FirebaseMessagingService.class.getSimpleName();
public static String CHAT_PUSH_NOTIFICATION_INTENT = "chatPushNotificationIntent";
private PreferencesManager preferencesManager;
#Override
public void onNewToken(String s) {
super.onNewToken(s);
Log.e("push token >>", s);
String reliableIdentifier = FirebaseInstanceId.getInstance().getId();
FCMPreferencesManager pref = FCMPreferencesManager.getInstance(this);
if (pref != null) {
pref.setStringValue(FCMPreferencesManager.FCM_KEY_VALUE, s);
pref.setStringValue(FCMPreferencesManager.DEVICE_ID, reliableIdentifier);
}
}
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
try {
preferencesManager = PreferencesManager.getInstance(this);
int userId = preferencesManager.getIntValue(PreferencesManager.LOGIN_USER_ID);
Log.e("onMessage received >>", "inside service");
Log.e("userId >>", userId + "");
if (userId > 0) {
Log.e("remote message >>", remoteMessage.getNotification().getBody() + "");
if (remoteMessage.getData().size() > 0) {
Log.e(TAG, "Data Payload: " + remoteMessage.getData().toString());
try {
JSONObject json = new JSONObject(remoteMessage.getData().toString());
handleDataMessage(json);
} catch (Exception e) {
e.printStackTrace();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void classApprovedNotification(int jobId, String stage) {
if (!Utils.isAppIsInBackground(getApplicationContext())) {
Intent pushNotification = new Intent(Constants.PUSH_NOTIFICATION_INTENT);
pushNotification.putExtra("jobId", jobId);
pushNotification.putExtra("stage", stage);
LocalBroadcastManager.getInstance(this).sendBroadcast(pushNotification);
}
}
private void handleDataMessage(JSONObject json) {
try {
Log.e("total json >> ", json.toString());
JSONObject data = json.optJSONObject("data");
Log.e("data >> ", data.toString());
String title = data.optString("title");
String message = data.optString("message");
Log.e("title >>", title);
Log.e("message >>", message);
localNotification(data, title, message, false);
} catch (Exception e) {
Log.e(TAG, "Json Exception: " + e.getMessage());
}
}
private void localNotification(JSONObject data, String title, String message, boolean isSendBird) {
int type = 0, groupId = 0, classId = 0, jobId = 0;
String stage = "";
int notificationId = (int) System.currentTimeMillis();
int userId = preferencesManager.getIntValue(PreferencesManager.LOGIN_USER_ID);
String className = "", fileName = "";
if (data != null) {
jobId = data.optInt("job_id");
stage = data.optString("stage", "");
}
Log.e("jobId in service >>", jobId + "");
Log.e("stage in service >>", stage);
Intent intent = new Intent(FirebaseMessagingService.this, VendorHomeActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.putExtra("jobId", jobId);
intent.putExtra("stage","stage");
int requestID = (int) System.currentTimeMillis();
final PendingIntent resultPendingIntent =
PendingIntent.getActivity(
this,
requestID,
intent,
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
);
String channelId = "";
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.fyxt_logo)
.setContentTitle(title)
.setContentText(message)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(resultPendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
/* NotificationManagerCompat notificationManager =
NotificationManagerCompat.from(this);*/
// Since android Oreo notification channel is needed.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(channelId,
"Channel human readable title",
NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(channel);
}
notificationManager.notify(notificationId /* ID of notification */, notificationBuilder.build());
try {
PowerManager.WakeLock screenLock = null;
if ((getSystemService(POWER_SERVICE)) != null) {
screenLock = ((PowerManager) getSystemService(POWER_SERVICE)).newWakeLock(
PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, "OOTUSER:WAKE");
screenLock.acquire(10 * 60 * 1000L /*10 minutes*/);
screenLock.release();
}
} catch (Exception e) {
e.printStackTrace();
}
classApprovedNotification(jobId, stage);
}
}
In my Activity, I have the following code.
private BroadcastReceiver notificationReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
jobIdFromNotification = intent.getIntExtra("jobId", 0);
stageFromNotification = intent.getStringExtra("stage");
Log.e("jobIdFromNotification >>", jobIdFromNotification + "");
Log.e("stageFromNotification >>", stageFromNotification);
prefManager.setIntValue(PreferencesManager.JOB_ID_IN_PREF, jobIdFromNotification);
prefManager.setStringValue(PreferencesManager.JOB_STAGE_IN_PREF, stageFromNotification);
classApprovedViewUpdate();
}
};
private void classApprovedViewUpdate() {
if (jobIdFromNotification > 0) {
fragmentInteractionCallback = (BaseFragment.FragmentInteractionCallback) this;
Log.e("inside push receiver update ", "sfs");
if (stageFromNotification.trim().equalsIgnoreCase(Constants.STAGE_TICKET_APPROVAL)) {
sendActionToActivity(ACTION_CREATE_MAINTENANCE_REQUEST, currentTab, true, fragmentInteractionCallback);
}
}
}
Edit:
data payload:
{
"data": {
"type": 0,
"job_id": 123,
"stage": "STAGE_TICKET_APPROVAL",
}

org.apache.fop.fo.flow.ExternalGraphic catches and logs ImageException I want to handle myself

I am transforming an Image into pdf for test purposes.
To ensure that the Image is compatible with the printing process later on, I'm running a quick test print during the upload.
I'm creating a simple Test-PDF with a transformer. When I try to print an image with an incompatible format, the ImageManager of the transformer throws an ImageException, starting in the preloadImage() function:
public ImageInfo preloadImage(String uri, Source src)
throws ImageException, IOException {
Iterator iter = registry.getPreloaderIterator();
while (iter.hasNext()) {
ImagePreloader preloader = (ImagePreloader)iter.next();
ImageInfo info = preloader.preloadImage(uri, src, imageContext);
if (info != null) {
return info;
}
}
throw new ImageException("The file format is not supported. No ImagePreloader found for "
+ uri);
}
throwing it to:
public ImageInfo needImageInfo(String uri, ImageSessionContext session, ImageManager manager)
throws ImageException, IOException {
//Fetch unique version of the URI and use it for synchronization so we have some sort of
//"row-level" locking instead of "table-level" locking (to use a database analogy).
//The fine locking strategy is necessary since preloading an image is a potentially long
//operation.
if (isInvalidURI(uri)) {
throw new FileNotFoundException("Image not found: " + uri);
}
String lockURI = uri.intern();
synchronized (lockURI) {
ImageInfo info = getImageInfo(uri);
if (info == null) {
try {
Source src = session.needSource(uri);
if (src == null) {
registerInvalidURI(uri);
throw new FileNotFoundException("Image not found: " + uri);
}
info = manager.preloadImage(uri, src);
session.returnSource(uri, src);
} catch (IOException ioe) {
registerInvalidURI(uri);
throw ioe;
} catch (ImageException e) {
registerInvalidURI(uri);
throw e;
}
putImageInfo(info);
}
return info;
}
}
throwing it to :
public ImageInfo getImageInfo(String uri, ImageSessionContext session)
throws ImageException, IOException {
if (getCache() != null) {
return getCache().needImageInfo(uri, session, this);
} else {
return preloadImage(uri, session);
}
}
Finally it gets caught and logged in the ExternalGraphic.class:
/** {#inheritDoc} */
public void bind(PropertyList pList) throws FOPException {
super.bind(pList);
src = pList.get(PR_SRC).getString();
//Additional processing: obtain the image's intrinsic size and baseline information
url = URISpecification.getURL(src);
FOUserAgent userAgent = getUserAgent();
ImageManager manager = userAgent.getFactory().getImageManager();
ImageInfo info = null;
try {
info = manager.getImageInfo(url, userAgent.getImageSessionContext());
} catch (ImageException e) {
ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
getUserAgent().getEventBroadcaster());
eventProducer.imageError(this, url, e, getLocator());
} catch (FileNotFoundException fnfe) {
ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
getUserAgent().getEventBroadcaster());
eventProducer.imageNotFound(this, url, fnfe, getLocator());
} catch (IOException ioe) {
ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
getUserAgent().getEventBroadcaster());
eventProducer.imageIOError(this, url, ioe, getLocator());
}
if (info != null) {
this.intrinsicWidth = info.getSize().getWidthMpt();
this.intrinsicHeight = info.getSize().getHeightMpt();
int baseline = info.getSize().getBaselinePositionFromBottom();
if (baseline != 0) {
this.intrinsicAlignmentAdjust
= FixedLength.getInstance(-baseline);
}
}
}
That way it isn't accessible for me in my code that uses the transformer.
I tried to use a custom ErrorListener, but the transformer only registers fatalErrors to the ErrorListener.
Is there any way to access the Exception and handle it myself without changing the code of the library?
It was easier than I thought. Before I call the transformation I register a costum EventListener to the User Agent of the Fop I'm using. This Listener just stores the Information what kind of Event was triggered, so I can throw an Exception if it's an ImageError.
My Listener:
import org.apache.fop.events.Event;
import org.apache.fop.events.EventListener;
public class ImageErrorListener implements EventListener
{
private String eventKey = "";
private boolean imageError = false;
#Override
public void processEvent(Event event)
{
eventKey = event.getEventKey();
if(eventKey.equals("imageError")) {
imageError = true;
}
}
public String getEventKey()
{
return eventKey;
}
public void setEventKey(String eventKey)
{
this.eventKey = eventKey;
}
public boolean isImageError()
{
return imageError;
}
public void setImageError(boolean imageError)
{
this.imageError = imageError;
}
}
Use of the Listener:
// Start XSLT transformation and FOP processing
ImageErrorListener imageListener = new ImageErrorListener();
fop.getUserAgent().getEventBroadcaster().addEventListener(imageListener);
if (res != null)
{
transformer.transform(xmlDomStreamSource, res);
}
if(imageListener.isImageError()) {
throw new ImageException("");
}
fop is of the type Fop ,xmlDomStreamSource ist the xml-Source I want to transform and res is my SAXResult.

Null pointer Exception when using volley

i am using volley to fetch data from database.I always get null pointer error. I don't know whats the error. it always executes onErrorResponse() (error.null) and shows this in my logcat
BasicNetwork.performRequest: Unexpected response code 301
here is my java method to fetch data from php file .
private void fetchChatThread() {
StringRequest strReq = new StringRequest(Request.Method.POST,
EndPoints.chatMessages, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONObject obj = new JSONObject(response);
Load_datato_list load_data_tolist = new Load_datato_list();
load_data_tolist.execute(obj);
} catch (JSONException e) {
Log.e(TAG, "json parsing error: " + e.getMessage());
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Messages messages=new Messages();
error.printStackTrace();
NetworkResponse networkResponse = error.networkResponse;
Log.e(TAG, " Try Again fetch error" + networkResponse);
Toast.makeText(Chat_Rooms.this, messages.getSender_id(), Toast.LENGTH_SHORT).show();
}
}) {
#Override
protected Map<String, String> getParams() {
Map<String, String> myparams = new HashMap<String, String>();
try {
myparams.put("sender_userid", sender_userid);
myparams.put("reciver_id", String_username.reciver_user_id);
} catch (Exception e) {
e.printStackTrace();
}
Log.e(TAG, "Params: " + myparams.toString());
return myparams;
}
};
//Adding request to request queue
addToRequestQueue(strReq);
}
here is my private that loads data from php to objects
private class Load_datato_list extends AsyncTask<JSONObject, Void, ArrayList<Messages>> {
#Override
protected ArrayList<Messages> doInBackground(JSONObject[] params) {
JSONObject obj = params[0];
try {
JSONArray commentsObj = obj.getJSONArray("messages");
for (int i = 0; i < commentsObj.length(); i++) {
JSONObject commentObj = (JSONObject) commentsObj.get(i);
String commentId = commentObj.getString("id");
String commentText = commentObj.getString("message");
String createddate = commentObj.getString("date");
String commentuser_id = commentObj.getString("user_id_fk");
URL url;
Bitmap image, image1;
try {
url = new URL(Base_URL.BASE_URL_IMAGES + "user_profile_pictures" + commentuser_id + ".jpg");
image1 = BitmapFactory.decodeStream(url.openStream());
image = getResizedBitmap(image1, 50);
} catch (Exception e) {
image = null;
e.printStackTrace();
}
Users user;
user = new Users(commentuser_id, String_username.user_name, image);
Messages message = new Messages();
message.setId(commentId);
message.setMessage(commentText);
message.setCreatedAt(createddate);
message.setSender_id(commentuser_id);
message.setUser(user);
messageArrayList.add(message);
}
} catch (Exception e) {
e.printStackTrace();
}
return messageArrayList;
}
#Override
protected void onPostExecute(ArrayList<Messages> messageArrayList) {
Collections.reverse(messageArrayList);
chatroom_adapter.notifyDataSetChanged();
if (chatroom_adapter.getItemCount() > 1) {
rec_chatBubble.getLayoutManager().smoothScrollToPosition(rec_chatBubble, null, chatroom_adapter.getItemCount() - 1);
}
}
}
TRY THIS
private void fetchChatThread(final String count) {
StringRequest strReq = new StringRequest(Request.Method.POST,
EndPoints.chatMessages, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONObject obj = new JSONObject(response);
Log.d("response error", response);
Load_datato_list load_data_tolist = new Load_datato_list();
load_data_tolist.execute(obj);
} catch (JSONException e) {
Log.e(TAG, "json parsing error: " + e.getMessage());
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Messages messages = new Messages();
error.printStackTrace();
NetworkResponse networkResponse = error.networkResponse;
Log.e(TAG, " Try Again fetch error" + networkResponse);
}
}) {
#Override
protected HashMap<String, String> getParams() {
HashMap<String, String> myparams = new HashMap<String, String>();
try {
myparams.put("sender_userid", sender_userid);
myparams.put("reciver_id", String_username.reciver_user_id);
myparams.put("count", count);
} catch (Exception e) {
e.printStackTrace();
}
Log.e(TAG, "Params: " + myparams.toString());
return myparams;
}
};
//Adding request to request queueok
addToRequestQueue(strReq);
}
AND THIS
private class Load_datato_list extends AsyncTask<JSONObject, Void, ArrayList<Messages>> {
#Override
protected ArrayList<Messages> doInBackground(JSONObject[] params) {
messageArrayList.clear();
JSONObject obj = params[0];
try {
JSONArray commentsObj = obj.getJSONArray("messages");
for (int i = 0; i < commentsObj.length(); i++) {
JSONObject commentObj = (JSONObject) commentsObj.get(i);
String Id = commentObj.getString("id");
String Text = commentObj.getString("message");
String date = commentObj.getString("date");
String yuser_id = commentObj.getString("user_id");
Messages message = new Messages();
message.setId(Id);
message.setMessage(Text);
message.setCreatedAt(date);
message.setSender_id(yuser_id);
messageArrayList.add(message);
}
} catch (Exception e) {
e.printStackTrace();
}
return messageArrayList;
}

Playing videos with TextureViews in RecyclerView, like Vine

I have a RecyclerView and each ViewHolder of the RecyclerView has a MediaPlayer object. I am using a TextureView to hold the MediaPlayer objects, the issue is that when i am scrolling down it is very laggy. Why is this the case? I believe I am properly deallocating the MediaPlayer objects when the surface is destroyed.
For a reference I am using the following TextureView implementation taken from this repo: TextureVideoView
TextureVideoView.java
public class TextureVideoView extends TextureView implements TextureView.SurfaceTextureListener {
// Indicate if logging is on
public static final boolean LOG_ON = true;
// Log tag
private static final String TAG = TextureVideoView.class.getName();
private MediaPlayer mMediaPlayer;
private float mVideoHeight;
private float mVideoWidth;
private boolean mIsDataSourceSet;
private boolean mIsViewAvailable;
private boolean mIsVideoPrepared;
private boolean mIsPlayCalled;
private ScaleType mScaleType;
private State mState;
public enum ScaleType {
CENTER_CROP, TOP, BOTTOM
}
public enum State {
UNINITIALIZED, PLAY, STOP, PAUSE, END
}
public TextureVideoView(Context context) {
super(context);
initView();
}
public TextureVideoView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public TextureVideoView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
}
private void initView() {
if (!isInEditMode()) {
initPlayer();
setScaleType(ScaleType.CENTER_CROP);
setSurfaceTextureListener(this);
}
}
public void setScaleType(ScaleType scaleType) {
mScaleType = scaleType;
}
private void updateTextureViewSize() {
float viewWidth = getWidth();
float viewHeight = getHeight();
float scaleX = 1.0f;
float scaleY = 1.0f;
if (mVideoWidth > viewWidth && mVideoHeight > viewHeight) {
scaleX = mVideoWidth / viewWidth;
scaleY = mVideoHeight / viewHeight;
} else if (mVideoWidth < viewWidth && mVideoHeight < viewHeight) {
scaleY = viewWidth / mVideoWidth;
scaleX = viewHeight / mVideoHeight;
} else if (viewWidth > mVideoWidth) {
scaleY = (viewWidth / mVideoWidth) / (viewHeight / mVideoHeight);
} else if (viewHeight > mVideoHeight) {
scaleX = (viewHeight / mVideoHeight) / (viewWidth / mVideoWidth);
}
// Calculate pivot points, in our case crop from center
int pivotPointX;
int pivotPointY;
switch (mScaleType) {
case TOP:
pivotPointX = 0;
pivotPointY = 0;
break;
case BOTTOM:
pivotPointX = (int) (viewWidth);
pivotPointY = (int) (viewHeight);
break;
case CENTER_CROP:
pivotPointX = (int) (viewWidth / 2);
pivotPointY = (int) (viewHeight / 2);
break;
default:
pivotPointX = (int) (viewWidth / 2);
pivotPointY = (int) (viewHeight / 2);
break;
}
Matrix matrix = new Matrix();
matrix.setScale(scaleX, scaleY, pivotPointX, pivotPointY);
setTransform(matrix);
}
private void initPlayer() {
if (mMediaPlayer == null) {
mMediaPlayer = new MediaPlayer();
} else {
mMediaPlayer.reset();
}
mIsVideoPrepared = false;
mIsPlayCalled = false;
mState = State.UNINITIALIZED;
}
/**
* #see android.media.MediaPlayer#setDataSource(String)
*/
public void setDataSource(final String path) {
initPlayer();
((Activity) getContext()).runOnUiThread(new Runnable() {
#Override
public void run() {
try {
mMediaPlayer.setDataSource(path);
} catch (IOException e) {
e.printStackTrace();
}
}
});
mIsDataSourceSet = true;
prepare();
}
/**
* #see android.media.MediaPlayer#setDataSource(android.content.Context, android.net.Uri)
*/
public void setDataSource(Context context, Uri uri) {
initPlayer();
try {
mMediaPlayer.setDataSource(context, uri);
mIsDataSourceSet = true;
//prepare();
mMediaPlayer.setWakeMode(context.getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
} catch (IOException e) {
Log.d(TAG, e.getMessage());
}
}
/**
* #see android.media.MediaPlayer#setDataSource(java.io.FileDescriptor)
*/
public void setDataSource(AssetFileDescriptor afd) {
initPlayer();
try {
long startOffset = afd.getStartOffset();
long length = afd.getLength();
mMediaPlayer.setDataSource(afd.getFileDescriptor(), startOffset, length);
mIsDataSourceSet = true;
prepare();
} catch (IOException e) {
Log.d(TAG, e.getMessage());
}
}
private void prepare() {
try {
// Adjust the size of the MediaPlayer based on the Screen Resolution
mMediaPlayer.setOnVideoSizeChangedListener(
new MediaPlayer.OnVideoSizeChangedListener() {
#Override
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
mVideoWidth = width * metrics.density;
mVideoHeight = height * metrics.density;
updateTextureViewSize();
}
}
);
// Add a completion listener for when video ends and pass it to the listener
mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
mState = State.END;
if (mListener != null) {
mListener.onVideoEnd();
}
}
});
// don't forget to call MediaPlayer.prepareAsync() method when you use constructor for
// creating MediaPlayer
mMediaPlayer.prepareAsync();
// Play video when the media source is ready for playback.
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mIsVideoPrepared = true;
if (mIsPlayCalled && mIsViewAvailable) {
log("Player is prepared and play() was called.");
play();
}
if (mListener != null) {
mListener.onVideoPrepared();
}
}
});
} catch (IllegalArgumentException e) {
Log.d(TAG, "IllegalArgumentException");
Log.d(TAG, e.getMessage());
} catch (SecurityException e) {
Log.d(TAG, "SecurityException");
Log.d(TAG, e.getMessage());
} catch (IllegalStateException e) {
Log.d(TAG, "IllegalStateException");
Log.d(TAG, e.toString());
}
}
/**
* Play or resume video. Video will be played as soon as view is available and media player is
* prepared.
* <p/>
* If video is stopped or ended and play() method was called, video will start over.
*/
public void play() {
if (!mIsDataSourceSet) {
log("play() was called but data source was not set.");
return;
}
mIsPlayCalled = true;
if (!mIsVideoPrepared) {
log("play() was called but video is not prepared yet, waiting.");
return;
}
if (!mIsViewAvailable) {
log("play() was called but view is not available yet, waiting.");
return;
}
if (mState == State.PLAY) {
log("play() was called but video is already playing.");
return;
}
if (mState == State.PAUSE) {
log("play() was called but video is paused, resuming.");
mState = State.PLAY;
mMediaPlayer.start();
return;
}
if (mState == State.END || mState == State.STOP) {
log("play() was called but video already ended, starting over.");
mState = State.PLAY;
mMediaPlayer.seekTo(0);
mMediaPlayer.start();
return;
}
mState = State.PLAY;
mMediaPlayer.start();
}
/**
* Pause video. If video is already paused, stopped or ended nothing will happen.
*/
public void pause() {
if (mState == State.PAUSE) {
log("pause() was called but video already paused.");
return;
}
if (mState == State.STOP) {
log("pause() was called but video already stopped.");
return;
}
if (mState == State.END) {
log("pause() was called but video already ended.");
return;
}
mState = State.PAUSE;
if (mMediaPlayer.isPlaying()) {
mMediaPlayer.pause();
}
}
/**
* Stop video (pause and seek to beginning). If video is already stopped or ended nothing will
* happen.
*/
public void stop() {
if (mState == State.UNINITIALIZED) {
log("stop() was called but video already released.");
}
if (mState == State.STOP) {
log("stop() was called but video already stopped.");
return;
}
if (mState == State.END) {
log("stop() was called but video already ended.");
return;
}
mState = State.STOP;
if (mMediaPlayer.isPlaying()) {
Log.d(TAG, "MediaPlayer is playing");
mMediaPlayer.pause();
mMediaPlayer.seekTo(0);
mMediaPlayer.stop();
}
}
/**
* Reset the video (only if the video has been stopped). This will reset the mediaplayer, else
* nothing happens
*/
public void reset() {
if (mState == State.PLAY) {
log("reset() was called but video is currently playing.");
return;
}
if (mState == State.PAUSE) {
log("reset() was called but video is currently paused.");
return;
}
if (mState == State.STOP) {
mMediaPlayer.reset();
}
}
public void release() {
if (mState == State.PLAY) {
log("release() was called but video is currently playing.");
return;
}
if (mState == State.PAUSE) {
log("release() was called but video is currently paused.");
return;
}
if (mState == State.STOP) {
log("release() was called but video is currently stopped.");
return;
}
mMediaPlayer.release();
mMediaPlayer = null;
mState = State.UNINITIALIZED;
}
/**
* #see android.media.MediaPlayer#setLooping(boolean)
*/
public void setLooping(boolean looping) {
Log.d(TAG, "Looping");
mMediaPlayer.setLooping(looping);
}
/**
* #see android.media.MediaPlayer#seekTo(int)
*/
public void seekTo(int milliseconds) {
mMediaPlayer.seekTo(milliseconds);
}
/**
* #see android.media.MediaPlayer#getDuration()
*/
public int getDuration() {
return mMediaPlayer.getDuration();
}
static void log(String message) {
if (LOG_ON) {
Log.d(TAG, message);
}
}
private MediaPlayerListener mListener;
/**
* Listener trigger 'onVideoPrepared' and `onVideoEnd` events
*/
public void setListener(MediaPlayerListener listener) {
mListener = listener;
}
public interface MediaPlayerListener {
void onVideoPrepared();
void onVideoEnd();
}
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
Surface surface = new Surface(surfaceTexture);
mMediaPlayer.setSurface(surface);
mIsViewAvailable = true;
if (mIsDataSourceSet && mIsPlayCalled && mIsVideoPrepared) {
play();
}
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
// Deallocate MediaPlayer
if (mMediaPlayer != null) {
mMediaPlayer.pause();
mMediaPlayer.stop();
mMediaPlayer.reset();
mMediaPlayer.release();
mMediaPlayer = null;
}
return false;
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
}
When I get an asset downloaded I serve it inside my adapter like so:
final TextureVideoView videoView = holder.videoview;
videoView.setDataSource(path);
videoView.setLooping(true);
videoView.setScaleType(TextureVideoView.ScaleType.CENTER_CROP);
videoView.play();
The issue is that when I am scrolling it is really laggy, how can I fix this? How can I make a RecyclerView like Vine where the TextureView pauses unless the view is mostly visible.
Looking for a very thorough answer to this question and good optimized implementation.