diff --git a/.classpath b/.classpath
index 5777aed3..25b09fcf 100644
--- a/.classpath
+++ b/.classpath
@@ -6,5 +6,6 @@
+
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index fe7d7828..0221ef33 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -48,6 +48,7 @@
+
diff --git a/res/drawable-hdpi-v4/default_profile.png b/res/drawable-hdpi-v4/default_profile.png
new file mode 100644
index 00000000..4f8c38be
Binary files /dev/null and b/res/drawable-hdpi-v4/default_profile.png differ
diff --git a/res/drawable-hdpi-v4/icon_home_profile.png b/res/drawable-hdpi-v4/icon_home_profile.png
new file mode 100644
index 00000000..af140846
Binary files /dev/null and b/res/drawable-hdpi-v4/icon_home_profile.png differ
diff --git a/res/drawable/default_profile.png b/res/drawable/default_profile.png
new file mode 100644
index 00000000..c88a2951
Binary files /dev/null and b/res/drawable/default_profile.png differ
diff --git a/res/drawable/icon_home_profile.png b/res/drawable/icon_home_profile.png
new file mode 100644
index 00000000..79d20867
Binary files /dev/null and b/res/drawable/icon_home_profile.png differ
diff --git a/res/layout/profile_password.xml b/res/layout/profile_password.xml
new file mode 100644
index 00000000..2da84bd8
--- /dev/null
+++ b/res/layout/profile_password.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml
index 11ccac36..c0898a32 100644
--- a/res/xml/preferences.xml
+++ b/res/xml/preferences.xml
@@ -68,6 +68,11 @@
android:title="Show Pictures"
android:summary="Show the "Browse your Pictures" item"
android:enabled="true" android:defaultValue="true" />
+ response, final ICover
case MediaType.PICTURES:
done(controller, response);
break;
+ case MediaType.PROFILE:
+ try {
+ bitmap = profile(manager, context).getCover(manager, cover, thumbSize);
+ } catch (WifiStateException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+ break;
default:
done(controller, response);
break;
diff --git a/src/org/xbmc/android/remote/business/ManagerFactory.java b/src/org/xbmc/android/remote/business/ManagerFactory.java
index 49f7ae67..caeb833d 100644
--- a/src/org/xbmc/android/remote/business/ManagerFactory.java
+++ b/src/org/xbmc/android/remote/business/ManagerFactory.java
@@ -25,6 +25,7 @@
import org.xbmc.api.business.IEventClientManager;
import org.xbmc.api.business.IInfoManager;
import org.xbmc.api.business.IMusicManager;
+import org.xbmc.api.business.IProfileManager;
import org.xbmc.api.business.ITvShowManager;
import org.xbmc.api.business.IVideoManager;
import org.xbmc.api.presentation.INotifiableController;
@@ -48,6 +49,9 @@ public static ITvShowManager getTvManager(INotifiableController controller) {
public static IMusicManager getMusicManager(INotifiableController controller) {
return ManagerThread.music(controller);
}
+ public static IProfileManager getProfileManager(INotifiableController controller) {
+ return ManagerThread.profile(controller);
+ }
public static IEventClientManager getEventClientManager(INotifiableController controller) {
if (sEventClientManager == null) {
sEventClientManager = new EventClientManager();
diff --git a/src/org/xbmc/android/remote/business/ManagerThread.java b/src/org/xbmc/android/remote/business/ManagerThread.java
index 8cc96fa7..1283e4ee 100644
--- a/src/org/xbmc/android/remote/business/ManagerThread.java
+++ b/src/org/xbmc/android/remote/business/ManagerThread.java
@@ -42,6 +42,7 @@ public class ManagerThread extends Thread {
private final MusicManager mMusicManager;
private final VideoManager mVideoManager;
private final TvShowManager mTvShowManager;
+ private final ProfileManager mProfileManager;
private ManagerThread() {
super("ManagerThread");
@@ -50,6 +51,7 @@ private ManagerThread() {
mMusicManager = new MusicManager();
mVideoManager = new VideoManager();
mTvShowManager = new TvShowManager();
+ mProfileManager = new ProfileManager();
}
public static ManagerThread get() {
if (sManagerThread == null) {
@@ -75,6 +77,7 @@ public void run() {
mMusicManager.setHandler(mHandler);
mVideoManager.setHandler(mHandler);
mTvShowManager.setHandler(mHandler);
+ mProfileManager.setHandler(mHandler);
Looper.loop();
}
@@ -98,7 +101,11 @@ public static VideoManager video(INotifiableController controller) {
vm.setController(controller);
return vm;
}
-
+ public static ProfileManager profile(INotifiableController controller) {
+ final ProfileManager pm = get().mProfileManager;
+ pm.setController(controller);
+ return pm;
+ }
public static TvShowManager shows(INotifiableController controller) {
final TvShowManager shows = get().mTvShowManager;
shows.setController(controller);
diff --git a/src/org/xbmc/android/remote/business/ProfileManager.java b/src/org/xbmc/android/remote/business/ProfileManager.java
new file mode 100644
index 00000000..5b75b512
--- /dev/null
+++ b/src/org/xbmc/android/remote/business/ProfileManager.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2005-2009 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC Remote; see the file license. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+package org.xbmc.android.remote.business;
+
+import java.net.SocketTimeoutException;
+import java.util.ArrayList;
+
+import org.xbmc.api.business.DataResponse;
+import org.xbmc.api.business.INotifiableManager;
+import org.xbmc.api.business.ISortableManager;
+import org.xbmc.api.business.IProfileManager;
+import org.xbmc.api.object.Profile;
+
+import android.content.Context;
+
+/**
+ * Asynchronously wraps the {@link org.xbmc.httpapi.client.ProfileClient} class.
+ *
+ * @author Team XBMC
+ */
+public class ProfileManager extends AbstractManager implements IProfileManager, ISortableManager, INotifiableManager {
+
+ /**
+ * Gets all profiles from database
+ * @param response Response object
+ */
+ public void getProfiles(final DataResponse> response, final Context context) {
+ mHandler.post(new Command>(response, this) {
+ @Override
+ public void doRun() throws Exception {
+ response.value = profile(context).getProfiles(ProfileManager.this);
+ }
+ });
+ }
+
+ /**
+ * Gets the current active profile
+ * @param response Response object
+ */
+ public void GetCurrentProfile(final DataResponse response, final Context context) {
+ mHandler.post(new Command(response, this) {
+ @Override
+ public void doRun() throws Exception, SocketTimeoutException {
+ response.value = profile(context).getCurrentProfile(ProfileManager.this);
+ }
+ });
+ }
+
+ /**
+ * Loads a new profile
+ * @param response Response object
+ * @param profileName The new profile to load
+ * @param profilePassword The password for the new profile
+ */
+ public void loadProfile(final DataResponse response, final String profileName, final String profilePassword, final Context context) {
+ mHandler.post(new Command(response, this) {
+ public void doRun() throws Exception{
+ response.value = profile(context).loadProfile(ProfileManager.this, profileName, profilePassword);
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/org/xbmc/android/remote/presentation/activity/ProfileActivity.java b/src/org/xbmc/android/remote/presentation/activity/ProfileActivity.java
new file mode 100644
index 00000000..cfabea1c
--- /dev/null
+++ b/src/org/xbmc/android/remote/presentation/activity/ProfileActivity.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2005-2009 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC Remote; see the file license. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+package org.xbmc.android.remote.presentation.activity;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+
+import org.xbmc.android.remote.R;
+import org.xbmc.android.remote.presentation.activity.AbsListActivity;
+import org.xbmc.android.remote.presentation.controller.ProfileListController;
+import org.xbmc.android.remote.presentation.controller.RemoteController;
+
+/**
+ * Activity for remote control. At the moment that's the good ol' Xbox remote
+ * control, more to come...
+ *
+ * @author Team XBMC
+ */
+public class ProfileActivity extends ListActivity {
+
+ private ProfileListController mProfileListController;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mProfileListController = (ProfileListController)mListController;
+ }
+
+ @Override
+ public Dialog onCreateDialog(int id) {
+ super.onCreateDialog(id);
+ return mProfileListController.onCreateDialog(id, this);
+ }
+
+ @Override
+ public void onPrepareDialog(int id, Dialog dialog) {
+ super.onPrepareDialog(id, dialog);
+ mProfileListController.onPrepareDialog(id, dialog);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mProfileListController.onActivityResume(this);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mProfileListController.onActivityPause();
+ }
+
+}
diff --git a/src/org/xbmc/android/remote/presentation/controller/HomeController.java b/src/org/xbmc/android/remote/presentation/controller/HomeController.java
index 7b683d03..e17dfb31 100644
--- a/src/org/xbmc/android/remote/presentation/controller/HomeController.java
+++ b/src/org/xbmc/android/remote/presentation/controller/HomeController.java
@@ -36,6 +36,7 @@
import org.xbmc.android.remote.presentation.activity.MovieLibraryActivity;
import org.xbmc.android.remote.presentation.activity.MusicLibraryActivity;
import org.xbmc.android.remote.presentation.activity.NowPlayingActivity;
+import org.xbmc.android.remote.presentation.activity.ProfileActivity;
import org.xbmc.android.remote.presentation.activity.RemoteActivity;
import org.xbmc.android.remote.presentation.activity.TvShowLibraryActivity;
import org.xbmc.android.remote.presentation.notification.NowPlayingNotificationManager;
@@ -106,6 +107,7 @@ public class HomeController extends AbstractController implements INotifiableCon
private static final int HOME_ACTION_WOL = 6;
private static final int HOME_ACTION_TVSHOWS = 7;
private static final int HOME_ACTION_POWERDOWN = 8;
+ private static final int HOME_ACTION_PROFILES = 9;
private IInfoManager mInfoManager;
@@ -218,6 +220,7 @@ public void run() {
private void setupMenuItems(GridView menuGrid) {
final HomeItem remote = new HomeItem(HOME_ACTION_REMOTE, R.drawable.icon_home_remote, "Remote Control", "Use as");
+ final HomeItem profiles = new HomeItem(HOME_ACTION_PROFILES, R.drawable.icon_home_profile, "Profiles", "List of");
final ArrayList homeItems = new ArrayList();
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mActivity.getApplicationContext());
@@ -233,6 +236,8 @@ private void setupMenuItems(GridView menuGrid) {
prefs.registerOnSharedPreferenceChangeListener(this);
homeItems.add(new HomeItem(HOME_ACTION_NOWPLAYING, R.drawable.icon_home_playing, "Now Playing", "See what's"));
homeItems.add(remote);
+ if (prefs.getBoolean("setting_show_home_profiles", false))
+ homeItems.add(profiles);
if (prefs.getBoolean("setting_show_home_powerdown", false))
homeItems.add(new HomeItem(HOME_ACTION_POWERDOWN, R.drawable.icon_home_power, "Power Off", "Turn your XBMC off"));
@@ -285,6 +290,10 @@ public void onItemClick(AdapterView> listView, View v, int position, long ID)
}
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NO_HISTORY);
break;
+ case HOME_ACTION_PROFILES:
+ intent = new Intent(v.getContext(), ProfileActivity.class);
+ intent.putExtra(ListController.EXTRA_LIST_CONTROLLER, new ProfileListController());
+ break;
case HOME_ACTION_MUSIC:
intent = new Intent(v.getContext(), MusicLibraryActivity.class);
break;
@@ -593,7 +602,7 @@ public void onActivityResume(Activity activity) {
}
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
- if (key.equals("setting_show_home_music") || key.equals("setting_show_home_movies") || key.equals("setting_show_home_tv") || key.equals("setting_show_home_pictures") || key.equals("setting_show_home_powerdown")) {
+ if (key.equals("setting_show_home_music") || key.equals("setting_show_home_movies") || key.equals("setting_show_home_tv") || key.equals("setting_show_home_pictures") || key.equals("setting_show_home_profiles") || key.equals("setting_show_home_powerdown")) {
setupMenuItems(mMenuGrid);
}
}
diff --git a/src/org/xbmc/android/remote/presentation/controller/ProfileListController.java b/src/org/xbmc/android/remote/presentation/controller/ProfileListController.java
new file mode 100644
index 00000000..9113d723
--- /dev/null
+++ b/src/org/xbmc/android/remote/presentation/controller/ProfileListController.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2005-2009 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC Remote; see the file license. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+package org.xbmc.android.remote.presentation.controller;
+
+import java.util.ArrayList;
+
+import org.xbmc.android.remote.R;
+import org.xbmc.android.remote.business.ManagerFactory;
+import org.xbmc.android.remote.presentation.widget.FiveLabelsItemView;
+import org.xbmc.android.util.ImportUtilities;
+import org.xbmc.api.business.DataResponse;
+import org.xbmc.api.business.IControlManager;
+import org.xbmc.api.business.IProfileManager;
+import org.xbmc.api.object.Profile;
+import org.xbmc.api.type.ThumbSize;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.Context;
+import android.graphics.BitmapFactory;
+import android.os.Handler;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.View.OnClickListener;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ListAdapter;
+import android.widget.Toast;
+
+public class ProfileListController extends ListController implements IController {
+
+ private static final int mThumbSize = ThumbSize.SMALL;
+
+ public static final int DIALOG_PASSWORD = 500;
+
+ private IProfileManager mProfileManager;
+ private IControlManager mControlManager;
+ private String mCurrentProfile; // The current active profile name
+ private String mClickedProfile; // The clicked profile name (Needed in the password dialog)
+
+ private boolean mLoadCovers = false;
+ // Loading a new profile might take some time, let's try to reconnect more than once.
+ public static final int MAX_NUMBER_OF_TRIES = 5;
+ private byte mNumberOfTries = 0; //
+
+ public void onCreate(Activity activity, Handler handler, AbsListView list) {
+
+ mProfileManager = ManagerFactory.getProfileManager(this);
+ mControlManager = ManagerFactory.getControlManager(this);
+
+ final String sdError = ImportUtilities.assertSdCard();
+ mLoadCovers = sdError == null;
+
+ if (!isCreated()) {
+ super.onCreate(activity, handler, list);
+
+ if (!mLoadCovers) {
+ Toast toast = Toast.makeText(activity, sdError + " Displaying place holders only.", Toast.LENGTH_LONG);
+ toast.show();
+ }
+
+ activity.registerForContextMenu(mList);
+
+ mFallbackBitmap = BitmapFactory.decodeResource(activity.getResources(), R.drawable.default_profile);
+ setupIdleListener();
+
+ mList.setOnItemClickListener(new OnItemClickListener() {
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ if(isLoading()) return;
+ final Profile profile = (Profile)mList.getAdapter().getItem(((FiveLabelsItemView)view).position);
+ mClickedProfile = profile.title;
+ if (profile.lockmode == 0) { // No password required
+ mProfileManager.loadProfile(new DataResponse(), profile.title, null, mActivity.getApplicationContext());
+ refreshList();
+ } else {
+ showDialog(DIALOG_PASSWORD);
+ }
+ }
+ });
+ mList.setOnKeyListener(new ListControllerOnKeyListener());
+ fetch();
+ }
+ }
+
+ private void fetch() {
+ final String title = "Profiles";
+ DataResponse response1 = new DataResponse() { // Holds the current active profile
+ public void run() {
+ mCurrentProfile = value;
+ }
+ };
+ DataResponse> response2 = new DataResponse>() { // Holds the list of all the profiles
+ public void run() {
+ setTitle(title + " (" + value.size() + ")");
+ ((AdapterView) mList).setAdapter(new ProfileAdapter(mActivity, value));
+ }
+ };
+
+ showOnLoading();
+ setTitle(title + "...");
+ mProfileManager.GetCurrentProfile(response1, mActivity.getApplicationContext());
+ mProfileManager.getProfiles(response2, mActivity.getApplicationContext());
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { } // No context menu
+
+ @Override
+ public void onContextItemSelected(MenuItem item) { } // No context menu
+
+ @Override
+ protected void refreshList() {
+ mNumberOfTries = 0;
+ hideMessage();
+ fetch();
+ }
+
+ @Override
+ public void onError(Exception exception) {
+ if (mActivity == null) {
+ return;
+ }
+ try {
+ throw exception;
+ } catch (Exception e) {
+ mNumberOfTries++;
+ if (mNumberOfTries < MAX_NUMBER_OF_TRIES) {
+ fetch(); // Let's try to fetch it more than once
+ } else {
+ super.onError(exception);
+ }
+ }
+ }
+
+ private class ProfileAdapter extends ArrayAdapter {
+ ProfileAdapter(Activity activity, ArrayList items) {
+ super(activity, 0, items);
+ }
+ public View getView(int position, View convertView, ViewGroup parent) {
+
+ final FiveLabelsItemView view;
+ if (convertView == null) {
+ view = new FiveLabelsItemView(mActivity, mProfileManager, parent.getWidth(), mFallbackBitmap, mList.getSelector(), false);
+ } else {
+ view = (FiveLabelsItemView)convertView;
+ }
+
+ final Profile profile = getItem(position);
+ view.reset();
+ view.position = position;
+ view.title = profile.title;
+ view.subtitle = profile.lockmode > 0 ? "Protected" : "Public";
+ view.subtitleRight = profile.title.equals(mCurrentProfile) ? "Active" : "";
+
+ if (mLoadCovers) {
+ if(mProfileManager.coverLoaded(profile, mThumbSize)){
+ view.setCover(mProfileManager.getCoverSync(profile, mThumbSize));
+ }else{
+ view.setCover(null);
+ view.getResponse().load(profile, !mPostScrollLoader.isListIdle());
+ }
+ }
+ return view;
+ }
+ }
+
+ private static final long serialVersionUID = -8661723658167343976L;
+
+ public void onActivityPause() {
+ if (mProfileManager != null) {
+ mProfileManager.setController(null);
+ mProfileManager.postActivity();
+ }
+ if (mControlManager != null) {
+ mControlManager.setController(null);
+ }
+ super.onActivityPause();
+ }
+
+ public void onActivityResume(Activity activity) {
+ super.onActivityResume(activity);
+ if (mProfileManager != null) {
+ mProfileManager.setController(this);
+ }
+ if (mControlManager != null) {
+ mControlManager.setController(this);
+ }
+ }
+
+ // Need Context passed in because this can be called at times when mActivity is null.
+ public Dialog onCreateDialog(int id, final Context context) {
+ Dialog dialog;
+ switch(id) {
+ case DIALOG_PASSWORD:
+ dialog = new Dialog(context);
+ dialog.setContentView(R.layout.profile_password);
+ Button sendbutton = (Button) dialog.findViewById(R.id.password_button_send);
+ sendbutton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ EditText text = (EditText) v.getRootView().findViewById(R.id.password_text);
+ mProfileManager.loadProfile(new DataResponse(), mClickedProfile, text.getText().toString(), mActivity.getApplicationContext());
+ dismissDialog(DIALOG_PASSWORD);
+ refreshList();
+ }
+ });
+ Button cancelbutton = (Button) dialog.findViewById(R.id.password_button_cancel);
+ cancelbutton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ dismissDialog(DIALOG_PASSWORD);
+ }
+ });
+ break;
+ default:
+ dialog = null;
+ }
+ return dialog;
+ }
+
+ public void onPrepareDialog (int id, Dialog dialog) {
+ dialog.setTitle(mClickedProfile);
+ EditText text = (EditText) dialog.findViewById(R.id.password_text);
+ text.setText("");
+ }
+
+}
diff --git a/src/org/xbmc/android/util/ClientFactory.java b/src/org/xbmc/android/util/ClientFactory.java
index fc6fc270..2781991d 100644
--- a/src/org/xbmc/android/util/ClientFactory.java
+++ b/src/org/xbmc/android/util/ClientFactory.java
@@ -32,6 +32,7 @@
import org.xbmc.api.data.IEventClient;
import org.xbmc.api.data.IInfoClient;
import org.xbmc.api.data.IMusicClient;
+import org.xbmc.api.data.IProfileClient;
import org.xbmc.api.data.ITvShowClient;
import org.xbmc.api.data.IVideoClient;
import org.xbmc.api.info.SystemInfo;
@@ -129,6 +130,19 @@ public static ITvShowClient getTvShowClient(INotifiableManager manager, Context
}
}
+ public static IProfileClient getProfileClient(INotifiableManager manager, Context context) throws WifiStateException {
+ assertWifiState(context);
+ probeQueryApiType(manager);
+ switch (sApiType) {
+ case API_TYPE_JSONRPC:
+ return createJsonClient(manager).profile;
+ case API_TYPE_UNSET:
+ case API_TYPE_HTTPIAPI:
+ default:
+ return null;
+ }
+ }
+
private static void assertWifiState(Context context) throws WifiStateException {
if (context != null && HostFactory.host != null && HostFactory.host.wifi_only){
final int state = WifiHelper.getInstance(context).getWifiState();
diff --git a/src/org/xbmc/api/business/IProfileManager.java b/src/org/xbmc/api/business/IProfileManager.java
new file mode 100644
index 00000000..dcf66903
--- /dev/null
+++ b/src/org/xbmc/api/business/IProfileManager.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2005-2009 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC Remote; see the file license. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+package org.xbmc.api.business;
+
+import java.util.ArrayList;
+
+import org.xbmc.api.object.Profile;
+
+import android.content.Context;
+
+/**
+ * This is the interface between the presentation layer and the business layer.
+ * All the controller of the presentation layer gets to see is this interface.
+ *
+ * @author Team XBMC
+ */
+public interface IProfileManager extends IManager {
+
+ /**
+ * Gets all profiles from database
+ * @param response Response object
+ */
+ public void getProfiles(final DataResponse> response, final Context context);
+
+ /**
+ * Gets the current active profile
+ * @param response Response object
+ */
+ public void GetCurrentProfile(final DataResponse response, final Context context);
+
+ /**
+ * Loads a new profile
+ * @param response Response object
+ * @param profileName The new profile to load
+ * @param profilePassword The password for the new profile
+ */
+ public void loadProfile(final DataResponse response, final String profileName, final String profilePassword, final Context context);
+
+ /**
+ * Put in here everything that has to be cleaned up after leaving an activity.
+ */
+ public void postActivity();
+}
\ No newline at end of file
diff --git a/src/org/xbmc/api/data/IProfileClient.java b/src/org/xbmc/api/data/IProfileClient.java
new file mode 100644
index 00000000..5f932200
--- /dev/null
+++ b/src/org/xbmc/api/data/IProfileClient.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2005-2009 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC Remote; see the file license. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+package org.xbmc.api.data;
+
+import java.util.ArrayList;
+
+import org.xbmc.api.business.INotifiableManager;
+import org.xbmc.api.object.ICoverArt;
+import org.xbmc.api.object.Profile;
+
+import android.graphics.Bitmap;
+
+
+/**
+ * This is the interface between the business layer and the presentation layer.
+ * All the business layer gets to see is this interface.
+ *
+ * @author Team XBMC
+ */
+public interface IProfileClient extends IClient {
+
+ /**
+ * Gets all profiles from database
+ * @return All profiles
+ */
+ public ArrayList getProfiles(INotifiableManager manager);
+
+
+ /**
+ * Gets the current active profile
+ * @return Current active profile name
+ */
+ public String getCurrentProfile(INotifiableManager manager);
+
+ /**
+ * Loads a new profile
+ * @param profileName The new profile to load
+ * @param profilePassword The password for the new profile
+ * @return True on success, false otherwise.
+ */
+ public boolean loadProfile(INotifiableManager manager, String profileName, String profilePassword);
+
+ /**
+ * Returns a cover as bitmap
+ * @param cover
+ * @return Cover
+ */
+ public Bitmap getCover(INotifiableManager manager, ICoverArt cover, int size);
+}
\ No newline at end of file
diff --git a/src/org/xbmc/api/object/Profile.java b/src/org/xbmc/api/object/Profile.java
new file mode 100644
index 00000000..12aeb26c
--- /dev/null
+++ b/src/org/xbmc/api/object/Profile.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2005-2009 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC Remote; see the file license. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+package org.xbmc.api.object;
+
+import java.io.Serializable;
+
+import org.xbmc.android.util.Crc32;
+import org.xbmc.api.type.MediaType;
+
+/**
+ * Stores what we can get from the profileview table.
+ *
+ * @author Team XBMC
+ */
+public class Profile implements ICoverArt, Serializable, INamedResource {
+
+ /**
+ * Constructor
+ * @param id Database ID
+ * @param title Profile name
+ * @param lockmode 0 is unlocked, anything else is locked
+ * @param artUrl Path for the profile thumbnail
+ */
+ public Profile(int id, String title, int lockmode, String artUrl) {
+ this.id = id;
+ this.title = title;
+ this.lockmode = lockmode;
+ this.artUrl = artUrl;
+ }
+
+ public int getMediaType() {
+ return MediaType.PROFILE;
+ }
+
+ public String getPath() {
+ return artUrl;
+ }
+
+ public String getShortName() {
+ return this.title;
+ }
+
+ public static String getThumbUri(ICoverArt cover) {
+ return cover.getThumbUrl();
+ }
+
+ /**
+ * Returns the CRC of the profile on which the thumb name is based upon.
+ * @return CRC32
+ */
+ public long getCrc() {
+ thumbID = Crc32.computeLowerCase(artUrl);
+ return thumbID;
+ }
+
+ public int getFallbackCrc() {
+ return Crc32.computeLowerCase(artUrl);
+ }
+
+ public String getThumbUrl() {
+ return artUrl;
+ }
+
+ /**
+ * Returns database ID.
+ * @return
+ */
+ public int getId() {
+ return this.id;
+ }
+
+ /**
+ * Returns profile name.
+ * @return
+ */
+ public String getName() {
+ return title;
+ }
+
+ /**
+ * Something descriptive
+ */
+ public String toString() {
+ return "[" + id + "] " + title;
+ }
+
+ /**
+ * Database ID
+ */
+ private final int id;
+ /**
+ * Profile title
+ */
+
+ public final String title;
+ /**
+ * LockMode
+ */
+ public final int lockmode;
+
+ public String artUrl;
+ /**
+ * Save this once it's calculated
+ */
+ public long thumbID = 0L;
+
+ private static final long serialVersionUID = 1208620993981377535L;
+
+}
diff --git a/src/org/xbmc/api/type/MediaType.java b/src/org/xbmc/api/type/MediaType.java
index fee44477..96316030 100644
--- a/src/org/xbmc/api/type/MediaType.java
+++ b/src/org/xbmc/api/type/MediaType.java
@@ -31,6 +31,7 @@ public abstract class MediaType {
public static final int VIDEO_TVSEASON = 23;
public static final int VIDEO_TVEPISODE = 24;
public static final int PICTURES = 3;
+ public static final int PROFILE = 4;
public static String getName(int type) {
switch (type) {
@@ -44,6 +45,8 @@ public static String getName(int type) {
return "video";
case PICTURES:
return "pictures";
+ case PROFILE:
+ return "profile";
default:
return "";
}
@@ -90,6 +93,8 @@ public static String getArtFolder(int type) {
return "/Video";
case PICTURES:
return "/Pictures";
+ case PROFILE:
+ return "/Profile";
default:
return "";
}
diff --git a/src/org/xbmc/api/type/ThumbSize.java b/src/org/xbmc/api/type/ThumbSize.java
index 8bf41a5d..715d2216 100644
--- a/src/org/xbmc/api/type/ThumbSize.java
+++ b/src/org/xbmc/api/type/ThumbSize.java
@@ -121,6 +121,7 @@ public static Dimension getTargetDimension(int size, int mediaType, int x, int y
case MediaType.VIDEO_TVEPISODE:
case MediaType.VIDEO_TVSEASON:
case MediaType.VIDEO_TVSHOW:
+ case MediaType.PROFILE:
final double ar = ((double)x) / ((double)y);
if (ar > 0.95 && ar < 1.05) { // square
return new Dimension(getPixel(size), getPixel(size), Dimension.SQUARE);
diff --git a/src/org/xbmc/jsonrpc/JsonRpc.java b/src/org/xbmc/jsonrpc/JsonRpc.java
index 84d264b8..d62478c5 100644
--- a/src/org/xbmc/jsonrpc/JsonRpc.java
+++ b/src/org/xbmc/jsonrpc/JsonRpc.java
@@ -25,6 +25,7 @@
import org.xbmc.jsonrpc.client.ControlClient;
import org.xbmc.jsonrpc.client.InfoClient;
import org.xbmc.jsonrpc.client.MusicClient;
+import org.xbmc.jsonrpc.client.ProfileClient;
import org.xbmc.jsonrpc.client.TvShowClient;
import org.xbmc.jsonrpc.client.VideoClient;
@@ -63,6 +64,11 @@ public class JsonRpc {
*/
public final TvShowClient shows;
+ /**
+ * Use this client for anything profile related
+ */
+ public final ProfileClient profile;
+
/**
* Construct with all paramaters
* @param host Connection data of the host
@@ -82,6 +88,7 @@ public JsonRpc(Host host, int timeout) {
video = new VideoClient(connection);
control = new ControlClient(connection);
shows = new TvShowClient(connection);
+ profile = new ProfileClient(connection);
}
/**
diff --git a/src/org/xbmc/jsonrpc/client/ProfileClient.java b/src/org/xbmc/jsonrpc/client/ProfileClient.java
new file mode 100644
index 00000000..4d75514a
--- /dev/null
+++ b/src/org/xbmc/jsonrpc/client/ProfileClient.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2005-2009 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC Remote; see the file license. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+package org.xbmc.jsonrpc.client;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.codehaus.jackson.JsonNode;
+import org.xbmc.api.business.INotifiableManager;
+import org.xbmc.api.data.IProfileClient;
+import org.xbmc.api.object.Host;
+import org.xbmc.api.object.ICoverArt;
+import org.xbmc.api.object.Profile;
+import org.xbmc.jsonrpc.Connection;
+import android.graphics.Bitmap;
+
+/**
+ * Takes care of everything related to the profile database.
+ *
+ * @author Team XBMC
+ */
+public class ProfileClient extends Client implements IProfileClient {
+
+ /**
+ * Class constructor needs reference to HTTP client connection
+ * @param connection
+ */
+ public ProfileClient(Connection connection) {
+ super(connection);
+ }
+
+ /**
+ * Updates host info on the connection.
+ * @param host
+ */
+ public void setHost(Host host) {
+ mConnection.setHost(host);
+ }
+
+ /**
+ * Gets all profiles from database
+ * @return All profiles
+ */
+ public ArrayList getProfiles(INotifiableManager manager) {
+ ObjNode obj = obj().p(PARAM_PROPERTIES, arr().add("thumbnail").add("lockmode"));
+
+ final ArrayList profiles = new ArrayList();
+ final JsonNode result = mConnection.getJson(manager, "Profiles.GetProfiles", obj);
+ final JsonNode jsonProfiles = result.get("profiles");
+ if(jsonProfiles != null){
+ for (Iterator i = jsonProfiles.getElements(); i.hasNext();) {
+ JsonNode jsonProfile = (JsonNode)i.next();
+
+ profiles.add(new Profile(
+ getInt(jsonProfile, "movieid"),
+ getString(jsonProfile, "label"),
+ getInt(jsonProfile, "lockmode"),
+ getString(jsonProfile, "thumbnail")
+ ));
+ }
+ }
+ return profiles;
+ }
+
+ /**
+ * Gets the current active profile
+ * @return The current active profile
+ */
+ public String getCurrentProfile(INotifiableManager manager) {
+ final ObjNode obj = obj().p(PARAM_PROPERTIES, arr());
+ final JsonNode result = mConnection.getJson(manager, "Profiles.GetCurrentProfile", obj);
+ return getString(result, "label");
+ }
+
+ /**
+ * Loads a new profile
+ * @param profileName The new profile name
+ * @param profilePassword The profile password
+ * @return True on success, false otherwise.
+ */
+ public boolean loadProfile(INotifiableManager manager, String profileName, String password) {
+ ObjNode obj = obj().p("profile", profileName).p("prompt", false);
+ if (password != null) {
+ obj = obj.p("password", obj().p("value", md5(password)).p("encryption", "md5"));
+ }
+ return mConnection.getString(manager, "Profiles.LoadProfile", obj).equals("OK");
+ }
+
+ /**
+ * Returns a pre-resized profile cover. Pre-resizing is done in a way that
+ * the bitmap at least as large as the specified size but not larger than
+ * the double.
+ * @param manager Postback manager
+ * @param cover Cover object
+ * @param size Minmal size to pre-resize to.
+ * @return Thumbnail bitmap
+ */
+ public Bitmap getCover(INotifiableManager manager, ICoverArt cover, int size) {
+ String url = null;
+ if(Profile.getThumbUri(cover) != ""){
+ final JsonNode dl = mConnection.getJson(manager, "Files.PrepareDownload", obj().p("path", Profile.getThumbUri(cover)));
+ if(dl != null){
+ JsonNode details = dl.get("details");
+ if(details != null)
+ url = mConnection.getUrl(getString(details, "path"));
+ }
+ }
+ return getCover(manager, cover, size, url);
+ }
+
+ /**
+ * Converts string to an MD5 string
+ * @param s A string to convert
+ * @return The converted string
+ */
+ private static String md5(String s) {
+ try {
+ MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
+ digest.update(s.getBytes());
+ byte messageDigest[] = digest.digest();
+
+ return bytesToHex(messageDigest);
+
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ }
+ return "";
+ }
+
+ /**
+ * Converts list of bytes to HEX string
+ * @param bytes A list of bytes to convert
+ * @return The converted list of bytes
+ */
+ private static String bytesToHex(byte[] bytes) {
+ final char[] hexArray = "0123456789ABCDEF".toCharArray();
+ char[] hexChars = new char[bytes.length * 2];
+ for ( int j = 0; j < bytes.length; j++ ) {
+ int v = bytes[j] & 0xFF;
+ hexChars[j * 2] = hexArray[v >>> 4];
+ hexChars[j * 2 + 1] = hexArray[v & 0x0F];
+ }
+ return new String(hexChars);
+ }
+}