Initial UI work

Look and feel based on CMUpdater.
This commit is contained in:
Gabriele M
2017-07-16 21:23:45 +02:00
parent a455c95e0c
commit e51be80f6a
16 changed files with 571 additions and 109 deletions

View File

@@ -33,5 +33,6 @@ LOCAL_AAPT_FLAGS := \
LOCAL_PACKAGE_NAME := Updater
LOCAL_PRIVILEGED_MODULE := true
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
include $(BUILD_PACKAGE)

View File

@@ -14,11 +14,12 @@
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.Light.DarkActionBar">
android:theme="@style/AppTheme">
<activity
android:name=".UpdatesActivity"
android:label="@string/display_name"
android:theme="@style/AppTheme.NoActionBar"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

2
proguard.flags Normal file
View File

@@ -0,0 +1,2 @@
-keep class android.support.design.widget.** { *; }
-keep interface android.support.design.widget.** { *; }

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z"/>
</vector>

10
res/drawable/ic_pause.xml Normal file
View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
</vector>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M8,5v14l11,-7z"/>
</vector>

View File

@@ -2,10 +2,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@android:color/white"
android:pathData="M17 1.01L7 1c-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM17 19H7V5h10v14zm-1-6h-3V8h-2v5H8l4 4 4-4z" />
android:fillColor="#FF000000"
android:pathData="M17,1.01L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19L7,19L7,5h10v14zM16,13h-3L13,8h-2v5L8,13l4,4 4,-4z"/>
</vector>

View File

@@ -1,13 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="@style/AppTheme.AppBarOverlay"
app:collapsedTitleTextAppearance="@style/TextAppearanceWhite"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleTextAppearance="@style/TextAppearanceTransparent"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="86dp"
android:paddingBottom="16dp"
android:paddingEnd="16dp"
android:paddingStart="16dp"
app:layout_collapseMode="parallax">
<TextView
android:id="@+id/header_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"
android:paddingBottom="16dp"
android:textColor="@color/white"
android:textSize="56sp" />
<TextView
android:id="@+id/header_build_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/header_title"
android:textColor="@color/white"
android:textSize="12sp" />
<TextView
android:id="@+id/header_build_version"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/header_build_date"
android:textColor="@color/white"
android:textSize="12sp" />
<TextView
android:id="@+id/header_last_check"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/header_build_version"
android:textColor="@color/white"
android:textSize="12sp" />
</RelativeLayout>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="56dp"
android:titleTextColor="@color/white"
app:layout_collapseMode="pin" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical" />
</FrameLayout>
android:scrollbars="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>

View File

@@ -1,31 +1,102 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:layout_marginEnd="5dp"
android:layout_marginStart="5dp"
android:layout_marginTop="5dp">
<Button
android:id="@+id/button1"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="false"
android:orientation="horizontal"
android:paddingBottom="16dp"
android:paddingStart="16dp"
android:paddingTop="16dp"
android:weightSum="1">
<RelativeLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<RelativeLayout
android:id="@+id/build_layout_not_active"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true">
<TextView
android:id="@+id/build_version"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="8sp" />
<TextView
android:id="@+id/build_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/build_version" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/build_layout_active"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:visibility="invisible">
<TextView
android:id="@+id/build_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/download_button" />
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/cancel_button" />
android:layout_below="@id/build_info"
android:layout_gravity="center">
<ProgressBar
android:id="@+id/progress_bar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
android:layout_height="wrap_content"
android:progress="30" />
<TextView
android:id="@+id/progress_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/progress_bar"
android:textSize="12sp" />
<TextView
android:id="@+id/progress_percentage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_below="@id/progress_bar"
android:textSize="12sp" />
</RelativeLayout>
</RelativeLayout>
</RelativeLayout>
<RelativeLayout
android:layout_width="80dp"
android:layout_height="match_parent">
<ImageButton
android:id="@+id/update_action"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="Download"
android:src="@drawable/ic_download" />
</RelativeLayout>
</LinearLayout>
</android.support.v7.widget.CardView>

10
res/values/colors.xml Normal file
View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="theme_primary">#ff212121</color>
<color name="theme_primary_dark">#ff000000</color>
<color name="theme_accent">#ff009688</color>
<color name="background">#fafafa</color>
<color name="dark_gray">#333333</color>
<color name="white">#f5f5f5</color>
</resources>

