Cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Your update to Fitbit app Stainless Steel cannot be published at thistime

I received yesterday this feedback from a reviewer:

 

Due to the fact that your Clock Face has functionality issues, we regretfully inform you that we have declined your submission.

Issue #1 Settings menu does not appear on Samsung S8 paired with Versa. Please consult the following link for more information: https://dev.fitbit.com/legal/app-gallery-guidelines/

 

But the settings menu appears on other smartphones (Huawei Mate 20, UMIDGI G) paired with Versa and I think this issue is not caused by my Clock Face. In the last release I didn't touch the settings.

 

The Clock Face was downloaded over 400 times since launch 09/06/2019 and no one has reported this Issue.

 

The link above doesn't contain any information about "how to fix the settings menu on Samsung S8"

 

Clock Face: Stainless Steel
UUID: ad21f2fe-8acb-4bbb-9109-29230409d763

 

Best regards
Fouad Messaaia

 

Screenshot_20190913_083817_com.fitbit.FitbitMobile[1073].jpg

Best Answer
0 Votes
4 REPLIES 4

This is a long-shot: occasionally, the settings page gets executed before the companion index.js. If your .jsx depends on any settings that would normally be initialised in index.js, Bad Things can happen. In particular, if you try to JSON.parse an undefined setting in the .jsx, the settings page will crash and just show as white.

You can test for this vulnerability by removing everything from your index.js.

Peter McLennan
Gondwana Software
Best Answer
0 Votes

But that doesn't explain why this behavior only occurs on Samsung S8 (and probably only on the smartphone of the reviewer).

 

The source code of the setting page is pretty straightforward. Line 17 (settingsStorage.getItem('modelName') == 'Versa Lite') removes the Floors because they are not supported on Versa Lite. The model name gets stored in settingsStorage by the companion: settingsStorage.setItem('modelName', device.modelName);

 

import { gettext } from "i18n";

function mySettings(props) {
	const { settings, settingsStorage } = props;

	let ringOptions = [
		{ name: gettext("Hide"), value: "none" },
		{ name: gettext("Battery status"), value: "battery" },
		//...
	];

	let colorOptions = [
		{ color: "black" },
		//...
	];

	if (settingsStorage.getItem('modelName') == 'Versa Lite') {
		ringOptions.splice(4, 1);
	}

	// #region K-Pay code
	let kpayStatusMessage = settings.kpayStatus || "Unlicensed product. Trial period in progress.";
	let endTrialVisible = (settings.btnEndTrialVisible === undefined ? false : JSON.parse(settings.btnEndTrialVisible));
	if (kpayStatusMessage == 'trial') {
		kpayStatusMessage = getTrialEndsInMessage(props);
	}
	// #endregion K-Pay code

	return (
		<Page>
			<Section title={gettext('Product Status')}>
				<Text align="center">{`${kpayStatusMessage}`}</Text>
				{endTrialVisible && <Toggle settingsKey="kpayPurchase" label="End Trial Now" />}
			</Section>

			<Section title={gettext('General')} description="">
				<Select label={gettext('Temperature unit')} selectViewTitle={gettext('Temperature unit')} settingsKey="temperatureUnit" options={[
					{ name: gettext("Celcius"), value: "celcius" },
					{ name: gettext("Fahrenheit"), value: "fahrenheit" }
				]} />

				<Toggle settingsKey="hideSteps" label={gettext('Hide steps')} />
				<Toggle settingsKey="hideHeartRate" label={gettext('Hide heart rate')} />
				<Toggle settingsKey="hideWeather" label={gettext('Hide weather')} />
				<Toggle settingsKey="hideDate" label={gettext('Hide date')} />
			</Section>

			<Section title={gettext('Corner rings')} description="">
				<Select label={gettext('Top left')} selectViewTitle={gettext('Top left')} settingsKey="ringTopLeft" options={ringOptions} />
				<Select label={gettext('Top right')} selectViewTitle={gettext('Top right')} settingsKey="ringTopRight" options={ringOptions} />
				<Select label={gettext('Bottom left')} selectViewTitle={gettext('Bottom left')} settingsKey="ringBottomLeft" options={ringOptions} />
				<Select label={gettext('Bottom right')} selectViewTitle={gettext('Bottom right')} settingsKey="ringBottomRight" options={ringOptions} />
			</Section>

			<Section title={gettext('Display')}>
				<Text>{gettext('Background color')}</Text>
				<ColorSelect settingsKey="backgroundColor" colors={[
					{ color: "black" },
					//...
				]} />

				<Text>{gettext('Primary color')}</Text>
				<ColorSelect settingsKey="primaryColor" colors={colorOptions} />

				<Text>{gettext('Secondary color')}</Text>
				<ColorSelect settingsKey="secondaryColor" colors={colorOptions} />

				<Text>{gettext('Text color')}</Text>
				<ColorSelect settingsKey="textColor" colors={colorOptions} />
			</Section>
		</Page>);
}

// #region K-Pay code
function getTrialEndsInMessage(props) {
	let trialEndDate = props.settings.kpayTrialEndDate;
	let trialDuration = trialEndDate ? trialEndDate - new Date().getTime() : 0;
	if (!trialEndDate) {
		// there has not been any contact with the server yet, 
		// so trail time left is unknown
		return "Unlicensed product. Trial period in progress.";
	}

	if (trialDuration > 0) {
		return `Unlicensed product. Trial ends in ${getFuzzyDuration(trialDuration)}`;
	}

	// Returned in the case where the user ended the trial early,
	// and while in this condition, the trial period also ended.
	return "Unlicensed product. Trial period ended.";
}

function getFuzzyDuration(durationInMilliseconds) {
	//get duration in minutes, rounded up
	let durationInMinutes = Math.ceil(durationInMilliseconds / 60000.0);
	let numberOfHours = Math.floor(durationInMinutes / 60);
	let numberOfMinutes = durationInMinutes - (numberOfHours * 60);

	let fuzzyDuration = "";
	if (numberOfHours > 0) {
		fuzzyDuration = numberOfHours + " hrs, ";
	}
	return fuzzyDuration + numberOfMinutes + " min.";
}
// #endregion K-Pay code

registerSettingsPage(mySettings);
Best Answer
0 Votes

I suspect the phone type is a red herring (although, because it may be a race condition, different devices may execute different things at different speeds).

You are assuming that your companion code runs before your settings code. It ain't necessarily so. You can't rely on modelName to have been initialised before your settings code runs, so sometimes your jsx will see 'undefined'.

That said, it shouldn't crash if you're only using it as you've indicated. It just means that Versa Lite won't be correctly identified sometimes—but I don't think you can do anything about that.

The problem probably lies elsewhere.

btnEndTrialVisible might be worth looking at. Where is it initialised? Should undefined be 'undefined'? A parse exception there would explain it.

Peter McLennan
Gondwana Software
Best Answer
0 Votes

I also submitted a clock watch face on the same date (Sept. 6, 2019) and was rejected for the exact same reason verbatim, but it was a first publish for me. I'm not sure the "Samsung 8" (S8? A8?) is a red herring, honestly. I've tested on a Google Pixel 3, iPhone XR and iPhone 8 -- all of which work without any issues with a Versa. Re-installed on those phones multiple times and cannot seem to reproduce the issue on those devices.

Best Answer
0 Votes