I'm using the aws sample code to upload files to the S3, but when they get uploaded they come with no access for anyone, even with the bucket made Public for everyone, the only way to read it is to manually set the files on the S3 console to give public access.
MyService:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null && intent.getStringExtra(INTENT_KEY_NAME) != null) {
final String key = intent.getStringExtra(INTENT_KEY_NAME);
final File file = (File) intent.getSerializableExtra(INTENT_FILE);
final String transferOperation = intent.getStringExtra(INTENT_TRANSFER_OPERATION);
TransferObserver transferObserver;
switch (transferOperation) {
case TRANSFER_OPERATION_DOWNLOAD:
Log.d(TAG, "Downloading " + key);
transferObserver = transferUtility.download("aws-MYBUCKET", key, file);
transferObserver.setTransferListener(new DownloadListener());
break;
case TRANSFER_OPERATION_UPLOAD:
Log.d(TAG, "Uploading " + key);
transferObserver = transferUtility.upload("aws-MYBUCKET", key, file);
transferObserver.setTransferListener(new UploadListener());
break;
}
return START_STICKY;
} else return START_NOT_STICKY;
You should create a Bucket Policy. This can grant public access for the whole bucket, or a portion of the bucket.
From Bucket Policy Examples - Amazon Simple Storage Service:
{
"Version":"2012-10-17",
"Statement":[
{
"Sid":"AddPerm",
"Effect":"Allow",
"Principal": "*",
"Action":["s3:GetObject"],
"Resource":["arn:aws:s3:::examplebucket/*"]
}
]
}
This is preferable to granting access on individual objects.
Related
In my app I have given facility to download reports in PDF formats. Everything was good until I was testing my app on Android 10 and below. Recently I updated a device to Android 11 and now I am not able to download any file in shared storage. I am getting Access Denied error while downloading file.
I have given permission to read and write external storage
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
and my download path is
Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads).AbsolutePath;
I have also set android:requestLegacyExternalStorage="true" in AndroidManifest.xml
My code to download file looks like this
public async void DownloadSupplierSample(object sender, EventArgs e)
{
try
{
string basepath;
if (Device.RuntimePlatform == Device.Android)
basepath = DependencyService.Get<IDownloadPath>().GetDownloadsPath();
else
basepath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "..", "Library");
PermissionStatus readstatus = await Permissions.CheckStatusAsync<Permissions.StorageRead>();
PermissionStatus writestatus = await Permissions.CheckStatusAsync<Permissions.StorageWrite>();
if (readstatus == PermissionStatus.Granted && writestatus == PermissionStatus.Granted)
{
DownloadSupplier(basepath);
}
else
{
var read = await Permissions.RequestAsync<Permissions.StorageRead>();
var write = await Permissions.RequestAsync<Permissions.StorageWrite>();
if (read == PermissionStatus.Granted && write == PermissionStatus.Granted)
{
DownloadSupplier(basepath);
}
}
await DisplayAlert("", "Sample is downaloaded at Downloads directory","Ok");
}
catch (Exception ex)
{
}
}
async void DownloadSupplier(string basepath)
{
using (var stream = await FileSystem.OpenAppPackageFileAsync("SupplierMaster.xlsx"))
{
string filename = $"SupplierMaster_{DateTime.Now.ToString("yyyyMMddHHmmss")}.xlsx";
string filepath = Path.Combine(basepath, filename);
byte[] bytes = Utils.Common.StreamToBytes(stream);
File.WriteAllBytes(filepath, bytes);
//using (Stream filestream = File.Create(filepath))
//{
// //stream.Seek(0, SeekOrigin.Begin);
// stream.CopyTo(filestream);
//}
}
}
I have tried setting flag from device as well from this link
How can I define scoped storage for my app ?
Any update how can I download PDF file in my downloads directory ?
Thank you.
I had the same issue a couple of months ago.
I found this on developer.android page:
(https://developer.android.com/reference/android/os/Environment#getExternalStorageDirectory())
The first alternative (getExternalFilesDir) won't work as you want, files won't appear in the download directory, this location is internal to the app.
The second alternative is for media files.
I ended up using the third alternative. I created a service class in Android project, something like this:
public class FilesManager : IFilesManager
{
private const int CREATE_FILE_REQUEST_CODE = 123;
private static Context _context;
public static void Init(Context context)
{
_context = context;
}
public async Task SaveFile(string fileName, byte[] content)
{
Intent intent = new Intent(Intent.ActionCreateDocument);
intent.AddCategory(Intent.CategoryOpenable);
intent.SetType(your_file_mimeType);
intent.PutExtra(Intent.ExtraTitle, fileName);
var activity = (MainActivity)_context;
var listener = new ActivityResultListener(activity);
activity.StartActivityForResult(intent, CREATE_FILE_REQUEST_CODE);
var result = await listener.Task;
if (result == null)
{
// Cancelled by user
return;
}
using (Stream os = _context.ContentResolver.OpenOutputStream(result.Data))
{
os?.Write(content);
os?.Close();
}
}
private class ActivityResultListener
{
private readonly TaskCompletionSource<Intent> Complete = new TaskCompletionSource<Intent>();
public Task<Intent> Task { get { return this.Complete.Task; } }
public ActivityResultListener(MainActivity activity)
{
// subscribe to activity results
activity.ActivityResult += OnActivityResult;
}
private void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
if(requestCode != CREATE_FILE_REQUEST_CODE)
{
return;
}
// unsubscribe from activity results
var activity = (MainActivity)_context;
activity.ActivityResult -= OnActivityResult;
// process result
if (resultCode == Result.Ok)
{
Complete.TrySetResult(data);
}
else
{
Complete.TrySetResult(null);
}
}
}
}
In MainActivity.cs add:
public event Action<int, Result, Intent> ActivityResult;
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
ActivityResult?.Invoke(requestCode, resultCode, data);
}
You can call Init method after Forms.Init:
FilesManager.Init(this);
Note: This method requires user interaction. The user will be prompted to choose the location where to save the file.
How can I define scoped storage for my app ?Any update how can I download PDF file in my downloads directory ?
Android 10 introduced a new storage paradigm for apps called scoped storage which changes the way apps store and access files on a device's external storage. If you target Android 10 (API level 29) or higher, set the value of requestLegacyExternalStorage to true in your app's manifest file.
<application android:requestLegacyExternalStorage="true" android:label="FormsSample.Android" android:theme="#style/MainTheme"></application>
Then request runtime write and read EXTERNAL_STORAGE permission before access download folder.
public void requestpermission()
{
if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.WriteExternalStorage) != (int)Permission.Granted)
{
ActivityCompat.RequestPermissions(this, new string[] { Manifest.Permission.WriteExternalStorage }, 1);
}
if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.ReadExternalStorage) != (int)Permission.Granted)
{
ActivityCompat.RequestPermissions(this, new string[] { Manifest.Permission.ReadExternalStorage }, 1);
}
}
Create interface IAccessFileService in Xamarin.Forms shared code.
public interface IAccessFileService
{
void CreateFile(string FileName);
}
Implementing IAccessFileService interface on Android platform.You could use File.Exists(xx) to check if the folder exists, and use Directory.CreateDirectory(xx) to create the folder if not.
[assembly: Xamarin.Forms.Dependency(typeof(AccessFileImplement))]
namespace FormsSample.Droid
{
public class AccessFileImplement : IAccessFileService
{
public void CreateFile(string FileName)
{
string text = "hello world";
byte[] data = Encoding.ASCII.GetBytes(text);
string rootPath = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, Android.OS.Environment.DirectoryDownloads);
var filePathDir = Path.Combine(rootPath, "folder");
if (!File.Exists(filePathDir))
{
Directory.CreateDirectory(filePathDir);
}
string filePath = Path.Combine(filePathDir, FileName);
File.WriteAllBytes(filePath, data);
}
}
}
So using dependencyservice to call android CreateFile method.
private void btn1_Clicked(object sender, EventArgs e)
{
DependencyService.Get<IAccessFileService>().CreateFile("test.txt");
}
I have a back-end that I have written with Laravel and I am currently writing and Android app which is doing calls to my back-end.
I have some png's and pdf's stored in s3 buckets in my aws account. I need to get the images and documents from the bucket and store them locally on the device as well as displaying them.
I also need to send new png's from the phone to be stored in the s3 bucket.
What is the best way to go around doing this. Are there any useful libraries. I have already added Picasso but that only helps with displaying the image not getting from/storing in the s3 bucket.
You can use the AWS Android SDK for S3. You can consume it in gradle via maven as:
dependencies {
compile 'com.amazonaws:aws-android-sdk-s3:2.6.+'
}
For example to upload a file to S3:
import android.app.Activity;
import android.util.Log;
import com.amazonaws.mobile.client.AWSMobileClient;
import com.amazonaws.mobileconnectors.s3.transferutility.TransferUtility;
import com.amazonaws.mobileconnectors.s3.transferutility.TransferState;
import com.amazonaws.mobileconnectors.s3.transferutility.TransferObserver;
import com.amazonaws.mobileconnectors.s3.transferutility.TransferListener;
import com.amazonaws.services.s3.AmazonS3Client;
import java.io.File;
public class YourActivity extends Activity {
public void uploadData() {
// Initialize AWSMobileClient if not initialized upon the app startup.
// AWSMobileClient.getInstance().initialize(this).execute();
TransferUtility transferUtility =
TransferUtility.builder()
.context(getApplicationContext())
.awsConfiguration(AWSMobileClient.getInstance().getConfiguration())
.s3Client(new AmazonS3Client(AWSMobileClient.getInstance().getCredentialsProvider()))
.build();
TransferObserver uploadObserver =
transferUtility.upload(
"s3Folder/s3Key.txt",
new File("/path/to/file/localFile.txt"));
uploadObserver.setTransferListener(new TransferListener() {
#Override
public void onStateChanged(int id, TransferState state) {
if (TransferState.COMPLETED == state) {
// Handle a completed upload.
}
}
#Override
public void onProgressChanged(int id, long bytesCurrent, long bytesTotal) {
float percentDonef = ((float)bytesCurrent/(float)bytesTotal) * 100;
int percentDone = (int)percentDonef;
Log.d("MainActivity", " ID:" + id + " bytesCurrent: " + bytesCurrent + " bytesTotal: " + bytesTotal + " " + percentDone + "%");
}
#Override
public void onError(int id, Exception ex) {
// Handle errors
}
});
// If your upload does not trigger the onStateChanged method inside your
// TransferListener, you can directly check the transfer state as shown here.
if (TransferState.COMPLETED == uploadObserver.getState()) {
// Handle a completed upload.
}
}
}
For more information:
https://docs.aws.amazon.com/aws-mobile/latest/developerguide/add-aws-mobile-user-data-storage.html#add-aws-user-data-storage-upload
https://docs.aws.amazon.com/aws-mobile/latest/developerguide/how-to-storage.html
AWS has a set of libraries that you could use to get and store in the S3 bucket.
You should check: https://docs.aws.amazon.com/aws-mobile/latest/developerguide/getting-started.html
For upload file to s3
String ACCESS_KEY="****************",
SECRET_KEY="****************",
MY_BUCKET="bucket_name",
OBJECT_KEY="unique_id";
AWSCredentials credentials = new BasicAWSCredentials(ACCESS_KEY, SECRET_KEY);
AmazonS3 s3 = new AmazonS3Client(credentials);
java.security.Security.setProperty("networkaddress.cache.ttl" , "60");
s3.setRegion(Region.getRegion(Regions.AP_SOUTHEAST_1));
s3.setEndpoint("https://s3-ap-southeast-1.amazonaws.com/");
List<Bucket> buckets=s3.listBuckets();
for(Bucket bucket:buckets){
Log.e("Bucket ","Name "+bucket.getName()+" Owner "+bucket.getOwner()+ " Date " + bucket.getCreationDate());
}
Log.e("Size ", "" + s3.listBuckets().size());
TransferUtility transferUtility = new TransferUtility(s3, getApplicationContext());
UPLOADING_IMAGE=new File(Environment.getExternalStorageDirectory().getPath()+"/Screenshot.png");
TransferObserver observer = transferUtility.upload(MY_BUCKET,OBJECT_KEY,UPLOADING_IMAGE);
observer.setTransferListener(new TransferListener() {
#Override
public void onStateChanged(int id, TransferState state) {
// do something
progress.hide();
path.setText("ID "+id+"\nState "+state.name()+"\nImage ID "+OBJECT_KEY);
}
#Override
public void onProgressChanged(int id, long bytesCurrent, long bytesTotal) {
int percentage = (int) (bytesCurrent / bytesTotal * 100);
progress.setProgress(percentage);
//Display percentage transfered to user
}
#Override
public void onError(int id, Exception ex) {
// do something
Log.e("Error ",""+ex );
}
});
For downloading image
https://github.com/jontyankit/Glide-Amazon-Image-Load
I'm trying to get 'Change Subscriptions' to work using the Drive API for Android, but been unsuccessful so far.
Here the simple use case:
2 android devices, both using the same google account
both subscribe to the same 'file of interest' in their drive folder
if the file 'changes', be it from a change performed by one of the two devices or any external source, all devices that subscribed to this file are notified
As far as I understand, this is exactly what 'Change Subscriptions' are supposed to do for me. I'm using play services revision 27.
The problem I have:
A 'file content change' (or some other file event) made locally on one device is never properly propagated to the all other devices that subscribed to the same file.
Does anyone know of any solutions to this issue, or can point my to what I'm doing wrong?
I've written some simple testcode (see below), that only needs a connected googleApiClient, here's what I tested:
1.
device 1 creates a new testfile calling testFileWriteNew() and adds a change subscription to this file using testFileAddAndRemoveSubscription(), the expected log output:
testfile.txt created, driveId=DriveId:CAESABi0AyDAu9XZhVMoAA== resourceId=null
onCompletion; driveId=DriveId:CAESHDBCLXNzaGVuNGlURkFOMGh0ZWtGWU5FeHVTRVUYtAMgwLvV2YVTKAA= resourceId=0B-sshen4iTFAN0htekFYNExuSEU
STATUS_SUCCESS
added subscription to testfile.txt, driveId=DriveId:CAESHDBCLXNzaGVuNGlURkFOMGh0ZWtGWU5FeHVTRVUYtAMgwLvV2YVTKAA= resourceId=0B-sshen4iTFAN0htekFYNExuSEU
2.
device 2 adds a change subscription to the same file using testFileAddAndRemoveSubscription(), the expected log output:
added subscription to testfile.txt, driveId=DriveId:CAESHDBCLXNzaGVuNGlURkFOMGh0ZWtGWU5FeHVTRVUYwgIg9I-GyZRTKAA= resourceId=0B-sshen4iTFAN0htekFYNExuSEU
As expected, the driveId is different on both devices, but the resourceId is the same 0B-sshen4iTFAN0htekFYNExuSEU, so that same 'cloud' file is referenced
3.
If I update the file with some new data via testFileUpdate I get the following on device 1:
testfile.txt updated, driveId=DriveId:CAESHDBCLXNzaGVuNGlURkFOMGh0ZWtGWU5FeHVTRVUYtAMgwLvV2YVTKAA= resourceId=0B-sshen4iTFAN0htekFYNExuSEU
and device 2:
testfile.txt updated, driveId=DriveId:CAESHDBCLXNzaGVuNGlURkFOMGh0ZWtGWU5FeHVTRVUYwgIg9I-GyZRTKAA= resourceId=0B-sshen4iTFAN0htekFYNExuSEU
4.
Unfortunately, the 'change of content' in the onChange method of the service is only triggered locally. A changed done by device 1 never reaches device 2 and vice versa. If I update the file using device 2 I see the following log on device 2 coming from the service:
onChange; driveId=DriveId:CAESHDBCLXNzaGVuNGlURkFOMGh0ZWtGWU5FeHVTRVUYwgIg9I-GyZRTKAA= resourceId=0B-sshen4iTFAN0htekFYNExuSEU
contentChanged
onChange; driveId=DriveId:CAESHDBCLXNzaGVuNGlURkFOMGh0ZWtGWU5FeHVTRVUYwgIg9I-GyZRTKAA= resourceId=0B-sshen4iTFAN0htekFYNExuSEU
metadataChanged
but I never see the onChange method being triggered on device 1, if device 2 triggered a change, which I would expect.
Code:
private boolean testFileWriteNew() {
final DriveFolder folderRoot = Drive.DriveApi.getRootFolder(mGoogleApiClient);
DriveContentsResult contentsResult = Drive.DriveApi.newDriveContents(mGoogleApiClient).await();
if (!contentsResult.getStatus().isSuccess()) {
return false;
}
DriveContents originalContents = contentsResult.getDriveContents();
OutputStream os = originalContents.getOutputStream();
try {
os.write(String.valueOf(System.currentTimeMillis()).getBytes());
MetadataChangeSet originalMetadata = new MetadataChangeSet.Builder().setTitle("testfile.txt").setMimeType("text/plain").build();
// create the file in root
DriveFolder.DriveFileResult fileResult = folderRoot.createFile(mGoogleApiClient, originalMetadata, originalContents, new ExecutionOptions.Builder().setNotifyOnCompletion(true).build()).await();
if (!fileResult.getStatus().isSuccess()) {
return false;
}
// check 'locally created' file, not yet synced to drive
DriveResource.MetadataResult metadataResult = fileResult.getDriveFile().getMetadata(mGoogleApiClient).await();
if (!metadataResult.getStatus().isSuccess()) {
return false;
}
Log.d(TAG, "testfile.txt created, driveId=" + metadataResult.getMetadata().getDriveId().encodeToString() + " resourceId=" + metadataResult.getMetadata().getDriveId().getResourceId());
return true;
} catch (IOException ioe) {
return false;
}
}
private boolean testFileUpdate() {
final DriveFolder folderRoot = Drive.DriveApi.getRootFolder(mGoogleApiClient);
// find testfile
DriveId testFile = null;
MetadataBufferResult folderFilesSyncFolder = folderRoot.listChildren(mGoogleApiClient).await();
if (!folderFilesSyncFolder.getStatus().isSuccess()) {
return false;
} else {
MetadataBuffer bufferMetaData = folderFilesSyncFolder.getMetadataBuffer();
for(int i = 0; i < bufferMetaData.getCount(); ++i) {
final Metadata data = bufferMetaData.get(i);
if(!data.isFolder() && !data.isTrashed() && data.isEditable() && data.getTitle().equalsIgnoreCase("testfile.txt")) {
testFile = data.getDriveId();
break;
}
}
bufferMetaData.release();
}
if(testFile == null) {
return false;
}
// update testfile
DriveFile file = Drive.DriveApi.getFile(mGoogleApiClient, testFile);
DriveContentsResult driveContentsResult = file.open(mGoogleApiClient, DriveFile.MODE_WRITE_ONLY, null).await();
if (!driveContentsResult.getStatus().isSuccess()) {
return false;
}
DriveContents originalContents = driveContentsResult.getDriveContents();
OutputStream os = originalContents.getOutputStream();
try {
os.write(String.valueOf(System.currentTimeMillis()).getBytes());
// commit changes
com.google.android.gms.common.api.Status status = originalContents.commit(mGoogleApiClient, null).await();
if(!status.isSuccess()) {
return false;
}
Log.d(TAG, "testfile.txt updated, driveId=" + file.getDriveId().encodeToString() + " resourceId=" + file.getDriveId().getResourceId());
return true;
} catch (IOException ioe) {
return false;
}
}
private boolean testFileAddAndRemoveSubscription(boolean subscribe) {
final DriveFolder folderRoot = Drive.DriveApi.getRootFolder(mGoogleApiClient);
// find testfile
DriveId testFile = null;
MetadataBufferResult folderFilesSyncFolder = folderRoot.listChildren(mGoogleApiClient).await();
if (!folderFilesSyncFolder.getStatus().isSuccess()) {
return false;
} else {
MetadataBuffer bufferMetaData = folderFilesSyncFolder.getMetadataBuffer();
for(int i = 0; i < bufferMetaData.getCount(); ++i) {
final Metadata data = bufferMetaData.get(i);
if(!data.isFolder() && !data.isTrashed() && data.isEditable() && data.getTitle().equalsIgnoreCase("testfile.txt")) {
testFile = data.getDriveId();
break;
}
}
bufferMetaData.release();
}
if(testFile == null) {
return false;
}
// subscribe & unsubscribe
DriveFile file = Drive.DriveApi.getFile(mGoogleApiClient, testFile);
if(subscribe) {
com.google.android.gms.common.api.Status status = file.addChangeSubscription(mGoogleApiClient).await();
if(!status.isSuccess()) {
return false;
}
Log.d(TAG, "added subscription to testfile.txt, driveId=" + file.getDriveId().encodeToString() + " resourceId=" + file.getDriveId().getResourceId());
return true;
} else {
com.google.android.gms.common.api.Status status = file.removeChangeSubscription(mGoogleApiClient).await();
if(!status.isSuccess()) {
return false;
}
Log.d(TAG, "removed subscription from testfile.txt, driveId=" + file.getDriveId().encodeToString() + " resourceId=" + file.getDriveId().getResourceId());
return true;
}
}
And here the service class:
public class ChangeService extends DriveEventService {
// TAG
private static final String TAG = ChangeService.class.getSimpleName();
#Override
public void onChange(ChangeEvent event) {
final DriveId driveId = event.getDriveId();
Log.e(TAG, "onChange; driveId=" + driveId.encodeToString() + " resourceId=" + driveId.getResourceId());
if(event.hasContentChanged()) { Log.e(TAG, "contentChanged"); }
else if(event.hasMetadataChanged()) { Log.e(TAG, "metadataChanged"); }
else if(event.hasBeenDeleted()) { Log.e(TAG, "beenDeleted"); }
}
#Override
public void onCompletion(CompletionEvent event) {
final DriveId driveId = event.getDriveId();
Log.e(TAG, "onCompletion; driveId=" + driveId.encodeToString() + " resourceId=" + driveId.getResourceId());
switch (event.getStatus()) {
case CompletionEvent.STATUS_CONFLICT: Log.e(TAG, "STATUS_CONFLICT"); break;
case CompletionEvent.STATUS_FAILURE: Log.e(TAG, "STATUS_FAILURE"); break;
case CompletionEvent.STATUS_SUCCESS: Log.e(TAG, "STATUS_SUCCESS "); break;
case CompletionEvent.STATUS_CANCELED: Log.e(TAG, "STATUS_CANCELED "); break;
}
event.dismiss();
}
}
I believe, you are falling into the same trap as many of us did before. I too originally assumed that the 'DriveEventService' takes care of notifications between multiple devices running under the same account. I tried and failed miserably, see here (and notice the resounding silence - since April 2014). I was always getting events on a single device only. So, I actually realized that Change Events work only locally within the GooPlaySvcs instance.
This was more or less confirmed by a comment from Steve Bazyl in this unrelated answer (please read including the 'ORIGINAL POST' paragraph), confirming my theory that both 'Change Events' and 'Completion Events' are local (Completion Events report result of network action - like http response).
So to answer your question. after fighting this for awhile, I had to develop a different strategy:
1/ perform GDAA action (create, update)
2/ wait for a Completion Event indicating your mod has been promoted to the Drive
3/ broadcast GCM message that include ResourceId (not DriveId !) plus optional data (up to 4K) to the registered participants.
4/ 'Registered participants' react to the message and download updated metadata/content, resolving the conflicts.
This solution is from summer 2014 and there may be some other pre-packaged solutions from Google since. I'd be happy myself to hear from people who know if there is more elegant solution.
Quite frankly, I don't understand what is this and this for, if the Completion Events do not timely reflect (notify of) the update from another device.
Good Luck
I have created an encrypted .obb file using the jobb tool. I use the following code to mount the obb file:
public void mountExpansion() {
final StorageManager storageManager = (StorageManager) getContext()
.getSystemService(Context.STORAGE_SERVICE);
String packageName = "name.of.the.package";
String filePath = Environment.getExternalStorageDirectory()
+ "/Android/obb/" + packageName + "/" + "main."
+ version + "." + packageName + ".obb";
final File mainFile = new File(filePath);
if (mainFile.exists()) {
Log.d("STORAGE", "FILE: " + filePath + " Exists");
} else {
Log.d("STORAGE", "FILE: " + filePath + " DOESNT EXIST");
}
String key = "thisIsMyPassword";
if (!storageManager.isObbMounted(mainFile.getAbsolutePath())) {
if (mainFile.exists()) {
if(storageManager.mountObb(mainFile.getAbsolutePath(), key,
new OnObbStateChangeListener() {
#Override
public void onObbStateChange(String path, int state) {
super.onObbStateChange(path, state);
Log.d("PATH = ",path);
Log.d("STATE = ", state+"");
expansionFilePath = storageManager.getMountedObbPath(path);
if (state == OnObbStateChangeListener.MOUNTED) {
expansionFilePath = storageManager
.getMountedObbPath(path);
Log.d("STORAGE","-->MOUNTED");
}
else {
Log.d("##", "Path: " + path + "; state: " + state);
}
}
}))
{
Log.d("STORAGE_MNT","SUCCESSFULLY QUEUED");
}
else
{
Log.d("STORAGE_MNT","FAILED");
}
} else {
Log.d("STORAGE", "Patch file not found");
}
}
}
I am getting the following output:
FILE: filePath Exists
SUCCESSFULLY QUEUED
But nothing inside onObbStateChangeListener is getting called. I am calling this function from a custom view and testing this on Nexus 4/ KitKat.
What could be the reason for this behaviour?
I know this question is old but this may help someone else.
The StorageManager stores the listener in a weak reference which means that, given your example code (an anonymous instance created in the method call), it is gone almost as soon as you create it and usually well before the mount completes. You must maintain a reference to the listener object in your own code until it is no longer needed.
Something like this should work:
public class MyClass {
...
private OnObbStateChangeListener mListener =
new OnObbStateChangeListener() {
#Override
public void onObbStateChange(String path, int state) {
// your code here
}
};
public void mountExpansion() {
...
if (storageManager.mountObb(mainFile.getAbsolutePath(), key, mListener)
{
Log.d("STORAGE_MNT","SUCCESSFULLY QUEUED");
}
else
{
Log.d("STORAGE_MNT","FAILED");
}
...
}
...
}
This particular quirk of obb mounting has existed since at least honeycomb to my knowledge.
There seems to be a bug with OBB mounting that was introduced with KitKat. Currently no workarounds are known however it should be fixed with the next incremental update.
http://code.google.com/p/android/issues/detail?id=61881
I develop an app with in app version 2 and publish it on developers Google now some one download the app and try to purchase it. On purchasing a message showed to him that "Your card is decline" now even after this message app got "unlock" . Now this is a very difficult situation that payment is not made while app got unlock. Is this issue with in app billing method(logic) in my app or its with Google.
If its in my app then is there any method which check that the person card is a "declined card" so in this method i can restrict my app not to unlock the app.
Any app will be appreciated.
I have this code in my BillingReceviver:
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Consts.ACTION_PURCHASE_STATE_CHANGED.equals(action)) {
String signedData = intent.getStringExtra(Consts.INAPP_SIGNED_DATA);
String signature = intent.getStringExtra(Consts.INAPP_SIGNATURE);
purchaseStateChanged(context, signedData, signature);
} else if (Consts.ACTION_NOTIFY.equals(action)) {
String notifyId = intent.getStringExtra(Consts.NOTIFICATION_ID);
if (Consts.DEBUG) {
Log.i(TAG, "notifyId: " + notifyId);
}
notify(context, notifyId);
} else if (Consts.ACTION_RESPONSE_CODE.equals(action)) {
long requestId = intent.getLongExtra(Consts.INAPP_REQUEST_ID, -1);
int responseCodeIndex = intent.getIntExtra(Consts.INAPP_RESPONSE_CODE,
ResponseCode.RESULT_ERROR.ordinal());
checkResponseCode(context, requestId, responseCodeIndex);
} else {
Log.w(TAG, "unexpected action: " + action);
}
}
After making ACTION_PURCHASE_STATE_CHANGED matches the action the my purchsedstatechanged calls in file BillingService
private void purchaseStateChanged(int startId, String signedData, String signature) {
ArrayList<Security.VerifiedPurchase> purchases;
DatabaseHandler db=new DatabaseHandler(this);
purchases = Security.verifyPurchase(signedData, signature);
if (purchases == null) {
return;
}
ArrayList<String> notifyList = new ArrayList<String>();
for (VerifiedPurchase vp : purchases) {
if (vp.notificationId != null) {
notifyList.add(vp.notificationId);
}
ResponseHandler.purchaseResponse(this, vp.purchaseState, vp.productId,
vp.orderId, vp.purchaseTime, vp.developerPayload);
db.addUser("com.example.app", "111", "1222");
Log.i("Usgdgfer",""+db.isUserPresent("com.example.app"));
}
if (!notifyList.isEmpty()) {
String[] notifyIds = notifyList.toArray(new String[notifyList.size()]);
confirmNotifications(startId, notifyIds);
}
}
Usually, the card has to be mapped to the Google Wallet account before the purchase is made. If the card is invalid, it will show an error while you map the card to your Google account.
Seems strange that the card is being validated during purchase. Even then, if the payment is not made properly , you should get an error response
BILLING_RESPONSE_RESULT_USER_CANCELED = 1;
BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE = 3;
Check if you handle these errors properly after you receive them in the IabResult. Make sure you unlock your app only when you receive a proper response.