OLD | NEW |
| (Empty) |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 package org.chromium.blimp; | |
6 | |
7 import android.annotation.SuppressLint; | |
8 import android.app.Activity; | |
9 import android.content.Intent; | |
10 import android.os.Bundle; | |
11 import android.os.Handler; | |
12 import android.text.TextUtils; | |
13 import android.view.View; | |
14 import android.widget.TextView; | |
15 | |
16 import org.chromium.base.CommandLine; | |
17 import org.chromium.base.Log; | |
18 import org.chromium.base.annotations.SuppressFBWarnings; | |
19 import org.chromium.base.library_loader.ProcessInitException; | |
20 import org.chromium.blimp.auth.RetryingTokenSource; | |
21 import org.chromium.blimp.auth.TokenSource; | |
22 import org.chromium.blimp.auth.TokenSourceImpl; | |
23 import org.chromium.blimp.core.BlimpClientSwitches; | |
24 import org.chromium.blimp.preferences.PreferencesUtil; | |
25 import org.chromium.blimp.session.BlimpClientSession; | |
26 import org.chromium.blimp.session.EngineInfo; | |
27 import org.chromium.blimp.session.TabControlFeature; | |
28 import org.chromium.blimp.toolbar.Toolbar; | |
29 import org.chromium.blimp.toolbar.ToolbarMenu; | |
30 import org.chromium.ui.base.WindowAndroid; | |
31 import org.chromium.ui.widget.Toast; | |
32 | |
33 /** | |
34 * The {@link Activity} for rendering the main Blimp client. This loads the Bli
mp rendering stack | |
35 * and displays it. | |
36 */ | |
37 public class BlimpRendererActivity | |
38 extends Activity implements BlimpLibraryLoader.Callback, TokenSource.Cal
lback, | |
39 BlimpClientSession.ConnectionObserver, | |
40 ToolbarMenu.ToolbarMenuDelegate, Toolbar.Too
lbarDelegate { | |
41 private static final int ACCOUNT_CHOOSER_INTENT_REQUEST_CODE = 100; | |
42 private static final String TAG = "BlimpRendererActivity"; | |
43 | |
44 // Refresh interval for the debug view in milliseconds. | |
45 private static final int DEBUG_VIEW_REFRESH_INTERVAL = 1000; | |
46 private static final int BYTES_PER_KILO = 1024; | |
47 | |
48 /** Provides user authentication tokens that can be used to query for engine
assignments. This | |
49 * can potentially query GoogleAuthUtil for an OAuth2 authentication token
with userinfo.email | |
50 * privileges for a chosen Android account. */ | |
51 private TokenSource mTokenSource; | |
52 | |
53 private BlimpView mBlimpView; | |
54 private Toolbar mToolbar; | |
55 private BlimpClientSession mBlimpClientSession; | |
56 private TabControlFeature mTabControlFeature; | |
57 private WindowAndroid mWindowAndroid; | |
58 | |
59 private Handler mHandler = new Handler(); | |
60 | |
61 private boolean mFirstUrlLoadDone = false; | |
62 | |
63 // Flag to record the base value of the metrics when the debug view is turne
d on. | |
64 private boolean mStatsBaseRecorded = false; | |
65 private int mSentBase; | |
66 private int mReceivedBase; | |
67 private int mCommitsBase; | |
68 private int mSent; | |
69 private int mReceived; | |
70 private int mCommits; | |
71 private String mToken = null; | |
72 | |
73 @Override | |
74 @SuppressFBWarnings("DM_EXIT") // FindBugs doesn't like System.exit(). | |
75 protected void onCreate(Bundle savedInstanceState) { | |
76 super.onCreate(savedInstanceState); | |
77 buildAndTriggerTokenSourceIfNeeded(); | |
78 try { | |
79 BlimpLibraryLoader.startAsync(this); | |
80 } catch (ProcessInitException e) { | |
81 Log.e(TAG, "Native startup exception", e); | |
82 System.exit(-1); | |
83 return; | |
84 } | |
85 } | |
86 | |
87 @Override | |
88 protected void onDestroy() { | |
89 if (mTabControlFeature != null) { | |
90 mTabControlFeature.destroy(); | |
91 mTabControlFeature = null; | |
92 } | |
93 | |
94 if (mBlimpView != null) { | |
95 mBlimpView.destroyRenderer(); | |
96 mBlimpView = null; | |
97 } | |
98 | |
99 if (mToolbar != null) { | |
100 mToolbar.destroy(); | |
101 mToolbar = null; | |
102 } | |
103 | |
104 if (mTokenSource != null) { | |
105 mTokenSource.destroy(); | |
106 mTokenSource = null; | |
107 } | |
108 | |
109 // Destroy the BlimpClientSession last, as all other features may rely o
n it. | |
110 if (mBlimpClientSession != null) { | |
111 mBlimpClientSession.removeObserver(this); | |
112 mBlimpClientSession.destroy(); | |
113 mBlimpClientSession = null; | |
114 } | |
115 | |
116 super.onDestroy(); | |
117 } | |
118 | |
119 @Override | |
120 protected void onActivityResult(int requestCode, int resultCode, Intent data
) { | |
121 switch (requestCode) { | |
122 case ACCOUNT_CHOOSER_INTENT_REQUEST_CODE: | |
123 if (resultCode == RESULT_OK) { | |
124 mTokenSource.onAccountSelected(data); | |
125 mTokenSource.getToken(); | |
126 } else { | |
127 onTokenUnavailable(false); | |
128 } | |
129 break; | |
130 default: | |
131 break; | |
132 } | |
133 } | |
134 | |
135 @Override | |
136 public void onBackPressed() { | |
137 // Check if the toolbar can handle the back navigation. | |
138 if (mToolbar != null && mToolbar.onBackPressed()) return; | |
139 | |
140 // If not, use the default Activity behavior. | |
141 super.onBackPressed(); | |
142 } | |
143 | |
144 // BlimpLibraryLoader.Callback implementation. | |
145 @Override | |
146 public void onStartupComplete(boolean success) { | |
147 if (!success) { | |
148 Log.e(TAG, "Native startup failed"); | |
149 finish(); | |
150 return; | |
151 } | |
152 | |
153 setContentView(R.layout.blimp_main); | |
154 | |
155 mWindowAndroid = new WindowAndroid(BlimpRendererActivity.this); | |
156 mBlimpClientSession = | |
157 new BlimpClientSession(PreferencesUtil.findAssignerUrl(this), mW
indowAndroid); | |
158 mBlimpClientSession.addObserver(this); | |
159 | |
160 mBlimpView = (BlimpView) findViewById(R.id.renderer); | |
161 mBlimpView.initializeRenderer(mBlimpClientSession); | |
162 | |
163 mToolbar = (Toolbar) findViewById(R.id.toolbar); | |
164 mToolbar.initialize(mBlimpClientSession, this); | |
165 | |
166 mTabControlFeature = new TabControlFeature(mBlimpClientSession, mBlimpVi
ew); | |
167 | |
168 handleUrlFromIntent(getIntent()); | |
169 | |
170 // If Blimp client has command line flag "engine-ip", client will use th
e command line token | |
171 // to connect. See GetAssignmentFromCommandLine() in | |
172 // blimp/client/session/assignment_source.cc | |
173 // In normal cases, where client uses the engine ip given by the Assigne
r, | |
174 // connection to the engine is triggered by the successful retrieval of
a token as | |
175 // TokenSource.Callback. | |
176 if (CommandLine.getInstance().hasSwitch(BlimpClientSwitches.ENGINE_IP))
{ | |
177 mBlimpClientSession.connect(null); | |
178 } else { | |
179 if (mToken != null) { | |
180 mBlimpClientSession.connect(mToken); | |
181 mToken = null; | |
182 } | |
183 } | |
184 } | |
185 | |
186 // ToolbarMenu.ToolbarMenuDelegate implementation. | |
187 @Override | |
188 public void showDebugView(boolean show) { | |
189 View debugView = findViewById(R.id.debug_stats); | |
190 debugView.setVisibility(show ? View.VISIBLE : View.INVISIBLE); | |
191 if (show) { | |
192 Runnable debugStatsRunnable = new Runnable() { | |
193 @Override | |
194 public void run() { | |
195 if (mToolbar.getToolbarMenu().isDebugInfoEnabled()) { | |
196 int[] metrics = mBlimpClientSession.getDebugStats(); | |
197 updateDebugStats(metrics); | |
198 mHandler.postDelayed(this, DEBUG_VIEW_REFRESH_INTERVAL); | |
199 } | |
200 } | |
201 }; | |
202 debugStatsRunnable.run(); | |
203 } else { | |
204 mStatsBaseRecorded = false; | |
205 } | |
206 } | |
207 | |
208 @Override | |
209 protected void onNewIntent(Intent intent) { | |
210 super.onNewIntent(intent); | |
211 handleUrlFromIntent(intent); | |
212 } | |
213 | |
214 /** | |
215 * Retrieve the URL from the Intent. | |
216 * @param intent Intent to examine. | |
217 * @return URL from the Intent, or null if a valid URL couldn't be found. | |
218 */ | |
219 private String getUrlFromIntent(Intent intent) { | |
220 if (intent == null) return null; | |
221 | |
222 String url = intent.getDataString(); | |
223 if (url == null) return null; | |
224 | |
225 url = url.trim(); | |
226 return TextUtils.isEmpty(url) ? null : url; | |
227 } | |
228 | |
229 /** | |
230 * Retrieves an URL from an Intent and loads it in the browser. | |
231 * If the toolbar already has an URL and the new intent doesn't have an URL
(e.g. bringing back | |
232 * from background), the intent gets ignored. | |
233 * @param intent Intent that contains the URL. | |
234 */ | |
235 private void handleUrlFromIntent(Intent intent) { | |
236 // TODO(shaktisahu): On a slow device, this might happen. Load the corre
ct URL once the | |
237 // toolbar loading is complete (crbug/601226) | |
238 if (mToolbar == null) return; | |
239 | |
240 String url = getUrlFromIntent(intent); | |
241 if (mFirstUrlLoadDone && url == null) return; | |
242 mFirstUrlLoadDone = true; | |
243 | |
244 mToolbar.loadUrl(url == null ? "http://www.google.com/" : url); | |
245 } | |
246 | |
247 // TokenSource.Callback implementation. | |
248 @Override | |
249 public void onTokenReceived(String token) { | |
250 if (mBlimpClientSession != null) { | |
251 mBlimpClientSession.connect(token); | |
252 } else { | |
253 mToken = token; | |
254 } | |
255 } | |
256 | |
257 @Override | |
258 public void onTokenUnavailable(boolean isTransient) { | |
259 // Ignore isTransient here because we're relying on the auto-retry Token
Source. | |
260 // TODO(dtrainor): Show a better error dialog/message. | |
261 Toast.makeText(this, R.string.signin_get_token_failed, Toast.LENGTH_LONG
).show(); | |
262 } | |
263 | |
264 @Override | |
265 public void onNeedsAccountToBeSelected(Intent suggestedIntent) { | |
266 startActivityForResult(suggestedIntent, ACCOUNT_CHOOSER_INTENT_REQUEST_C
ODE); | |
267 } | |
268 | |
269 // BlimpClientSession.ConnectionObserver interface. | |
270 @Override | |
271 public void onAssignmentReceived( | |
272 int result, int suggestedMessageResourceId, EngineInfo engineInfo) { | |
273 Toast.makeText(this, suggestedMessageResourceId, Toast.LENGTH_LONG).show
(); | |
274 } | |
275 | |
276 @Override | |
277 public void onConnected() { | |
278 Toast.makeText(this, R.string.network_connected, Toast.LENGTH_SHORT).sho
w(); | |
279 } | |
280 | |
281 /** | |
282 * Displays debug metrics up to one decimal place. | |
283 */ | |
284 @Override | |
285 @SuppressLint("DefaultLocale") | |
286 public void updateDebugStatsUI(int received, int sent, int commits) { | |
287 TextView tv = (TextView) findViewById(R.id.bytes_received_client); | |
288 tv.setText(String.format("%.1f", (float) received / BYTES_PER_KILO)); | |
289 tv = (TextView) findViewById(R.id.bytes_sent_client); | |
290 tv.setText(String.format("%.1f", (float) sent / BYTES_PER_KILO)); | |
291 tv = (TextView) findViewById(R.id.commit_count); | |
292 tv.setText(String.valueOf(commits)); | |
293 } | |
294 | |
295 private void updateDebugStats(int[] metrics) { | |
296 assert metrics.length == 3; | |
297 mReceived = metrics[0]; | |
298 mSent = metrics[1]; | |
299 mCommits = metrics[2]; | |
300 if (!mStatsBaseRecorded) { | |
301 mReceivedBase = mReceived; | |
302 mSentBase = mSent; | |
303 mCommitsBase = mCommits; | |
304 mStatsBaseRecorded = true; | |
305 } | |
306 updateDebugStatsUI(mReceived - mReceivedBase, mSent - mSentBase, mCommit
s - mCommitsBase); | |
307 } | |
308 | |
309 // Toolbar.ToolbarDelegate interface. | |
310 @Override | |
311 public void resetDebugStats() { | |
312 mReceivedBase = mReceived; | |
313 mSentBase = mSent; | |
314 mCommitsBase = mCommits; | |
315 } | |
316 | |
317 @Override | |
318 public void onDisconnected(String reason) { | |
319 Toast.makeText(this, | |
320 String.format(getResources().getString(R.string.network_dis
connected), reason), | |
321 Toast.LENGTH_LONG) | |
322 .show(); | |
323 } | |
324 | |
325 private void buildAndTriggerTokenSourceIfNeeded() { | |
326 // If Blimp client is given the engine ip by the command line, then ther
e is no need to | |
327 // build a TokenSource, because token, engine ip, engine port, and trans
port protocol are | |
328 // all given by command line. | |
329 if (CommandLine.getInstance().hasSwitch(BlimpClientSwitches.ENGINE_IP))
return; | |
330 | |
331 // Build a TokenSource that will internally retry accessing the underlyi
ng | |
332 // TokenSourceImpl. This will exponentially backoff while it tries to ge
t the access | |
333 // token. See {@link RetryingTokenSource} for more information. The un
derlying | |
334 // TokenSourceImpl will attempt to query GoogleAuthUtil, but might fail
if there is no | |
335 // account selected, in which case it will ask this Activity to show an
account chooser | |
336 // and notify it of the selection result. | |
337 mTokenSource = new RetryingTokenSource(new TokenSourceImpl(this)); | |
338 mTokenSource.setCallback(this); | |
339 mTokenSource.getToken(); | |
340 } | |
341 } | |
OLD | NEW |