fix: move wrapped storage to unwrapped if not encrypted (#1603)
This commit is contained in:
@@ -1,2 +1,19 @@
|
|||||||
node_modules
|
node_modules
|
||||||
scripts
|
scripts
|
||||||
|
actions
|
||||||
|
.yarn
|
||||||
|
.vscode
|
||||||
|
.sass-cache
|
||||||
|
.husky
|
||||||
|
.github
|
||||||
|
.git
|
||||||
|
**/dist
|
||||||
|
**/coverage
|
||||||
|
**/node_modules
|
||||||
|
packages/components
|
||||||
|
packages/docs
|
||||||
|
packages/mobile/android
|
||||||
|
packages/mobile/ios
|
||||||
|
packages/mobile/html
|
||||||
|
packages/snjs/mocha
|
||||||
|
*.d.ts
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"extends": ["./node_modules/@standardnotes/config/src/.eslintrc"],
|
"extends": ["./node_modules/@standardnotes/config/src/.eslintrc"],
|
||||||
"ignorePatterns": [".eslintrc.js", "*.webpack.*.js", "webpack-defaults.js", "jest.config.js", "__mocks__"]
|
"ignorePatterns": [".eslintrc.js", "*.webpack.*.js", "webpack-defaults.js", "jest.config.js", "__mocks__", "**/**/coverage"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
||||||
|
coverage
|
||||||
3
packages/desktop/.eslintignore
Normal file
3
packages/desktop/.eslintignore
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
jsign
|
||||||
@@ -5,4 +5,5 @@ ios
|
|||||||
e2e
|
e2e
|
||||||
android
|
android
|
||||||
fastlane
|
fastlane
|
||||||
WebFrame
|
WebFrame
|
||||||
|
__tests__
|
||||||
@@ -570,7 +570,7 @@ export class ApplicationState extends ApplicationService {
|
|||||||
).includes(state)
|
).includes(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getScreenshotPrivacyEnabled(): Promise<boolean | undefined> {
|
private async getScreenshotPrivacyEnabled(): Promise<boolean> {
|
||||||
return this.application.getMobileScreenshotPrivacyEnabled()
|
return this.application.getMobileScreenshotPrivacyEnabled()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,11 @@ export class MobileDevice implements MobileDeviceInterface {
|
|||||||
;(this.stateObserverService as unknown) = undefined
|
;(this.stateObserverService as unknown) = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
consoleLog(...args: any[]): void {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(args)
|
||||||
|
}
|
||||||
|
|
||||||
async setLegacyRawKeychainValue(value: LegacyRawKeychainValue): Promise<void> {
|
async setLegacyRawKeychainValue(value: LegacyRawKeychainValue): Promise<void> {
|
||||||
await Keychain.setKeys(value)
|
await Keychain.setKeys(value)
|
||||||
}
|
}
|
||||||
@@ -378,7 +383,7 @@ export class MobileDevice implements MobileDeviceInterface {
|
|||||||
await Keychain.clearKeys()
|
await Keychain.clearKeys()
|
||||||
}
|
}
|
||||||
|
|
||||||
async setAndroidScreenshotPrivacy(enable: boolean): Promise<void> {
|
setAndroidScreenshotPrivacy(enable: boolean): void {
|
||||||
if (Platform.OS === 'android') {
|
if (Platform.OS === 'android') {
|
||||||
enable ? FlagSecure.activate() : FlagSecure.deactivate()
|
enable ? FlagSecure.activate() : FlagSecure.deactivate()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,14 +38,14 @@ export const SecuritySection = (props: Props) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let mounted = true
|
let mounted = true
|
||||||
const getHasScreenshotPrivacy = async () => {
|
const getHasScreenshotPrivacy = async () => {
|
||||||
const hasScreenshotPrivacyEnabled = (await application?.getMobileScreenshotPrivacyEnabled()) ?? true
|
const hasScreenshotPrivacyEnabled = application?.getMobileScreenshotPrivacyEnabled()
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setHasScreenshotPrivacy(hasScreenshotPrivacyEnabled)
|
setHasScreenshotPrivacy(hasScreenshotPrivacyEnabled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void getHasScreenshotPrivacy()
|
void getHasScreenshotPrivacy()
|
||||||
const getHasBiometrics = async () => {
|
const getHasBiometrics = async () => {
|
||||||
const appHasBiometrics = await application!.hasBiometrics()
|
const appHasBiometrics = application!.hasBiometrics()
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setHasBiometrics(appHasBiometrics)
|
setHasBiometrics(appHasBiometrics)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
||||||
|
coverage
|
||||||
@@ -6,8 +6,9 @@ export interface MobileDeviceInterface extends DeviceInterface {
|
|||||||
|
|
||||||
getRawKeychainValue(): Promise<RawKeychainValue | undefined>
|
getRawKeychainValue(): Promise<RawKeychainValue | undefined>
|
||||||
getDeviceBiometricsAvailability(): Promise<boolean>
|
getDeviceBiometricsAvailability(): Promise<boolean>
|
||||||
setAndroidScreenshotPrivacy(enable: boolean): Promise<void>
|
setAndroidScreenshotPrivacy(enable: boolean): void
|
||||||
authenticateWithBiometrics(): Promise<boolean>
|
authenticateWithBiometrics(): Promise<boolean>
|
||||||
hideMobileInterfaceFromScreenshots(): void
|
hideMobileInterfaceFromScreenshots(): void
|
||||||
stopHidingMobileInterfaceFromScreenshots(): void
|
stopHidingMobileInterfaceFromScreenshots(): void
|
||||||
|
consoleLog(...args: any[]): void
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,4 +2,8 @@ node_modules
|
|||||||
dist
|
dist
|
||||||
test
|
test
|
||||||
*.config.js
|
*.config.js
|
||||||
mocha/**/*
|
mocha/**/*
|
||||||
|
coverage
|
||||||
|
e2e-server.js
|
||||||
|
jest-global.ts
|
||||||
|
webpack.*.js
|
||||||
@@ -324,6 +324,7 @@ export class SNApplication
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.handleStage(ExternalServices.ApplicationStage.StorageDecrypted_09)
|
await this.handleStage(ExternalServices.ApplicationStage.StorageDecrypted_09)
|
||||||
|
|
||||||
this.apiService.loadHost()
|
this.apiService.loadHost()
|
||||||
@@ -929,30 +930,30 @@ export class SNApplication
|
|||||||
return this.deinit(this.getDeinitMode(), DeinitSource.Lock)
|
return this.deinit(this.getDeinitMode(), DeinitSource.Lock)
|
||||||
}
|
}
|
||||||
|
|
||||||
async setBiometricsTiming(timing: MobileUnlockTiming) {
|
setBiometricsTiming(timing: MobileUnlockTiming) {
|
||||||
return this.protectionService.setBiometricsTiming(timing)
|
return this.protectionService.setBiometricsTiming(timing)
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMobileScreenshotPrivacyEnabled(): Promise<boolean | undefined> {
|
getMobileScreenshotPrivacyEnabled(): boolean {
|
||||||
return this.protectionService.getMobileScreenshotPrivacyEnabled()
|
return this.protectionService.getMobileScreenshotPrivacyEnabled()
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMobilePasscodeTiming(): Promise<MobileUnlockTiming | undefined> {
|
setMobileScreenshotPrivacyEnabled(isEnabled: boolean) {
|
||||||
return this.getValue(StorageKey.MobilePasscodeTiming, StorageValueModes.Nonwrapped) as Promise<
|
|
||||||
MobileUnlockTiming | undefined
|
|
||||||
>
|
|
||||||
}
|
|
||||||
|
|
||||||
async getMobileBiometricsTiming(): Promise<MobileUnlockTiming | undefined> {
|
|
||||||
return this.getValue(StorageKey.MobileBiometricsTiming, StorageValueModes.Nonwrapped) as Promise<
|
|
||||||
MobileUnlockTiming | undefined
|
|
||||||
>
|
|
||||||
}
|
|
||||||
|
|
||||||
async setMobileScreenshotPrivacyEnabled(isEnabled: boolean) {
|
|
||||||
return this.protectionService.setMobileScreenshotPrivacyEnabled(isEnabled)
|
return this.protectionService.setMobileScreenshotPrivacyEnabled(isEnabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getMobilePasscodeTiming(): MobileUnlockTiming | undefined {
|
||||||
|
return this.getValue(StorageKey.MobilePasscodeTiming, StorageValueModes.Nonwrapped) as
|
||||||
|
| MobileUnlockTiming
|
||||||
|
| undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
getMobileBiometricsTiming(): MobileUnlockTiming | undefined {
|
||||||
|
return this.getValue(StorageKey.MobileBiometricsTiming, StorageValueModes.Nonwrapped) as
|
||||||
|
| MobileUnlockTiming
|
||||||
|
| undefined
|
||||||
|
}
|
||||||
|
|
||||||
async loadMobileUnlockTiming() {
|
async loadMobileUnlockTiming() {
|
||||||
return this.protectionService.loadMobileUnlockTiming()
|
return this.protectionService.loadMobileUnlockTiming()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -257,36 +257,36 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getBiometricsTiming(): Promise<MobileUnlockTiming | undefined> {
|
private getBiometricsTiming(): MobileUnlockTiming | undefined {
|
||||||
return this.storageService.getValue<Promise<MobileUnlockTiming | undefined>>(
|
return this.storageService.getValue<MobileUnlockTiming | undefined>(
|
||||||
StorageKey.MobileBiometricsTiming,
|
StorageKey.MobileBiometricsTiming,
|
||||||
StorageValueModes.Nonwrapped,
|
StorageValueModes.Nonwrapped,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getPasscodeTiming(): Promise<MobileUnlockTiming | undefined> {
|
private getPasscodeTiming(): MobileUnlockTiming | undefined {
|
||||||
return this.storageService.getValue<Promise<MobileUnlockTiming | undefined>>(
|
return this.storageService.getValue<MobileUnlockTiming | undefined>(
|
||||||
StorageKey.MobilePasscodeTiming,
|
StorageKey.MobilePasscodeTiming,
|
||||||
StorageValueModes.Nonwrapped,
|
StorageValueModes.Nonwrapped,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async setBiometricsTiming(timing: MobileUnlockTiming) {
|
async setBiometricsTiming(timing: MobileUnlockTiming) {
|
||||||
await this.storageService.setValue(StorageKey.MobileBiometricsTiming, timing, StorageValueModes.Nonwrapped)
|
this.storageService.setValue(StorageKey.MobileBiometricsTiming, timing, StorageValueModes.Nonwrapped)
|
||||||
this.mobileBiometricsTiming = timing
|
this.mobileBiometricsTiming = timing
|
||||||
}
|
}
|
||||||
|
|
||||||
async setMobileScreenshotPrivacyEnabled(isEnabled: boolean) {
|
setMobileScreenshotPrivacyEnabled(isEnabled: boolean) {
|
||||||
return this.storageService.setValue(StorageKey.MobileScreenshotPrivacyEnabled, isEnabled, StorageValueModes.Default)
|
return this.storageService.setValue(StorageKey.MobileScreenshotPrivacyEnabled, isEnabled, StorageValueModes.Default)
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMobileScreenshotPrivacyEnabled(): Promise<boolean | undefined> {
|
getMobileScreenshotPrivacyEnabled(): boolean {
|
||||||
return this.storageService.getValue(StorageKey.MobileScreenshotPrivacyEnabled, StorageValueModes.Default)
|
return this.storageService.getValue(StorageKey.MobileScreenshotPrivacyEnabled, StorageValueModes.Default, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadMobileUnlockTiming() {
|
loadMobileUnlockTiming(): void {
|
||||||
this.mobilePasscodeTiming = await this.getPasscodeTiming()
|
this.mobilePasscodeTiming = this.getPasscodeTiming()
|
||||||
this.mobileBiometricsTiming = await this.getBiometricsTiming()
|
this.mobileBiometricsTiming = this.getBiometricsTiming()
|
||||||
}
|
}
|
||||||
|
|
||||||
private async validateOrRenewSession(
|
private async validateOrRenewSession(
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { DiskStorageService } from './DiskStorageService'
|
||||||
|
|
||||||
|
import { InternalEventBus, DeviceInterface, InternalEventBusInterface } from '@standardnotes/services'
|
||||||
|
import { Environment } from '@standardnotes/models'
|
||||||
|
|
||||||
|
describe('diskStorageService', () => {
|
||||||
|
let storageService: DiskStorageService
|
||||||
|
let internalEventBus: InternalEventBusInterface
|
||||||
|
let device: DeviceInterface
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
internalEventBus = {} as jest.Mocked<InternalEventBus>
|
||||||
|
device = {} as jest.Mocked<DeviceInterface>
|
||||||
|
|
||||||
|
storageService = new DiskStorageService(device, 'test', Environment.Desktop, internalEventBus)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('setInitialValues should set unwrapped values as wrapped value if wrapped value is not encrypted', async () => {
|
||||||
|
storageService.isStorageWrapped = jest.fn().mockReturnValue(false)
|
||||||
|
|
||||||
|
await storageService['setInitialValues']({
|
||||||
|
wrapped: { content: { foo: 'bar' } } as never,
|
||||||
|
nonwrapped: {},
|
||||||
|
unwrapped: { bar: 'zoo' },
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(storageService['values']).toEqual({
|
||||||
|
wrapped: { content: { foo: 'bar' } } as never,
|
||||||
|
nonwrapped: {},
|
||||||
|
unwrapped: { bar: 'zoo', foo: 'bar' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -112,14 +112,10 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
|||||||
const value = await this.deviceInterface.getRawStorageValue(this.getPersistenceKey())
|
const value = await this.deviceInterface.getRawStorageValue(this.getPersistenceKey())
|
||||||
const values = value ? JSON.parse(value as string) : undefined
|
const values = value ? JSON.parse(value as string) : undefined
|
||||||
|
|
||||||
this.setInitialValues(values)
|
await this.setInitialValues(values)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private async setInitialValues(values?: Services.StorageValuesObject) {
|
||||||
* Called by platforms with the value they load from disk,
|
|
||||||
* after they handle initializeFromDisk
|
|
||||||
*/
|
|
||||||
private setInitialValues(values?: Services.StorageValuesObject) {
|
|
||||||
const sureValues = values || this.defaultValuesObject()
|
const sureValues = values || this.defaultValuesObject()
|
||||||
|
|
||||||
if (!sureValues[Services.ValueModesKeys.Unwrapped]) {
|
if (!sureValues[Services.ValueModesKeys.Unwrapped]) {
|
||||||
@@ -127,6 +123,13 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.values = sureValues
|
this.values = sureValues
|
||||||
|
|
||||||
|
if (!this.isStorageWrapped()) {
|
||||||
|
this.values[Services.ValueModesKeys.Unwrapped] = {
|
||||||
|
...(this.values[Services.ValueModesKeys.Wrapped].content as object),
|
||||||
|
...this.values[Services.ValueModesKeys.Unwrapped],
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public isStorageWrapped(): boolean {
|
public isStorageWrapped(): boolean {
|
||||||
@@ -370,7 +373,7 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
|||||||
* Clears simple values from storage only. Does not affect payloads.
|
* Clears simple values from storage only. Does not affect payloads.
|
||||||
*/
|
*/
|
||||||
async clearValues() {
|
async clearValues() {
|
||||||
this.setInitialValues()
|
await this.setInitialValues()
|
||||||
await this.immediatelyPersistValuesToDisk()
|
await this.immediatelyPersistValuesToDisk()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
extends: ['../.eslintrc.js'],
|
extends: ['../.eslintrc'],
|
||||||
globals: {
|
globals: {
|
||||||
chai: true,
|
chai: true,
|
||||||
chaiAsPromised: true,
|
chaiAsPromised: true,
|
||||||
|
|||||||
4
packages/web/.eslintignore
Normal file
4
packages/web/.eslintignore
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
dist
|
||||||
|
node_modules
|
||||||
|
web.webpack-defaults.js
|
||||||
|
coverage
|
||||||
@@ -19,7 +19,9 @@
|
|||||||
"__mocks__",
|
"__mocks__",
|
||||||
"src/components",
|
"src/components",
|
||||||
"src/favicon",
|
"src/favicon",
|
||||||
"src/vendor"
|
"src/vendor",
|
||||||
|
"coverage",
|
||||||
|
"*.config.js"
|
||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"standard/no-callback-literal": 0, // Disable this as we have too many callbacks relying on literals
|
"standard/no-callback-literal": 0, // Disable this as we have too many callbacks relying on literals
|
||||||
|
|||||||
@@ -76,6 +76,11 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
|||||||
|
|
||||||
if (this.isNativeMobileWeb()) {
|
if (this.isNativeMobileWeb()) {
|
||||||
this.mobileWebReceiver = new MobileWebReceiver(this)
|
this.mobileWebReceiver = new MobileWebReceiver(this)
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log = (...args) => {
|
||||||
|
this.mobileDevice.consoleLog(...args)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.onVisibilityChange = () => {
|
this.onVisibilityChange = () => {
|
||||||
@@ -228,7 +233,7 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
|||||||
async handleMobileGainingFocusEvent(): Promise<void> {}
|
async handleMobileGainingFocusEvent(): Promise<void> {}
|
||||||
|
|
||||||
async handleMobileLosingFocusEvent(): Promise<void> {
|
async handleMobileLosingFocusEvent(): Promise<void> {
|
||||||
if (await this.getMobileScreenshotPrivacyEnabled()) {
|
if (this.getMobileScreenshotPrivacyEnabled()) {
|
||||||
this.mobileDevice.stopHidingMobileInterfaceFromScreenshots()
|
this.mobileDevice.stopHidingMobileInterfaceFromScreenshots()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,7 +241,7 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
|||||||
}
|
}
|
||||||
|
|
||||||
async handleMobileResumingFromBackgroundEvent(): Promise<void> {
|
async handleMobileResumingFromBackgroundEvent(): Promise<void> {
|
||||||
if (await this.getMobileScreenshotPrivacyEnabled()) {
|
if (this.getMobileScreenshotPrivacyEnabled()) {
|
||||||
this.mobileDevice.hideMobileInterfaceFromScreenshots()
|
this.mobileDevice.hideMobileInterfaceFromScreenshots()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const BiometricsLock = ({ application }: Props) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const getHasBiometrics = async () => {
|
const getHasBiometrics = async () => {
|
||||||
const appHasBiometrics = await application.hasBiometrics()
|
const appHasBiometrics = application.hasBiometrics()
|
||||||
setHasBiometrics(appHasBiometrics)
|
setHasBiometrics(appHasBiometrics)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ const BiometricsLock = ({ application }: Props) => {
|
|||||||
await disableBiometrics()
|
await disableBiometrics()
|
||||||
} else {
|
} else {
|
||||||
setHasBiometrics(true)
|
setHasBiometrics(true)
|
||||||
await application.enableBiometrics()
|
application.enableBiometrics()
|
||||||
await setBiometricsTimingValue(MobileUnlockTiming.OnQuit)
|
await setBiometricsTimingValue(MobileUnlockTiming.OnQuit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,22 +13,19 @@ type Props = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const MultitaskingPrivacy = ({ application }: Props) => {
|
const MultitaskingPrivacy = ({ application }: Props) => {
|
||||||
const [hasScreenshotPrivacy, setHasScreenshotPrivacy] = useState<boolean | undefined>(false)
|
const [hasScreenshotPrivacy, setHasScreenshotPrivacy] = useState<boolean>(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const getHasScreenshotPrivacy = async () => {
|
const hasScreenshotPrivacyEnabled = application.getMobileScreenshotPrivacyEnabled()
|
||||||
const hasScreenshotPrivacyEnabled = (await application.getMobileScreenshotPrivacyEnabled()) ?? true
|
setHasScreenshotPrivacy(hasScreenshotPrivacyEnabled)
|
||||||
setHasScreenshotPrivacy(hasScreenshotPrivacyEnabled)
|
|
||||||
}
|
|
||||||
void getHasScreenshotPrivacy()
|
|
||||||
}, [application])
|
}, [application])
|
||||||
|
|
||||||
const onScreenshotPrivacyPress = async () => {
|
const onScreenshotPrivacyPress = async () => {
|
||||||
const enable = !hasScreenshotPrivacy
|
const enable = !hasScreenshotPrivacy
|
||||||
setHasScreenshotPrivacy(enable)
|
setHasScreenshotPrivacy(enable)
|
||||||
|
|
||||||
await application.setMobileScreenshotPrivacyEnabled(enable)
|
application.setMobileScreenshotPrivacyEnabled(enable)
|
||||||
await (application.deviceInterface as MobileDeviceInterface).setAndroidScreenshotPrivacy(enable)
|
;(application.deviceInterface as MobileDeviceInterface).setAndroidScreenshotPrivacy(enable)
|
||||||
}
|
}
|
||||||
|
|
||||||
const screenshotPrivacyFeatureText = isIOS() ? 'Multitasking Privacy' : 'Multitasking/Screenshot Privacy'
|
const screenshotPrivacyFeatureText = isIOS() ? 'Multitasking Privacy' : 'Multitasking/Screenshot Privacy'
|
||||||
|
|||||||
Reference in New Issue
Block a user