Hi, I’m developing a ionic app targeting, for now, Android and iOS. The app runs without problem with ionic serve and browser, genymotion and iOS simulator. It does not work using a physical android device, more precisely a Samsung Galaxy Trend Plus GT-S7580 mounting Android 4.2.2 (I cannot root it to install a more updated custom rom, this is my only physical device).
This app doesn’t have a local state, doesn’t have a particular logic, it’s just a simple REST client for an API I developed using PHP and Laravel 5. I don’t have CORS errors (in the emulators the app works great) and the white-list plugin is installed and configured properly (I think I properly configured also the meta tag and ng-csp, but look at my body and head tags and let me know if they are correct).
I’m using ngresource. The same problems is present with pure $http provider. Every time a make a request, i.d. the use is logging using a form to receive an oauth token from my server, the POST or GET request is not resolved nor timeouted: my app doesn’t call the success or error function inside the resource or $http promise and the spinner is loading forever. I put two console.log inside the success and error callbacks, the apps doesn’t run them.
In chrome::inspect there’s no trace of network activity. If I don’t use crosswalk, I can see through adb logcat a null pointer exception and a Fatal signal 11 (SIGSEGV) error every time an http request is made.
My app can’t work even minimally if it can’t do http requests and I’m stuck. I have to finish this work soon or I’ll be in toruble. Can you help me please?
Here you can find all the details:
- Ionic CLI version: 1.7.0
- Ionic Framework version: the latest stable one (1.1 I guess)
- Cordova CLI version: 5.3.3
- Android Platform version: 4.1.1
- iOS Platform version: 3.9.1
And I’m using these plugins versions:
- com.ionic.keyboard 1.0.4 “Keyboard”
- com.rjfun.cordova.httpd 0.9.2 “CorHttpd”
- cordova-plugin-camera 1.2.0 “Camera”
- cordova-plugin-console 1.0.1 “Console”
- cordova-plugin-crosswalk-webview 1.3.1 “Crosswalk WebView Engine”
- cordova-plugin-device 1.0.1 “Device”
- cordova-plugin-file 3.0.0 “File”
- cordova-plugin-file-transfer 1.3.0 “File Transfer”
- cordova-plugin-media-capture 1.0.1 “Capture”
- cordova-plugin-network-information 1.0.1 “Network Information”
- cordova-plugin-screen-orientation 1.4.0 “Screen Orientation”
- cordova-plugin-splashscreen 2.1.0 “Splashscreen”
- cordova-plugin-whitelist 1.0.0 “Whitelist”
- ionic-plugin-keyboard 1.0.7 “Keyboard”
And this is my config.xml:
<?xml version='1.0' encoding='utf-8'?>
<widget id="com.ionicframework.fillussoclient920328" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<name>fillusso-client</name>
<description>
An Ionic Framework and Cordova project.
</description>
<author email="hi@ionicframework" href="http://ionicframework.com/">
Ionic Framework Team
</author>
<content src="index.html" />
<preference name="webviewbounce" value="false" />
<preference name="UIWebViewBounce" value="false" />
<preference name="DisallowOverscroll" value="true" />
<preference name="android-minSdkVersion" value="17" />
<preference name="BackupWebStorage" value="none" />
<preference name="SplashScreen" value="screen" />
<preference name="AutoHideSplashScreen" value="false" />
<preference name="SplashScreenDelay" value="300000000" />
<feature name="StatusBar">
<param name="ios-package" onload="true" value="CDVStatusBar" />
</feature>
<allow-navigation href="*" />
<access origin="*" subdomains="true" />
<allow-intent href="*" />
<platform name="android">
<icon density="ldpi" src="resources/android/icon/drawable-ldpi-icon.png" />
<icon density="mdpi" src="resources/android/icon/drawable-mdpi-icon.png" />
<icon density="hdpi" src="resources/android/icon/drawable-hdpi-icon.png" />
<icon density="xhdpi" src="resources/android/icon/drawable-xhdpi-icon.png" />
<icon density="xxhdpi" src="resources/android/icon/drawable-xxhdpi-icon.png" />
<icon density="xxxhdpi" src="resources/android/icon/drawable-xxxhdpi-icon.png" />
<splash density="land-ldpi" src="resources/android/splash/drawable-land-ldpi-screen.png" />
<splash density="land-mdpi" src="resources/android/splash/drawable-land-mdpi-screen.png" />
<splash density="land-hdpi" src="resources/android/splash/drawable-land-hdpi-screen.png" />
<splash density="land-xhdpi" src="resources/android/splash/drawable-land-xhdpi-screen.png" />
<splash density="land-xxhdpi" src="resources/android/splash/drawable-land-xxhdpi-screen.png" />
<splash density="land-xxxhdpi" src="resources/android/splash/drawable-land-xxxhdpi-screen.png" />
<splash density="port-ldpi" src="resources/android/splash/drawable-port-ldpi-screen.png" />
<splash density="port-mdpi" src="resources/android/splash/drawable-port-mdpi-screen.png" />
<splash density="port-hdpi" src="resources/android/splash/drawable-port-hdpi-screen.png" />
<splash density="port-xhdpi" src="resources/android/splash/drawable-port-xhdpi-screen.png" />
<splash density="port-xxhdpi" src="resources/android/splash/drawable-port-xxhdpi-screen.png" />
<splash density="port-xxxhdpi" src="resources/android/splash/drawable-port-xxxhdpi-screen.png" />
</platform>
<platform name="ios">
<icon height="57" src="resources/ios/icon/icon.png" width="57" />
<icon height="114" src="resources/ios/icon/icon@2x.png" width="114" />
<icon height="40" src="resources/ios/icon/icon-40.png" width="40" />
<icon height="80" src="resources/ios/icon/icon-40@2x.png" width="80" />
<icon height="50" src="resources/ios/icon/icon-50.png" width="50" />
<icon height="100" src="resources/ios/icon/icon-50@2x.png" width="100" />
<icon height="60" src="resources/ios/icon/icon-60.png" width="60" />
<icon height="120" src="resources/ios/icon/icon-60@2x.png" width="120" />
<icon height="180" src="resources/ios/icon/icon-60@3x.png" width="180" />
<icon height="72" src="resources/ios/icon/icon-72.png" width="72" />
<icon height="144" src="resources/ios/icon/icon-72@2x.png" width="144" />
<icon height="76" src="resources/ios/icon/icon-76.png" width="76" />
<icon height="152" src="resources/ios/icon/icon-76@2x.png" width="152" />
<icon height="29" src="resources/ios/icon/icon-small.png" width="29" />
<icon height="58" src="resources/ios/icon/icon-small@2x.png" width="58" />
<icon height="87" src="resources/ios/icon/icon-small@3x.png" width="87" />
<splash height="1136" src="resources/ios/splash/Default-568h@2x~iphone.png" width="640" />
<splash height="1334" src="resources/ios/splash/Default-667h.png" width="750" />
<splash height="2208" src="resources/ios/splash/Default-736h.png" width="1242" />
<splash height="1242" src="resources/ios/splash/Default-Landscape-736h.png" width="2208" />
<splash height="1536" src="resources/ios/splash/Default-Landscape@2x~ipad.png" width="2048" />
<splash height="768" src="resources/ios/splash/Default-Landscape~ipad.png" width="1024" />
<splash height="2048" src="resources/ios/splash/Default-Portrait@2x~ipad.png" width="1536" />
<splash height="1024" src="resources/ios/splash/Default-Portrait~ipad.png" width="768" />
<splash height="960" src="resources/ios/splash/Default@2x~iphone.png" width="640" />
<splash height="480" src="resources/ios/splash/Default~iphone.png" width="320" />
</platform>
<preference name="xwalkVersion" value="14+" />
<preference name="xwalkCommandLine" value="--disable-pull-to-refresh-effect" />
<preference name="xwalkMode" value="embedded" />
</widget>
My index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<meta http-equiv="Content-Security-Policy" content="default-src *; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval'">
<title></title>
<link href="lib/ionic/css/ionic.css" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">
<link href="lib/angular/angular-csp.css" rel="stylesheet">
<!-- IF using Sass (run gulp sass first), then uncomment below and remove the CSS includes above
<link href="css/ionic.app.css" rel="stylesheet">
-->
<!-- ionic/angularjs js -->
<script src="lib/ionic/js/ionic.bundle.js"></script>
<script src="lib/angular-messages/angular-messages.js"></script>
<script src="lib/angular-resource/angular-resource.js"></script>
<script src="lib/localforage/dist/localforage.js"></script>
<script src="lib/angular-localforage/dist/angular-localForage.js"></script>
<script src="lib/angular-sanitize/angular-sanitize.js"></script>
<script src="lib/videogular/videogular.js"></script>
<script src="lib/videogular-controls/vg-controls.js"></script>
<script src="lib/videogular-overlay-play/vg-overlay-play.js"></script>
<script src="lib/videogular-poster/vg-poster.js"></script>
<script src="lib/videogular-buffering/vg-buffering.js"></script>
<!-- cordova script (this will be a 404 during development) -->
<script src="lib/ngCordova/dist/ng-cordova.js"></script>
<script src="cordova.js"></script>
<!-- your app's js -->
<script src="js/app.js"></script>
<script src="js/controllers.js"></script>
<script src="js/services.js"></script>
</head>
<body ng-app="starter" ng-csp>
<!--
The views will be rendered in the <ion-nav-view> directive below
Templates are in the /templates folder (but you could also
have templates inline in this html file if you'd like).
-->
<ion-nav-view></ion-nav-view>
</body>
</html>
My app.js
// Ionic Starter App
// angular.module is a global place for creating, registering and retrieving Angular modules
// 'starter' is the name of this angular module example (also set in a <body> attribute in index.html)
// the 2nd parameter is an array of 'requires'
// 'starter.services' is found in services.js
// 'starter.controllers' is found in controllers.js
angular.module('starter', ['ionic', 'ngCordova', 'starter.controllers', 'starter.services', 'ngMessages',
'ngResource', 'LocalForageModule', "ngSanitize", "com.2fdevs.videogular", "com.2fdevs.videogular.plugins.controls",
"com.2fdevs.videogular.plugins.overlayplay", "com.2fdevs.videogular.plugins.poster"])
.run(function ($ionicPlatform, $cordovaSplashscreen, $state, loginService, $rootScope, $ionicLoading,
$ionicHistory, $localForage, $ionicPopup) {
$ionicPlatform.ready(function () {
// Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
// for form inputs)
if (window.cordova && window.cordova.plugins && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
cordova.plugins.Keyboard.disableScroll(true);
}
if (window.StatusBar) {
// org.apache.cordova.statusbar required
StatusBar.styleLightContent();
}
//Imposto variabili globali
$localForage.setItem('clientSecret', 'cicciopasticcio').then(function() {
$localForage.getItem('clientSecret').then(function(clientSecret) {
loginService.setClientSecret(clientSecret);
})
});
$localForage.setItem('clientId', 'cordova-app').then(function() {
$localForage.getItem('clientId').then(function(clientId) {
loginService.setClientId(clientId);
})
});
//
//Nascondo la Splash Screen
setTimeout(function () {
$cordovaSplashscreen.hide()
}, 2000);
//Loading splash screen
$rootScope.$on('loading:show', function () {
console.log("CICCIO: RICHIESTA ON");
$ionicLoading.show({
template: '<div>Attendi per favore ...</div> <br><br> <div><ion-spinner></ion-spinner></div>'
})
});
//Registrazione di tutti gli eventi
$rootScope.$on('loading:hide', function () {
console.log("CICCIO: RICHIESTA OFF");
$ionicLoading.hide();
});
$rootScope.$on('logout', function () {
$ionicHistory.nextViewOptions({
disableAnimate: false,
disableBack: true
});
$ionicLoading.hide();
$state.go('login');
});
$rootScope.$on('$cordovaNetwork:online', function(event, networkState){
if (!loginService.isLogged()) {
$ionicHistory.nextViewOptions({
disableAnimate: false,
disableBack: true
});
$state.go('login');
} else {
$ionicHistory.nextViewOptions({
disableAnimate: false,
disableBack: true
});
$state.go('home');
}
});
$rootScope.$on('$cordovaNetwork:offline', function(event, networkState){
$ionicHistory.nextViewOptions({
disableAnimate: false,
disableBack: true
});
$state.go('offline');
});
//
$rootScope.$on("$stateChangeError", function(event, toState, toParams, fromState, fromParams, error) {
$ionicPopup.alert({
title: 'Errore',
template: error,
okText: 'Chiudi',
okType: 'button button-assertive'
});
});
});
})
.config(function ($stateProvider, $urlRouterProvider, $httpProvider, $sceDelegateProvider) {
// Ionic uses AngularUI Router which uses the concept of states
// Learn more here: https://github.com/angular-ui/ui-router
// Set up the various states which the app can be in.
// Each state's controller can be found in controllers.js
$stateProvider
.state('home', {
url: "/home",
cache: false,
templateUrl: "templates/home.html",
controller: "HomeCtrl",
resolve: {
item: function(loginService, $localForage) {
return $localForage.getItem('token').then(function(token) {
loginService.setToken(token);
return $localForage.getItem('refreshToken').then(function(refreshToken) {
loginService.setRefreshToken(refreshToken);
return $localForage.getItem('clientSecret').then(function(clientSecret) {
loginService.setClientSecret(clientSecret);
return $localForage.getItem('clientId').then(function(clienId) {
loginService.setClientId(clienId);
return $localForage.getItem('email').then(function(email) {
loginService.setEmail(email);
})
})
})
})
})
}
}
})
.state('login', {
url: "/login",
cache: false,
templateUrl: "templates/login.html"
//controller: "LoginCtrl"
})
.state('tab', {
url: '/tab',
cache: false,
abstract: 'true',
templateUrl: 'templates/tabs.html'
})
.state('tab.images', {
url: '/images',
cache: false,
views: {
'tab-images': {
templateUrl: 'templates/tab-images.html',
controller: 'ImagesCtrl'
}
}
})
.state('tab.videos', {
url: '/videos',
cache: false,
views: {
'tab-videos' : {
templateUrl: 'templates/tab-videos.html',
controller: 'VideosCtrl',
resolve: {
/*mediaServer: function(MediaServerService) {
return MediaServerService.startServer().then(function(url) {
return url;
});
}*/
mediaServer: function(MediaServerService) {
MediaServerService.updateStatus().then(function(url) {
return MediaServerService.stopServer();
}, function(error) {
//
})
}
}
}
}
})
.state('tab.texts', {
url: '/texts',
cache: false,
views: {
'tab-texts': {
templateUrl: 'templates/tab-texts.html',
controller: 'TextsCtrl'
}
}
})
.state('edit', {
url: '/edit',
abstract: true,
cache: false,
controller: 'EditCtrl',
templateUrl: 'templates/edit.html'
})
.state('edit.image', {
url: ('/image'),
cache: false,
views: {
'edit-image': {
templateUrl: 'templates/edit-image.html',
controller: 'MediaCtrl'
}
},
data: {
type: 'image'
}
})
.state('edit.video', {
url: ('/video'),
cache: false,
views: {
'edit-video': {
templateUrl: 'templates/edit-video.html',
controller: 'MediaCtrl'
}
},
data: {
type: 'video'
}/*, resolve: {
media: function(MediaFileFactory, loginService, MediaService) {
return MediaFileFactory.auth(loginService.getToken()).query({media_id: MediaService.getCurrentMediaId()}).$promise;
}
}*/
})
.state('edit-product', {
url: ('/product'),
cache: false,
templateUrl: 'templates/edit-product.html',
controller: 'ProductCtrl'
})
.state('view', {
url: '/view',
abstract: true,
cache: false,
controller: 'ViewCtrl',
templateUrl: 'templates/view.html',
data: {
type: ''
}
})
.state('view.image', {
url: ('/image'),
cache: false,
views: {
'view-image': {
templateUrl: 'templates/view-image.html',
controller: 'MediaCtrl'
}
},data: {
type: 'image'
}
})
.state('view.video', {
url: ('/video'),
cache: false,
views: {
'view-image': {
templateUrl: 'templates/view-video.html',
controller: 'MediaCtrl'
}
},data: {
type: 'video'
}
})
.state('stages', {
url: '/stages',
cache: false,
templateUrl: 'templates/stages.html',
controller: 'StagesCtrl'
})
.state('offline', {
url: '/offline',
templateUrl: 'templates/offline.html',
controller: 'OfflineCtrl'
})
.state('camera', {
url: '/camera',
cache: false,
templateUrl: 'templates/camera.html',
controller: 'CameraCtrl',
data: {
type: 'image'
}
})
.state('videocamera', {
url: '/videocamera',
cache: false,
templateUrl: 'templates/videocamera.html',
controller: 'VideoCameraCtrl',
resolve: {
mediaServer: function(MediaServerService) {
return MediaServerService.startServer().then(function(url) {
return url;
});
}
},data: {
type: 'video'
}
})
.state('product', {
url: '/product',
cache: false,
templateUrl: 'templates/product.html',
controller: 'ProductCtrl'
});
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/home');
//Http Provider
$httpProvider.interceptors.push(function ($q, $rootScope) {
return {
request: function (config) {
$rootScope.$broadcast('loading:show');
return config;
},
response: function (response) {
$rootScope.$broadcast('loading:hide');
return response;
},
requestError: function (rejection) {
console.log(rejection.status);
$rootScope.$broadcast('loading:hide');
return $q.reject(rejection);
},
responseError: function (rejection) {
console.log(rejection.status);
$rootScope.$broadcast('loading:hide');
/*if (rejection.status === 401) {
$rootScope.$broadcast('401');
} else if(rejection.status == 500) {
$rootScope.$broadcast('500');
}*/
return $q.reject(rejection);
}
}
});
$httpProvider.defaults.useXDomain = true;
//$httpProvider.defaults.withCredentials = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
$httpProvider.defaults.headers.common["Accept"] = "application/json";
$httpProvider.defaults.headers.common["Content-Type"] = "application/json";
$httpProvider.defaults.headers.common["Content-Type"] = "application/json";
$sceDelegateProvider.resourceUrlWhitelist(['**']);
});
The controller:
.controller('LoginCtrl', function ($scope, $ionicPopup, loginService, $state, LoginFactory, $localForage) {
function login(loginForm) {
console.log("CICCIO: FORM");
if (loginForm.$valid) {
console.log("CICCIO :VALIDA");
if (!loginService.isLogged()) {
$localForage.setItem('email', $scope.email).then(function() {
loginService.setEmail($scope.email);
});
var loginQueryString = "username=" + $scope.email +
"&client_secret=" + loginService.getClientSecret() +
"&grant_type=password&client_id=" + loginService.getClientId() +
"&password=" + $scope.pass;
console.log("CICCIO :STOP PER");
LoginFactory.login({}, loginQueryString, function(data) {
console.log("CICCIO: ECCHE " + data);
var token = data.access_token;
var refreshToken = data.refresh_token;
if (token != null) {
$localForage.setItem('token', token).then(function() {
loginService.setToken(token);
$localForage.setItem('refreshToken', refreshToken).then(function() {
loginService.setRefreshToken(refreshToken);
$state.go('home');
});
});
} else {
loginForm.email.$touched = false;
loginForm.pass.$touched = false;
$ionicPopup.alert({
title: 'Errore',
template: '<div class="alert">Dati non corretti o problema alla rete, riprovare.</div>',
okText: 'Chiudi',
okType: 'button button-assertive'
});
}
}, function(data) {
console.log("CICCIO: ECCHE ERRORE " + data);
loginForm.email.$touched = false;
loginForm.pass.$touched = false;
$scope.email = '';
$scope.pass = '';
if(data == null) {
$ionicPopup.alert({
title: 'Errore',
template: '<div class="alert">Problema di rete, riprovare più tardi.</div>',
okText: 'Chiudi',
okType: 'button button-assertive'
});
} else {
$ionicPopup.alert({
title: 'Errore',
template: '<div class="alert">Nome utente o password errati, riprovare.</div>',
okText: 'Chiudi',
okType: 'button button-assertive'
});
}
});
console.log("CICCIO :FINITO");
}
}
}
function logout() {
loginService.logout();
$state.go('login');
}
$scope.init = function() {
if(loginService.isLogged()) {
$state.go('home');
}
};
//$scope.init();
$scope.login = login;
$scope.logout = logout;
$scope.email = '';
$scope.pass = '';
})
and using this factory and service:
.factory('LoginFactory', ['$resource', 'AppService', function($resource, AppService) {
return $resource(AppService.getServerAddress() + "/oauth/access_token", {},
{
login: {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
timeout: 1
}
}
);
}])
.service('loginService', ["$rootScope", 'LoginFactory', '$localForage', function ($rootScope, LoginFactory, $localForage) {
var token = null;
var refreshToken = null;
var clientSecret = null;
var clientId = null;
var email = null;
var loginTries = 0;
this.setToken = function(t) {
token = t;
};
this.setRefreshToken = function(r) {
refreshToken = r;
};
this.setClientSecret = function(s) {
clientSecret = s;
};
this.setClientId = function(i) {
clientId = i;
};
this.setEmail = function(e) {
email = e;
};
this.getToken = function() {
return token;
};
this.getRefreshToken = function() {
return refreshToken;
};
this.getClientSecret = function() {
return clientSecret;
};
this.getClientId = function() {
return clientId;
};
this.getEmail = function() {
return email;
};
this.isLogged = function() {
if(token == null) {
return false;
} else {
return true;
}
};
this.logout = function() {
if (this.isLogged()) {
$localForage.removeItem('token').then(function() {
token = null;
$rootScope.$broadcast('logout');
});
}
};
this.refreshToken = function() {
if(refreshToken != null) {
var refreshQueryString = "username=" + email +
"&client_secret=" + clientSecret +
"&grant_type=refresh_token&client_id=" + clientId +
"&refresh_token=" + refreshToken;
LoginFactory.login({}, refreshQueryString, function(data) {
var nToken = data.access_token;
var rToken = data.refresh_token;
if (nToken != null) {
$localForage.setItem('token', nToken).then(function(){
token = nToken;
$localForage.setItem('refreshToken', rToken).then(function() {
refreshToken = rToken;
})
})
} else {
logout();
}
}, function(data) {
$localForage.getItem('loginTries').then(function(loginTries) {
if(loginTries < 3) {
loginTries = loginTries + 1;
} else {
loginTries = 0;
logout();
}
})
});
}
};
}])