blob: 949a789f032ce970cf78cfff185b8fdf0106204a [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'chrome://resources/cr_components/settings_prefs/prefs.js';
import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
import 'chrome://resources/cr_elements/cr_link_row/cr_link_row.js';
import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
import 'chrome://resources/cr_elements/icons.html.js';
import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
import '../../settings_shared.css.js';
import './storage_external.js';
import {CrLinkRowElement} from 'chrome://resources/cr_elements/cr_link_row/cr_link_row.js';
import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {isCrostiniSupported} from '../common/load_time_booleans.js';
import {routes} from '../os_settings_routes.js';
import {RouteOriginMixin} from '../route_origin_mixin.js';
import {Route, Router} from '../router.js';
import {DevicePageBrowserProxy, DevicePageBrowserProxyImpl, StorageSpaceState} from './device_page_browser_proxy.js';
import {getTemplate} from './storage.html.js';
interface StorageSizeStat {
availableSize: string;
usedSize: string;
usedRatio: number;
spaceState: StorageSpaceState;
}
interface SettingsStorageElement {
$: {
availableLabelArea: HTMLElement,
browsingDataSize: CrLinkRowElement,
inUseLabelArea: HTMLElement,
myFilesSize: CrLinkRowElement,
};
}
const SettingsStorageElementBase =
RouteOriginMixin(WebUiListenerMixin(PolymerElement));
class SettingsStorageElement extends SettingsStorageElementBase {
static get is() {
return 'settings-storage';
}
static get template() {
return getTemplate();
}
static get properties() {
return {
androidEnabled: Boolean,
showCrostiniStorage_: {
type: Boolean,
value: false,
},
showDriveOfflineStorage_: {
type: Boolean,
value: loadTimeData.getBoolean('enableDriveFsBulkPinning'),
readonly: true,
},
isEphemeralUser_: {
type: Boolean,
value() {
return loadTimeData.getBoolean('isCryptohomeDataEphemeral');
},
},
showOtherUsers_: {
type: Boolean,
// Initialize showOtherUsers_ to false if the user is ephemeral.
value() {
return !loadTimeData.getBoolean('isCryptohomeDataEphemeral');
},
},
sizeStat_: Object,
};
}
static get observers() {
return ['handleCrostiniEnabledChanged_(prefs.crostini.enabled.value)'];
}
private browserProxy_: DevicePageBrowserProxy;
private isEphemeralUser_: boolean;
private route_: Route;
private showCrostiniStorage_: boolean;
private showDriveOfflineStorage_: boolean;
private showOtherUsers_: boolean;
private sizeStat_: StorageSizeStat;
private updateTimerId_: number;
constructor() {
super();
/** RouteOriginMixin override */
this.route_ = routes.STORAGE;
/**
* Timer ID for periodic update.
*/
this.updateTimerId_ = -1;
this.browserProxy_ = DevicePageBrowserProxyImpl.getInstance();
}
override connectedCallback() {
super.connectedCallback();
this.addWebUiListener(
'storage-size-stat-changed',
(sizeStat: StorageSizeStat) => this.handleSizeStatChanged_(sizeStat));
this.addWebUiListener(
'storage-my-files-size-changed',
(size: string) => this.handleMyFilesSizeChanged_(size));
this.addWebUiListener(
'storage-browsing-data-size-changed',
(size: string) => this.handleBrowsingDataSizeChanged_(size));
this.addWebUiListener(
'storage-apps-size-changed',
(size: string) => this.handleAppsSizeChanged_(size));
this.addWebUiListener(
'storage-drive-offline-size-changed',
(size: string) => this.handleDriveOfflineSizeChanged_(size));
this.addWebUiListener(
'storage-crostini-size-changed',
(size: string) => this.handleCrostiniSizeChanged_(size));
if (this.showOtherUsers_) {
this.addWebUiListener(
'storage-other-users-size-changed',
(size: string, noOtherUsers: boolean) =>
this.handleOtherUsersSizeChanged_(size, noOtherUsers));
this.addWebUiListener(
'storage-system-size-changed',
(size: string) => this.handleSystemSizeChanged_(size));
}
}
override ready() {
super.ready();
const r = routes;
this.addFocusConfig(r.CROSTINI_DETAILS, '#crostiniSize');
this.addFocusConfig(r.ACCOUNTS, '#otherUsersSize');
this.addFocusConfig(
r.EXTERNAL_STORAGE_PREFERENCES, '#externalStoragePreferences');
this.addFocusConfig(r.APP_MANAGEMENT, '#appsSize');
}
override currentRouteChanged(newRoute: Route, oldRoute?: Route) {
super.currentRouteChanged(newRoute, oldRoute);
if (newRoute !== this.route_) {
return;
}
this.onPageShown_();
}
private onPageShown_(): void {
// Updating storage information can be expensive (e.g. computing directory
// sizes recursively), so we delay this operation until the page is shown.
this.browserProxy_.updateStorageInfo();
// We update the storage usage periodically when the overlay is visible.
this.startPeriodicUpdate_();
}
/**
* Handler for tapping the "My files" item.
*/
private onMyFilesClick_(): void {
this.browserProxy_.openMyFiles();
}
/**
* Handler for tapping the "Browsing data" item.
*/
private onBrowsingDataClick_(): void {
this.browserProxy_.openBrowsingDataSettings();
}
/**
* Handler for tapping the "Apps and Extensions" item.
*/
private onAppsClick_(): void {
Router.getInstance().navigateTo(
routes.APP_MANAGEMENT,
/* dynamicParams= */ undefined, /* removeSearch= */ true);
}
/**
* Handler for tapping the "Offline files" item.
*/
private onDriveOfflineClick_(): void {
Router.getInstance().navigateTo(
routes.GOOGLE_DRIVE,
/* dynamicParams= */ undefined, /* removeSearch= */ true);
}
/**
* Handler for tapping the "Linux storage" item.
*/
private onCrostiniClick_(): void {
Router.getInstance().navigateTo(
routes.CROSTINI_DETAILS, /* dynamicParams= */ undefined,
/* removeSearch= */ true);
}
/**
* Handler for tapping the "Other users" item.
*/
private onOtherUsersClick_(): void {
Router.getInstance().navigateTo(
routes.ACCOUNTS,
/* dynamicParams= */ undefined, /* removeSearch= */ true);
}
/**
* Handler for tapping the "External storage preferences" item.
*/
private onExternalStoragePreferencesClick_(): void {
Router.getInstance().navigateTo(routes.EXTERNAL_STORAGE_PREFERENCES);
}
private handleSizeStatChanged_(sizeStat: StorageSizeStat): void {
this.sizeStat_ = sizeStat;
this.$.inUseLabelArea.style.width = (sizeStat.usedRatio * 100) + '%';
this.$.availableLabelArea.style.width =
((1 - sizeStat.usedRatio) * 100) + '%';
}
/**
* @param size Formatted string representing the size of My files.
*/
private handleMyFilesSizeChanged_(size: string): void {
this.$.myFilesSize.subLabel = size;
}
/**
* @param size Formatted string representing the size of Browsing data.
*/
private handleBrowsingDataSizeChanged_(size: string): void {
this.$.browsingDataSize.subLabel = size;
}
/**
* @param size Formatted string representing the size of Apps and
* extensions storage.
*/
private handleAppsSizeChanged_(size: string): void {
this.shadowRoot!.querySelector<CrLinkRowElement>('#appsSize')!.subLabel =
size;
}
/**
* @param size Formatted string representing the size of pinned files in
* Google Drive.
*/
private handleDriveOfflineSizeChanged_(size: string): void {
if (this.showDriveOfflineStorage_) {
this.shadowRoot!.querySelector<CrLinkRowElement>(
'#driveOfflineSize')!.subLabel = size;
}
}
/**
* @param size Formatted string representing the size of Crostini storage.
*/
private handleCrostiniSizeChanged_(size: string): void {
if (this.showCrostiniStorage_) {
this.shadowRoot!.querySelector<CrLinkRowElement>(
'#crostiniSize')!.subLabel = size;
}
}
/**
* @param size Formatted string representing the size of Other users.
* @param noOtherUsers True if there is no other registered users
* on the device.
*/
private handleOtherUsersSizeChanged_(size: string, noOtherUsers: boolean):
void {
if (this.isEphemeralUser_ || noOtherUsers) {
this.showOtherUsers_ = false;
return;
}
this.showOtherUsers_ = true;
this.shadowRoot!.querySelector<CrLinkRowElement>(
'#otherUsersSize')!.subLabel = size;
}
/**
* @param size Formatted string representing the System size.
*/
private handleSystemSizeChanged_(size: string): void {
this.shadowRoot!.getElementById('systemSizeSubLabel')!.innerText = size;
}
/**
* @param enabled True if Crostini is enabled.
*/
private handleCrostiniEnabledChanged_(enabled: boolean): void {
this.showCrostiniStorage_ = enabled && isCrostiniSupported();
}
/**
* Starts periodic update for storage usage.
*/
private startPeriodicUpdate_(): void {
// We update the storage usage every 5 seconds.
if (this.updateTimerId_ === -1) {
this.updateTimerId_ = window.setInterval(() => {
if (Router.getInstance().currentRoute !== routes.STORAGE) {
this.stopPeriodicUpdate_();
return;
}
this.browserProxy_.updateStorageInfo();
}, 5000);
}
}
/**
* Stops periodic update for storage usage.
*/
private stopPeriodicUpdate_(): void {
if (this.updateTimerId_ !== -1) {
window.clearInterval(this.updateTimerId_);
this.updateTimerId_ = -1;
}
}
/**
* Returns true if the remaining space is low, but not critically low.
* @param spaceState Status about the remaining space.
*/
private isSpaceLow_(spaceState: StorageSpaceState): boolean {
return spaceState === StorageSpaceState.LOW;
}
/**
* Returns true if the remaining space is critically low.
* @param spaceState Status about the remaining space.
*/
private isSpaceCriticallyLow_(spaceState: StorageSpaceState): boolean {
return spaceState === StorageSpaceState.CRITICALLY_LOW;
}
/**
* Computes class name of the bar based on the remaining space size.
* @param spaceState Status about the remaining space.
*/
private getBarClass_(spaceState: StorageSpaceState): string {
switch (spaceState) {
case StorageSpaceState.LOW:
return 'space-low';
case StorageSpaceState.CRITICALLY_LOW:
return 'space-critically-low';
default:
return '';
}
}
}
declare global {
interface HTMLElementTagNameMap {
'settings-storage': SettingsStorageElement;
}
}
customElements.define(SettingsStorageElement.is, SettingsStorageElement);