View File

@@ -25,7 +25,7 @@
<string name="conf_update_server_url_def" translatable="false">https://download.lineageos.org/api</string>
<string name="verification_failed_notification">Verification failed</string>
<string name="verifying_download_notification">Verifying download</string>
<string name="verifying_download_notification">Verifying update</string>
<string name="downloading_notification">Downloading</string>
<string name="download_paused_notification">Download paused</string>
<string name="download_paused_error_notification">Download error</string>
@@ -59,4 +59,24 @@
<string name="snack_updates_found">New updates found</string>
<string name="snack_no_updates_found">No new updates found</string>
<string name="snack_updates_check_failed">The update check failed. Please check your internet connection and try again later.</string>
<string name="header_title_text">LineageOS\n%1$s</string>
<string name="header_android_version">Android <xliff:g id="version" example="7.1.2">%1$s</xliff:g></string>
<string name="header_last_updates_check">Last checked: <xliff:g id="date" example="1 January 1970">%1$s</xliff:g> (<xliff:g id="time" example="01:23">%2$s</xliff:g>)</string>
<string name="list_build_version">LineageOS <xliff:g id="version" example="14.1">%1$s</xliff:g></string>
<string name="list_build_version_date">LineageOS <xliff:g id="version" example="14.1">%1$s</xliff:g> - <xliff:g id="date" example="July 11, 2017">%2$s</xliff:g></string>
<string name="list_download_progress"><xliff:g id="filesize_without_unit" example="12.2">%1$s</xliff:g> of <xliff:g id="filesize_without_unit" example="310 MB">%2$s</xliff:g></string>
<string name="list_download_progress_eta"><xliff:g id="filesize_without_unit" example="12.2">%1$s</xliff:g> of <xliff:g id="filesize_without_unit" example="310 MB">%2$s</xliff:g> - <xliff:g id="duration" example="3 minutes">%3$s</xliff:g> left</string>
<string name="list_installing_update">Installing update</string>
<string name="list_verifying_update">Verifying update</string>
<string name="action_description_download">Download</string>
<string name="action_description_pause">Pause download</string>
<string name="action_description_resume">Resume download</string>
<string name="action_description_install">Install update</string>
<string name="action_description_verify">Verify update</string>
<string name="confirm_delete_dialog_title">Delete file</string>
<string name="confirm_delete_dialog_message">Delete the selected update file?</string>
</resources>

24
res/values/styles.xml Normal file
View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">@color/theme_primary</item>
<item name="colorPrimaryDark">@color/theme_primary_dark</item>
<item name="colorAccent">@color/theme_accent</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="TextAppearanceWhite" parent="@android:style/TextAppearance">
<item name="android:textColor">@color/white</item>
</style>
<style name="TextAppearanceTransparent" parent="@android:style/TextAppearance">
<item name="android:textColor">@android:color/transparent</item>
</style>
</resources>

View File

