Skip to content

Commit a168d4e

Browse files
jeremymanningclaude
andcommitted
Refactor to native Android app with profile viewing and author search
Major changes: - Replace WebView with native UI components and HTML parsing - Add Google Sign-In authentication for profile and library access - Add ProfileViewActivity for viewing any author's profile with stats, citation charts, and publications - Add AuthorSearchActivity with relevance filtering to reduce unrelated co-authors in search results - Add clickable author names in search results (blue text) - Require sign-in for My Profile and My Library features - Add LibraryRepository with Room database for saved articles New components: - ScholarApiClient: HTTP client for Google Scholar - ScholarHtmlParser: Jsoup-based HTML parsing - ScholarRepository: Data layer with author relevance scoring - ProfileViewModel with citation time range filtering - LibraryViewModel with auth state management UI additions: - Article cards with PDF, save, share, and citation actions - Author profile view with photo, stats, interests, citation chart - Author search results with name-based filtering - Settings screen with preferences 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent ae7052a commit a168d4e

80 files changed

Lines changed: 11253 additions & 383 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

REFACTOR_PLAN.md

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
# Google Scholar App - Native Interface Refactoring Plan
2+
3+
## Current Problems
4+
1. **Double hamburger menus**: App's navigation drawer + Google Scholar's mobile site menu
5+
2. **PDF links broken**: WebView doesn't properly handle PDF downloads/viewing
6+
3. **Limited control**: WebView wrapper limits customization and native feel
7+
8+
## Solution: Native Google Scholar Interface
9+
10+
Replace WebView with native Android components that parse and display Scholar content.
11+
12+
---
13+
14+
## Architecture Overview
15+
16+
```
17+
┌─────────────────────────────────────────────────────────┐
18+
│ UI Layer │
19+
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
20+
│ │ SearchFragment│ │ResultsFragment│ │ArticleDetail│ │
21+
│ └─────────────┘ └─────────────┘ └─────────────┘ │
22+
├─────────────────────────────────────────────────────────┤
23+
│ ViewModel Layer │
24+
│ ┌─────────────────────────────────────────────────┐ │
25+
│ │ ScholarViewModel │ │
26+
│ │ - searchResults: LiveData<List<Article>> │ │
27+
│ │ - loading: LiveData<Boolean> │ │
28+
│ └─────────────────────────────────────────────────┘ │
29+
├─────────────────────────────────────────────────────────┤
30+
│ Repository Layer │
31+
│ ┌─────────────────────────────────────────────────┐ │
32+
│ │ ScholarRepository │ │
33+
│ │ - search(query): List<Article> │ │
34+
│ │ - getArticleDetails(id): ArticleDetails │ │
35+
│ └─────────────────────────────────────────────────┘ │
36+
├─────────────────────────────────────────────────────────┤
37+
│ Network Layer │
38+
│ ┌──────────────────┐ ┌────────────────────────┐ │
39+
│ │ ScholarApiClient │ │ ScholarHtmlParser │ │
40+
│ │ (OkHttp) │ │ (Jsoup) │ │
41+
│ └──────────────────┘ └────────────────────────┘ │
42+
├─────────────────────────────────────────────────────────┤
43+
│ Data Models │
44+
│ ┌─────────┐ ┌──────────┐ ┌─────────┐ ┌────────┐ │
45+
│ │ Article │ │ Author │ │Citation │ │PdfLink │ │
46+
│ └─────────┘ └──────────┘ └─────────┘ └────────┘ │
47+
└─────────────────────────────────────────────────────────┘
48+
```
49+
50+
---
51+
52+
## Implementation Tasks
53+
54+
### Task 1: Data Models (`data/model/`)
55+
Create Kotlin data classes:
56+
- `Article` - title, authors, year, citations, abstract snippet, PDF link
57+
- `Author` - name, affiliation, profile URL
58+
- `SearchResult` - list of articles, pagination info
59+
- `PdfSource` - URL, source name (publisher, arxiv, etc.)
60+
61+
### Task 2: Network Layer (`network/`)
62+
- `ScholarApiClient` - OkHttp client with cookie handling, user-agent spoofing
63+
- `ScholarHtmlParser` - Jsoup-based HTML parser for search results
64+
- Handle rate limiting and CAPTCHA detection
65+
66+
### Task 3: Repository (`repository/`)
67+
- `ScholarRepository` - Coordinates API calls and parsing
68+
- Caching layer for recent searches
69+
- Error handling and retry logic
70+
71+
### Task 4: ViewModel (`viewmodel/`)
72+
- `ScholarViewModel` - Manages UI state
73+
- `SearchState` sealed class (Loading, Success, Error)
74+
- Pagination support
75+
76+
### Task 5: UI Components (`ui/`)
77+
- `SearchFragment` - Search bar with suggestions
78+
- `ResultsFragment` - RecyclerView with article cards
79+
- `ArticleCardAdapter` - Material card for each result
80+
- `ArticleDetailFragment` - Full article view with actions
81+
82+
### Task 6: PDF Handling (`util/`)
83+
- `PdfHandler` - Download manager integration
84+
- Intent handling for viewing PDFs
85+
- Progress notification for downloads
86+
87+
### Task 7: Navigation Update
88+
- Remove WebView completely
89+
- Update navigation drawer for native fragments
90+
- Single hamburger menu (app's only)
91+
92+
---
93+
94+
## New Dependencies
95+
96+
```kotlin
97+
// HTML Parsing
98+
implementation("org.jsoup:jsoup:1.17.2")
99+
100+
// Networking
101+
implementation("com.squareup.okhttp3:okhttp:4.12.0")
102+
103+
// Image Loading (for author avatars, thumbnails)
104+
implementation("io.coil-kt:coil:2.5.0")
105+
106+
// Coroutines
107+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
108+
```
109+
110+
---
111+
112+
## File Structure After Refactoring
113+
114+
```
115+
app/src/main/java/com/scholar/android/
116+
├── ScholarApplication.kt
117+
├── data/
118+
│ └── model/
119+
│ ├── Article.kt
120+
│ ├── Author.kt
121+
│ ├── SearchResult.kt
122+
│ └── PdfSource.kt
123+
├── network/
124+
│ ├── ScholarApiClient.kt
125+
│ └── ScholarHtmlParser.kt
126+
├── repository/
127+
│ └── ScholarRepository.kt
128+
├── viewmodel/
129+
│ └── ScholarViewModel.kt
130+
├── ui/
131+
│ ├── MainActivity.kt (refactored)
132+
│ ├── search/
133+
│ │ └── SearchFragment.kt
134+
│ ├── results/
135+
│ │ ├── ResultsFragment.kt
136+
│ │ └── ArticleCardAdapter.kt
137+
│ └── detail/
138+
│ └── ArticleDetailFragment.kt
139+
└── util/
140+
├── PdfHandler.kt
141+
├── NetworkUtils.kt
142+
└── ScholarUrls.kt (updated)
143+
```
144+
145+
---
146+
147+
## Parallel Execution Plan
148+
149+
### Agent 1: Data & Network Layer
150+
- Create data models
151+
- Implement ScholarApiClient
152+
- Implement ScholarHtmlParser
153+
- Add new dependencies
154+
155+
### Agent 2: Repository & ViewModel
156+
- Implement ScholarRepository
157+
- Create ScholarViewModel
158+
- Define state classes
159+
160+
### Agent 3: UI Components
161+
- Create fragment layouts
162+
- Implement RecyclerView adapter
163+
- Create article card layout
164+
- Update navigation
165+
166+
### Agent 4: PDF & Integration
167+
- Implement PdfHandler
168+
- Update MainActivity
169+
- Remove old WebView code
170+
- Test integration
171+
172+
---
173+
174+
## Risk Mitigation
175+
176+
1. **Google Scholar blocking**: Use realistic user-agent, respect rate limits
177+
2. **HTML structure changes**: Parser should be resilient, log warnings
178+
3. **CAPTCHA challenges**: Detect and prompt user, fallback to WebView if needed
179+
4. **PDF access**: Some PDFs require authentication - handle gracefully
180+
181+
---
182+
183+
## Success Criteria
184+
185+
- [ ] Single hamburger menu (app navigation only)
186+
- [ ] Search results display in native RecyclerView
187+
- [ ] PDF links open in PDF viewer or download
188+
- [ ] Smooth scrolling and native feel
189+
- [ ] Navigation drawer works correctly
190+
- [ ] Back navigation works properly

app/build.gradle.kts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
plugins {
22
id("com.android.application")
33
id("org.jetbrains.kotlin.android")
4+
id("com.google.devtools.ksp") version "1.9.21-1.0.15"
45
}
56

67
android {
@@ -54,6 +55,14 @@ dependencies {
5455
// Material Design 2
5556
implementation("com.google.android.material:material:1.10.0")
5657

58+
// Google Sign-In
59+
implementation("com.google.android.gms:play-services-auth:20.7.0")
60+
61+
// Credentials Manager for modern auth flow
62+
implementation("androidx.credentials:credentials:1.3.0")
63+
implementation("androidx.credentials:credentials-play-services-auth:1.3.0")
64+
implementation("com.google.android.libraries.identity.googleid:googleid:1.1.1")
65+
5766
// ConstraintLayout
5867
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
5968

@@ -68,9 +77,32 @@ dependencies {
6877
// SwipeRefreshLayout
6978
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
7079

80+
// Preferences
81+
implementation("androidx.preference:preference-ktx:1.2.1")
82+
7183
// WebKit
7284
implementation("androidx.webkit:webkit:1.9.0")
7385

86+
// HTML Parsing
87+
implementation("org.jsoup:jsoup:1.17.2")
88+
89+
// Networking
90+
implementation("com.squareup.okhttp3:okhttp:4.12.0")
91+
92+
// Image Loading
93+
implementation("io.coil-kt:coil:2.5.0")
94+
95+
// Charts
96+
implementation("com.github.PhilJay:MPAndroidChart:v3.1.0")
97+
98+
// Coroutines
99+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
100+
101+
// Room Database
102+
implementation("androidx.room:room-runtime:2.6.1")
103+
implementation("androidx.room:room-ktx:2.6.1")
104+
ksp("androidx.room:room-compiler:2.6.1")
105+
74106
// Testing
75107
testImplementation("junit:junit:4.13.2")
76108
androidTestImplementation("androidx.test.ext:junit:1.1.5")

app/src/main/AndroidManifest.xml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,55 @@
4141
</intent-filter>
4242
</activity>
4343

44+
<!-- Profile Edit Activity - WebView for editing Google Scholar profile -->
45+
<activity
46+
android:name=".ui.profile.ProfileEditActivity"
47+
android:exported="false"
48+
android:label="@string/edit_profile"
49+
android:theme="@style/Theme.GoogleScholar"
50+
android:configChanges="orientation|screenSize|keyboardHidden"
51+
android:parentActivityName=".ui.MainActivity">
52+
<meta-data
53+
android:name="android.support.PARENT_ACTIVITY"
54+
android:value=".ui.MainActivity" />
55+
</activity>
56+
57+
<!-- Article View Activity - WebView for viewing articles in-app -->
58+
<activity
59+
android:name=".ui.article.ArticleViewActivity"
60+
android:exported="false"
61+
android:theme="@style/Theme.GoogleScholar"
62+
android:configChanges="orientation|screenSize|keyboardHidden"
63+
android:parentActivityName=".ui.MainActivity">
64+
<meta-data
65+
android:name="android.support.PARENT_ACTIVITY"
66+
android:value=".ui.MainActivity" />
67+
</activity>
68+
69+
<!-- Profile View Activity - View any author's profile (read-only) -->
70+
<activity
71+
android:name=".ui.profile.ProfileViewActivity"
72+
android:exported="false"
73+
android:theme="@style/Theme.GoogleScholar"
74+
android:configChanges="orientation|screenSize|keyboardHidden"
75+
android:parentActivityName=".ui.MainActivity">
76+
<meta-data
77+
android:name="android.support.PARENT_ACTIVITY"
78+
android:value=".ui.MainActivity" />
79+
</activity>
80+
81+
<!-- Author Search Activity - Search results for authors by interest -->
82+
<activity
83+
android:name=".ui.profile.AuthorSearchActivity"
84+
android:exported="false"
85+
android:theme="@style/Theme.GoogleScholar"
86+
android:configChanges="orientation|screenSize|keyboardHidden"
87+
android:parentActivityName=".ui.MainActivity">
88+
<meta-data
89+
android:name="android.support.PARENT_ACTIVITY"
90+
android:value=".ui.MainActivity" />
91+
</activity>
92+
4493
</application>
4594

4695
</manifest>

app/src/main/java/com/scholar/android/ScholarApplication.kt

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ package com.scholar.android
22

33
import android.app.Application
44
import android.webkit.WebView
5+
import coil.ImageLoader
6+
import coil.ImageLoaderFactory
7+
import okhttp3.OkHttpClient
58

69
/**
710
* Application class for Google Scholar Android app.
8-
* Initializes WebView debugging in debug builds.
11+
* Initializes WebView debugging in debug builds and configures Coil for image loading.
912
*/
10-
class ScholarApplication : Application() {
13+
class ScholarApplication : Application(), ImageLoaderFactory {
1114

1215
override fun onCreate() {
1316
super.onCreate()
@@ -17,4 +20,26 @@ class ScholarApplication : Application() {
1720
WebView.setWebContentsDebuggingEnabled(true)
1821
}
1922
}
23+
24+
/**
25+
* Creates a custom ImageLoader with proper User-Agent headers.
26+
* Google Scholar requires browser-like headers to serve images.
27+
*/
28+
override fun newImageLoader(): ImageLoader {
29+
val okHttpClient = OkHttpClient.Builder()
30+
.addInterceptor { chain ->
31+
val request = chain.request().newBuilder()
32+
.header("User-Agent", "Mozilla/5.0 (Linux; Android 14) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36")
33+
.header("Accept", "image/webp,image/apng,image/*,*/*;q=0.8")
34+
.header("Referer", "https://scholar.google.com/")
35+
.build()
36+
chain.proceed(request)
37+
}
38+
.build()
39+
40+
return ImageLoader.Builder(this)
41+
.okHttpClient(okHttpClient)
42+
.crossfade(true)
43+
.build()
44+
}
2045
}

0 commit comments

Comments
 (0)