summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--remoting/android/java/AndroidManifest.xml.jinja23
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/AccountsAdapter.java43
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/Chromoting.java125
-rw-r--r--remoting/remoting_android.gypi10
-rw-r--r--remoting/resources/android/layout/account_dropdown.xml13
-rw-r--r--remoting/resources/android/layout/account_selected.xml29
-rw-r--r--remoting/resources/android/menu/chromoting_actionbar.xml4
-rw-r--r--remoting/resources/android/values-land/dimens.xml13
-rw-r--r--remoting/resources/android/values/dimens.xml13
-rw-r--r--remoting/resources/android/values/strings.xml1
-rw-r--r--remoting/resources/android/values/styles.xml15
11 files changed, 216 insertions, 53 deletions
diff --git a/remoting/android/java/AndroidManifest.xml.jinja2 b/remoting/android/java/AndroidManifest.xml.jinja2
index ba07b53..7afae2f 100644
--- a/remoting/android/java/AndroidManifest.xml.jinja2
+++ b/remoting/android/java/AndroidManifest.xml.jinja2
@@ -10,7 +10,8 @@
<application android:label="@string/product_name"
android:icon="@drawable/chromoting128">
<activity android:name="org.chromium.chromoting.Chromoting"
- android:configChanges="orientation|screenSize">
+ android:configChanges="orientation|screenSize"
+ android:theme="@style/MainTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
diff --git a/remoting/android/java/src/org/chromium/chromoting/AccountsAdapter.java b/remoting/android/java/src/org/chromium/chromoting/AccountsAdapter.java
new file mode 100644
index 0000000..8812fc7
--- /dev/null
+++ b/remoting/android/java/src/org/chromium/chromoting/AccountsAdapter.java
@@ -0,0 +1,43 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chromoting;
+
+import android.accounts.Account;
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+
+/** SpinnerAdapter class used for the ActionBar accounts spinner. */
+public class AccountsAdapter extends ArrayAdapter<Account> {
+ private LayoutInflater mInflater;
+
+ public AccountsAdapter(Context context, Account[] accounts) {
+ // ArrayAdapter only uses the |resource| parameter to return a View from getView() and
+ // getDropDownView(). But these methods are overridden here to return custom Views, so it's
+ // OK to provide 0 as the resource for the base class.
+ super(context, 0, accounts);
+ mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View view = mInflater.inflate(R.layout.account_selected, parent, false);
+ Account account = getItem(position);
+ TextView target = (TextView)view.findViewById(R.id.account_name);
+ target.setText(account.name);
+ return view;
+ }
+
+ @Override
+ public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ TextView view = (TextView)mInflater.inflate(R.layout.account_dropdown, parent, false);
+ Account account = getItem(position);
+ view.setText(account.name);
+ return view;
+ }
+}
diff --git a/remoting/android/java/src/org/chromium/chromoting/Chromoting.java b/remoting/android/java/src/org/chromium/chromoting/Chromoting.java
index 04741a4..9335f8a 100644
--- a/remoting/android/java/src/org/chromium/chromoting/Chromoting.java
+++ b/remoting/android/java/src/org/chromium/chromoting/Chromoting.java
@@ -10,11 +10,13 @@ import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
+import android.app.ActionBar;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.res.Configuration;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
@@ -34,7 +36,8 @@ import java.util.Arrays;
* also requests and renews authentication tokens using the system account manager.
*/
public class Chromoting extends Activity implements JniInterface.ConnectionListener,
- AccountManagerCallback<Bundle>, HostListLoader.Callback {
+ AccountManagerCallback<Bundle>, ActionBar.OnNavigationListener,
+ HostListLoader.Callback {
/** Only accounts of this type will be selectable for authentication. */
private static final String ACCOUNT_TYPE = "com.google";
@@ -45,6 +48,9 @@ public class Chromoting extends Activity implements JniInterface.ConnectionListe
/** User's account details. */
private Account mAccount;
+ /** List of accounts on the system. */
+ private Account[] mAccounts;
+
/** Account auth token. */
private String mToken;
@@ -57,9 +63,6 @@ public class Chromoting extends Activity implements JniInterface.ConnectionListe
/** Refresh button. */
private MenuItem mRefreshButton;
- /** Account switcher. */
- private MenuItem mAccountSwitcher;
-
/** Greeting at the top of the displayed list. */
private TextView mGreeting;
@@ -74,7 +77,7 @@ public class Chromoting extends Activity implements JniInterface.ConnectionListe
* this flag is set and a fresh authentication token is fetched from the AccountsService, and
* used to request the host list a second time.
*/
- boolean mAlreadyTried;
+ boolean mTriedNewAuthToken;
/**
* Called when the activity is first created. Loads the native library and requests an
@@ -85,7 +88,7 @@ public class Chromoting extends Activity implements JniInterface.ConnectionListe
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
- mAlreadyTried = false;
+ mTriedNewAuthToken = false;
mHostListLoader = new HostListLoader();
// Get ahold of our view widgets.
@@ -95,21 +98,40 @@ public class Chromoting extends Activity implements JniInterface.ConnectionListe
// Bring native components online.
JniInterface.loadLibrary(this);
+ mAccounts = AccountManager.get(this).getAccountsByType(ACCOUNT_TYPE);
+ if (mAccounts.length == 0) {
+ // TODO(lambroslambrou): Show a dialog with message: "To use <App Name>, you'll need
+ // to add a Google Account to your device." and two buttons: "Close", "Add account".
+ // The "Add account" button should navigate to the system "Add a Google Account"
+ // screen.
+ return;
+ }
+
SharedPreferences prefs = getPreferences(MODE_PRIVATE);
+ int index = -1;
if (prefs.contains("account_name") && prefs.contains("account_type")) {
- // Perform authentication using saved account selection.
mAccount = new Account(prefs.getString("account_name", null),
prefs.getString("account_type", null));
- AccountManager.get(this).getAuthToken(mAccount, TOKEN_SCOPE, null, this, this, null);
- if (mAccountSwitcher != null) {
- mAccountSwitcher.setTitle(mAccount.name);
- }
+ index = Arrays.asList(mAccounts).indexOf(mAccount);
+ }
+ if (index == -1) {
+ // Preference not loaded, or does not correspond to a valid account, so just pick the
+ // first account arbitrarily.
+ index = 0;
+ mAccount = mAccounts[0];
+ }
+
+ if (mAccounts.length == 1) {
+ getActionBar().setDisplayShowTitleEnabled(true);
+ getActionBar().setSubtitle(mAccount.name);
} else {
- // Request auth callback once user has chosen an account.
- Log.i("auth", "Requesting auth token from system");
- AccountManager.get(this).getAuthTokenByFeatures(ACCOUNT_TYPE, TOKEN_SCOPE, null, this,
- null, null, this, null);
+ AccountsAdapter adapter = new AccountsAdapter(this, mAccounts);
+ getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
+ getActionBar().setListNavigationCallbacks(adapter, this);
+ getActionBar().setSelectedNavigationItem(index);
}
+
+ refreshHostList();
}
/** Called when the activity is finally finished. */
@@ -119,26 +141,28 @@ public class Chromoting extends Activity implements JniInterface.ConnectionListe
JniInterface.disconnectFromHost();
}
+ /** Called when the display is rotated (as registered in the manifest). */
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ // Reload the spinner resources, since the font sizes are dependent on the screen
+ // orientation.
+ if (mAccounts.length != 1) {
+ AccountsAdapter adapter = new AccountsAdapter(this, mAccounts);
+ getActionBar().setListNavigationCallbacks(adapter, this);
+ }
+ }
+
/** Called to initialize the action bar. */
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.chromoting_actionbar, menu);
mRefreshButton = menu.findItem(R.id.actionbar_directoryrefresh);
- mAccountSwitcher = menu.findItem(R.id.actionbar_accountswitcher);
-
- Account[] usableAccounts = AccountManager.get(this).getAccountsByType(ACCOUNT_TYPE);
- if (usableAccounts.length == 1 && usableAccounts[0].equals(mAccount)) {
- // If we're using the only available account, don't offer account switching.
- // (If there are *no* accounts available, clicking this allows you to add a new one.)
- mAccountSwitcher.setEnabled(false);
- }
if (mAccount == null) {
- // If no account has been chosen, don't allow the user to refresh the listing.
+ // If there is no account, don't allow the user to refresh the listing.
mRefreshButton.setEnabled(false);
- } else {
- // If the user has picked an account, show its name directly on the account switcher.
- mAccountSwitcher.setTitle(mAccount.name);
}
return super.onCreateOptionsMenu(menu);
@@ -147,16 +171,7 @@ public class Chromoting extends Activity implements JniInterface.ConnectionListe
/** Called whenever an action bar button is pressed. */
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- mAlreadyTried = false;
- if (item == mAccountSwitcher) {
- // The account switcher triggers a listing of all available accounts.
- AccountManager.get(this).getAuthTokenByFeatures(ACCOUNT_TYPE, TOKEN_SCOPE, null, this,
- null, null, this, null);
- } else {
- // The refresh button simply makes use of the currently-chosen account.
- AccountManager.get(this).getAuthToken(mAccount, TOKEN_SCOPE, null, this, this, null);
- }
-
+ refreshHostList();
return true;
}
@@ -174,6 +189,13 @@ public class Chromoting extends Activity implements JniInterface.ConnectionListe
this);
}
+ private void refreshHostList() {
+ mTriedNewAuthToken = false;
+
+ // The refresh button simply makes use of the currently-chosen account.
+ AccountManager.get(this).getAuthToken(mAccount, TOKEN_SCOPE, null, this, this, null);
+ }
+
@Override
public void run(AccountManagerFuture<Bundle> future) {
Log.i("auth", "User finished with auth dialogs");
@@ -182,6 +204,12 @@ public class Chromoting extends Activity implements JniInterface.ConnectionListe
try {
// Here comes our auth token from the Android system.
result = future.getResult();
+ String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
+ Log.i("auth", "Received an auth token from system");
+
+ mToken = authToken;
+
+ mHostListLoader.retrieveHostList(authToken, this);
} catch (OperationCanceledException ex) {
explanation = getString(R.string.error_auth_canceled);
} catch (AuthenticatorException ex) {
@@ -195,20 +223,26 @@ public class Chromoting extends Activity implements JniInterface.ConnectionListe
return;
}
- String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
- String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
Log.i("auth", "Received an auth token from system");
- mAccount = new Account(accountName, accountType);
mToken = authToken;
- getPreferences(MODE_PRIVATE).edit().putString("account_name", accountName).
- putString("account_type", accountType).apply();
mHostListLoader.retrieveHostList(authToken, this);
}
@Override
+ public boolean onNavigationItemSelected(int itemPosition, long itemId) {
+ mAccount = mAccounts[itemPosition];
+
+ getPreferences(MODE_PRIVATE).edit().putString("account_name", mAccount.name).
+ putString("account_type", mAccount.type).apply();
+
+ refreshHostList();
+ return true;
+ }
+
+ @Override
public void onHostListReceived(HostInfo[] hosts) {
// Store a copy of the array, so that it can't be mutated by the HostListLoader. HostInfo
// is an immutable type, so a shallow copy of the array is sufficient here.
@@ -244,11 +278,11 @@ public class Chromoting extends Activity implements JniInterface.ConnectionListe
// This is the AUTH_FAILED case.
- if (!mAlreadyTried) {
+ if (!mTriedNewAuthToken) {
// This was our first connection attempt.
AccountManager authenticator = AccountManager.get(this);
- mAlreadyTried = true;
+ mTriedNewAuthToken = true;
Log.w("auth", "Requesting renewal of rejected auth token");
authenticator.invalidateAuthToken(mAccount.type, mToken);
@@ -270,9 +304,6 @@ public class Chromoting extends Activity implements JniInterface.ConnectionListe
*/
private void updateUi() {
mRefreshButton.setEnabled(mAccount != null);
- if (mAccount != null) {
- mAccountSwitcher.setTitle(mAccount.name);
- }
if (mHosts == null) {
mGreeting.setText(getString(R.string.inst_empty_list));
diff --git a/remoting/remoting_android.gypi b/remoting/remoting_android.gypi
index d8e136a..371eb2a 100644
--- a/remoting/remoting_android.gypi
+++ b/remoting/remoting_android.gypi
@@ -54,6 +54,8 @@
{
'destination': '<(SHARED_INTERMEDIATE_DIR)/remoting/android/res/layout',
'files': [
+ 'resources/android/layout/account_dropdown.xml',
+ 'resources/android/layout/account_selected.xml',
'resources/android/layout/desktop.xml',
'resources/android/layout/host.xml',
'resources/android/layout/main.xml',
@@ -70,7 +72,15 @@
{
'destination': '<(SHARED_INTERMEDIATE_DIR)/remoting/android/res/values',
'files': [
+ 'resources/android/values/dimens.xml',
'resources/android/values/strings.xml',
+ 'resources/android/values/styles.xml',
+ ],
+ },
+ {
+ 'destination': '<(SHARED_INTERMEDIATE_DIR)/remoting/android/res/values-land',
+ 'files': [
+ 'resources/android/values-land/dimens.xml',
],
},
],
diff --git a/remoting/resources/android/layout/account_dropdown.xml b/remoting/resources/android/layout/account_dropdown.xml
new file mode 100644
index 0000000..a9f0c96
--- /dev/null
+++ b/remoting/resources/android/layout/account_dropdown.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright 2014 The Chromium Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:drawablePadding="15sp"
+ android:padding="15sp"
+ android:gravity="center_vertical"/>
diff --git a/remoting/resources/android/layout/account_selected.xml b/remoting/resources/android/layout/account_selected.xml
new file mode 100644
index 0000000..cbf523a
--- /dev/null
+++ b/remoting/resources/android/layout/account_selected.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright 2014 The Chromium Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_vertical|start"
+ android:orientation="vertical">
+ <TextView android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/action_bar_title_top_margin"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:text="@string/product_name"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="@dimen/action_bar_title_text_size"/>
+ <TextView android:id="@+id/account_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/action_bar_subtitle_top_margin"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textSize="@dimen/action_bar_subtitle_text_size"/>
+</LinearLayout>
diff --git a/remoting/resources/android/menu/chromoting_actionbar.xml b/remoting/resources/android/menu/chromoting_actionbar.xml
index d22685b..6f057d0 100644
--- a/remoting/resources/android/menu/chromoting_actionbar.xml
+++ b/remoting/resources/android/menu/chromoting_actionbar.xml
@@ -11,8 +11,4 @@
android:title="@string/actionbar_directoryrefresh"
android:icon="@android:drawable/ic_popup_sync"
android:showAsAction="ifRoom"/>
- <item android:id="@+id/actionbar_accountswitcher"
- android:title="@string/actionbar_accountswitcher"
- android:icon="@android:drawable/ic_menu_more"
- android:showAsAction="ifRoom|withText"/>
</menu>
diff --git a/remoting/resources/android/values-land/dimens.xml b/remoting/resources/android/values-land/dimens.xml
new file mode 100644
index 0000000..ffdc015
--- /dev/null
+++ b/remoting/resources/android/values-land/dimens.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright 2014 The Chromium Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+-->
+
+<resources>
+ <dimen name="action_bar_title_text_size">16dp</dimen>
+ <dimen name="action_bar_subtitle_text_size">12dp</dimen>
+ <dimen name="action_bar_title_top_margin">-4dp</dimen>
+ <dimen name="action_bar_subtitle_top_margin">-2dp</dimen>
+</resources>
diff --git a/remoting/resources/android/values/dimens.xml b/remoting/resources/android/values/dimens.xml
new file mode 100644
index 0000000..3e7b686
--- /dev/null
+++ b/remoting/resources/android/values/dimens.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright 2014 The Chromium Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+-->
+
+<resources>
+ <dimen name="action_bar_title_text_size">18dp</dimen>
+ <dimen name="action_bar_subtitle_text_size">14dp</dimen>
+ <dimen name="action_bar_title_top_margin">0dp</dimen>
+ <dimen name="action_bar_subtitle_top_margin">-3dp</dimen>
+</resources>
diff --git a/remoting/resources/android/values/strings.xml b/remoting/resources/android/values/strings.xml
index c51e3cf..6997aae 100644
--- a/remoting/resources/android/values/strings.xml
+++ b/remoting/resources/android/values/strings.xml
@@ -22,7 +22,6 @@
<!--Action bar buttons-->
<string name="actionbar_directoryrefresh">Refresh</string>
- <string name="actionbar_accountswitcher">Accounts</string>
<string name="actionbar_disconnect">Disconnect</string>
<string name="actionbar_hide">Hide</string>
<string name="actionbar_keyboard">Keyboard</string>
diff --git a/remoting/resources/android/values/styles.xml b/remoting/resources/android/values/styles.xml
new file mode 100644
index 0000000..7157205
--- /dev/null
+++ b/remoting/resources/android/values/styles.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright 2014 The Chromium Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+-->
+
+<resources>
+ <style name="MainTheme" parent="@android:style/Theme.DeviceDefault">
+ <item name="android:actionBarStyle">@style/MainActionBar</item>
+ </style>
+ <style name="MainActionBar" parent="@android:style/Widget.DeviceDefault.ActionBar">
+ <item name="android:displayOptions">showHome</item>
+ </style>
+</resources>