Bug 953381 - Basic media casting control bar r=wesj
2
CLOBBER
|
@ -22,4 +22,4 @@
|
|||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Bug 917896 requires a clobber due to bug 961339.
|
||||
Bug 953381 requires a clobber due to bug 961339.
|
||||
|
|
|
@ -156,6 +156,7 @@ abstract public class BrowserApp extends GeckoApp
|
|||
};
|
||||
|
||||
private FindInPageBar mFindInPageBar;
|
||||
private MediaCastingBar mMediaCastingBar;
|
||||
|
||||
private boolean mAccessibilityEnabled = false;
|
||||
|
||||
|
@ -530,6 +531,7 @@ abstract public class BrowserApp extends GeckoApp
|
|||
}
|
||||
|
||||
mFindInPageBar = (FindInPageBar) findViewById(R.id.find_in_page);
|
||||
mMediaCastingBar = (MediaCastingBar) findViewById(R.id.media_casting);
|
||||
|
||||
registerEventListener("CharEncoding:Data");
|
||||
registerEventListener("CharEncoding:State");
|
||||
|
@ -830,6 +832,11 @@ abstract public class BrowserApp extends GeckoApp
|
|||
mFindInPageBar = null;
|
||||
}
|
||||
|
||||
if (mMediaCastingBar != null) {
|
||||
mMediaCastingBar.onDestroy();
|
||||
mMediaCastingBar = null;
|
||||
}
|
||||
|
||||
if (mSharedPreferencesHelper != null) {
|
||||
mSharedPreferencesHelper.uninit();
|
||||
mSharedPreferencesHelper = null;
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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/. */
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import org.mozilla.gecko.util.GeckoEventListener;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class MediaCastingBar extends RelativeLayout implements View.OnClickListener, GeckoEventListener {
|
||||
private static final String LOGTAG = "MediaCastingBar";
|
||||
|
||||
private TextView mCastingTo;
|
||||
private ImageButton mMediaPlay;
|
||||
private ImageButton mMediaPause;
|
||||
private ImageButton mMediaStop;
|
||||
|
||||
private boolean mInflated = false;
|
||||
|
||||
public MediaCastingBar(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
GeckoAppShell.getEventDispatcher().registerEventListener("Casting:Started", this);
|
||||
GeckoAppShell.getEventDispatcher().registerEventListener("Casting:Stopped", this);
|
||||
}
|
||||
|
||||
public void inflateContent() {
|
||||
LayoutInflater inflater = LayoutInflater.from(getContext());
|
||||
View content = inflater.inflate(R.layout.media_casting, this);
|
||||
|
||||
mMediaPlay = (ImageButton) content.findViewById(R.id.media_play);
|
||||
mMediaPlay.setOnClickListener(this);
|
||||
mMediaPause = (ImageButton) content.findViewById(R.id.media_pause);
|
||||
mMediaPause.setOnClickListener(this);
|
||||
mMediaStop = (ImageButton) content.findViewById(R.id.media_stop);
|
||||
mMediaStop.setOnClickListener(this);
|
||||
|
||||
mCastingTo = (TextView) content.findViewById(R.id.media_casting_to);
|
||||
|
||||
// Capture clicks on the rest of the view to prevent them from
|
||||
// leaking into other views positioned below.
|
||||
content.setOnClickListener(this);
|
||||
|
||||
mInflated = true;
|
||||
}
|
||||
|
||||
public void show() {
|
||||
if (!mInflated)
|
||||
inflateContent();
|
||||
|
||||
setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
setVisibility(GONE);
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
GeckoAppShell.getEventDispatcher().unregisterEventListener("Casting:Started", this);
|
||||
GeckoAppShell.getEventDispatcher().unregisterEventListener("Casting:Stopped", this);
|
||||
}
|
||||
|
||||
// View.OnClickListener implementation
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
final int viewId = v.getId();
|
||||
|
||||
if (viewId == R.id.media_play) {
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Casting:Play", ""));
|
||||
mMediaPlay.setVisibility(GONE);
|
||||
mMediaPause.setVisibility(VISIBLE);
|
||||
} else if (viewId == R.id.media_pause) {
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Casting:Pause", ""));
|
||||
mMediaPause.setVisibility(GONE);
|
||||
mMediaPlay.setVisibility(VISIBLE);
|
||||
} else if (viewId == R.id.media_stop) {
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Casting:Stop", ""));
|
||||
}
|
||||
}
|
||||
|
||||
// GeckoEventListener implementation
|
||||
@Override
|
||||
public void handleMessage(final String event, final JSONObject message) {
|
||||
final String device = message.optString("device");
|
||||
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (event.equals("Casting:Started")) {
|
||||
show();
|
||||
if (!TextUtils.isEmpty(device)) {
|
||||
mCastingTo.setText(device);
|
||||
} else {
|
||||
// Should not happen
|
||||
mCastingTo.setText("");
|
||||
Log.d(LOGTAG, "Device name is empty.");
|
||||
}
|
||||
mMediaPlay.setVisibility(GONE);
|
||||
mMediaPause.setVisibility(VISIBLE);
|
||||
} else if (event.equals("Casting:Stopped")) {
|
||||
hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -219,6 +219,13 @@ size. -->
|
|||
<!ENTITY find_next "Next">
|
||||
<!ENTITY find_close "Close">
|
||||
|
||||
<!-- Localization note (media_casting_to, media_play, media_pause, media_stop) : These strings are used
|
||||
as alternate text for accessibility. They are not visible in the UI. -->
|
||||
<!ENTITY media_casting_to "Casting to Device">
|
||||
<!ENTITY media_play "Play">
|
||||
<!ENTITY media_pause "Pause">
|
||||
<!ENTITY media_stop "Stop">
|
||||
|
||||
<!ENTITY contextmenu_open_new_tab "Open in New Tab">
|
||||
<!ENTITY contextmenu_open_private_tab "Open in Private Tab">
|
||||
<!ENTITY contextmenu_open_in_reader "Open in Reader">
|
||||
|
|
|
@ -248,6 +248,7 @@ gbjar.sources += [
|
|||
'LightweightTheme.java',
|
||||
'LightweightThemeDrawable.java',
|
||||
'LocaleManager.java',
|
||||
'MediaCastingBar.java',
|
||||
'MemoryMonitor.java',
|
||||
'menu/GeckoMenu.java',
|
||||
'menu/GeckoMenuInflater.java',
|
||||
|
|
После Ширина: | Высота: | Размер: 362 B |
После Ширина: | Высота: | Размер: 670 B |
После Ширина: | Высота: | Размер: 763 B |
После Ширина: | Высота: | Размер: 252 B |
После Ширина: | Высота: | Размер: 463 B |
После Ширина: | Высота: | Размер: 497 B |
После Ширина: | Высота: | Размер: 389 B |
После Ширина: | Высота: | Размер: 853 B |
После Ширина: | Высота: | Размер: 1.2 KiB |
|
@ -48,6 +48,13 @@
|
|||
style="@style/FindBar"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<org.mozilla.gecko.MediaCastingBar android:id="@+id/media_casting"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
style="@style/FindBar"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<RelativeLayout android:id="@+id/camera_layout"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<RelativeLayout android:id="@+id/media_controls"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true">
|
||||
|
||||
<ImageButton android:id="@+id/media_play"
|
||||
style="@style/FindBar.ImageButton"
|
||||
android:contentDescription="@string/media_play"
|
||||
android:src="@drawable/media_bar_play"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<ImageButton android:id="@+id/media_pause"
|
||||
style="@style/FindBar.ImageButton"
|
||||
android:contentDescription="@string/media_pause"
|
||||
android:src="@drawable/media_bar_pause"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<TextView android:id="@+id/media_casting_to"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="5dip"
|
||||
android:layout_marginRight="5dip"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_toLeftOf="@id/media_controls"
|
||||
android:layout_centerVertical="true"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:contentDescription="@string/media_casting_to"/>
|
||||
|
||||
<ImageButton android:id="@+id/media_stop"
|
||||
style="@style/FindBar.ImageButton"
|
||||
android:contentDescription="@string/media_stop"
|
||||
android:layout_alignParentRight="true"
|
||||
android:src="@drawable/media_bar_stop"/>
|
||||
|
||||
</merge>
|
|
@ -83,6 +83,11 @@
|
|||
<string name="find_next">&find_next;</string>
|
||||
<string name="find_close">&find_close;</string>
|
||||
|
||||
<string name="media_casting_to">&media_casting_to;</string>
|
||||
<string name="media_play">&media_play;</string>
|
||||
<string name="media_pause">&media_pause;</string>
|
||||
<string name="media_stop">&media_stop;</string>
|
||||
|
||||
<string name="settings">&settings;</string>
|
||||
<string name="settings_title">&settings_title;</string>
|
||||
<string name="pref_category_advanced">&pref_category_advanced;</string>
|
||||
|
|
|
@ -19,9 +19,17 @@ var CastingApps = {
|
|||
this.filterCast,
|
||||
this.openExternal.bind(this)
|
||||
);
|
||||
|
||||
Services.obs.addObserver(this, "Casting:Play", false);
|
||||
Services.obs.addObserver(this, "Casting:Pause", false);
|
||||
Services.obs.addObserver(this, "Casting:Stop", false);
|
||||
},
|
||||
|
||||
uninit: function ca_uninit() {
|
||||
Services.obs.removeObserver(this, "Casting:Play");
|
||||
Services.obs.removeObserver(this, "Casting:Pause");
|
||||
Services.obs.removeObserver(this, "Casting:Stop");
|
||||
|
||||
NativeWindow.contextmenus.remove(this._castMenuId);
|
||||
},
|
||||
|
||||
|
@ -29,6 +37,26 @@ var CastingApps = {
|
|||
return Services.prefs.getBoolPref("browser.casting.enabled");
|
||||
},
|
||||
|
||||
observe: function (aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
case "Casting:Play":
|
||||
if (this.session && this.session.remoteMedia.status == "paused") {
|
||||
this.session.remoteMedia.play();
|
||||
}
|
||||
break;
|
||||
case "Casting:Pause":
|
||||
if (this.session && this.session.remoteMedia.status == "started") {
|
||||
this.session.remoteMedia.pause();
|
||||
}
|
||||
break;
|
||||
case "Casting:Stop":
|
||||
if (this.session) {
|
||||
this.closeExternal();
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
makeURI: function makeURI(aURL, aOriginCharset, aBaseURI) {
|
||||
return Services.io.newURI(aURL, aOriginCharset, aBaseURI);
|
||||
},
|
||||
|
@ -192,9 +220,11 @@ var CastingApps = {
|
|||
}
|
||||
|
||||
aRemoteMedia.load(this.session.data);
|
||||
sendMessageToJava({ type: "Casting:Started", device: this.session.service.friendlyName });
|
||||
},
|
||||
|
||||
onRemoteMediaStop: function(aRemoteMedia) {
|
||||
sendMessageToJava({ type: "Casting:Stopped" });
|
||||
},
|
||||
|
||||
onRemoteMediaStatus: function(aRemoteMedia) {
|
||||
|
|