Skip to content

Commit a0304f8

Browse files
committed
[Updater] Moved Updater to GitHub product flavor
With Google handling automatic updates through the Google Play Store, the updater functionality becomes unnecessary. The updater is now included only in the versions downloaded from the GitHub release page (GitHub product flavor).
1 parent 4903853 commit a0304f8

35 files changed

+624
-307
lines changed

app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ dependencies {
133133
implementation 'com.github.devnied.emvnfccard:library:3.0.1'
134134
implementation 'net.grey-panther:natural-comparator:1.1'
135135

136-
implementation "com.squareup.retrofit2:retrofit:2.9.0"
136+
githubImplementation "com.squareup.retrofit2:retrofit:2.9.0"
137+
githubImplementation 'org.kohsuke:github-api:1.314'
137138

138139
implementation "androidx.room:room-runtime:2.5.1"
139140
annotationProcessor "androidx.room:room-compiler:2.5.1"
@@ -144,7 +145,6 @@ dependencies {
144145
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
145146

146147
implementation "org.slf4j:slf4j-simple:2.0.7"
147-
implementation 'org.kohsuke:github-api:1.314'
148148
}
149149

150150
class RoomSchemaArgProvider implements CommandLineArgumentProvider {

app/src/github/AndroidManifest.xml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
2+
xmlns:tools="http://schemas.android.com/tools">
3+
4+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
5+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
6+
<uses-permission android:name="android.permission.INTERNET"/>
7+
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
8+
9+
<application
10+
android:name=".App"
11+
tools:replace="android:name">
12+
13+
<receiver android:name="de.davis.passwordmanager.updater.installer.InstallBroadcastReceiver"
14+
android:exported="false">
15+
<intent-filter>
16+
<action android:name="de.davis.passwordmanager.action.INSTALL"/>
17+
</intent-filter>
18+
</receiver>
19+
20+
<service android:name="de.davis.passwordmanager.updater.downloader.DownloadService"/>
21+
</application>
22+
23+
</manifest>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package de.davis.passwordmanager;
2+
3+
import java.io.File;
4+
5+
import de.davis.passwordmanager.updater.Updater;
6+
7+
public class App extends PasswordManagerApplication{
8+
9+
private Updater updater;
10+
11+
@Override
12+
public void onCreate() {
13+
super.onCreate();
14+
updater = new Updater(this);
15+
}
16+
17+
public Updater getUpdater() {
18+
return updater;
19+
}
20+
21+
public File getDownloadDir(){
22+
return new File(getCacheDir(), "apks/");
23+
}
24+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package de.davis.passwordmanager.ui;
2+
3+
import static de.davis.passwordmanager.utils.BackgroundUtil.doInBackground;
4+
5+
import android.os.Bundle;
6+
7+
import com.google.android.material.badge.BadgeDrawable;
8+
import com.google.android.material.navigation.NavigationBarView;
9+
10+
import java.io.IOException;
11+
12+
import de.davis.passwordmanager.App;
13+
import de.davis.passwordmanager.R;
14+
import de.davis.passwordmanager.updater.Updater;
15+
import de.davis.passwordmanager.updater.version.Release;
16+
import de.davis.passwordmanager.utils.PreferenceUtil;
17+
18+
public class MainActivity extends BaseMainActivity {
19+
20+
@Override
21+
protected void onCreate(Bundle savedInstanceState) {
22+
super.onCreate(savedInstanceState);
23+
Updater updater = ((App)getApplication()).getUpdater();
24+
doInBackground(() -> {
25+
try {
26+
Release release = updater.fetchByChannel(PreferenceUtil.getUpdateChannel(this));
27+
setBadge(release, findViewById(R.id.navigationView));
28+
} catch (IOException ignore) {}
29+
});
30+
}
31+
32+
private void setBadge(Release release, NavigationBarView navigationBarView){
33+
BadgeDrawable badgeDrawable = navigationBarView.getOrCreateBadge(R.id.settingsFragment);
34+
badgeDrawable.setVisible(release.isNewer());
35+
}
36+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package de.davis.passwordmanager.ui.settings;
2+
3+
import static de.davis.passwordmanager.utils.BackgroundUtil.doInBackground;
4+
5+
import android.os.Bundle;
6+
import android.os.Looper;
7+
8+
import androidx.annotation.Nullable;
9+
import androidx.core.os.HandlerCompat;
10+
11+
import java.io.IOException;
12+
13+
import de.davis.passwordmanager.App;
14+
import de.davis.passwordmanager.R;
15+
import de.davis.passwordmanager.ui.views.VersionPreference;
16+
import de.davis.passwordmanager.updater.Updater;
17+
import de.davis.passwordmanager.updater.version.Release;
18+
import de.davis.passwordmanager.utils.PreferenceUtil;
19+
import de.davis.passwordmanager.version.CurrentVersion;
20+
21+
public class SettingsFragment extends BaseSettingsFragment {
22+
23+
@Override
24+
public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
25+
super.onCreatePreferences(savedInstanceState, rootKey);
26+
27+
findPreference(getString(R.string.version)).setIcon(R.drawable.ic_baseline_refresh_24);
28+
29+
Updater updater = ((App)requireActivity().getApplication()).getUpdater();
30+
doInBackground(() -> {
31+
try {
32+
Release cached = updater.fetchByChannel(PreferenceUtil.getUpdateChannel(requireContext()));
33+
boolean newer = cached.isNewer();
34+
((VersionPreference)findPreference(getString(R.string.version))).setHighlighted(newer);
35+
HandlerCompat.createAsync(Looper.getMainLooper())
36+
.post(() -> findPreference(getString(R.string.version))
37+
.setSummary(newer
38+
? getString(R.string.newer_version_available, cached.getVersionTag())
39+
: CurrentVersion.getInstance().getVersionTag()));
40+
} catch (IOException ignored) {}
41+
});
42+
}
43+
}

app/src/main/java/de/davis/passwordmanager/ui/settings/UpdaterFragment.java renamed to app/src/github/java/de/davis/passwordmanager/ui/settings/VersionFragment.java

Lines changed: 62 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -11,39 +11,36 @@
1111
import android.content.pm.PackageManager;
1212
import android.os.Build;
1313
import android.os.Bundle;
14-
import android.view.LayoutInflater;
1514
import android.view.View;
16-
import android.view.ViewGroup;
1715

1816
import androidx.activity.result.ActivityResultLauncher;
1917
import androidx.activity.result.contract.ActivityResultContracts;
2018
import androidx.annotation.NonNull;
2119
import androidx.annotation.Nullable;
2220
import androidx.annotation.RequiresApi;
23-
import androidx.fragment.app.Fragment;
21+
import androidx.lifecycle.Lifecycle;
22+
import androidx.lifecycle.LifecycleOwner;
2423
import androidx.lifecycle.ViewModelProvider;
2524
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
2625

2726
import org.apache.commons.io.FileUtils;
2827

2928
import java.io.IOException;
3029

30+
import de.davis.passwordmanager.App;
3131
import de.davis.passwordmanager.PasswordManagerApplication;
3232
import de.davis.passwordmanager.R;
33-
import de.davis.passwordmanager.databinding.FragmentUpdaterBinding;
3433
import de.davis.passwordmanager.ui.viewmodels.UpdaterViewModel;
3534
import de.davis.passwordmanager.updater.Updater;
3635
import de.davis.passwordmanager.updater.downloader.DownloadService;
3736
import de.davis.passwordmanager.updater.exception.RateLimitException;
3837
import de.davis.passwordmanager.updater.installer.InstallBroadcastReceiver;
39-
import de.davis.passwordmanager.updater.version.CurrentVersion;
4038
import de.davis.passwordmanager.updater.version.Release;
4139
import de.davis.passwordmanager.utils.PreferenceUtil;
4240
import de.davis.passwordmanager.utils.VersionUtil;
4341

44-
public class UpdaterFragment extends Fragment {
42+
public class VersionFragment extends BaseVersionFragment {
4543

46-
private FragmentUpdaterBinding binding;
4744
private UpdaterViewModel viewModel;
4845

4946
private String scanDownload;
@@ -89,7 +86,7 @@ public void onReceive(Context context, Intent intent) {
8986
release.getVersionTag()));
9087

9188
try {
92-
FileUtils.deleteDirectory(((PasswordManagerApplication)requireActivity()
89+
FileUtils.deleteDirectory(((App)requireActivity()
9390
.getApplication()).getDownloadDir());
9491
} catch (IOException ignore) {}
9592
}
@@ -125,75 +122,10 @@ public void onReceive(Context context, Intent intent) {
125122
}
126123
};
127124

128-
private final ActivityResultLauncher<String> requestPermissionLauncher =
125+
private ActivityResultLauncher<String> requestPermissionLauncher =
129126
registerForActivityResult(new ActivityResultContracts.RequestPermission(),
130127
isGranted -> permissionsRequested());
131128

132-
@Override
133-
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
134-
binding = FragmentUpdaterBinding.inflate(inflater, container, false);
135-
return binding.getRoot();
136-
}
137-
138-
@Override
139-
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
140-
super.onViewCreated(view, savedInstanceState);
141-
142-
updater = ((PasswordManagerApplication)requireActivity().getApplication()).getUpdater();
143-
viewModel = new ViewModelProvider(requireActivity()).get(UpdaterViewModel.class);
144-
145-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
146-
askForNotificationPermission();
147-
}
148-
149-
scanDownload = getString(R.string.download);
150-
151-
binding.autoComplete.setText(VersionUtil.getChannelName(PreferenceUtil.getUpdateChannel(requireContext()), requireContext()), false);
152-
binding.autoComplete.setOnItemClickListener((parent, view1, position, id) -> {
153-
PreferenceUtil.putUpdateChannel(requireContext(),
154-
VersionUtil.getChannelByName((String) parent.getItemAtPosition(position), requireContext()));
155-
fetch(false);
156-
});
157-
158-
binding.build.setInformationText(CurrentVersion.getInstance().getVersionTag());
159-
binding.channel.setInformationText(VersionUtil.getChannelName(CurrentVersion.getInstance().getChannel(), requireContext()));
160-
161-
viewModel.getErrorLiveData().observe(getViewLifecycleOwner(), e -> {
162-
binding.progressBar.setVisibility(View.GONE);
163-
if(e instanceof RateLimitException){
164-
binding.scan.setEnabled(false);
165-
binding.scan.setText(getString(R.string.try_in_x_seconds,
166-
(((RateLimitException) e).getReset() - System.currentTimeMillis() / 1000)));
167-
binding.update.setInformationText(R.string.gh_api_limit_exceeded);
168-
return;
169-
}
170-
171-
binding.update.setInformationText(e.getMessage());
172-
});
173-
174-
IntentFilter filter = new IntentFilter();
175-
filter.addAction(DownloadService.ACTION_START);
176-
filter.addAction(DownloadService.ACTION_DESTROY);
177-
filter.addAction(DownloadService.ACTION_PROGRESS);
178-
filter.addAction(InstallBroadcastReceiver.ACTION_INSTALL);
179-
180-
LocalBroadcastManager.getInstance(requireContext()).registerReceiver(broadcastReceiver, filter);
181-
}
182-
183-
@Override
184-
public void onStart() {
185-
super.onStart();
186-
187-
((PasswordManagerApplication)requireActivity().getApplication())
188-
.setShouldAuthenticate(true);
189-
}
190-
191-
@Override
192-
public void onDestroyView() {
193-
super.onDestroyView();
194-
LocalBroadcastManager.getInstance(requireContext()).unregisterReceiver(broadcastReceiver);
195-
}
196-
197129
@RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
198130
private void askForNotificationPermission(){
199131
if(requireContext().checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS)
@@ -202,6 +134,7 @@ private void askForNotificationPermission(){
202134
return;
203135
}
204136

137+
((PasswordManagerApplication)requireActivity().getApplication()).setShouldAuthenticate(false);
205138
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS);
206139
}
207140

@@ -271,7 +204,7 @@ private void startFetchingIfNeeded(){
271204
return;
272205
}
273206

274-
if(release.getDownloadedFile((PasswordManagerApplication) requireActivity().getApplication()).isFile()){
207+
if(release.getDownloadedFile((App) requireActivity().getApplication()).isFile()){
275208
prepareUiForInstallation(release);
276209
return;
277210
}
@@ -287,4 +220,58 @@ private void fetch(boolean useCached){
287220
binding.scan.setEnabled(false);
288221
viewModel.fetchGitHubReleases(PreferenceUtil.getUpdateChannel(requireContext()), useCached);
289222
}
223+
224+
@Override
225+
public void onDestroy() {
226+
super.onDestroy();
227+
LocalBroadcastManager.getInstance(requireContext()).unregisterReceiver(broadcastReceiver);
228+
}
229+
230+
@Override
231+
public void onStart() {
232+
super.onStart();
233+
((PasswordManagerApplication) requireActivity().getApplication()).setShouldAuthenticate(true);
234+
}
235+
236+
@Override
237+
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
238+
super.onViewCreated(view, savedInstanceState);
239+
240+
updater = ((App)requireActivity().getApplication()).getUpdater();
241+
viewModel = new ViewModelProvider(requireActivity()).get(UpdaterViewModel.class);
242+
243+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
244+
askForNotificationPermission();
245+
}
246+
247+
scanDownload = getString(R.string.download);
248+
249+
binding.autoComplete.setText(VersionUtil.getChannelName(PreferenceUtil.getUpdateChannel(requireContext()), requireContext()), false);
250+
binding.autoComplete.setOnItemClickListener((parent, view1, position, id) -> {
251+
PreferenceUtil.putUpdateChannel(requireContext(),
252+
VersionUtil.getChannelByName((String) parent.getItemAtPosition(position), requireContext()));
253+
fetch(false);
254+
});
255+
256+
viewModel.getErrorLiveData().observe(getViewLifecycleOwner(), e -> {
257+
binding.progressBar.setVisibility(View.GONE);
258+
if(e instanceof RateLimitException){
259+
binding.scan.setEnabled(false);
260+
binding.scan.setText(getString(R.string.try_in_x_seconds,
261+
(((RateLimitException) e).getReset() - System.currentTimeMillis() / 1000)));
262+
binding.update.setInformationText(R.string.gh_api_limit_exceeded);
263+
return;
264+
}
265+
266+
binding.update.setInformationText(e.getMessage());
267+
});
268+
269+
IntentFilter filter = new IntentFilter();
270+
filter.addAction(DownloadService.ACTION_START);
271+
filter.addAction(DownloadService.ACTION_DESTROY);
272+
filter.addAction(DownloadService.ACTION_PROGRESS);
273+
filter.addAction(InstallBroadcastReceiver.ACTION_INSTALL);
274+
275+
LocalBroadcastManager.getInstance(requireContext()).registerReceiver(broadcastReceiver, filter);
276+
}
290277
}

app/src/main/java/de/davis/passwordmanager/ui/viewmodels/UpdaterViewModel.java renamed to app/src/github/java/de/davis/passwordmanager/ui/viewmodels/UpdaterViewModel.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010

1111
import java.io.IOException;
1212

13-
import de.davis.passwordmanager.PasswordManagerApplication;
13+
import de.davis.passwordmanager.App;
1414
import de.davis.passwordmanager.updater.version.Release;
15-
import de.davis.passwordmanager.updater.version.Version;
15+
import de.davis.passwordmanager.version.Version;
1616
import de.davis.passwordmanager.utils.PreferenceUtil;
1717

1818
public class UpdaterViewModel extends AndroidViewModel {
@@ -28,7 +28,7 @@ public UpdaterViewModel(@NonNull Application application) {
2828
public void fetchGitHubReleases(@Version.Channel int channel, boolean useCached) {
2929
doInBackground(() -> {
3030
try {
31-
releaseLiveData.postValue(((PasswordManagerApplication)getApplication())
31+
releaseLiveData.postValue(((App)getApplication())
3232
.getUpdater().fetchByChannel(channel, useCached));
3333
} catch (IOException e) {
3434
errorLiveData.postValue(e);

0 commit comments

Comments
 (0)