Can anyone point me to an example of take a Photo and store it using MVVMCross?
I have been searching but only have found this:
Monodroid Take a picture with Camera (Doesn't Implement MVVMCross)
Video Recording (It's Video and i can't make it work :S)
The Oficialy Recipe Example (It Works but does not implement MVVMCross)
Thanks!!!
Resolved! Thanks!
To Future References: (Using Master Branch)
Credits to Stuart, I just changed the code to work with my reality
using Cirrious.MvvmCross.ExtensionMethods;
using Cirrious.MvvmCross.Interfaces.Platform.Tasks;
using Cirrious.MvvmCross.Interfaces.ServiceProvider;
using SIGEP.DummyService;
using SIGEP.Mobile.Core.Interfaces;
namespace SIGEP.Mobile.Core.Models
{
public class PhotoService : IMvxServiceConsumer<IMvxPictureChooserTask>
{
private const int MaxPixelDimension = 1024;
private const int DefaultJpegQuality = 92;
public void GetNewPhoto()
{
this.GetService<IMvxPictureChooserTask>().TakePicture(
MaxPixelDimension,
DefaultJpegQuality,
HandlePhotoAvailable,
() => { /* cancel is ignored */ });
}
public event EventHandler<PhotoStreamEventArgs> PhotoStreamAvailable;
private void HandlePhotoAvailable(Stream pictureStream)
{
var handler = PhotoStreamAvailable;
if (handler != null)
{
handler(this, new PhotoStreamEventArgs() { PictureStream = pictureStream, OnSucessGettingPhotoFileName = OnSucessGettingPhotoFileName });
}
}
public static void TakePhoto(Action<string> successFileName, Action<Exception> error)
{
var service = new PhotoService();
service.OnSucessGettingPhotoFileName = successFileName;
service.OnError = error;
service.GetNewPhoto();
service.PhotoStreamAvailable += new EventHandler<PhotoStreamEventArgs>(service_PhotoStreamAvailable);
}
static void service_PhotoStreamAvailable(object sender, PhotoStreamEventArgs e)
{
//grava pra ficheiro!!!
var directory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var filename = Path.Combine(directory, "photo.jpeg");
string saveTo = filename;
FileStream writeStream = new FileStream(saveTo, FileMode.Create, FileAccess.Write);
ReadWriteStream(e.PictureStream, writeStream);
e.OnSucessGettingPhotoFileName(filename);
}
private static void ReadWriteStream(Stream readStream, Stream writeStream)
{
int Length = 256;
Byte[] buffer = new Byte[Length];
int bytesRead = readStream.Read(buffer, 0, Length);
// write the required bytes
while (bytesRead > 0)
{
writeStream.Write(buffer, 0, bytesRead);
bytesRead = readStream.Read(buffer, 0, Length);
}
readStream.Close();
writeStream.Close();
}
public Action<string> OnSucessGettingPhotoFileName { get; set; }
public Action<Exception> OnError { get; set; }
}
[Serializable]
[ComVisible(true)]
public class PhotoStreamEventArgs : EventArgs
{
public Stream PictureStream { get; set; }
public Action<string> OnSucessGettingPhotoFileName { get; set; }
}
}
I generally implement a service using the built-in IMvxPictureChooserTask (this is in a Plugin if using vNext):
using Cirrious.MvvmCross.ExtensionMethods;
using Cirrious.MvvmCross.Interfaces.Platform.Tasks;
using Cirrious.MvvmCross.Interfaces.ServiceProvider;
public class PhotoService
: IMvxServiceConsumer<IMvxPictureChooserTask>
, IPhotoService
{
private const int MaxPixelDimension = 1024;
private const int DefaultJpegQuality = 92;
public void GetNewPhoto()
{
Trace.Info("Get a new photo started.");
this.GetService<IMvxPictureChooserTask>().TakePicture(
MaxPixelDimension,
DefaultJpegQuality,
HandlePhotoAvailable,
() => { /* cancel is ignored */ });
}
public event EventHandler<PhotoStreamEventArgs> PhotoStreamAvailable;
private void HandlePhotoAvailable(Stream pictureStream)
{
Trace.Info("Picture available");
var handler = PhotoStreamAvailable;
if (handler != null)
{
handler(this, new PhotoStreamEventArgs() { PictureStream = pictureStream });
}
}
}
I generally register this service as a singleton during startup, and then call it from a ViewModel ICommand handler.
One app which uses this service is the Blooor sample - see BaseEditProductViewModel.cs - this isn't a sample I had anything to do with, but I believe it brings in both Picture taking and ZXing - both using external services.
One warning: On MonoDroid, you can see some strange/unexpected Activity/ViewModel lifecycle behaviour - basically you can see that the Activity you take the photo from is unloaded/wiped from memory during the photo taking. If this happens to your app then you'll probably need to start looking at questions like: Saving Android Activity state using Save Instance State - this isn't automatically handled in MvvmCross (yet).
I believe the Blooor sample might suffer from this issue - but whether a user would ever see it in normal app use is debatable.
As an alternative to the IMvxPictureChooserTask service, you can also look at using some of the cross-platform APIs from Xamarin.Mobile - see MvvmCross vnext : monodroid use a VideoView inside a plugin for a possible starting place - or for Android only you can easily implement your own.
Related
I'm developing an Android App (Android 6.0 and above) and I have performance issues with large XML-Files. (about 17 mB)
When the app is started the required XML-document is loaded from the private storage and a List is returned (takes about 2-3 sek.) and filled in an custom adapter -> this works perfectly fine.
But the user can start a synchronization manually inside the app (e.g.: data
was updated on server)
Therefor I've implemented a background download-service so that the UI stays responsive during the download.
The downloaded data is stored inside the private folder again.
problem:
The background download works perfectly and my UI stays responive until I start reading the information from the new XML-File.
I don't get any result back -> even after 3 min there is no return value from the function although I use the same function for reading the XML like I do at the beginning -> GetKontaktliste()
public class MainActivity : AppCompatActivity
{
alert.SetPositiveButton("Synchronisieren", async (senderAlert, args) =>
{ new DownloadTask(this).Execute("");});
}
public class DownloadTask : AsyncTask
{
protected override Java.Lang.Object DoInBackground(params Java.Lang.Object[] #params)
{
App_Tools lAppTools = new App_Tools();
ThreadPool.QueueUserWorkItem(async state =>
{
//Download Function -> received XML-Data is saved in local storage
bool lKontakte = await lAppTools.DownloadKontakte(_context);
bool lVorgaenge = await lAppTools.DownloadVorgaenge(_context);
bool lVorgaengeImport = await lAppTools.ImportVorgaenge(_context);
if (lKontakte == true)
{
//Problem is that i dont get any results back here
List<KontaktItem> lResult = new List<KontaktItem>();
LinqAbfragen lLinq = new LinqAbfragen();
lLinq.GetKontaktliste();
}
});
return true;
}
}
public class LinqAbfragen
{
//Read Contacts
public List<KontaktItem> GetKontaktliste()
{
List<KontaktItem> lResult = new List<KontaktItem>();
//Read from IEnumerable<XElement>
var KontakteAsXElement = ReadXmlAsXElement("Kontakte.xml", "Kontakt");
lResult = (from kontakt in KontakteAsXElement
select new KontaktItem
{
AdressNr = kontakt.Element("AdressNr").Value,
Vorname = kontakt.Element("Vorname1").Value,
Nachname = kontakt.Element("Name1").Value,
xmlData = (string)kontakt.ToString()
}
).ToList();
return lResult;
}
}
I have a Xamarin application which plays remote(internet) audio files using the MediaPlayer with the following setup:
_mediaPlayer.SetDataSource(mediaUri);
_mediaPlayer.PrepareAsync();
Now I would like to change the implementation to also cache files. For the caching part I found a really nice library called MonkeyCache which saves the files in this JSON format:
{"$type":"System.Byte[], mscorlib","$value":"UklGRhAoAgBXQVZFZm10IBAAAAABAAIARKwAABCxAgAEABAAZGF0YdAnAgAAAAAAAAAAAAAAAAAA+P/4/+z/7P8KAAoA7//v//L/8v8JAAkA6f/p//j/+P8FAAUA6P/o/wEAAQD+//7/5//n ................."}
So my MediaPlayer setup has now changed to:
if (Barrel.Current.Exists(mediaUri)){
var audio = Barrel.Current.Get<byte[]>(mediaUri);
_mediaPlayer.SetDataSource(???);
}else{
using (var webClient = new WebClient()){
var downloadDataBytes = webClient.DownloadData(mediaUri);
if (downloadDataBytes != null && downloadDataBytes.Length > 0)
{
Barrel.Current.Add(mediaUri, downloadDataBytes, TimeSpan.FromDays(1));
_mediaPlayer.SetDataSource(???);
}
}
}
I would like to play the audio from a byte[] instead of the mediaUri.
Is there any way to actually play an in memory byte[]?
The only solutions that I could find were to create a FileInputStream out of a File by using a filepath, but the implementation of the MonkeyCache actually hashes the file name, before adding it:
static string Hash(string input){
var md5Hasher = MD5.Create();
var data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input));
return BitConverter.ToString(data);
}
Therefore the downloaded bytes, will be saved under:
/data/data/com.package.name.example/cache/com.package.name.example/MonkeyCacheFS/81-D6-E8-62-F3-4D-F1-64-A6-A1-53-46-34-1E-FE-D1
Even if I were to use the same hashing logic to actually compute the file path myself and use the FileInputStream which might be working by what I've read, it would defeat the purpose of using the var audio = Barrel.Current.Get<byte[]>(mediaUri); functionality of the MonkeyCache.
However, if that is the only way, I will do it.
Edit: Even with my described approach, it would probably not work right away as even if I compute the right file name, it is still in JSON format.
Edit2: A working solution is:
var audio = Barrel.Current.Get<byte[]>(mediaUri);
var url = "data:audio/mp3;base64," + Convert.ToBase64String(audio);
_mediaPlayer.SetDataSource(url);
Finally figured it out:
The MediaPlayer.SetDataSource method has an overload which expects a MediaDataSource object.
MediaDataSource
For supplying media data to the framework. Implement
this if your app has special requirements for the way media data is
obtained
Therefore, you can use your implementation of the MediaDataSource and feed it your byte array.
public class StreamMediaDataSource : MediaDataSource
{
private byte[] _data;
public StreamMediaDataSource(byte[] data)
{
_data = data;
}
public override long Size
=> _data.Length;
public override void Close()
{
_data = null;
}
public override int ReadAt(long position, byte[] buffer, int offset, int size)
{
if (position >= _data.Length)
{
return -1;
}
if (position + size > _data.Length)
{
size -= (Convert.ToInt32(position) + size) - _data.Length;
}
Array.Copy(_data, position, buffer, offset, size);
return size;
}
}
which will lead to the MediaPlayer data source to be
_mediaPlayer.SetDataSource(new StreamMediaDataSource(downloadedDataBytes));
Edit: This only works for API >= 23. I still have to find a solution for API<23(which I still haven't).
Edit2: For API < 23 support I used the
var url = $"data:audio;base64,{Convert.ToBase64String(downloadedDataBytes)}";
_mediaPlayer.SetDataSource(url);
approach.
I am trying run a local server for a Xamarin.Forms WebView. This is to get around CORS, and so the html can be structured like a normal page. This works for UWP and iOS, but Android always comes up with an ERR_CONNECTION_REFUSED. Some further details/things I have tried:
The App is running it's own server, so it is not the case of trying to access a server on a separate device.
Internet permission is enabled.
The path to the files do exist, otherwise the Webserver would fail to start.
Link to the local server nuget package: https://github.com/unosquare/embedio
Below is an outline of the code I'm using. In practise, I'm using a custom renderer, injecting Javascript to access platform features, etc. but this should simplify it:
The class that creates and starts the WebServer with EmbedIO:
public class LocalWebServer: IDisposable
{
public static string Url = "http://localhost:8787/";
private readonly string _filePath;
private WebServer _server;
public LocalWebServer(string filePath)
{
_filePath = filePath;
}
public void StartWebServer()
{
_server = new WebServer(Url);
_server.RegisterModule(new LocalSessionModule());
_server.RegisterModule(new StaticFilesModule(_filePath));
_server.Module<StaticFilesModule>().UseRamCache = true;
_server.Module<StaticFilesModule>().DefaultExtension = ".html";
_server.Module<StaticFilesModule>().DefaultDocument = "index.html";
_server.Module<StaticFilesModule>().UseGzip = false;
Task.Factory.StartNew(async ()=>
{
Debug.WriteLine("Starting Server");
await _server.RunAsync();
});
}
public void Dispose()
{
_server?.Dispose();
}
}
Code which starts the server and displays the webview:
public App()
{
InitializeComponent();
//Create and display a Webview
_webView = new WebView();
MainPage = new ContentPage()
{
Content = _webView,
};
}
protected override async void OnStart()
{
//Service which can initialize app for first time use, and stores
//the folder location for the html page on each platform
var htmlService = DependencyService.Get<IHandleHtmlContentService>();
//Local webserver
var localWebServer = new LocalWebServer(htmlService.DirectoryPath);
//This is just a function that loads the html content from the
//bundle resource or assets into a folder. Will only really
//matter during the first time the App boots up.
await htmlService.InitializeHtmlContent();
//Start the Webserver
localWebServer.StartWebServer();
//Navigate to the webserver
_webView.Source = LocalWebServer.Url;
}
I'v been bashing my head on this for a while, so any help would be appreciated. If you need any more details, let me know.
Turns out, Android has no concept of "localhost" (at least from what I can read). Instead, I need to find the IP Address of my device. I have done this with the following code:
public class LocalWebServer: IDisposable
{
public readonly string Url;
...
public LocalWebServer(string filePath)
{
_filePath = filePath;
Url = "http://" + GetLocalIpAddress() + ":8787/";
}
...
private static string GetLocalIpAddress()
{
var listener = new TcpListener(IPAddress.Loopback, 0);
try
{
listener.Start();
return ((IPEndPoint)listener.LocalEndpoint).Address.ToString();
}
finally
{
listener.Stop();
}
}
}
Code was found on this Xamarin Forums post: https://forums.xamarin.com/discussion/42345/simple-android-http-listener-not-working
I am trying to get the app code and display it, for an example if button X starts a new activity then a textView displays the whole method
I reached only how can I display code in HTML format from this question
But is there is a way to get the code of my app out, I think that there are 2 ways
An Internal one by getting it by the app itself
An External one by reading the java file then filtering it and getting the text of the method
Is there are any ideas about that?
Thanks in advance
The above is not currently possible as mentioned by others is the comments. What i can suggest is shipping your application with the source code in the assets folder and using a helper function to extract a certain methods from the source at runtime (your second proposed approach). I have written example code but it is in pure java and needs to be ported to android (a few lines).
NB: You may need to reformat the code after extraction depending on your use case.
Hope it helps :)
The code for the helper method:
static String getTheCode(String classname ,String methodSignature ) throws FileNotFoundException {
//**********************A few lines of code below need changing when porting ***********//
// open file, your will be in the assets folder not in the home dir of user, don't forget the .java extension when porting
File file = new File(System.getProperty("user.home") +"/"+ classname +".java");
// get the source, you can use FileInputReader or some reader supported by android
Scanner scanner = new Scanner(file);
String source = "";
while(scanner.hasNext()) {
source += " "+ scanner.next();
}
//**********************The above code needs changing when porting **********//
// extract code using the method signature
methodSignature = methodSignature.trim();
source = source.trim();
//appending { to differentiate from argument as it can be matched also if in the same file
methodSignature = methodSignature+"{";
//making sure we find what we are looking for
methodSignature = methodSignature.replaceAll("\\s*[(]\\s*", "(");
methodSignature = methodSignature.replaceAll("\\s*[)]\\s*", ")");
methodSignature = methodSignature.replaceAll("\\s*[,]\\s*", ",");
methodSignature = methodSignature.replaceAll("\\s+", " ");
source =source.replaceAll("\\s*[(]\\s*", "(");
source = source.replaceAll("\\s*[)]\\s*", ")");
source = source.replaceAll("\\s*[,]\\s*", ",");
source = source.replaceAll("\\s+", " ");
if(!source.contains(methodSignature)) return null;
// trimming all text b4 method signature
source = source.substring(source.indexOf(methodSignature));
//getting last index, a methods ends when there are matching pairs of these {}
int lastIndex = 0;
int rightBraceCount = 0;
int leftBraceCount = 0;
char [] remainingSource = source.toCharArray();
for (int i = 0; i < remainingSource.length ; i++
) {
if(remainingSource[i] == '}'){
rightBraceCount++;
if(rightBraceCount == leftBraceCount){
lastIndex = (i + 1);
break;
}
}else if(remainingSource[i] == '{'){
leftBraceCount++;
}
}
return source.substring(0 ,lastIndex);
}
Example usage (getTheCode methods is static and in a class called GetTheCode):
public static void main(String... s) throws FileNotFoundException {
System.out.println(GetTheCode.getTheCode("Main", "private static void shoutOut()"));
System.out.println(GetTheCode.getTheCode("Main", "private static void shoutOut(String word)"));
}
Output:
private static void shoutOut(){ // nothing to here }
private static void shoutOut(String word){ // nothing to here }
NB: When starting your new activity create a method eg
private void myStartActivty(){
Intent intent = new Intent(MyActivity.this, AnotherActivity.class);
startActivity(intent);
}
Then in your onClick:
#Override
public void onClick(View v) {
myStartActivity();
myTextView.setText(GetTheCode.getTheCode("MyActivity","private void myStartActivity()"));
}
Update: Ported the Code for android:
import android.content.Context;
import java.io.IOException;
import java.util.Scanner;
public class GetTheCode {
static String getTheCode(Context context, String classname , String methodSignature ) {
Scanner scanner = null;
String source = "";
try {
scanner = new Scanner(context.getAssets().open(classname+".java"));
while(scanner.hasNext()) {
source += " "+ scanner.next();
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
scanner.close();
// extract code using the method signature
methodSignature = methodSignature.trim();
source = source.trim();
//appending { to differentiate from argument as it can be matched also if in the same file
methodSignature = methodSignature+"{";
//making sure we find what we are looking for
methodSignature = methodSignature.replaceAll("\\s*[(]\\s*", "(");
methodSignature = methodSignature.replaceAll("\\s*[)]\\s*", ")");
methodSignature = methodSignature.replaceAll("\\s*[,]\\s*", ",");
methodSignature = methodSignature.replaceAll("\\s+", " ");
source =source.replaceAll("\\s*[(]\\s*", "(");
source = source.replaceAll("\\s*[)]\\s*", ")");
source = source.replaceAll("\\s*[,]\\s*", ",");
source = source.replaceAll("\\s+", " ");
if(!source.contains(methodSignature)) return null;
// trimming all text b4 method signature
source = source.substring(source.indexOf(methodSignature));
//getting last index, a methods ends when there are matching pairs of these {}
int lastIndex = 0;
int rightBraceCount = 0;
int leftBraceCount = 0;
char [] remainingSource = source.toCharArray();
for (int i = 0; i < remainingSource.length ; i++
) {
if(remainingSource[i] == '}'){
rightBraceCount++;
if(rightBraceCount == leftBraceCount){
lastIndex = (i + 1);
break;
}
}else if(remainingSource[i] == '{'){
leftBraceCount++;
}
}
return source.substring(0,lastIndex);
}
}
Usage:
// the method now takes in context as the first parameter, the line below was in an Activity
Log.d("tag",GetTheCode.getTheCode(this,"MapsActivity","protected void onCreate(Bundle savedInstanceState)"));
Let's start with a broader overview of the problem:
Display App code
Press X button
Open new activity with a textview which displays the method
The goal is to do the following:
Viewing app method by extracting it and then building & running it.
There are some methods we can use to run Java/Android code dynamically. The way I would personally do it is DexClassLoader and with Reflection.
If you need more details, let me know. Here is what it'd do though:
View app method
Upon pressing X, launch intent with extra to new Activity
Parse and compile code dynamically and then run it with DexClassLoader and Reflection
Sources:
Sample file loading Java method from TerminalIDE Android App
Android Library I made for Auto-Updating Android Applications without needing the Play Store on non-root devices
I am developing a Cordova plugin for Android and I am having difficulty overcoming accessing project resources from within an activity - the plugin should be project independent, but accessing the resources (e.g. R.java) is proving tricky.
My plugin, for now, is made up of two very simple classes: RedLaser.java and RedLaserScanner.java.
RedLaser.java
Inherits from CordovaPlugin and so contains the execute method and looks similar to the following.
public class RedLaser extends CordovaPlugin {
private static final string SCAN_ACTION = "scan";
public boolean execute(String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException {
if (action.equals(SCAN_ACTION)) {
this.cordova.getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
scan(args, callbackContext);
}
});
return true;
}
return false;
}
private void scan(JSONArray args, CallbackContext callbackContext) {
Intent intent = new Intent(this.cordova.getActivity().getApplicationContext(), RedLaserScanner.class);
this.cordova.startActivityForResult((CordovaPlugin) this, intent, 1);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// Do something with the result
}
}
RedLaserScanner.java
The RedLaserScanner contains the Android Activity logic and inherits from BarcodeScanActivity (which is a RedLaser SDK class, presumably itself inherits from Activity);
A very simple structure is as follows:
public class RedLaserScanner extends BarcodeScanActivity {
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.preview_overlay_new_portrait);
}
}
I am having trouble because I need to access the project's resources to access R.layout.preview_overlay_new_portrait (which are scatted in the Eclipse project) - but I cannot do this unless I import com.myProject.myApp.R - which makes my plugin have a dependency on the project itself.
I did some investigation and found cordova.getActivity().getResources() which seems useful, but this is not accessible from within my RedLaserScanner - because it does not inherit from CordovaPlugin.
Can somebody please help me with some pointers?
Thanks
I just ran into the same issue and it turns out to be pretty easy to solve. RedLaserScanner extends an activity, so you can just call getResources() like this:
setContentView(getResources("preview_overlay_new_portrait", "layout", getPackageName()));
Hooks can be used to replace source file contents to remove wrong imports and/or add the right imports of resources.
I created a script that do it without needing to specify the files. It tries to find source files (with .java extension), removes any resource import already in it and then put the right resources import (if needed), using the Cordova application package name.
This is the script:
#!/usr/bin/env node
/*
* A hook to add resources class (R.java) import to Android classes which uses it.
*/
function getRegexGroupMatches(string, regex, index) {
index || (index = 1)
var matches = [];
var match;
if (regex.global) {
while (match = regex.exec(string)) {
matches.push(match[index]);
console.log('Match:', match);
}
}
else {
if (match = regex.exec(string)) {
matches.push(match[index]);
}
}
return matches;
}
module.exports = function (ctx) {
// If Android platform is not installed, don't even execute
if (ctx.opts.cordova.platforms.indexOf('android') < 0)
return;
var fs = ctx.requireCordovaModule('fs'),
path = ctx.requireCordovaModule('path'),
Q = ctx.requireCordovaModule('q');
var deferral = Q.defer();
var platformSourcesRoot = path.join(ctx.opts.projectRoot, 'platforms/android/src');
var pluginSourcesRoot = path.join(ctx.opts.plugin.dir, 'src/android');
var androidPluginsData = JSON.parse(fs.readFileSync(path.join(ctx.opts.projectRoot, 'plugins', 'android.json'), 'utf8'));
var appPackage = androidPluginsData.installed_plugins[ctx.opts.plugin.id]['PACKAGE_NAME'];
fs.readdir(pluginSourcesRoot, function (err, files) {
if (err) {
console.error('Error when reading file:', err)
deferral.reject();
return
}
var deferrals = [];
files.filter(function (file) { return path.extname(file) === '.java'; })
.forEach(function (file) {
var deferral = Q.defer();
var filename = path.basename(file);
var file = path.join(pluginSourcesRoot, filename);
fs.readFile(file, 'utf-8', function (err, contents) {
if (err) {
console.error('Error when reading file:', err)
deferral.reject();
return
}
if (contents.match(/[^\.\w]R\./)) {
console.log('Trying to get packages from file:', filename);
var packages = getRegexGroupMatches(contents, /package ([^;]+);/);
for (var p = 0; p < packages.length; p++) {
try {
var package = packages[p];
var sourceFile = path.join(platformSourcesRoot, package.replace(/\./g, '/'), filename)
if (!fs.existsSync(sourceFile))
throw 'Can\'t find file in installed platform directory: "' + sourceFile + '".';
var sourceFileContents = fs.readFileSync(sourceFile, 'utf8');
if (!sourceFileContents)
throw 'Can\'t read file contents.';
var newContents = sourceFileContents
.replace(/(import ([^;]+).R;)/g, '')
.replace(/(package ([^;]+);)/g, '$1 import ' + appPackage + '.R;');
fs.writeFileSync(sourceFile, newContents, 'utf8');
break;
}
catch (ex) {
console.log('Could not add import to "' + filename + '" using package "' + package + '". ' + ex);
}
}
}
});
deferrals.push(deferral.promise);
});
Q.all(deferrals)
.then(function() {
console.log('Done with the hook!');
deferral.resolve();
})
});
return deferral.promise;
}
Just add as an after_plugin_install hook (for Android platform) in your plugin.xml:
<hook type="after_plugin_install" src="scripts/android/addResourcesClassImport.js" />
Hope it helps someone!
I implemented a helper for this to keep things clean. It also helps when you create a plugin which takes config.xml arguments which you store in a string resource file in the plugin.
private int getAppResource(String name, String type) {
return cordova.getActivity().getResources().getIdentifier(name, type, cordova.getActivity().getPackageName());
}
You can use it as follows:
getAppResource("app_name", "string");
That would return the string resource ID for app_name, the actually value still needs to be retrieved by calling:
this.activity.getString(getAppResource("app_name", "string"))
Or for the situation in the original question:
setContentView(getAppResource("preview_overlay_new_portrait", "layout"));
These days I just create a helper which returns the value immediately from the the helper:
private String getStringResource(String name) {
return this.activity.getString(
this.activity.getResources().getIdentifier(
name, "string", this.activity.getPackageName()));
}
which in turn you'd call like this:
this.getStringResource("app_name");
I think it's important to point out that when you have the resource ID you're not always there yet.
try using android.R.layout.preview_overlay_new_portrait