MediaScanner and ScopedStorage on SDK-29 - mediastore

the app i'm working on uses a File as a target for photo capturing. this is executed externally by user selecting a camera-app using Intent(ACTION_IMAGE_CAPTURE). since upgrading build and target-sdk to 29, there have been a series of issues starting with the restriction on freely accessing files on external storage. the first change was to use one of either the application's private-cache directory, eg:
File.createTempFile("tempImage", ".jpg", context.cacheDir)
or the applications private external-storage directory:
File.createTempFile("tempImage", ".jpg", context.getExternalFilesDir(Environment.DIRECTORY_PICTURES))
in combination with FileProvider access in file_paths.xml, eg:
<paths>
<external-path name="images" path="Pictures/" /><!-- prior to SDK-29, use public external storage -->
<external-files-path name="externalImages" path="Pictures/" />
<files-path name="internalImages" path="internalImages/"/>
<cache-path name="cache" path="/" />
</paths>
these work well now after being properly configured, however implementing "Save to Gallery" functionality, eg: notifying other apps of new images is no longer working on devices running Android-10
// use FileProvider to make this new photo publicly accessible
val shareableUri = FileProvider.getUriForFile(context, FILE_PROVIDER_AUTHORITY, newImage)
context.sendBroadcast(
Intent(ACTION_MEDIA_SCANNER_SCAN_FILE).apply { data = uris.externalUri }
)
this approach should work but doesn't, regardless of where the original image is saved (private-app-dir, cache-dir, external-private)
MediaScannerConnection.scanFile(context, arrayOf(newImage.absolutePath), arrayOf("image/jpeg")) { path: String, uri: Uri? ->
if (uri == null) {
throw IllegalStateException("media scan failed...")
} else {
// successful
}
}
Are there are new restrictions in Android's SDK-29 which necessitate a change in MediaScanning, specifically related to the way in which a (potentially) private image file is scanned? I noticed that MediaScanner methods mostly expect a String path instead of a URI, so this leads me to think that the new restrictions won't apply to it since its a system component.

Apps targeting Android 10 (API level 29) and higher are given scoped access into an external storage device, or scoped storage, by default.
so you need to make it compatible with scopedStorage but you can use this temporary approach for now mentioned in Google Android Developers guide.
Before your app is fully compatible with scoped storage, you can temporarily opt out based on your app's target SDK level or the requestLegacyExternalStorage manifest attribute:
<manifest ... >
<!-- This attribute is "false" by default on apps targeting
Android 10 or higher. -->
<application android:requestLegacyExternalStorage="true" ... >
...
</application>
</manifest>
visit this link for more info:
https://developer.android.com/training/data-storage/files/external-scoped

Related

Using Trusted Web Activity to link multiple websites with native application

I've managed to link my native application to a website and launch the same on a button click. As the website is trusted, the URL bar is not visible. In the launched website there is a button which then further redirects to another website. I've created a digital asset link for both and have added the JSON file in <websitename>/.well-known/<json-file>.
Both the websites have also been referenced in strings.xml under
asset_statements. However, on launching the first website and then redirecting to the second website from the first, the second website launches as a regular custom chrome tab with the URL bar visible.
Is it possible to hide both the URL's? If so, how?
To enable multi-domain, you need to check 3 things
Each origin has a .well-known/assetlinks.json file
The android asset_statements contains all origins
Tell the Trusted Web Activity about additional origins when launching.
It seems you have the first two points covered, but not the last one.
Using the support library LauncherActivity:
If using the LauncherActivity that comes with the library, you can provide additional origins by updating the AndroidManifest:
Add a list of additional origins to res/values/strings.xml:
<string-array name="additional_trusted_origins">
<item>https://www.google.com</item>
</string-array>
Update AndroidManifest.xml:
<activity android:name="com.google.androidbrowserhelper.trusted.LauncherActivity"
android:label="#string/app_name">
<meta-data
android:name="android.support.customtabs.trusted.ADDITIONAL_TRUSTED_ORIGINS"
android:resource="#array/additional_trusted_origins" />
...
</activity>
Using a custom LauncherActivity
If using your own LauncherActivity, launching with additional origins can implemented like this:
public void launcherWithMultipleOrigins(View view) {
List<String> origins = Arrays.asList(
"https://checkout.example.com/"
);
TrustedWebActivityIntentBuilder builder = new TrustedWebActivityIntentBuilder(LAUNCH_URI)
.setAdditionalTrustedOrigins(origins);
new TwaLauncher(this).launch(builder, null, null);
}
Resources:
Article with more details here: https://developers.google.com/web/android/trusted-web-activity/multi-origin
Sample multi-origin implementation: https://github.com/GoogleChrome/android-browser-helper/tree/master/demos/twa-multi-domain

