diff options
author | Alex Ning <chineseperson5@gmail.com> | 2019-08-07 02:54:47 +0000 |
---|---|---|
committer | Alex Ning <chineseperson5@gmail.com> | 2019-08-07 02:54:47 +0000 |
commit | 7f2bc01180c284154fb69976122016c4638e9973 (patch) | |
tree | b009b9cfab924cce98c25ae5b6355af4f18c311c | |
parent | 77d83654aa8063b0c4d9f95ba91bd3d77be84ad9 (diff) | |
download | infinity-for-reddit-7f2bc01180c284154fb69976122016c4638e9973.tar infinity-for-reddit-7f2bc01180c284154fb69976122016c4638e9973.tar.gz infinity-for-reddit-7f2bc01180c284154fb69976122016c4638e9973.tar.bz2 infinity-for-reddit-7f2bc01180c284154fb69976122016c4638e9973.tar.lz infinity-for-reddit-7f2bc01180c284154fb69976122016c4638e9973.tar.xz infinity-for-reddit-7f2bc01180c284154fb69976122016c4638e9973.tar.zst infinity-for-reddit-7f2bc01180c284154fb69976122016c4638e9973.zip |
Preparing to support multi user. Use the database to store accounts' info. LoginActivity is successfully refactored. Any other features are unavailable for now.
10 files changed, 358 insertions, 21 deletions
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000..9223ff2c --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,15 @@ +<component name="InspectionProjectProfileManager"> + <profile version="1.0"> + <option name="myName" value="Project Default" /> + <inspection_tool class="FieldCanBeLocal" enabled="true" level="WARNING" enabled_by_default="true"> + <option name="EXCLUDE_ANNOS"> + <value> + <list size="1"> + <item index="0" class="java.lang.String" itemvalue="androidx.room.ColumnInfo" /> + </list> + </value> + </option> + <option name="IGNORE_FIELDS_USED_IN_MULTIPLE_METHODS" value="true" /> + </inspection_tool> + </profile> +</component>
\ No newline at end of file diff --git a/app/src/main/java/Account/Account.java b/app/src/main/java/Account/Account.java new file mode 100644 index 00000000..95873c61 --- /dev/null +++ b/app/src/main/java/Account/Account.java @@ -0,0 +1,72 @@ +package Account; + +import androidx.annotation.NonNull; +import androidx.room.ColumnInfo; +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +@Entity(tableName = "accounts") +public class Account { + @PrimaryKey + @NonNull + @ColumnInfo(name = "username") + private String username; + @ColumnInfo(name = "profile_image_url") + private String profileImageUrl; + @ColumnInfo(name = "banner_image_url") + private String bannerImageUrl; + @ColumnInfo(name = "karma") + private int karma; + @ColumnInfo(name = "access_token") + private String accessToken; + @ColumnInfo(name = "refresh_token") + private String refreshToken; + @ColumnInfo(name = "code") + private String code; + @ColumnInfo(name = "is_current_user") + private boolean isCurrentUser; + + public Account(@NonNull String username, String accessToken, String refreshToken, String code, + String profileImageUrl, String bannerImageUrl, int karma, boolean isCurrentUser) { + this.username = username; + this.accessToken = accessToken; + this.refreshToken = refreshToken; + this.code = code; + this.profileImageUrl = profileImageUrl; + this.bannerImageUrl = bannerImageUrl; + this.karma = karma; + this.isCurrentUser = isCurrentUser; + } + + public String getUsername() { + return username; + } + + public String getProfileImageUrl() { + return profileImageUrl; + } + + public String getBannerImageUrl() { + return bannerImageUrl; + } + + public int getKarma() { + return karma; + } + + public String getAccessToken() { + return accessToken; + } + + public String getRefreshToken() { + return refreshToken; + } + + public String getCode() { + return code; + } + + public boolean isCurrentUser() { + return isCurrentUser; + } +} diff --git a/app/src/main/java/Account/AccountDao.java b/app/src/main/java/Account/AccountDao.java new file mode 100644 index 00000000..bd53b92b --- /dev/null +++ b/app/src/main/java/Account/AccountDao.java @@ -0,0 +1,22 @@ +package Account; + +import androidx.lifecycle.LiveData; +import androidx.room.Dao; +import androidx.room.Insert; +import androidx.room.OnConflictStrategy; +import androidx.room.Query; + +@Dao +public interface AccountDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + void insert(Account account); + + @Query("DELETE FROM accounts") + void deleteAllAccounts(); + + @Query("SELECT * FROM accounts WHERE username = :userName COLLATE NOCASE LIMIT 1") + LiveData<Account> getAccountLiveData(String userName); + + @Query("SELECT * FROM accounts WHERE username = :userName COLLATE NOCASE LIMIT 1") + Account getAccountData(String userName); +} diff --git a/app/src/main/java/Account/AccountRepository.java b/app/src/main/java/Account/AccountRepository.java new file mode 100644 index 00000000..49bdfacf --- /dev/null +++ b/app/src/main/java/Account/AccountRepository.java @@ -0,0 +1,40 @@ +package Account; + +import android.app.Application; +import android.os.AsyncTask; + +import androidx.lifecycle.LiveData; + +public class AccountRepository { + private AccountDao mAccountDao; + private LiveData<Account> mAccountLiveData; + + AccountRepository(Application application, String username) { + mAccountDao = AccountRoomDatabase.getDatabase(application).accountDao(); + + mAccountLiveData = mAccountDao.getAccountLiveData(username); + } + + LiveData<Account> getAccountLiveData() { + return mAccountLiveData; + } + + public void insert(Account Account) { + new InsertAsyncTask(mAccountDao).execute(Account); + } + + private static class InsertAsyncTask extends AsyncTask<Account, Void, Void> { + + private AccountDao mAsyncTaskDao; + + InsertAsyncTask(AccountDao dao) { + mAsyncTaskDao = dao; + } + + @Override + protected Void doInBackground(final Account... params) { + mAsyncTaskDao.insert(params[0]); + return null; + } + } +} diff --git a/app/src/main/java/Account/AccountRoomDatabase.java b/app/src/main/java/Account/AccountRoomDatabase.java new file mode 100644 index 00000000..2f5d0120 --- /dev/null +++ b/app/src/main/java/Account/AccountRoomDatabase.java @@ -0,0 +1,27 @@ +package Account; + +import android.content.Context; + +import androidx.room.Database; +import androidx.room.Room; +import androidx.room.RoomDatabase; + +@Database(entities = {Account.class}, version = 1) +public abstract class AccountRoomDatabase extends RoomDatabase { + private static AccountRoomDatabase INSTANCE; + + public abstract AccountDao accountDao(); + + public static AccountRoomDatabase getDatabase(final Context context) { + if(INSTANCE == null) { + synchronized (AccountRoomDatabase.class) { + if(INSTANCE == null) { + INSTANCE = Room.databaseBuilder(context.getApplicationContext(), + AccountRoomDatabase.class, "accounts") + .build(); + } + } + } + return INSTANCE; + } +} diff --git a/app/src/main/java/Account/AccountViewModel.java b/app/src/main/java/Account/AccountViewModel.java new file mode 100644 index 00000000..b649d9a7 --- /dev/null +++ b/app/src/main/java/Account/AccountViewModel.java @@ -0,0 +1,47 @@ +package Account; + +import android.app.Application; + +import androidx.annotation.NonNull; +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.ViewModel; +import androidx.lifecycle.ViewModelProvider; + +public class AccountViewModel extends AndroidViewModel { + private AccountRepository mAccountRepository; + private LiveData<Account> mAccountLiveData; + + public AccountViewModel(Application application, String id) { + super(application); + mAccountRepository = new AccountRepository(application, id); + mAccountLiveData = mAccountRepository.getAccountLiveData(); + } + + public LiveData<Account> getAccountLiveData() { + return mAccountLiveData; + } + + public void insert(Account userData) { + mAccountRepository.insert(userData); + } + + public static class Factory extends ViewModelProvider.NewInstanceFactory { + + @NonNull + private final Application mApplication; + + private final String userName; + + public Factory(@NonNull Application application, String userName) { + mApplication = application; + this.userName = userName; + } + + @Override + public <T extends ViewModel> T create(Class<T> modelClass) { + //noinspection unchecked + return (T) new AccountViewModel(mApplication, userName); + } + } +} diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/FetchMyInfo.java b/app/src/main/java/ml/docilealligator/infinityforreddit/FetchMyInfo.java index 393349a1..63b80532 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/FetchMyInfo.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/FetchMyInfo.java @@ -1,9 +1,10 @@ package ml.docilealligator.infinityforreddit; import android.content.SharedPreferences; -import androidx.annotation.NonNull; import android.util.Log; +import androidx.annotation.NonNull; + import retrofit2.Call; import retrofit2.Callback; import retrofit2.Retrofit; @@ -39,4 +40,28 @@ class FetchMyInfo { } }); } + + static void fetchMyInfo(final Retrofit retrofit, String accessToken, + final FetchUserMyListener fetchUserMyListener) { + RedditAPI api = retrofit.create(RedditAPI.class); + + Call<String> userInfo = api.getMyInfo(RedditUtils.getOAuthHeader(accessToken)); + userInfo.enqueue(new Callback<String>() { + @Override + public void onResponse(@NonNull Call<String> call, @NonNull retrofit2.Response<String> response) { + if(response.isSuccessful()) { + fetchUserMyListener.onFetchMyInfoSuccess(response.body()); + } else { + Log.i("call failed", response.message()); + fetchUserMyListener.onFetchMyInfoFail(); + } + } + + @Override + public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) { + Log.i("call failed", t.getMessage()); + fetchUserMyListener.onFetchMyInfoFail(); + } + }); + } } diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/LoginActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/LoginActivity.java index 53b0e143..20fa44fa 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/LoginActivity.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/LoginActivity.java @@ -12,6 +12,9 @@ import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; + import org.json.JSONException; import org.json.JSONObject; @@ -21,8 +24,7 @@ import java.util.Map; import javax.inject.Inject; import javax.inject.Named; -import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatActivity; +import Account.AccountRoomDatabase; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; @@ -36,6 +38,10 @@ public class LoginActivity extends AppCompatActivity { @Named("no_oauth") Retrofit mRetrofit; + @Inject + @Named("oauth") + Retrofit mOauthRetrofit; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -85,41 +91,69 @@ public class LoginActivity extends AppCompatActivity { public void onResponse(@NonNull Call<String> call, @NonNull Response<String> response) { if(response.isSuccessful()) { try { - JSONObject responseJSON = new JSONObject(response.body()); + String accountResponse = response.body(); + if(accountResponse == null) { + //Handle error + return; + } + + JSONObject responseJSON = new JSONObject(accountResponse); String accessToken = responseJSON.getString(RedditUtils.ACCESS_TOKEN_KEY); String refreshToken = responseJSON.getString(RedditUtils.REFRESH_TOKEN_KEY); - editor.putString(SharedPreferencesUtils.ACCESS_TOKEN_KEY, accessToken); - editor.putString(SharedPreferencesUtils.REFRESH_TOKEN_KEY, refreshToken); - editor.apply(); - - Intent resultIntent = new Intent(); - setResult(Activity.RESULT_OK, resultIntent); - finish(); + FetchMyInfo.fetchMyInfo(mOauthRetrofit, accessToken, new FetchMyInfo.FetchUserMyListener() { + @Override + public void onFetchMyInfoSuccess(String response) { + ParseMyInfo.parseMyInfo(response, new ParseMyInfo.ParseMyInfoListener() { + @Override + public void onParseMyInfoSuccess(String name, String profileImageUrl, String bannerImageUrl, int karma) { + new ParseAndInsertAccount(name, accessToken, refreshToken, profileImageUrl, bannerImageUrl, + karma, authCode, AccountRoomDatabase.getDatabase(LoginActivity.this).accountDao(), + () -> { + Intent resultIntent = new Intent(); + setResult(Activity.RESULT_OK, resultIntent); + finish(); + }).execute(); + } + + @Override + public void onParseMyInfoFail() { + Toast.makeText(LoginActivity.this, R.string.parse_user_info_error, Toast.LENGTH_SHORT).show(); + finish(); + } + }); + } + + @Override + public void onFetchMyInfoFail() { + Toast.makeText(LoginActivity.this, R.string.cannot_fetch_user_info, Toast.LENGTH_SHORT).show(); + finish(); + } + }); } catch (JSONException e) { e.printStackTrace(); - Toast.makeText(LoginActivity.this, "Error occurred when parsing the JSON response", Toast.LENGTH_SHORT).show(); + Toast.makeText(LoginActivity.this, R.string.parse_json_response_error, Toast.LENGTH_SHORT).show(); finish(); } } else { - Toast.makeText(LoginActivity.this, "Error Retrieving the token", Toast.LENGTH_SHORT).show(); + Toast.makeText(LoginActivity.this, R.string.retrieve_token_error, Toast.LENGTH_SHORT).show(); finish(); } } @Override - public void onFailure(Call<String> call, Throwable t) { - Toast.makeText(LoginActivity.this, "Error Retrieving the token", Toast.LENGTH_SHORT).show(); + public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) { + Toast.makeText(LoginActivity.this, R.string.retrieve_token_error, Toast.LENGTH_SHORT).show(); finish(); } }); } else { - Toast.makeText(LoginActivity.this, "Something went wrong. Try again later.", Toast.LENGTH_SHORT).show(); + Toast.makeText(LoginActivity.this, R.string.something_went_wrong, Toast.LENGTH_SHORT).show(); finish(); } } else if (url.contains("error=access_denied")) { - Toast.makeText(LoginActivity.this, "Access denied", Toast.LENGTH_SHORT).show(); + Toast.makeText(LoginActivity.this, R.string.access_denied, Toast.LENGTH_SHORT).show(); finish(); } else { view.loadUrl(url); @@ -142,10 +176,9 @@ public class LoginActivity extends AppCompatActivity { @Override public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - finish(); - return true; + if (item.getItemId() == android.R.id.home) { + finish(); + return true; } return false; } diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/ParseAndInsertAccount.java b/app/src/main/java/ml/docilealligator/infinityforreddit/ParseAndInsertAccount.java new file mode 100644 index 00000000..e86fbada --- /dev/null +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/ParseAndInsertAccount.java @@ -0,0 +1,50 @@ +package ml.docilealligator.infinityforreddit; + +import android.os.AsyncTask; + +import Account.Account; +import Account.AccountDao; + +class ParseAndInsertAccount extends AsyncTask<Void, Void, Void> { + + interface ParseAndInsertAccountListener { + void success(); + } + + private String username; + private String accessToken; + private String refreshToken; + private String profileImageUrl; + private String bannerImageUrl; + private int karma; + private String code; + private AccountDao accountDao; + private ParseAndInsertAccountListener parseAndInsertAccountListener; + + ParseAndInsertAccount(String username, String accessToken, String refreshToken, String profileImageUrl, String bannerImageUrl, + int karma, String code, AccountDao accountDao, + ParseAndInsertAccountListener parseAndInsertAccountListener) { + this.username = username; + this.accessToken = accessToken; + this.refreshToken = refreshToken; + this.profileImageUrl = profileImageUrl; + this.bannerImageUrl = bannerImageUrl; + this.karma = karma; + this.code = code; + this.accountDao = accountDao; + this.parseAndInsertAccountListener = parseAndInsertAccountListener; + } + + @Override + protected Void doInBackground(Void... voids) { + Account account = new Account(username, accessToken, refreshToken, code, profileImageUrl, + bannerImageUrl, karma, true); + accountDao.insert(account); + return null; + } + + @Override + protected void onPostExecute(Void aVoid) { + parseAndInsertAccountListener.success(); + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 93de5843..2801d12b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -25,6 +25,12 @@ <string name="action_send">Send</string> <string name="action_sort">Sort</string> + <string name="parse_json_response_error">Error occurred when parsing the JSON response</string> + <string name="retrieve_token_error">Error Retrieving the token</string> + <string name="something_went_wrong">Something went wrong. Try again later.</string> + <string name="access_denied">Access denied</string> + <string name="parse_user_info_error">Error occurred when parsing the user info</string> + <string name="tap_to_retry">Error loading image. Tap to retry.</string> <string name="load_posts_error">Error loading posts.\nTap to retry.</string> <string name="search_subreddits_error">Error searching subreddits.\nTap to retry.</string> |