Windows phone bug - access denied to device camera - windows

I am facing an issue with the windows phone camera: I am not able to open it using the Titanium.Media.showCamera method. It returns a zero error code (access denied).
I've put all the necessary permissions in the app.xml (correct me if I am wrong):
<windows>
<manifest>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10">
<Capabilities>
<DeviceCapability Name="webcam"/>
<uap:Capability Name="picturesLibrary"/>
</Capabilities>
</Package>
</manifest>
</windows>
I've been stuck on this issue for several days, any help would be greatly appreciated.
Thanks

Did you try this code to ask Camera permissions first :: ?
if (Ti.Media.hasCameraPermissions()) {
takePicture();
} else {
Ti.Media.requestCameraPermissions(function(cameraArgs) {
if (cameraArgs.success) {
takePicture();
} else {
alert('Camera access denied.\nError: ' + cameraArgs.error);
}
});
}
Note that above code requires minimum Ti SDK 5.1.0

Related

Outlook OnNewMessageCompose event not firing in Windows client

We need to act on the OnNewMessageCompose and OnMessageRecipientsChanged events, and have registered them in the app manifest as shown below.
This works in the browser version of Outlook: outlook.office.com
But it doesn't work in the Windows client. (Outlook 365, currently testing with version 2209 build 16.0.15629.20068 32-bit)
This has worked for us earlier, but recently it stopped working in the update channel: Current Channel (Preview)
Switching to the non-preview update channel temporarily solved this, but now it is not working in either channel.
JavaScript event handlers (simplified a bit for this example):
function EventOnCompose(event) {
HandleComposeEvent(event, false)
.then((result) => console.log(result))
.catch((err) => {
console.error(err);
})
.finally(() => {
console.info("Event completed");
event.completed();
});
}
function EventOnRecipientsChanged(event) {
HandleRecipientsChangedEvent(event, false)
.then((result) => console.log(result))
.catch((err) => {
console.error(err);
})
.finally(() => {
console.info("Event completed");
event.completed();
});
}
At the very bottom of addinfunction.js, I have the following code to ensure the actions are associated with these events.
Office.actions.associate("EventOnCompose", EventOnCompose);
Office.actions.associate("EventOnRecipientsChanged", EventOnRecipientsChanged);
Another symptom we're experiencing, when running the app in the Windows Client, with Debugging enabled, it does trigger the events (and stops as soon as debugging is disabled again).
When the debugger is disabled, it shows the following message in the top of the compose window:
We're sorry, we couldn't access <app-name>. Make sure you have a network connection. If the problem continues, please try again later. Dismiss
The application manifest looks like:
(trimmed down to mainly show the LaunchEvents-related elements.)
<?xml version="1.0" encoding="UTF-8"?>
<OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0"
xmlns:mailappor="http://schemas.microsoft.com/office/mailappversionoverrides/1.0" xsi:type="MailApp">
<VersionOverrides xmlns="http://schemas.microsoft.com/office/mailappversionoverrides" xsi:type="VersionOverridesV1_0">
<VersionOverrides xmlns="http://schemas.microsoft.com/office/mailappversionoverrides/1.1" xsi:type="VersionOverridesV1_1">
<Hosts>
<Host xsi:type="MailHost">
<Runtimes>
<Runtime resid="functionFile">
<Override type="javascript" resid="functionFileJs"/>
</Runtime>
</Runtimes>
<DesktopFormFactor>
<ExtensionPoint xsi:type="LaunchEvent">
<LaunchEvents>
<LaunchEvent Type="OnNewMessageCompose" FunctionName="EventOnCompose"/>
<LaunchEvent Type="OnMessageRecipientsChanged" FunctionName="EventOnRecipientsChanged"/>
</LaunchEvents>
<SourceLocation resid="functionFile"/>
</ExtensionPoint>
</DesktopFormFactor>
</Host>
</Hosts>
<Resources>
<bt:Urls>
<bt:Url id="functionFileJs" DefaultValue="https://---host---/js/addinfunction.js"/>
<bt:Url id="functionFile" DefaultValue="https://---host---/function.html"/>
</bt:Urls>
</Resources>
<WebApplicationInfo>
<Id>--- guid ---</Id>
<Resource>api://---host---/---guid---</Resource>
<Scopes>
<Scope>profile</Scope>
</Scopes>
</WebApplicationInfo>
</VersionOverrides>
</VersionOverrides>
</OfficeApp>
Important note: We tried debugging, but since the events aren't triggering, that doesn't help.
Does anyone have a fix for this, or ideas on what to look into as a possible cause of this problematic behavior?
The fact that this issue does not repro when the add-in is marked for debugging suggests that the code might be using syntax that is beyond ECMAScript 2016. The solution here is to use syntax that is only supported up to ECMAScript 2016.