@@ -23,8 +23,13 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.icu.text.DateFormat;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CollapsingToolbarLayout;
import android.support.design.widget.Snackbar;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
@@ -32,17 +37,21 @@ import android.support.v7.preference.PreferenceManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SimpleItemAnimator;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import org.json.JSONException;
import org.lineageos.updater.controller.UpdaterController;
import org.lineageos.updater.controller.UpdaterControllerInt;
import org.lineageos.updater.controller.UpdaterService;
import org.lineageos.updater.download.DownloadClient;
import org.lineageos.updater.misc.BuildInfoUtils;
import org.lineageos.updater.misc.Constants;
import org.lineageos.updater.misc.LegacySupport;
import org.lineageos.updater.misc.StringGenerator;
import org.lineageos.updater.misc.Utils;
import java.io.File;
@@ -91,7 +100,44 @@ public class UpdatesActivity extends AppCompatActivity {
}
};
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayShowTitleEnabled(false);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
TextView headerTitle = (TextView) findViewById(R.id.header_title);
headerTitle.setText(getString(R.string.header_title_text,
BuildInfoUtils.getBuildVersion()));
updateLastCheckedString();
TextView headerBuildVersion = (TextView) findViewById(R.id.header_build_version);
headerBuildVersion.setText(
getString(R.string.header_android_version, Build.VERSION.RELEASE));
TextView headerBuildDate = (TextView) findViewById(R.id.header_build_date);
headerBuildDate.setText(StringGenerator.getDateLocalized(this,
DateFormat.LONG, BuildInfoUtils.getBuildDateTimestamp()));
// Switch between header title and appbar title minimizing overlaps
final CollapsingToolbarLayout collapsingToolbar =
(CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar);
final AppBarLayout appBar = (AppBarLayout) findViewById(R.id.app_bar);
appBar.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
boolean mIsShown = false;
@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
int scrollRange = appBarLayout.getTotalScrollRange();
if (!mIsShown && scrollRange + verticalOffset < 10) {
collapsingToolbar.setTitle(getString(R.string.display_name));
mIsShown = true;
} else if (mIsShown && scrollRange + verticalOffset > 100) {
collapsingToolbar.setTitle(null);
mIsShown = false;
}
}
});
}
@Override
@@ -277,6 +323,7 @@ public class UpdatesActivity extends AppCompatActivity {
.putLong(Constants.PREF_LAST_UPDATE_CHECK, millis)
.apply();
jsonFileTmp.renameTo(jsonFile);
updateLastCheckedString();
} catch (IOException | JSONException e) {
Log.e(TAG, "Could not read json", e);
showSnackBar(R.string.snack_updates_check_failed, Snackbar.LENGTH_LONG);
@@ -304,6 +351,17 @@ public class UpdatesActivity extends AppCompatActivity {
downloadClient.start();
}
private void updateLastCheckedString() {
final SharedPreferences preferences =
PreferenceManager.getDefaultSharedPreferences(this);
long lastCheck = preferences.getLong(Constants.PREF_LAST_UPDATE_CHECK, -1) / 1000;
String lastCheckString = getString(R.string.header_last_updates_check,
StringGenerator.getDateLocalized(this, DateFormat.LONG, lastCheck),
StringGenerator.getTimeLocalized(this, lastCheck));
TextView headerLastCheck = (TextView) findViewById(R.id.header_last_check);
headerLastCheck.setText(lastCheckString);
}
private void showSnackBar(int stringId, int duration) {
Snackbar.make(findViewById(R.id.main_container), stringId, duration).show();
}

View File

@@ -17,22 +17,31 @@ package org.lineageos.updater;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.text.format.DateUtils;
import android.text.format.Formatter;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.TextView;
import org.lineageos.updater.controller.UpdaterControllerInt;
import org.lineageos.updater.misc.BuildInfoUtils;
import org.lineageos.updater.misc.StringGenerator;
import org.lineageos.updater.misc.Utils;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.util.List;
public class UpdatesListAdapter extends RecyclerView.Adapter<UpdatesListAdapter.ViewHolder> {
private static final String TAG = "UpdateListAdapter";
private final float mAlphaDisabledValue;
private List<String> mDownloadIds;
private UpdaterControllerInt mUpdaterController;
private Context mContext;
@@ -42,27 +51,44 @@ public class UpdatesListAdapter extends RecyclerView.Adapter<UpdatesListAdapter.
PAUSE,
RESUME,
VERIFY,
CANCEL,
INSTALL
}
public static class ViewHolder extends RecyclerView.ViewHolder {
private TextView mName;
private Button mButton1;
private Button mButton2;
private ImageButton mAction;
private View mNotActiveLayout;
private TextView mBuildDate;
private TextView mBuildVersion;
private View mActiveLayout;
private TextView mBuildInfo;
private ProgressBar mProgressBar;
private TextView mProgressText;
private TextView mProgressPercentage;
public ViewHolder(final View view) {
super(view);
mName = (TextView) view.findViewById(R.id.name);
mButton1 = (Button) view.findViewById(R.id.button1);
mButton2 = (Button) view.findViewById(R.id.button2);
mAction = (ImageButton) view.findViewById(R.id.update_action);
mNotActiveLayout = view.findViewById(R.id.build_layout_not_active);
mBuildDate = (TextView) view.findViewById(R.id.build_date);
mBuildVersion = (TextView) view.findViewById(R.id.build_version);
mActiveLayout = view.findViewById(R.id.build_layout_active);
mBuildInfo = (TextView) view.findViewById(R.id.build_info);
mProgressBar = (ProgressBar) view.findViewById(R.id.progress_bar);
mProgressText = (TextView) view.findViewById(R.id.progress_text);
mProgressPercentage = (TextView) view.findViewById(R.id.progress_percentage);
}
}
public UpdatesListAdapter(Context context) {
mContext = context;
TypedValue tv = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, tv, true);
mAlphaDisabledValue = tv.getFloat();
}
@Override
@@ -77,11 +103,85 @@ public class UpdatesListAdapter extends RecyclerView.Adapter<UpdatesListAdapter.
notifyDataSetChanged();
}
private void handleActiveStatus(ViewHolder viewHolder, UpdateDownload update) {
String buildDate = StringGenerator.getDateLocalized(mContext,
DateFormat.MEDIUM, update.getTimestamp());
String buildInfoText = mContext.getString(R.string.list_build_version_date,
BuildInfoUtils.getBuildVersion(), buildDate);
viewHolder.mBuildInfo.setText(buildInfoText);
boolean busy = mUpdaterController.hasActiveDownloads() ||
mUpdaterController.isVerifyingUpdate();
boolean enabled = !busy && Utils.canInstall(update);
final String downloadId = update.getDownloadId();
if (mUpdaterController.isDownloading(downloadId)) {
String downloaded = Formatter.formatBytes(mContext.getResources(),
update.getFile().length(), Formatter.FLAG_SHORTER).value;
String total = Formatter.formatShortFileSize(mContext, update.getFileSize());
long eta = update.getEta();
if (eta > 0) {
CharSequence etaString = DateUtils.formatDuration(eta * 1000);
viewHolder.mProgressText.setText(mContext.getString(
R.string.list_download_progress_eta, downloaded, total, etaString));
} else {
viewHolder.mProgressText.setText(mContext.getString(
R.string.list_download_progress, downloaded, total));
}
setButtonAction(viewHolder.mAction, Action.PAUSE, downloadId, true);
viewHolder.mProgressBar.setIndeterminate(update.getStatus() == UpdateStatus.STARTING);
viewHolder.mProgressBar.setProgress(update.getProgress());
String percentage = NumberFormat.getPercentInstance().format(
update.getProgress() / 100.f);
viewHolder.mProgressPercentage.setText(percentage);
} else if (mUpdaterController.isInstallingUpdate(downloadId)) {
setButtonAction(viewHolder.mAction, Action.INSTALL, downloadId, false);
viewHolder.mProgressText.setText(R.string.list_installing_update);
viewHolder.mProgressBar.setIndeterminate(true);
} else if (mUpdaterController.isVerifyingUpdate(downloadId)) {
setButtonAction(viewHolder.mAction, Action.INSTALL, downloadId, false);
viewHolder.mProgressText.setText(R.string.list_verifying_update);
viewHolder.mProgressBar.setIndeterminate(true);
viewHolder.mProgressPercentage.setText(NumberFormat.getPercentInstance().format(1));
} else {
if (update.getFile().length() == update.getFileSize()) {
setButtonAction(viewHolder.mAction, Action.VERIFY, downloadId, !busy);
} else {
setButtonAction(viewHolder.mAction, Action.RESUME, downloadId, enabled);
}
String downloaded = Formatter.formatBytes(mContext.getResources(),
update.getFile().length(), Formatter.FLAG_SHORTER).value;
String total = Formatter.formatShortFileSize(mContext, update.getFileSize());
viewHolder.mProgressText.setText(mContext.getString(R.string.list_download_progress,
downloaded, total));
viewHolder.mProgressBar.setIndeterminate(false);
viewHolder.mProgressBar.setProgress(update.getProgress());
}
}
private void handleNotActiveStatus(ViewHolder viewHolder, UpdateDownload update) {
String buildDate = StringGenerator.getDateLocalized(mContext,
DateFormat.LONG, update.getTimestamp());
String buildVersion = mContext.getString(R.string.list_build_version,
BuildInfoUtils.getBuildVersion());
viewHolder.mBuildDate.setText(buildDate);
viewHolder.mBuildVersion.setText(buildVersion);
boolean busy = mUpdaterController.hasActiveDownloads() ||
mUpdaterController.isVerifyingUpdate();
boolean enabled = !busy && Utils.canInstall(update);
if (update.getPersistentStatus() == UpdateStatus.Persistent.VERIFIED) {
setButtonAction(viewHolder.mAction, Action.INSTALL, update.getDownloadId(), enabled);
} else {
setButtonAction(viewHolder.mAction, Action.DOWNLOAD, update.getDownloadId(), enabled);
}
}
@Override
public void onBindViewHolder(final ViewHolder viewHolder, int i) {
if (mUpdaterController == null) {
viewHolder.mButton1.setEnabled(false);
viewHolder.mButton2.setEnabled(false);
viewHolder.mAction.setEnabled(false);
return;
}
@@ -89,50 +189,34 @@ public class UpdatesListAdapter extends RecyclerView.Adapter<UpdatesListAdapter.
UpdateDownload update = mUpdaterController.getUpdate(downloadId);
if (update == null) {
// The update was deleted
viewHolder.mButton1.setEnabled(false);
viewHolder.mButton2.setEnabled(false);
viewHolder.mAction.setEnabled(false);
viewHolder.mAction.setImageResource(R.drawable.ic_download);
return;
}
viewHolder.mName.setText(update.getName());
boolean indeterminate = update.getStatus() == UpdateStatus.STARTING ||
update.getStatus() == UpdateStatus.VERIFYING ||
update.getStatus() == UpdateStatus.INSTALLING;
viewHolder.mProgressBar.setIndeterminate(indeterminate);
if (!indeterminate) {
boolean installing = update.getStatus() == UpdateStatus.INSTALLING;
viewHolder.mProgressBar.setProgress(
installing ? update.getInstallProgress() : update.getProgress());
boolean activeLayout;
switch (update.getStatus()) {
case DOWNLOADING:
case INSTALLING:
case VERIFYING:
case STARTING:
case PAUSED:
case DOWNLOADED:
case PAUSED_ERROR:
activeLayout = update.getPersistentStatus() != UpdateStatus.Persistent.VERIFIED;
break;
default:
activeLayout = false;
}
if (mUpdaterController.isDownloading(downloadId)) {
setButtonAction(viewHolder.mButton1, Action.PAUSE, downloadId, true);
viewHolder.mButton2.setEnabled(false);
} else if (mUpdaterController.isInstallingUpdate()) {
viewHolder.mButton1.setEnabled(false);
viewHolder.mButton2.setEnabled(false);
if (activeLayout) {
handleActiveStatus(viewHolder, update);
viewHolder.mActiveLayout.setVisibility(View.VISIBLE);
viewHolder.mNotActiveLayout.setVisibility(View.INVISIBLE);
} else {
// Allow one active download
boolean busy = mUpdaterController.hasActiveDownloads() ||
mUpdaterController.isVerifyingUpdate();
boolean enabled = !busy && Utils.canInstall(update);
int persistentStatus = update.getPersistentStatus();
if (persistentStatus == UpdateStatus.Persistent.INCOMPLETE) {
if (update.getFile().length() == update.getFileSize()) {
setButtonAction(viewHolder.mButton1, Action.VERIFY, downloadId, !busy);
} else {
setButtonAction(viewHolder.mButton1, Action.RESUME, downloadId,
enabled && update.getAvailableOnline());
}
setButtonAction(viewHolder.mButton2, Action.CANCEL, downloadId, !busy);
} else if (persistentStatus == UpdateStatus.Persistent.VERIFIED) {
setButtonAction(viewHolder.mButton1, Action.INSTALL, downloadId, enabled);
setButtonAction(viewHolder.mButton2, Action.CANCEL, downloadId, !busy);
} else {
setButtonAction(viewHolder.mButton1, Action.DOWNLOAD, downloadId, enabled);
setButtonAction(viewHolder.mButton2, Action.CANCEL, downloadId, false);
}
handleNotActiveStatus(viewHolder, update);
viewHolder.mActiveLayout.setVisibility(View.INVISIBLE);
viewHolder.mNotActiveLayout.setVisibility(View.VISIBLE);
}
}
@@ -156,11 +240,13 @@ public class UpdatesListAdapter extends RecyclerView.Adapter<UpdatesListAdapter.
notifyItemRangeChanged(position, getItemCount());
}
private void setButtonAction(Button button, Action action, final String downloadId,
private void setButtonAction(ImageButton button, Action action, final String downloadId,
boolean enabled) {
switch (action) {
case DOWNLOAD:
button.setText(R.string.download_button);
button.setImageResource(R.drawable.ic_download);
button.setContentDescription(
mContext.getString(R.string.action_description_download));
button.setEnabled(enabled);
button.setOnClickListener(!enabled ? null : new View.OnClickListener() {
@Override
@@ -170,7 +256,9 @@ public class UpdatesListAdapter extends RecyclerView.Adapter<UpdatesListAdapter.
});
break;
case PAUSE:
button.setText(R.string.pause_button);
button.setImageResource(R.drawable.ic_pause);
button.setContentDescription(
mContext.getString(R.string.action_description_pause));
button.setEnabled(enabled);
button.setOnClickListener(!enabled ? null : new View.OnClickListener() {
@Override
@@ -180,17 +268,11 @@ public class UpdatesListAdapter extends RecyclerView.Adapter<UpdatesListAdapter.
});
break;
case RESUME:
button.setText(R.string.resume_button);
button.setEnabled(enabled);
button.setOnClickListener(!enabled ? null : new View.OnClickListener() {
@Override
public void onClick(View view) {
mUpdaterController.resumeDownload(downloadId);
}
});
break;
case VERIFY:
button.setText(R.string.verify_button);
button.setImageResource(R.drawable.ic_resume);
button.setContentDescription(action == Action.RESUME ?
mContext.getString(R.string.action_description_resume) :
mContext.getString(R.string.action_description_verify));
button.setEnabled(enabled);
button.setOnClickListener(!enabled ? null : new View.OnClickListener() {
@Override
@@ -199,18 +281,10 @@ public class UpdatesListAdapter extends RecyclerView.Adapter<UpdatesListAdapter.
}
});
break;
case CANCEL:
button.setText(R.string.cancel_button);
button.setEnabled(enabled);
button.setOnClickListener(!enabled ? null : new View.OnClickListener() {
@Override
public void onClick(View view) {
mUpdaterController.cancelDownload(downloadId);
}
});
break;
case INSTALL:
button.setText(R.string.install_button);
button.setImageResource(R.drawable.ic_system_update);
button.setContentDescription(
mContext.getString(R.string.action_description_install));
button.setEnabled(enabled);
button.setOnClickListener(!enabled ? null : new View.OnClickListener() {
@Override
@@ -220,5 +294,6 @@ public class UpdatesListAdapter extends RecyclerView.Adapter<UpdatesListAdapter.
});
break;
}
button.setAlpha(enabled ? 1.f : mAlphaDisabledValue);
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2017 The LineageOS Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.lineageos.updater.misc;
import android.os.SystemProperties;
public final class BuildInfoUtils {
private BuildInfoUtils() {
}
public static long getBuildDateTimestamp() {
return SystemProperties.getLong(Constants.PROP_BUILD_DATE, 0);
}
public static String getBuildVersion() {
return SystemProperties.get(Constants.PROP_BUILD_VERSION);
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2017 The LineageOS Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.lineageos.updater.misc;
import android.content.Context;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
public final class StringGenerator {
private StringGenerator() {
}
public static String getTimeLocalized(Context context, long unixTimestamp) {
DateFormat f = DateFormat.getTimeInstance(DateFormat.SHORT, getCurrentLocale(context));
Date date = new Date(unixTimestamp * 1000);
return f.format(date);
}
public static String getTimeLocalizedUTC(Context context, long unixTimestamp) {
DateFormat f = DateFormat.getTimeInstance(DateFormat.SHORT, getCurrentLocale(context));
f.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = new Date(unixTimestamp * 1000);
return f.format(date);
}
public static String getDateLocalized(Context context, int dateFormat, long unixTimestamp) {
DateFormat f = DateFormat.getDateInstance(dateFormat, getCurrentLocale(context));
Date date = new Date(unixTimestamp * 1000);
return f.format(date);
}
public static String getDateLocalizedUTC(Context context, int dateFormat, long unixTimestamp) {
DateFormat f = DateFormat.getDateInstance(dateFormat, getCurrentLocale(context));
f.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = new Date(unixTimestamp * 1000);
return f.format(date);
}
public static String getDateTimeLocalized(Context context, long unixTimestamp) {
DateFormat f = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.SHORT,
getCurrentLocale(context));
Date date = new Date(unixTimestamp * 1000);
return f.format(date);
}
public static Locale getCurrentLocale(Context context) {
return context.getResources().getConfiguration().getLocales()
.getFirstMatch(context.getResources().getAssets().getLocales());
}
}