From 47cf90727a5634c05373ddc4bd1b73dd9d8b30a5 Mon Sep 17 00:00:00 2001 From: Stefan Schueller Date: Sat, 29 Dec 2018 22:10:13 +0100 Subject: [PATCH] WIP --- app/src/main/AndroidManifest.xml | 7 +- .../peertube/activity/AccountActivity.java | 123 +++++++++++++++++ .../peertube/activity/LoginActivity.java | 14 +- .../peertube/activity/UserActivity.java | 41 ------ .../peertube/activity/VideoListActivity.java | 41 +++++- .../peertube/application/AppApplication.java | 18 +++ .../network/AuthorizationInterceptor.java | 42 +++++- .../peertube/network/GetUserService.java | 9 ++ .../peertube/network/RetrofitInstance.java | 1 - .../schueller/peertube/network/Session.java | 128 ++++++++++++++++++ .../network/TokenRenewInterceptor.java | 29 ---- app/src/main/res/layout/activity_account.xml | 25 ++++ app/src/main/res/layout/activity_user.xml | 9 -- app/src/main/res/menu/menu_top_user.xml | 12 ++ .../{menu_main.xml => menu_top_videolist.xml} | 0 app/src/main/res/values/strings.xml | 7 + 16 files changed, 411 insertions(+), 95 deletions(-) create mode 100644 app/src/main/java/net/schueller/peertube/activity/AccountActivity.java delete mode 100644 app/src/main/java/net/schueller/peertube/activity/UserActivity.java create mode 100644 app/src/main/java/net/schueller/peertube/application/AppApplication.java create mode 100644 app/src/main/java/net/schueller/peertube/network/Session.java delete mode 100644 app/src/main/java/net/schueller/peertube/network/TokenRenewInterceptor.java create mode 100644 app/src/main/res/layout/activity_account.xml delete mode 100644 app/src/main/res/layout/activity_user.xml create mode 100644 app/src/main/res/menu/menu_top_user.xml rename app/src/main/res/menu/{menu_main.xml => menu_top_videolist.xml} (100%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5aa5606..de4a1d2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -18,7 +18,8 @@ android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme" - tools:ignore="GoogleAppIndexingWarning"> + tools:ignore="GoogleAppIndexingWarning" + android:name=".application.AppApplication"> - diff --git a/app/src/main/java/net/schueller/peertube/activity/AccountActivity.java b/app/src/main/java/net/schueller/peertube/activity/AccountActivity.java new file mode 100644 index 0000000..095c1d4 --- /dev/null +++ b/app/src/main/java/net/schueller/peertube/activity/AccountActivity.java @@ -0,0 +1,123 @@ +/* + * Copyright 2018 Stefan Schüller + * + * License: GPL-3.0+ + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.schueller.peertube.activity; + +import android.app.SearchManager; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.util.Log; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; + +import com.mikepenz.fontawesome_typeface_library.FontAwesome; +import com.mikepenz.iconics.IconicsDrawable; + +import net.schueller.peertube.R; +import net.schueller.peertube.network.Session; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.SearchView; +import androidx.appcompat.widget.Toolbar; + +import static net.schueller.peertube.helper.Constants.DEFAULT_THEME; +import static net.schueller.peertube.helper.Constants.THEME_PREF_KEY; + +public class AccountActivity extends AppCompatActivity { + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.menu_top_user, menu); + + // Set an icon in the ActionBar + menu.findItem(R.id.action_logout).setIcon( + new IconicsDrawable(this, FontAwesome.Icon.faw_sign_out_alt).actionBar()); + + return true; + } + + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + // action with ID action_refresh was selected + + case R.id.action_logout: + Session.getInstance().invalidate(); + Intent intent = new Intent(this, LoginActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + this.startActivity(intent); + return true; + default: + break; + } + + return super.onOptionsItemSelected(item); + } + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Set theme + SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this); + setTheme(getResources().getIdentifier( + sharedPref.getString(THEME_PREF_KEY, DEFAULT_THEME), + "style", + getPackageName()) + ); + + setContentView(R.layout.activity_account); + + // Attaching the layout to the toolbar object + Toolbar toolbar = findViewById(R.id.tool_bar_user); + // Setting toolbar as the ActionBar with setSupportActionBar() call + setSupportActionBar(toolbar); + + init(); + } + + private void init() { + // try to get user data + getUserData(); + } + + private boolean getUserData() { + + // TODO + + return false; + } + + @Override + protected void onResume() { + super.onResume(); + + init(); + + } +} diff --git a/app/src/main/java/net/schueller/peertube/activity/LoginActivity.java b/app/src/main/java/net/schueller/peertube/activity/LoginActivity.java index e34c397..9ec1427 100644 --- a/app/src/main/java/net/schueller/peertube/activity/LoginActivity.java +++ b/app/src/main/java/net/schueller/peertube/activity/LoginActivity.java @@ -18,10 +18,10 @@ package net.schueller.peertube.activity; +import android.content.Context; +import android.content.Intent; import android.content.SharedPreferences; import android.preference.PreferenceManager; -import android.support.annotation.NonNull; -import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.widget.AutoCompleteTextView; @@ -35,6 +35,8 @@ import net.schueller.peertube.model.Token; import net.schueller.peertube.network.AuthenticationService; import net.schueller.peertube.network.RetrofitInstance; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; @@ -78,6 +80,8 @@ public class LoginActivity extends AppCompatActivity { SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this); + Context context = this; + // Reset errors. mEmailView.setError(null); mPasswordView.setError(null); @@ -129,6 +133,12 @@ public class LoginActivity extends AppCompatActivity { editor.putString(getString(R.string.pref_token_type), token.getTokenType()); editor.commit(); + Log.wtf(TAG, "Logged in"); + + Intent intent = new Intent(context, AccountActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + context.startActivity(intent); + } else { Log.wtf(TAG, response2.toString()); } diff --git a/app/src/main/java/net/schueller/peertube/activity/UserActivity.java b/app/src/main/java/net/schueller/peertube/activity/UserActivity.java deleted file mode 100644 index b178c98..0000000 --- a/app/src/main/java/net/schueller/peertube/activity/UserActivity.java +++ /dev/null @@ -1,41 +0,0 @@ -package net.schueller.peertube.activity; - -import android.content.Intent; -import android.support.v7.app.AppCompatActivity; -import android.os.Bundle; - -import net.schueller.peertube.R; - -public class UserActivity extends AppCompatActivity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_user); - - init(); - } - - private void init() { - // try to get user data - if (!getUserData()) { - Intent intent = new Intent(this, LoginActivity.class); - this.startActivity(intent); - } - } - - private boolean getUserData() { - - // TODO - - return false; - } - - @Override - protected void onResume() { - super.onResume(); - - init(); - - } -} diff --git a/app/src/main/java/net/schueller/peertube/activity/VideoListActivity.java b/app/src/main/java/net/schueller/peertube/activity/VideoListActivity.java index bef23d9..ed8b3d4 100644 --- a/app/src/main/java/net/schueller/peertube/activity/VideoListActivity.java +++ b/app/src/main/java/net/schueller/peertube/activity/VideoListActivity.java @@ -53,8 +53,10 @@ import net.schueller.peertube.R; import net.schueller.peertube.adapter.VideoAdapter; import net.schueller.peertube.helper.APIUrlHelper; import net.schueller.peertube.model.VideoList; +import net.schueller.peertube.network.GetUserService; import net.schueller.peertube.network.GetVideoDataService; import net.schueller.peertube.network.RetrofitInstance; +import net.schueller.peertube.network.Session; import net.schueller.peertube.provider.SearchSuggestionsProvider; import net.schueller.peertube.service.VideoPlayerService; @@ -82,6 +84,7 @@ public class VideoListActivity extends AppCompatActivity { private String sort = "-createdAt"; private String filter = null; private String searchQuery = ""; + private Boolean subscriptions = false; private TextView emptyView; private RecyclerView recyclerView; @@ -125,7 +128,7 @@ public class VideoListActivity extends AppCompatActivity { @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.menu_main, menu); + inflater.inflate(R.menu.menu_top_videolist, menu); // Set an icon in the ActionBar menu.findItem(R.id.action_settings).setIcon( @@ -261,6 +264,9 @@ public class VideoListActivity extends AppCompatActivity { Call call; if (!searchQuery.equals("")) { call = service.searchVideosData(start, count, sort, nsfw, searchQuery, filter); + } else if (subscriptions) { + GetUserService userService = RetrofitInstance.getRetrofitInstance(apiBaseURL).create(GetUserService.class); + call = userService.getVideosSubscripions(start, count, sort); } else { call = service.getVideosData(start, count, sort, nsfw, filter); } @@ -380,6 +386,7 @@ public class VideoListActivity extends AppCompatActivity { sort = "-createdAt"; currentStart = 0; filter = null; + subscriptions = false; loadVideos(currentStart, count, sort, filter); } @@ -391,6 +398,7 @@ public class VideoListActivity extends AppCompatActivity { sort = "-trending"; currentStart = 0; filter = null; + subscriptions = false; loadVideos(currentStart, count, sort, filter); } @@ -402,24 +410,43 @@ public class VideoListActivity extends AppCompatActivity { sort = "-publishedAt"; filter = "local"; currentStart = 0; + subscriptions = false; loadVideos(currentStart, count, sort, filter); } return true; case R.id.navigation_subscriptions: //Log.v(TAG, "navigation_subscriptions"); - Toast.makeText(VideoListActivity.this, "Subscriptions Not Implemented", Toast.LENGTH_SHORT).show(); - return false; + if (!Session.getInstance().isLoggedIn()) { + Intent intent = new Intent(this, LoginActivity.class); + this.startActivity(intent); + } else { + + if (!isLoading) { + sort = "-publishedAt"; + filter = null; + currentStart = 0; + subscriptions = true; + loadVideos(currentStart, count, sort, filter); + } + } + + return true; case R.id.navigation_account: //Log.v(TAG, "navigation_account"); - Toast.makeText(VideoListActivity.this, "Account Not Implemented", Toast.LENGTH_SHORT).show(); + //Toast.makeText(VideoListActivity.this, "Account Not Implemented", Toast.LENGTH_SHORT).show(); -// Intent intent = new Intent(this, LoginActivity.class); -// this.startActivity(intent); + if (!Session.getInstance().isLoggedIn()) { + Intent intent = new Intent(this, LoginActivity.class); + this.startActivity(intent); + } else { + Intent intent = new Intent(this, AccountActivity.class); + this.startActivity(intent); + } - return false; + return true; } return false; }); diff --git a/app/src/main/java/net/schueller/peertube/application/AppApplication.java b/app/src/main/java/net/schueller/peertube/application/AppApplication.java new file mode 100644 index 0000000..05869b4 --- /dev/null +++ b/app/src/main/java/net/schueller/peertube/application/AppApplication.java @@ -0,0 +1,18 @@ +package net.schueller.peertube.application; + +import android.app.Application; +import android.content.Context; + +public class AppApplication extends Application { + private static Application instance; + + @Override + public void onCreate() { + super.onCreate(); + instance = this; + } + + public static Context getContext() { + return instance.getApplicationContext(); + } +} \ No newline at end of file diff --git a/app/src/main/java/net/schueller/peertube/network/AuthorizationInterceptor.java b/app/src/main/java/net/schueller/peertube/network/AuthorizationInterceptor.java index da559c0..5329b7b 100644 --- a/app/src/main/java/net/schueller/peertube/network/AuthorizationInterceptor.java +++ b/app/src/main/java/net/schueller/peertube/network/AuthorizationInterceptor.java @@ -1,24 +1,58 @@ +/* + * Copyright 2018 Stefan Schüller + * + * License: GPL-3.0+ + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package net.schueller.peertube.network; -import net.schueller.peertube.model.Token; +import android.util.Log; import java.io.IOException; import okhttp3.Interceptor; +import okhttp3.Request; import okhttp3.Response; public class AuthorizationInterceptor implements Interceptor { public AuthorizationInterceptor() { - } @Override public Response intercept(Chain chain) throws IOException { - Response mainResponse = chain.proceed(chain.request()); - if (mainResponse.code() == 401 || mainResponse.code() == 403) { + Session session = Session.getInstance(); + + Response mainResponse = chain.proceed(chain.request()); + Request mainRequest = chain.request(); + + if (session.isLoggedIn()) { + +// if (mainResponse.code() == 401 || mainResponse.code() == 403) { +// session.invalidate(); +// return mainResponse; +// } + + Request.Builder builder = mainRequest.newBuilder().header("Authorization", session.getToken()). + method(mainRequest.method(), mainRequest.body()); + mainResponse = chain.proceed(builder.build()); + Log.v("Authorization", "Intercept: " + session.getToken()); } + return mainResponse; } + } \ No newline at end of file diff --git a/app/src/main/java/net/schueller/peertube/network/GetUserService.java b/app/src/main/java/net/schueller/peertube/network/GetUserService.java index 7c05bae..7c4d459 100644 --- a/app/src/main/java/net/schueller/peertube/network/GetUserService.java +++ b/app/src/main/java/net/schueller/peertube/network/GetUserService.java @@ -1,6 +1,7 @@ package net.schueller.peertube.network; import net.schueller.peertube.model.Me; +import net.schueller.peertube.model.VideoList; import retrofit2.Call; import retrofit2.http.GET; @@ -11,4 +12,12 @@ public interface GetUserService { @GET("users/me") Call getMe(@Header("Authorization") String authorization); + + @GET("users/me/subscriptions/videos") + Call getVideosSubscripions( + @Query("start") int start, + @Query("count") int count, + @Query("sort") String sort + ); + } \ No newline at end of file diff --git a/app/src/main/java/net/schueller/peertube/network/RetrofitInstance.java b/app/src/main/java/net/schueller/peertube/network/RetrofitInstance.java index 86fd92c..23e38da 100644 --- a/app/src/main/java/net/schueller/peertube/network/RetrofitInstance.java +++ b/app/src/main/java/net/schueller/peertube/network/RetrofitInstance.java @@ -32,7 +32,6 @@ public class RetrofitInstance { OkHttpClient.Builder okhttpClientBuilder = new OkHttpClient.Builder(); - okhttpClientBuilder.addInterceptor(new TokenRenewInterceptor()); okhttpClientBuilder.addInterceptor(new AuthorizationInterceptor()); retrofit = new retrofit2.Retrofit.Builder() diff --git a/app/src/main/java/net/schueller/peertube/network/Session.java b/app/src/main/java/net/schueller/peertube/network/Session.java new file mode 100644 index 0000000..525cbb9 --- /dev/null +++ b/app/src/main/java/net/schueller/peertube/network/Session.java @@ -0,0 +1,128 @@ +/* + * Copyright 2018 Stefan Schüller + * + * License: GPL-3.0+ + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package net.schueller.peertube.network; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.util.Log; + +import net.schueller.peertube.R; +import net.schueller.peertube.application.AppApplication; + +public class Session { + + private static volatile Session sSoleInstance; + private static SharedPreferences sharedPreferences; + + //private constructor. + private Session(){ + + Context context = AppApplication.getContext(); + sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); + + //Prevent form the reflection api. + if (sSoleInstance != null){ + throw new RuntimeException("Use getInstance() method to get the single instance of this class."); + } + } + + public static Session getInstance() { + if (sSoleInstance == null) { //if there is no instance available... create new one + synchronized (Session.class) { + if (sSoleInstance == null) sSoleInstance = new Session(); + } + } + + return sSoleInstance; + } + + //Make singleton from serialize and deserialize operation. + protected Session readResolve() { + return getInstance(); + } + + + + public boolean isLoggedIn() { + // check if token exist or not + // return true if exist otherwise false + // assuming that token exists + + //Log.v("Session", "isLoggedIn: " + (getToken() != null)); + + return getToken() != null; + } + + public void saveToken(String token) { + // save the token + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putString(AppApplication.getContext().getString(R.string.pref_token_access), token); + editor.commit(); + } + + public String getToken() { + // return the token that was saved earlier + + String token = sharedPreferences.getString(AppApplication.getContext().getString(R.string.pref_token_access), null); + String type = sharedPreferences.getString(AppApplication.getContext().getString(R.string.pref_token_type), "Bearer"); + + if (token != null) { + return type + " " + token; + } + + return null; + } + + public void saveUsername(String username) { + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putString(AppApplication.getContext().getString(R.string.pref_auth_username), username); + editor.commit(); + } + + public String getEmail() { + return sharedPreferences.getString(AppApplication.getContext().getString(R.string.pref_auth_username), null); + } + + public void savePassword(String password) { + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putString(AppApplication.getContext().getString(R.string.pref_auth_password), password); + editor.commit(); + } + + public String getPassword() { + return sharedPreferences.getString(AppApplication.getContext().getString(R.string.pref_auth_password), null); + + } + + public void invalidate() { + // get called when user become logged out + // delete token and other user info + // (i.e: email, password) + // from the storage + + Context context = AppApplication.getContext(); + + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putString(context.getString(R.string.pref_auth_password), null); + editor.putString(context.getString(R.string.pref_auth_username), null); + editor.putString(context.getString(R.string.pref_token_access), null); + + editor.commit(); + } +} \ No newline at end of file diff --git a/app/src/main/java/net/schueller/peertube/network/TokenRenewInterceptor.java b/app/src/main/java/net/schueller/peertube/network/TokenRenewInterceptor.java deleted file mode 100644 index 2ae3a69..0000000 --- a/app/src/main/java/net/schueller/peertube/network/TokenRenewInterceptor.java +++ /dev/null @@ -1,29 +0,0 @@ -package net.schueller.peertube.network; - -import net.schueller.peertube.model.Token; - -import java.io.IOException; - -import okhttp3.Interceptor; -import okhttp3.Response; - -public class TokenRenewInterceptor implements Interceptor { - - public TokenRenewInterceptor() { - } - - @Override - public Response intercept(Chain chain) throws IOException { - Response response = chain.proceed(chain.request()); - - // if 'x-auth-token' is available into the response header - // save the new token into session.The header key can be - // different upon implementation of backend. - String newToken = response.header("x-auth-token"); - if (newToken != null) { - - } - - return response; - } -} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_account.xml b/app/src/main/res/layout/activity_account.xml new file mode 100644 index 0000000..bc4f3a5 --- /dev/null +++ b/app/src/main/res/layout/activity_account.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_user.xml b/app/src/main/res/layout/activity_user.xml deleted file mode 100644 index 39f110a..0000000 --- a/app/src/main/res/layout/activity_user.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/menu/menu_top_user.xml b/app/src/main/res/menu/menu_top_user.xml new file mode 100644 index 0000000..efd2356 --- /dev/null +++ b/app/src/main/res/menu/menu_top_user.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_top_videolist.xml similarity index 100% rename from app/src/main/res/menu/menu_main.xml rename to app/src/main/res/menu/menu_top_videolist.xml diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 36158aa..524f60d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -22,6 +22,7 @@ Search Settings + Logout Home @@ -96,9 +97,15 @@ Background Playback If enabled, continues to play video in background. Local + + Account + + pref_token_access pref_token_refresh pref_token_expiration pref_token_type + pref_auth_username + pref_auth_password