mirror of
https://github.com/whyorean/AuroraStore.git
synced 2026-06-19 04:58:47 -04:00
SelfUpdate : Add initial bits of self update module
This commit is contained in:
@@ -25,13 +25,14 @@
|
||||
package="com.aurora.store">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.ACCESS_MOCK_LOCATION"
|
||||
@@ -168,7 +169,10 @@
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service android:name=".installer.InstallerService" />
|
||||
<service android:name=".SelfUpdateService" />
|
||||
|
||||
<meta-data
|
||||
android:name="preloaded_fonts"
|
||||
android:resource="@array/preloaded_fonts" />
|
||||
|
||||
@@ -24,6 +24,8 @@ public class Constants {
|
||||
public static final String SHARED_PREFERENCES_KEY = "com.aurora.store";
|
||||
public static final String SERVICE_PACKAGE = "com.aurora.services";
|
||||
public static final String APP_DETAIL_URL = "https://play.google.com/store/apps/details?id=";
|
||||
public static final String APP_ICON_URL = "https://gitlab.com/AuroraOSS/AuroraStore/raw/master/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png";
|
||||
public static final String UPDATE_URL = "https://gitlab.com/AuroraOSS/AuroraStore/raw/master/updates.json";
|
||||
|
||||
public static final String INTENT_DEVICE_NAME = "INTENT_DEVICE_NAME";
|
||||
public static final String INTENT_DEVICE_INDEX = "INTENT_DEVICE_INDEX";
|
||||
@@ -36,7 +38,6 @@ public class Constants {
|
||||
public static final String PUB_PREFIX = "pub:";
|
||||
public static final String TAG = "Aurora Store";
|
||||
public static final String FILES = "Files";
|
||||
public static final String GZIPPED = "GZipped";
|
||||
|
||||
public static final String RECENT_HISTORY = "RECENT_HISTORY";
|
||||
|
||||
@@ -108,4 +109,5 @@ public class Constants {
|
||||
public static final String PREFERENCE_TOP_FAMILY = "PREFERENCE_TOP_FAMILY";
|
||||
public static final String PREFERENCE_INSTALLED_APPS = "PREFERENCE_INSTALLED_APPS";
|
||||
public static final String PREFERENCE_CACHE_DATE = "PREFERENCE_CACHE_DATE";
|
||||
public static final String PREFERENCE_SELF_UPDATE_DATE = "PREFERENCE_SELF_UPDATE_DATE";
|
||||
}
|
||||
|
||||
205
app/src/main/java/com/aurora/store/SelfUpdateService.java
Normal file
205
app/src/main/java/com/aurora/store/SelfUpdateService.java
Normal file
@@ -0,0 +1,205 @@
|
||||
package com.aurora.store;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
import com.aurora.store.download.DownloadManager;
|
||||
import com.aurora.store.download.RequestBuilder;
|
||||
import com.aurora.store.model.App;
|
||||
import com.aurora.store.model.Update;
|
||||
import com.aurora.store.task.NetworkTask;
|
||||
import com.aurora.store.utility.CertUtil;
|
||||
import com.aurora.store.utility.Log;
|
||||
import com.aurora.store.utility.PackageUtil;
|
||||
import com.google.gson.Gson;
|
||||
import com.tonyodev.fetch2.AbstractFetchGroupListener;
|
||||
import com.tonyodev.fetch2.Download;
|
||||
import com.tonyodev.fetch2.EnqueueAction;
|
||||
import com.tonyodev.fetch2.Error;
|
||||
import com.tonyodev.fetch2.Fetch;
|
||||
import com.tonyodev.fetch2.FetchGroup;
|
||||
import com.tonyodev.fetch2.FetchListener;
|
||||
import com.tonyodev.fetch2.Request;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
public class SelfUpdateService extends Service {
|
||||
|
||||
public static SelfUpdateService instance = null;
|
||||
|
||||
private CompositeDisposable disposable = new CompositeDisposable();
|
||||
private int hashCode = BuildConfig.APPLICATION_ID.hashCode();
|
||||
private App app;
|
||||
private Fetch fetch;
|
||||
private FetchListener fetchListener;
|
||||
private Gson gson = new Gson();
|
||||
|
||||
public static boolean isServiceRunning() {
|
||||
try {
|
||||
return instance != null && instance.isRunning();
|
||||
} catch (NullPointerException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRunning() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
||||
startForeground(1, getNotification());
|
||||
} else {
|
||||
Notification notification = getNotification(new NotificationCompat.Builder(this));
|
||||
startForeground(1, notification);
|
||||
}
|
||||
startUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
instance = null;
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
private void destroyService() {
|
||||
Log.e("Self-update service destroyed");
|
||||
stopForeground(true);
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
private void startUpdate() {
|
||||
disposable.add(Observable.fromCallable(() -> new NetworkTask(this)
|
||||
.get(Constants.UPDATE_URL))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(response -> {
|
||||
try {
|
||||
gson = new Gson();
|
||||
Update update = gson.fromJson(response, Update.class);
|
||||
downloadAndUpdate(update);
|
||||
} catch (Exception e) {
|
||||
Log.e("Error checking self-update");
|
||||
destroyService();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private void downloadAndUpdate(Update update) {
|
||||
app = new App();
|
||||
app.setPackageName(BuildConfig.APPLICATION_ID);
|
||||
app.setDisplayName(getString(R.string.app_name));
|
||||
app.setVersionName(update.getVersionName());
|
||||
app.setVersionCode(update.getVersionCode());
|
||||
|
||||
Request request = RequestBuilder.buildRequest(this, app, isFDroidVariant() ? update.getFdroidBuild() : update.getAuroraBuild());
|
||||
request.setEnqueueAction(EnqueueAction.REPLACE_EXISTING);
|
||||
List<Request> requestList = new ArrayList<>();
|
||||
requestList.add(request);
|
||||
|
||||
fetch = DownloadManager.getFetchInstance(this);
|
||||
fetch.enqueue(requestList, result -> {
|
||||
Log.d("Downloading latest self-update");
|
||||
});
|
||||
|
||||
fetchListener = getFetchListener();
|
||||
fetch.addListener(fetchListener);
|
||||
|
||||
//Add <PackageName,DisplayName> and <PackageName,IconURL> to PseudoMaps
|
||||
PackageUtil.addToPseudoPackageMap(this, app.getPackageName(), app.getDisplayName());
|
||||
PackageUtil.addToPseudoURLMap(this, app.getPackageName(), Constants.APP_ICON_URL);
|
||||
}
|
||||
|
||||
private boolean isFDroidVariant() {
|
||||
return CertUtil.isFDroidApp(this, BuildConfig.APPLICATION_ID);
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
private Notification getNotification() {
|
||||
String NOTIFICATION_CHANNEL_ID = BuildConfig.APPLICATION_ID;
|
||||
String channelName = "Self Update Service";
|
||||
|
||||
NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_HIGH);
|
||||
notificationChannel.setLightColor(Color.BLUE);
|
||||
notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
|
||||
|
||||
NotificationManager manager = (NotificationManager) getSystemService(SelfUpdateService.NOTIFICATION_SERVICE);
|
||||
manager.createNotificationChannel(notificationChannel);
|
||||
|
||||
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
|
||||
return getNotification(notificationBuilder);
|
||||
}
|
||||
|
||||
private Notification getNotification(NotificationCompat.Builder builder) {
|
||||
int versionCode = Build.VERSION.SDK_INT;
|
||||
return builder
|
||||
.setAutoCancel(true)
|
||||
.setCategory(Notification.CATEGORY_PROGRESS)
|
||||
.setContentTitle("Self update")
|
||||
.setContentText("Updating Aurora Store in background")
|
||||
.setOngoing(false)
|
||||
.setPriority(versionCode >= Build.VERSION_CODES.O ? NotificationCompat.PRIORITY_DEFAULT : Notification.PRIORITY_DEFAULT)
|
||||
.setSmallIcon(R.drawable.ic_update)
|
||||
.build();
|
||||
}
|
||||
|
||||
private FetchListener getFetchListener() {
|
||||
return new AbstractFetchGroupListener() {
|
||||
@Override
|
||||
public void onError(int groupId, @NotNull Download download, @NotNull Error error,
|
||||
@org.jetbrains.annotations.Nullable Throwable throwable, @NotNull FetchGroup fetchGroup) {
|
||||
if (groupId == hashCode) {
|
||||
Log.e("Error self-updating %s", app.getDisplayName());
|
||||
destroyService();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted(int groupId, @NotNull Download download, @NotNull FetchGroup fetchGroup) {
|
||||
if (groupId == hashCode && fetchGroup.getGroupDownloadProgress() == 100) {
|
||||
AuroraApplication.getInstaller().install(app);
|
||||
destroyService();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelled(int groupId, @NotNull Download download, @NotNull FetchGroup fetchGroup) {
|
||||
if (groupId == hashCode) {
|
||||
Log.e("Self-update cancelled %s", app.getDisplayName());
|
||||
destroyService();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,6 @@
|
||||
package com.aurora.store.activity;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.StatusBarManager;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
@@ -38,23 +37,37 @@ import androidx.core.graphics.ColorUtils;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
|
||||
import com.aurora.store.BuildConfig;
|
||||
import com.aurora.store.Constants;
|
||||
import com.aurora.store.R;
|
||||
import com.aurora.store.SelfUpdateService;
|
||||
import com.aurora.store.adapter.ViewPagerAdapter;
|
||||
import com.aurora.store.fragment.AppsFragment;
|
||||
import com.aurora.store.fragment.HomeFragment;
|
||||
import com.aurora.store.fragment.SearchFragment;
|
||||
import com.aurora.store.model.Update;
|
||||
import com.aurora.store.task.NetworkTask;
|
||||
import com.aurora.store.utility.Accountant;
|
||||
import com.aurora.store.utility.CertUtil;
|
||||
import com.aurora.store.utility.Log;
|
||||
import com.aurora.store.utility.PrefUtil;
|
||||
import com.aurora.store.utility.TextUtil;
|
||||
import com.aurora.store.utility.ThemeUtil;
|
||||
import com.aurora.store.utility.Util;
|
||||
import com.aurora.store.utility.ViewUtil;
|
||||
import com.aurora.store.view.CustomViewPager;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
public class AuroraActivity extends AppCompatActivity {
|
||||
|
||||
@@ -67,6 +80,7 @@ public class AuroraActivity extends AppCompatActivity {
|
||||
CustomViewPager viewPager;
|
||||
@BindView(R.id.bottom_navigation)
|
||||
BottomNavigationView bottomNavigationView;
|
||||
|
||||
private ActionBar actionBar;
|
||||
private ViewPagerAdapter pagerAdapter;
|
||||
private ThemeUtil themeUtil = new ThemeUtil();
|
||||
@@ -86,6 +100,7 @@ public class AuroraActivity extends AppCompatActivity {
|
||||
ButterKnife.bind(this);
|
||||
fragmentCur = Util.getDefaultTab(this);
|
||||
onNewIntent(getIntent());
|
||||
|
||||
if (!PrefUtil.getBoolean(this, Constants.PREFERENCE_DO_NOT_SHOW_INTRO)) {
|
||||
PrefUtil.putBoolean(this, Constants.PREFERENCE_DO_NOT_SHOW_INTRO, true);
|
||||
startActivity(new Intent(this, IntroActivity.class));
|
||||
@@ -100,6 +115,9 @@ public class AuroraActivity extends AppCompatActivity {
|
||||
if (Util.isCacheObsolete(this))
|
||||
Util.clearCache(this);
|
||||
|
||||
if (Util.shouldCheckUpdate(this))
|
||||
checkSelfUpdate();
|
||||
|
||||
checkPermissions();
|
||||
}
|
||||
|
||||
@@ -187,7 +205,6 @@ public class AuroraActivity extends AppCompatActivity {
|
||||
disposable.clear();
|
||||
}
|
||||
|
||||
|
||||
private void setupActionbar() {
|
||||
setSupportActionBar(toolbar);
|
||||
actionBar = getSupportActionBar();
|
||||
@@ -240,6 +257,35 @@ public class AuroraActivity extends AppCompatActivity {
|
||||
.getItem(fragmentCur).getItemId());
|
||||
}
|
||||
|
||||
private void checkSelfUpdate() {
|
||||
disposable.add(Observable.fromCallable(() -> new NetworkTask(this)
|
||||
.get(Constants.UPDATE_URL))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(response -> {
|
||||
try {
|
||||
Util.setSelfUpdateTime(this, Calendar.getInstance().getTimeInMillis());
|
||||
Gson gson = new Gson();
|
||||
Update update = gson.fromJson(response, Update.class);
|
||||
if (update.getVersionCode() > BuildConfig.VERSION_CODE) {
|
||||
if (CertUtil.isFDroidApp(this, BuildConfig.APPLICATION_ID)
|
||||
&& TextUtil.emptyIfNull(update.getFdroidBuild()).isEmpty()) {
|
||||
Log.d("FDroid build of latest version is not published yet");
|
||||
return;
|
||||
}
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
||||
showAddRepoDialog(update);
|
||||
} else {
|
||||
Log.i("No new update available");
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("Error checking updates");
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private void checkPermissions() {
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
@@ -248,4 +294,25 @@ public class AuroraActivity extends AppCompatActivity {
|
||||
1337);
|
||||
}
|
||||
}
|
||||
|
||||
protected void showAddRepoDialog(Update update) {
|
||||
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this)
|
||||
.setTitle("New update available")
|
||||
.setMessage(new StringBuilder()
|
||||
.append(update.getVersionName())
|
||||
.append("\nChangelog:\n")
|
||||
.append(TextUtil.emptyIfNull(update.getChangelog()))
|
||||
.append("\n")
|
||||
.append("Do you wish to update now ?")
|
||||
.toString())
|
||||
.setPositiveButton(getString(android.R.string.yes), (dialog, which) -> {
|
||||
Intent intent = new Intent(this, SelfUpdateService.class);
|
||||
startService(intent);
|
||||
})
|
||||
.setNegativeButton(getString(android.R.string.no), (dialog, which) -> {
|
||||
dialog.dismiss();
|
||||
});
|
||||
builder.create();
|
||||
builder.show();
|
||||
}
|
||||
}
|
||||
|
||||
62
app/src/main/java/com/aurora/store/model/Update.java
Normal file
62
app/src/main/java/com/aurora/store/model/Update.java
Normal file
@@ -0,0 +1,62 @@
|
||||
package com.aurora.store.model;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class Update {
|
||||
@SerializedName("version_name")
|
||||
@Expose
|
||||
private String versionName;
|
||||
@SerializedName("version_code")
|
||||
@Expose
|
||||
private Integer versionCode;
|
||||
@SerializedName("aurora_build")
|
||||
@Expose
|
||||
private String auroraBuild;
|
||||
@SerializedName("fdroid_build")
|
||||
@Expose
|
||||
private String fdroidBuild;
|
||||
@SerializedName("changelog")
|
||||
@Expose
|
||||
private String changelog;
|
||||
|
||||
public String getVersionName() {
|
||||
return versionName;
|
||||
}
|
||||
|
||||
public void setVersionName(String versionName) {
|
||||
this.versionName = versionName;
|
||||
}
|
||||
|
||||
public Integer getVersionCode() {
|
||||
return versionCode;
|
||||
}
|
||||
|
||||
public void setVersionCode(Integer versionCode) {
|
||||
this.versionCode = versionCode;
|
||||
}
|
||||
|
||||
public String getAuroraBuild() {
|
||||
return auroraBuild;
|
||||
}
|
||||
|
||||
public void setAuroraBuild(String auroraBuild) {
|
||||
this.auroraBuild = auroraBuild;
|
||||
}
|
||||
|
||||
public String getFdroidBuild() {
|
||||
return fdroidBuild;
|
||||
}
|
||||
|
||||
public void setFdroidBuild(String fdroidBuild) {
|
||||
this.fdroidBuild = fdroidBuild;
|
||||
}
|
||||
|
||||
public String getChangelog() {
|
||||
return changelog;
|
||||
}
|
||||
|
||||
public void setChangelog(String changelog) {
|
||||
this.changelog = changelog;
|
||||
}
|
||||
}
|
||||
@@ -489,7 +489,7 @@ public class Util {
|
||||
Log.i("Periodic update preferences updated");
|
||||
}
|
||||
|
||||
public static void clearOldInstallationSessions(Context context){
|
||||
public static void clearOldInstallationSessions(Context context) {
|
||||
final PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
|
||||
for (PackageInstaller.SessionInfo sessionInfo : packageInstaller.getMySessions()) {
|
||||
final int sessionId = sessionInfo.getSessionId();
|
||||
@@ -501,4 +501,20 @@ public class Util {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean shouldCheckUpdate(Context context) {
|
||||
try {
|
||||
long lastSyncDate = Long.parseLong(PrefUtil.getString(context, Constants.PREFERENCE_SELF_UPDATE_DATE));
|
||||
long currentSyncDate = Calendar.getInstance().getTimeInMillis();
|
||||
long diffDatesInMillis = currentSyncDate - lastSyncDate;
|
||||
long diffInDays = TimeUnit.MILLISECONDS.toDays(diffDatesInMillis);
|
||||
return diffInDays >= 1;
|
||||
} catch (Exception e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static void setSelfUpdateTime(Context context, Long dateInMillis) {
|
||||
PrefUtil.putString(context, Constants.PREFERENCE_SELF_UPDATE_DATE, String.valueOf(dateInMillis));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user