I am new to the community and I have joined because of the need to highlight a problem that I have not been able to solve.
Thank you in advance for any answer you can give me to help me.
I am currently developing a project in android studio and I am using as a firebase database, I have come to have the need to share a product, post or list that is within my application. Because of this I have decided to use Firebase Dynamic Link to share some specific object.
My current code tries to create a link and share it, I would like to know what I am doing wrong, since it creates the link and lets me share it, but it is not getting the parameter that I am passing. In my case "Lid" is the parameter that I want to pass through the link and when clicking on the link, just take the part where the "Lid" is stored.
holder.BtnShare.setOnClickListener( new View.OnClickListener() {
#Override
public void onClick(View v) {
generateDeepLinkUrl(postCurrent.getLid());
String url="https://palsuper.page.link/lista_compartida";
FirebaseDynamicLinks.getInstance().createDynamicLink()
.setLink( Uri.parse(url))
.setDomainUriPrefix("https://palsuper.page.link")
.setAndroidParameters(
new DynamicLink.AndroidParameters.Builder("com.ibrahim.palsuper")
.setMinimumVersion(1)
.build())
.buildShortDynamicLink( ShortDynamicLink.Suffix.SHORT).addOnCompleteListener( new OnCompleteListener<ShortDynamicLink>() {
#Override
public void onComplete(#NonNull Task<ShortDynamicLink> task) {
if (task.isSuccessful()) {
Uri shortURL = task.getResult().getShortLink();
shareDeepLink(shortURL.toString());
} else {
Toast.makeText(mContext, "error", Toast.LENGTH_SHORT).show();
}
}
});
shareDeepLink( url );
}
} );
}
private String generateDeepLinkUrl(String Lid) {
return "https://palsuper.com/lista_compartida=" + Lid ;
}
private void shareDeepLink(String url) {
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/plain");
shareIntent.putExtra(Intent.EXTRA_TEXT, "Hey! check this content out " + url);
shareIntent.putExtra(Intent.EXTRA_SUBJECT, "Check this out !");
mContext.startActivity(Intent.createChooser(shareIntent, "Share this cool content"));
}
GetLink.
private void getDynamicLink() {
FirebaseDynamicLinks.getInstance()
.getDynamicLink(getIntent())
.addOnSuccessListener(this, new OnSuccessListener<PendingDynamicLinkData>() {
#Override
public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
// Get deep link from result (may be null if no link is found)
Uri deepLink = null;
if (pendingDynamicLinkData != null) {
deepLink = pendingDynamicLinkData.getLink();
Toast.makeText(HomeActivity.this, "Link obtenido del intent " + deepLink, Toast.LENGTH_SHORT).show();
// Log.d(TAG, "Link obtenido del intent " + deepLink.getPath());
getAndParseSharedData(deepLink.getPath());
}
}
})
.addOnFailureListener(this, new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
// Log.w(TAG, "getDynamicLink:onFailure", e);
}
});
}
private void getAndParseSharedData(String url) {
String pushKey = "";
if (url.contains("_")) {
String[] parts = url.split("=");
pushKey = parts[1];
// Log.d(TAG, "getAndParseSharedData: " + pushKey);
Toast.makeText(HomeActivity.this, pushKey, Toast.LENGTH_SHORT).show();
}
}
}
Joe you need to pass the parameter you want to fetch as a query parameter.
Like this:
"https://palsuper.com?lista_compartida=" + Lid;
or
"https://palsuper.com/lista_compartida?lista_compartida=" + Lid;
And then you can fetch it simply using this firebase provided method:
Uri deepLink = null;
if (pendingDynamicLinkData != null) {
deepLink = pendingDynamicLinkData.getLink();
String lista_compartida = deepLink.getQueryParameter("lista_compartida");
}
Hope this answer helps.
Go to firbase console dynamic link section and then create a dynamic link such as
https://xyz.page.link and then use below code to generate and share dynamic link
fun generateContentLink(): Uri? {
val baseUrl = Uri.parse(BASE_URL)
val domain = "https://xyz.page.link"
val link = FirebaseDynamicLinks.getInstance()
.createDynamicLink()
.setLink(baseUrl)
.setDomainUriPrefix(domain)
.setAndroidParameters(AndroidParameters.Builder("com.xyz").build())
.buildDynamicLink()
return link.uri
}
private fun onShareClicked() {
try {
val link = generateContentLink()
Log.e("DynamicLnk", "onShareClicked: " + link.toString())
val subject = " Welcome to QuickPe"
val msg =
"Hi,\nInviting you to join xyz\n an interesting app which provides you\n" +
"incredible offers on Recharge, Shopping & many more.\n\n" +
"Use my referrer code :\n\n " + session()?.getInviteCode().toString() +
"\n\nDownload app from link : \n"
val intent = Intent(Intent.ACTION_SEND)
intent.type = "text/plain"
intent.putExtra(Intent.EXTRA_SUBJECT, subject)
intent.putExtra(Intent.EXTRA_TEXT,
msg + "\n" + link + "/" + session()?.getInviteCode().toString())
if (intent.resolveActivity(packageManager) != null) {
startActivity(intent)
}
} catch (e: Exception) {
e.printStackTrace()
}
}
Now to receive code as below code in AndroidManifest.xml
<activity android:name=".xyzActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data
android:host="https://xyz.page.link"
android:scheme="https"/>
</intent-filter>
</activity>
and in activity where you want to receive code
FirebaseDynamicLinks.getInstance()
.getDynamicLink(intent)
.addOnSuccessListener(this
) { pendingDynamicLinkData ->
// Get deep link from result (may be null if no link is found)
var deepLink: Uri? = null
if (pendingDynamicLinkData != null) {
deepLink = pendingDynamicLinkData.link
}
if (deepLink != null) { //Intent.ACTION_VIEW.equals(appLinkAction) &&
val code = deepLink.lastPathSegment
edtReferralCode.setText(code)
}
// Handle the deep link. For example, open the linked
// content, or apply promotional credit to the user's
// account.
// ...
// ...
}
.addOnFailureListener(this
) { e -> Log.w("=================>", "getDynamicLink:onFailure", e) }
Related
From the Flutter side, using the PlatformChannel, I am navigating to an Android Java activity, and doing some processes.
The activity successfully opens and I'm able to do the functionality and have the final result of it.
How may I navigate back to the Flutter side to a specific page and pass a value?
P.S.: without going back to the same page and then redirecting to the
next page.
On the Flutter side:
I have these variables
/// Filters Method Channel
final filtersChannel = const MethodChannel('flutter.native/filters');
/// Filters Method Channel
final filtersResultChannel = const MethodChannel("flutter.native/result_filters");
I have a floatingActionButton with this function which invokes a MethodChannel
Future<void> startNewActivity() async {
try {
await filtersChannel.invokeMethod('open_filters');
} on PlatformException catch (e) {
debugPrint("Failed to Invoke: '${e.message}'.");
}
}
On the MainActivity.java
On the protected void onCreate(#Nullable Bundle savedInstanceState) function, I'm starting an activity which has the AR video recording like this:
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(this, FiltersActivity.class);
startActivity(intent);
}
On the FiltersActivity.java
On the public void configureFlutterEngine(#NonNull FlutterEngine flutterEngine) function
I’m defining and invoking my two channels:
The flutter.native/result_filters channel which builds the UI and
the functionality.
The flutter.native/filters channel which returns the final result.
Here:
#Override
public void configureFlutterEngine(#NonNull FlutterEngine flutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
String resultFiltersChannelIdentifier = "flutter.native/result_filters";
filtersResultChannel = new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), resultFiltersChannelIdentifier);
String filtersChannelIdentifier = "flutter.native/filters";
MethodChannel filtersChannel = new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), filtersChannelIdentifier);
filtersChannel.setMethodCallHandler(this::filtersMethodCallHandler);
}
Then, the flutter.native/filters displays the UI using the filtersMethodCallHandler function. Here:
private void filtersMethodCallHandler(MethodCall methodCall, MethodChannel.Result result) {
if (methodCall.method.equals("open_filters")) {
openUI();
} else {
result.notImplemented();
}
}
In the openUI function, I'm assigning the record button a function, here:
recordButton.setOnClickListener(this::toggleRecording);
And here's the toggleRecording function:
public void toggleRecording(View unusedView) {
boolean recording = videoRecorder.onToggleRecord();
if (recording) {
recordButton.setImageResource(R.drawable.round_stop);
Toast.makeText(this, "Started Recording", Toast.LENGTH_SHORT).show();
} else {
recordButton.setImageResource(R.drawable.round_videocam);
Toast.makeText(this, "Recording Stopped", Toast.LENGTH_SHORT).show();
videoPath = videoRecorder.getVideoPath().getAbsolutePath();
Toast.makeText(this, "Video saved: " + videoPath, Toast.LENGTH_SHORT).show();
Log.d(TAG, "Video saved: " + videoPath);
// Send notification of updated content.
ContentValues values = new ContentValues();
values.put(MediaStore.Video.Media.TITLE, "Sceneform Video");
values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
values.put(MediaStore.Video.Media.DATA, videoPath);
getContentResolver().insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);
filtersResultChannel.invokeMethod("filters_result", videoPath);
finish();
}
}
As shown above, I'm invoking the filters_result method for the filtersResultChannel channel and I'm adding the videoPath to it.
And then, I'm calling the finish(); method to close the FiltersActivity and return back to the MainAvtivity which successfully returns me to the Flutter page!
BACK to the Flutter side,
I'm listening to the filtersResultChannel like this:
#override
void initState() {
super.initState();
filtersResultChannel.setMethodCallHandler(_filtersResultHandler);
}
Future _filtersResultHandler(MethodCall methodCall) async {
if (methodCall.method == "filters_result") {
final videoPath = methodCall.arguments;
if (videoPath != null && videoPath.length >= 0) {
SchedulerBinding.instance.addPostFrameCallback((_) {
debugPrint("YES YES YES => $videoPath");
setState(() {
reportStatus = videoPath;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => VideoShow(clipPath: videoPath),
),
);
});
});
}
return null;
} else {
return null;
}
}
As shown above, I have a debugPrint statement, this statement prints the returned videoPath from the filtersResultChannel
<--------->
THE PROBLEM
<--------->
Even though I'm successfully getting the videoPath value and successfully returning back to the Flutter page, I'm NOT able to use it!!
The setState(); doesn't update the UI NOR navigate to the next screen, the VideoShow screen!
HOW MAY I FIX SUCH AN ISSUE?
I'm trying to save user information in Firestore. Basically I save the image in Storage, then I save the information like name, email and URL of the image in Firestore. The main problem I'm facing: if inside uploadTask/taskSnapshot I use it only to get the URL of the image and assign it to a String, and outside of uploadTask/taskSnapshot I save all information at once, the String imageURL is null, it only receives the value of uri.tostring inside the uploadTask/taskSnapshot block. Outside, it doesn't matter where I put String imageURL, locally or globally, it always gets null. That way, I can save all the user information, but with a null image. The best way would be is below, that inside uploadTask/taskSnapshot I already saved all the information. At first I had problems doing it this way, because the user was created through FirebaseAuth, the image was uploaded to Storage, but the information was not saved to the firestore. After so many changes it worked perfectly, I thought it was problem solved. But now I'm adding other activities and this one of registering a user stopped working again. Having the same problem of not saving the information in Firestore, it just creates the user through FirebaseAuth and saves the image.
That way it doesn't save the information in Firestore:
private void SalveDataUser() {
loading(true);
userID = FirebaseAuth.getInstance().getCurrentUser().getUid();
DocumentReference documentReference = db.collection(
Constants.KEY_COLLECTION_USERS).document(String.valueOf(userID));
if (imageUri != null) {
String filename = UUID.randomUUID().toString();
final StorageReference reference = FirebaseStorage.getInstance().getReference("/images/" + filename);
final UploadTask uploadTask = reference.putFile(imageUri);
uploadTask.addOnSuccessListener(taskSnapshot -> {
taskSnapshot.getStorage().getDownloadUrl().addOnSuccessListener(uri -> {
String imageUrl = uri.toString();
HashMap<String, Object> users = new HashMap<>();
users.put(Constants.KEY_NAME, binding.etName.getText().toString());
users.put(Constants.KEY_IMAGE, imageUrl);
users.put(Constants.KEY_EMAIL, binding.etEmail.getText().toString());
documentReference.set(users)
.addOnSuccessListener(unused -> {
loading(false);
preferenceManager.putBoolean(Constants.KEY_IS_SIGNED_IN, true);
preferenceManager.putString(Constants.KEY_USER_ID, String.valueOf(userID));
preferenceManager.putString(Constants.KEY_IMAGE, imageUrl);
preferenceManager.putString(Constants.KEY_NAME, binding.etName.getText().toString());
Intent intent = new Intent(getApplicationContext(), Profile.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
})
.addOnFailureListener(e -> {
showToast("Erro ao cadastrar usuário: " + e);
});
}).addOnFailureListener(e -> {
showToast("Error: " + e);
});
}).addOnFailureListener(e -> {
showToast("Error: " + e);
});
}
}
This way it saves in Firestore, but the image is null:
private void SalveDataUser() {
loading(true);
userID = FirebaseAuth.getInstance().getCurrentUser().getUid();
DocumentReference documentReference = db.collection(
Constants.KEY_COLLECTION_USERS).document(String.valueOf(userID));
if (imageUri != null) {
String filename = UUID.randomUUID().toString();
final StorageReference reference = FirebaseStorage.getInstance().getReference("/images/" + filename);
final UploadTask uploadTask = reference.putFile(imageUri);
final String[] imageUrl = new String[1];
uploadTask.addOnSuccessListener(taskSnapshot -> {
taskSnapshot.getStorage().getDownloadUrl().addOnSuccessListener(uri -> {
imageUrl[0] = uri.toString();
}).addOnFailureListener(e -> {
showToast("Error: " + e);
});
}).addOnFailureListener(e -> {
showToast("Error: " + e);
});
HashMap<String, Object> users = new HashMap<>();
users.put(Constants.KEY_NAME, binding.etName.getText().toString());
users.put(Constants.KEY_IMAGE, imageUrl[0]);
users.put(Constants.KEY_EMAIL, binding.etEmail.getText().toString());
documentReference.set(users)
.addOnSuccessListener(unused -> {
loading(false);
preferenceManager.putBoolean(Constants.KEY_IS_SIGNED_IN, true);
preferenceManager.putString(Constants.KEY_USER_ID, String.valueOf(userID));
preferenceManager.putString(Constants.KEY_IMAGE, imageUrl[0]);
preferenceManager.putString(Constants.KEY_NAME, binding.etName.getText().toString());
Intent intent = new Intent(getApplicationContext(), Profile.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
})
.addOnFailureListener(e -> {
showToast("Erro ao cadastrar usuário: " + e);
});
}
}
IMAGE PRINTSCREEN: This way it saves in Firestore, but the image is null
The problem isn't so much where you access the download URL, but when you access it. Because uploading files, getting their download URL, and writing to Firestore are all asynchronous operations, the order in which they execute is different from what you may expect.
This is easiest to see if you add some logging information:
userID = FirebaseAuth.getInstance().getCurrentUser().getUid();
DocumentReference documentReference = db.collection(
Constants.KEY_COLLECTION_USERS).document(String.valueOf(userID));
if (imageUri != null) {
String filename = UUID.randomUUID().toString();
final StorageReference reference = FirebaseStorage.getInstance().getReference("/images/" + filename);
Log.d("Firebase", "Starting image upload");
final UploadTask uploadTask = reference.putFile(imageUri);
uploadTask.addOnSuccessListener(taskSnapshot -> {
taskSnapshot.getStorage().getDownloadUrl().addOnSuccessListener(uri -> {
Log.d("Firebase", "Got download URL");
});
});
Log.d("Firebase", "Writing to Firestore");
HashMap<String, Object> users = new HashMap<>();
users.put(Constants.KEY_NAME, binding.etName.getText().toString());
users.put(Constants.KEY_IMAGE, imageUrl[0]);
users.put(Constants.KEY_EMAIL, binding.etEmail.getText().toString());
documentReference.set(users);
}
If you run this, it logs:
Starting image upload
Writing to Firestore
Got download URL
This is probably not what you expected, but it is by design and does explain why you're seeing null in the database: by the you write to Firestore, the image upload hasn't completed yet.
The solution for this problem is always the same: any code that requires certain part of data has to be inside the completion listener that runs when that data is available, or it has to be called from there, or otherwise synchronized.
So in your case, the easiest solution is to move the code that writes to Firestore into the completion handler for the download URL:
uploadTask.addOnSuccessListener(taskSnapshot -> {
taskSnapshot.getStorage().getDownloadUrl().addOnSuccessListener(uri -> {
String imageUrl = uri.toString();
HashMap<String, Object> users = new HashMap<>();
users.put(Constants.KEY_NAME, binding.etName.getText().toString());
users.put(Constants.KEY_IMAGE, imageUrl[0]);
users.put(Constants.KEY_EMAIL, binding.etEmail.getText().toString());
documentReference.set(users)
.addOnSuccessListener(unused -> {
loading(false);
preferenceManager.putBoolean(Constants.KEY_IS_SIGNED_IN, true);
preferenceManager.putString(Constants.KEY_USER_ID, String.valueOf(userID));
preferenceManager.putString(Constants.KEY_IMAGE, imageUrl);
preferenceManager.putString(Constants.KEY_NAME, binding.etName.getText().toString());
Intent intent = new Intent(getApplicationContext(), Profile.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
})
.addOnFailureListener(e -> {
showToast("Erro ao cadastrar usuário: " + e);
});
}).addOnFailureListener(e -> {
showToast("Error: " + e);
});
}).addOnFailureListener(e -> {
showToast("Error: " + e);
});
Also see:
getContactsFromFirebase() method return an empty list
Setting Singleton property value in Firebase Listener
After so many modifications, I managed to make it work perfectly. I used continueWithTask() before adding, after that I used addOnCompleteListener() to write to Firestore
Solução:
private void SalveDataUser(View v) {
loading(true);
userID = Objects.requireNonNull(FirebaseAuth.getInstance().getCurrentUser()).getUid();
if (imageUri != null) {
String filename = UUID.randomUUID().toString();
final StorageReference reference = FirebaseStorage.getInstance().getReference("/images/" + filename);
UploadTask uploadTask = reference.putFile(imageUri);
Task<Uri> uriTask = uploadTask.continueWithTask(task -> {
if (!task.isSuccessful()) {
throw Objects.requireNonNull(task.getException());
}
return reference.getDownloadUrl();
}).addOnCompleteListener(task -> {
if (task.isSuccessful()) {
Uri downloadUri = task.getResult();
Map<String, Object> users = new HashMap<>();
users.put(Constants.KEY_NAME, binding.etName.getText().toString());
users.put(Constants.KEY_IMAGE, downloadUri.toString());
users.put(Constants.KEY_EMAIL, binding.etEmail.getText().toString());
DocumentReference documentReference = db.collection(
Constants.KEY_COLLECTION_USERS).document(userID);
documentReference.set(users)
.addOnSuccessListener(unused -> {
loading(false);
preferenceManager.putBoolean(Constants.KEY_IS_SIGNED_IN, true);
preferenceManager.putString(Constants.KEY_USER_ID, userID);
preferenceManager.putString(Constants.KEY_IMAGE, downloadUri.toString());
preferenceManager.putString(Constants.KEY_NAME, binding.etName.getText().toString());
Intent intent = new Intent(getApplicationContext(), Profile.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
showToast("Cadastro realizado com sucesso");
})
.addOnFailureListener(e -> {
showToast("Erro ao cadastrar "+ e);
});
}
});
}
}
I have upgraded to android 11. I am having an issue downloading PDF files.
I have used this code:
private void createFile(Uri pickerInitialUri, String title) {
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("application/pdf");
intent.putExtra(Intent.EXTRA_TITLE, title);
// Optionally, specify a URI for the directory that should be opened in
// the system file picker when your app creates the document.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri);
}
startActivityForResult(intent, CREATE_FILE);
}
The file is created but the file is empty. I am still unable to save the downloaded pdf file.
I used to use DownloadManager request to download the pdf file from web.
DownloadManager downloadManager = (DownloadManager) this.getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Request request = new DownloadManager.Request(uri);
if (SDK_INT > Build.VERSION_CODES.Q) {
// Uri uri1 = Uri.fromFile(new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "")); //before android 11 this was working fine
// Uri uri1 = Uri.fromFile(new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), ""));
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE)
.setAllowedOverRoaming(true).setTitle(title + strDate + ".pdf")
.setDescription(description)
//.setDestinationUri(uri1) // before android 11 it was working fine.
.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, title + strDate + ".pdf") // file is not saved on this directory.
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);//to show the DOWNLOAD notification when completed
// createFile(uri , title + strDate + ".pdf"); // for new scoped storage
} else {
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE)
.setAllowedOverRoaming(true).setTitle(title + strDate + ".pdf")
.setDescription(description)
.setDestinationInExternalPublicDir(FileUtils.downloadPdfDestination(), title + strDate + ".pdf")
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); //to show the DOWNLOAD notification when completed
}
long PDF_DOWNLOAD_ID = downloadManager.enqueue(request);```
ACTION_CREATE_DOCUMENT is used to create a new document. If one already existed, it will be overwritten. If you want to view an existing document, use ACTION_VIEW.
Of course none of the code you posted actually downloads a PDF. If you need help with that, post your DownloadManager code.
Check this code snippet:
override fun startDownload(url: String, onError: (e: Exception) -> Unit) {
try {
val request = DownloadManager.Request(Uri.parse(url))
request.setDestinationInExternalPublicDir(
Environment.DIRECTORY_DOWNLOADS,
UUID.randomUUID().toString()
)
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION)
(context.getSystemService(DOWNLOAD_SERVICE) as DownloadManager).enqueue(request)
} catch (e: Exception) {
e.printStackTrace()
onError.invoke(e)
}
}
It's working fine on Android 11 by using DownloadManger API.
Use below code to download & view pdf.
First you need to apply rxjava dependency for background task.
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
Don't forgot to check WRITE_EXTERNAL_STORAGE permission before call below method. Also check INTERNET permission as well.
Then use below method to perform operation in background.
private void downloadAndOpenInvoice() {
mDialog.show();
Observable.fromCallable(() -> {
String pdfName = "Invoice_"+ Calendar.getInstance().getTimeInMillis() + ".pdf";
String pdfUrl = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf";
File file = CommonUtils.downloadFile(mActivity, pdfUrl, pdfName,mDialog);
return file;
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(file -> {
CommonUtils.viewPdf(file, mActivity, mDialog);
});
}
To download file from url use below snippet
public static File downloadFile(Activity mActivity, String url, String fileName, CustomDialog mDialog) {
// write the document content
File fileDir = new File(CommonUtils.getAppDir(mActivity, "Invoice")); //Invoice folder inside your app directory
if (!fileDir.exists()) {
boolean mkdirs = fileDir.mkdirs();
}
File pdfFile = new File(CommonUtils.getAppDir(mActivity, "Invoice"), fileName); //Invoice folder inside your app directory
try {
URL u = new URL(url);
URLConnection conn = u.openConnection();
int contentLength = conn.getContentLength();
DataInputStream stream = new DataInputStream(u.openStream());
byte[] buffer = new byte[contentLength];
stream.readFully(buffer);
stream.close();
DataOutputStream fos = new DataOutputStream(new FileOutputStream(pdfFile));
fos.write(buffer);
fos.flush();
fos.close();
} catch (IOException e) {
e.printStackTrace();
if (mDialog.isShowing()) {
mDialog.dismiss();
}
Toast.makeText(mActivity, "Something wrong: " + e.toString(), Toast.LENGTH_LONG).show();
}
return pdfFile;
}
for app directory
public static String getAppDir(Context context, String folderName) {
return context.getExternalFilesDir(null).getAbsolutePath() + File.separator + folderName + File.separator;
}
Use below code to view pdf
public static void viewPdf(File pdfFile, Activity mActivity, CustomDialog mDialog) {
Uri uri = FileProvider.getUriForFile(mActivity, mActivity.getApplicationContext().getPackageName() + ".provider", pdfFile);
// Setting the intent for pdf reader
Intent pdfIntent = new Intent(Intent.ACTION_VIEW);
pdfIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
pdfIntent.setDataAndType(uri, "application/pdf");
//pdfIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
try {
mActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
if (mDialog.isShowing()) {
mDialog.dismiss();
}
}
});
mActivity.startActivity(pdfIntent);
Log.e("Invoice - PDF", pdfFile.getPath());
} catch (ActivityNotFoundException e) {
mActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
if (mDialog.isShowing()) {
mDialog.dismiss();
}
}
});
e.printStackTrace();
Log.e("Invoice - PDF", "Can't read pdf file");
Toast.makeText(mActivity, "Can't read pdf file", Toast.LENGTH_SHORT).show();
}
}
I have followed twitter fabric log in and everything is working fine except for the part where i try to post a tweet. When i execute this following code i need to login again, so it seems like i have to check an access token or some, but i have no idea and can't find how to do that.
#Override
public void onCreate(Bundle savedInstanceState) {
//initialize facebook sdk
FacebookSdk.sdkInitialize(getActivity().getApplicationContext());
super.onCreate(savedInstanceState);
TwitterSession session = Twitter.getSessionManager().getActiveSession();
TwitterAuthToken authToken = session.getAuthToken();
String token = authToken.token;
String secret = authToken.secret;
if (token != null ) {
Log.d(TAG, "twitter token" + token);
}
if (secret != null ) {
Log.d(TAG, "twitter secret" + secret);
}
TwitterAuthConfig authConfig = new TwitterAuthConfig(TWITTER_KEY, TWITTER_SECRET);
Fabric.with(this.getActivity(), new TwitterCore(authConfig), new TweetComposer());
}
then i am using a function to post the tweet
public void TwitterSharing() {
Log.d(TAG, "Running twitter share");
Log.d(TAG, "Share on twitter 1: " + sport);
Log.d(TAG, "Share on twitter 2: " + speed);
Log.d(TAG, "Share on twitter 3: " + distance);
Log.d(TAG, "Share on twitter 4: " + date);
Log.d(TAG, "Shared image url: " + sharedImage);
TweetComposer.Builder builder = new TweetComposer.Builder(this.getActivity())
.text("just setting up my Fabric.")
.image(Uri.parse(sharedImage));
builder.show();
}
It all works but on the web page it is loading i need to login again, that should not happen but i have no idea how.
Thanks for any input.
For that, you need to make an Activity as the launcher activity. Let's call it DispatchActivity
public class DispatchActivity extends Activity{
#Override
public void onCreate(Bundle savedInstanceState){
This returns true if user is logged in.
boolean isLoggedIn = mSharedPreferences.getBoolean(PREF_KEY_TWITTER_LOGIN, false);
if (isLoggedIn){
//User is logged in, take him to your activity
Intent i = new Intent(this,yourMainActivity.class);
this.startActivity(i);
}
else{
//User is not logged in, take him to your SignIn activity
Intent i = new Intent(this,SignUp.class);
this.startActivity(i);
}
}
}
Remember to make this your launcher activity, and don't create a layout file for it.
I'm trying to create a new android application that is comprised of multiple mini-games. The launcher activity extends BaseGameActivity and has a sign-in button and a ListView containing all the possible games that can be played.
Inside of a mini-game activity (also extends BaseGameActivity), how can I get it to create a notification which will launch a specific Activity? Currently, when I call invitePlayersToGame, the invitation that gets sent is for the full application (Mini-Games) and not the individual game (specific dice game).
public void invitePlayersToGame(View pView) {
Intent intent = Games.RealTimeMultiplayer.getSelectOpponentsIntent(getApiClient(), 1, 1);
intent.putExtra("gameName", "Patman Yahtzee");
startActivityForResult(intent, RC_SELECT_PLAYERS);
}
Is there a way to get the notification to generate with a specific message? Is there a way to get notification to open directly to the mini-game activity without going to the main launcher activity first?
Any help is appreciated. Thanks!
You can send sendReliableMessage for method handshaking.
First enter a room (quickgame or send invite).
public void openInvitationIntent() {
// launch the player selection screen
// minimum: 1 other player; maximum: 1 other players
Intent intent = Games.RealTimeMultiplayer.getSelectOpponentsIntent(mGoogleApiClient, 1, 1);
startActivityForResult(intent, RC_SELECT_PLAYERS);
}
onConnected:
#Override
public void onConnected(Bundle connectionHint) {
// QuickGame
if (mGameMode == 1) {
Log.d(TAG, "onConnected() called. Sign in successful!");
Log.d(TAG, "Sign-in succeeded.");
startQuickGame();
// register listener so we are notified if we receive an invitation to play
// while we are in the game
if (connectionHint != null) {
Log.d(TAG, "onConnected: connection hint provided. Checking for invite.");
Invitation inv = connectionHint.getParcelable(Multiplayer.EXTRA_INVITATION);
if (inv != null && inv.getInvitationId() != null) {
// retrieve and cache the invitation ID
Log.d(TAG, "onConnected: connection hint has a room invite!");
acceptInviteToRoom(inv.getInvitationId());
return;
}
}
}
// Send request
else if (mGameMode == 0) {
// request code for the "select players" UI
// can be any number as long as it's unique
invitationInbox();
}
// request accepted
else {
mIncomingInvitationId = getIntent().getExtras().getString(AppConstants.RC_INVITATION_ID);
RoomConfig.Builder roomConfigBuilder = makeBasicRoomConfigBuilder();
roomConfigBuilder.setInvitationIdToAccept(mIncomingInvitationId);
Games.RealTimeMultiplayer.join(mGoogleApiClient, roomConfigBuilder.build());
// prevent screen from sleeping during handshake
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
}
After this, you can send model class (includes what you need).
private void broadcastMessage(ModelGameRecievedMessage broadcastedMessage, boolean isFinal) {
try {
if ( mParticipants != null && broadcastedMessage != null) {
byte[] bytes = Utils.serialize(broadcastedMessage);
// Send to every other participant.
for (Participant p : mParticipants) {
if (p.getParticipantId().equals(mMyId)) {
continue;
}
if (p.getStatus() != Participant.STATUS_JOINED) {
continue;
}
if (mRoomId != null) {
if (isFinal) {
// final score notification must be sent via reliable broadcastedMessage
Games.RealTimeMultiplayer.sendReliableMessage(mGoogleApiClient, null, bytes,
mRoomId, p.getParticipantId());
} else {
// it's an interim score notification, so we can use unreliable
Games.RealTimeMultiplayer.sendUnreliableMessage(mGoogleApiClient, bytes,
mRoomId, p.getParticipantId());
}
}
}
Logy.l("broadcastedMessage.getMessageTypeId(); " + broadcastedMessage.getMessageTypeId());
Logy.l("broadcastedMessage.getMessage(); " + broadcastedMessage.getMessage());
}
} catch (IOException e) {
e.printStackTrace();
}
}
finally you can reach the data on other devices:
#Override
public void onRealTimeMessageReceived(RealTimeMessage rtm) {
byte[] bufy = rtm.getMessageData();
ModelGameRecievedMessage recievedMessage = null;
try {
recievedMessage = (ModelGameRecievedMessage) Utils.deserialize(bufy);
Logy.l("recievedMessage.getMessageTypeId(); " + recievedMessage.getMessageTypeId());
Logy.l("recievedMessage.getMessage(); " + recievedMessage.getMessage());
} catch (Exception e) {
Logy.e("Exception onRealTimeMessageReceived deserialize: " + e);
}
switch (recievedMessage.getMessageTypeId()) {
case AppConstants.RC_MULTI_START_TIMEMILIS_MULTIPLAYER:
....