Skip to content

Commit

Permalink
Merge pull request #212 from christianliebel/fullscreen-mode
Browse files Browse the repository at this point in the history
Add support for fullscreen display mode
  • Loading branch information
andreban authored Jun 30, 2020
2 parents 1194d06 + f669228 commit dfbc017
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 5 deletions.
10 changes: 9 additions & 1 deletion packages/cli/src/lib/cmds/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
import * as fs from 'fs';
import Color = require('color');
import * as inquirer from 'inquirer';
import {Config, JdkHelper, KeyTool, Log, TwaGenerator, TwaManifest, util} from '@bubblewrap/core';
import {Config, DisplayModes, JdkHelper, KeyTool, Log, TwaGenerator, TwaManifest,
util} from '@bubblewrap/core';
import {validateColor, validateKeyPassword, validateUrl, notEmpty} from '../inputHelpers';
import {ParsedArgs} from 'minimist';
import {APP_NAME} from '../constants';
Expand Down Expand Up @@ -49,6 +50,12 @@ async function confirmTwaConfig(twaManifest: TwaManifest): Promise<TwaManifest>
message: 'Name to be shown on the Android Launcher:',
default: twaManifest.launcherName,
validate: async (input): Promise<boolean> => notEmpty(input, 'Launcher name'),
}, {
name: 'display',
type: 'list',
message: 'Display mode to be used:',
default: twaManifest.display,
choices: DisplayModes,
}, {
name: 'themeColor',
type: 'input',
Expand Down Expand Up @@ -131,6 +138,7 @@ async function confirmTwaConfig(twaManifest: TwaManifest): Promise<TwaManifest>
twaManifest.host = result.host;
twaManifest.name = result.name;
twaManifest.launcherName = result.launcherName;
twaManifest.display = result.display;
twaManifest.themeColor = new Color(result.themeColor);
twaManifest.backgroundColor = new Color(result.backgroundColor);
twaManifest.startUrl = result.startUrl;
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {GradleWrapper} from './lib/GradleWrapper';
import Log from './lib/Log';
import {JdkHelper} from './lib/jdk/JdkHelper';
import {KeyTool} from './lib/jdk/KeyTool';
import {TwaManifest} from './lib/TwaManifest';
import {TwaManifest, DisplayModes} from './lib/TwaManifest';
import {TwaGenerator} from './lib/TwaGenerator';
import {DigitalAssetLinks} from './lib/DigitalAssetLinks';
import * as util from './lib/util';
Expand All @@ -34,5 +34,6 @@ export {AndroidSdkTools,
Log,
TwaGenerator,
TwaManifest,
DisplayModes,
util,
};
19 changes: 18 additions & 1 deletion packages/core/src/lib/TwaManifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,19 @@ const MIN_SHORTCUT_ICON_SIZE = 96;
// The minimum size needed for the notification icon
const MIN_NOTIFICATION_ICON_SIZE = 48;

// Supported display modes for TWA
const DISPLAY_MODE_VALUES = ['standalone', 'fullscreen'];
type DisplayMode = typeof DISPLAY_MODE_VALUES[number];
export const DisplayModes: DisplayMode[] = [...DISPLAY_MODE_VALUES];

export function asDisplayMode(input: string): DisplayMode | null {
return DISPLAY_MODE_VALUES.includes(input) ? input as DisplayMode : null;
}

// Default values used on the Twa Manifest
const DEFAULT_SPLASHSCREEN_FADEOUT_DURATION = 300;
const DEFAULT_APP_NAME = 'My TWA';
const DEFAULT_DISPLAY_MODE = 'standalone';
const DEFAULT_THEME_COLOR = '#FFFFFF';
const DEFAULT_NAVIGATION_COLOR = '#000000';
const DEFAULT_BACKGROUND_COLOR = '#FFFFFF';
Expand Down Expand Up @@ -72,6 +82,7 @@ export class ShortcutInfo {
* hostName: '<%= host %>', // The domain being opened in the TWA.
* launchUrl: '<%= startUrl %>', // The start path for the TWA. Must be relative to the domain.
* name: '<%= name %>', // The name shown on the Android Launcher.
* display: '<%= display %>', // The display mode for the TWA.
* themeColor: '<%= themeColor %>', // The color used for the status bar.
* navigationColor: '<%= themeColor %>', // The color used for the navigation bar.
* backgroundColor: '<%= backgroundColor %>', // The color used for the splash screen background.
Expand All @@ -94,6 +105,7 @@ export class TwaManifest {
host: string;
name: string;
launcherName: string;
display: DisplayMode;
themeColor: Color;
navigationColor: Color;
backgroundColor: Color;
Expand All @@ -117,7 +129,10 @@ export class TwaManifest {
this.packageId = data.packageId;
this.host = data.host;
this.name = data.name;
this.launcherName = data.launcherName || data.name; // Older Manifests may not have this field.
// Older manifests may not have this field:
this.launcherName = data.launcherName || data.name;
// Older manifests may not have this field:
this.display = asDisplayMode(data.display!) || DEFAULT_DISPLAY_MODE;
this.themeColor = new Color(data.themeColor);
this.navigationColor = new Color(data.navigationColor);
this.backgroundColor = new Color(data.backgroundColor);
Expand Down Expand Up @@ -254,6 +269,7 @@ export class TwaManifest {
name: webManifest['name'] || webManifest['short_name'] || DEFAULT_APP_NAME,
launcherName: webManifest['short_name'] ||
webManifest['name']?.substring(0, SHORT_NAME_MAX_SIZE) || DEFAULT_APP_NAME,
display: asDisplayMode(webManifest['display']!) || DEFAULT_DISPLAY_MODE,
themeColor: webManifest['theme_color'] || DEFAULT_THEME_COLOR,
navigationColor: DEFAULT_NAVIGATION_COLOR,
backgroundColor: webManifest['background_color'] || DEFAULT_BACKGROUND_COLOR,
Expand Down Expand Up @@ -306,6 +322,7 @@ export interface TwaManifestJson {
host: string;
name: string;
launcherName?: string; // Older Manifests may not have this field.
display?: string; // Older Manifests may not have this field.
themeColor: string;
navigationColor: string;
backgroundColor: string;
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/lib/types/WebManifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@ export interface WebManifestShortcutJson {
icons?: Array<WebManifestIcon>;
}

type WebManifestDisplayMode = 'browser' | 'minimal-ui' | 'standalone' | 'fullscreen';

export interface WebManifestJson {
name?: string;
short_name?: string;
start_url?: string;
display?: WebManifestDisplayMode;
theme_color?: string;
background_color?: string;
icons?: Array<WebManifestIcon>;
Expand Down
33 changes: 31 additions & 2 deletions packages/core/src/spec/lib/TwaManifestSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
* limitations under the License.
*/

import {TwaManifest, TwaManifestJson} from '../../lib/TwaManifest';
import {TwaManifest, TwaManifestJson, asDisplayMode} from '../../lib/TwaManifest';
import {WebManifestJson} from '../../lib/types/WebManifest';
import Color = require('color');

describe('TwaManifest', () => {
Expand All @@ -24,6 +25,7 @@ describe('TwaManifest', () => {
'name': 'PWA Directory',
'short_name': 'PwaDirectory',
'start_url': '/?utm_source=homescreen',
'display': 'fullscreen',
'icons': [{
'src': '/favicons/android-chrome-192x192.png',
'sizes': '192x192',
Expand All @@ -46,10 +48,11 @@ describe('TwaManifest', () => {
}],
};
const manifestUrl = new URL('https://pwa-directory.com/manifest.json');
const twaManifest = TwaManifest.fromWebManifestJson(manifestUrl, manifest);
const twaManifest = TwaManifest.fromWebManifestJson(manifestUrl, manifest as WebManifestJson);
expect(twaManifest.packageId).toBe('com.pwa_directory.twa');
expect(twaManifest.name).toBe('PWA Directory');
expect(twaManifest.launcherName).toBe('PwaDirectory');
expect(twaManifest.display).toBe('fullscreen');
expect(twaManifest.startUrl).toBe('/?utm_source=homescreen');
expect(twaManifest.iconUrl)
.toBe('https://pwa-directory.com/favicons/android-chrome-512x512.png');
Expand Down Expand Up @@ -89,6 +92,7 @@ describe('TwaManifest', () => {
expect(twaManifest.iconUrl).toBeUndefined();
expect(twaManifest.maskableIconUrl).toBeUndefined();
expect(twaManifest.monochromeIconUrl).toBeUndefined();
expect(twaManifest.display).toBe('standalone');
expect(twaManifest.themeColor.hex()).toBe('#FFFFFF');
expect(twaManifest.navigationColor.hex()).toBe('#000000');
expect(twaManifest.backgroundColor.hex()).toBe('#FFFFFF');
Expand Down Expand Up @@ -165,6 +169,14 @@ describe('TwaManifest', () => {
expect(twaManifest.maskableIconUrl).toBe('https://pwa-directory.com/favicons/maskable.png');
expect(twaManifest.monochromeIconUrl).toBe('https://pwa-directory.com/favicons/monochrome.png');
});

it('Replaces unsupported display modes with `standalone`', () => {
const manifestUrl = new URL('https://pwa-directory.com/manifest.json');
expect(TwaManifest.fromWebManifestJson(manifestUrl, {display: 'minimal-ui'}).display)
.toBe('standalone');
expect(TwaManifest.fromWebManifestJson(manifestUrl, {display: 'browser'}).display)
.toBe('standalone');
});
});

describe('#constructor', () => {
Expand All @@ -176,6 +188,7 @@ describe('TwaManifest', () => {
launcherName: 'PwaDirectory',
startUrl: '/',
iconUrl: 'https://pwa-directory.com/favicons/android-chrome-512x512.png',
display: 'fullscreen',
themeColor: '#00ff00',
navigationColor: '#000000',
backgroundColor: '#0000ff',
Expand All @@ -199,6 +212,7 @@ describe('TwaManifest', () => {
expect(twaManifest.launcherName).toEqual(twaManifest.launcherName);
expect(twaManifest.startUrl).toEqual(twaManifest.startUrl);
expect(twaManifest.iconUrl).toEqual(twaManifest.iconUrl);
expect(twaManifest.display).toEqual('fullscreen');
expect(twaManifest.themeColor).toEqual(new Color('#00ff00'));
expect(twaManifest.navigationColor).toEqual(new Color('#000000'));
expect(twaManifest.backgroundColor).toEqual(new Color('#0000ff'));
Expand Down Expand Up @@ -240,6 +254,7 @@ describe('TwaManifest', () => {
const twaManifest = new TwaManifest(twaManifestJson);
expect(twaManifest.webManifestUrl).toBeUndefined();
expect(twaManifest.fallbackType).toBe('customtabs');
expect(twaManifest.display).toBe('standalone');
});
});

Expand Down Expand Up @@ -272,4 +287,18 @@ describe('TwaManifest', () => {
expect(twaManifest.validate()).toBeNull();
});
});

describe('#asDisplayMode', () => {
it('Returns display mode if it is supported', () => {
expect(asDisplayMode('standalone')).toBe('standalone');
expect(asDisplayMode('fullscreen')).toBe('fullscreen');
});

it('Returns null for unsupported display modes', () => {
expect(asDisplayMode('browser')).toBeNull();
expect(asDisplayMode('minimal-ui')).toBeNull();
expect(asDisplayMode('bogus')).toBeNull();
expect(asDisplayMode('')).toBeNull();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@
<meta-data android:name="android.support.customtabs.trusted.FALLBACK_STRATEGY"
android:value="@string/fallbackType" />

<% if (display === 'fullscreen') { %>
<meta-data android:name="android.support.customtabs.trusted.DISPLAY_MODE"
android:value="immersive" />
<% }%>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
Expand Down

0 comments on commit dfbc017

Please sign in to comment.