diff options
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> |