diff --git a/mobile/android/base/TabsAccessor.java b/mobile/android/base/TabsAccessor.java index 6cd6a9a93cab..f9629f067c4e 100644 --- a/mobile/android/base/TabsAccessor.java +++ b/mobile/android/base/TabsAccessor.java @@ -7,7 +7,6 @@ package org.mozilla.gecko; import org.mozilla.gecko.db.BrowserContract; import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.gecko.util.UiAsyncTask; - import org.json.JSONArray; import org.json.JSONException; @@ -34,7 +33,8 @@ public final class TabsAccessor { BrowserContract.Tabs.TITLE, BrowserContract.Tabs.URL, BrowserContract.Clients.GUID, - BrowserContract.Clients.NAME + BrowserContract.Clients.NAME, + BrowserContract.Clients.LAST_MODIFIED, }; // Projection column numbers @@ -42,7 +42,8 @@ public final class TabsAccessor { TITLE, URL, GUID, - NAME + NAME, + LAST_MODIFIED, }; private static final String CLIENTS_SELECTION = BrowserContract.Clients.GUID + " IS NOT NULL"; @@ -57,6 +58,11 @@ public final class TabsAccessor { public String url; public String guid; public String name; + /** + * This is the last time the remote client uploaded a tabs record; that + * is, it is not per tab, but per remote client. + */ + public long lastModified; } public interface OnQueryTabsCompleteListener { @@ -105,7 +111,8 @@ public final class TabsAccessor { tab.url = cursor.getString(TABS_COLUMN.URL.ordinal()); tab.guid = cursor.getString(TABS_COLUMN.GUID.ordinal()); tab.name = cursor.getString(TABS_COLUMN.NAME.ordinal()); - + tab.lastModified = cursor.getLong(TABS_COLUMN.LAST_MODIFIED.ordinal()); + tabs.add(tab); } } finally { diff --git a/mobile/android/base/TelemetryContract.java b/mobile/android/base/TelemetryContract.java index bd7d3ae7fe1e..a4488c8ce93a 100644 --- a/mobile/android/base/TelemetryContract.java +++ b/mobile/android/base/TelemetryContract.java @@ -49,6 +49,10 @@ public interface TelemetryContract { // Generic action, usually for tracking menu and toolbar actions. public static final String ACTION = "action.1"; + + // Launching (opening) an external application + // Note: Only used in JavaScript for now, but here for completeness. + public static final String LAUNCH = "launch.1"; } /** @@ -82,6 +86,10 @@ public interface TelemetryContract { // Action triggered from a suggestion provided to the user. public static final String SUGGESTION = "suggestion"; + + // Action triggered from a pageaction in the URLBar. + // Note: Only used in JavaScript for now, but here for completeness. + public static final String PAGEACTION = "pageaction"; } /** @@ -101,10 +109,10 @@ public interface TelemetryContract { public static final String READER = "reader.1"; // URL bar focused. - public static final String URLBAR_FOCUSED = "urlbar.1:"; + public static final String URLBAR_FOCUSED = "urlbar.1"; // Awesomescreen frecency search is active. - public static final String FRECENCY = "frecency.1:"; + public static final String FRECENCY = "frecency.1"; } /** diff --git a/mobile/android/base/locales/en-US/android_strings.dtd b/mobile/android/base/locales/en-US/android_strings.dtd index 90a170eb81be..8e4a9d2cdffb 100644 --- a/mobile/android/base/locales/en-US/android_strings.dtd +++ b/mobile/android/base/locales/en-US/android_strings.dtd @@ -434,3 +434,11 @@ just addresses the organization to follow, e.g. "This site is run by " --> They are never shown to users --> + + + diff --git a/mobile/android/base/resources/layout-xlarge-v11/remote_tabs_child.xml b/mobile/android/base/resources/layout-xlarge-v11/remote_tabs_child.xml index 2715cc2a10b3..ff0ec06934d8 100644 --- a/mobile/android/base/resources/layout-xlarge-v11/remote_tabs_child.xml +++ b/mobile/android/base/resources/layout-xlarge-v11/remote_tabs_child.xml @@ -4,24 +4,24 @@ - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> diff --git a/mobile/android/base/resources/layout-xlarge-v11/remote_tabs_group.xml b/mobile/android/base/resources/layout-xlarge-v11/remote_tabs_group.xml index 887bd903c2a7..54b08d0fc295 100644 --- a/mobile/android/base/resources/layout-xlarge-v11/remote_tabs_group.xml +++ b/mobile/android/base/resources/layout-xlarge-v11/remote_tabs_group.xml @@ -3,16 +3,28 @@ - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - + + + + + + + diff --git a/mobile/android/base/resources/layout/remote_tabs_child.xml b/mobile/android/base/resources/layout/remote_tabs_child.xml index 23fe57bc9247..32f79a8e53ab 100644 --- a/mobile/android/base/resources/layout/remote_tabs_child.xml +++ b/mobile/android/base/resources/layout/remote_tabs_child.xml @@ -4,25 +4,25 @@ - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + android:textSize="18sp" /> + android:maxLength="1024" + android:textSize="14sp" /> diff --git a/mobile/android/base/resources/layout/remote_tabs_group.xml b/mobile/android/base/resources/layout/remote_tabs_group.xml index 7f776651458f..3a4f134acd13 100644 --- a/mobile/android/base/resources/layout/remote_tabs_group.xml +++ b/mobile/android/base/resources/layout/remote_tabs_group.xml @@ -3,14 +3,27 @@ - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - + + + + + + + diff --git a/mobile/android/base/strings.xml.in b/mobile/android/base/strings.xml.in index d19a990d8ab9..d54e375cb778 100644 --- a/mobile/android/base/strings.xml.in +++ b/mobile/android/base/strings.xml.in @@ -404,4 +404,5 @@ &ellipsis; + &remote_tabs_last_synced; diff --git a/mobile/android/base/tabspanel/RemoteTabsList.java b/mobile/android/base/tabspanel/RemoteTabsList.java index 669e83af8487..6f95bdbac7c8 100644 --- a/mobile/android/base/tabspanel/RemoteTabsList.java +++ b/mobile/android/base/tabspanel/RemoteTabsList.java @@ -4,14 +4,6 @@ package org.mozilla.gecko.tabspanel; -import android.content.Context; -import android.text.TextUtils; -import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ExpandableListView; -import android.widget.SimpleExpandableListAdapter; - import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -22,6 +14,14 @@ import org.mozilla.gecko.TabsAccessor; import org.mozilla.gecko.Telemetry; import org.mozilla.gecko.TelemetryContract; +import android.content.Context; +import android.text.TextUtils; +import android.text.format.DateUtils; +import android.util.AttributeSet; +import android.view.View; +import android.widget.ExpandableListView; +import android.widget.SimpleExpandableListAdapter; + /** * The actual list of synced tabs. This serves as the only child view of {@link RemoteTabsContainer} * so it can be refreshed using a swipe-to-refresh gesture. @@ -30,9 +30,9 @@ class RemoteTabsList extends ExpandableListView implements ExpandableListView.OnGroupClickListener, ExpandableListView.OnChildClickListener, TabsAccessor.OnQueryTabsCompleteListener { - private static final String[] CLIENT_KEY = new String[] { "name" }; + private static final String[] CLIENT_KEY = new String[] { "name", "last_synced" }; private static final String[] TAB_KEY = new String[] { "title", "url" }; - private static final int[] CLIENT_RESOURCE = new int[] { R.id.client }; + private static final int[] CLIENT_RESOURCE = new int[] { R.id.client, R.id.last_synced }; private static final int[] TAB_RESOURCE = new int[] { R.id.tab, R.id.url }; private final Context context; @@ -92,10 +92,13 @@ class RemoteTabsList extends ExpandableListView HashMap client; HashMap tab; + final long now = System.currentTimeMillis(); + for (TabsAccessor.RemoteTab remoteTab : remoteTabs) { if (oldGuid == null || !TextUtils.equals(oldGuid, remoteTab.guid)) { client = new HashMap (); client.put("name", remoteTab.name); + client.put("last_synced", getLastSyncedString(now, remoteTab.lastModified)); clients.add(client); tabsForClient = new ArrayList >(); @@ -124,4 +127,16 @@ class RemoteTabsList extends ExpandableListView expandGroup(i); } } + + /** + * Return a relative "Last synced" time span for the given tab record. + * + * @param now local time. + * @param time to format string for. + * @return string describing time span + */ + protected String getLastSyncedString(long now, long time) { + CharSequence relativeTimeSpanString = DateUtils.getRelativeTimeSpanString(time, now, DateUtils.MINUTE_IN_MILLIS); + return getResources().getString(R.string.remote_tabs_last_synced, relativeTimeSpanString); + } } diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 9edd5d04500a..f2bc233d41d7 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -8101,6 +8101,10 @@ var ExternalApps = { icon: "drawable://icon_openinapp", clickCallback: () => { + // Create a relative timestamp for telemetry + let uptime = Date.now() - Services.startup.getStartupInfo().linkerInitialized; + UITelemetry.addEvent("launch.1", "pageaction", uptime, "helper"); + if (apps.length > 1) { // Use the HelperApps prompt here to filter out any Http handlers HelperApps.prompt(apps, { diff --git a/mobile/android/installer/Makefile.in b/mobile/android/installer/Makefile.in index 763680a9158b..96dceab183f2 100644 --- a/mobile/android/installer/Makefile.in +++ b/mobile/android/installer/Makefile.in @@ -46,15 +46,22 @@ DEFINES += -DENABLE_MARIONETTE=1 endif ifdef MOZ_PKG_MANIFEST_P -$(MOZ_PKG_MANIFEST): $(MOZ_PKG_MANIFEST_P) $(GLOBAL_DEPS) +# When MOZ_CHROME_MULTILOCALE is defined, we write multilocale.json like: +# {"locales": ["en-US", "de", "ar", ...]} + +$(MOZ_PKG_MANIFEST): $(MOZ_PKG_MANIFEST_P) $(GLOBAL_DEPS) FORCE $(call py_action,preprocessor,$(DEFINES) $(ACDEFINES) $< -o $@) ifdef MOZ_CHROME_MULTILOCALE printf '\n[multilocale]\n' >> $@ + printf '@BINPATH@/res/multilocale.json\n' >> $@ for LOCALE in en-US $(MOZ_CHROME_MULTILOCALE) ;\ do \ printf '$(BINPATH)/chrome/'"$$LOCALE"'$(JAREXT)\n' >> $@; \ printf '$(BINPATH)/chrome/'"$$LOCALE"'.manifest\n' >> $@; \ done + COMMA=, + echo '{"locales": [$(foreach l,$(MOZ_CHROME_MULTILOCALE),"$(l)"$(COMMA)) "en-US"]}' \ + > $(FINAL_TARGET)/res/multilocale.json endif GARBAGE += $(MOZ_PKG_MANIFEST)