mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge pull request #13730 from Cristo86/android_friends_main
Android - Friends in home screen menu
This commit is contained in:
commit
c8addd0581
18 changed files with 1037 additions and 14 deletions
|
@ -145,5 +145,7 @@ dependencies {
|
|||
compile 'com.squareup.retrofit2:converter-gson:2.4.0'
|
||||
implementation 'com.squareup.picasso:picasso:2.71828'
|
||||
|
||||
compile 'com.sothree.slidinguppanel:library:3.4.0'
|
||||
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
}
|
||||
|
|
|
@ -209,6 +209,11 @@ JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeGotoUr
|
|||
DependencyManager::get<AddressManager>()->loadSettings(jniUrl.toString());
|
||||
}
|
||||
|
||||
JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeGoToUser(JNIEnv* env, jobject obj, jstring username) {
|
||||
QAndroidJniObject jniUsername("java/lang/String", "(Ljava/lang/String;)V", username);
|
||||
DependencyManager::get<AddressManager>()->goToUser(jniUsername.toString(), false);
|
||||
}
|
||||
|
||||
JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnPause(JNIEnv* env, jobject obj) {
|
||||
}
|
||||
|
||||
|
@ -285,6 +290,18 @@ Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeLogin(JNIEnv *en
|
|||
Q_ARG(const QString&, username), Q_ARG(const QString&, password));
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_io_highfidelity_hifiinterface_fragment_FriendsFragment_nativeIsLoggedIn(JNIEnv *env, jobject instance) {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
return accountManager->isLoggedIn();
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_io_highfidelity_hifiinterface_fragment_FriendsFragment_nativeGetAccessToken(JNIEnv *env, jobject instance) {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
return env->NewStringUTF(accountManager->getAccountInfo().getAccessToken().token.toLatin1().data());
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_io_highfidelity_hifiinterface_SplashActivity_registerLoadCompleteListener(JNIEnv *env,
|
||||
jobject instance) {
|
||||
|
|
|
@ -48,6 +48,7 @@ import com.google.vr.ndk.base.GvrApi;*/
|
|||
public class InterfaceActivity extends QtActivity implements WebViewFragment.OnWebViewInteractionListener {
|
||||
|
||||
public static final String DOMAIN_URL = "url";
|
||||
public static final String EXTRA_GOTO_USERNAME = "gotousername";
|
||||
private static final String TAG = "Interface";
|
||||
private static final int WEB_DRAWER_RIGHT_MARGIN = 262;
|
||||
private static final int WEB_DRAWER_BOTTOM_MARGIN = 150;
|
||||
|
@ -59,6 +60,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
private native long nativeOnCreate(InterfaceActivity instance, AssetManager assetManager);
|
||||
private native void nativeOnDestroy();
|
||||
private native void nativeGotoUrl(String url);
|
||||
private native void nativeGoToUser(String username);
|
||||
private native void nativeBeforeEnterBackground();
|
||||
private native void nativeEnterBackground();
|
||||
private native void nativeEnterForeground();
|
||||
|
@ -280,6 +282,9 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW
|
|||
if (intent.hasExtra(DOMAIN_URL)) {
|
||||
webSlidingDrawer.setVisibility(View.GONE);
|
||||
nativeGotoUrl(intent.getStringExtra(DOMAIN_URL));
|
||||
} else if (intent.hasExtra(EXTRA_GOTO_USERNAME)) {
|
||||
webSlidingDrawer.setVisibility(View.GONE);
|
||||
nativeGoToUser(intent.getStringExtra(EXTRA_GOTO_USERNAME));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import android.widget.TextView;
|
|||
import com.squareup.picasso.Callback;
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import io.highfidelity.hifiinterface.fragment.FriendsFragment;
|
||||
import io.highfidelity.hifiinterface.fragment.HomeFragment;
|
||||
import io.highfidelity.hifiinterface.fragment.LoginFragment;
|
||||
import io.highfidelity.hifiinterface.fragment.PolicyFragment;
|
||||
|
@ -36,7 +37,8 @@ import io.highfidelity.hifiinterface.task.DownloadProfileImageTask;
|
|||
|
||||
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener,
|
||||
LoginFragment.OnLoginInteractionListener,
|
||||
HomeFragment.OnHomeInteractionListener {
|
||||
HomeFragment.OnHomeInteractionListener,
|
||||
FriendsFragment.OnHomeInteractionListener {
|
||||
|
||||
private static final int PROFILE_PICTURE_PLACEHOLDER = R.drawable.default_profile_avatar;
|
||||
public static final String DEFAULT_FRAGMENT = "Home";
|
||||
|
@ -56,6 +58,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
private View mLoginPanel;
|
||||
private View mProfilePanel;
|
||||
private TextView mLogoutOption;
|
||||
private MenuItem mPeopleMenuItem;
|
||||
|
||||
private boolean backToScene;
|
||||
|
||||
|
@ -75,6 +78,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
mDisplayName = mNavigationView.getHeaderView(0).findViewById(R.id.displayName);
|
||||
mProfilePicture = mNavigationView.getHeaderView(0).findViewById(R.id.profilePicture);
|
||||
|
||||
mPeopleMenuItem = mNavigationView.getMenu().findItem(R.id.action_people);
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
toolbar.setTitleTextAppearance(this, R.style.HomeActionBarTitleStyle);
|
||||
setSupportActionBar(toolbar);
|
||||
|
@ -109,40 +114,69 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
loadLoginFragment();
|
||||
break;
|
||||
case "Home":
|
||||
loadHomeFragment();
|
||||
loadHomeFragment(true);
|
||||
break;
|
||||
case "Privacy Policy":
|
||||
loadPrivacyPolicyFragment();
|
||||
break;
|
||||
case "People":
|
||||
loadPeopleFragment();
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Unknown fragment " + fragment);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void loadHomeFragment() {
|
||||
private void loadHomeFragment(boolean addToBackStack) {
|
||||
Fragment fragment = HomeFragment.newInstance();
|
||||
loadFragment(fragment, getString(R.string.home), false);
|
||||
loadFragment(fragment, getString(R.string.home), getString(R.string.tagFragmentHome), addToBackStack);
|
||||
}
|
||||
|
||||
private void loadLoginFragment() {
|
||||
Fragment fragment = LoginFragment.newInstance();
|
||||
|
||||
loadFragment(fragment, getString(R.string.login), true);
|
||||
loadFragment(fragment, getString(R.string.login), getString(R.string.tagFragmentLogin), true);
|
||||
}
|
||||
|
||||
private void loadPrivacyPolicyFragment() {
|
||||
Fragment fragment = PolicyFragment.newInstance();
|
||||
|
||||
loadFragment(fragment, getString(R.string.privacyPolicy), true);
|
||||
loadFragment(fragment, getString(R.string.privacyPolicy), getString(R.string.tagFragmentPolicy), true);
|
||||
}
|
||||
|
||||
private void loadFragment(Fragment fragment, String title, boolean addToBackStack) {
|
||||
private void loadPeopleFragment() {
|
||||
Fragment fragment = FriendsFragment.newInstance();
|
||||
|
||||
loadFragment(fragment, getString(R.string.people), getString(R.string.tagFragmentPeople), true);
|
||||
}
|
||||
|
||||
private void loadFragment(Fragment fragment, String title, String tag, boolean addToBackStack) {
|
||||
FragmentManager fragmentManager = getFragmentManager();
|
||||
|
||||
// check if it's the same fragment
|
||||
String currentFragmentName = fragmentManager.getBackStackEntryCount() > 0
|
||||
? fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount() - 1).getName()
|
||||
: "";
|
||||
if (currentFragmentName.equals(title)) {
|
||||
mDrawerLayout.closeDrawer(mNavigationView);
|
||||
return; // cancel as we are already in that fragment
|
||||
}
|
||||
|
||||
// go back until first transaction
|
||||
int backStackEntryCount = fragmentManager.getBackStackEntryCount();
|
||||
for (int i = 0; i < backStackEntryCount - 1; i++) {
|
||||
fragmentManager.popBackStackImmediate();
|
||||
}
|
||||
|
||||
// this case is when we wanted to go home.. rollback already did that!
|
||||
// But asking for a new Home fragment makes it easier to have an updated list so we let it to continue
|
||||
|
||||
FragmentTransaction ft = fragmentManager.beginTransaction();
|
||||
ft.replace(R.id.content_frame, fragment);
|
||||
ft.replace(R.id.content_frame, fragment, tag);
|
||||
|
||||
if (addToBackStack) {
|
||||
ft.addToBackStack(null);
|
||||
ft.addToBackStack(title);
|
||||
}
|
||||
ft.commit();
|
||||
setTitle(title);
|
||||
|
@ -155,11 +189,13 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
mLoginPanel.setVisibility(View.GONE);
|
||||
mProfilePanel.setVisibility(View.VISIBLE);
|
||||
mLogoutOption.setVisibility(View.VISIBLE);
|
||||
mPeopleMenuItem.setVisible(true);
|
||||
updateProfileHeader();
|
||||
} else {
|
||||
mLoginPanel.setVisibility(View.VISIBLE);
|
||||
mProfilePanel.setVisibility(View.GONE);
|
||||
mLogoutOption.setVisibility(View.GONE);
|
||||
mPeopleMenuItem.setVisible(false);
|
||||
mDisplayName.setText("");
|
||||
}
|
||||
}
|
||||
|
@ -200,7 +236,10 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
|
||||
switch(item.getItemId()) {
|
||||
case R.id.action_home:
|
||||
loadHomeFragment();
|
||||
loadHomeFragment(false);
|
||||
return true;
|
||||
case R.id.action_people:
|
||||
loadPeopleFragment();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -219,6 +258,19 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
public void onLogoutClicked(View view) {
|
||||
nativeLogout();
|
||||
updateLoginMenu();
|
||||
exitLoggedInFragment();
|
||||
|
||||
}
|
||||
|
||||
private void exitLoggedInFragment() {
|
||||
// If we are in a "logged in" fragment (like People), go back to home. This could be expanded to multiple fragments
|
||||
FragmentManager fragmentManager = getFragmentManager();
|
||||
String currentFragmentName = fragmentManager.getBackStackEntryCount() > 0
|
||||
? fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount() - 1).getName()
|
||||
: "";
|
||||
if (currentFragmentName.equals(getString(R.string.people))) {
|
||||
loadHomeFragment(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void onSelectedDomain(String domainUrl) {
|
||||
|
@ -237,9 +289,17 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void goToUser(String username) {
|
||||
Intent intent = new Intent(this, InterfaceActivity.class);
|
||||
intent.putExtra(InterfaceActivity.EXTRA_GOTO_USERNAME, username);
|
||||
finish();
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoginCompleted() {
|
||||
loadHomeFragment();
|
||||
loadHomeFragment(false);
|
||||
updateLoginMenu();
|
||||
if (backToScene) {
|
||||
backToScene = false;
|
||||
|
@ -266,6 +326,11 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
loadPrivacyPolicyFragment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVisitUserSelected(String username) {
|
||||
goToUser(username);
|
||||
}
|
||||
|
||||
private class RoundProfilePictureCallback implements Callback {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
|
@ -284,15 +349,30 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
int index = getFragmentManager().getBackStackEntryCount() - 1;
|
||||
if (index > -1) {
|
||||
// if a fragment needs to internally manage back presses..
|
||||
FragmentManager fm = getFragmentManager();
|
||||
Log.d("[BACK]", "getBackStackEntryCount " + fm.getBackStackEntryCount());
|
||||
Fragment friendsFragment = fm.findFragmentByTag(getString(R.string.tagFragmentPeople));
|
||||
if (friendsFragment != null && friendsFragment instanceof FriendsFragment) {
|
||||
if (((FriendsFragment) friendsFragment).onBackPressed()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int index = fm.getBackStackEntryCount() - 1;
|
||||
|
||||
if (index > 0) {
|
||||
super.onBackPressed();
|
||||
index--;
|
||||
if (index > -1) {
|
||||
setTitle(fm.getBackStackEntryAt(index).getName());
|
||||
}
|
||||
if (backToScene) {
|
||||
backToScene = false;
|
||||
goToLastLocation();
|
||||
}
|
||||
} else {
|
||||
finishAffinity();
|
||||
finishAffinity();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
package io.highfidelity.hifiinterface.fragment;
|
||||
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.widget.GridLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.sothree.slidinguppanel.SlidingUpPanelLayout;
|
||||
|
||||
import io.highfidelity.hifiinterface.R;
|
||||
import io.highfidelity.hifiinterface.provider.EndpointUsersProvider;
|
||||
import io.highfidelity.hifiinterface.provider.UsersProvider;
|
||||
import io.highfidelity.hifiinterface.view.UserListAdapter;
|
||||
|
||||
public class FriendsFragment extends Fragment {
|
||||
|
||||
public native boolean nativeIsLoggedIn();
|
||||
|
||||
public native String nativeGetAccessToken();
|
||||
|
||||
private RecyclerView mUsersView;
|
||||
private View mUserActions;
|
||||
private UserListAdapter mUsersAdapter;
|
||||
private SlidingUpPanelLayout mSlidingUpPanelLayout;
|
||||
private EndpointUsersProvider mUsersProvider;
|
||||
private String mSelectedUsername;
|
||||
|
||||
private OnHomeInteractionListener mListener;
|
||||
private SwipeRefreshLayout mSwipeRefreshLayout;
|
||||
|
||||
public FriendsFragment() {
|
||||
// Required empty public constructor
|
||||
}
|
||||
|
||||
public static FriendsFragment newInstance() {
|
||||
FriendsFragment fragment = new FriendsFragment();
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View rootView = inflater.inflate(R.layout.fragment_friends, container, false);
|
||||
|
||||
String accessToken = nativeGetAccessToken();
|
||||
mUsersProvider = new EndpointUsersProvider(accessToken);
|
||||
|
||||
Log.d("[USERS]", "token : [" + accessToken + "]");
|
||||
|
||||
mSwipeRefreshLayout = rootView.findViewById(R.id.swipeRefreshLayout);
|
||||
|
||||
mUsersView = rootView.findViewById(R.id.rvUsers);
|
||||
int numberOfColumns = 1;
|
||||
GridLayoutManager gridLayoutMgr = new GridLayoutManager(getContext(), numberOfColumns);
|
||||
mUsersView.setLayoutManager(gridLayoutMgr);
|
||||
|
||||
mUsersAdapter = new UserListAdapter(getContext(), mUsersProvider);
|
||||
mSwipeRefreshLayout.setRefreshing(true);
|
||||
|
||||
mUserActions = rootView.findViewById(R.id.userActionsLayout);
|
||||
|
||||
mSlidingUpPanelLayout = rootView.findViewById(R.id.sliding_layout);
|
||||
mSlidingUpPanelLayout.setPanelHeight(0);
|
||||
|
||||
rootView.findViewById(R.id.userActionDelete).setOnClickListener(view -> onRemoveConnectionClick());
|
||||
|
||||
rootView.findViewById(R.id.userActionVisit).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (mListener != null && mSelectedUsername != null) {
|
||||
mListener.onVisitUserSelected(mSelectedUsername);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mUsersAdapter.setClickListener(new UserListAdapter.ItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(View view, int position, UserListAdapter.User user) {
|
||||
// 1. 'select' user
|
||||
mSelectedUsername = user.name;
|
||||
// ..
|
||||
// 2. adapt options
|
||||
// ..
|
||||
rootView.findViewById(R.id.userActionVisit).setVisibility(user.online ? View.VISIBLE : View.GONE);
|
||||
// 3. show
|
||||
mSlidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.EXPANDED);
|
||||
}
|
||||
});
|
||||
|
||||
mUsersAdapter.setListener(new UserListAdapter.AdapterListener() {
|
||||
@Override
|
||||
public void onEmptyAdapter() {
|
||||
mSwipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNonEmptyAdapter() {
|
||||
mSwipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e, String message) {
|
||||
mSwipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
});
|
||||
|
||||
mUsersView.setAdapter(mUsersAdapter);
|
||||
|
||||
mSlidingUpPanelLayout.setFadeOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
mSlidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
|
||||
mSelectedUsername = null;
|
||||
}
|
||||
});
|
||||
|
||||
mSwipeRefreshLayout.setOnRefreshListener(() -> mUsersAdapter.loadUsers());
|
||||
|
||||
return rootView;
|
||||
}
|
||||
|
||||
private void onRemoveConnectionClick() {
|
||||
if (mSelectedUsername == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
builder.setMessage("Remove '" + mSelectedUsername + "' from People?");
|
||||
builder.setPositiveButton("Remove", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
mUsersProvider.removeConnection(mSelectedUsername, new UsersProvider.UserActionCallback() {
|
||||
@Override
|
||||
public void requestOk() {
|
||||
mSlidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
|
||||
mSelectedUsername = null;
|
||||
mUsersAdapter.loadUsers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestError(Exception e, String message) {
|
||||
// CLD: Show error message?
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
// Cancelled, nothing to do
|
||||
}
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the back pressed event and returns true if it was managed by this Fragment
|
||||
* @return
|
||||
*/
|
||||
public boolean onBackPressed() {
|
||||
if (mSlidingUpPanelLayout.getPanelState().equals(SlidingUpPanelLayout.PanelState.EXPANDED)) {
|
||||
mSlidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
|
||||
mSelectedUsername = null;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof OnHomeInteractionListener) {
|
||||
mListener = (OnHomeInteractionListener) context;
|
||||
} else {
|
||||
throw new RuntimeException(context.toString()
|
||||
+ " must implement OnHomeInteractionListener");
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnHomeInteractionListener {
|
||||
void onVisitUserSelected(String username);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,225 @@
|
|||
package io.highfidelity.hifiinterface.provider;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.highfidelity.hifiinterface.view.UserListAdapter;
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Retrofit;
|
||||
import retrofit2.converter.gson.GsonConverterFactory;
|
||||
import retrofit2.http.Body;
|
||||
import retrofit2.http.DELETE;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.POST;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
/**
|
||||
* Created by cduarte on 6/13/18.
|
||||
*/
|
||||
|
||||
public class EndpointUsersProvider implements UsersProvider {
|
||||
|
||||
public static final String BASE_URL = "https://metaverse.highfidelity.com/";
|
||||
private final Retrofit mRetrofit;
|
||||
private final EndpointUsersProviderService mEndpointUsersProviderService;
|
||||
|
||||
public EndpointUsersProvider(String accessToken) {
|
||||
mRetrofit = createAuthorizedRetrofit(accessToken);
|
||||
mEndpointUsersProviderService = mRetrofit.create(EndpointUsersProviderService.class);
|
||||
}
|
||||
|
||||
private Retrofit createAuthorizedRetrofit(String accessToken) {
|
||||
Retrofit mRetrofit;
|
||||
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
|
||||
httpClient.addInterceptor(new Interceptor() {
|
||||
@Override
|
||||
public Response intercept(Chain chain) throws IOException {
|
||||
Request original = chain.request();
|
||||
|
||||
Request request = original.newBuilder()
|
||||
.header("Authorization", "Bearer " + accessToken)
|
||||
.method(original.method(), original.body())
|
||||
.build();
|
||||
|
||||
return chain.proceed(request);
|
||||
}
|
||||
});
|
||||
|
||||
OkHttpClient client = httpClient.build();
|
||||
|
||||
mRetrofit = new Retrofit.Builder()
|
||||
.baseUrl(BASE_URL)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.client(client)
|
||||
.build();
|
||||
return mRetrofit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void retrieve(UsersCallback usersCallback) {
|
||||
Call<UsersResponse> friendsCall = mEndpointUsersProviderService.getUsers(
|
||||
CONNECTION_FILTER_CONNECTIONS,
|
||||
400,
|
||||
null);
|
||||
friendsCall.enqueue(new Callback<UsersResponse>() {
|
||||
@Override
|
||||
public void onResponse(Call<UsersResponse> call, retrofit2.Response<UsersResponse> response) {
|
||||
if (!response.isSuccessful()) {
|
||||
usersCallback.retrieveError(new Exception("Error calling Users API"), "Error calling Users API");
|
||||
return;
|
||||
}
|
||||
UsersResponse usersResponse = response.body();
|
||||
List<UserListAdapter.User> adapterUsers = new ArrayList<>(usersResponse.total_entries);
|
||||
for (User user : usersResponse.data.users) {
|
||||
UserListAdapter.User adapterUser = new UserListAdapter.User();
|
||||
adapterUser.connection = user.connection;
|
||||
adapterUser.imageUrl = user.images.thumbnail;
|
||||
adapterUser.name = user.username;
|
||||
adapterUser.online = user.online;
|
||||
adapterUser.locationName = (user.location != null ?
|
||||
(user.location.root != null ? user.location.root.name :
|
||||
(user.location.domain != null ? user.location.domain.name : ""))
|
||||
: "");
|
||||
adapterUsers.add(adapterUser);
|
||||
}
|
||||
usersCallback.retrieveOk(adapterUsers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<UsersResponse> call, Throwable t) {
|
||||
usersCallback.retrieveError(new Exception(t), "Error calling Users API");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public class UserActionRetrofitCallback implements Callback<UsersResponse> {
|
||||
|
||||
UserActionCallback callback;
|
||||
|
||||
public UserActionRetrofitCallback(UserActionCallback callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Call<UsersResponse> call, retrofit2.Response<UsersResponse> response) {
|
||||
if (!response.isSuccessful()) {
|
||||
callback.requestError(new Exception("Error with "
|
||||
+ call.request().url().toString() + " "
|
||||
+ call.request().method() + " call " + response.message()),
|
||||
response.message());
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.body() == null || !"success".equals(response.body().status)) {
|
||||
callback.requestError(new Exception("Error with "
|
||||
+ call.request().url().toString() + " "
|
||||
+ call.request().method() + " call " + response.message()),
|
||||
response.message());
|
||||
return;
|
||||
}
|
||||
callback.requestOk();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<UsersResponse> call, Throwable t) {
|
||||
callback.requestError(new Exception(t), t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addFriend(String friendUserName, UserActionCallback callback) {
|
||||
Call<UsersResponse> friendCall = mEndpointUsersProviderService.addFriend(new BodyAddFriend(friendUserName));
|
||||
friendCall.enqueue(new UserActionRetrofitCallback(callback));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFriend(String friendUserName, UserActionCallback callback) {
|
||||
Call<UsersResponse> friendCall = mEndpointUsersProviderService.removeFriend(friendUserName);
|
||||
friendCall.enqueue(new UserActionRetrofitCallback(callback));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeConnection(String connectionUserName, UserActionCallback callback) {
|
||||
Call<UsersResponse> connectionCall = mEndpointUsersProviderService.removeConnection(connectionUserName);
|
||||
connectionCall.enqueue(new UserActionRetrofitCallback(callback));
|
||||
}
|
||||
|
||||
public interface EndpointUsersProviderService {
|
||||
@GET("api/v1/users")
|
||||
Call<UsersResponse> getUsers(@Query("filter") String filter,
|
||||
@Query("per_page") int perPage,
|
||||
@Query("online") Boolean online);
|
||||
|
||||
@DELETE("api/v1/user/connections/{connectionUserName}")
|
||||
Call<UsersResponse> removeConnection(@Path("connectionUserName") String connectionUserName);
|
||||
|
||||
@DELETE("api/v1/user/friends/{friendUserName}")
|
||||
Call<UsersResponse> removeFriend(@Path("friendUserName") String friendUserName);
|
||||
|
||||
@POST("api/v1/user/friends")
|
||||
Call<UsersResponse> addFriend(@Body BodyAddFriend friendUserName);
|
||||
|
||||
/* response
|
||||
{
|
||||
"status": "success"
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
class BodyAddFriend {
|
||||
String username;
|
||||
public BodyAddFriend(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
}
|
||||
|
||||
class UsersResponse {
|
||||
public UsersResponse() {}
|
||||
String status;
|
||||
int current_page;
|
||||
int total_pages;
|
||||
int per_page;
|
||||
int total_entries;
|
||||
Data data;
|
||||
}
|
||||
|
||||
class Data {
|
||||
public Data() {}
|
||||
List<User> users;
|
||||
}
|
||||
|
||||
class User {
|
||||
public User() {}
|
||||
String username;
|
||||
boolean online;
|
||||
String connection;
|
||||
Images images;
|
||||
LocationData location;
|
||||
}
|
||||
|
||||
class Images {
|
||||
public Images() {}
|
||||
String hero;
|
||||
String thumbnail;
|
||||
String tiny;
|
||||
}
|
||||
|
||||
class LocationData {
|
||||
public LocationData() {}
|
||||
NameContainer root;
|
||||
NameContainer domain;
|
||||
}
|
||||
class NameContainer {
|
||||
String name;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package io.highfidelity.hifiinterface.provider;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.highfidelity.hifiinterface.view.UserListAdapter;
|
||||
|
||||
/**
|
||||
* Created by cduarte on 6/13/18.
|
||||
*/
|
||||
|
||||
public interface UsersProvider {
|
||||
|
||||
public static String CONNECTION_TYPE_FRIEND = "friend";
|
||||
public static String CONNECTION_FILTER_CONNECTIONS = "connections";
|
||||
|
||||
void retrieve(UsersProvider.UsersCallback usersCallback);
|
||||
|
||||
interface UsersCallback {
|
||||
void retrieveOk(List<UserListAdapter.User> users);
|
||||
void retrieveError(Exception e, String message);
|
||||
}
|
||||
|
||||
|
||||
void addFriend(String friendUserName, UserActionCallback callback);
|
||||
|
||||
void removeFriend(String friendUserName, UserActionCallback callback);
|
||||
|
||||
void removeConnection(String connectionUserName, UserActionCallback callback);
|
||||
|
||||
interface UserActionCallback {
|
||||
void requestOk();
|
||||
void requestError(Exception e, String message);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,247 @@
|
|||
package io.highfidelity.hifiinterface.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.net.Uri;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
|
||||
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.squareup.picasso.Callback;
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.highfidelity.hifiinterface.R;
|
||||
import io.highfidelity.hifiinterface.provider.UsersProvider;
|
||||
|
||||
/**
|
||||
* Created by cduarte on 6/13/18.
|
||||
*/
|
||||
|
||||
public class UserListAdapter extends RecyclerView.Adapter<UserListAdapter.ViewHolder> {
|
||||
|
||||
private UsersProvider mProvider;
|
||||
private LayoutInflater mInflater;
|
||||
private Context mContext;
|
||||
private List<User> mUsers = new ArrayList<>();
|
||||
private ItemClickListener mClickListener;
|
||||
private AdapterListener mAdapterListener;
|
||||
|
||||
public UserListAdapter(Context c, UsersProvider usersProvider) {
|
||||
mContext = c;
|
||||
mInflater = LayoutInflater.from(mContext);
|
||||
mProvider = usersProvider;
|
||||
loadUsers();
|
||||
}
|
||||
|
||||
public void setListener(AdapterListener adapterListener) {
|
||||
mAdapterListener = adapterListener;
|
||||
}
|
||||
|
||||
public void loadUsers() {
|
||||
mProvider.retrieve(new UsersProvider.UsersCallback() {
|
||||
@Override
|
||||
public void retrieveOk(List<User> users) {
|
||||
mUsers = new ArrayList<>(users);
|
||||
notifyDataSetChanged();
|
||||
if (mAdapterListener != null) {
|
||||
if (mUsers.isEmpty()) {
|
||||
mAdapterListener.onEmptyAdapter();
|
||||
} else {
|
||||
mAdapterListener.onNonEmptyAdapter();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void retrieveError(Exception e, String message) {
|
||||
Log.e("[USERS]", message, e);
|
||||
if (mAdapterListener != null) {
|
||||
mAdapterListener.onError(e, message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserListAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = mInflater.inflate(R.layout.user_item, parent, false);
|
||||
return new UserListAdapter.ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(UserListAdapter.ViewHolder holder, int position) {
|
||||
User aUser = mUsers.get(position);
|
||||
holder.mUsername.setText(aUser.name);
|
||||
|
||||
holder.mOnlineInfo.setVisibility(aUser.online? View.VISIBLE : View.GONE);
|
||||
holder.mLocation.setText("- " + aUser.locationName); // Bring info from the API and use it here
|
||||
|
||||
holder.mFriendStar.onBindSet(aUser.name, aUser.connection.equals(UsersProvider.CONNECTION_TYPE_FRIEND));
|
||||
Uri uri = Uri.parse(aUser.imageUrl);
|
||||
Picasso.get().load(uri).into(holder.mImage, new RoundProfilePictureCallback(holder.mImage));
|
||||
}
|
||||
|
||||
private class RoundProfilePictureCallback implements Callback {
|
||||
private ImageView mProfilePicture;
|
||||
public RoundProfilePictureCallback(ImageView imageView) {
|
||||
mProfilePicture = imageView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
Bitmap imageBitmap = ((BitmapDrawable) mProfilePicture.getDrawable()).getBitmap();
|
||||
RoundedBitmapDrawable imageDrawable = RoundedBitmapDrawableFactory.create(mProfilePicture.getContext().getResources(), imageBitmap);
|
||||
imageDrawable.setCircular(true);
|
||||
imageDrawable.setCornerRadius(Math.max(imageBitmap.getWidth(), imageBitmap.getHeight()) / 2.0f);
|
||||
mProfilePicture.setImageDrawable(imageDrawable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
mProfilePicture.setImageResource(R.drawable.default_profile_avatar);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mUsers.size();
|
||||
}
|
||||
|
||||
public class ToggleWrapper {
|
||||
|
||||
private ViewGroup mFrame;
|
||||
private ImageView mImage;
|
||||
private boolean mChecked = false;
|
||||
private String mUsername;
|
||||
private boolean waitingChangeConfirm = false;
|
||||
|
||||
public ToggleWrapper(ViewGroup toggleFrame) {
|
||||
mFrame = toggleFrame;
|
||||
mImage = toggleFrame.findViewById(R.id.userFavImage);
|
||||
mFrame.setOnClickListener(view -> toggle());
|
||||
}
|
||||
|
||||
private void refreshUI() {
|
||||
mImage.setColorFilter(ContextCompat.getColor(mImage.getContext(),
|
||||
mChecked ? R.color.starSelectedTint : R.color.starUnselectedTint));
|
||||
}
|
||||
|
||||
class RollbackUICallback implements UsersProvider.UserActionCallback {
|
||||
|
||||
boolean previousStatus;
|
||||
|
||||
RollbackUICallback(boolean previousStatus) {
|
||||
this.previousStatus = previousStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestOk() {
|
||||
if (!waitingChangeConfirm) {
|
||||
return;
|
||||
}
|
||||
mFrame.setClickable(true);
|
||||
// nothing to do, new status was set
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestError(Exception e, String message) {
|
||||
if (!waitingChangeConfirm) {
|
||||
return;
|
||||
}
|
||||
// new status was not set, rolling back
|
||||
mChecked = previousStatus;
|
||||
mFrame.setClickable(true);
|
||||
refreshUI();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void toggle() {
|
||||
// TODO API CALL TO CHANGE
|
||||
final boolean previousStatus = mChecked;
|
||||
mChecked = !mChecked;
|
||||
mFrame.setClickable(false);
|
||||
refreshUI();
|
||||
waitingChangeConfirm = true;
|
||||
if (mChecked) {
|
||||
mProvider.addFriend(mUsername, new RollbackUICallback(previousStatus));
|
||||
} else {
|
||||
mProvider.removeFriend(mUsername, new RollbackUICallback(previousStatus));
|
||||
}
|
||||
}
|
||||
|
||||
protected void onBindSet(String username, boolean checked) {
|
||||
mChecked = checked;
|
||||
mUsername = username;
|
||||
waitingChangeConfirm = false;
|
||||
mFrame.setClickable(true);
|
||||
refreshUI();
|
||||
}
|
||||
}
|
||||
|
||||
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
|
||||
|
||||
TextView mUsername;
|
||||
TextView mOnline;
|
||||
View mOnlineInfo;
|
||||
TextView mLocation;
|
||||
ImageView mImage;
|
||||
ToggleWrapper mFriendStar;
|
||||
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
mUsername = itemView.findViewById(R.id.userName);
|
||||
mOnline = itemView.findViewById(R.id.userOnline);
|
||||
mImage = itemView.findViewById(R.id.userImage);
|
||||
mOnlineInfo = itemView.findViewById(R.id.userOnlineInfo);
|
||||
mLocation = itemView.findViewById(R.id.userLocation);
|
||||
mFriendStar = new ToggleWrapper(itemView.findViewById(R.id.userFav));
|
||||
itemView.setOnClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
int position = getAdapterPosition();
|
||||
if (mClickListener != null) {
|
||||
mClickListener.onItemClick(view, position, mUsers.get(position));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// allows clicks events to be caught
|
||||
public void setClickListener(ItemClickListener itemClickListener) {
|
||||
this.mClickListener = itemClickListener;
|
||||
}
|
||||
|
||||
public interface ItemClickListener {
|
||||
void onItemClick(View view, int position, User user);
|
||||
}
|
||||
|
||||
public static class User {
|
||||
public String name;
|
||||
public String imageUrl;
|
||||
public String connection;
|
||||
public boolean online;
|
||||
|
||||
public String locationName;
|
||||
|
||||
public User() {}
|
||||
}
|
||||
|
||||
public interface AdapterListener {
|
||||
void onEmptyAdapter();
|
||||
void onNonEmptyAdapter();
|
||||
void onError(Exception e, String message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
|
||||
</vector>
|
4
android/app/src/main/res/drawable/ic_star.xml
Normal file
4
android/app/src/main/res/drawable/ic_star.xml
Normal file
|
@ -0,0 +1,4 @@
|
|||
<vector android:height="31dp" android:viewportHeight="25.0"
|
||||
android:viewportWidth="27.0" android:width="31dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FBD92A" android:pathData="M12.549,0.927C12.848,0.006 14.152,0.006 14.451,0.927L16.756,8.019C16.889,8.431 17.273,8.71 17.706,8.71H25.164C26.132,8.71 26.535,9.95 25.751,10.519L19.719,14.903C19.368,15.157 19.221,15.608 19.355,16.021L21.66,23.113C21.959,24.034 20.904,24.8 20.121,24.231L14.088,19.847C13.737,19.593 13.263,19.593 12.912,19.847L6.879,24.231C6.096,24.8 5.041,24.034 5.34,23.113L7.645,16.021C7.779,15.608 7.632,15.157 7.282,14.903L1.249,10.519C0.465,9.95 0.868,8.71 1.836,8.71H9.293C9.727,8.71 10.111,8.431 10.245,8.019L12.549,0.927Z"/>
|
||||
</vector>
|
31
android/app/src/main/res/drawable/ic_teleporticon.xml
Normal file
31
android/app/src/main/res/drawable/ic_teleporticon.xml
Normal file
|
@ -0,0 +1,31 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M0.6,3h22.8v18.7h-22.8z"
|
||||
android:fillAlpha="0"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M0.6,3h16.3v18.7h-16.3z"
|
||||
android:fillAlpha="0"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M0.6,3h16.3v18.7h-16.3z"
|
||||
android:fillAlpha="0"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M13.8,9.9h9.6v7.8h-9.6z"
|
||||
android:fillAlpha="0"/>
|
||||
<path
|
||||
android:pathData="M11.9,16.9c-0.2,-0.9 -0.3,-2.3 -0.4,-3.4c-0.1,-0.7 -0.1,-1.3 -0.2,-1.7c0,-0.1 -0.1,-0.3 0.3,-0.4c0.1,0 0.1,0 0.2,-0.1l4.4,-1.7c0.3,-0.1 0.5,-0.4 0.6,-0.7c0.1,-0.3 0.1,-0.7 -0.2,-0.9L16.6,8c-0.2,-0.2 -0.5,-0.3 -0.8,-0.3c-0.1,0 -4.8,0.7 -6.8,0.7c-0.1,0 -0.1,0 -0.1,0c-2,0 -6.9,-0.8 -7,-0.8c-0.4,-0.1 -0.8,0.1 -1,0.4L0.7,8.3C0.6,8.5 0.6,8.8 0.6,9.1c0.1,0.3 0.3,0.5 0.5,0.6C2,10 5,11.2 5.9,11.3c0.2,0 0.4,0.1 0.5,0.6c0.1,0.6 -0.2,3.6 -0.6,5c-0.4,1.4 -1,3.2 -1,3.2c-0.2,0.5 0.1,1 0.6,1.2l0.6,0.2c0.2,0.1 0.5,0.1 0.7,-0.1c0.2,-0.1 0.4,-0.3 0.5,-0.6l1.7,-5l1.6,5.1c0.1,0.3 0.3,0.5 0.5,0.6c0.1,0.1 0.3,0.1 0.4,0.1c0.1,0 0.2,0 0.3,-0.1l0.5,-0.2c0.4,-0.2 0.7,-0.6 0.6,-1.1C12.8,20.3 12.3,18.5 11.9,16.9z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
<path
|
||||
android:pathData="M8.9,7.5c1.3,0 2.3,-1 2.3,-2.3S10.2,3 8.9,3S6.6,4 6.6,5.3S7.7,7.5 8.9,7.5z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
<path
|
||||
android:pathData="M23,13.4L22.6,13c0,0 0,0 0,0l-2.9,-2.8c-0.2,-0.2 -0.5,-0.2 -0.7,0l-0.7,0.7c-0.2,0.2 -0.2,0.5 0,0.7l1.2,1.2h-5.2c-0.3,0 -0.5,0.2 -0.5,0.5v0.9c0,0.3 0.2,0.5 0.5,0.5h5.1l-1.2,1.1c-0.2,0.2 -0.2,0.5 0,0.7l0.7,0.7c0.2,0.2 0.5,0.2 0.7,0l3.3,-3.2C23.2,13.9 23.2,13.6 23,13.4z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
</vector>
|
88
android/app/src/main/res/layout/fragment_friends.xml
Normal file
88
android/app/src/main/res/layout/fragment_friends.xml
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.sothree.slidinguppanel.SlidingUpPanelLayout
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/sliding_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="bottom"
|
||||
app:umanoFadeColor="@color/slidingUpPanelFadeColor"
|
||||
app:umanoShadowHeight="4dp"
|
||||
android:background="@color/backgroundLight">
|
||||
|
||||
<android.support.v4.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/rvUsers"
|
||||
android:paddingTop="@dimen/list_vertical_padding"
|
||||
android:paddingBottom="@dimen/list_vertical_padding"
|
||||
android:clipToPadding="false"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</android.support.v4.widget.SwipeRefreshLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/userActionsLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="270dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/backgroundDark">
|
||||
|
||||
<android.support.constraint.ConstraintLayout
|
||||
android:id="@+id/userActionVisit"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<ImageView android:id="@+id/userActionVisitIcon"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||
android:src="@drawable/ic_teleporticon"
|
||||
android:tint="@color/white_opaque" />
|
||||
<TextView android:id="@+id/userActionVisitText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Visit In-World"
|
||||
android:fontFamily="@font/raleway"
|
||||
android:textColor="@color/white_opaque"
|
||||
app:layout_constraintStart_toEndOf="@id/userActionVisitIcon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginStart="32dp" />
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
<android.support.constraint.ConstraintLayout
|
||||
android:id="@+id/userActionDelete"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<ImageView android:id="@+id/userActionDeleteIcon"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||
android:src="@drawable/ic_delete_black_24dp"
|
||||
android:tint="@color/white_opaque" />
|
||||
<TextView android:id="@+id/userActionDeleteText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Remove from People"
|
||||
android:fontFamily="@font/raleway"
|
||||
android:textColor="@color/white_opaque"
|
||||
app:layout_constraintStart_toEndOf="@id/userActionDeleteIcon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginStart="32dp" />
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
</LinearLayout>
|
||||
</com.sothree.slidinguppanel.SlidingUpPanelLayout>
|
69
android/app/src/main/res/layout/user_item.xml
Normal file
69
android/app/src/main/res/layout/user_item.xml
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/userImage"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginStart="@dimen/activity_horizontal_margin"
|
||||
app:layout_constraintStart_toEndOf="@id/userImage"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:id="@+id/userName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/raleway"
|
||||
android:textColor="@color/menuOption"/>
|
||||
<LinearLayout android:id="@+id/userOnlineInfo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView
|
||||
android:id="@+id/userOnline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/online"
|
||||
android:fontFamily="@font/raleway"
|
||||
android:textColor="@color/hifiAquamarine" />
|
||||
<TextView
|
||||
android:id="@+id/userLocation"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="3dp"
|
||||
android:fontFamily="@font/raleway_italic"
|
||||
android:textColor="@color/menuOption"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
<RelativeLayout android:id="@+id/userFav"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginEnd="5.5dp">
|
||||
<ImageView android:id="@+id/userFavImage"
|
||||
android:layout_width="27dp"
|
||||
android:layout_height="27dp"
|
||||
android:src="@drawable/ic_star"
|
||||
android:tint="@color/starUnselectedTint"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_marginEnd="0dp" />
|
||||
</RelativeLayout>
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
|
@ -5,4 +5,8 @@
|
|||
android:id="@+id/action_home"
|
||||
android:title="@string/home"
|
||||
/>
|
||||
<item
|
||||
android:id="@+id/action_people"
|
||||
android:title="@string/people"
|
||||
/>
|
||||
</menu>
|
||||
|
|
|
@ -18,4 +18,8 @@
|
|||
<color name="black_060">#99000000</color>
|
||||
<color name="statusbar_color">#292929</color>
|
||||
<color name="hifiLogoColor">#23B2E7</color>
|
||||
<color name="hifiAquamarine">#62D5C6</color>
|
||||
<color name="starSelectedTint">#FBD92A</color>
|
||||
<color name="starUnselectedTint">#8A8A8A</color>
|
||||
<color name="slidingUpPanelFadeColor">#40000000</color>
|
||||
</resources>
|
||||
|
|
|
@ -37,4 +37,6 @@
|
|||
<dimen name="header_hifi_height">101dp</dimen>
|
||||
<dimen name="header_hifi_width">425dp</dimen>
|
||||
|
||||
<dimen name="list_vertical_padding">8dp</dimen>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<resources>
|
||||
<string name="app_name" translatable="false">Interface</string>
|
||||
<string name="home">Home</string>
|
||||
<string name="people">People</string>
|
||||
<string name="web_view_action_open_in_browser" translatable="false">Open in browser</string>
|
||||
<string name="web_view_action_share" translatable="false">Share link</string>
|
||||
<string name="web_view_action_share_subject" translatable="false">Shared a link</string>
|
||||
|
@ -21,5 +22,11 @@
|
|||
<string name="search_no_results">No places exist with that name</string>
|
||||
<string name="privacyPolicy">Privacy Policy</string>
|
||||
<string name="your_last_location">Your Last Location</string>
|
||||
<string name="online">Online</string>
|
||||
|
||||
<!-- tags -->
|
||||
<string name="tagFragmentHome">tagFragmentHome</string>
|
||||
<string name="tagFragmentLogin">tagFragmentLogin</string>
|
||||
<string name="tagFragmentPolicy">tagFragmentPolicy</string>
|
||||
<string name="tagFragmentPeople">tagFragmentPeople</string>
|
||||
</resources>
|
||||
|
|
|
@ -28,6 +28,7 @@ allprojects {
|
|||
repositories {
|
||||
jcenter()
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue