I updated my project to NativeScript 6 and also updated Android Studio. Now, on both emulators and real devices, I get permission errors when I invoke telephony():
JS: nativeException: java.lang.SecurityException: getLine1NumberForDisplay: Neither user 10089 nor current process has android.permission.READ_PHONE_STATE, android.permission.READ_SMS, or android.permission.READ_PHONE_NUMBERS
Adding the permissions to my own AndroidManifest.xml makes no difference. And, Google Play has a recent policy that restricts READ_SMS to apps that can act as the default text app.
The code in question is in the nativescript-telephony plugin, and I've added an issue to that repository, tho it has not been active for some time.
Try to first install nativescript-contacts and nativescript-permissions plugin for get contact list with set permission for contacts read, edit and add permission.
Set permission in
App_Resources->Android->src->main->Androidmanifest.xml File
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
contact.component.html:-
<ListView row="0" [items]="contactList" separatorColor="transparent">
<ng-template let-contact="item" let-i="index">
<GridLayout rows="*, auto" columns="auto, *, auto">
<Image col="0" *ngIf="contact && contact.photo" class="site-template-image" stretch="aspectFill" [src]="contact ? contact.photo : '~/app/images/avatar.jpg'"></Image>
<StackLayout col="0" class="site-template-first-icon" verticalAlignment="center">
<Label col="0" *ngIf="contact && contact.photo == null" horizontalAlignment="center" fontSize="24" color="#fff" [text]="contact && contact.name.displayname ? contact.name.displayname.charAt(0) : 'N/A'"></Label>
</StackLayout>
<StackLayout col="1" verticalAlignment="center">
<Label class="site-template-title" [text]="contact ? contact.name.displayname:'No data'"></Label>
<WrapLayout orientation="horizontal" *ngIf="contact && contact.phoneNumbers.length > 0">
<Label class="site-template-description" *ngFor="let phoneNumber of contact.phoneNumbers" text="{{phoneNumber.value + ' '}}"></Label>
</WrapLayout>
</StackLayout>
<StackLayout margin="0,16,0,35" row="1" colSpan="2" backgroundColor="lightGray" height="0.3"></StackLayout>
</GridLayout>
</ng-template>
</ListView>
contact.component.ts
import * as contacts from "nativescript-contacts";
import * as permissions from "nativescript-permissions";
declare var android;
#Component({
selector: "Contact",
moduleId: module.id,
templateUrl: "./contact.component.html",
styleUrls: ["./contact.component.css"]
})
export class HomeComponent implements OnInit {
public contactList: any;
constructor() { }
ngOnInit(): void {
// Init your component properties here.
this.getContactList();
}
getContactList() {
permissions.requestPermissions([android.Manifest.permission.GET_ACCOUNTS, android.Manifest.permission.READ_CONTACTS, android.Manifest.permission.WRITE_CONTACTS], "I need these permissions.")
.then(() => {
let contactFields = ["name", "phoneNumbers"];
contacts.getAllContacts(contactFields).then(
(data) => {
if (data.data.length > 0) {
this.contactList = data.data;
}
}, (err) => {
console.log("Error: " + err);
this.isLoading = false;
}
);
});
}
}
Related
In my NativeScript Angular Project this is my router module:
export const routes = [
{path: "user", component: UserComponent,
children: [
{
path: "dashboard",
component: DashboardComponent
},
{
path: "notice",
component: NoticeComponent
},
{
path: "attendance",
component: AttendanceComponent
},
{
path: "subject",
component: SubjectComponent,
children: [
{
path: "discussion",
component: DiscussionComponent
},
{
path: "assignment",
component: AssignmentComponent,
children: [
{
path: "assignment-report", component: AssignmentReportComponent
}
]
}
]
}
]
}
];
The user.component.html has a bottom bar with links for Dashboard, Notice and Attendance components which are displayed in a router outlet in UserComponent. A user clicks on a subject in user/dashboard route to go to a subject. The SubjectComponent again has two tabs for 'Discussion' and 'Assignment' displayed in a router outlet inside SubjectComponent.
Problem: When I am in SubjectComponent and click on any of the links in the bottom bar to navigate to Dashboard, Attendance or Notice, the app crashes and I get this error:
An uncaught Exception occurred on "main" thread.
Calling js method onPause failed
Error: java.lang.NullPointerException: Attempt to invoke virtual method 'int android.graphics.Bitmap.getWidth()' on a null object reference .
It works fine if I just remove the <page-router-outlet> in SubjectComponent. I know I am somewhere messing up in nesting these routes but the error doesn't throw anything specific so I am unable to figure out.
Playground Sample: Open the playground sample and click on "Open Subject" button. When you are on the Subject Page, click on "Dashboard" button at the bottom. You will get the error.
Playground Link: https://play.nativescript.org/?template=play-ng&id=qGvsVF&v=11
Adding some code snippets that might be relevant to this problem.
user.component.html
<GridLayout style="background-color: #ffffff;" rows="*, auto">
<StackLayout class="content-container" row="0">
<page-router-outlet></page-router-outlet>
</StackLayout>
<StackLayout class="bottom-bar" row="1">
<StackLayout class="menu-container" horizontalAlignment="center" orientation="horizontal">
<Image (tap)="openDashboard()" [ngStyle]="{'border-color': isHome?'#1d87c9' :'#ffffff'}" class="btn-home" [src]="menuImages[0]"></Image>
<Image (tap)="openNotice()" [ngClass]="{'btn-active': isNotice}" class="btn-icon" [src]="menuImages[1]"></Image>
<Image (tap)="openAttendance()" [ngClass]="{'btn-active': isAttendance}" class="btn-icon" [src]="menuImages[2]"></Image>
</StackLayout>
</StackLayout>
openDashboard() method in user.component.ts. openNotice() and openAttendance() are similar
import { RouterExtensions } from "nativescript-angular/router";
constructor(private routerExtensions: RouterExtensions) {}
openDashboard(){
this.routerExtensions.navigateByUrl('user/dashboard');
this.menuImages = ["res://uni_full","res://notice_default","res://attendance_default"];
this.isHome = true;
this.isNotice = false;
this.isAttendance = false;
}
subject.component.html
<StackLayout class="main">
<GridLayout rows="auto, *">
<StackLayout row="0" class="subject-ribbon">
<StackLayout class="tab-container" orientation="horizontal">
<StackLayout (tap)="changeTab('discussion')" class="tab">
<Label class="tab-label" text=" Discussion"></Label>
<Label class="tab-indicator"></Label>
</StackLayout>
<StackLayout (tap)="changeTab('assignment')" class="tab">
<Label class="tab-label" text=" Assignment"></Label>
<Label class="tab-indicator"></Label>
</StackLayout>
</StackLayout>
</StackLayout>
<ScrollView (scroll)="onScroll($event)" row="1">
<StackLayout>
<page-router-outlet></page-router-outlet>
</StackLayout>
</ScrollView>
</GridLayout>
subject.component.ts
import { RouterExtensions } from "nativescript-angular/router";
constructor(private routerExtensions: RouterExtensions) {}
changeTab(tabName){
this.tabMode = tabName;
this.routerExtensions.navigateByUrl('user/subject/' + tabName);
}
I suspect the issue is that you had wrapped your frame (page-router-outlet) with a ScrollView. It's not a good practice for various reasons and the app seems to crash because of that. Moving the ScrollView within the page resolves the issue.
Updated Playground
I’m new to NativeScript so please excuse me if I’m asking a stupid question. I tried to figure it out using google for days now but had no success.
On the bottom of the app I have some labels with icon font. So what I want to do is to change the label color when clicked.
Screenshot
Here is my app.components.ts
import { Component } from "#angular/core";
import * as dockModule from "tns-core-modules/ui/layouts/dock-layout";
import { TNSFontIconService } from 'nativescript-ng2-fonticon';
import {topmost} from "ui/frame";
import {Page} from "ui/page";
#Component({
selector: "my-app",
template: `
<!-- <ActionBar title="Rupa GIS" class="action-bar" font-size= "7"></ActionBar> -->
<ActionBar title="Rupa GIS" android.icon="res://icon" android.iconVisibility="always" class="action-bar" ></ActionBar>
<!-- Your UI components go here -->
<Page class="pg">
<DockLayout class="formMessag">
<GridLayout class="formMessage1" columns="2*,2*,2*,2*" rows="" dock="bottom" verticalAlignment="bottom" class="mdi" >
<Label class="mdi1" id="dd" [text]="'mdi-map' | fonticon" row="0" col="0" (tap)="onTapMap()" backgroundColor="transparent" verticalAlignment="center" horizontalAlignment="center" ></Label>
<Label class="mdi2" [text]="'mdi-camera' | fonticon" row="0" col="1" (tap)="onTapCam()" backgroundColor="transparent" verticalAlignment="center" horizontalAlignment="center" ></Label>
<Label class="mdi3" [text]="'mdi-info' | fonticon" row="0" col="2" (tap)="onTapInfo()" backgroundColor="transparent" verticalAlignment="center" horizontalAlignment="center" ></Label>
<Label class="mdi4" [text]="'mdi-settings' | fonticon" row="0" col="3" (tap)="onTapSett()" backgroundColor="transparent" verticalAlignment="center" horizontalAlignment="center" ></Label>
</GridLayout>
</DockLayout>
</Page>
`
})
export class AppComponent {
// Your TypeScript logic goes here
// var isSelected = "true";
onTapMap(dd) {
// boolean isSelected = true;
let self = this;
console.log("MAPA");
}
onTapCam() {
console.log("KAMERA");
}
onTapInfo() {
console.log("INFORMACIJE");
}
onTapSett() {
console.log("PODESAVANJA");
}
constructor(private fonticon: TNSFontIconService, private page: Page) {
page.actionBarHidden = true;
}
}
export function pageLoaded() {
console.log("DOBAR DAN!");
}
And here is my app.module.ts
import { NgModule, NO_ERRORS_SCHEMA } from "#angular/core";
import { NativeScriptModule } from "nativescript-angular/nativescript.module";
import { TNSFontIconModule } from 'nativescript-ng2-fonticon';
import { AppComponent } from "./app.component";
#NgModule({
declarations: [AppComponent],
bootstrap: [AppComponent],
imports: [
NativeScriptModule,
TNSFontIconModule.forRoot({
'mdi': 'material-design-icons.css'
})
],
schemas: [NO_ERRORS_SCHEMA],
})
export class AppModule {}
And here is the main.ts
And here is the main.ts
import { platformNativeScriptDynamic } from "nativescript-angular/platform";
import { AppModule } from "./app.module";
platformNativeScriptDynamic().bootstrapModule(AppModule);
How could I make it so that the onTapMap function changes the color of a label? Any advise or guidance would be greatly appreciated.
Thank you!
Srdjan
You can apply class based on tap events -
<Label class="{{ checkYes ? 'redColor' : 'defaultColor'}}" id="dd"
[text]="'mdi-map' | fonticon" row="0" col="0" (tap)="onTapMap()"
backgroundColor="transparent" verticalAlignment="center"
horizontalAlignment="center" ></Label>`
here i've changed the css class impl. -
class="{{ checkYes ? 'redColor' : 'defaultColor'}}"
on tap event make this variable checkYes true or false and in your css file define two classes as -
.redColor {
color:red;
}
.defaultColor {
color:gray
}
and for function onTapMap() -
onTapMap() {
if(this.checkYes)
this.checkYes = false;
else
this.checkYes = true;
}
hi i need to toggle individual content in listview when the respective button is clicked in nativescript angular,
i added bellow my code. if anyone know please answer me. thanks in advance
import { Component, OnInit } from "#angular/core";
import { Item } from "./item";
import { ItemService } from "./item.service";
#Component({
selector: "ns-items",
moduleId: module.id,
templateUrl: "./items.component.html",
})
export class ItemsComponent implements OnInit {
items: Item[];
isList = true;
toggle(){
this.isList = !this.isList;
}
constructor(private itemService: ItemService) { }
ngOnInit(): void {
this.items = this.itemService.getItems();
}
}
and here my items.component.html
<ActionBar title="My App" class="action-bar">
</ActionBar>
<StackLayout class="page">
<ListView [items]="items" class="list-group">
<template let-item="item">
<GridLayout columns="auto,auto" width="210" height="210" >
<Label [text]="item.name" col="0"
class="list-group-item" visibility="{{isList ? 'visible' : 'collapse'}}"></Label>
<Button [text]="isList ? 'hide' : 'Show'" col="1" (tap)="toggle()"></Button>
</GridLayout>
</template>
</ListView>
</StackLayout>
here problem is when i click the button all the labels are toggle. so i need to generate the variable dynamically. i m very beginner so anyone can help me?
thank in advance.
Guess you have modified the starter template. So if you want hide label on particular list item, try this.
Add visible property to item.ts
export class Item {
id: number;
name: string;
role: string;
visible: boolean;
}
Set value to visible in your item.service.ts. It will be like below.
{ id: 1, name: "Ter Stegen", role: "Goalkeeper", visible: true },
{ id: 3, name: "Piqué", role: "Defender", visible: true },
Your list template should be
<template let-item="item">
<GridLayout class="list-group-item" columns="*,auto">
<Label col="0" [text]="item.name" [visibility]="item.visible ? 'visible' : 'collapse'"></Label>
<Button class="btn" col="1" [text]="item.visible ? 'Hide' : 'Show'" (tap)="toggle(item)"></Button>
</GridLayout>
</template>
And finally the toggle method will be,
ngOnInit(): void {
this.items = this.itemService.getItems();
}
toggle(item: Item) {
item.visible = !item.visible;
}
I followed the guidelines to use nativescript-fresco with ng2+nativescript for my android app because it was crashing every time I scrolled more than once down then up. The component in which I'm using it is a listview which displays 20 online images. The urls are passed from the parent component through a #Input directive which works. However, when I switched to FrescoDrawee, the listview is rendered but the images are not.
Here is the html for the component
<GridLayout columns="*,*,*,*,*,*,*,*,*" height="100" class="item"
xmlns:nativescript-fresco="nativescript-fresco">
<ios>
<Image [src]="data.imageUrl" row="0" col="0" colspan="3" class="ios-product-image" (tap)="details(data)"></Image>
</ios>
<android>
<!--<Image [src]="data.imageUrl" row="0" col="0" colspan="3" class="android-product-image" (tap)="details(data)"></Image>-->
<nativescript-fresco:FrescoDrawee width="100" height="100"
[imageUri]="data.imageUrl"
></nativescript-fresco:FrescoDrawee>
</android>
<StackLayout row="0" col="3" colSpan="5" (tap)="details(data)">
<Label [text]="data.name" class="product-name"></Label>
<Label [text]="data.description" textWrap="true" class="product-description"></Label>
<GridLayout columns="*,*,*,*,*,*,*,*,*,*" class="increment-decrement" style="width: 100%; height: 20%; vertical-align: bottom">
<Label [text]="data.price" class="product-price" col="0" colSpan="5"></Label>
<ios>
<Button text="-" col="5" colSpan="1" (tap)="dec()"></Button>
<Label [text]="count" col="6" colSpan="3" class="product-amount" ></Label>
<Button text="+" col="9" colSpan="1" (tap)="inc()"></Button>
</ios>
<android>
<Label text="-" col="5" colSpan="1" class="inc-dec" (tap)="dec()"></Label>
<Label [text]="count" col="6" colSpan="3" class="product-amount"></Label>
<Label text="+" col="9" colSpan="1" class="inc-dec" (tap)="inc()"></Label>
</android>
</GridLayout>
</StackLayout>
<ios>
<Button col="11" class="cart" colSpan="1"></Button>
</ios>
<android>
<Label col="11" class="cart" colSpan="1"></Label>
</android>
<Image src="~/media/ic_add_shopping_cart_white_24dp.png" col="11"
style="height: 20%; width: 20%;" (tap)="addToCart()"></Image>
</GridLayout>
And this is the Type-script
import { Component, OnInit, Input} from "#angular/core";
import LabelModule = require("ui/label");
import application = require("application");
import { RouterExtensions } from "nativescript-angular/router";
import { PublicVariables } from "../../shared/publicVariables";
#Component({
selector:'product-list',
templateUrl: 'pages/products/product_list.html',
styleUrls: ['pages/products/product_list-common.css', 'pages/products/product_list.css']
})
export class ProductListComponent implements OnInit {
#Input() data: any;
private count=0;
constructor(private routerExtensions: RouterExtensions) {}
ngOnInit() {
}
details() {
this.routerExtensions.navigate(["/product/:id"]);
PublicVariables.currentProduct = this.data;
}
inc() {
++this.count;
}
dec() {
if(this.count>0) {
--this.count;
}
}
}
I'm relatively new to native-script so my code may not be the cleanest.
I have added the nativescript-fresco plugin to my project, imported and initialized it in AppModule. What I don't know is if I need to add anything to the component itself apart from the FrescoDrawee tag because I didn't see anything indicating that in the documentation.
Kindly help me figure out what the problem with my code?
I think the problem is with the prefix, you can use it as:
<FrescoDrawee width="100" height="100" [imageUri]="data.imageUrl"></FrescoDrawee>
And for sake of completeness this is what needs to be added to app.module.ts when used with Angular:
import { TNSFrescoModule } from "nativescript-fresco/angular";
import fresco = require("nativescript-fresco");
import application = require("application");
if (application.android) {
application.onLaunch = function (intent) {
fresco.initialize();
};
}
#NgModule({
imports: [
TNSFrescoModule
I have a login button which when clicked will log the user in by making http calls to the server.
While this is happening I want the activity indicator to show up and disable the button and every other thing on the page and just show the activity indicator over it.
Note that I will place time outs and other measures to make sure that the Activity indicator doesn't end up trapping the user.
Also, I do not want the content in the background to disappear. I just want the activity indicator to overlap it.
Here is my ui code which was taken for this SO answer https://stackoverflow.com/a/39124735/4412482
<GridLayout>
<StackLayout>
//page content goes here
</StackLayout>
</StackLayout>
<StackLayout class="dimmer" visibility="{{showLoading ? 'visible' : 'collapsed'}}"></StackLayout>
<GridLayout rows="*" visibility="{{showLoading ? 'visible' : 'collapsed'}}">
<ActivityIndicator busy="true" width="50" height="50" color="#0c60ee"></ActivityIndicator>
</GridLayout>
</GridLayout>
To disable the events to the components you could use isUserInteractionEnabled property, which will disable the whole interaction to the component and then you could show the ActivityIndicator. I am providing sample code below.
app.component.html
<GridLayout rows="*">
<StackLayout row="0" class="p-20">
<Label text="Tap the button" class="h1 text-center"></Label>
<Button text="TAP" (tap)="onTap()" class="btn btn-primary btn-active" [isUserInteractionEnabled]="buttoninteraction" ></Button>
<Label [text]="message" class="h2 text-center" textWrap="true"></Label>
</StackLayout>
<ActivityIndicator row="0" [busy]="acstate" row="1" class="activity-indicator"></ActivityIndicator>
</GridLayout>
app.component.ts
import { Component } from "#angular/core";
#Component({
selector: "my-app",
templateUrl: "app.component.html",
})
export class AppComponent {
public counter: number = 16;
public buttoninteraction = true;
public acstate = false;
public get message(): string {
if (this.counter > 0) {
return this.counter + " taps left";
} else {
return "Hoorraaay! \nYou are ready to start building!";
}
}
public onTap() {
this.counter--;
this.buttoninteraction=false;
this.acstate=true;
}
}
Hope this helps.