How Can I have IIS properly serve .webmanifest files on my web site?

The Favicon Generator assembles a package for webmasters to use in order to have icons available for many different devices. The page comes with a file called site.manifest which is linked to via the following tag in the web page's document <head>:
<link rel="manifest" href="site.webmanifest">
According to Mozilla: "The web app manifest provides information about an application (such as name, author, icon, and description) in a JSON text file. The purpose of the manifest is to install web applications to the homescreen of a device, providing users with quicker access and a richer experience."
Unfortunately if you are using Microsoft's Internet Information Services (IIS), you'll get a 404.3 error if you try and access the site.webmanifest file.
The exact error message is as follows: "The page you are requesting cannot be served because of the extension configuration. If the page is a script, add a handler. If the file should be downloaded, add a MIME map."
How can I properly serve site.webmanifest files in IIS?
By default, IIS does not serve any files that does not have a MIME map associated with it in its (IIS) core settings.
To address this challenge, you will need to map the .webmanifest file extension to its appropriate MIME type.
To accomplish this, open IIS and follow the steps below;
On the left hand side, select either your web site or the entire server in the "Connections" menu.
If you select the server, your MIME mapping will apply to every web site on the server.
If you select a web site, it will only apply to a single web site.
Next, select "MIME Types" from the IIS menu:
Once there, click "add..." from the right hand menu.
In the dialog box that opens specify .webmanifest in the file name extension box application/manifest+json in the MIME type box.
Click "OK".
Congratulations; you've just defined the MIME type for .webmanifest on IIS.
For Azure I added this as the web.config
<?xml version="1.0"?>
<configuration>
<system.webServer>
<staticContent>
<mimeMap fileExtension=".json" mimeType="application/json" />
<mimeMap fileExtension=".webmanifest" mimeType="application/manifest+json" />
</staticContent>
</system.webServer>
</configuration>
For those using ASP.NET Core (I am using 2.1) you can configure the MIME types that can be served in the application Startup.cs file as per the static files docs:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
FileExtensionContentTypeProvider provider = new FileExtensionContentTypeProvider();
provider.Mappings[".webmanifest"] = "application/manifest+json";
app.UseStaticFiles(new StaticFileOptions()
{
ContentTypeProvider = provider
});
app.UseMvc();
}
Easier solution is to rename your manifest file to site.webmanifest.json and link as
<link rel="manifest" href="site.webmanifest.json">
IIS should already have a MIME Type for .json files
This is also helpful if deploying to Azure where its not so easy to change the IIS settings.
Adding to #Ben's answer: if you have a SPA you should put StaticFileOptions code into the UseSpaStaticFiles() call:
FileExtensionContentTypeProvider provider = new FileExtensionContentTypeProvider();
provider.Mappings[".webmanifest"] = "application/manifest+json";
app.UseSpaStaticFiles(new StaticFileOptions()
{
ContentTypeProvider = provider
});
I found that the IIS server had ".json" listed in the Request Filtering feature saying it was not allowed.
Removing that allowed the file to be served.

MVC bundle with Azure CDN - how to enable caching