Ionic - Make the windows app run at fullscreen

Is it possible to build the windows desktop app at fullscreen? 'Cause initially when I run my app, the size is at 1200x900.
In my config.xml I have this <preference name="Fullscreen" value="true" />
I also have this code based on my research from cordova-plugin-statusbar :
this.platform.ready().then(() => {
StatusBar.hide();
});
But I have no luck in running the app at fullscreen for Windows. I have found a solution but only for android:
if (this.platform.is("android")) {
this.androidFullScreen.isImmersiveModeSupported().then(
() => this.androidFullScreen.immersiveMode()
).catch((error: any) => console.log(error));
}
you can try below code , add in page.ts file
ionViewWillEnter() {
this.statusBar.overlaysWebView(true);
}// where you want full screen
ionViewWillEnter() {
this.statusBar.overlaysWebView(true);
}// where you want normal screen

Cordova plugin.xml add "Header Search Paths" entry

I would like a new entry to be added under "Header Search Paths" once my Cordova plugin is added to an Xcode project.
How can I config it in my Cordova plugin.xml file?
Thanks.
As far as I know, there's no way to do this on a project-wide basis.
However, there is a way to add to the search path for each of your source files, which accomplishes the same thing. The source-file element in plugin.xml supports a compiler-flags attribute. To this attribute, you may add any flags that the compiler (in this case the clang command) supports. The compiler flag to add to the header search path is -I<path>. Note that if you include a space (eg -I <path>), Cordova will produce a misformatted .xcodeproj folder, and you will not be able to open the project in Xcode, nor build the Cordova project from the command line. Note also that <path> in this case is relative to your .xcodeproj folder that Cordova generates.
So, for example, if you have these files in your plugin folder (let's call it ~/com.MyPlugin/):
myAngleHeader.h
mySource.m
mySource.h
where mySource.h contains the line #include <myAngleHeader.h>
and mySource.m contains the line #include "mySource.h"
You would then want to put into your plugin.xml (~/com.MyPlugin/plugin.xml):
<source-file src="myAngleHeader.h" />
<source-file src="mySource.h" />
<source-file src="mySource.m" compiler-flags="-ImyApp/Plugins/com.MyPlugin/" />
where "myApp" is the name of your Cordova project. Note again that there must be no space after the -I flag.
This method unfortunately requires that the developer control both the plugin and the Cordova project. It won't be very useful if you want to publish a plugin for all to use. There is probably a better way to do this; I'd love to hear other solutions.
Hope that helps!
#JohnWalthour
I needed to do this for a cordova plugin I am creating and with the "after plugin install" hook it is possible. I realized I could run a node.js script after install and modify the HEADER_SEARCH_PATHS in ${project}/platforms/ios/cordova/build.xcconfig.
It required getting dirty but it works great. One crucial part of this working is that the Bundle name is assigned to ${PRODUCT_NAME} in the info.plist so you can use ${PRODUCT_NAME} in your build.xcconfig and it will interpolate with your project/app name. Cordova already has the ${PRODUCT_NAME} variable set for you.
Here is the relevant code -
plugin.xml (shortened for brevity and the important stuff)
<platform name="ios">
.....
<hook type="after_plugin_install" src="hooks/AfterPluginInstall.js" />
<hook type="before_plugin_uninstall" src="hooks/BeforePluginUninstall.js" />
.....
</platform>
AfterPluginInstall.js
#!/usr/bin/env node
'use strict';
let cwd = process.cwd();
let fs = require('fs');
let path = require('path');
console.log('InstagramAssetsPicker AfterPluginInstall.js, attempting to modify build.xcconfig');
let xcConfigBuildFilePath = path.join(cwd, 'platforms', 'ios', 'cordova', 'build.xcconfig');
console.log('xcConfigBuildFilePath: ', xcConfigBuildFilePath);
let lines = fs.readFileSync(xcConfigBuildFilePath, 'utf8').split('\n');
let headerSearchPathLineNumber;
lines.forEach((l, i) => {
if (l.indexOf('HEADER_SEARCH_PATHS') > -1) {
headerSearchPathLineNumber = i;
}
});
if (lines[headerSearchPathLineNumber].indexOf('InstagramAssetsPicker') > -1) {
console.log('build.xcconfig already setup for InstagramAssetsPicker');
return;
}
lines[headerSearchPathLineNumber] += ' "$(SRCROOT)/$(PRODUCT_NAME)/cordova-plugin-InstagramAssetsPicker/GPUImageHeaders"';
let newConfig = lines.join('\n');
fs.writeFile(xcConfigBuildFilePath, newConfig, function (err) {
if (err) {
console.log('error updating build.xcconfig, err: ', err);
return;
}
console.log('successfully updated HEADER_SEARCH_PATHS in build.xcconfig');
});
BeforePluginUninstall.js
#!/usr/bin/env node
'use strict';
let cwd = process.cwd();
let fs = require('fs');
let path = require('path');
console.log('InstagramAssetsPicker BeforePluginInstall.js, attempting to modify build.xcconfig');
let xcConfigBuildFilePath = path.join(cwd, 'platforms', 'ios', 'cordova', 'build.xcconfig');
console.log('xcConfigBuildFilePath: ', xcConfigBuildFilePath);
let lines = fs.readFileSync(xcConfigBuildFilePath, 'utf8').split('\n');
let headerSearchPathLineNumber;
lines.forEach((l, i) => {
if (l.indexOf('HEADER_SEARCH_PATHS') > -1) {
headerSearchPathLineNumber = i;
}
});
if (lines[headerSearchPathLineNumber].indexOf('InstagramAssetsPicker') === -1) {
console.log('build.xcconfig does not have header path for InstagramAssetsPicker.');
return;
}
let line = lines[headerSearchPathLineNumber];
lines[headerSearchPathLineNumber] = line.replace(/\ "\$\(SRCROOT\)\/\$\(PRODUCT_NAME\)\/cordova-plugin-InstagramAssetsPicker\/GPUImageHeaders\"/i, '');
let newConfig = lines.join('\n');
fs.writeFile(xcConfigBuildFilePath, newConfig, function (err) {
if (err) {
console.log('error updating build.xcconfig, err: ', err);
return;
}
console.log('successfully updated HEADER_SEARCH_PATHS in build.xcconfig');
});
I wanted to update my header search paths via the config as it was always a manual task setting it on build day. I have since added this plugin:
Cordova-Custom-Plugin
I was then able to add these to my config and didn't have to worry about it again.
<platform name="ios">
<!-- Set orientation on iPhone -->
<config-file platform="ios" target="*-Info.plist" parent="UISupportedInterfaceOrientations">
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
</config-file>
<!-- Set orientation on iPad -->
<config-file platform="ios" target="*-Info.plist" parent="UISupportedInterfaceOrientations~ipad">
<array>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</config-file>
<!-- Set Header Search Paths-->
<preference name="ios-XCBuildConfiguration-HEADER\_SEARCH\_PATHS" value="'$(TARGET_BUILD_DIR)/usr/local/lib/include' '$(OBJROOT)/UninstalledProducts/$(PLATFORM_NAME)/include' '$(BUILT_PRODUCTS_DIR)'" buildType="release" xcconfigEnforce="true" />

Titanium Android retrieve intent extras through notification

I have a background service that I start as soon as the app is lunched. This background service asks to the server every 5 seconds if there are new data available, as soon as there are new data I create a notification. When I click the notification app is lunched, but I am not able to retrieve the intent extra parameters that I put when I create the notification.
Below the code:
index.js
startService();
function startService(){
if(OS_ANDROID){
log("START SERVICE");
var intent = Titanium.Android.createServiceIntent({
url: 'service.js'
});
Titanium.Android.startService(intent);
}
}
//===========================================
// I tried to add this code so that I could get the intent but it does not work
if(OS_ANDROID){
log("==========================" + JSON.stringify(Ti.Android.currentActivity));
// prints : {"bubbleParent":true,"actionBar":{"navigationMode":0,"bubbleParent":true,"title":null,"apiName":"Ti.Android.ActionBar"},"apiName":"Ti.Android.Activity"}
var _intent = Ti.Android.currentActivity.getIntent();
log(JSON.stringify(_intent));
//prints {"bubbleParent":true,"action":null,"type":null,"data":null,"flags":268435456,"apiName":"Ti.Android.Intent"}
if (_intent.hasExtra('ntfId')) {
log("========= INTENT HAS EXTRA =========");
}else{
log("========= NO INTENT HAS EXTRA =========");
}
var currActivity = Titanium.Android.currentActivity;
var passedInText = currActivity.getIntent().getStringExtra(Ti.Android.EXTRA_TEXT);
log(passedInText);
//prints null
}
service.js
function createNotification(data){
log(data.id + " <<<<<<>>>>>> " + data.type);
var activity = Ti.Android.currentActivity;
var intent = Ti.Android.createIntent({
className : 'packageName.AppNameActivity',
action: Ti.Android.ACTION_MAIN,
packageName: Ti.App.id,
flags: Ti.Android.FLAG_ACTIVITY_NEW_TASK | Ti.Android.FLAG_ACTIVITY_SINGLE_TOP
});
intent.addCategory(Titanium.Android.CATEGORY_LAUNCHER);
intent.putExtra("ntfId", data.id);
var pending = Ti.Android.createPendingIntent({
intent : intent
});
var notification = Ti.Android.createNotification({
contentIntent : pending,
contentTitle : data.title,
contentText : data.desription,
tickerText : "AppName",
icon : Ti.App.Android.R.drawable.appicon,
sound: 'default',
defaults: Titanium.Android.NotificationManager.DEFAULT_VIBRATE,
flags : Titanium.Android.ACTION_DEFAULT | Titanium.Android.FLAG_AUTO_CANCEL | Titanium.Android.FLAG_SHOW_LIGHTS
});
Ti.Android.NotificationManager.notify(data.notification_id, notification);
}
I want to get "ntfId" when my app opens, so I can know which notification I clicked and show the correct data.
What I have found so far with the notifications is just how to lunch the application, and that does work but there is no clear example how to get notification detail.
I followed this link and docs , but I'm using Alloy model.
In case it is useful, I'll add my TiApp.xml too:
<android xmlns:android="http://schemas.android.com/apk/res/android">
<manifest>
<uses-permission android:name="android.permission.VIBRATE"/>
<application android:debuggable="true">
<activity
android:configChanges="keyboardHidden|orientation"
android:name="org.appcelerator.titanium.TiActivity"
android:screenOrientation="portrait"/>
<activity
android:configChanges="keyboardHidden|orientation"
android:label="Appname"
android:launchMode="singleTask"
android:name=".AppnameActivity"
android:screenOrientation="portrait" android:theme="#style/Theme.Titanium">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
<services>
<service url="service.js"/>
</services>
</android>
How do I get the extra parameters passed from the notification intent? Seems like when I do getCurrentActivity.getIntent() it's another intent totally different from what I want to get.
Can you suggest me something or point somewhere that maybe I missed looking at?
Thanks!
Last Edit
I pushed my project on gihub, TiAndroidNotification you can clone it and try, and see if you can change something.. It's not working yet, I'm still working on trying to solve this problem..
Thanks for any help or feedback!
I figure half of it out.
When the pending intent is created we should add even the activity:
var activity = Ti.Android.currentActivity;
var pending = Ti.Android.createPendingIntent({
activity:activity,
intent : intent
});
and from index.js I get it like this:
var act = Titanium.Android.currentActivity;
var _intent = act.intent;
var message = _intent.getStringExtra("ntfId");
This works ok as long as I close the application, but if the application is just in background and I click the notification it passes null and I do not understand why..
i've replaced the flags with Ti.Android.FLAG_ACTIVITY_NEW_TASK | Ti.Android.FLAG_ACTIVITY_MULTIPLE_TASK and it's working perfectly. It restarts the app, but it works.
and also you forgot to add "type : Titanium.Android.PENDING_INTENT_FOR_ACTIVITY" to pending.
here the js module I'm using
https://gist.github.com/muka/d4afae20a5732f3937e8
Seems responding properly when app is in background or foreground
Basically, the main activity listen to the newintent event and look for expected data in the intent extras received as argument
I'm curious to see if it works for different use cases, let me know.
Thanks

Exit on back button press windows phone using javascript

I used following function for the windows mobile app(phonegap) to handle the backbutton navigation function.
function onBackKeyDown() {
var currentPageId = $.mobile.activePage.attr('id');
if(currentPageId == 'contact-us' || currentPageId == 'about-us' || currentPageId == 'location-map' || currentPageId == 'services'){
$.mobile.changePage("index.html", {
transition : "slide",
reverse : true,
changeHash : false
});
}else{
navigator.app.exitApp();
}
}
I wanted to come to the index if the current page is not index. Otherwise exit the app.
It seems like navigator.app.exitApp() doesn't work in windows phone 7. Is there any solution for overcome this issue.
This plugin came in handy for me.
http://shinymetilda.github.io/Cordova_Exit_Plugin/
Add the .cs file to your plugin directory.
Add the following code to config.xml
<feature name="AppTerminate">
<param name="wp-package" value="AppTerminate" />
</feature>
write this code instead of navigator.app.exitApp()
cordova.exec(null, null, "AppTerminate", "execute", []);

Resources