Initial UI work
Look and feel based on CMUpdater.
This commit is contained in:
@@ -33,5 +33,6 @@ LOCAL_AAPT_FLAGS := \
|
||||
|
||||
LOCAL_PACKAGE_NAME := Updater
|
||||
LOCAL_PRIVILEGED_MODULE := true
|
||||
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
|
||||
|
||||
include $(BUILD_PACKAGE)
|
||||
|
@@ -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
2
proguard.flags
Normal file
@@ -0,0 +1,2 @@
|
||||
-keep class android.support.design.widget.** { *; }
|
||||
-keep interface android.support.design.widget.** { *; }
|
10
res/drawable/ic_download.xml
Normal file
10
res/drawable/ic_download.xml
Normal 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
10
res/drawable/ic_pause.xml
Normal 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>
|
10
res/drawable/ic_resume.xml
Normal file
10
res/drawable/ic_resume.xml
Normal 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>
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
@@ -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
10
res/values/colors.xml
Normal 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>
|
@@ -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
24
res/values/styles.xml
Normal 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>
|
@@ -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();
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
32
src/org/lineageos/updater/misc/BuildInfoUtils.java
Normal file
32
src/org/lineageos/updater/misc/BuildInfoUtils.java
Normal 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);
|
||||
}
|
||||
}
|
68
src/org/lineageos/updater/misc/StringGenerator.java
Normal file
68
src/org/lineageos/updater/misc/StringGenerator.java
Normal 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());
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user