From 440ba7a6de63779422944ed7933e1da5e8448c27 Mon Sep 17 00:00:00 2001 From: Cristian Duarte Date: Tue, 12 Jun 2018 19:46:59 -0300 Subject: [PATCH 01/30] Add a friends fragment and expose the access token to java --- android/app/src/main/cpp/native.cpp | 11 +++++ .../hifiinterface/MainActivity.java | 13 ++++++ .../fragment/FriendsFragment.java | 40 +++++++++++++++++++ .../src/main/res/layout/fragment_friends.xml | 9 +++++ .../app/src/main/res/menu/menu_navigation.xml | 4 ++ android/app/src/main/res/values/strings.xml | 1 + 6 files changed, 78 insertions(+) create mode 100644 android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java create mode 100644 android/app/src/main/res/layout/fragment_friends.xml diff --git a/android/app/src/main/cpp/native.cpp b/android/app/src/main/cpp/native.cpp index 437505be3f..3e3fb8f0b5 100644 --- a/android/app/src/main/cpp/native.cpp +++ b/android/app/src/main/cpp/native.cpp @@ -252,6 +252,17 @@ 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) { + return AndroidHelper::instance().getAccountManager()->isLoggedIn(); +} + +JNIEXPORT jstring JNICALL +Java_io_highfidelity_hifiinterface_fragment_FriendsFragment_nativeGetAccessToken(JNIEnv *env, jobject instance) { + auto accountManager = AndroidHelper::instance().getAccountManager(); + return env->NewStringUTF(accountManager->getAccountInfo().getAccessToken().token.toLatin1().data()); +} + JNIEXPORT void JNICALL Java_io_highfidelity_hifiinterface_SplashActivity_registerLoadCompleteListener(JNIEnv *env, jobject instance) { diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java index 54161f60c6..8bb84168a5 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java @@ -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; @@ -114,6 +115,9 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On case "Privacy Policy": loadPrivacyPolicyFragment(); break; + case "Friends": + loadFriendsFragment(); + break; default: Log.e(TAG, "Unknown fragment " + fragment); } @@ -137,6 +141,12 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On loadFragment(fragment, getString(R.string.privacyPolicy), true); } + private void loadFriendsFragment() { + Fragment fragment = FriendsFragment.newInstance(); + + loadFragment(fragment, getString(R.string.friends), true); + } + private void loadFragment(Fragment fragment, String title, boolean addToBackStack) { FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction ft = fragmentManager.beginTransaction(); @@ -202,6 +212,9 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On case R.id.action_home: loadHomeFragment(); return true; + case R.id.action_friends: + loadFriendsFragment(); + return true; } return false; } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java new file mode 100644 index 0000000000..64c02505cb --- /dev/null +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java @@ -0,0 +1,40 @@ +package io.highfidelity.hifiinterface.fragment; + + +import android.app.Fragment; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import io.highfidelity.hifiinterface.R; + +public class FriendsFragment extends Fragment { + + public native boolean nativeIsLoggedIn(); + + public native String nativeGetAccessToken(); + + 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(); + + Log.d("[TOKENX]", "token : [" + accessToken + "]"); + + return rootView; + } + +} diff --git a/android/app/src/main/res/layout/fragment_friends.xml b/android/app/src/main/res/layout/fragment_friends.xml new file mode 100644 index 0000000000..347d7ffe24 --- /dev/null +++ b/android/app/src/main/res/layout/fragment_friends.xml @@ -0,0 +1,9 @@ + + + + diff --git a/android/app/src/main/res/menu/menu_navigation.xml b/android/app/src/main/res/menu/menu_navigation.xml index cf80c84177..de496bc4cb 100644 --- a/android/app/src/main/res/menu/menu_navigation.xml +++ b/android/app/src/main/res/menu/menu_navigation.xml @@ -5,4 +5,8 @@ android:id="@+id/action_home" android:title="@string/home" /> + diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 4f5f29e671..e29252c2c0 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -1,6 +1,7 @@ Interface Home + Friends Open in browser Share link Shared a link From 248be87fa3bb69c4e704f8df3b47dbe426d4a985 Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Thu, 14 Jun 2018 15:19:21 -0300 Subject: [PATCH 02/30] Android - First list of friends implementation in List of Friends --- .../fragment/FriendsFragment.java | 15 +- .../provider/EndpointUsersProvider.java | 130 ++++++++++++++++++ .../hifiinterface/provider/UsersProvider.java | 20 +++ .../hifiinterface/view/UserListAdapter.java | 97 +++++++++++++ .../src/main/res/layout/fragment_friends.xml | 9 ++ android/app/src/main/res/layout/user_item.xml | 26 ++++ 6 files changed, 296 insertions(+), 1 deletion(-) create mode 100644 android/app/src/main/java/io/highfidelity/hifiinterface/provider/EndpointUsersProvider.java create mode 100644 android/app/src/main/java/io/highfidelity/hifiinterface/provider/UsersProvider.java create mode 100644 android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java create mode 100644 android/app/src/main/res/layout/user_item.xml diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java index 64c02505cb..e70ecfbc57 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java @@ -3,12 +3,15 @@ package io.highfidelity.hifiinterface.fragment; import android.app.Fragment; import android.os.Bundle; +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 io.highfidelity.hifiinterface.R; +import io.highfidelity.hifiinterface.view.UserListAdapter; public class FriendsFragment extends Fragment { @@ -16,6 +19,9 @@ public class FriendsFragment extends Fragment { public native String nativeGetAccessToken(); + private RecyclerView mUsersView; + private UserListAdapter mUsersAdapter; + public FriendsFragment() { // Required empty public constructor } @@ -32,7 +38,14 @@ public class FriendsFragment extends Fragment { String accessToken = nativeGetAccessToken(); - Log.d("[TOKENX]", "token : [" + accessToken + "]"); + Log.d("[USERS]", "token : [" + accessToken + "]"); + + mUsersView = rootView.findViewById(R.id.rvUsers); + int numberOfColumns = 1; + GridLayoutManager gridLayoutMgr = new GridLayoutManager(getContext(), numberOfColumns); + mUsersView.setLayoutManager(gridLayoutMgr); + mUsersAdapter = new UserListAdapter(getContext(), accessToken); + mUsersView.setAdapter(mUsersAdapter); return rootView; } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/provider/EndpointUsersProvider.java b/android/app/src/main/java/io/highfidelity/hifiinterface/provider/EndpointUsersProvider.java new file mode 100644 index 0000000000..3e034b654b --- /dev/null +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/provider/EndpointUsersProvider.java @@ -0,0 +1,130 @@ +package io.highfidelity.hifiinterface.provider; + +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.GET; +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 friendsCall = mEndpointUsersProviderService.getUsers( + "friends", + 400, + null); + friendsCall.enqueue(new Callback() { + @Override + public void onResponse(Call call, retrofit2.Response response) { + if (!response.isSuccessful()) { + usersCallback.retrieveError(new Exception("Error calling Users API"), "Error calling Users API"); + return; + } + UsersResponse usersResponse = response.body(); + List 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; + adapterUsers.add(adapterUser); + } + usersCallback.retrieveOk(adapterUsers); + } + + @Override + public void onFailure(Call call, Throwable t) { + usersCallback.retrieveError(new Exception(t), "Error calling Users API"); + } + }); + } + + public interface EndpointUsersProviderService { + @GET("api/v1/users") + Call getUsers(@Query("filter") String filter, + @Query("per_page") int perPage, + @Query("online") Boolean online); + } + + 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 users; + } + + class User { + public User() {} + String username; + boolean online; + String connection; + Images images; + } + + class Images { + public Images() {} + String hero; + String thumbnail; + String tiny; + } + +} diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/provider/UsersProvider.java b/android/app/src/main/java/io/highfidelity/hifiinterface/provider/UsersProvider.java new file mode 100644 index 0000000000..13ed812ce6 --- /dev/null +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/provider/UsersProvider.java @@ -0,0 +1,20 @@ +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 { + + void retrieve(UsersProvider.UsersCallback usersCallback); + + interface UsersCallback { + void retrieveOk(List users); + void retrieveError(Exception e, String message); + } + +} diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java new file mode 100644 index 0000000000..32993500fe --- /dev/null +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java @@ -0,0 +1,97 @@ +package io.highfidelity.hifiinterface.view; + +import android.content.Context; +import android.net.Uri; +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.Picasso; + +import java.util.ArrayList; +import java.util.List; + +import io.highfidelity.hifiinterface.R; +import io.highfidelity.hifiinterface.provider.EndpointUsersProvider; +import io.highfidelity.hifiinterface.provider.UsersProvider; + +/** + * Created by cduarte on 6/13/18. + */ + +public class UserListAdapter extends RecyclerView.Adapter { + + private UsersProvider mProvider; + private LayoutInflater mInflater; + private Context mContext; + private List mUsers = new ArrayList<>(); + + public UserListAdapter(Context c, String accessToken) { + mContext = c; + mInflater = LayoutInflater.from(mContext); + mProvider = new EndpointUsersProvider(accessToken); + loadUsers(); + } + + private void loadUsers() { + mProvider.retrieve(new UsersProvider.UsersCallback() { + @Override + public void retrieveOk(List users) { + mUsers = new ArrayList<>(users); + notifyDataSetChanged(); + } + + @Override + public void retrieveError(Exception e, String message) { + Log.e("[USERS]", message, e); + } + }); + } + + @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.mOnline.setText(aUser.online?"ONLINE":"OFFLINE"); + Uri uri = Uri.parse(aUser.imageUrl); + Picasso.get().load(uri).into(holder.mImage); + } + + @Override + public int getItemCount() { + return mUsers.size(); + } + + public class ViewHolder extends RecyclerView.ViewHolder { + + TextView mUsername; + TextView mOnline; + ImageView mImage; + + public ViewHolder(View itemView) { + super(itemView); + mUsername = itemView.findViewById(R.id.userName); + mOnline = itemView.findViewById(R.id.userOnline); + mImage = itemView.findViewById(R.id.userImage); + } + } + + public static class User { + public String name; + public String imageUrl; + public String connection; + public boolean online; + + public User() {} + } +} diff --git a/android/app/src/main/res/layout/fragment_friends.xml b/android/app/src/main/res/layout/fragment_friends.xml index 347d7ffe24..8129f5d53c 100644 --- a/android/app/src/main/res/layout/fragment_friends.xml +++ b/android/app/src/main/res/layout/fragment_friends.xml @@ -6,4 +6,13 @@ android:layout_height="match_parent" android:background="@color/backgroundLight"> + + diff --git a/android/app/src/main/res/layout/user_item.xml b/android/app/src/main/res/layout/user_item.xml new file mode 100644 index 0000000000..043c9161de --- /dev/null +++ b/android/app/src/main/res/layout/user_item.xml @@ -0,0 +1,26 @@ + + + + + + + + + \ No newline at end of file From 61d1bf7bf7e7ae2e203604f1dd2e94f12efd478e Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Thu, 14 Jun 2018 21:06:40 -0300 Subject: [PATCH 03/30] Android - Show names of screens when going back in the Main screen (Home-Login-Friends) --- .../io/highfidelity/hifiinterface/MainActivity.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java index 8bb84168a5..d1ac6d5ec8 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java @@ -126,7 +126,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On private void loadHomeFragment() { Fragment fragment = HomeFragment.newInstance(); - loadFragment(fragment, getString(R.string.home), false); + loadFragment(fragment, getString(R.string.home), true); } private void loadLoginFragment() { @@ -152,7 +152,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On FragmentTransaction ft = fragmentManager.beginTransaction(); ft.replace(R.id.content_frame, fragment); if (addToBackStack) { - ft.addToBackStack(null); + ft.addToBackStack(title); } ft.commit(); setTitle(title); @@ -298,8 +298,12 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On @Override public void onBackPressed() { int index = getFragmentManager().getBackStackEntryCount() - 1; - if (index > -1) { + if (index > 0) { super.onBackPressed(); + index--; + if (index > -1) { + setTitle(getFragmentManager().getBackStackEntryAt(index).getName()); + } if (backToScene) { backToScene = false; goToLastLocation(); From cdca20101bcb68eff82812b12d9e618afe867951 Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Tue, 31 Jul 2018 14:37:51 -0300 Subject: [PATCH 04/30] Not an accountManager in AndroidHelper anymore --- android/app/src/main/cpp/native.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/cpp/native.cpp b/android/app/src/main/cpp/native.cpp index def706c128..9f0e088157 100644 --- a/android/app/src/main/cpp/native.cpp +++ b/android/app/src/main/cpp/native.cpp @@ -287,12 +287,13 @@ Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeLogin(JNIEnv *en JNIEXPORT jboolean JNICALL Java_io_highfidelity_hifiinterface_fragment_FriendsFragment_nativeIsLoggedIn(JNIEnv *env, jobject instance) { - return AndroidHelper::instance().getAccountManager()->isLoggedIn(); + auto accountManager = DependencyManager::get(); + return accountManager->isLoggedIn(); } JNIEXPORT jstring JNICALL Java_io_highfidelity_hifiinterface_fragment_FriendsFragment_nativeGetAccessToken(JNIEnv *env, jobject instance) { - auto accountManager = AndroidHelper::instance().getAccountManager(); + auto accountManager = DependencyManager::get(); return env->NewStringUTF(accountManager->getAccountInfo().getAccessToken().token.toLatin1().data()); } From 1a261bea04c5c4065d17dcd08e16f9eff5691e68 Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Wed, 8 Aug 2018 15:41:28 -0300 Subject: [PATCH 05/30] Android - People screen renaming --- .../io/highfidelity/hifiinterface/MainActivity.java | 12 ++++++------ android/app/src/main/res/menu/menu_navigation.xml | 4 ++-- android/app/src/main/res/values/strings.xml | 1 + 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java index d1ac6d5ec8..220a69381d 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java @@ -115,8 +115,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On case "Privacy Policy": loadPrivacyPolicyFragment(); break; - case "Friends": - loadFriendsFragment(); + case "People": + loadPeopleFragment(); break; default: Log.e(TAG, "Unknown fragment " + fragment); @@ -141,10 +141,10 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On loadFragment(fragment, getString(R.string.privacyPolicy), true); } - private void loadFriendsFragment() { + private void loadPeopleFragment() { Fragment fragment = FriendsFragment.newInstance(); - loadFragment(fragment, getString(R.string.friends), true); + loadFragment(fragment, getString(R.string.people), true); } private void loadFragment(Fragment fragment, String title, boolean addToBackStack) { @@ -212,8 +212,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On case R.id.action_home: loadHomeFragment(); return true; - case R.id.action_friends: - loadFriendsFragment(); + case R.id.action_people: + loadPeopleFragment(); return true; } return false; diff --git a/android/app/src/main/res/menu/menu_navigation.xml b/android/app/src/main/res/menu/menu_navigation.xml index de496bc4cb..3cce64f9f5 100644 --- a/android/app/src/main/res/menu/menu_navigation.xml +++ b/android/app/src/main/res/menu/menu_navigation.xml @@ -6,7 +6,7 @@ android:title="@string/home" /> diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index e29252c2c0..9fe7f0cbee 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -2,6 +2,7 @@ Interface Home Friends + People Open in browser Share link Shared a link From 60338f98991e6de42957942a7fe28062d0d757d8 Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Thu, 9 Aug 2018 17:54:07 -0300 Subject: [PATCH 06/30] Android - People - Swipe to reveal delete + layout adaptations for design --- .../hifiinterface/view/SwipeRevealLayout.java | 729 ++++++++++++++++++ .../hifiinterface/view/UserListAdapter.java | 14 +- android/app/src/main/res/layout/user_item.xml | 78 +- android/app/src/main/res/values/attrs.xml | 9 + 4 files changed, 810 insertions(+), 20 deletions(-) create mode 100644 android/app/src/main/java/io/highfidelity/hifiinterface/view/SwipeRevealLayout.java create mode 100644 android/app/src/main/res/values/attrs.xml diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/view/SwipeRevealLayout.java b/android/app/src/main/java/io/highfidelity/hifiinterface/view/SwipeRevealLayout.java new file mode 100644 index 0000000000..06ac4ac3ec --- /dev/null +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/view/SwipeRevealLayout.java @@ -0,0 +1,729 @@ +package io.highfidelity.hifiinterface.view; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Rect; +import android.os.Bundle; +import android.os.Parcelable; +import android.support.annotation.Nullable; +import android.support.v4.view.GestureDetectorCompat; +import android.support.v4.view.ViewCompat; +import android.support.v4.widget.ViewDragHelper; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; + +import io.highfidelity.hifiinterface.R; + +/** + * Created by Mark O'Sullivan on 25th February 2018. + + MIT License + + Copyright (c) 2018 Mark O'Sullivan + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + + */ + +@SuppressLint("RtlHardcoded") +public class SwipeRevealLayout extends ViewGroup { + + private static final String SUPER_INSTANCE_STATE = "saved_instance_state_parcelable"; + + private static final int DEFAULT_MIN_FLING_VELOCITY = 300; // dp per second + private static final int DEFAULT_MIN_DIST_REQUEST_DISALLOW_PARENT = 1; // dp + + public static final int DRAG_EDGE_LEFT = 0x1; + public static final int DRAG_EDGE_RIGHT = 0x1 << 1; + + /** + * The secondary view will be under the main view. + */ + public static final int MODE_NORMAL = 0; + + /** + * The secondary view will stick the edge of the main view. + */ + public static final int MODE_SAME_LEVEL = 1; + + /** + * Main view is the view which is shown when the layout is closed. + */ + private View mMainView; + + /** + * Secondary view is the view which is shown when the layout is opened. + */ + private View mSecondaryView; + + /** + * The rectangle position of the main view when the layout is closed. + */ + private Rect mRectMainClose = new Rect(); + + /** + * The rectangle position of the main view when the layout is opened. + */ + private Rect mRectMainOpen = new Rect(); + + /** + * The rectangle position of the secondary view when the layout is closed. + */ + private Rect mRectSecClose = new Rect(); + + /** + * The rectangle position of the secondary view when the layout is opened. + */ + private Rect mRectSecOpen = new Rect(); + + /** + * The minimum distance (px) to the closest drag edge that the SwipeRevealLayout + * will disallow the parent to intercept touch event. + */ + private int mMinDistRequestDisallowParent = 0; + + private boolean mIsOpenBeforeInit = false; + private volatile boolean mIsScrolling = false; + private volatile boolean mLockDrag = false; + + private int mMinFlingVelocity = DEFAULT_MIN_FLING_VELOCITY; + private int mMode = MODE_NORMAL; + + private int mDragEdge = DRAG_EDGE_LEFT; + + private float mDragDist = 0; + private float mPrevX = -1; + + private ViewDragHelper mDragHelper; + private GestureDetectorCompat mGestureDetector; + + public SwipeRevealLayout(Context context) { + super(context); + init(context, null); + } + + public SwipeRevealLayout(Context context, AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public SwipeRevealLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Nullable + @Override + protected Parcelable onSaveInstanceState() { + Bundle bundle = new Bundle(); + bundle.putParcelable(SUPER_INSTANCE_STATE, super.onSaveInstanceState()); + return super.onSaveInstanceState(); + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + Bundle bundle = (Bundle) state; + state = bundle.getParcelable(SUPER_INSTANCE_STATE); + super.onRestoreInstanceState(state); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + mGestureDetector.onTouchEvent(event); + mDragHelper.processTouchEvent(event); + return true; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (isDragLocked()) { + return super.onInterceptTouchEvent(ev); + } + + mDragHelper.processTouchEvent(ev); + mGestureDetector.onTouchEvent(ev); + accumulateDragDist(ev); + + boolean couldBecomeClick = couldBecomeClick(ev); + boolean settling = mDragHelper.getViewDragState() == ViewDragHelper.STATE_SETTLING; + boolean idleAfterScrolled = mDragHelper.getViewDragState() == ViewDragHelper.STATE_IDLE + && mIsScrolling; + + // must be placed as the last statement + mPrevX = ev.getX(); + + // return true => intercept, cannot trigger onClick event + return !couldBecomeClick && (settling || idleAfterScrolled); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + // get views + if (getChildCount() >= 2) { + mSecondaryView = getChildAt(0); + mMainView = getChildAt(1); + } + else if (getChildCount() == 1) { + mMainView = getChildAt(0); + } + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings("ConstantConditions") + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + for (int index = 0; index < getChildCount(); index++) { + final View child = getChildAt(index); + + int left, right, top, bottom; + left = right = top = bottom = 0; + + final int minLeft = getPaddingLeft(); + final int maxRight = Math.max(r - getPaddingRight() - l, 0); + final int minTop = getPaddingTop(); + final int maxBottom = Math.max(b - getPaddingBottom() - t, 0); + + int measuredChildHeight = child.getMeasuredHeight(); + int measuredChildWidth = child.getMeasuredWidth(); + + // need to take account if child size is match_parent + final LayoutParams childParams = child.getLayoutParams(); + boolean matchParentHeight = false; + boolean matchParentWidth = false; + + if (childParams != null) { + matchParentHeight = (childParams.height == LayoutParams.MATCH_PARENT) || + (childParams.height == LayoutParams.FILL_PARENT); + matchParentWidth = (childParams.width == LayoutParams.MATCH_PARENT) || + (childParams.width == LayoutParams.FILL_PARENT); + } + + if (matchParentHeight) { + measuredChildHeight = maxBottom - minTop; + childParams.height = measuredChildHeight; + } + + if (matchParentWidth) { + measuredChildWidth = maxRight - minLeft; + childParams.width = measuredChildWidth; + } + + switch (mDragEdge) { + case DRAG_EDGE_RIGHT: + left = Math.max(r - measuredChildWidth - getPaddingRight() - l, minLeft); + top = Math.min(getPaddingTop(), maxBottom); + right = Math.max(r - getPaddingRight() - l, minLeft); + bottom = Math.min(measuredChildHeight + getPaddingTop(), maxBottom); + break; + + case DRAG_EDGE_LEFT: + left = Math.min(getPaddingLeft(), maxRight); + top = Math.min(getPaddingTop(), maxBottom); + right = Math.min(measuredChildWidth + getPaddingLeft(), maxRight); + bottom = Math.min(measuredChildHeight + getPaddingTop(), maxBottom); + break; + } + + child.layout(left, top, right, bottom); + } + + // taking account offset when mode is SAME_LEVEL + if (mMode == MODE_SAME_LEVEL) { + switch (mDragEdge) { + case DRAG_EDGE_LEFT: + mSecondaryView.offsetLeftAndRight(-mSecondaryView.getWidth()); + break; + + case DRAG_EDGE_RIGHT: + mSecondaryView.offsetLeftAndRight(mSecondaryView.getWidth()); + break; + } + } + + initRects(); + + if (mIsOpenBeforeInit) { + open(false); + } else { + close(false); + } + + } + + /** + * {@inheritDoc} + */ + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (getChildCount() < 2) { + throw new RuntimeException("Layout must have two children"); + } + + final LayoutParams params = getLayoutParams(); + + final int widthMode = MeasureSpec.getMode(widthMeasureSpec); + final int heightMode = MeasureSpec.getMode(heightMeasureSpec); + + int desiredWidth = 0; + int desiredHeight = 0; + + // first find the largest child + for (int i = 0; i < getChildCount(); i++) { + final View child = getChildAt(i); + measureChild(child, widthMeasureSpec, heightMeasureSpec); + desiredWidth = Math.max(child.getMeasuredWidth(), desiredWidth); + desiredHeight = Math.max(child.getMeasuredHeight(), desiredHeight); + } + // create new measure spec using the largest child width + widthMeasureSpec = MeasureSpec.makeMeasureSpec(desiredWidth, widthMode); + heightMeasureSpec = MeasureSpec.makeMeasureSpec(desiredHeight, heightMode); + + final int measuredWidth = MeasureSpec.getSize(widthMeasureSpec); + final int measuredHeight = MeasureSpec.getSize(heightMeasureSpec); + + for (int i = 0; i < getChildCount(); i++) { + final View child = getChildAt(i); + final LayoutParams childParams = child.getLayoutParams(); + + if (childParams != null) { + if (childParams.height == LayoutParams.MATCH_PARENT) { + child.setMinimumHeight(measuredHeight); + } + + if (childParams.width == LayoutParams.MATCH_PARENT) { + child.setMinimumWidth(measuredWidth); + } + } + + measureChild(child, widthMeasureSpec, heightMeasureSpec); + desiredWidth = Math.max(child.getMeasuredWidth(), desiredWidth); + desiredHeight = Math.max(child.getMeasuredHeight(), desiredHeight); + } + + // taking accounts of padding + desiredWidth += getPaddingLeft() + getPaddingRight(); + desiredHeight += getPaddingTop() + getPaddingBottom(); + + // adjust desired width + if (widthMode == MeasureSpec.EXACTLY) { + desiredWidth = measuredWidth; + } else { + if (params.width == LayoutParams.MATCH_PARENT) { + desiredWidth = measuredWidth; + } + + if (widthMode == MeasureSpec.AT_MOST) { + desiredWidth = (desiredWidth > measuredWidth)? measuredWidth : desiredWidth; + } + } + + // adjust desired height + if (heightMode == MeasureSpec.EXACTLY) { + desiredHeight = measuredHeight; + } else { + if (params.height == LayoutParams.MATCH_PARENT) { + desiredHeight = measuredHeight; + } + + if (heightMode == MeasureSpec.AT_MOST) { + desiredHeight = (desiredHeight > measuredHeight)? measuredHeight : desiredHeight; + } + } + + setMeasuredDimension(desiredWidth, desiredHeight); + } + + @Override + public void computeScroll() { + if (mDragHelper.continueSettling(true)) { + ViewCompat.postInvalidateOnAnimation(this); + } + } + + /** + * Open the panel to show the secondary view + */ + public void open(boolean animation) { + mIsOpenBeforeInit = true; + + if (animation) { + mDragHelper.smoothSlideViewTo(mMainView, mRectMainOpen.left, mRectMainOpen.top); + } else { + mDragHelper.abort(); + + mMainView.layout( + mRectMainOpen.left, + mRectMainOpen.top, + mRectMainOpen.right, + mRectMainOpen.bottom + ); + + mSecondaryView.layout( + mRectSecOpen.left, + mRectSecOpen.top, + mRectSecOpen.right, + mRectSecOpen.bottom + ); + } + + ViewCompat.postInvalidateOnAnimation(this); + } + + /** + * Close the panel to hide the secondary view + */ + public void close(boolean animation) { + mIsOpenBeforeInit = false; + + if (animation) { + mDragHelper.smoothSlideViewTo(mMainView, mRectMainClose.left, mRectMainClose.top); + } else { + mDragHelper.abort(); + mMainView.layout( + mRectMainClose.left, + mRectMainClose.top, + mRectMainClose.right, + mRectMainClose.bottom + ); + mSecondaryView.layout( + mRectSecClose.left, + mRectSecClose.top, + mRectSecClose.right, + mRectSecClose.bottom + ); + } + + ViewCompat.postInvalidateOnAnimation(this); + } + + /** + * @return true if the drag/swipe motion is currently locked. + */ + public boolean isDragLocked() { + return mLockDrag; + } + + private int getMainOpenLeft() { + switch (mDragEdge) { + case DRAG_EDGE_LEFT: + return mRectMainClose.left + mSecondaryView.getWidth(); + + case DRAG_EDGE_RIGHT: + return mRectMainClose.left - mSecondaryView.getWidth(); + + + default: + return 0; + } + } + + private int getMainOpenTop() { + switch (mDragEdge) { + case DRAG_EDGE_LEFT: + return mRectMainClose.top; + + case DRAG_EDGE_RIGHT: + return mRectMainClose.top; + + + default: + return 0; + } + } + + private int getSecOpenLeft() { + return mRectSecClose.left; + } + + private int getSecOpenTop() { + return mRectSecClose.top; + } + + private void initRects() { + // close position of main view + mRectMainClose.set( + mMainView.getLeft(), + mMainView.getTop(), + mMainView.getRight(), + mMainView.getBottom() + ); + + // close position of secondary view + mRectSecClose.set( + mSecondaryView.getLeft(), + mSecondaryView.getTop(), + mSecondaryView.getRight(), + mSecondaryView.getBottom() + ); + + // open position of the main view + mRectMainOpen.set( + getMainOpenLeft(), + getMainOpenTop(), + getMainOpenLeft() + mMainView.getWidth(), + getMainOpenTop() + mMainView.getHeight() + ); + + // open position of the secondary view + mRectSecOpen.set( + getSecOpenLeft(), + getSecOpenTop(), + getSecOpenLeft() + mSecondaryView.getWidth(), + getSecOpenTop() + mSecondaryView.getHeight() + ); + } + + private boolean couldBecomeClick(MotionEvent ev) { + return isInMainView(ev) && !shouldInitiateADrag(); + } + + private boolean isInMainView(MotionEvent ev) { + float x = ev.getX(); + float y = ev.getY(); + + boolean withinVertical = mMainView.getTop() <= y && y <= mMainView.getBottom(); + boolean withinHorizontal = mMainView.getLeft() <= x && x <= mMainView.getRight(); + + return withinVertical && withinHorizontal; + } + + private boolean shouldInitiateADrag() { + float minDistToInitiateDrag = mDragHelper.getTouchSlop(); + return mDragDist >= minDistToInitiateDrag; + } + + private void accumulateDragDist(MotionEvent ev) { + final int action = ev.getAction(); + if (action == MotionEvent.ACTION_DOWN) { + mDragDist = 0; + return; + } + + float dragged = Math.abs(ev.getX() - mPrevX); + + mDragDist += dragged; + } + + private void init(Context context, AttributeSet attrs) { + if (attrs != null && context != null) { + TypedArray a = context.getTheme().obtainStyledAttributes( + attrs, + R.styleable.SwipeRevealLayout, + 0, 0 + ); + + mDragEdge = a.getInteger(R.styleable.SwipeRevealLayout_dragFromEdge, DRAG_EDGE_LEFT); + mMode = MODE_NORMAL; + mMinFlingVelocity = DEFAULT_MIN_FLING_VELOCITY; + mMinDistRequestDisallowParent = DEFAULT_MIN_DIST_REQUEST_DISALLOW_PARENT; + } + + mDragHelper = ViewDragHelper.create(this, 1.0f, mDragHelperCallback); + mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_ALL); + + mGestureDetector = new GestureDetectorCompat(context, mGestureListener); + } + + private final GestureDetector.OnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() { + boolean hasDisallowed = false; + + @Override + public boolean onDown(MotionEvent e) { + mIsScrolling = false; + hasDisallowed = false; + return true; + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + mIsScrolling = true; + return false; + } + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { + mIsScrolling = true; + + if (getParent() != null) { + boolean shouldDisallow; + + if (!hasDisallowed) { + shouldDisallow = getDistToClosestEdge() >= mMinDistRequestDisallowParent; + if (shouldDisallow) { + hasDisallowed = true; + } + } else { + shouldDisallow = true; + } + + // disallow parent to intercept touch event so that the layout will work + // properly on RecyclerView or view that handles scroll gesture. + getParent().requestDisallowInterceptTouchEvent(shouldDisallow); + } + + return false; + } + }; + + private int getDistToClosestEdge() { + switch (mDragEdge) { + case DRAG_EDGE_LEFT: + final int pivotRight = mRectMainClose.left + mSecondaryView.getWidth(); + + return Math.min( + mMainView.getLeft() - mRectMainClose.left, + pivotRight - mMainView.getLeft() + ); + + case DRAG_EDGE_RIGHT: + final int pivotLeft = mRectMainClose.right - mSecondaryView.getWidth(); + + return Math.min( + mMainView.getRight() - pivotLeft, + mRectMainClose.right - mMainView.getRight() + ); + } + + return 0; + } + + private int getHalfwayPivotHorizontal() { + if (mDragEdge == DRAG_EDGE_LEFT) { + return mRectMainClose.left + mSecondaryView.getWidth() / 2; + } else { + return mRectMainClose.right - mSecondaryView.getWidth() / 2; + } + } + + private final ViewDragHelper.Callback mDragHelperCallback = new ViewDragHelper.Callback() { + @Override + public boolean tryCaptureView(View child, int pointerId) { + + if (mLockDrag) + return false; + + mDragHelper.captureChildView(mMainView, pointerId); + return false; + } + + @Override + public int clampViewPositionHorizontal(View child, int left, int dx) { + switch (mDragEdge) { + case DRAG_EDGE_RIGHT: + return Math.max( + Math.min(left, mRectMainClose.left), + mRectMainClose.left - mSecondaryView.getWidth() + ); + + case DRAG_EDGE_LEFT: + return Math.max( + Math.min(left, mRectMainClose.left + mSecondaryView.getWidth()), + mRectMainClose.left + ); + + default: + return child.getLeft(); + } + } + + @Override + public void onViewReleased(View releasedChild, float xvel, float yvel) { + final boolean velRightExceeded = pxToDp((int) xvel) >= mMinFlingVelocity; + final boolean velLeftExceeded = pxToDp((int) xvel) <= -mMinFlingVelocity; + + final int pivotHorizontal = getHalfwayPivotHorizontal(); + + switch (mDragEdge) { + case DRAG_EDGE_RIGHT: + if (velRightExceeded) { + close(true); + } else if (velLeftExceeded) { + open(true); + } else { + if (mMainView.getRight() < pivotHorizontal) { + open(true); + } else { + close(true); + } + } + break; + + case DRAG_EDGE_LEFT: + if (velRightExceeded) { + open(true); + } else if (velLeftExceeded) { + close(true); + } else { + if (mMainView.getLeft() < pivotHorizontal) { + close(true); + } else { + open(true); + } + } + break; + } + } + + @Override + public void onEdgeDragStarted(int edgeFlags, int pointerId) { + super.onEdgeDragStarted(edgeFlags, pointerId); + + if (mLockDrag) { + return; + } + + boolean edgeStartLeft = (mDragEdge == DRAG_EDGE_RIGHT) + && edgeFlags == ViewDragHelper.EDGE_LEFT; + + boolean edgeStartRight = (mDragEdge == DRAG_EDGE_LEFT) + && edgeFlags == ViewDragHelper.EDGE_RIGHT; + + if (edgeStartLeft || edgeStartRight) { + mDragHelper.captureChildView(mMainView, pointerId); + } + } + + @Override + public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { + super.onViewPositionChanged(changedView, left, top, dx, dy); + if (mMode == MODE_SAME_LEVEL) { + if (mDragEdge == DRAG_EDGE_LEFT || mDragEdge == DRAG_EDGE_RIGHT) { + mSecondaryView.offsetLeftAndRight(dx); + } else { + mSecondaryView.offsetTopAndBottom(dy); + } + } + ViewCompat.postInvalidateOnAnimation(SwipeRevealLayout.this); + } + }; + + private int pxToDp(int px) { + Resources resources = getContext().getResources(); + DisplayMetrics metrics = resources.getDisplayMetrics(); + return (int) (px / ((float)metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT)); + } +} \ No newline at end of file diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java index 32993500fe..79a931d93b 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java @@ -7,8 +7,11 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.Button; +import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; +import android.widget.Toast; import com.squareup.picasso.Picasso; @@ -62,7 +65,14 @@ public class UserListAdapter extends RecyclerView.Adapter - + android:layout_gravity="center_vertical" + app:dragFromEdge="right"> - - + android:layout_height="match_parent" + android:background="@android:color/holo_red_dark"> + - + + + - \ No newline at end of file + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/values/attrs.xml b/android/app/src/main/res/values/attrs.xml new file mode 100644 index 0000000000..c12f28ccde --- /dev/null +++ b/android/app/src/main/res/values/attrs.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file From d61aaa3bd76b907794dd3abccf8ecfcfad7b92b1 Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Fri, 10 Aug 2018 14:16:44 -0300 Subject: [PATCH 07/30] Android - People - Round profile image --- .../hifiinterface/view/UserListAdapter.java | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java index 79a931d93b..f9df28d5ca 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java @@ -1,18 +1,22 @@ 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.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.Button; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; +import com.squareup.picasso.Callback; import com.squareup.picasso.Picasso; import java.util.ArrayList; @@ -74,7 +78,28 @@ public class UserListAdapter extends RecyclerView.Adapter Date: Fri, 10 Aug 2018 17:36:32 -0300 Subject: [PATCH 08/30] Android - Friends - rollback swipe to reveal items --- .../hifiinterface/view/SwipeRevealLayout.java | 729 ------------------ .../hifiinterface/view/UserListAdapter.java | 10 - android/app/src/main/res/layout/user_item.xml | 83 +- android/app/src/main/res/values/attrs.xml | 9 - 4 files changed, 26 insertions(+), 805 deletions(-) delete mode 100644 android/app/src/main/java/io/highfidelity/hifiinterface/view/SwipeRevealLayout.java delete mode 100644 android/app/src/main/res/values/attrs.xml diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/view/SwipeRevealLayout.java b/android/app/src/main/java/io/highfidelity/hifiinterface/view/SwipeRevealLayout.java deleted file mode 100644 index 06ac4ac3ec..0000000000 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/view/SwipeRevealLayout.java +++ /dev/null @@ -1,729 +0,0 @@ -package io.highfidelity.hifiinterface.view; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.Rect; -import android.os.Bundle; -import android.os.Parcelable; -import android.support.annotation.Nullable; -import android.support.v4.view.GestureDetectorCompat; -import android.support.v4.view.ViewCompat; -import android.support.v4.widget.ViewDragHelper; -import android.util.AttributeSet; -import android.util.DisplayMetrics; -import android.view.GestureDetector; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; - -import io.highfidelity.hifiinterface.R; - -/** - * Created by Mark O'Sullivan on 25th February 2018. - - MIT License - - Copyright (c) 2018 Mark O'Sullivan - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - - */ - -@SuppressLint("RtlHardcoded") -public class SwipeRevealLayout extends ViewGroup { - - private static final String SUPER_INSTANCE_STATE = "saved_instance_state_parcelable"; - - private static final int DEFAULT_MIN_FLING_VELOCITY = 300; // dp per second - private static final int DEFAULT_MIN_DIST_REQUEST_DISALLOW_PARENT = 1; // dp - - public static final int DRAG_EDGE_LEFT = 0x1; - public static final int DRAG_EDGE_RIGHT = 0x1 << 1; - - /** - * The secondary view will be under the main view. - */ - public static final int MODE_NORMAL = 0; - - /** - * The secondary view will stick the edge of the main view. - */ - public static final int MODE_SAME_LEVEL = 1; - - /** - * Main view is the view which is shown when the layout is closed. - */ - private View mMainView; - - /** - * Secondary view is the view which is shown when the layout is opened. - */ - private View mSecondaryView; - - /** - * The rectangle position of the main view when the layout is closed. - */ - private Rect mRectMainClose = new Rect(); - - /** - * The rectangle position of the main view when the layout is opened. - */ - private Rect mRectMainOpen = new Rect(); - - /** - * The rectangle position of the secondary view when the layout is closed. - */ - private Rect mRectSecClose = new Rect(); - - /** - * The rectangle position of the secondary view when the layout is opened. - */ - private Rect mRectSecOpen = new Rect(); - - /** - * The minimum distance (px) to the closest drag edge that the SwipeRevealLayout - * will disallow the parent to intercept touch event. - */ - private int mMinDistRequestDisallowParent = 0; - - private boolean mIsOpenBeforeInit = false; - private volatile boolean mIsScrolling = false; - private volatile boolean mLockDrag = false; - - private int mMinFlingVelocity = DEFAULT_MIN_FLING_VELOCITY; - private int mMode = MODE_NORMAL; - - private int mDragEdge = DRAG_EDGE_LEFT; - - private float mDragDist = 0; - private float mPrevX = -1; - - private ViewDragHelper mDragHelper; - private GestureDetectorCompat mGestureDetector; - - public SwipeRevealLayout(Context context) { - super(context); - init(context, null); - } - - public SwipeRevealLayout(Context context, AttributeSet attrs) { - super(context, attrs); - init(context, attrs); - } - - public SwipeRevealLayout(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - @Nullable - @Override - protected Parcelable onSaveInstanceState() { - Bundle bundle = new Bundle(); - bundle.putParcelable(SUPER_INSTANCE_STATE, super.onSaveInstanceState()); - return super.onSaveInstanceState(); - } - - @Override - protected void onRestoreInstanceState(Parcelable state) { - Bundle bundle = (Bundle) state; - state = bundle.getParcelable(SUPER_INSTANCE_STATE); - super.onRestoreInstanceState(state); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - mGestureDetector.onTouchEvent(event); - mDragHelper.processTouchEvent(event); - return true; - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - if (isDragLocked()) { - return super.onInterceptTouchEvent(ev); - } - - mDragHelper.processTouchEvent(ev); - mGestureDetector.onTouchEvent(ev); - accumulateDragDist(ev); - - boolean couldBecomeClick = couldBecomeClick(ev); - boolean settling = mDragHelper.getViewDragState() == ViewDragHelper.STATE_SETTLING; - boolean idleAfterScrolled = mDragHelper.getViewDragState() == ViewDragHelper.STATE_IDLE - && mIsScrolling; - - // must be placed as the last statement - mPrevX = ev.getX(); - - // return true => intercept, cannot trigger onClick event - return !couldBecomeClick && (settling || idleAfterScrolled); - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - - // get views - if (getChildCount() >= 2) { - mSecondaryView = getChildAt(0); - mMainView = getChildAt(1); - } - else if (getChildCount() == 1) { - mMainView = getChildAt(0); - } - } - - /** - * {@inheritDoc} - */ - @SuppressWarnings("ConstantConditions") - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - for (int index = 0; index < getChildCount(); index++) { - final View child = getChildAt(index); - - int left, right, top, bottom; - left = right = top = bottom = 0; - - final int minLeft = getPaddingLeft(); - final int maxRight = Math.max(r - getPaddingRight() - l, 0); - final int minTop = getPaddingTop(); - final int maxBottom = Math.max(b - getPaddingBottom() - t, 0); - - int measuredChildHeight = child.getMeasuredHeight(); - int measuredChildWidth = child.getMeasuredWidth(); - - // need to take account if child size is match_parent - final LayoutParams childParams = child.getLayoutParams(); - boolean matchParentHeight = false; - boolean matchParentWidth = false; - - if (childParams != null) { - matchParentHeight = (childParams.height == LayoutParams.MATCH_PARENT) || - (childParams.height == LayoutParams.FILL_PARENT); - matchParentWidth = (childParams.width == LayoutParams.MATCH_PARENT) || - (childParams.width == LayoutParams.FILL_PARENT); - } - - if (matchParentHeight) { - measuredChildHeight = maxBottom - minTop; - childParams.height = measuredChildHeight; - } - - if (matchParentWidth) { - measuredChildWidth = maxRight - minLeft; - childParams.width = measuredChildWidth; - } - - switch (mDragEdge) { - case DRAG_EDGE_RIGHT: - left = Math.max(r - measuredChildWidth - getPaddingRight() - l, minLeft); - top = Math.min(getPaddingTop(), maxBottom); - right = Math.max(r - getPaddingRight() - l, minLeft); - bottom = Math.min(measuredChildHeight + getPaddingTop(), maxBottom); - break; - - case DRAG_EDGE_LEFT: - left = Math.min(getPaddingLeft(), maxRight); - top = Math.min(getPaddingTop(), maxBottom); - right = Math.min(measuredChildWidth + getPaddingLeft(), maxRight); - bottom = Math.min(measuredChildHeight + getPaddingTop(), maxBottom); - break; - } - - child.layout(left, top, right, bottom); - } - - // taking account offset when mode is SAME_LEVEL - if (mMode == MODE_SAME_LEVEL) { - switch (mDragEdge) { - case DRAG_EDGE_LEFT: - mSecondaryView.offsetLeftAndRight(-mSecondaryView.getWidth()); - break; - - case DRAG_EDGE_RIGHT: - mSecondaryView.offsetLeftAndRight(mSecondaryView.getWidth()); - break; - } - } - - initRects(); - - if (mIsOpenBeforeInit) { - open(false); - } else { - close(false); - } - - } - - /** - * {@inheritDoc} - */ - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - if (getChildCount() < 2) { - throw new RuntimeException("Layout must have two children"); - } - - final LayoutParams params = getLayoutParams(); - - final int widthMode = MeasureSpec.getMode(widthMeasureSpec); - final int heightMode = MeasureSpec.getMode(heightMeasureSpec); - - int desiredWidth = 0; - int desiredHeight = 0; - - // first find the largest child - for (int i = 0; i < getChildCount(); i++) { - final View child = getChildAt(i); - measureChild(child, widthMeasureSpec, heightMeasureSpec); - desiredWidth = Math.max(child.getMeasuredWidth(), desiredWidth); - desiredHeight = Math.max(child.getMeasuredHeight(), desiredHeight); - } - // create new measure spec using the largest child width - widthMeasureSpec = MeasureSpec.makeMeasureSpec(desiredWidth, widthMode); - heightMeasureSpec = MeasureSpec.makeMeasureSpec(desiredHeight, heightMode); - - final int measuredWidth = MeasureSpec.getSize(widthMeasureSpec); - final int measuredHeight = MeasureSpec.getSize(heightMeasureSpec); - - for (int i = 0; i < getChildCount(); i++) { - final View child = getChildAt(i); - final LayoutParams childParams = child.getLayoutParams(); - - if (childParams != null) { - if (childParams.height == LayoutParams.MATCH_PARENT) { - child.setMinimumHeight(measuredHeight); - } - - if (childParams.width == LayoutParams.MATCH_PARENT) { - child.setMinimumWidth(measuredWidth); - } - } - - measureChild(child, widthMeasureSpec, heightMeasureSpec); - desiredWidth = Math.max(child.getMeasuredWidth(), desiredWidth); - desiredHeight = Math.max(child.getMeasuredHeight(), desiredHeight); - } - - // taking accounts of padding - desiredWidth += getPaddingLeft() + getPaddingRight(); - desiredHeight += getPaddingTop() + getPaddingBottom(); - - // adjust desired width - if (widthMode == MeasureSpec.EXACTLY) { - desiredWidth = measuredWidth; - } else { - if (params.width == LayoutParams.MATCH_PARENT) { - desiredWidth = measuredWidth; - } - - if (widthMode == MeasureSpec.AT_MOST) { - desiredWidth = (desiredWidth > measuredWidth)? measuredWidth : desiredWidth; - } - } - - // adjust desired height - if (heightMode == MeasureSpec.EXACTLY) { - desiredHeight = measuredHeight; - } else { - if (params.height == LayoutParams.MATCH_PARENT) { - desiredHeight = measuredHeight; - } - - if (heightMode == MeasureSpec.AT_MOST) { - desiredHeight = (desiredHeight > measuredHeight)? measuredHeight : desiredHeight; - } - } - - setMeasuredDimension(desiredWidth, desiredHeight); - } - - @Override - public void computeScroll() { - if (mDragHelper.continueSettling(true)) { - ViewCompat.postInvalidateOnAnimation(this); - } - } - - /** - * Open the panel to show the secondary view - */ - public void open(boolean animation) { - mIsOpenBeforeInit = true; - - if (animation) { - mDragHelper.smoothSlideViewTo(mMainView, mRectMainOpen.left, mRectMainOpen.top); - } else { - mDragHelper.abort(); - - mMainView.layout( - mRectMainOpen.left, - mRectMainOpen.top, - mRectMainOpen.right, - mRectMainOpen.bottom - ); - - mSecondaryView.layout( - mRectSecOpen.left, - mRectSecOpen.top, - mRectSecOpen.right, - mRectSecOpen.bottom - ); - } - - ViewCompat.postInvalidateOnAnimation(this); - } - - /** - * Close the panel to hide the secondary view - */ - public void close(boolean animation) { - mIsOpenBeforeInit = false; - - if (animation) { - mDragHelper.smoothSlideViewTo(mMainView, mRectMainClose.left, mRectMainClose.top); - } else { - mDragHelper.abort(); - mMainView.layout( - mRectMainClose.left, - mRectMainClose.top, - mRectMainClose.right, - mRectMainClose.bottom - ); - mSecondaryView.layout( - mRectSecClose.left, - mRectSecClose.top, - mRectSecClose.right, - mRectSecClose.bottom - ); - } - - ViewCompat.postInvalidateOnAnimation(this); - } - - /** - * @return true if the drag/swipe motion is currently locked. - */ - public boolean isDragLocked() { - return mLockDrag; - } - - private int getMainOpenLeft() { - switch (mDragEdge) { - case DRAG_EDGE_LEFT: - return mRectMainClose.left + mSecondaryView.getWidth(); - - case DRAG_EDGE_RIGHT: - return mRectMainClose.left - mSecondaryView.getWidth(); - - - default: - return 0; - } - } - - private int getMainOpenTop() { - switch (mDragEdge) { - case DRAG_EDGE_LEFT: - return mRectMainClose.top; - - case DRAG_EDGE_RIGHT: - return mRectMainClose.top; - - - default: - return 0; - } - } - - private int getSecOpenLeft() { - return mRectSecClose.left; - } - - private int getSecOpenTop() { - return mRectSecClose.top; - } - - private void initRects() { - // close position of main view - mRectMainClose.set( - mMainView.getLeft(), - mMainView.getTop(), - mMainView.getRight(), - mMainView.getBottom() - ); - - // close position of secondary view - mRectSecClose.set( - mSecondaryView.getLeft(), - mSecondaryView.getTop(), - mSecondaryView.getRight(), - mSecondaryView.getBottom() - ); - - // open position of the main view - mRectMainOpen.set( - getMainOpenLeft(), - getMainOpenTop(), - getMainOpenLeft() + mMainView.getWidth(), - getMainOpenTop() + mMainView.getHeight() - ); - - // open position of the secondary view - mRectSecOpen.set( - getSecOpenLeft(), - getSecOpenTop(), - getSecOpenLeft() + mSecondaryView.getWidth(), - getSecOpenTop() + mSecondaryView.getHeight() - ); - } - - private boolean couldBecomeClick(MotionEvent ev) { - return isInMainView(ev) && !shouldInitiateADrag(); - } - - private boolean isInMainView(MotionEvent ev) { - float x = ev.getX(); - float y = ev.getY(); - - boolean withinVertical = mMainView.getTop() <= y && y <= mMainView.getBottom(); - boolean withinHorizontal = mMainView.getLeft() <= x && x <= mMainView.getRight(); - - return withinVertical && withinHorizontal; - } - - private boolean shouldInitiateADrag() { - float minDistToInitiateDrag = mDragHelper.getTouchSlop(); - return mDragDist >= minDistToInitiateDrag; - } - - private void accumulateDragDist(MotionEvent ev) { - final int action = ev.getAction(); - if (action == MotionEvent.ACTION_DOWN) { - mDragDist = 0; - return; - } - - float dragged = Math.abs(ev.getX() - mPrevX); - - mDragDist += dragged; - } - - private void init(Context context, AttributeSet attrs) { - if (attrs != null && context != null) { - TypedArray a = context.getTheme().obtainStyledAttributes( - attrs, - R.styleable.SwipeRevealLayout, - 0, 0 - ); - - mDragEdge = a.getInteger(R.styleable.SwipeRevealLayout_dragFromEdge, DRAG_EDGE_LEFT); - mMode = MODE_NORMAL; - mMinFlingVelocity = DEFAULT_MIN_FLING_VELOCITY; - mMinDistRequestDisallowParent = DEFAULT_MIN_DIST_REQUEST_DISALLOW_PARENT; - } - - mDragHelper = ViewDragHelper.create(this, 1.0f, mDragHelperCallback); - mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_ALL); - - mGestureDetector = new GestureDetectorCompat(context, mGestureListener); - } - - private final GestureDetector.OnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() { - boolean hasDisallowed = false; - - @Override - public boolean onDown(MotionEvent e) { - mIsScrolling = false; - hasDisallowed = false; - return true; - } - - @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - mIsScrolling = true; - return false; - } - - @Override - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - mIsScrolling = true; - - if (getParent() != null) { - boolean shouldDisallow; - - if (!hasDisallowed) { - shouldDisallow = getDistToClosestEdge() >= mMinDistRequestDisallowParent; - if (shouldDisallow) { - hasDisallowed = true; - } - } else { - shouldDisallow = true; - } - - // disallow parent to intercept touch event so that the layout will work - // properly on RecyclerView or view that handles scroll gesture. - getParent().requestDisallowInterceptTouchEvent(shouldDisallow); - } - - return false; - } - }; - - private int getDistToClosestEdge() { - switch (mDragEdge) { - case DRAG_EDGE_LEFT: - final int pivotRight = mRectMainClose.left + mSecondaryView.getWidth(); - - return Math.min( - mMainView.getLeft() - mRectMainClose.left, - pivotRight - mMainView.getLeft() - ); - - case DRAG_EDGE_RIGHT: - final int pivotLeft = mRectMainClose.right - mSecondaryView.getWidth(); - - return Math.min( - mMainView.getRight() - pivotLeft, - mRectMainClose.right - mMainView.getRight() - ); - } - - return 0; - } - - private int getHalfwayPivotHorizontal() { - if (mDragEdge == DRAG_EDGE_LEFT) { - return mRectMainClose.left + mSecondaryView.getWidth() / 2; - } else { - return mRectMainClose.right - mSecondaryView.getWidth() / 2; - } - } - - private final ViewDragHelper.Callback mDragHelperCallback = new ViewDragHelper.Callback() { - @Override - public boolean tryCaptureView(View child, int pointerId) { - - if (mLockDrag) - return false; - - mDragHelper.captureChildView(mMainView, pointerId); - return false; - } - - @Override - public int clampViewPositionHorizontal(View child, int left, int dx) { - switch (mDragEdge) { - case DRAG_EDGE_RIGHT: - return Math.max( - Math.min(left, mRectMainClose.left), - mRectMainClose.left - mSecondaryView.getWidth() - ); - - case DRAG_EDGE_LEFT: - return Math.max( - Math.min(left, mRectMainClose.left + mSecondaryView.getWidth()), - mRectMainClose.left - ); - - default: - return child.getLeft(); - } - } - - @Override - public void onViewReleased(View releasedChild, float xvel, float yvel) { - final boolean velRightExceeded = pxToDp((int) xvel) >= mMinFlingVelocity; - final boolean velLeftExceeded = pxToDp((int) xvel) <= -mMinFlingVelocity; - - final int pivotHorizontal = getHalfwayPivotHorizontal(); - - switch (mDragEdge) { - case DRAG_EDGE_RIGHT: - if (velRightExceeded) { - close(true); - } else if (velLeftExceeded) { - open(true); - } else { - if (mMainView.getRight() < pivotHorizontal) { - open(true); - } else { - close(true); - } - } - break; - - case DRAG_EDGE_LEFT: - if (velRightExceeded) { - open(true); - } else if (velLeftExceeded) { - close(true); - } else { - if (mMainView.getLeft() < pivotHorizontal) { - close(true); - } else { - open(true); - } - } - break; - } - } - - @Override - public void onEdgeDragStarted(int edgeFlags, int pointerId) { - super.onEdgeDragStarted(edgeFlags, pointerId); - - if (mLockDrag) { - return; - } - - boolean edgeStartLeft = (mDragEdge == DRAG_EDGE_RIGHT) - && edgeFlags == ViewDragHelper.EDGE_LEFT; - - boolean edgeStartRight = (mDragEdge == DRAG_EDGE_LEFT) - && edgeFlags == ViewDragHelper.EDGE_RIGHT; - - if (edgeStartLeft || edgeStartRight) { - mDragHelper.captureChildView(mMainView, pointerId); - } - } - - @Override - public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { - super.onViewPositionChanged(changedView, left, top, dx, dy); - if (mMode == MODE_SAME_LEVEL) { - if (mDragEdge == DRAG_EDGE_LEFT || mDragEdge == DRAG_EDGE_RIGHT) { - mSecondaryView.offsetLeftAndRight(dx); - } else { - mSecondaryView.offsetTopAndBottom(dy); - } - } - ViewCompat.postInvalidateOnAnimation(SwipeRevealLayout.this); - } - }; - - private int pxToDp(int px) { - Resources resources = getContext().getResources(); - DisplayMetrics metrics = resources.getDisplayMetrics(); - return (int) (px / ((float)metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT)); - } -} \ No newline at end of file diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java index f9df28d5ca..3489210b62 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java @@ -11,10 +11,8 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; -import android.widget.Toast; import com.squareup.picasso.Callback; import com.squareup.picasso.Picasso; @@ -71,12 +69,6 @@ public class UserListAdapter extends RecyclerView.Adapter - + android:background="?attr/selectableItemBackground" + android:clickable="true"> - + - + - - - - - - - - - + - - - + android:layout_height="wrap_content" /> + - - - \ No newline at end of file + \ No newline at end of file diff --git a/android/app/src/main/res/values/attrs.xml b/android/app/src/main/res/values/attrs.xml deleted file mode 100644 index c12f28ccde..0000000000 --- a/android/app/src/main/res/values/attrs.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file From b65f2d46a64d5bcda85938edd621bfec162c8c29 Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Fri, 10 Aug 2018 18:18:24 -0300 Subject: [PATCH 09/30] Android - People - Letters styling in row --- .../hifiinterface/view/UserListAdapter.java | 8 +++++-- android/app/src/main/res/layout/user_item.xml | 21 +++++++++++++++---- android/app/src/main/res/values/colors.xml | 1 + android/app/src/main/res/values/strings.xml | 2 +- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java index 3489210b62..202fd82c8b 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java @@ -67,9 +67,9 @@ public class UserListAdapter extends RecyclerView.Adapter - + + android:layout_height="wrap_content"> + + + \ No newline at end of file diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml index 7e6cf52d36..9de0ae5347 100644 --- a/android/app/src/main/res/values/colors.xml +++ b/android/app/src/main/res/values/colors.xml @@ -18,4 +18,5 @@ #99000000 #292929 #23B2E7 + #62D5C6 diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 9fe7f0cbee..d5da2f857b 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -23,5 +23,5 @@ No places exist with that name Privacy Policy Your Last Location - + Online From 610306c49c05353bd3f1c0b90aaa5db411272421 Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Fri, 10 Aug 2018 19:02:01 -0300 Subject: [PATCH 10/30] Android - People - add friend star --- android/app/src/main/res/drawable/ic_star.xml | 4 ++++ android/app/src/main/res/layout/user_item.xml | 10 +++++++++- android/app/src/main/res/values/colors.xml | 2 ++ 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 android/app/src/main/res/drawable/ic_star.xml diff --git a/android/app/src/main/res/drawable/ic_star.xml b/android/app/src/main/res/drawable/ic_star.xml new file mode 100644 index 0000000000..abd1798942 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_star.xml @@ -0,0 +1,4 @@ + + + diff --git a/android/app/src/main/res/layout/user_item.xml b/android/app/src/main/res/layout/user_item.xml index 703c99945e..dec9f04af8 100644 --- a/android/app/src/main/res/layout/user_item.xml +++ b/android/app/src/main/res/layout/user_item.xml @@ -44,5 +44,13 @@ android:fontFamily="@font/raleway_italic"/> - + \ No newline at end of file diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml index 9de0ae5347..ecca075298 100644 --- a/android/app/src/main/res/values/colors.xml +++ b/android/app/src/main/res/values/colors.xml @@ -19,4 +19,6 @@ #292929 #23B2E7 #62D5C6 + #FBD92A + #8A8A8A From b381a182bd66d0ee8c4d22dcbdf3042f0975e262 Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Mon, 13 Aug 2018 14:31:20 -0300 Subject: [PATCH 11/30] Android - People - add padding to list --- android/app/src/main/res/layout/fragment_friends.xml | 3 +++ android/app/src/main/res/values/dimens.xml | 2 ++ 2 files changed, 5 insertions(+) diff --git a/android/app/src/main/res/layout/fragment_friends.xml b/android/app/src/main/res/layout/fragment_friends.xml index 8129f5d53c..4e4abb7f28 100644 --- a/android/app/src/main/res/layout/fragment_friends.xml +++ b/android/app/src/main/res/layout/fragment_friends.xml @@ -12,6 +12,9 @@ app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" + android:paddingTop="@dimen/list_vertical_padding" + android:paddingBottom="@dimen/list_vertical_padding" + android:clipToPadding="false" android:layout_width="0dp" android:layout_height="0dp" /> diff --git a/android/app/src/main/res/values/dimens.xml b/android/app/src/main/res/values/dimens.xml index bb5be1c070..d40132939b 100644 --- a/android/app/src/main/res/values/dimens.xml +++ b/android/app/src/main/res/values/dimens.xml @@ -37,4 +37,6 @@ 101dp 425dp + 8dp + From 1bfd6fe97dd1e73999f4db9995c1cc4958f14f3b Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Mon, 13 Aug 2018 20:32:45 -0300 Subject: [PATCH 12/30] Android - People - Sliding panel (for users actions) --- android/app/build.gradle | 2 + .../hifiinterface/MainActivity.java | 12 +++++- .../fragment/FriendsFragment.java | 39 +++++++++++++++++++ .../hifiinterface/view/UserListAdapter.java | 19 ++++++++- .../src/main/res/layout/fragment_friends.xml | 30 ++++++++------ android/app/src/main/res/values/strings.xml | 3 ++ android/build.gradle | 1 + 7 files changed, 93 insertions(+), 13 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index d5058a7f40..a32ab20f6c 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -144,5 +144,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') } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java index 220a69381d..d259e18ee7 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java @@ -150,7 +150,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On private void loadFragment(Fragment fragment, String title, boolean addToBackStack) { FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction ft = fragmentManager.beginTransaction(); - ft.replace(R.id.content_frame, fragment); + ft.replace(R.id.content_frame, fragment, getString(R.string.tagFragmentPeople)); + if (addToBackStack) { ft.addToBackStack(title); } @@ -297,6 +298,15 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On @Override public void onBackPressed() { + // if a fragment needs to internally manage back presses.. + FragmentManager fm = getFragmentManager(); + Fragment friendsFragment = fm.findFragmentByTag(getString(R.string.tagFragmentPeople)); + if (friendsFragment != null && friendsFragment instanceof FriendsFragment) { + if (((FriendsFragment) friendsFragment).onBackPressed()) { + return; + } + } + int index = getFragmentManager().getBackStackEntryCount() - 1; if (index > 0) { super.onBackPressed(); diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java index e70ecfbc57..2cd80bcca9 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java @@ -10,6 +10,8 @@ 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.view.UserListAdapter; @@ -20,7 +22,9 @@ public class FriendsFragment extends Fragment { public native String nativeGetAccessToken(); private RecyclerView mUsersView; + private View mUserActions; private UserListAdapter mUsersAdapter; + private SlidingUpPanelLayout mSlidingUpPanelLayout; public FriendsFragment() { // Required empty public constructor @@ -45,9 +49,44 @@ public class FriendsFragment extends Fragment { GridLayoutManager gridLayoutMgr = new GridLayoutManager(getContext(), numberOfColumns); mUsersView.setLayoutManager(gridLayoutMgr); mUsersAdapter = new UserListAdapter(getContext(), accessToken); + + mUserActions = rootView.findViewById(R.id.userActionsLayout); + + mSlidingUpPanelLayout = rootView.findViewById(R.id.sliding_layout); + mSlidingUpPanelLayout.setPanelHeight(0); + mUsersAdapter.setClickListener(new UserListAdapter.ItemClickListener() { + @Override + public void onItemClick(View view, int position, UserListAdapter.User user) { + // 1. 'select' user + // .. + // 2. adapt options + // .. + // 3. show + mSlidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.EXPANDED); + } + }); mUsersView.setAdapter(mUsersAdapter); + mSlidingUpPanelLayout.setFadeOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + mSlidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED); + } + }); + return rootView; } + /** + * 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); + return true; + } else { + return false; + } + } } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java index 202fd82c8b..ad3a5cc136 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java @@ -34,6 +34,7 @@ public class UserListAdapter extends RecyclerView.Adapter mUsers = new ArrayList<>(); + private ItemClickListener mClickListener; public UserListAdapter(Context c, String accessToken) { mContext = c; @@ -99,7 +100,7 @@ public class UserListAdapter extends RecyclerView.Adapter - + android:gravity="bottom" + sothree:umanoShadowHeight="4dp" + android:background="@color/backgroundLight"> + + android:layout_width="match_parent" + android:layout_height="match_parent" /> - + + + + diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index d5da2f857b..ea4e59a35a 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -24,4 +24,7 @@ Privacy Policy Your Last Location Online + + + tagFragmentPeople diff --git a/android/build.gradle b/android/build.gradle index bc39c30472..6ecdd34542 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -28,6 +28,7 @@ allprojects { repositories { jcenter() google() + mavenCentral() } } From 36e9c8ba3bceceb4995cd003cf34a3f61d5f80f7 Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Tue, 14 Aug 2018 12:44:09 -0300 Subject: [PATCH 13/30] Android - People - UI changes to match design spec --- .../res/drawable/ic_delete_black_24dp.xml | 9 +++ .../app/src/main/res/drawable/ic_visit.xml | 9 +++ .../src/main/res/layout/fragment_friends.xml | 64 +++++++++++++++++-- android/app/src/main/res/layout/user_item.xml | 6 +- 4 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 android/app/src/main/res/drawable/ic_delete_black_24dp.xml create mode 100644 android/app/src/main/res/drawable/ic_visit.xml diff --git a/android/app/src/main/res/drawable/ic_delete_black_24dp.xml b/android/app/src/main/res/drawable/ic_delete_black_24dp.xml new file mode 100644 index 0000000000..39e64d6980 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_delete_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/app/src/main/res/drawable/ic_visit.xml b/android/app/src/main/res/drawable/ic_visit.xml new file mode 100644 index 0000000000..0ecbf6838c --- /dev/null +++ b/android/app/src/main/res/drawable/ic_visit.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/app/src/main/res/layout/fragment_friends.xml b/android/app/src/main/res/layout/fragment_friends.xml index f0f83089fc..3c8e0af6bc 100644 --- a/android/app/src/main/res/layout/fragment_friends.xml +++ b/android/app/src/main/res/layout/fragment_friends.xml @@ -1,12 +1,13 @@ - - + + + + + + + + diff --git a/android/app/src/main/res/layout/user_item.xml b/android/app/src/main/res/layout/user_item.xml index dec9f04af8..acdc6a8aea 100644 --- a/android/app/src/main/res/layout/user_item.xml +++ b/android/app/src/main/res/layout/user_item.xml @@ -26,7 +26,8 @@ android:id="@+id/userName" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:fontFamily="@font/raleway" /> + android:fontFamily="@font/raleway" + android:textColor="@color/menuOption"/> @@ -41,7 +42,8 @@ android:id="@+id/userLocation" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:fontFamily="@font/raleway_italic"/> + android:fontFamily="@font/raleway_italic" + android:textColor="@color/menuOption"/> Date: Tue, 14 Aug 2018 13:15:21 -0300 Subject: [PATCH 14/30] Android - People - swap-fix actions icons --- android/app/src/main/res/layout/fragment_friends.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/res/layout/fragment_friends.xml b/android/app/src/main/res/layout/fragment_friends.xml index 3c8e0af6bc..cc541e569e 100644 --- a/android/app/src/main/res/layout/fragment_friends.xml +++ b/android/app/src/main/res/layout/fragment_friends.xml @@ -39,7 +39,7 @@ 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:src="@drawable/ic_visit" android:tint="@color/white_opaque"/> Date: Tue, 14 Aug 2018 15:15:24 -0300 Subject: [PATCH 15/30] Android - People - Use designed icon for visit + add fade color --- .../src/main/res/drawable/ic_teleporticon.xml | 31 +++++++++++++++++++ .../app/src/main/res/drawable/ic_visit.xml | 9 ------ .../src/main/res/layout/fragment_friends.xml | 4 +-- android/app/src/main/res/values/colors.xml | 1 + 4 files changed, 34 insertions(+), 11 deletions(-) create mode 100644 android/app/src/main/res/drawable/ic_teleporticon.xml delete mode 100644 android/app/src/main/res/drawable/ic_visit.xml diff --git a/android/app/src/main/res/drawable/ic_teleporticon.xml b/android/app/src/main/res/drawable/ic_teleporticon.xml new file mode 100644 index 0000000000..429e6b795d --- /dev/null +++ b/android/app/src/main/res/drawable/ic_teleporticon.xml @@ -0,0 +1,31 @@ + + + + + + + + + diff --git a/android/app/src/main/res/drawable/ic_visit.xml b/android/app/src/main/res/drawable/ic_visit.xml deleted file mode 100644 index 0ecbf6838c..0000000000 --- a/android/app/src/main/res/drawable/ic_visit.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/android/app/src/main/res/layout/fragment_friends.xml b/android/app/src/main/res/layout/fragment_friends.xml index cc541e569e..6cee738244 100644 --- a/android/app/src/main/res/layout/fragment_friends.xml +++ b/android/app/src/main/res/layout/fragment_friends.xml @@ -6,7 +6,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="bottom" - app:umanoFadeColor="@android:color/transparent" + app:umanoFadeColor="@color/slidingUpPanelFadeColor" app:umanoShadowHeight="4dp" android:background="@color/backgroundLight"> @@ -39,7 +39,7 @@ app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" android:layout_marginStart="@dimen/activity_horizontal_margin" - android:src="@drawable/ic_visit" + android:src="@drawable/ic_teleporticon" android:tint="@color/white_opaque"/> #62D5C6 #FBD92A #8A8A8A + #40000000 From 410873312884b4cb9ad4c24fc5fdc3398c13f105 Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Tue, 14 Aug 2018 15:23:09 -0300 Subject: [PATCH 16/30] Android - People - tags spacing --- .../app/src/main/res/layout/fragment_friends.xml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/android/app/src/main/res/layout/fragment_friends.xml b/android/app/src/main/res/layout/fragment_friends.xml index 6cee738244..62b62b3caf 100644 --- a/android/app/src/main/res/layout/fragment_friends.xml +++ b/android/app/src/main/res/layout/fragment_friends.xml @@ -9,7 +9,7 @@ app:umanoFadeColor="@color/slidingUpPanelFadeColor" app:umanoShadowHeight="4dp" android:background="@color/backgroundLight"> - + + android:background="@color/backgroundDark"> + + android:tint="@color/white_opaque" /> + android:layout_marginStart="32dp" /> + android:tint="@color/white_opaque" /> + android:layout_marginStart="32dp" /> From 3032c7802b5494ef3ac71af7710fd9b1ba985bfa Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Tue, 14 Aug 2018 17:56:42 -0300 Subject: [PATCH 17/30] Android - People - Star for friends, toggle UI, use data from API for initial status. Change+Post is pending --- .../provider/EndpointUsersProvider.java | 5 ++- .../hifiinterface/provider/UsersProvider.java | 3 ++ .../hifiinterface/view/UserListAdapter.java | 37 +++++++++++++++++++ android/app/src/main/res/layout/user_item.xml | 3 ++ 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/provider/EndpointUsersProvider.java b/android/app/src/main/java/io/highfidelity/hifiinterface/provider/EndpointUsersProvider.java index 3e034b654b..d35249a476 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/provider/EndpointUsersProvider.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/provider/EndpointUsersProvider.java @@ -1,5 +1,7 @@ package io.highfidelity.hifiinterface.provider; +import android.util.Log; + import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -47,6 +49,7 @@ public class EndpointUsersProvider implements UsersProvider { return chain.proceed(request); } }); + Log.d("[USERZ]", "Authorization: Bearer " + accessToken);// CLD DELETE THIS LINE! OkHttpClient client = httpClient.build(); mRetrofit = new Retrofit.Builder() @@ -60,7 +63,7 @@ public class EndpointUsersProvider implements UsersProvider { @Override public void retrieve(UsersCallback usersCallback) { Call friendsCall = mEndpointUsersProviderService.getUsers( - "friends", + CONNECTION_FILTER_CONNECTIONS, 400, null); friendsCall.enqueue(new Callback() { diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/provider/UsersProvider.java b/android/app/src/main/java/io/highfidelity/hifiinterface/provider/UsersProvider.java index 13ed812ce6..75f978800f 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/provider/UsersProvider.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/provider/UsersProvider.java @@ -10,6 +10,9 @@ import io.highfidelity.hifiinterface.view.UserListAdapter; public interface UsersProvider { + public static String CONNECTION_TYPE_FRIEND = "friend"; + public static String CONNECTION_FILTER_CONNECTIONS = "connections"; + void retrieve(UsersProvider.UsersCallback usersCallback); interface UsersCallback { diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java index ad3a5cc136..bcb379c501 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java @@ -2,8 +2,10 @@ package io.highfidelity.hifiinterface.view; import android.content.Context; import android.graphics.Bitmap; +import android.graphics.PorterDuff; 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; @@ -69,6 +71,7 @@ public class UserListAdapter extends RecyclerView.Adapter Date: Tue, 14 Aug 2018 20:22:23 -0300 Subject: [PATCH 18/30] Android - People - Befriending working --- .../provider/EndpointUsersProvider.java | 78 +++++++++++++++++++ .../hifiinterface/provider/UsersProvider.java | 12 +++ .../hifiinterface/view/UserListAdapter.java | 51 ++++++++++-- 3 files changed, 135 insertions(+), 6 deletions(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/provider/EndpointUsersProvider.java b/android/app/src/main/java/io/highfidelity/hifiinterface/provider/EndpointUsersProvider.java index d35249a476..102d0995ee 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/provider/EndpointUsersProvider.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/provider/EndpointUsersProvider.java @@ -15,7 +15,11 @@ 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; /** @@ -93,11 +97,85 @@ public class EndpointUsersProvider implements UsersProvider { }); } + public class UserActionRetrofitCallback implements Callback { + + UserActionCallback callback; + + public UserActionRetrofitCallback(UserActionCallback callback) { + this.callback = callback; + } + + @Override + public void onResponse(Call call, retrofit2.Response 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 call, Throwable t) { + callback.requestError(new Exception(t), t.getMessage()); + } + } + + @Override + public void addFriend(String friendUserName, UserActionCallback callback) { + Call friendCall = mEndpointUsersProviderService.addFriend(new BodyAddFriend(friendUserName)); + friendCall.enqueue(new UserActionRetrofitCallback(callback)); + } + + @Override + public void removeFriend(String friendUserName, UserActionCallback callback) { + Call friendCall = mEndpointUsersProviderService.removeFriend(friendUserName); + friendCall.enqueue(new UserActionRetrofitCallback(callback)); + } + + @Override + public void removeConnection(String connectionUserName, UserActionCallback callback) { + Call connectionCall = mEndpointUsersProviderService.removeConnection(connectionUserName); + connectionCall.enqueue(new UserActionRetrofitCallback(callback)); + } + public interface EndpointUsersProviderService { @GET("api/v1/users") Call getUsers(@Query("filter") String filter, @Query("per_page") int perPage, @Query("online") Boolean online); + + @DELETE("api/v1/user/connections/{connectionUserName}") + Call removeConnection(@Path("connectionUserName") String connectionUserName); + + @DELETE("api/v1/user/friends/{friendUserName}") + Call removeFriend(@Path("friendUserName") String friendUserName); + + @POST("api/v1/user/friends") + Call addFriend(@Body BodyAddFriend friendUserName); + + /* response + { + "status": "success" + } + */ + } + + class BodyAddFriend { + String username; + public BodyAddFriend(String username) { + this.username = username; + } } class UsersResponse { diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/provider/UsersProvider.java b/android/app/src/main/java/io/highfidelity/hifiinterface/provider/UsersProvider.java index 75f978800f..0088506407 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/provider/UsersProvider.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/provider/UsersProvider.java @@ -20,4 +20,16 @@ public interface UsersProvider { 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); + } + } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java index bcb379c501..7e6ccc10d6 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java @@ -71,7 +71,7 @@ public class UserListAdapter extends RecyclerView.Adapter Date: Wed, 15 Aug 2018 14:50:53 -0300 Subject: [PATCH 19/30] Android - People - Bigger touch area for friend star --- .../hifiinterface/view/UserListAdapter.java | 21 ++++++++----------- android/app/src/main/res/layout/user_item.xml | 21 ++++++++++++------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java index 7e6ccc10d6..b92c0dd97a 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java @@ -105,19 +105,16 @@ public class UserListAdapter extends RecyclerView.Adapter toggle()); } private void refreshUI() { @@ -136,7 +133,7 @@ public class UserListAdapter extends RecyclerView.Adapter - + android:layout_marginEnd="5.5dp"> + + + \ No newline at end of file From dd5bd0229af41014b5f3b95c0da1d0c18501561c Mon Sep 17 00:00:00 2001 From: Cristian Duarte Date: Wed, 15 Aug 2018 14:51:37 -0300 Subject: [PATCH 20/30] Android - People - Hide visit option if user is offline --- .../io/highfidelity/hifiinterface/fragment/FriendsFragment.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java index 2cd80bcca9..37576a0e2f 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java @@ -13,6 +13,7 @@ import android.view.ViewGroup; import com.sothree.slidinguppanel.SlidingUpPanelLayout; import io.highfidelity.hifiinterface.R; +import io.highfidelity.hifiinterface.provider.UsersProvider; import io.highfidelity.hifiinterface.view.UserListAdapter; public class FriendsFragment extends Fragment { @@ -61,6 +62,7 @@ public class FriendsFragment extends Fragment { // .. // 2. adapt options // .. + rootView.findViewById(R.id.userActionVisit).setVisibility(user.online?View.VISIBLE:View.GONE); // 3. show mSlidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.EXPANDED); } From 81e24bb28ed02c3bb74d9ac0db2549eb44db7426 Mon Sep 17 00:00:00 2001 From: Cristian Duarte Date: Wed, 15 Aug 2018 17:29:28 -0300 Subject: [PATCH 21/30] Android - People - Remove connection (from People) working with API --- .../fragment/FriendsFragment.java | 46 ++++++++++++++++++- .../hifiinterface/view/UserListAdapter.java | 6 +-- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java index 37576a0e2f..306b965501 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java @@ -2,7 +2,9 @@ package io.highfidelity.hifiinterface.fragment; import android.app.Fragment; +import android.content.DialogInterface; import android.os.Bundle; +import android.support.v7.app.AlertDialog; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; @@ -13,6 +15,7 @@ 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; @@ -26,6 +29,8 @@ public class FriendsFragment extends Fragment { private View mUserActions; private UserListAdapter mUsersAdapter; private SlidingUpPanelLayout mSlidingUpPanelLayout; + private EndpointUsersProvider mUsersProvider; + private String mSelectedUsername; public FriendsFragment() { // Required empty public constructor @@ -42,6 +47,7 @@ public class FriendsFragment extends Fragment { View rootView = inflater.inflate(R.layout.fragment_friends, container, false); String accessToken = nativeGetAccessToken(); + mUsersProvider = new EndpointUsersProvider(accessToken); Log.d("[USERS]", "token : [" + accessToken + "]"); @@ -49,16 +55,21 @@ public class FriendsFragment extends Fragment { int numberOfColumns = 1; GridLayoutManager gridLayoutMgr = new GridLayoutManager(getContext(), numberOfColumns); mUsersView.setLayoutManager(gridLayoutMgr); - mUsersAdapter = new UserListAdapter(getContext(), accessToken); + + mUsersAdapter = new UserListAdapter(getContext(), mUsersProvider); mUserActions = rootView.findViewById(R.id.userActionsLayout); mSlidingUpPanelLayout = rootView.findViewById(R.id.sliding_layout); mSlidingUpPanelLayout.setPanelHeight(0); + + rootView.findViewById(R.id.userActionDelete).setOnClickListener(view -> onRemoveConnectionClick()); + 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 // .. @@ -73,12 +84,44 @@ public class FriendsFragment extends Fragment { @Override public void onClick(View view) { mSlidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED); + mSelectedUsername = null; } }); return rootView; } + private void onRemoveConnectionClick() { + if (mSelectedUsername == null) return; + + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setMessage("Remove this user 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() { + // CLD: Show success message + 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 @@ -86,6 +129,7 @@ public class FriendsFragment extends Fragment { public boolean onBackPressed() { if (mSlidingUpPanelLayout.getPanelState().equals(SlidingUpPanelLayout.PanelState.EXPANDED)) { mSlidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED); + mSelectedUsername = null; return true; } else { return false; diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java index b92c0dd97a..c1ea698b08 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java @@ -38,14 +38,14 @@ public class UserListAdapter extends RecyclerView.Adapter mUsers = new ArrayList<>(); private ItemClickListener mClickListener; - public UserListAdapter(Context c, String accessToken) { + public UserListAdapter(Context c, UsersProvider usersProvider) { mContext = c; mInflater = LayoutInflater.from(mContext); - mProvider = new EndpointUsersProvider(accessToken); + mProvider = usersProvider; loadUsers(); } - private void loadUsers() { + public void loadUsers() { mProvider.retrieve(new UsersProvider.UsersCallback() { @Override public void retrieveOk(List users) { From 3217f61a53bf749e93bbe5d299b1abe375552ffd Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Wed, 15 Aug 2018 20:21:34 -0300 Subject: [PATCH 22/30] Android - People - Visit Online connections working --- android/app/src/main/cpp/native.cpp | 5 ++++ .../hifiinterface/InterfaceActivity.java | 5 ++++ .../hifiinterface/MainActivity.java | 16 ++++++++++- .../fragment/FriendsFragment.java | 27 +++++++++++++++++++ .../provider/EndpointUsersProvider.java | 14 ++++++++++ .../hifiinterface/view/UserListAdapter.java | 6 ++++- android/app/src/main/res/layout/user_item.xml | 1 + 7 files changed, 72 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/cpp/native.cpp b/android/app/src/main/cpp/native.cpp index 9f0e088157..ce5af01f29 100644 --- a/android/app/src/main/cpp/native.cpp +++ b/android/app/src/main/cpp/native.cpp @@ -209,6 +209,11 @@ JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeGotoUr DependencyManager::get()->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()->goToUser(jniUsername.toString(), false); +} + JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnPause(JNIEnv* env, jobject obj) { } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java index 8fd8b9d0e6..f161783d6a 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java @@ -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)); } } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java index d259e18ee7..1916962756 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java @@ -37,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"; @@ -251,6 +252,14 @@ 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(); @@ -280,6 +289,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() { diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java index 306b965501..4d48743883 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java @@ -2,6 +2,7 @@ package io.highfidelity.hifiinterface.fragment; import android.app.Fragment; +import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; import android.support.v7.app.AlertDialog; @@ -32,6 +33,8 @@ public class FriendsFragment extends Fragment { private EndpointUsersProvider mUsersProvider; private String mSelectedUsername; + private OnHomeInteractionListener mListener; + public FriendsFragment() { // Required empty public constructor } @@ -65,6 +68,15 @@ public class FriendsFragment extends Fragment { 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) { @@ -135,4 +147,19 @@ public class FriendsFragment extends Fragment { 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); + } } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/provider/EndpointUsersProvider.java b/android/app/src/main/java/io/highfidelity/hifiinterface/provider/EndpointUsersProvider.java index 102d0995ee..af5a441737 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/provider/EndpointUsersProvider.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/provider/EndpointUsersProvider.java @@ -85,6 +85,10 @@ public class EndpointUsersProvider implements UsersProvider { 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); @@ -199,6 +203,7 @@ public class EndpointUsersProvider implements UsersProvider { boolean online; String connection; Images images; + LocationData location; } class Images { @@ -208,4 +213,13 @@ public class EndpointUsersProvider implements UsersProvider { String tiny; } + class LocationData { + public LocationData() {} + NameContainer root; + NameContainer domain; + } + class NameContainer { + String name; + } + } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java index c1ea698b08..c1a69639c0 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java @@ -70,10 +70,12 @@ public class UserListAdapter extends RecyclerView.Adapter From 1a343c1c3313732321c97f364a9ac32ff935c518 Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Wed, 15 Aug 2018 20:57:04 -0300 Subject: [PATCH 23/30] Android - People - Hide People menu if the user is not logged in --- .../java/io/highfidelity/hifiinterface/MainActivity.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java index 1916962756..1d34470146 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java @@ -58,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; @@ -77,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); @@ -167,11 +170,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(""); } } From 6f83c7bff0dd157d641502503a07a25fedcde260 Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Wed, 15 Aug 2018 21:01:42 -0300 Subject: [PATCH 24/30] Android - People - Show name of user to delete in confirmation --- .../io/highfidelity/hifiinterface/fragment/FriendsFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java index 4d48743883..35e64aae2f 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java @@ -107,7 +107,7 @@ public class FriendsFragment extends Fragment { if (mSelectedUsername == null) return; AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setMessage("Remove this user from People?"); + builder.setMessage("Remove '" + mSelectedUsername + "' from People?"); builder.setPositiveButton("Remove", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { From 43b2a862242dc2b2539197fcf7746844d6edb6cf Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Wed, 15 Aug 2018 21:36:11 -0300 Subject: [PATCH 25/30] Android - People - Make list refreshable --- .../fragment/FriendsFragment.java | 25 +++++++++++++++++++ .../hifiinterface/view/UserListAdapter.java | 21 ++++++++++++++-- .../src/main/res/layout/fragment_friends.xml | 17 ++++++++----- 3 files changed, 55 insertions(+), 8 deletions(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java index 35e64aae2f..b30b387f4b 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java @@ -5,6 +5,7 @@ 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; @@ -34,6 +35,7 @@ public class FriendsFragment extends Fragment { private String mSelectedUsername; private OnHomeInteractionListener mListener; + private SwipeRefreshLayout mSwipeRefreshLayout; public FriendsFragment() { // Required empty public constructor @@ -54,12 +56,15 @@ public class FriendsFragment extends Fragment { 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); @@ -90,6 +95,24 @@ public class FriendsFragment extends Fragment { 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() { @@ -100,6 +123,8 @@ public class FriendsFragment extends Fragment { } }); + mSwipeRefreshLayout.setOnRefreshListener(() -> mUsersAdapter.loadUsers()); + return rootView; } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java index c1a69639c0..2f1f1c8a82 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java @@ -2,7 +2,6 @@ package io.highfidelity.hifiinterface.view; import android.content.Context; import android.graphics.Bitmap; -import android.graphics.PorterDuff; import android.graphics.drawable.BitmapDrawable; import android.net.Uri; import android.support.v4.content.ContextCompat; @@ -23,7 +22,6 @@ import java.util.ArrayList; import java.util.List; import io.highfidelity.hifiinterface.R; -import io.highfidelity.hifiinterface.provider.EndpointUsersProvider; import io.highfidelity.hifiinterface.provider.UsersProvider; /** @@ -37,6 +35,7 @@ public class UserListAdapter extends RecyclerView.Adapter mUsers = new ArrayList<>(); private ItemClickListener mClickListener; + private AdapterListener mAdapterListener; public UserListAdapter(Context c, UsersProvider usersProvider) { mContext = c; @@ -45,17 +44,29 @@ public class UserListAdapter extends RecyclerView.Adapter 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); } }); } @@ -219,4 +230,10 @@ public class UserListAdapter extends RecyclerView.Adapter - + android:layout_height="match_parent"> + + Date: Thu, 16 Aug 2018 12:36:25 -0300 Subject: [PATCH 26/30] delete access token log --- .../hifiinterface/provider/EndpointUsersProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/provider/EndpointUsersProvider.java b/android/app/src/main/java/io/highfidelity/hifiinterface/provider/EndpointUsersProvider.java index af5a441737..7c32a8e8fb 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/provider/EndpointUsersProvider.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/provider/EndpointUsersProvider.java @@ -53,7 +53,7 @@ public class EndpointUsersProvider implements UsersProvider { return chain.proceed(request); } }); - Log.d("[USERZ]", "Authorization: Bearer " + accessToken);// CLD DELETE THIS LINE! + OkHttpClient client = httpClient.build(); mRetrofit = new Retrofit.Builder() From 99760e2b401454cc2da9e37a2a6abe77a36caa85 Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Thu, 16 Aug 2018 17:29:25 -0300 Subject: [PATCH 27/30] Android - People - Better navigation after logged in prevents log in screen to appear after pressing back --- .../hifiinterface/MainActivity.java | 74 +++++++++++++++---- android/app/src/main/res/values/strings.xml | 3 + 2 files changed, 64 insertions(+), 13 deletions(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java index 1d34470146..28af228541 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java @@ -7,6 +7,7 @@ import android.content.Intent; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.os.Bundle; +import android.os.Handler; import android.support.annotation.NonNull; import android.support.design.widget.NavigationView; import android.support.v4.content.ContextCompat; @@ -114,7 +115,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On loadLoginFragment(); break; case "Home": - loadHomeFragment(); + loadHomeFragment(true); break; case "Privacy Policy": loadPrivacyPolicyFragment(); @@ -128,33 +129,57 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On } - private void loadHomeFragment() { + private void loadHomeFragment(boolean addToBackStack) { Fragment fragment = HomeFragment.newInstance(); - loadFragment(fragment, getString(R.string.home), true); + 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 loadPeopleFragment() { Fragment fragment = FriendsFragment.newInstance(); - loadFragment(fragment, getString(R.string.people), true); + loadFragment(fragment, getString(R.string.people), getString(R.string.tagFragmentPeople), true); } - private void loadFragment(Fragment fragment, String title, boolean addToBackStack) { + 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() + : ""; + + Log.d("[BACK]", "Before it's " + currentFragmentName + ", now adding " + title + " (before) backstackCount " + fragmentManager.getBackStackEntryCount()); + + // check if it's the same fragment than the one being shown + 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, getString(R.string.tagFragmentPeople)); + ft.replace(R.id.content_frame, fragment, tag); if (addToBackStack) { ft.addToBackStack(title); @@ -162,6 +187,14 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On ft.commit(); setTitle(title); mDrawerLayout.closeDrawer(mNavigationView); + final Handler handler = new Handler(); + handler.postDelayed(new Runnable() { + @Override + public void run() { + //Do something after 100ms + Log.d("[BACK]", "added " + title + " backstackCount " + fragmentManager.getBackStackEntryCount()); + } + }, 100); } @@ -217,7 +250,7 @@ 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(); @@ -239,6 +272,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) { @@ -267,7 +313,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On @Override public void onLoginCompleted() { - loadHomeFragment(); + loadHomeFragment(false); updateLoginMenu(); if (backToScene) { backToScene = false; @@ -319,6 +365,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On public void onBackPressed() { // 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()) { @@ -326,19 +373,20 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On } } - int index = getFragmentManager().getBackStackEntryCount() - 1; + int index = fm.getBackStackEntryCount() - 1; + if (index > 0) { super.onBackPressed(); index--; if (index > -1) { - setTitle(getFragmentManager().getBackStackEntryAt(index).getName()); + setTitle(fm.getBackStackEntryAt(index).getName()); } if (backToScene) { backToScene = false; goToLastLocation(); } } else { - finishAffinity(); + finishAffinity(); } } diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index ea4e59a35a..dae4fe26f9 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -26,5 +26,8 @@ Online + tagFragmentHome + tagFragmentLogin + tagFragmentPolicy tagFragmentPeople From a3927f4d4c0a83151d1bc37b623440cca71ef6b2 Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Thu, 16 Aug 2018 17:31:31 -0300 Subject: [PATCH 28/30] Android - People - Remove fragments and stack related logs --- .../highfidelity/hifiinterface/MainActivity.java | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java index 28af228541..db6f0fca24 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java @@ -7,7 +7,6 @@ import android.content.Intent; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.os.Bundle; -import android.os.Handler; import android.support.annotation.NonNull; import android.support.design.widget.NavigationView; import android.support.v4.content.ContextCompat; @@ -155,18 +154,13 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On 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() : ""; - - Log.d("[BACK]", "Before it's " + currentFragmentName + ", now adding " + title + " (before) backstackCount " + fragmentManager.getBackStackEntryCount()); - - // check if it's the same fragment than the one being shown if (currentFragmentName.equals(title)) { mDrawerLayout.closeDrawer(mNavigationView); - return;// cancel as we are already in that fragment + return; // cancel as we are already in that fragment } // go back until first transaction @@ -187,14 +181,6 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On ft.commit(); setTitle(title); mDrawerLayout.closeDrawer(mNavigationView); - final Handler handler = new Handler(); - handler.postDelayed(new Runnable() { - @Override - public void run() { - //Do something after 100ms - Log.d("[BACK]", "added " + title + " backstackCount " + fragmentManager.getBackStackEntryCount()); - } - }, 100); } From 4851b44de1720ff2dcb1ea76b7a6f9c07ee18500 Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Thu, 16 Aug 2018 19:39:44 -0300 Subject: [PATCH 29/30] Android - People - Hide actions panel after remove --- .../highfidelity/hifiinterface/fragment/FriendsFragment.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java index b30b387f4b..a313b3591e 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java @@ -139,7 +139,8 @@ public class FriendsFragment extends Fragment { mUsersProvider.removeConnection(mSelectedUsername, new UsersProvider.UserActionCallback() { @Override public void requestOk() { - // CLD: Show success message + mSlidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED); + mSelectedUsername = null; mUsersAdapter.loadUsers(); } From ccb5bae4b6c942acb304176a2feabb0ab440bfad Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Fri, 17 Aug 2018 14:24:04 -0300 Subject: [PATCH 30/30] Android - People - Code review --- .../hifiinterface/fragment/FriendsFragment.java | 6 ++++-- .../hifiinterface/view/UserListAdapter.java | 16 ++++++++++++---- android/app/src/main/res/values/strings.xml | 1 - 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java index a313b3591e..2a008d7950 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java @@ -90,7 +90,7 @@ public class FriendsFragment extends Fragment { // .. // 2. adapt options // .. - rootView.findViewById(R.id.userActionVisit).setVisibility(user.online?View.VISIBLE:View.GONE); + rootView.findViewById(R.id.userActionVisit).setVisibility(user.online ? View.VISIBLE : View.GONE); // 3. show mSlidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.EXPANDED); } @@ -129,7 +129,9 @@ public class FriendsFragment extends Fragment { } private void onRemoveConnectionClick() { - if (mSelectedUsername == null) return; + if (mSelectedUsername == null) { + return; + } AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setMessage("Remove '" + mSelectedUsername + "' from People?"); diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java index 2f1f1c8a82..9f62b21250 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java @@ -66,7 +66,9 @@ public class UserListAdapter extends RecyclerView.Adapter Interface Home - Friends People Open in browser Share link