package com.aptoide.amethyst.services; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.database.Cursor; import android.os.IBinder; import android.preference.PreferenceManager; import android.support.v7.app.NotificationCompat; import android.text.TextUtils; import com.aptoide.amethyst.Aptoide; import com.aptoide.amethyst.R; import com.aptoide.amethyst.database.AptoideDatabase; import com.aptoide.amethyst.events.BusProvider; import com.aptoide.amethyst.events.OttoEvents; import com.aptoide.amethyst.utils.AptoideUtils; import com.aptoide.amethyst.utils.IconSizeUtils; import com.aptoide.amethyst.utils.Logger; import com.aptoide.dataprovider.webservices.Webservices; import com.aptoide.dataprovider.webservices.models.Constants; import com.aptoide.dataprovider.webservices.models.UpdatesApi; import com.aptoide.dataprovider.webservices.models.UpdatesResponse; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.android.gms.ads.identifier.AdvertisingIdClient; import com.google.android.gms.common.GooglePlayServicesNotAvailableException; import com.google.android.gms.common.GooglePlayServicesRepairableException; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import com.aptoide.amethyst.MainActivity; import retrofit.RestAdapter; import retrofit.converter.JacksonConverter; /** * Created by rmateus on 15/06/15. */ public class UpdatesService extends Service { public static final String FORCE_UPDATE = "force_update"; public static final int MAX_UPDATES = 50; @Override public IBinder onBind(Intent intent) { return null; } static ScheduledExecutorService executor; GetUpdates task = new GetUpdates(); static int retries = 0; @Override public int onStartCommand(Intent intent, int flags, int startId) { Logger.d("AptoideUpdates", "OnStartCommand"); synchronized (this) { // AN-342: The updates tab is not populated on the first run when the device doesn't have internet connection if (AptoideUtils.NetworkUtils.isNetworkAvailable(getApplicationContext()) || !(new AptoideDatabase(Aptoide.getDb()).hasInstalled())) { if (executor == null) { executor = Executors.newSingleThreadScheduledExecutor(); executor.scheduleAtFixedRate(task, 0, 30, TimeUnit.SECONDS); } if (intent != null && intent.hasExtra(FORCE_UPDATE)) { new Thread(new Runnable() { @Override public void run() { try { executor.shutdown(); Logger.d("AptoideUpdates", "Awaiting previous executor to terminate"); executor.awaitTermination(2, TimeUnit.MINUTES); Logger.d("AptoideUpdates", "Previous terminatated"); } catch (InterruptedException e) { Logger.printException(e); } executor = Executors.newSingleThreadScheduledExecutor(); executor.scheduleAtFixedRate(task, 0, 30, TimeUnit.SECONDS); } }).start(); } } else { if (executor != null) { executor.shutdown(); } broadcastFinishEvent(); executor = null; stopSelf(); } } return START_STICKY_COMPATIBILITY; } public class GetUpdates implements Runnable { @Override public void run() { List<UpdatesResponse.UpdateApk> responseList = new ArrayList<>(); AptoideDatabase database = new AptoideDatabase(Aptoide.getDb()); if (!database.hasInstalled()) { Logger.d("AptoideUpdates", "First run install"); PackageManager packageManager = Aptoide.getContext().getPackageManager(); if (packageManager != null) { List<PackageInfo> installedPackages = packageManager.getInstalledPackages(PackageManager.GET_SIGNATURES); for (PackageInfo anInstalledPackage : installedPackages) { try { UpdatesApi.Package aPackage = new UpdatesApi.Package(); aPackage.signature = AptoideUtils.Algorithms.computeSHA1sumFromBytes(anInstalledPackage.signatures[0].toByteArray()).toUpperCase(Locale.ENGLISH); aPackage.vercode = anInstalledPackage.versionCode; aPackage.packageName = anInstalledPackage.packageName; database.insertInstalled(aPackage); } catch (Exception e) { Logger.printException(e); } } } } try { List<UpdatesApi.Package> updates = database.getUpdates(MAX_UPDATES); if (updates.isEmpty()) { executor.shutdown(); Logger.d("AptoideUpdates", "database.getUpdates(50) is Empty. Stopping service and executor is " + executor.isShutdown()); if (PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getBoolean("showUpdatesNotification", true)) { showUpdatesNotification(); } // broadcastFinishEvent(0); stopSelf(); return; } UpdatesApi api = new UpdatesApi(); initUpdatesApi(api); Cursor servers = database.getStoresCursor(); for (servers.moveToFirst(); !servers.isAfterLast(); servers.moveToNext()) { String name = servers.getString(servers.getColumnIndex("name")); api.store_names.add(name); // the username of the store, not the Login String username = servers.getString(servers.getColumnIndex("username")); if (!TextUtils.isEmpty(username)) { String password = servers.getString(servers.getColumnIndex("password")); if (api.stores_auth == null) { api.stores_auth = new ArrayList<>(); } UpdatesApi.StoreAuth storeAuth = new UpdatesApi.StoreAuth(); storeAuth.store_name = name; storeAuth.store_user = username; storeAuth.store_pass_sha1 = password; api.stores_auth.add(storeAuth); } } //} servers.close(); if (!api.store_names.isEmpty() && AptoideUtils.NetworkUtils.isNetworkAvailable(getApplicationContext())) { api.apks_data.addAll(updates); ObjectMapper mapper = new ObjectMapper(); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); RestAdapter adapter = new RestAdapter.Builder().setLogLevel(RestAdapter.LogLevel.FULL).setConverter(new JacksonConverter(mapper)).setEndpoint("http://").build(); Logger.d("AptoideUpdates", "Getting updates"); UpdatesResponse webUpdates = adapter.create(Webservices.class).getUpdates(api); if (webUpdates != null && webUpdates.data != null && webUpdates.data.list != null) { Logger.d("AptoideUpdates", "Got updates: " + webUpdates.data.list.size()); List<UpdatesResponse.UpdateApk> list = webUpdates.data.list; responseList.addAll(list); } } for (UpdatesApi.Package aPackage : updates) { database.resetPackage(aPackage.packageName); for (UpdatesResponse.UpdateApk aPackage2 : responseList) { if (aPackage2.packageName.equals(aPackage.packageName)) { database.updatePackage(aPackage2); } } } handleAutoUpdate(database.getAvailableUpdates(getApplicationContext())); retries = 0; } catch (Exception e) { Logger.printException(e); Logger.d("AptoideUpdates", "Exception retries: " + retries); if (retries == 7) { executor.shutdown(); stopSelf(); Logger.d("AptoideUpdates", "Service exceeded retries. Shutting down. "); retries = 0; } retries++; } broadcastFinishEvent(); Logger.d("AptoideUpdates", "Stopped"); } private void initUpdatesApi(UpdatesApi api) { api.q = AptoideUtils.HWSpecifications.filters(Aptoide.getContext()); api.cpuid = PreferenceManager.getDefaultSharedPreferences(Aptoide.getContext()).getString("APTOIDE_CLIENT_UUID", UpdatesApi.DEFAULT_CPUID); api.mature = PreferenceManager.getDefaultSharedPreferences(Aptoide.getContext()).getBoolean(Constants.MATURE_CHECK_BOX, false); api.aaid = getAdvertisementId(); } private String getAdvertisementId() { try { return AdvertisingIdClient.getAdvertisingIdInfo(Aptoide.getContext()).getId(); } catch (IOException | GooglePlayServicesNotAvailableException | GooglePlayServicesRepairableException e) { Logger.printException(e); } return null; } } //TODO: notification builder private void showUpdatesNotification() { int updates = 0; Cursor data = null; SharedPreferences defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); try { data = new AptoideDatabase(Aptoide.getDb()).getUpdates(); updates = data.getCount(); } finally { if (data != null) data.close(); } if (updates > 0 && updates != defaultSharedPreferences.getInt("updates", 0)) { NotificationManager managerNotification = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); if(Aptoide.getConfiguration().getMarketName().equals("Aptoide")) { int icon = R.drawable.ic_stat_aptoide_notification; //} Context context = getApplicationContext(); CharSequence tickerText = AptoideUtils.StringUtils.getFormattedString(context, R.string.has_updates, Aptoide.getConfiguration().getMarketName()); CharSequence contentTitle = Aptoide.getConfiguration().getMarketName(); CharSequence contentText = AptoideUtils.StringUtils.getFormattedString(context, R.string.new_updates, updates); if (updates == 1) { contentText = AptoideUtils.StringUtils.getFormattedString(context, R.string.one_new_update, updates); } Intent notificationIntent = new Intent(); notificationIntent.setClassName(getPackageName(), MainActivity.class.getName()); notificationIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK); notificationIntent.setAction(""); notificationIntent.putExtra("new_updates", true); PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT); Notification notification = new NotificationCompat.Builder(this) .setSmallIcon(icon) .setContentTitle(contentTitle) .setContentText(contentText) .setContentIntent(contentIntent) .setTicker(tickerText) .build(); notification.flags = Notification.DEFAULT_LIGHTS | Notification.FLAG_AUTO_CANCEL; managerNotification.notify(546, notification); defaultSharedPreferences.edit().putInt("updates", data.getCount()).apply(); } } } @Override public void onDestroy() { super.onDestroy(); executor = null; Logger.d("AptoideUpdates", "OnDestroy"); } private void handleAutoUpdate(final List<UpdatesResponse.UpdateApk> updates) { final Context context = getApplicationContext(); final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); if (!preferences.getBoolean("auto_update", false) || updates.isEmpty()) { return; } final Intent intent = new Intent(context, DownloadService.class); context.bindService(intent, new ServiceConnection() { @Override public void onServiceConnected(final ComponentName name, final IBinder service) { final DownloadService.LocalBinder binder = (DownloadService.LocalBinder) service; final DownloadService downloadService = binder.getService(); for (final UpdatesResponse.UpdateApk updateApk : updates) { if (updateApk.icon != null && updateApk.icon.contains("_icon")) { String[] splittedUrl = updateApk.icon.split("\\.(?=[^\\.]+$)"); String iconSize = IconSizeUtils.generateSizeString(context); updateApk.icon = splittedUrl[0] + "_" + iconSize + "." + splittedUrl[1]; } downloadService.downloadFromV7(updateApk, false); } } @Override public void onServiceDisconnected(final ComponentName name) { } }, BIND_AUTO_CREATE); } private void broadcastFinishEvent() { Cursor data = new AptoideDatabase(Aptoide.getDb()).getUpdates(); int size = data.getCount(); data.close(); BusProvider.getInstance().post(new OttoEvents.GetUpdatesFinishedEvent(size)); } }