I have a web site to be hosted in Azure that has a lot of javascript and CSS but very small pages. I would like to have the javascript and the CSS delivered via a CDN.
Azure provides a really neat and convenient mechanism to allow this as described here https://azure.microsoft.com/en-us/documentation/articles/cdn-cloud-service-with-cdn/#integrate-aspnet-bundling-and-minification-with-azure-cdn
In short, you add the following code to your BundleConfig.cs
bundles.UseCdn = true;
var version = System.Reflection.Assembly.GetAssembly(typeof(Controllers.HomeController))
.GetName().Version.ToString();
var cdnUrl = "http://axxxxxx6.vo.msecnd.net/{0}?v=" + version;
ScriptBundle scriptBundle = new ScriptBundle("~/bundles/xx", string.Format(cdnUrl, "bundles/xx"));
scriptBundle.Include(
"~/Scripts/modernizr-*",
"~/Scripts/jquery-{version}.js",
"~/Scripts/jquery.signalR-{version}.js",
"~/Scripts/jquery.watermark.js", ....
I have followed the instructions to the letter and on the surface it appears to work exactly as expected.
But I realised that the caching for these CDN provided resources is disabled. Every time the web page is requested the JS and the CSS are downloaded again - which defeats the purpose of the CDN altogether.
I have also included the following in the web.config
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="15.00:00:00"/>
</staticContent>
What am I missing here?
Thanks in advance.
Dave A

Blob wad-iis-logfiles is never created

I just started checking out Windows Azure and I have trouble getting any access logs from IIS for my test web role. The web role itself works fine, but I would like to see a log for accesses (both successful and failed).
As far as I can see the default configuration files for a web role contain instructions to send those logs to a blob named "wad-iis-logfiles", but that blob is never even created (it doesn't exist in my blob storage).
My diagnostics.wadcfg for the web role currently is:
<DiagnosticMonitorConfiguration configurationChangePollInterval="PT1M" overallQuotaInMB="4096" xmlns="http://schemas.microsoft.com/ServiceHosting/2010/10/DiagnosticsConfiguration">
<DiagnosticInfrastructureLogs bufferQuotaInMB="512" scheduledTransferPeriod="PT5M" />
<Directories bufferQuotaInMB="512" scheduledTransferPeriod="PT5M">
<IISLogs container="wad-iis-logfiles" />
<CrashDumps container="wad-crash-dumps" />
</Directories>
<Logs bufferQuotaInMB="512" scheduledTransferPeriod="PT5M" scheduledTransferLogLevelFilter="Information" />
<PerformanceCounters bufferQuotaInMB="512">
(... snip...)
</PerformanceCounters>
<WindowsEventLog bufferQuotaInMB="512" scheduledTransferPeriod="PT1M" scheduledTransferLogLevelFilter="Error">
<DataSource name="Application!*" />
</WindowsEventLog>
</DiagnosticMonitorConfiguration>
Question 1: is this configuration file correct?
Question 2: are there other things that need to be set before I can get the IIS log files?
With the help of the commenters I was able to solve the issue.
There are several interacting things causing the issue.
As commenter #kwill mentioned, an existing configuration blob in wad-control-container overrides any other configuration, and that configuration is not replaced during an in-place update. I was using in-place update to put my modified diagnostics.wadcfg in place, so that is the explanation why my attempts to change settings that way didn't work. Note that editing the properties of the web test role (in the "Roles" branch of the Azure Cloud services project operates by editing that same file, so that didn't work either. More information on how that wad-control-container overrides setting can be found in http://msdn.microsoft.com/en-us/library/windowsazure/dn205146.aspx .
The reason that blob already existed may have been that I had been changing some other performance measurement settings in the azure management window earlier.
I managed to "fix" the situation by editing the blob found in wad-control-container for my instance, using the tool mentioned by commenter #Gaurav Mantri - "Azure Explorer". As mentioned, without that tool you can download the blob and edit it, but never put it back properly, since the '/' characters in the blob's name get translated to '%2F', and those are not translated back on upload.
Note that the XML schema is not the same as the schema for diagnostics.wadcfg, but some similarities exist. I changed the "Directories" element toward the bottom of the blob to read:
<Directories>
<BufferQuotaInMB>512</BufferQuotaInMB>
<ScheduledTransferPeriodInMinutes>2</ScheduledTransferPeriodInMinutes>
<Subscriptions>
<DirectoryConfiguration>
<Path>C:\Resources\directory\8091b0be14e54213ac12fcbd5f9c8e1b.WebTestRole.DiagnosticStore\CrashDumps</Path>
<Container>wad-crash-dumps</Container>
<DirectoryQuotaInMB>0</DirectoryQuotaInMB>
</DirectoryConfiguration>
<DirectoryConfiguration>
<Path>C:\Resources\directory\8091b0be14e54213ac12fcbd5f9c8e1b.WebTestRole.DiagnosticStore\LogFiles</Path>
<Container>wad-iis-logfiles</Container>
<DirectoryQuotaInMB>16</DirectoryQuotaInMB>
</DirectoryConfiguration>
</Subscriptions>
</Directories>
In the original version the "BufferQuotaInMB" and "DirectoryQuotaInMB" fields were 0.
Note that after uploading the blob again the effect is not immediate. It takes a while for the changed configuration to get picked up, and then it takes another while before the IIS log files are copied for the first time.
Last note: it may be obvious, but I don't think editing that blob is a recommendable solution. It is good to know the option exists though.

does JWS support to launch One-Jar

Can I supposed to launch One-Jar using JWS? One-JAR provides custom classloader that knows how to load classes and resources from a jars inside an archive whereas in JWS we need to specify each JAR that is being used in resources.
What I supposed to specify in JNLP if I am trying to launch One-Jar -
<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="1.0+" codebase="" href="">
<information>
<title>Application</title>
<vendor>ABC</vendor>
</information>
<resources>
<!-- Application Resources -->
<j2se version="1.5+"
href="http://java.sun.com/products/autodl/j2se"/>
<jar href="Application.jar" main="true" download="eager" />
</resources>
<application-desc main-class="com.simontuffs.onejar.Boot">
</application-desc>
<update check="background"/>
<security>
<all-permissions/>
</security>
</jnlp>
My Application JAR that is One-JAR contains -
com\simontuffs\onejar\<contains complied classes> like JarClassLoader$1.class etc
lib/<contains all jar>
OneJar.class
main/<my application's jar>
META-INF\MANIFEST.MF\ <contains >
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.1
Created-By: One-Jar 0.96 Ant taskdef
Main-Class: com.simontuffs.onejar.Boot
One-Jar-Main-Class: com.application.main.Entry
Name: com/simontuffs/onejar/Boot$3.class
SHA1-Digest: +LPrezs+UEFcE3J7QvumcAEO8Z0=
Name: OneJar.class
SHA1-Digest: 28pzzJWqEpLk1xFwJ/jsAav8LyI=
Name: lib/commons-io-1.4.jar
SHA1-Digest: qHYtB+ds/eI5Ulel2ke6fB29Pc4=
etc..etc..
How to specify these com/simontuffs/onejar/Boot$3.class paths in resource ?
Part of the problem of supporting tools like One-Jar is that loading Jars within Jars requires a custom class-loader. By default JWS will use the usual JRE class-loaders - which do not support it.
There are two possible ways that you might get around that (that I know of).
1. Get access to the custom class-loader.
Get an instance of the custom loader used by One-Jar and set it as the context class-loader. This would require a trusted app., but I get the impression that your app. is trusted.
I have no idea if the One-Jar API provides this loader for your own app.'s use.
2. Use some options when building.
From Options and VM Properties..
one-jar.jar.names
true: Recorded classes are kept in directories corresponding to their jar names.
false: Recorded classes are flattened into a single directory.
Duplicates are ignored (first wins)
The 2nd option sounds like a standard 'fat jar'. That should work with the conventional (default) class-loader used by JWS.

Resources